Improvements for transaction page

This commit is contained in:
2025-03-22 23:21:21 -07:00
parent f3ca8afcc9
commit 0bae8f3d1b
8 changed files with 158 additions and 101 deletions

File diff suppressed because one or more lines are too long

View File

@@ -25,6 +25,5 @@
(alog/info ::closed :count (count invoices-to-close)))) (alog/info ::closed :count (count invoices-to-close))))
(defn -main [& _] (defn -main [& _]
(execute "close-auto-invoices" close-auto-invoices)) (execute "close-auto-invoices" close-auto-invoices))

View File

@@ -135,7 +135,7 @@
:fetch-page fetch-page :fetch-page fetch-page
:page-specific-nav filters :page-specific-nav filters
:row-buttons (fn [_ entity] :row-buttons (fn [_ entity]
[(com/a-icon-button {:href (hu/url (bidi/path-for ssr-routes/only-routes ::transaction-routes/all-page) [(com/a-icon-button {:href (hu/url (bidi/path-for ssr-routes/only-routes ::transaction-routes/page)
{:import-batch-id (:db/id entity)}) {:import-batch-id (:db/id entity)})
:hx-boost true} :hx-boost true}
svg/external-link)]) svg/external-link)])

View File

@@ -57,7 +57,7 @@
(atime/unparse-local atime/standard-time) (atime/unparse-local atime/standard-time)
(#(str "Synced " %)))] (#(str "Synced " %)))]
(when-let [n (cond (-> b :bank-account/intuit-bank-account) #_(when-let [n (cond (-> b :bank-account/intuit-bank-account)
"Intuit" "Intuit"
(-> b :bank-account/yodlee-account) (-> b :bank-account/yodlee-account)
"Yodlee" "Yodlee"

View File

@@ -171,6 +171,10 @@
(merge-query {:query {:in ['?vendor-id] (merge-query {:query {:in ['?vendor-id]
:where ['[?e :transaction/vendor ?vendor-id]]} :where ['[?e :transaction/vendor ?vendor-id]]}
:args [(:db/id (:vendor args))]}) :args [(:db/id (:vendor args))]})
(:import-batch-id args)
(merge-query {:query {:in ['?import-batch-id]
:where ['[?import-batch-id :import-batch/entry ?e]]}
:args [(:import-batch-id args)]})
(:status route-params) (:status route-params)
(merge-query {:query {:in ['?status] (merge-query {:query {:in ['?status]
@@ -251,6 +255,7 @@
[:amount-gte {:optional true} [:maybe :double]] [:amount-gte {:optional true} [:maybe :double]]
[:amount-lte {:optional true} [:maybe :double]] [:amount-lte {:optional true} [:maybe :double]]
[:client-id {:optional true} [:maybe entity-id]] [:client-id {:optional true} [:maybe entity-id]]
[:import-batch-id {:optional true} [:maybe entity-id]]
[:description {:optional true} [:maybe [:string {:decode/string strip}]]] [:description {:optional true} [:maybe [:string {:decode/string strip}]]]
[:vendor {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :vendor/name]}]]] [:vendor {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :vendor/name]}]]]
[:bank-account {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :bank-account/numeric-code]}]]] [:bank-account {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :bank-account/numeric-code]}]]]

View File

