progress
This commit is contained in:
@@ -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
9
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
|
||||
Reference in New Issue
Block a user