handwriting checks.
This commit is contained in:
@@ -37,11 +37,10 @@
|
||||
:bank-account/plaid-account [:plaid-account/name :db/id :plaid-account/number :plaid-account/balance]
|
||||
:bank-account/intuit-bank-account [:intuit-bank-account/name :intuit-bank-account/external-id :db/id]
|
||||
:bank-account/integration-status [:integration-status/message
|
||||
:db/id
|
||||
:integration-status/last-attempt
|
||||
:integration-status/last-updated
|
||||
{:integration-status/state [:db/ident]}]}
|
||||
]}
|
||||
:db/id
|
||||
:integration-status/last-attempt
|
||||
:integration-status/last-updated
|
||||
{:integration-status/state [:db/ident]}]}]}
|
||||
{:yodlee-provider-account/_client [*]}
|
||||
{:plaid-item/_client [*]}
|
||||
{:client/emails [:db/id :email-contact/email :email-contact/description]}])
|
||||
@@ -61,7 +60,7 @@
|
||||
(fn [bas]
|
||||
(map (fn [i ba]
|
||||
(-> ba
|
||||
(update :bank-account/type :db/ident )
|
||||
(update :bank-account/type :db/ident)
|
||||
(update-in [:bank-account/integration-status :integration-status/state] :db/ident)
|
||||
(update-in [:bank-account/integration-status :integration-status/last-attempt] #(some-> % coerce/to-date-time))
|
||||
(update-in [:bank-account/integration-status :integration-status/last-updated] #(some-> % coerce/to-date-time))
|
||||
@@ -71,9 +70,9 @@
|
||||
(defn get-all []
|
||||
(->> (dc/q '[:find (pull ?e r)
|
||||
:in $ r
|
||||
:where [?e :client/name]]
|
||||
(dc/db conn)
|
||||
full-read)
|
||||
:where [?e :client/name]]
|
||||
(dc/db conn)
|
||||
full-read)
|
||||
(map first)
|
||||
(map cleanse)))
|
||||
|
||||
@@ -98,23 +97,23 @@
|
||||
(map cleanse)))
|
||||
|
||||
(defn get-by-id [id]
|
||||
(->>
|
||||
(dc/pull (dc/db conn )
|
||||
full-read
|
||||
id)
|
||||
(->>
|
||||
(dc/pull (dc/db conn)
|
||||
full-read
|
||||
id)
|
||||
(cleanse)))
|
||||
|
||||
(defn code->id [code]
|
||||
(->>
|
||||
(->>
|
||||
(dc/q '[:find ?e
|
||||
:in $ ?code
|
||||
:where [?e :client/code ?code]]
|
||||
:in $ ?code
|
||||
:where [?e :client/code ?code]]
|
||||
(dc/db conn) code)
|
||||
(first)
|
||||
(first)))
|
||||
|
||||
(defn best-match [identifier]
|
||||
(when (and identifier (not-empty identifier))
|
||||
(when (and identifier (not-empty identifier))
|
||||
(some-> (solr/query solr/impl "clients"
|
||||
{"query" (format "_text_:\"%s\"" (str/upper-case (solr/escape identifier)))
|
||||
"fields" "id"})
|
||||
@@ -126,7 +125,7 @@
|
||||
|
||||
|
||||
(defn exact-match [identifier]
|
||||
(when (and identifier (not-empty identifier))
|
||||
(when (and identifier (not-empty identifier))
|
||||
(some-> (solr/query solr/impl "clients"
|
||||
{"query" (format "exact:\"%s\"" (str/upper-case (solr/escape identifier)))
|
||||
"fields" "id"})
|
||||
@@ -149,7 +148,6 @@
|
||||
"code" (:client/code result)
|
||||
"exact" (map str/upper-case matches)})))
|
||||
|
||||
|
||||
(defn raw-graphql-ids [db args]
|
||||
(let [name-like-ids (cond (not (str/blank? (:name-like args)))
|
||||
(set (map (comp #(Long/parseLong %) :id)
|
||||
@@ -172,24 +170,24 @@
|
||||
matching-ids)
|
||||
(set (map :db/id (:clients args))))
|
||||
|
||||
|
||||
|
||||
query (cond-> {:query {:find []
|
||||
:in ['$ ]
|
||||
:in ['$]
|
||||
:where []}
|
||||
:args [db]}
|
||||
valid-ids
|
||||
(merge-query {:query {:in ['[?e ...]]}
|
||||
:args [(set valid-ids)]})
|
||||
|
||||
|
||||
|
||||
(:sort args) (add-sorter-fields {"name" ['[?e :client/name ?sort-name]]}
|
||||
args)
|
||||
|
||||
true
|
||||
(merge-query {:query {:find ['?sort-default '?e] :where ['[?e :client/name ?sort-default]]}}))]
|
||||
(->> (query2 query)
|
||||
(apply-sort-3 (update args :sort conj {:sort-key "default-2" :asc true}))
|
||||
(apply-pagination args))))
|
||||
(apply-sort-3 (update args :sort conj {:sort-key "default-2" :asc true}))
|
||||
(apply-pagination args))))
|
||||
|
||||
(defn graphql-results [ids db args]
|
||||
(let [results (->> (pull-many db full-read
|
||||
@@ -201,6 +199,6 @@
|
||||
(defn get-graphql-page [args]
|
||||
(let [db (dc/db conn)
|
||||
{ids-to-retrieve :ids matching-count :count} (raw-graphql-ids db args)]
|
||||
|
||||
|
||||
[(->> (graphql-results ids-to-retrieve db args))
|
||||
matching-count]))
|
||||
|
||||
@@ -423,7 +423,6 @@
|
||||
nil)}))
|
||||
|
||||
(defn get-payment-page [context args _]
|
||||
(alog/info ::TEST)
|
||||
(let [[payments checks-count] (d-checks/get-graphql (-> args
|
||||
:filters
|
||||
(<-graphql)
|
||||
|
||||
@@ -4,9 +4,14 @@
|
||||
:refer [add-sorter-fields apply-pagination apply-sort-3
|
||||
audit-transact conn merge-query observable-query
|
||||
pull-many]]
|
||||
[auto-ap.graphql.checks :as gq-checks :refer [print-checks-internal]]
|
||||
[auto-ap.datomic.bank-accounts :as d-bank-accounts]
|
||||
[auto-ap.datomic.invoices :as d-invoices]
|
||||
[auto-ap.graphql.checks :as gq-checks :refer [base-payment
|
||||
invoice-payments
|
||||
print-checks-internal
|
||||
validate-belonging]]
|
||||
[auto-ap.graphql.utils :refer [assert-can-see-client
|
||||
exception->4xx
|
||||
assert-not-locked exception->4xx
|
||||
exception->notification
|
||||
extract-client-ids notify-if-locked]]
|
||||
[auto-ap.logging :as alog]
|
||||
@@ -15,6 +20,7 @@
|
||||
[auto-ap.routes.payments :as payment-route]
|
||||
[auto-ap.routes.utils
|
||||
:refer [wrap-admin wrap-client-redirect-unauthenticated]]
|
||||
[auto-ap.solr :as solr]
|
||||
[auto-ap.ssr-routes :as ssr-routes]
|
||||
[auto-ap.ssr.components :as com]
|
||||
[auto-ap.ssr.components.link-dropdown :refer [link-dropdown]]
|
||||
@@ -29,10 +35,13 @@
|
||||
:refer [apply-middleware-to-all-handlers clj-date-schema
|
||||
dissoc-nil-transformer entity-id html-response
|
||||
main-transformer modal-response money ref->enum-schema
|
||||
strip wrap-entity wrap-merge-prior-hx wrap-schema-enforce]]
|
||||
round-money strip wrap-entity wrap-merge-prior-hx
|
||||
wrap-schema-enforce]]
|
||||
[auto-ap.time :as atime]
|
||||
[auto-ap.utils :refer [by dollars=]]
|
||||
[bidi.bidi :as bidi]
|
||||
[clj-time.coerce :as coerce]
|
||||
[clj-time.core :as time]
|
||||
[clojure.string :as str]
|
||||
[datomic.api :as dc]
|
||||
[hiccup.util :as hu]
|
||||
@@ -112,6 +121,7 @@
|
||||
(def default-read '[:db/id
|
||||
:invoice/invoice-number
|
||||
:invoice/total
|
||||
:invoice/outstanding-balance
|
||||
:invoice/source-url
|
||||
|
||||
[:invoice/date :xform clj-time.coerce/from-date]
|
||||
@@ -446,12 +456,15 @@
|
||||
(com/pill {:color :secondary}
|
||||
"+ " (dec (count expense-accounts)) " more")])])}
|
||||
|
||||
{:key "total"
|
||||
:name "Total"
|
||||
:sort-key "total"
|
||||
{:key "outstanding"
|
||||
:name "Outstanding"
|
||||
:sort-key "outstanding-balance"
|
||||
:class "text-right"
|
||||
:render (fn [{:invoice/keys [total]}]
|
||||
(some->> total (format "$%,.2f")))}
|
||||
:render (fn [{:invoice/keys [outstanding-balance total]}]
|
||||
[:div
|
||||
(some->> outstanding-balance (format "$%,.2f"))
|
||||
(when-not (dollars= outstanding-balance total)
|
||||
[:div.text-xs.text-gray-400 (format "of $%,.2f" total)])])}
|
||||
{:key "links"
|
||||
:name "Links"
|
||||
:show-starting "lg"
|
||||
@@ -594,40 +607,61 @@
|
||||
updated-count
|
||||
(count ids))})})))
|
||||
|
||||
;; TODO
|
||||
;; voiding invoice - should you allow if there are ANY payments? i think no
|
||||
;; Do filtering of invoices on the dialog. Show a little banner if the total count doesn't match
|
||||
;; Allow for paying balances from set of invoices for one vendor
|
||||
;; Allow for credits, just highlight it
|
||||
;; Entering 0.00 payment should just remove it from the set
|
||||
;; Better text to link to pdfs
|
||||
;; include hint about margins from the dialog
|
||||
;; refresh the window after printing
|
||||
;; Handwritten date
|
||||
;; filter only for unlocked
|
||||
;; filter only for unpaid
|
||||
|
||||
(defn does-amount-exceed-outstanding? [amount outstanding-balance]
|
||||
(or (and (> outstanding-balance 0)
|
||||
(> amount outstanding-balance))
|
||||
(and (> outstanding-balance 0)
|
||||
(<= amount 0))
|
||||
(and (< outstanding-balance 0)
|
||||
(< amount outstanding-balance))
|
||||
(and (< outstanding-balance 0)
|
||||
(>= amount 0))))
|
||||
(let [outstanding-balance (round-money outstanding-balance)
|
||||
amount (round-money amount)]
|
||||
(or (and (> outstanding-balance 0)
|
||||
(> amount outstanding-balance))
|
||||
(and (> outstanding-balance 0)
|
||||
(<= amount 0))
|
||||
(and (< outstanding-balance 0)
|
||||
(< amount outstanding-balance))
|
||||
(and (< outstanding-balance 0)
|
||||
(>= amount 0)))))
|
||||
|
||||
|
||||
(def payment-form-schema
|
||||
(mc/schema [:map
|
||||
[:client entity-id]
|
||||
[:invoices [:and
|
||||
[:vector {:coerce? true}
|
||||
[:map
|
||||
[:invoice-id entity-id]
|
||||
[:amount money]]]
|
||||
[:fn {:error/message "All payments must not exceed their outstanding balance."}
|
||||
(fn [invoices]
|
||||
(let [outstanding-balances (->> (dc/q '[:find ?i ?ob
|
||||
:in $ [?i ...]
|
||||
:where [?i :invoice/outstanding-balance ?ob]]
|
||||
(dc/db conn)
|
||||
(map :invoice-id invoices))
|
||||
(into {}))]
|
||||
(every? #(not (does-amount-exceed-outstanding? (:amount %) (outstanding-balances (:invoice-id %))))
|
||||
invoices)))]]]
|
||||
[:bank-account entity-id]
|
||||
[:mode [:enum :simple :advanced]]
|
||||
[:method [:enum :debit :print-check :cash :handwrite-check]]]))
|
||||
(mc/schema
|
||||
[:map
|
||||
[:client entity-id]
|
||||
[:invoices [:and
|
||||
[:vector {:coerce? true}
|
||||
[:map
|
||||
[:invoice-id entity-id]
|
||||
[:amount money]]]
|
||||
[:fn {:error/message "All payments must not exceed their outstanding balance."}
|
||||
(fn [invoices]
|
||||
(let [outstanding-balances (->> (dc/q '[:find ?i ?ob
|
||||
:in $ [?i ...]
|
||||
:where [?i :invoice/outstanding-balance ?ob]]
|
||||
(dc/db conn)
|
||||
(map :invoice-id invoices))
|
||||
(into {}))]
|
||||
(every? (fn [%]
|
||||
(not (does-amount-exceed-outstanding? (:amount %) (outstanding-balances (:invoice-id %)))))
|
||||
invoices)))]]]
|
||||
[:has-warning? :boolean]
|
||||
[:bank-account entity-id]
|
||||
[:check-number {:optional true}
|
||||
:int]
|
||||
[:mode [:enum :simple :advanced]]
|
||||
[:method [:enum :debit :print-check :cash :handwrite-check]]]))
|
||||
|
||||
(defn bank-account-card-base [{:keys [bg-color text-color icon bank-account]}]
|
||||
[:div {:class "w-[30em] cursor-move"}
|
||||
(defn bank-account-card-base [{:keys [bg-color text-color icon bank-account can-handwrite?]}]
|
||||
[:div {:class "w-[30em]"}
|
||||
(com/card {:class "w-full"}
|
||||
[:div.flex.items-stretch {:x-data (hx/json {:chosen false
|
||||
:popper nil})
|
||||
@@ -685,7 +719,8 @@
|
||||
{:from (mm/encode-step-key :choose-method)
|
||||
:to (mm/encode-step-key :payment-details)})}
|
||||
"Debit"))
|
||||
(when (= :bank-account-type/check (:bank-account/type bank-account))
|
||||
(when (and (= :bank-account-type/check (:bank-account/type bank-account))
|
||||
can-handwrite?)
|
||||
(com/button {:minimal-loading? true
|
||||
:hx-vals (hx/json {"step-params[bank-account]" (:db/id bank-account)
|
||||
"step-params[method]" "handwrite-check"})
|
||||
@@ -695,29 +730,39 @@
|
||||
"Handwrite check"))]]])])
|
||||
|
||||
|
||||
(defmulti bank-account-card (comp :bank-account/type))
|
||||
(defmethod bank-account-card :bank-account-type/cash [bank-account]
|
||||
(defmulti bank-account-card (fn [ba _]
|
||||
(:bank-account/type ba)))
|
||||
(defmethod bank-account-card :bank-account-type/cash [bank-account can-handwrite?]
|
||||
(bank-account-card-base {:bg-color "bg-green-50"
|
||||
:text-color "text-green-600"
|
||||
:icon svg/dollar
|
||||
:bank-account bank-account}))
|
||||
:bank-account bank-account
|
||||
:can-handwrite? can-handwrite?}))
|
||||
|
||||
(defmethod bank-account-card
|
||||
:bank-account-type/credit
|
||||
[bank-account]
|
||||
[bank-account can-handwrite?]
|
||||
(bank-account-card-base {:bg-color "bg-purple-50"
|
||||
:text-color "text-purple-600"
|
||||
:icon svg/credit-card
|
||||
:bank-account bank-account}))
|
||||
:bank-account bank-account
|
||||
:can-handwrite? can-handwrite?}))
|
||||
|
||||
(defmethod bank-account-card
|
||||
:bank-account-type/check [bank-account]
|
||||
:bank-account-type/check [bank-account can-handwrite?]
|
||||
(bank-account-card-base {:bg-color "bg-blue-50"
|
||||
:text-color "text-blue-600"
|
||||
:icon svg/check
|
||||
:bank-account bank-account}))
|
||||
:bank-account bank-account
|
||||
:can-handwrite? can-handwrite?}))
|
||||
|
||||
|
||||
(defn can-handwrite? [invoices]
|
||||
(let [selected-vendors (set (map (comp :db/id :invoice/vendor) invoices))]
|
||||
(and
|
||||
(= 1 (count selected-vendors))
|
||||
(> (reduce + 0 (map :invoice/outstanding-balance invoices)) 0.0))))
|
||||
|
||||
|
||||
(defrecord ChoosePaymentMethodModal [linear-wizard]
|
||||
mm/ModalWizardStep
|
||||
@@ -732,29 +777,24 @@
|
||||
(step-schema [_]
|
||||
(mut/select-keys (mm/form-schema linear-wizard) #{:bank-account :method}))
|
||||
|
||||
(render-step [this request]
|
||||
(mm/default-render-step
|
||||
linear-wizard this
|
||||
:head [:div.p-2 "Pay " (count (:invoices (:snapshot (:multi-form-state request)))) " invoices"]
|
||||
:body (mm/default-step-body
|
||||
{}
|
||||
(let [bank-accounts (->> (dc/q '[:find (pull ?ba [:bank-account/name :bank-account/sort-order :bank-account/visible
|
||||
:bank-account/bank-name
|
||||
:db/id
|
||||
{[:bank-account/type :xform iol-ion.query/ident] [:db/ident]}])
|
||||
:in $ ?c
|
||||
:where [?c :client/bank-accounts ?ba]]
|
||||
(dc/db conn)
|
||||
(:client (:snapshot (:multi-form-state request)))) ;; TODO
|
||||
(map first)
|
||||
(sort-by :bank-account/sort-order))]
|
||||
(render-step
|
||||
[this request]
|
||||
(let [invoices (:invoices (:snapshot (:multi-form-state request)))
|
||||
can-handwrite? (can-handwrite? (map (comp (:invoice-by-id linear-wizard) :invoice-id) invoices))]
|
||||
(mm/default-render-step
|
||||
linear-wizard this
|
||||
:head [:div.p-2.inline-flex.gap-2.items-center "Pay " (count invoices) " invoices"
|
||||
(when (:has-warning? (:snapshot (:multi-form-state request)))
|
||||
(com/pill {:color :yellow}
|
||||
"Some of the selected invoices may be locked or paid."))]
|
||||
:body (mm/default-step-body
|
||||
{}
|
||||
[:div.flex.flex-col.space-y-2
|
||||
(for [ba bank-accounts]
|
||||
(bank-account-card ba))]))
|
||||
:footer
|
||||
nil
|
||||
#_(mm/default-step-footer linear-wizard this :validation-route ::route/navigate)
|
||||
:validation-route ::route/pay-wizard-navigate)))
|
||||
(for [ba (:bank-accounts linear-wizard)]
|
||||
(bank-account-card ba can-handwrite?))])
|
||||
:footer
|
||||
nil
|
||||
:validation-route ::route/pay-wizard-navigate))))
|
||||
|
||||
(defrecord PaymentDetailsStep [linear-wizard]
|
||||
mm/ModalWizardStep
|
||||
@@ -767,7 +807,7 @@
|
||||
[])
|
||||
|
||||
(step-schema [_]
|
||||
(mut/select-keys (mm/form-schema linear-wizard) #{:invoices}))
|
||||
(mut/select-keys (mm/form-schema linear-wizard) #{:invoices :check-number}))
|
||||
|
||||
(render-step [this request]
|
||||
(mm/default-render-step
|
||||
@@ -776,10 +816,21 @@
|
||||
:body (mm/default-step-body
|
||||
{}
|
||||
[:div {}
|
||||
(if (= :handwrite-check (:method (:snapshot (:multi-form-state request))))
|
||||
(fc/with-field :check-number
|
||||
(com/validated-field
|
||||
{:errors (fc/field-errors)
|
||||
:label "Check number"}
|
||||
(com/int-input {:value (fc/field-value)
|
||||
:name (fc/field-name)
|
||||
:error? (fc/field-errors)
|
||||
:placeholder "10001"}))))
|
||||
(com/radio-list {:x-model "mode"
|
||||
:name "step-params[mode]"
|
||||
:options [{:value "simple"
|
||||
:content "Pay in full"}
|
||||
:content (format "Pay in full ($%,.2f)" (reduce + 0.0
|
||||
(map (comp :invoice/outstanding-balance (:invoice-by-id linear-wizard) :invoice-id)
|
||||
(:invoices (:snapshot (:multi-form-state request))))))}
|
||||
{:value "advanced"
|
||||
:content "Customize payments"}]})
|
||||
[:div.space-y-4 (hx/alpine-appear {:x-show "mode==\"advanced\""})
|
||||
@@ -820,22 +871,39 @@
|
||||
:validation-route ::route/pay-wizard-navigate)))
|
||||
|
||||
|
||||
(defn add-handwritten-check [request wizard snapshot]
|
||||
(let [snapshot (assoc snapshot :date (time/now))
|
||||
invoices (d-invoices/get-multi (map :invoice-id (:invoices snapshot))) ;; TODO shouldn't need datomic
|
||||
bank-account-id (:bank-account snapshot)
|
||||
bank-account (d-bank-accounts/get-by-id bank-account-id)
|
||||
_ (doseq [invoice invoices]
|
||||
(assert-can-see-client (:identity request) (:invoice/client invoice)))
|
||||
client-id (:db/id (:invoice/client (first invoices)))
|
||||
_ (validate-belonging (:db/id (:client/_bank-accounts bank-account)) invoices bank-account)
|
||||
_ (assert-not-locked client-id (:date snapshot))
|
||||
invoice-payment-lookup (by :invoice-id :amount (:invoices snapshot))
|
||||
base-payment (base-payment invoices
|
||||
(:invoice/vendor (first invoices))
|
||||
(:invoice/client (first invoices))
|
||||
bank-account
|
||||
:payment-type/check
|
||||
0
|
||||
invoice-payment-lookup)]
|
||||
(let [result (audit-transact
|
||||
(into [(assoc base-payment
|
||||
:payment/type :payment-type/check
|
||||
:payment/status :payment-status/pending
|
||||
:payment/check-number (:check-number snapshot)
|
||||
:payment/date (coerce/to-date (:date snapshot)))]
|
||||
(invoice-payments invoices invoice-payment-lookup))
|
||||
(:identity request))]
|
||||
(doseq [[_ i] (:tempids result)]
|
||||
(solr/touch-with-ledger i)))))
|
||||
|
||||
(defrecord PayWizard [form-params current-step entity]
|
||||
(defrecord PayWizard [form-params current-step invoice-by-id]
|
||||
mm/LinearModalWizard
|
||||
(hydrate-from-request
|
||||
[this request]
|
||||
this
|
||||
#_(assoc this :entity (:entity request)))
|
||||
(navigate [this step-key]
|
||||
(assoc this :current-step step-key))
|
||||
(get-current-step [this]
|
||||
(if current-step
|
||||
(mm/get-step this current-step)
|
||||
(mm/get-step this :choose-method)))
|
||||
(render-wizard [this {:keys [multi-form-state] :as request}]
|
||||
|
||||
;; TODO should this be customized based off the selections they make?
|
||||
(let [invoices (->> (dc/q '[:find (pull ?i [{:invoice/vendor [:vendor/name :db/id]
|
||||
:invoice/client [:db/id]}
|
||||
:invoice/outstanding-balance
|
||||
@@ -843,14 +911,37 @@
|
||||
:db/id])
|
||||
:in $ [?i ...]]
|
||||
(dc/db conn)
|
||||
(map :invoice-id (get-in request [:multi-form-state :step-params :invoices])))
|
||||
(map :invoice-id (get-in request [:multi-form-state :snapshot :invoices])))
|
||||
(map first)
|
||||
(sort-by (juxt (comp :invoice/vendor :vendor/name)
|
||||
:invoice/invoice-number)))
|
||||
request (update-in request [:multi-form-state :step-params :invoices]
|
||||
:invoice/invoice-number)))]
|
||||
(assoc this :invoice-by-id (by :db/id invoices)
|
||||
:bank-accounts (->> (dc/q '[:find (pull ?ba [:bank-account/name :bank-account/sort-order :bank-account/visible
|
||||
:bank-account/bank-name
|
||||
:db/id
|
||||
{[:bank-account/type :xform iol-ion.query/ident] [:db/ident]}])
|
||||
:in $ ?c
|
||||
:where [?c :client/bank-accounts ?ba]]
|
||||
(dc/db conn)
|
||||
(:client (:snapshot (:multi-form-state request))))
|
||||
(map first)
|
||||
(sort-by :bank-account/sort-order)))))
|
||||
(navigate [this step-key]
|
||||
(assoc this :current-step step-key))
|
||||
(get-current-step [this]
|
||||
(if current-step
|
||||
(mm/get-step this current-step)
|
||||
(mm/get-step this :choose-method)))
|
||||
(render-wizard [this {:keys [multi-form-state] :as request}]
|
||||
(let [request (update-in request [:multi-form-state :step-params :invoices]
|
||||
(fn [form-invoices]
|
||||
(mapv (fn [form-invoice i]
|
||||
(assoc form-invoice :invoice i)) form-invoices invoices)))]
|
||||
(->> form-invoices
|
||||
(map (fn [form-invoice]
|
||||
(assoc form-invoice :invoice ((:invoice-by-id this) (:invoice-id form-invoice)))))
|
||||
(sort-by
|
||||
(juxt (comp :vendor/name :invoice/vendor :invoice)
|
||||
(comp :invoice/invoice-number :invoice)))
|
||||
(into []))))]
|
||||
(mm/default-render-wizard
|
||||
this request
|
||||
:form-params
|
||||
@@ -858,9 +949,9 @@
|
||||
(assoc :hx-post
|
||||
(str (bidi/path-for ssr-routes/only-routes ::route/pay-submit)))
|
||||
(assoc :x-data (hx/json {:mode (some-> multi-form-state
|
||||
:step-params
|
||||
:mode
|
||||
name)}))))))
|
||||
:step-params
|
||||
:mode
|
||||
name)}))))))
|
||||
|
||||
(steps [_]
|
||||
[:choose-method
|
||||
@@ -877,31 +968,37 @@
|
||||
(get {:bank-account (->ChoosePaymentMethodModal this)}
|
||||
(first step-key)))))
|
||||
(form-schema [_] payment-form-schema)
|
||||
(submit [_ {:keys [multi-form-state request-method identity] :as request}]
|
||||
(submit [this {:keys [multi-form-state request-method identity] :as request}]
|
||||
(let [snapshot (mc/decode
|
||||
payment-form-schema
|
||||
(:snapshot multi-form-state)
|
||||
mt/strip-extra-keys-transformer)
|
||||
_ (exception->4xx
|
||||
#(if (= :handwrite-check (:method snapshot))
|
||||
(when (or (not (some? (:check-number snapshot)))
|
||||
(= "" (:check-number snapshot)))
|
||||
(throw (Exception. "Check number is required")))
|
||||
true))
|
||||
result (exception->4xx
|
||||
#(print-checks-internal (map (fn [i] {:invoice-id (:invoice-id i)
|
||||
:amount (:amount i)})
|
||||
(:invoices snapshot))
|
||||
(:client snapshot)
|
||||
(:bank-account snapshot)
|
||||
(cond (= :print-check (:method snapshot))
|
||||
:payment-type/check
|
||||
(= :debit (:method snapshot))
|
||||
:payment-type/debit
|
||||
(= :cash (:method snapshot))
|
||||
:payment-type/cash
|
||||
:else :payment-type/debit)
|
||||
identity))]
|
||||
(alog/info ::printed :result result)
|
||||
#(if (= :handwrite-check (:method snapshot)) ;; TODO validation for single vendor again?
|
||||
(add-handwritten-check request this snapshot)
|
||||
(print-checks-internal (map (fn [i] {:invoice-id (:invoice-id i)
|
||||
:amount (:amount i)})
|
||||
(:invoices snapshot))
|
||||
(:client snapshot)
|
||||
(:bank-account snapshot)
|
||||
(cond (= :print-check (:method snapshot))
|
||||
:payment-type/check
|
||||
(= :debit (:method snapshot))
|
||||
:payment-type/debit
|
||||
(= :cash (:method snapshot))
|
||||
:payment-type/cash
|
||||
:else :payment-type/debit) ;; TODO might not be right
|
||||
identity)))]
|
||||
(modal-response
|
||||
(com/modal {}
|
||||
(com/modal-card-advanced
|
||||
{:class "transition duration-300 ease-in-out htmx-swapping:-translate-x-2/3 htmx-swapping:opacity-0 htmx-swapping:scale-0 htmx-added:translate-x-2/3 htmx-added:opacity-0 htmx-added:scale-0 scale-100 translate-x-0 opacity-100"}
|
||||
|
||||
(com/modal-body {}
|
||||
[:div.flex.flex-col.mt-4.space-y-4.items-center
|
||||
[:div.w-24.h-24.bg-green-50.rounded-full.p-4.text-green-300.animate-gg
|
||||
@@ -931,26 +1028,45 @@
|
||||
|
||||
(mm/wrap-wizard pay-wizard)
|
||||
(mm/wrap-init-multi-form-state (fn [request]
|
||||
(exception->notification
|
||||
;; TODO handle locked
|
||||
(let [selected-ids (selected->ids request (:query-params request))
|
||||
invoices (->> (dc/q '[:find (pull ?i [{:invoice/vendor [:vendor/name :db/id]
|
||||
:invoice/client [:db/id]}
|
||||
:invoice/outstanding-balance
|
||||
:invoice/invoice-number
|
||||
:db/id])
|
||||
:in $ [?i ...]]
|
||||
(dc/db conn)
|
||||
selected-ids)
|
||||
(map first)
|
||||
(sort-by (juxt (comp :invoice/vendor :vendor/name)
|
||||
:invoice/invoice-number)))]
|
||||
(mm/->MultiStepFormState {:invoices (mapv (fn [i] {:invoice-id (:db/id i)
|
||||
:amount (:invoice/outstanding-balance i)})
|
||||
invoices)
|
||||
:mode :simple
|
||||
:client (-> invoices first :invoice/client :db/id)}
|
||||
[]
|
||||
{:mode :simple})))))
|
||||
#(let [selected-ids (selected->ids request (:query-params request))
|
||||
selected-ids (->> (dc/q '[:find ?i
|
||||
:in $ [?i ...]
|
||||
:where [?i :invoice/status :invoice-status/unpaid]
|
||||
[?i :invoice/client ?c]
|
||||
[(get-else $ ?c :client/locked-until #inst "2000-01-01") ?lu]
|
||||
[?i :invoice/date ?d]
|
||||
[(>= ?d ?lu)]]
|
||||
(dc/db conn)
|
||||
selected-ids)
|
||||
(map first))
|
||||
_ (when (= 0 (count selected-ids))
|
||||
(throw (ex-info "No selected invoices are applicable for payment" {:type :notification}) ))
|
||||
|
||||
has-warning? (and (:selected (:query-params request))
|
||||
(not= (count selected-ids)
|
||||
(count (:selected (:query-params request)))))
|
||||
invoices (->> (dc/q '[:find (pull ?i [{:invoice/vendor [:vendor/name :db/id]
|
||||
:invoice/client [:db/id]}
|
||||
:invoice/outstanding-balance
|
||||
:invoice/invoice-number
|
||||
:db/id])
|
||||
:in $ [?i ...]]
|
||||
(dc/db conn)
|
||||
selected-ids)
|
||||
(map first)
|
||||
(sort-by (juxt (comp :invoice/vendor :vendor/name)
|
||||
:invoice/invoice-number)))]
|
||||
(mm/->MultiStepFormState {:invoices (mapv (fn [i] {:invoice-id (:db/id i)
|
||||
:amount (:invoice/outstanding-balance i)})
|
||||
invoices)
|
||||
:mode :simple
|
||||
:client (-> invoices first :invoice/client :db/id)
|
||||
:has-warning? (boolean has-warning?)}
|
||||
[]
|
||||
{:mode :simple
|
||||
:has-warning? (boolean has-warning?)}))))))
|
||||
::route/pay-submit (-> mm/submit-handler
|
||||
|
||||
(mm/wrap-wizard pay-wizard)
|
||||
|
||||
@@ -557,4 +557,9 @@
|
||||
{:x 87}
|
||||
main-transformer))
|
||||
|
||||
(defn round-money [d]
|
||||
(with-precision 2
|
||||
(double (.setScale (bigdec d) 2 java.math.RoundingMode/HALF_UP))))
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user