Proper validations and total row.

This commit is contained in:
2024-03-25 20:26:04 -07:00
parent 3683d58232
commit ea7ad57da2
5 changed files with 122 additions and 65 deletions

View File

@@ -0,0 +1,21 @@
(ns auto-ap.ssr.invoice.common)
(def default-read '[:db/id
:invoice/invoice-number
:invoice/total
:invoice/outstanding-balance
:invoice/source-url
[:invoice/date :xform clj-time.coerce/from-date]
{:invoice/client [:client/code :db/id :client/name]
:invoice/expense-accounts [* {:invoice-expense-account/account [:account/name :db/id
:account/location
{:account/client-overrides [:account-client-override/name
{:account-client-override/client [:db/id]}]}]}]
[:transaction/_invoices :as :invoice/transaction] [:db/id]
[:payment/_invoices :as :invoice/payments] [:db/id :payment/date :payment/amount
{[:transaction/_payment :as :payment/transaction] [:db/id]
[:payment/status :xform iol-ion.query/ident] [:db/ident]}]
[:invoice/status :xform iol-ion.query/ident] [:db/ident]
:invoice/vendor [:vendor/name :db/id]}])

View File

@@ -1,8 +1,9 @@
(ns auto-ap.ssr.invoice.new-invoice-wizard
(:require [auto-ap.datomic
:refer [audit-transact conn pull-attr pull-ref]]
:refer [audit-transact conn pull-attr]]
[auto-ap.datomic.accounts :as d-accounts]
[auto-ap.datomic.invoices :as d-invoices]
[auto-ap.ssr.invoice.common :refer [default-read]]
[auto-ap.graphql.utils :refer [assert-can-see-client
assert-not-locked exception->4xx]]
[auto-ap.logging :as alog]
@@ -29,8 +30,7 @@
[datomic.api :as dc]
[iol-ion.query :refer [dollars=]]
[malli.core :as mc]
[malli.util :as mut]
[manifold.deferred :as d]))
[malli.util :as mut]))
(defn get-vendor [vendor-id]
(dc/pull
@@ -88,7 +88,7 @@
[:fn {:error/message "Not an allowed account."}
check-allowance]]]
[:invoice-expense-account/location :string]
[:invoice-expense-account/amount money]]
[:invoice-expense-account/amount [:double {:min 0}]]]
[:fn {:error/fn (fn [r x] (:type r))
:error/path [:invoice-expense-account/location]} check-invoice-expense-account-location]]]]])
@@ -122,6 +122,7 @@
vendor))
;; TODO account / vendor search should use allowances
(defrecord BasicDetailsStep [linear-wizard]
@@ -330,11 +331,22 @@
(com/validated-field
{:errors (fc/field-errors)}
(com/money-input {:name (fc/field-name)
:class "w-16"
:class "w-16 amount-field"
:value (fc/field-value)}))))
(com/data-grid-cell {:class "align-top"}
(com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x))))
(defn invoice-expense-account-total* [request]
(format "$%,.2f" (->> (-> request
:multi-form-state
:step-params
:invoice/expense-accounts)
(map (fnil :invoice-expense-account/amount 0.0))
(filter number?)
(reduce + 0.0))))
(defn invoice-expense-account-total [request]
(html-response (invoice-expense-account-total* request)))
(defrecord AccountsStep [linear-wizard]
mm/ModalWizardStep
@@ -349,7 +361,7 @@
(step-schema [_]
(mut/select-keys (mm/form-schema linear-wizard) #{:invoice/expense-accounts}))
(render-step [this {{:keys [snapshot]} :multi-form-state :as request}]
(render-step [this {{:keys [snapshot] :as multi-form-state} :multi-form-state :as request}]
(alog/peek ::mfs (:step-params (:multi-form-state request)))
(mm/default-render-step
linear-wizard this
@@ -363,27 +375,50 @@
{:errors (fc/field-errors)}
(com/data-grid {:headers [(com/data-grid-header {} "Account")
(com/data-grid-header {:class "w-32"} "Location")
(com/data-grid-header {:class "w-16"} "%")
(com/data-grid-header {:class "w-16"} "$")
(com/data-grid-header {:class "w-16"})]}
(fc/cursor-map #(invoice-expense-account-row* {:value %
:client-id (:invoice/client snapshot)}))
(com/data-grid-new-row {:colspan 4
:hx-get (bidi/path-for ssr-routes/only-routes
::route/new-wizard-new-account)
:row-offset 0
:index (count (fc/field-value))
:tr-params {:hx-vals (hx/json {:client-id (:invoice/client snapshot)})}}
"New account"))))])
"New account")
(com/data-grid-row {}
(com/data-grid-cell {})
(com/data-grid-cell {:class "text-right"} [:span.font-bold.text-right "TOTAL"])
(com/data-grid-cell {:id "total"
:class "text-right"
:hx-trigger "change from:closest form target:.amount-field"
:hx-put (bidi.bidi/path-for ssr-routes/only-routes ::route/expense-account-total)
:hx-target "this"
:hx-swap "innerHTML"}
(invoice-expense-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 "INVOICE TOTAL"])
(com/data-grid-cell {:class "text-right"}
(format "$%,.2f" (:invoice/total snapshot)))
(com/data-grid-cell {})))))])
:footer
(mm/default-step-footer linear-wizard this :validation-route ::route/new-wizard-navigate)
:validation-route ::route/new-wizard-navigate))
mm/Initializable
(init-step-params
[_ current request]
{:invoice/expense-accounts [{:db/id "123"
:invoice-expense-account/location "Shared"
:invoice-expense-account/account (:db/id (:vendor/default-account (clientize-vendor (get-vendor (->db-id (:invoice/vendor (:snapshot current))))
(->db-id (:invoice/client (:snapshot current))))))
:invoice-expense-account/amount 100}]}))
(alog/peek ::step (:step-params current))
(if (not (seq (:invoice/expense-accounts (:step-params current))))
(assoc (:step-params current) :invoice/expense-accounts [{:db/id "123"
:invoice-expense-account/location "Shared"
:invoice-expense-account/account (:db/id (:vendor/default-account (clientize-vendor (get-vendor (->db-id (:invoice/vendor (:snapshot current))))
(->db-id (:invoice/client (:snapshot current))))))
:invoice-expense-account/amount (:invoice/total (:step-params current))}])
(:step-params current))))
(defn assert-no-conflicting [{:invoice/keys [invoice-number client vendor]}]
@@ -431,36 +466,50 @@
(form-schema [_] new-form-schema)
(submit [this {:keys [multi-form-state request-method identity] :as request}]
(let [invoice (:snapshot multi-form-state)
client-id (->db-id (:invoice/client invoice))
vendor-id (->db-id (:invoice/vendor invoice))
transaction [:upsert-invoice (-> multi-form-state
:snapshot
(assoc :db/id "invoice")
(assoc
:invoice/outstanding-balance (:invoice/total (:snapshot multi-form-state))
:invoice/import-status :import-status/imported
:invoice/status :invoice-status/unpaid)
(let [invoice (:snapshot multi-form-state)
client-id (->db-id (:invoice/client invoice))
vendor-id (->db-id (:invoice/vendor invoice))
transaction [:upsert-invoice (-> multi-form-state
:snapshot
(assoc :db/id "invoice")
(assoc
:invoice/outstanding-balance (:invoice/total (:snapshot multi-form-state))
:invoice/import-status :import-status/imported
:invoice/status :invoice-status/unpaid)
(update :invoice/expense-accounts
(fn [eas]
(mapv (fn [ea]
(-> ea
(assoc :invoice-expense-account/amount
(:invoice/total (:snapshot multi-form-state)))))
eas)))
(update :invoice/date coerce/to-date)
(update :invoice/due coerce/to-date))]]
(update :invoice/expense-accounts
(fn [eas]
(mapv (fn [ea]
(-> ea
(assoc :invoice-expense-account/amount
(:invoice/total (:snapshot multi-form-state)))))
eas)))
(update :invoice/date coerce/to-date)
(update :invoice/due coerce/to-date))]]
(assert-invoice-amounts-add-up invoice)
(assert-no-conflicting invoice)
(exception->4xx #(assert-can-see-client (:identity request) client-id))
(assert-invoice-amounts-add-up invoice)
(assert-no-conflicting invoice)
(exception->4xx #(assert-can-see-client (:identity request) client-id))
(exception->4xx #(assert-not-locked client-id (:invoice/date invoice)))
(let [transaction-result (audit-transact [transaction] (:identity request))]
(solr/touch-with-ledger (get-in transaction-result [:tempids "invoice"]))))
(html-response [:div]
:headers {"hx-trigger" "modalclose,invalidated"})))
(exception->4xx #(assert-not-locked client-id (:invoice/date invoice)))
(let [transaction-result (audit-transact [transaction] (:identity request))]
(solr/touch-with-ledger (get-in transaction-result [:tempids "invoice"]))
(html-response
(@(resolve 'auto-ap.ssr.invoices/row*)
identity
(dc/pull (dc/db conn) default-read (get-in transaction-result [:tempids "invoice"]))
{:flash? true
:request request})
:headers {"hx-trigger" "modalclose"
"hx-retarget" "#entity-table tbody"
"hx-reswap" "afterbegin"
#_(format "#entity-table tr[data-id=\"%d\"]" (get-in transaction-result [:tempids "invoice"]))})))
#_(html-response [:div]
:headers {"hx-trigger" "modalclose,invalidated"})))
(def new-wizard (->NewWizard2 nil nil))
@@ -548,6 +597,9 @@
(mm/wrap-wizard new-wizard)
(mm/wrap-decode-multi-form-state)
(wrap-nested-form-params))
::route/expense-account-total (-> invoice-expense-account-total
(mm/wrap-wizard new-wizard)
(mm/wrap-decode-multi-form-state))
::route/location-select (-> location-select
(wrap-schema-enforce :query-schema [:map
[:name :string]