Proper validations and total row.
This commit is contained in:
21
src/clj/auto_ap/ssr/invoice/common.clj
Normal file
21
src/clj/auto_ap/ssr/invoice/common.clj
Normal 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]}])
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user