diff --git a/src/clj/auto_ap/ssr/transaction/edit.clj b/src/clj/auto_ap/ssr/transaction/edit.clj index df5bf40c..2e46ebd4 100644 --- a/src/clj/auto_ap/ssr/transaction/edit.clj +++ b/src/clj/auto_ap/ssr/transaction/edit.clj @@ -382,39 +382,7 @@ [:h3.text-lg.font-semibold.mb-4 "Editable Fields"]] ;; Vendor field - (fc/with-field :transaction/vendor - (com/validated-field - {:label "Vendor" - :errors (fc/field-errors)} - [:div.w-96 - (com/typeahead {:name (fc/field-name) - :error? (fc/error?) - :class "w-96" - :placeholder "Search..." - :url (bidi/path-for ssr-routes/only-routes :vendor-search) - :value (fc/field-value) - :content-fn (fn [c] (pull-attr (dc/db conn) :vendor/name c)) })])) - - ;; Memo field - (fc/with-field :transaction/memo - (com/validated-field - {:label "Memo" - :errors (fc/field-errors)} - [:div.w-96 - (com/text-input {:value (-> (fc/field-value)) - :name (fc/field-name) - :error? (fc/field-errors) - :placeholder "Optional note"})])) - - ;; Approval status field - (fc/with-field :transaction/approval-status - (com/validated-field - {:label "Status" - :errors (fc/field-errors)} - (com/radio-card {:options (mapv (fn [[k v]] {:value (name k) :content v}) - transaction-approval-status) - :value (name (or (fc/field-value) :transaction-approval-status/unapproved)) - :name (fc/field-name)})))) + ) :footer (mm/default-step-footer linear-wizard this :validation-route ::route/edit-wizard-navigate) :validation-route ::route/edit-wizard-navigate))) @@ -604,7 +572,7 @@ (-> tx :transaction/payment :db/id))] [:div#payment-matches - (when (and payment (:db/id payment)) + (if (and payment (:db/id payment)) [:div.my-4.p-4.bg-blue-50.rounded [:h3.text-lg.font-bold.mb-2 "Linked Payment" (com/a-icon-button {:href (hu/url (bidi/path-for ssr-routes/only-routes ::payment-route/all-page) @@ -626,6 +594,8 @@ [:div.flex.justify-between [:div.font-medium "Date"] [:div (some-> payment :payment/date (atime/unparse-local atime/normal-date))]] + (fc/with-field :payment-id (com/hidden {:name (fc/field-name) + :value (:db/id payment)})) [:div.mt-4 {:hx-post (bidi/path-for ssr-routes/only-routes ::route/unlink-payment) :hx-trigger "unlinkPayment" :hx-target "#payment-matches" @@ -634,24 +604,24 @@ :hx-confirm "Are you sure you want to unlink this payment?"} (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.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."])])) + "@click" "$dispatch('unlinkPayment')"} "Unlink Payment")]]] + (if (seq payments) + [:div + [:h3.text-lg.font-bold.mb-4 "Available Payments"] + [: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] (count (get-available-payments request))) @@ -686,17 +656,25 @@ :body (mm/default-step-body {} [:div - + (fc/with-field :transaction/memo + (com/validated-field + {:label "Memo" + :errors (fc/field-errors)} + [:div.w-96 + (com/text-input {:value (-> (fc/field-value)) + :name (fc/field-name) + :error? (fc/field-errors) + :placeholder "Optional note"})])) [:div {:x-data (hx/json {:activeForm (if (:transaction/payment (:entity request)) "link-payment" - (fc/with-field :action (fc/field-value))) + (fc/with-field :action (fc/field-value))) :canChange (boolean (not (:transaction/payment (:entity request))))}) "@unlinked" "canChange=true"} [:div {:class "flex space-x-2 mb-4"} -(fc/with-field :action - (com/hidden {:name (fc/field-name) - :value (fc/field-value) - ":value" "activeForm"})) + (fc/with-field :action + (com/hidden {:name (fc/field-name) + :value (fc/field-value) + ":value" "activeForm"})) (com/button-group {:name "method"} (com/button-group-button {"@click" "activeForm = 'link-payment'" :value "payment" ":class" "{ '!bg-primary-200 text-primary-800': activeForm === 'link-payment'}" :class "relative"} (let [count (count-payment-matches request)] @@ -725,7 +703,7 @@ ":disabled" "!canChange"} "Manual"))] [:div {:x-show "activeForm === 'link-payment'", :x-transition:enter "transition ease-out duration-500", :x-transition:enter-start "opacity-0 transform scale-95", :x-transition:enter-end "opacity-100 transform scale-100"} - + (payment-matches-view request)] [:div {:x-show "activeForm === 'link-unpaid-invoices'", :x-transition:enter "transition ease-out duration-500", :x-transition:enter-start "opacity-0 transform scale-95", :x-transition:enter-end "opacity-100 transform scale-100"} (unpaid-invoices-view request)] @@ -735,7 +713,31 @@ (transaction-rules-view request)] [:div {:x-show "activeForm === 'manual'", :x-transition:enter "transition ease-out duration-500", :x-transition:enter-start "opacity-0 transform scale-95", :x-transition:enter-end "opacity-100 transform scale-100"} [:div {} - + (fc/with-field :transaction/vendor + (com/validated-field + {:label "Vendor" + :errors (fc/field-errors)} + [:div.w-96 + (com/typeahead {:name (fc/field-name) + :error? (fc/error?) + :class "w-96" + :placeholder "Search..." + :url (bidi/path-for ssr-routes/only-routes :vendor-search) + :value (fc/field-value) + :content-fn (fn [c] (pull-attr (dc/db conn) :vendor/name c))})])) + + ;; Memo field + + + ;; Approval status field + (fc/with-field :transaction/approval-status + (com/validated-field + {:label "Status" + :errors (fc/field-errors)} + (com/radio-card {:options (mapv (fn [[k v]] {:value (name k) :content v}) + transaction-approval-status) + :value (name (or (fc/field-value) :transaction-approval-status/unapproved)) + :name (fc/field-name)}))) (fc/with-field :transaction/accounts (com/validated-field {:errors (fc/field-errors)} @@ -746,7 +748,7 @@ (println "WE ARE NOW HERE" (fc/field-value)) (fc/cursor-map #(transaction-account-row* {:value % :client-id (-> request :entity :transaction/client :db/id)})) - + (com/data-grid-new-row {:colspan 4 :hx-get (bidi/path-for ssr-routes/only-routes ::route/edit-wizard-new-account) @@ -765,7 +767,7 @@ :hx-swap "innerHTML"} (account-total* request)) (com/data-grid-cell {})) - + (com/data-grid-row {} (com/data-grid-cell {}) (com/data-grid-cell {:class "text-right"} [:span.font-bold.text-right "BALANCE"]) @@ -777,9 +779,9 @@ :hx-swap "innerHTML"} (account-balance* request)) (com/data-grid-cell {})) - + (com/data-grid-row {} - + (com/data-grid-cell {}) (com/data-grid-cell {:class "text-right"} [:span.font-bold.text-right "TRANSACTION TOTAL"]) (com/data-grid-cell {:class "text-right"} @@ -793,15 +795,46 @@ (defmulti save-handler (fn [request] (-> request :multi-form-state :snapshot :action))) +(defn- default-update-tx [snapshot transaction] + (merge {:transaction/memo (:transaction/memo snapshot) } + transaction)) + +(defn- save-linked-transaction [{{ snapshot :snapshot} :multi-form-state :as request transaction :entity} payment] + (exception->4xx #(assert-not-locked (-> transaction :transaction/client :db/id) (:transaction/date transaction))) + (audit-transact (into + [{:db/id (:db/id payment) + :payment/status :payment-status/cleared + :payment/date (coerce/to-date (first (sort [(:payment/date payment) + (coerce/to-date-time (:transaction/date transaction))])))} + [:upsert-transaction + (default-update-tx + snapshot + {:db/id (:db/id transaction) + :transaction/payment (:db/id payment) + :transaction/vendor (-> payment :payment/vendor :db/id) + :transaction/approval-status :transaction-approval-status/approved + :transaction/accounts [{:db/id (random-tempid) + :transaction-account/account (:db/id (d-accounts/get-account-by-numeric-code-and-sets 21000 ["default"])) + :transaction-account/location "A" + :transaction-account/amount (Math/abs (:transaction/amount transaction))}]})]]) + (:identity request))) + +(defn- save-memo-only [{{ snapshot :snapshot} :multi-form-state :as request}] + (audit-transact [[:upsert-transaction (default-update-tx snapshot {})]] + (:identity request))) + +(defn- is-already-linked-to-this-payment? [transaction payment-id] + (= (pull-attr (dc/db conn) + :transaction/payment + (:db/id transaction)) + payment-id)) + (defmethod save-handler - :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)] + :link-payment [{{ {:keys [transaction-id payment-id] :as snapshot} :snapshot} :multi-form-state :as request transaction :entity}] + (let [ 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))) - (exception->4xx #(assert-not-locked (-> transaction :transaction/client :db/id) (:transaction/date transaction))) - (when (not= (-> transaction :transaction/client :db/id) (-> payment :payment/client :db/id)) (form-validation-error "Clients don't match." @@ -812,23 +845,9 @@ (when-not (dollars= (- (:transaction/amount transaction)) (:payment/amount payment)) (throw (ex-info "Amounts don't match" {:validation-error "Amounts don't match"}))) - - (audit-transact (into - [{:db/id (:db/id payment) - :payment/status :payment-status/cleared - :payment/date (coerce/to-date (first (sort [(:payment/date payment) - (coerce/to-date-time (:transaction/date transaction))])))} - - [:upsert-transaction - {:db/id (:db/id transaction) - :transaction/payment (:db/id payment) - :transaction/vendor (-> payment :payment/vendor :db/id) - :transaction/approval-status :transaction-approval-status/approved - :transaction/accounts [{:db/id (random-tempid) - :transaction-account/account (:db/id (d-accounts/get-account-by-numeric-code-and-sets 21000 ["default"])) - :transaction-account/location "A" - :transaction-account/amount (Math/abs (:transaction/amount transaction))}]}]]) - (:identity request)) + (if (is-already-linked-to-this-payment? transaction payment-id) + (save-memo-only request) + (save-linked-transaction request payment)) (solr/touch-with-ledger (:db/id transaction)) (modal-response @@ -1120,7 +1139,6 @@ (form-schema [_] edit-form-schema) (submit [this {:keys [multi-form-state request-method identity] :as request}] - (println "SUBMITTING") (save-handler request) )) diff --git a/tasks b/tasks index 36472f73..09dfefcb 100644 --- a/tasks +++ b/tasks @@ -1,15 +1,10 @@ -* Convert transaction form to one where you pick options and hit a single endpoint to persist. * Add tests for edit transaction. * Make it so you can create a new vendor again. -* Switch login screen * Hide unhelpful report from the dashboard * 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 -* unlinking then linking requires first closing the dialog. -* make approved/unapproved be on the actions page -* make memo on the acations page - +* make locked transactions clearer on the transaction table +* Make locked transactions not look butt ugly with errors \ No newline at end of file