@@ -10,6 +10,7 @@
exception->4xx]] exception->4xx]]
[auto-ap.import.transactions :as i-transactions] [auto-ap.import.transactions :as i-transactions]
[auto-ap.logging :as alog] [auto-ap.logging :as alog]
[auto-ap.permissions :refer [wrap-must]]
[auto-ap.routes.payments :as payment-route] [auto-ap.routes.payments :as payment-route]
[auto-ap.routes.transactions :as route] [auto-ap.routes.transactions :as route]
[auto-ap.routes.utils [auto-ap.routes.utils
@@ -26,11 +27,12 @@
[auto-ap.ssr.utils [auto-ap.ssr.utils
:refer [->db-id apply-middleware-to-all-handlers check-allowance :refer [->db-id apply-middleware-to-all-handlers check-allowance
check-location-belongs entity-id form-validation-error check-location-belongs entity-id form-validation-error
html-response modal-response ref->enum-schema strip wrap-entity html-response modal-response ref->enum-schema strip temp-id
wrap-schema-enforce]] wrap-entity wrap-schema-enforce]]
[auto-ap.time :as atime] [auto-ap.time :as atime]
[bidi.bidi :as bidi] [bidi.bidi :as bidi]
[clj-time.coerce :as coerce] [clj-time.coerce :as coerce]
[clojure.edn :as edn]
[datomic.api :as dc] [datomic.api :as dc]
[hiccup.util :as hu] [hiccup.util :as hu]
[iol-ion.query :refer [dollars=]] [iol-ion.query :refer [dollars=]]
@@ -72,7 +74,7 @@
[:vector {:coerce? true} [:vector {:coerce? true}
[:and [:and
[:map [:map
[:db/id {:optional true} [:maybe entity-id]] [:db/id {:optional true} [:maybe [:or temp-id entity-id]]]
[:transaction-account/account [:and entity-id [:transaction-account/account [:and entity-id
[:fn {:error/message "Not an allowed account."} [:fn {:error/message "Not an allowed account."}
#(check-allowance % :account/default-allowance)]]] #(check-allowance % :account/default-allowance)]]]
@@ -90,10 +92,11 @@
[:transaction-id entity-id]]] [:transaction-id entity-id]]]
[:link-unpaid-invoices [:map [:link-unpaid-invoices [:map
[:unpaid-invoice-ids [:vector {:coerce? true} entity-id]]]] [:unpaid-invoice-ids {:decode/string (fn [x] (edn/read-string x))}
[:vector {:coerce? true} entity-id]]]]
[:link-autopay-invoices [:map [:link-autopay-invoices [:map
[:autopay-invoice-ids [:vector {:coerce? true} entity-id]]]] [:autopay-invoice-ids {:decode/string (fn [x] (edn/read-string x))} [:vector {:coerce? true} entity-id]]]]
[:link-payment [:map [:link-payment [:map
[:payment-id entity-id]]] [:payment-id entity-id]]]
[:manual [:map [:manual [:map
@@ -378,9 +381,6 @@
;; Editable fields section ;; Editable fields section
[:div.mt-6
[:h3.text-lg.font-semibold.mb-4 "Editable Fields"]]
;; Vendor field ;; Vendor field
) )
:footer :footer
@@ -416,7 +416,6 @@
(get-in request [:route-params :db/id])) (get-in request [:route-params :db/id]))
tx (when tx-id (d-transactions/get-by-id tx-id)) tx (when tx-id (d-transactions/get-by-id tx-id))
client-id (-> request :entity :transaction/client :db/id) client-id (-> request :entity :transaction/client :db/id)
_ (println "TRANSACTION" client-id)
matches-set (when (and tx client-id) matches-set (when (and tx client-id)
(i-transactions/match-transaction-to-unfulfilled-autopayments (i-transactions/match-transaction-to-unfulfilled-autopayments
(:transaction/amount tx) (:transaction/amount tx)
@@ -432,28 +431,20 @@
(if (seq invoice-matches) (if (seq invoice-matches)
[:div [:div
[:h3.text-lg.font-bold.mb-4 "Available Autopay Invoices"] [:h3.text-lg.font-bold.mb-4 "Available Autopay Invoices"]
[:div {:hx-post (bidi/path-for ssr-routes/only-routes ::route/link-autopay-invoices) (com/hidden {:name "action"
:hx-include "this" :value "link-autopay-invoices"})
:hx-trigger "linkAutopayInvoices" [:div.space-y-2
:hx-target "#modal-holder" [:label.block.text-sm.font-medium.mb-1 "Select an autopay invoice to apply:"]
:hx-swap "outerHTML"} (com/radio-card {:options (for [match-group invoice-matches]
(com/hidden {:name "action" {:value (pr-str (map :db/id match-group))
:value "link-autopay-invoices"}) :content (doall (for [invoice match-group]
(com/hidden {:name "transaction-id" [:div.ml-3
:value (get-in request [:multi-form-state :snapshot :db/id]) [:span.block.text-sm.font-medium (:invoice/invoice-number invoice)]
:form ""}) [:span.block.text-sm.text-gray-500 (-> invoice :invoice/vendor :vendor/name)]
[:div.space-y-2 [:span.block.text-sm.font-medium (format "$%.2f" (:invoice/outstanding-balance invoice))]]))})
[:label.block.text-sm.font-medium.mb-1 "Select an autopay invoice to apply:"] :name (fc/with-field :autopay-invoice-ids (fc/field-name ))
(doall (for [match-group invoice-matches] :width "w-full"})]
(doall (for [invoice match-group] ]
[:div.flex.items-center
[:input {:type :radio :value (:db/id invoice) :name "autopay-invoice-ids"}]
[:div.ml-3
[:span.block.text-sm.font-medium (:invoice/invoice-number invoice)]
[:span.block.text-sm.text-gray-500 (-> invoice :invoice/vendor :vendor/name)]
[:span.block.text-sm.font-medium (format "$%.2f" (:invoice/total invoice))]]]))))]
(com/a-button {"@click" "$dispatch('linkAutopayInvoices')"} "Match")
]]
[:div.text-center.py-4.text-gray-500 "No matching autopay invoices available for this transaction."])])) [:div.text-center.py-4.text-gray-500 "No matching autopay invoices available for this transaction."])]))
(defn get-available-unpaid-invoices [request] (defn get-available-unpaid-invoices [request]
@@ -477,30 +468,31 @@
(if (seq invoice-matches) (if (seq invoice-matches)
[:div [:div
[:h3.text-lg.font-bold.mb-4 "Available Unpaid Invoices"] [:h3.text-lg.font-bold.mb-4 "Available Unpaid Invoices"]
[:div {:hx-post (bidi/path-for ssr-routes/only-routes ::route/link-unpaid-invoices) [:div #_{:hx-post (bidi/path-for ssr-routes/only-routes ::route/link-unpaid-invoices)
:hx-include "this" :hx-include "this"
:hx-params "transaction-id, action, unpaid-invoice-ids" :hx-params "transaction-id, action, unpaid-invoice-ids"
:hx-trigger "linkUnpaidInvoices" :hx-trigger "linkUnpaidInvoices"
:hx-target "#modal-holder" :hx-target "#modal-holder"
:hx-swap "outerHTML"} :hx-swap "outerHTML"}
(com/hidden {:name "action" (com/hidden {:name "action"
:value "link-unpaid-invoices" :value "link-unpaid-invoices"
:form ""}) :form ""})
(com/hidden {:name "transaction-id" #_(com/hidden {:name "transaction-id"
:value (get-in request [:multi-form-state :snapshot :db/id]) :value (get-in request [:multi-form-state :snapshot :db/id])
:form ""}) :form ""})
[:div.space-y-2 [:div.space-y-2
[:label.block.text-sm.font-medium.mb-1 "Select an unpaid invoice to apply:"] [:label.block.text-sm.font-medium.mb-1 "Select an unpaid invoice to apply:"]
(doall (for [match-group invoice-matches] (com/radio-card {:options (for [match-group invoice-matches]
(doall (for [invoice match-group] {:value (pr-str (map :db/id match-group))
[:div.flex.items-center :content (doall (for [invoice match-group]
[:input {:type :radio :value (:db/id invoice) :name "unpaid-invoice-ids"}] [:div.ml-3
[:div.ml-3 [:span.block.text-sm.font-medium (:invoice/invoice-number invoice)]
[:span.block.text-sm.font-medium (:invoice/invoice-number invoice)] [:span.block.text-sm.text-gray-500 (-> invoice :invoice/vendor :vendor/name)]
[:span.block.text-sm.text-gray-500 (-> invoice :invoice/vendor :vendor/name)] [:span.block.text-sm.font-medium (format "$%.2f" (:invoice/outstanding-balance invoice))]]))})
[:span.block.text-sm.font-medium (format "$%.2f" (:invoice/outstanding-balance invoice))]]]))))] :name (fc/with-field :unpaid-invoice-ids (fc/field-name ))
(com/a-button {:color :primary "@click" "$dispatch('linkUnpaidInvoices')"} "Link") :width "w-full"})
]] ]
#_(com/a-button {:color :primary "@click" "$dispatch('linkUnpaidInvoices')"} "Link")]]
[:div.text-center.py-4.text-gray-500 "No matching unpaid invoices available for this transaction."])])) [:div.text-center.py-4.text-gray-500 "No matching unpaid invoices available for this transaction."])]))
(defn get-available-rules [request] (defn get-available-rules [request]
@@ -536,24 +528,20 @@
(if (seq matching-rules) (if (seq matching-rules)
[:div [:div
[:h3.text-lg.font-bold.mb-4 "Matching Transaction Rules"] [:h3.text-lg.font-bold.mb-4 "Matching Transaction Rules"]
[:div {:hx-post (bidi/path-for ssr-routes/only-routes ::route/apply-rule) (fc/with-field :action
:hx-trigger "applyRule" (com/hidden {:name (fc/field-name)
:hx-include "this" :value "apply-rule"
:hx-target "#modal-holder" :form ""}))
:hx-swap "outerHTML"} [:div.space-y-2
(fc/with-field :action [:label.block.text-sm.font-medium.mb-1 "Select a rule to apply:"]
(com/hidden {:name (fc/field-name) (com/radio-card {:options (for [{:keys [:db/id :transaction-rule/note :transaction-rule/description]} matching-rules]
:value "apply-rule" {:value id
:form ""})) :content [:div.ml-3
[:div.space-y-2 [:span.block.text-sm.font-medium note]
[:label.block.text-sm.font-medium.mb-1 "Select a rule to apply:"] [:span.block.text-sm.text-gray-500 description]]})
(doall (for [{:keys [:db/id :transaction-rule/note :transaction-rule/description]} matching-rules] :name (fc/with-field :rule-id (fc/field-name ))
[:div.flex.items-center :width "w-full"}) ]
[:input {:type :radio :value id :name (fc/with-field :rule-id (fc/field-name))}] #_(com/a-button {"@click" "$dispatch('applyRule')"} "Apply")]
[:div.ml-3
[:span.block.text-sm.font-medium note]
[:span.block.text-sm.text-gray-500 description]]]))]
(com/a-button {"@click" "$dispatch('applyRule')"} "Apply")]]
[:div.text-center.py-4.text-gray-500 "No matching rules found for this transaction."])])) [:div.text-center.py-4.text-gray-500 "No matching rules found for this transaction."])]))
(defn payment-matches-view [request] (defn payment-matches-view [request]
@@ -619,8 +607,7 @@
" - Amount: $" (format "%.2f" (:payment/amount payment)) " - Amount: $" (format "%.2f" (:payment/amount payment))
" • Date: " (some-> payment :payment/date coerce/to-date-time (atime/unparse-local atime/normal-date)))}) " • Date: " (some-> payment :payment/date coerce/to-date-time (atime/unparse-local atime/normal-date)))})
:name payment-id-field :name payment-id-field
:width "w-full"}))) :width "w-full"}))) ]]
(com/a-button {"@click" "$dispatch('matchPayment')"} "Match" #_[:button.mt-4.w-full.py-2.bg-blue-500.text-white.rounded.hover:bg-blue-600 "Match"])]]
[:div.text-center.py-4.text-gray-500 "No matching payments available for this transaction."]))])) [:div.text-center.py-4.text-gray-500 "No matching payments available for this transaction."]))]))
(defn count-payment-matches [request] (defn count-payment-matches [request]
@@ -667,7 +654,8 @@
:placeholder "Optional note"})])) :placeholder "Optional note"})]))
[:div {:x-data (hx/json {:activeForm (if (:transaction/payment (:entity request)) [:div {:x-data (hx/json {:activeForm (if (:transaction/payment (:entity request))
"link-payment" "link-payment"
(fc/with-field :action (fc/field-value))) (or (fc/with-field :action (fc/field-value))
"manual"))
:canChange (boolean (not (:transaction/payment (:entity request))))}) :canChange (boolean (not (:transaction/payment (:entity request))))})
"@unlinked" "canChange=true"} "@unlinked" "canChange=true"}
[:div {:class "flex space-x-2 mb-4"} [:div {:class "flex space-x-2 mb-4"}
@@ -863,7 +851,7 @@
:headers {"hx-trigger" "invalidated"}))) :headers {"hx-trigger" "invalidated"})))
(defmethod save-handler :link-autopay-invoices (defmethod save-handler :link-autopay-invoices
[{{:keys [autopay-invoice-ids]} :form-params :as request transaction :entity}] [{{ {:keys [autopay-invoice-ids] :as snapshot} :snapshot} :multi-form-state :as request transaction :entity} ]
(let [db (dc/db conn) (let [db (dc/db conn)
invoice-clients (set (map #(pull-ref db :invoice/client %) autopay-invoice-ids)) invoice-clients (set (map #(pull-ref db :invoice/client %) autopay-invoice-ids))
invoice-amount (reduce + 0.0 (map #(pull-attr db :invoice/total %) autopay-invoice-ids))] invoice-amount (reduce + 0.0 (map #(pull-attr db :invoice/total %) autopay-invoice-ids))]
@@ -894,7 +882,8 @@
autopay-invoice-ids) autopay-invoice-ids)
(-> transaction :transaction/bank-account :db/id) (-> transaction :transaction/bank-account :db/id)
(-> transaction :transaction/client :db/id))] (-> transaction :transaction/client :db/id))]
(audit-transact payment-tx (:identity request))) (audit-transact (conj payment-tx
[:upsert-transaction (default-update-tx snapshot {:db/id (:db/id transaction)})]) (:identity request)))
(solr/touch-with-ledger (:db/id transaction)) (solr/touch-with-ledger (:db/id transaction))
@@ -905,7 +894,7 @@
:headers {"hx-trigger" "invalidated"}))) :headers {"hx-trigger" "invalidated"})))
(defmethod save-handler :link-unpaid-invoices (defmethod save-handler :link-unpaid-invoices
[{{:keys [unpaid-invoice-ids]} :form-params :as request transaction :entity}] [{{ {:keys [unpaid-invoice-ids] :as snapshot} :snapshot} :multi-form-state :as request transaction :entity} ]
(let [ db (dc/db conn) (let [ db (dc/db conn)
invoice-clients (set (map #(pull-ref db :invoice/client %) unpaid-invoice-ids)) invoice-clients (set (map #(pull-ref db :invoice/client %) unpaid-invoice-ids))
invoice-amount (reduce + 0.0 (map #(pull-attr db :invoice/outstanding-balance %) unpaid-invoice-ids))] invoice-amount (reduce + 0.0 (map #(pull-attr db :invoice/outstanding-balance %) unpaid-invoice-ids))]
@@ -938,7 +927,8 @@
unpaid-invoice-ids) unpaid-invoice-ids)
(-> transaction :transaction/bank-account :db/id) (-> transaction :transaction/bank-account :db/id)
(-> transaction :transaction/client :db/id))] (-> transaction :transaction/client :db/id))]
(audit-transact payment-tx (:identity request))) (audit-transact (conj payment-tx
[:upsert-transaction (default-update-tx snapshot {:db/id (:db/id transaction)})]) (:identity request)))
(solr/touch-with-ledger (:db/id transaction)) (solr/touch-with-ledger (:db/id transaction))
@@ -958,7 +948,7 @@
(defmethod save-handler (defmethod save-handler
:apply-rule :apply-rule
[{{{:keys [rule-id]} :snapshot} :multi-form-state :as request transaction :entity}] [{{{:keys [rule-id] :as snapshot} :snapshot} :multi-form-state :as request transaction :entity}]
(let [transaction-rule (dc/pull (dc/db conn) (let [transaction-rule (dc/pull (dc/db conn)
[:transaction-rule/description [:transaction-rule/description
:transaction-rule/vendor :transaction-rule/vendor
@@ -982,7 +972,8 @@
updated-tx (rm/apply-rule {:db/id (:db/id transaction) updated-tx (rm/apply-rule {:db/id (:db/id transaction)
:transaction/amount (:transaction/amount transaction)} :transaction/amount (:transaction/amount transaction)}
transaction-rule transaction-rule
locations)] locations)
updated-tx (default-update-tx snapshot updated-tx)]
(alog/info ::applying-rule-tx :tx-data updated-tx (alog/info ::applying-rule-tx :tx-data updated-tx
:transaction transaction :transaction transaction
:transaction-rule transaction-rule) :transaction-rule transaction-rule)
@@ -996,16 +987,73 @@
[:p.text-gray-600.mt-2 "The selected rule has been applied to this transaction."]) [:p.text-gray-600.mt-2 "The selected rule has been applied to this transaction."])
:headers {"hx-trigger" "invalidated"}))) :headers {"hx-trigger" "invalidated"})))
(defn- calculate-spread
"Helper function to calculate the amount to be assigned to each location"
[shared-amount total-locations]
(let [base-amount (int (/ shared-amount total-locations))
remainder (- shared-amount (* base-amount total-locations))]
{:base-amount base-amount
:remainder remainder}))
(defn- spread-account
"Spreads the expense account amount across the given locations"
[locations account]
(if (= "Shared" (:transaction-account/location account))
(let [{:keys [base-amount remainder]} (calculate-spread (:transaction-account/amount account) (count locations))]
(map-indexed (fn [idx _]
(assoc account
:transaction-account/amount (+ base-amount (if (< idx remainder) 1 0))
:transaction-account/location (nth locations idx)))
locations))
[account]))
(defn- apply-total-delta-to-account [invoice-total eas]
(when (seq eas)
(let [leftover (- invoice-total (reduce + 0 (map :transaction-account/amount eas)))
leftover-beyond-a-single-cent? (or (< leftover -1)
(> leftover 1))
leftover (if leftover-beyond-a-single-cent?
0
leftover)
[first-eas & rest] eas]
(cons
(update first-eas :transaction-account/amount #(+ % leftover))
rest))))
(defn $->cents [x]
(int
(let [result (* 100M (bigdec x))]
(.setScale result 0 java.math.BigDecimal/ROUND_HALF_UP))))
(defn cents->$ [x]
(double
(let [result (* 0.01M (bigdec x))]
(.setScale result 2 java.math.BigDecimal/ROUND_HALF_UP))))
(defn maybe-spread-locations
"Converts any expense account for a \"Shared\" location into a separate expense account for all valid locations for that client"
([transaction]
(maybe-spread-locations transaction (pull-attr (dc/db conn) :client/locations (:transaction/client transaction))))
([transaction locations]
(clojure.pprint/pprint transaction)
(update-in transaction
[:transaction/accounts]
(fn [accounts]
(->> accounts
(map (fn [ea] (update ea :transaction-account/amount $->cents)))
(mapcat (partial spread-account locations))
(apply-total-delta-to-account ($->cents (:transaction/amount transaction)))
(map (fn [ea] (update ea :transaction-account/amount cents->$))))))))
(defmethod save-handler :manual (defmethod save-handler :manual
[{:as request [{:as request
transaction :entity transaction :entity
:keys [multi-form-state]}] :keys [multi-form-state]}]
(let [tx-data (-> multi-form-state :snapshot (dissoc :action)) (let [tx-data (-> multi-form-state :snapshot (dissoc :action))
_ (clojure.pprint/pprint tx-data)
tx-id (:db/id tx-data) tx-id (:db/id tx-data)
client-id (->db-id (:transaction/client tx-data)) client-id (->db-id (:transaction/client tx-data))
existing-tx (d-transactions/get-by-id tx-id) existing-tx (d-transactions/get-by-id tx-id)
transaction [:upsert-transaction (assoc tx-data :db/id tx-id)]] transaction [:upsert-transaction (maybe-spread-locations (assoc tx-data :db/id tx-id))]]
(alog/info ::transaction transaction :entity transaction) (alog/info ::transaction transaction :entity transaction)
(exception->4xx #(assert-can-see-client (:identity request) client-id)) (exception->4xx #(assert-can-see-client (:identity request) client-id))
@@ -1102,10 +1150,6 @@
(html-response (fc/with-field :step-params (payment-matches-view request)) (html-response (fc/with-field :step-params (payment-matches-view request))
:headers {"hx-trigger" "unlinked"})))) :headers {"hx-trigger" "unlinked"}))))
#_(def save-schema
(mc/schema
))
(defrecord EditWizard [_ current-step] (defrecord EditWizard [_ current-step]
mm/LinearModalWizard mm/LinearModalWizard
@@ -1120,6 +1164,7 @@
(mm/get-step this :basic-details))) (mm/get-step this :basic-details)))
(render-wizard [this {:keys [multi-form-state] :as request}] (render-wizard [this {:keys [multi-form-state] :as request}]
(println "HERE XYZ" (:form-errors request)) (println "HERE XYZ" (:form-errors request))
(clojure.pprint/pprint (:snapshot multi-form-state) )
(mm/default-render-wizard (mm/default-render-wizard
this request this request
:form-params :form-params
@@ -1168,15 +1213,18 @@
(def key->handler (def key->handler
(apply-middleware-to-all-handlers (apply-middleware-to-all-handlers
{::route/edit-wizard (-> mm/open-wizard-handler {::route/edit-wizard (-> mm/open-wizard-handler
(wrap-must {:activity :edit :subject :transaction} (fn get-client [request] (-> request :entity :transaction/client)))
(mm/wrap-wizard edit-wizard) (mm/wrap-wizard edit-wizard)
(mm/wrap-init-multi-form-state initial-edit-wizard-state) (mm/wrap-init-multi-form-state initial-edit-wizard-state)
(wrap-entity [:route-params :db/id] d-transactions/default-read) (wrap-entity [:route-params :db/id] d-transactions/default-read)
(wrap-schema-enforce :route-schema [:map [:db/id entity-id]])) (wrap-schema-enforce :route-schema [:map [:db/id entity-id]]))
::route/edit-wizard-navigate (-> mm/next-handler ::route/edit-wizard-navigate (-> mm/next-handler
(wrap-must {:activity :edit :subject :transaction} (fn get-client [request] (-> request :entity :transaction/client)))
(wrap-entity [:multi-form-state :snapshot :db/id] d-transactions/default-read) (wrap-entity [:multi-form-state :snapshot :db/id] d-transactions/default-read)
(mm/wrap-wizard edit-wizard) (mm/wrap-wizard edit-wizard)
(mm/wrap-decode-multi-form-state)) (mm/wrap-decode-multi-form-state))
::route/edit-submit (-> mm/submit-handler ::route/edit-submit (-> mm/submit-handler
(wrap-must {:activity :edit :subject :transaction} (fn get-client [request] (-> request :entity :transaction/client)))
(wrap-entity [:multi-form-state :snapshot :db/id] d-transactions/default-read) (wrap-entity [:multi-form-state :snapshot :db/id] d-transactions/default-read)
(mm/wrap-wizard edit-wizard) (mm/wrap-wizard edit-wizard)
(mm/wrap-decode-multi-form-state)) (mm/wrap-decode-multi-form-state))
@@ -1206,11 +1254,12 @@
[:maybe entity-id]]])) [:maybe entity-id]]]))
::route/unlink-payment (-> unlink-payment ::route/unlink-payment (-> unlink-payment
(wrap-must {:activity :edit :subject :transaction} (fn get-client [request] (-> request :entity :transaction/client)))
(wrap-entity [:multi-form-state :snapshot :db/id] d-transactions/default-read) (wrap-entity [:multi-form-state :snapshot :db/id] d-transactions/default-read)
(mm/wrap-wizard edit-wizard) (mm/wrap-wizard edit-wizard)
(mm/wrap-decode-multi-form-state) (mm/wrap-decode-multi-form-state)
#_(wrap-schema-enforce :form-schema #_(wrap-schema-enforce :form-schema
save-schema))} save-schema))}
(fn [h] (fn [h]
(-> h (-> h
(wrap-client-redirect-unauthenticated))))) (wrap-client-redirect-unauthenticated)))))

