Proper validations and total row.
This commit is contained in:
@@ -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)])))
|
||||
|
||||
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]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user