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

@@ -123,10 +123,11 @@
[:div {:class "flex items-center justify-center w-full h-full border border-gray-200 rounded-lg bg-gray-50 dark:bg-gray-800 dark:border-gray-700 bg-opacity-50" }
[:div {:class "px-3 py-1 text-xs font-medium leading-none text-center text-blue-800 bg-blue-200 rounded-full animate-pulse dark:bg-blue-900 dark:text-blue-200"} "loading..."]]])])
(defn new-row- [{:keys [index colspan tr-params] :as params} & content]
(defn new-row- [{:keys [index colspan tr-params row-offset] :as params} & content]
(row-
(merge {:class "new-row"
:x-data (hx/json {:newRowIndex index})
:x-data (hx/json {:newRowIndex index
:offset (or row-offset 0)})
}
tr-params)
(cell- {:colspan colspan
@@ -135,10 +136,10 @@
(a-button- (merge
(dissoc params :index :colspan)
{
"@click" "$dispatch('newRow', {index: newRowIndex++})"
"@click" "$dispatch('newRow', {index: (newRowIndex++)})"
:color :secondary
:hx-trigger "newRow"
:hx-vals (hiccup/raw "js:{index: event.detail.index}")
:hx-vals (hiccup/raw "js:{index: event.detail.index }")
:hx-target "closest .new-row"
:hx-swap "beforebegin"})
content)])))

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]

View File

@@ -3,7 +3,7 @@
[auto-ap.datomic
:refer [add-sorter-fields apply-pagination apply-sort-3
audit-transact conn merge-query observable-query
pull-attr pull-many]]
pull-many]]
[auto-ap.datomic.accounts :as d-accounts]
[auto-ap.datomic.bank-accounts :as d-bank-accounts]
[auto-ap.datomic.invoices :as d-invoices]
@@ -23,7 +23,6 @@
:refer [wrap-admin wrap-client-redirect-unauthenticated]]
[auto-ap.solr :as solr]
[auto-ap.ssr-routes :as ssr-routes]
[auto-ap.ssr.common-handlers :refer [add-new-entity-handler]]
[auto-ap.ssr.components :as com]
[auto-ap.ssr.components.link-dropdown :refer [link-dropdown]]
[auto-ap.ssr.components.multi-modal :as mm]
@@ -31,6 +30,7 @@
[auto-ap.ssr.grid-page-helper :as helper :refer [wrap-apply-sort]]
[auto-ap.ssr.hiccup-helper :as hh]
[auto-ap.ssr.hx :as hx]
[auto-ap.ssr.invoice.common :refer [default-read]]
[auto-ap.ssr.invoice.new-invoice-wizard :as new-invoice-wizard]
[auto-ap.ssr.pos.common :refer [date-range-field*]]
[auto-ap.ssr.svg :as svg]
@@ -120,25 +120,7 @@
(exact-match-id* request)]])
(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]}])
(defn fetch-ids [db {:keys [query-params route-params] :as request}]
(let [valid-clients (extract-client-ids (:clients request)

View File

@@ -9,7 +9,8 @@
"/scheduled-payment-date" ::scheduled-payment-date
"/navigate" ::new-wizard-navigate
"/account/new" ::new-wizard-new-account
"/account/location-select" ::location-select}
"/account/location-select" ::location-select
"/total" ::expense-account-total}
"/pay-button" ::pay-button
"/pay" {:get ::pay-wizard
"/navigate" ::pay-wizard-navigate