View File

@@ -142,11 +142,19 @@
false))) false)))
#? (:clj #? (:clj
(defn wrap-must [handler policy] (defn wrap-must
(fn [request] ( [handler policy]
(if (can? (:identity request) policy) (fn [request]
(handler request) (if (can? (:identity request) policy)
{:status 302 (handler request)
:headers {"Location" (str "/login?" {:status 302
(url/map->query {"redirect-to" (:uri request)}))}})))) :headers {"Location" (str "/login?"
(url/map->query {"redirect-to" (:uri request)}))}})))
( [handler policy get-client]
(fn [request]
(if (can? (:identity request) (assoc policy :client (get-client request)))
(handler request)
{:status 302
:headers {"Location" (str "/login?"
(url/map->query {"redirect-to" (:uri request)}))}})))))

6
tasks
View File

@@ -1,10 +1,6 @@
* Add tests for edit transaction. * Add tests for edit transaction.
* Make it so you can create a new vendor again. * Make it so you can create a new vendor again.
* Hide unhelpful report from the dashboard
* Check permissions on ledger, transactions, reports * Check permissions on ledger, transactions, reports
* Make sure that you can't change a transaction if its payment is set
* also add tests
* Make sure that "Shared" really shares locations
* make sure transactions support import-batch-id query parameter
* make locked transactions clearer on the transaction table * make locked transactions clearer on the transaction table
* Make locked transactions not look butt ugly with errors * Make locked transactions not look butt ugly with errors
* Implement bulk actions