From dd2f6508fe652842e4518830d72a04fdaf27d717 Mon Sep 17 00:00:00 2001 From: Bryce Date: Sat, 15 Mar 2025 21:20:19 -0700 Subject: [PATCH] fixes --- src/clj/auto_ap/ssr/transaction/edit.clj | 208 ++++++++++------------- src/clj/auto_ap/ssr/ui.clj | 11 +- src/clj/auto_ap/ssr/utils.clj | 1 + tasks | 6 +- 4 files changed, 104 insertions(+), 122 deletions(-) diff --git a/src/clj/auto_ap/ssr/transaction/edit.clj b/src/clj/auto_ap/ssr/transaction/edit.clj index b79a4ef0..df5bf40c 100644 --- a/src/clj/auto_ap/ssr/transaction/edit.clj +++ b/src/clj/auto_ap/ssr/transaction/edit.clj @@ -25,8 +25,9 @@ [auto-ap.ssr.svg :as svg] [auto-ap.ssr.utils :refer [->db-id apply-middleware-to-all-handlers check-allowance - check-location-belongs entity-id html-response modal-response - ref->enum-schema strip wrap-entity wrap-schema-enforce]] + check-location-belongs entity-id form-validation-error + html-response modal-response ref->enum-schema strip wrap-entity + wrap-schema-enforce]] [auto-ap.time :as atime] [bidi.bidi :as bidi] [clj-time.coerce :as coerce] @@ -34,8 +35,7 @@ [hiccup.util :as hu] [iol-ion.query :refer [dollars=]] [iol-ion.tx :refer [random-tempid]] - [malli.core :as mc] - [malli.util :as mut])) + [malli.core :as mc])) (def transaction-approval-status {:transaction-approval-status/unapproved "Unapproved" @@ -627,45 +627,30 @@ [:div.font-medium "Date"] [:div (some-> payment :payment/date (atime/unparse-local atime/normal-date))]] [:div.mt-4 {:hx-post (bidi/path-for ssr-routes/only-routes ::route/unlink-payment) - :hx-params "transaction-id, action" :hx-trigger "unlinkPayment" :hx-target "#payment-matches" :hx-include "this" :hx-swap "outerHTML" :hx-confirm "Are you sure you want to unlink this payment?"} - (com/hidden {:name "action" - :value "unlink-payment" - :form ""}) - (com/hidden {:name "transaction-id" :value tx-id :form ""}) (com/a-button {:color :red :size :small "@click" "$dispatch('unlinkPayment')"} "Unlink Payment")]]]) (if (seq payments) [:div [:h3.text-lg.font-bold.mb-4 "Available Payments"] - [:div {:hx-post (bidi/path-for ssr-routes/only-routes ::route/link-payment) - :hx-trigger "matchPayment" - :hx-target "#modal-holder" - :hx-include "this" - :hx-swap "outerHTML"} - (com/hidden {:name "action" - :value "link-payment" - :form ""}) - (com/hidden {:name "transaction-id" - :value (-> request :entity :db/id) - :form ""}) - [:div.space-y-2 - [:label.block.text-sm.font-medium.mb-1 "Select a payment to match:"] - (when payments - (com/radio-card {:options (for [payment payments] - {:value (:db/id payment) - :content (str (:payment/invoice-number payment) " - " - (-> payment :payment/vendor :vendor/name) - " - Amount: $" (format "%.2f" (:payment/amount payment)) - " • Date: " (some-> payment :payment/date coerce/to-date-time (atime/unparse-local atime/normal-date)))}) - :name "payment-id" - :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.space-y-2 + [:label.block.text-sm.font-medium.mb-1 "Select a payment to match:"] + (when payments + (let [payment-id-field (fc/with-field :payment-id (fc/field-name ))] + (com/radio-card {:options (for [payment payments] + {:value (:db/id payment) + :content (str (:payment/invoice-number payment) " - " + (-> payment :payment/vendor :vendor/name) + " - Amount: $" (format "%.2f" (:payment/amount payment)) + " • Date: " (some-> payment :payment/date coerce/to-date-time (atime/unparse-local atime/normal-date)))}) + :name payment-id-field + :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."])])) (defn count-payment-matches [request] @@ -809,8 +794,9 @@ (-> request :multi-form-state :snapshot :action))) (defmethod save-handler - :link-payment [{{:keys [transaction-id payment-id]} :form-params :as request transaction :entity}] - (let [payment (d-checks/get-by-id payment-id)] + :link-payment [{{ {:keys [transaction-id payment-id]} :snapshot} :multi-form-state :as request transaction :entity}] + (let [_ (println "PAYMENT ID IS") + payment (d-checks/get-by-id payment-id)] (exception->4xx #(assert-can-see-client (:identity request) (-> transaction :transaction/client :db/id))) (exception->4xx #(assert-can-see-client (:identity request) (-> payment :payment/client :db/id))) @@ -818,7 +804,10 @@ (when (not= (-> transaction :transaction/client :db/id) (-> payment :payment/client :db/id)) - (throw (ex-info "Clients don't match" {:validation-error "Payment and client do not match."}))) + (form-validation-error "Clients don't match." + :payment-client-id (:payment/client payment) + :transaction-client-id (:transaction/client transaction)) + #_(throw (ex-info "Clients don't match" {:validation-error "Payment and client do not match."}))) (when-not (dollars= (- (:transaction/amount transaction)) (:payment/amount payment)) @@ -828,7 +817,7 @@ [{:db/id (:db/id payment) :payment/status :payment-status/cleared :payment/date (coerce/to-date (first (sort [(:payment/date payment) - (:transaction/date transaction)])))} + (coerce/to-date-time (:transaction/date transaction))])))} [:upsert-transaction {:db/id (:db/id transaction) @@ -1025,86 +1014,74 @@ [:div] :headers {"hx-trigger" "modalclose"})))) -(defn unlink-payment [{{:keys [transaction-id] :as fp} :form-params :as request}] - (let [transaction (dc/pull (dc/db conn) - '[:transaction/approval-status +(defn unlink-payment [{{{transaction-id :db/id} :snapshot} :multi-form-state :as request}] + + (fc/start-form (:multi-form-state request) (when (:form-errors request) {:step-params (:form-errors request)}) + (let [transaction (dc/pull (dc/db conn) + '[:transaction/approval-status - :transaction/date - :transaction/location - :transaction/vendor - :transaction/accounts - :transaction/status - :transaction/client [:db/id] - {:transaction/payment [:payment/date - {[:payment/status :xform iol-ion.query/ident] [:db/ident]} :db/id]}] - transaction-id) - payment (-> transaction :transaction/payment)] + :transaction/date + :transaction/location + :transaction/vendor + :transaction/accounts + :transaction/status + :transaction/client [:db/id] + {:transaction/payment [:payment/date + {[:payment/status :xform iol-ion.query/ident] [:db/ident]} :db/id]}] + transaction-id) + payment (-> transaction :transaction/payment)] - (exception->4xx #(assert-can-see-client (:identity request) (-> transaction :transaction/client :db/id))) - (exception->4xx #(assert-not-locked (-> transaction :transaction/client :db/id) (:transaction/date transaction))) + (exception->4xx #(assert-can-see-client (:identity request) (-> transaction :transaction/client :db/id))) + (exception->4xx #(assert-not-locked (-> transaction :transaction/client :db/id) (:transaction/date transaction))) - (when (not= :payment-status/cleared (-> payment :payment/status)) - (throw (ex-info "Payment can't be undone because it isn't cleared." - {:validation-error "Payment can't be undone because it isn't cleared."}))) + (when (not= :payment-status/cleared (-> payment :payment/status)) + (throw (ex-info "Payment can't be undone because it isn't cleared." + {:validation-error "Payment can't be undone because it isn't cleared."}))) - (let [is-autopay-payment? (some->> (dc/q {:find ['?sp] - :in ['$ '?payment] - :where ['[?ip :invoice-payment/payment ?payment] - '[?ip :invoice-payment/invoice ?i] - '[(get-else $ ?i :invoice/scheduled-payment "N/A") ?sp]]} - (dc/db conn) (:db/id payment)) - seq - (map first) - (every? #(instance? java.util.Date %)))] - (if is-autopay-payment? - (audit-transact - (-> [{:db/id (:db/id payment) - :payment/status :payment-status/pending} - [:upsert-transaction - {:db/id (:db/id transaction) - :transaction/approval-status :transaction-approval-status/unapproved - :transaction/payment nil - :transaction/vendor nil - :transaction/location nil - :transaction/accounts nil}] - [:db/retractEntity (:db/id payment)]] - (into (map (fn [[invoice-payment]] - [:db/retractEntity invoice-payment]) - (dc/q {:find ['?ip] - :in ['$ '?p] - :where ['[?ip :invoice-payment/payment ?p]]} - (dc/db conn) - (:db/id payment))))) - (:identity request)) - (audit-transact - [{:db/id (:db/id payment) - :payment/status :payment-status/pending} - [:upsert-transaction - {:db/id (:db/id transaction) - :transaction/approval-status :transaction-approval-status/unapproved - :transaction/payment nil - :transaction/vendor nil - :transaction/location nil - :transaction/accounts nil}]] - (:identity request)))) + (let [is-autopay-payment? (some->> (dc/q {:find ['?sp] + :in ['$ '?payment] + :where ['[?ip :invoice-payment/payment ?payment] + '[?ip :invoice-payment/invoice ?i] + '[(get-else $ ?i :invoice/scheduled-payment "N/A") ?sp]]} + (dc/db conn) (:db/id payment)) + seq + (map first) + (every? #(instance? java.util.Date %)))] + (if is-autopay-payment? + (audit-transact + (-> [{:db/id (:db/id payment) + :payment/status :payment-status/pending} + [:upsert-transaction + {:db/id (:db/id transaction) + :transaction/approval-status :transaction-approval-status/unapproved + :transaction/payment nil + :transaction/vendor nil + :transaction/location nil + :transaction/accounts nil}] + [:db/retractEntity (:db/id payment)]] + (into (map (fn [[invoice-payment]] + [:db/retractEntity invoice-payment]) + (dc/q {:find ['?ip] + :in ['$ '?p] + :where ['[?ip :invoice-payment/payment ?p]]} + (dc/db conn) + (:db/id payment))))) + (:identity request)) + (audit-transact + [{:db/id (:db/id payment) + :payment/status :payment-status/pending} + [:upsert-transaction + {:db/id (:db/id transaction) + :transaction/approval-status :transaction-approval-status/unapproved + :transaction/payment nil + :transaction/vendor nil + :transaction/location nil + :transaction/accounts nil}]] + (:identity request)))) - (solr/touch-with-ledger (:db/id transaction)) - (html-response (payment-matches-view request) - :headers {"hx-trigger" "unlinked"}) - - #_(modal-response - (com/success-modal {:title "Transaction unlinked successfully"} - - [:p.text-gray-600.mt-2 "The transaction has been unlinked from its payment."] - [:p "If you'd like to also void the payment, click " - (com/link - {:hx-boost true - :href (hu/url (bidi/path-for ssr-routes/only-routes ::payment-route/all-page) - {:exact-match-id (:db/id payment)})} - "here") - " to view the payment"]) - - :headers {"hx-trigger" "invalidated"}))) + (solr/touch-with-ledger (:db/id transaction)) + (html-response (fc/with-field :step-params (payment-matches-view request)) + :headers {"hx-trigger" "unlinked"})))) #_(def save-schema (mc/schema @@ -1143,6 +1120,7 @@ (form-schema [_] edit-form-schema) (submit [this {:keys [multi-form-state request-method identity] :as request}] + (println "SUBMITTING") (save-handler request) )) @@ -1209,9 +1187,11 @@ [:client-id {:optional true} [:maybe entity-id]]])) - #_#_::route/unlink-payment (-> unlink-payment - (wrap-entity [:form-params :transaction-id] d-transactions/default-read) - (wrap-schema-enforce :form-schema + ::route/unlink-payment (-> unlink-payment + (wrap-entity [:multi-form-state :snapshot :db/id] d-transactions/default-read) + (mm/wrap-wizard edit-wizard) + (mm/wrap-decode-multi-form-state) + #_(wrap-schema-enforce :form-schema save-schema))} (fn [h] (-> h diff --git a/src/clj/auto_ap/ssr/ui.clj b/src/clj/auto_ap/ssr/ui.clj index f8a43ea9..524c57d9 100644 --- a/src/clj/auto_ap/ssr/ui.clj +++ b/src/clj/auto_ap/ssr/ui.clj @@ -29,13 +29,10 @@ [:link {:rel "stylesheet", :href "/output.css"}] [:script {:src "https://cdn.plaid.com/link/v2/stable/link-initialize.js"}] [:script { :src "https://cdn.jsdelivr.net/npm/@ryangjchandler/alpine-tooltip@1.x.x/dist/cdn.min.js" :defer true}] - [:link {:rel "stylesheet" :href "https://unpkg.com/tippy.js@6/dist/tippy.css"}] - [:link {:rel "stylesheet" :href "https://unpkg.com/tippy.js@6/themes/light.css"}] - (if (= "dev" (:dd-env env)) - [:script {:src "https://unpkg.com/htmx.org@1.9.6/dist/htmx.js" - :crossorigin= "anonymous"}] - [:script {:src "https://unpkg.com/htmx.org@1.9.6/dist/htmx.min.js" - :crossorigin= "anonymous"}]) + [:link {:rel "stylesheet" :href "/css/tippy/tippy.css"}] + [:link {:rel "stylesheet" :href "/css/tippy/light.css"}] + [:script {:src "/js/htmx.min.js" + :crossorigin= "anonymous"}] [:script {:src "https://cdn.jsdelivr.net/npm/sortablejs@1.15.2/Sortable.min.js"}] [:script {:src "/js/htmx-disable.js"}] diff --git a/src/clj/auto_ap/ssr/utils.clj b/src/clj/auto_ap/ssr/utils.clj index 1627415d..68ad44f9 100644 --- a/src/clj/auto_ap/ssr/utils.clj +++ b/src/clj/auto_ap/ssr/utils.clj @@ -196,6 +196,7 @@ :form-errors (assoc-in {} path [m])})))) (defn form-validation-error [m & {:as data}] + (alog/warn ::form-validaiton-error :data data) (throw+ (ex-info m (merge data {:type :form-validation :form-validation-errors [m]})))) diff --git a/tasks b/tasks index d7675e00..36472f73 100644 --- a/tasks +++ b/tasks @@ -8,4 +8,8 @@ * also add tests * Make sure that "Shared" really shares locations * make sure transactions support import-batch-id query parameter -* make locked transactions clearer \ No newline at end of file +* make locked transactions clearer +* unlinking then linking requires first closing the dialog. +* make approved/unapproved be on the actions page +* make memo on the acations page +