feat(ssr): add delete selected to external ledger
Replicate the master CLJS "delete external ledger" feature on the SSR external ledger page: an admin-only bulk delete that retracts the selected journal entries, skipping any in a client's locked period and capping at 1000 per request. Return the result via modal-response (retargets the persistent #modal-content shell) and target #modal-content from the button so the request never relies on the outerHTML swap inherited from the data-grid card, which previously replaced #modal-holder and broke the next click. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -31,7 +31,7 @@
|
||||
[auto-ap.ssr.ui :refer [base-page]]
|
||||
[auto-ap.ssr.utils
|
||||
:refer [apply-middleware-to-all-handlers clj-date-schema
|
||||
html-response main-transformer money strip
|
||||
html-response modal-response main-transformer money strip
|
||||
wrap-form-4xx-2 wrap-implied-route-param
|
||||
wrap-merge-prior-hx wrap-schema-decode
|
||||
wrap-schema-enforce]]
|
||||
@@ -69,6 +69,40 @@
|
||||
selected)]
|
||||
ids))
|
||||
|
||||
(defn all-ids-not-locked
|
||||
"Filters journal-entry ids to only those whose date is on/after the client's
|
||||
locked-until date (i.e. not in a reconciled/locked period)."
|
||||
[all-ids]
|
||||
(->> all-ids
|
||||
(dc/q '[:find ?t
|
||||
:in $ [?t ...]
|
||||
:where
|
||||
[?t :journal-entry/client ?c]
|
||||
[(get-else $ ?c :client/locked-until #inst "2000-01-01") ?lu]
|
||||
[?t :journal-entry/date ?d]
|
||||
[(>= ?d ?lu)]]
|
||||
(dc/db conn))
|
||||
(map first)))
|
||||
|
||||
(defn bulk-delete [request]
|
||||
(assert-admin (:identity request))
|
||||
(let [params (:form-params request)
|
||||
ids (selected->ids (assoc-in request [:route-params :external?] true) params)
|
||||
all-ids (all-ids-not-locked ids)]
|
||||
(if (> (count all-ids) 1000)
|
||||
(modal-response
|
||||
(com/success-modal {:title "Too many ledger entries"}
|
||||
[:p "You can only delete 1000 ledger entries at a time."]))
|
||||
(do
|
||||
(alog/info ::bulk-delete-ledger :count (count all-ids) :sample (take 3 all-ids))
|
||||
(audit-transact-batch
|
||||
(map (fn [i] [:db/retractEntity i]) all-ids)
|
||||
(:identity request))
|
||||
(modal-response
|
||||
(com/success-modal {:title "Ledger Entries Deleted"}
|
||||
[:p (str "Successfully deleted " (count all-ids) " ledger entries.")])
|
||||
:headers {"hx-trigger" "invalidated, reset-selection"})))))
|
||||
|
||||
(defn delete [{invoice :entity :as request identity :identity}]
|
||||
(exception->notification
|
||||
#(when-not (= :invoice-status/unpaid (:invoice/status invoice))
|
||||
@@ -696,6 +730,8 @@
|
||||
::route/csv (helper/csv-route grid-page)
|
||||
::route/external-import-page external-import-page
|
||||
::route/bank-account-filter bank-account-filter
|
||||
::route/bulk-delete (-> bulk-delete
|
||||
(wrap-schema-enforce :form-schema query-schema))
|
||||
::route/external-import-parse (-> external-import-parse
|
||||
(wrap-schema-enforce :form-schema parse-form-schema)
|
||||
(wrap-form-4xx-2 external-import-parse)
|
||||
|
||||
@@ -482,10 +482,26 @@
|
||||
(assoc-in (exact-match-id* request) [1 :hx-swap-oob] true)])
|
||||
:query-schema query-schema
|
||||
:action-buttons (fn [request]
|
||||
[(when-not (:external? (:route-params request)) (com/button {:color :primary
|
||||
:hx-get (bidi/path-for ssr-routes/only-routes
|
||||
::route/new)}
|
||||
"Add journal entry"))])
|
||||
[(when-not (:external? (:route-params request))
|
||||
(com/button {:color :primary
|
||||
:hx-get (bidi/path-for ssr-routes/only-routes
|
||||
::route/new)}
|
||||
"Add journal entry"))
|
||||
(when (and (:external? (:route-params request))
|
||||
(= "admin" (:user/role (:identity request))))
|
||||
(com/button {:color :red
|
||||
:hx-post (bidi/path-for ssr-routes/only-routes ::route/bulk-delete)
|
||||
;; target the persistent modal shell content slot directly so the
|
||||
;; request never relies on the outerHTML swap inherited from the
|
||||
;; data-grid card (which would replace #modal-holder and break the
|
||||
;; next click). modal-response also retargets here.
|
||||
:hx-target "#modal-content"
|
||||
:hx-swap "innerHTML"
|
||||
"x-bind:hx-vals" "JSON.stringify({selected: $data.selected, 'all-selected': $data.all_selected})"
|
||||
"x-bind:disabled" "selected.length === 0 && !all_selected"
|
||||
"hx-include" "#ledger-filters"
|
||||
:hx-confirm "Are you sure you want to delete these ledger entries?"}
|
||||
"Delete selected"))])
|
||||
:row-buttons (fn [request entity]
|
||||
[(when (and (= :invoice-status/unpaid (:invoice/status entity))
|
||||
(can? (:identity request) {:subject :invoice :activity :delete}))
|
||||
|
||||
Reference in New Issue
Block a user