This commit is contained in:
2025-03-15 21:55:41 -07:00
parent d1b04f27c8
commit f3ca8afcc9
2 changed files with 107 additions and 94 deletions

View File

@@ -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)
))

9
tasks
View File

@@ -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