Switching to radio.
This commit is contained in:
@@ -2,11 +2,12 @@
|
|||||||
(:require [auto-ap.ssr.hiccup-helper :as hh]
|
(:require [auto-ap.ssr.hiccup-helper :as hh]
|
||||||
[auto-ap.ssr.hx :as hx]))
|
[auto-ap.ssr.hx :as hx]))
|
||||||
|
|
||||||
(defn radio-card- [{:keys [options name title size orientation] :or {size :medium} selected-value :value}]
|
(defn radio-card- [{:keys [options name title size orientation width] :or {size :medium width "w-48"} selected-value :value}]
|
||||||
[:h3 {:class "mb-4 font-semibold text-gray-900 dark:text-white"} title]
|
[:h3 {:class "mb-4 font-semibold text-gray-900 dark:text-white"} title]
|
||||||
[:ul {:class (cond-> "w-48 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-lg dark:bg-gray-700 dark:border-gray-600 dark:text-white"
|
[:ul {:class (cond-> " text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-lg dark:bg-gray-700 dark:border-gray-600 dark:text-white"
|
||||||
(= orientation :horizontal) (-> (hh/add-class "flex gap-2 flex-wrap")
|
(= orientation :horizontal) (-> (hh/add-class "flex gap-2 flex-wrap")
|
||||||
(hh/remove-wildcard ["w-" "rounded-lg" "border" "bg-"])))}
|
(hh/remove-wildcard ["w-" "rounded-lg" "border" "bg-"]))
|
||||||
|
true (str " " width " "))}
|
||||||
(for [{:keys [value content]} options]
|
(for [{:keys [value content]} options]
|
||||||
[:li {:class (cond-> "w-full border-b border-gray-200 rounded-t-lg dark:border-gray-600"
|
[:li {:class (cond-> "w-full border-b border-gray-200 rounded-t-lg dark:border-gray-600"
|
||||||
(= orientation :horizontal) (-> (hh/remove-wildcard ["w-full" "rounded-"])
|
(= orientation :horizontal) (-> (hh/remove-wildcard ["w-full" "rounded-"])
|
||||||
|
|||||||
@@ -79,8 +79,8 @@
|
|||||||
:error/path [:transaction-account/location]}
|
:error/path [:transaction-account/location]}
|
||||||
(fn [iea]
|
(fn [iea]
|
||||||
(check-location-belongs (:transaction-account/location iea)
|
(check-location-belongs (:transaction-account/location iea)
|
||||||
(:transaction-account/account iea)))]]]]
|
(:transaction-account/account iea)))]]]]])
|
||||||
])
|
|
||||||
|
|
||||||
(defn clientize-vendor [{:vendor/keys [terms-overrides automatically-paid-when-due default-account account-overrides] :as vendor} client-id]
|
(defn clientize-vendor [{:vendor/keys [terms-overrides automatically-paid-when-due default-account account-overrides] :as vendor} client-id]
|
||||||
(if (nil? vendor)
|
(if (nil? vendor)
|
||||||
@@ -262,7 +262,7 @@
|
|||||||
{}
|
{}
|
||||||
[:div {:x-data (hx/json {:clientId (or (fc/field-value (:transaction/client fc/*current*))
|
[:div {:x-data (hx/json {:clientId (or (fc/field-value (:transaction/client fc/*current*))
|
||||||
(:db/id (:client request)))
|
(:db/id (:client request)))
|
||||||
:vendorId (fc/field-value (:transaction/vendor fc/*current*))})}
|
:vendorId (fc/field-value (:transaction/vendor fc/*current*))})}
|
||||||
|
|
||||||
;; Read-only transaction details
|
;; Read-only transaction details
|
||||||
[:div.mb-6.border.rounded-lg.p-4.bg-gray-50
|
[:div.mb-6.border.rounded-lg.p-4.bg-gray-50
|
||||||
@@ -270,147 +270,147 @@
|
|||||||
[:div.grid.grid-cols-2.gap-4
|
[:div.grid.grid-cols-2.gap-4
|
||||||
[:div
|
[:div
|
||||||
[:div.text-sm.font-medium.text-gray-500 "Amount"]
|
[:div.text-sm.font-medium.text-gray-500 "Amount"]
|
||||||
[:div.text-base (format "$%,.2f" (Math/abs (:transaction/amount tx)))]
|
[:div.text-base (format "$%,.2f" (Math/abs (:transaction/amount tx)))]]
|
||||||
]
|
|
||||||
[:div
|
[:div
|
||||||
[:div.text-sm.font-medium.text-gray-500 "Date"]
|
[:div.text-sm.font-medium.text-gray-500 "Date"]
|
||||||
[:div.text-base (some-> tx :transaction/date coerce/to-date-time (atime/unparse-local atime/normal-date))]
|
[:div.text-base (some-> tx :transaction/date coerce/to-date-time (atime/unparse-local atime/normal-date))]]
|
||||||
]
|
|
||||||
[:div
|
[:div
|
||||||
[:div.text-sm.font-medium.text-gray-500 "Bank Account"]
|
[:div.text-sm.font-medium.text-gray-500 "Bank Account"]
|
||||||
[:div.text-base (or (-> tx :transaction/bank-account :bank-account/name) "-")]
|
[:div.text-base (or (-> tx :transaction/bank-account :bank-account/name) "-")]]
|
||||||
]
|
|
||||||
[:div
|
[:div
|
||||||
[:div.text-sm.font-medium.text-gray-500 "Post Date"]
|
[:div.text-sm.font-medium.text-gray-500 "Post Date"]
|
||||||
[:div.text-base (some-> tx :transaction/post-date coerce/to-date-time (atime/unparse-local atime/normal-date))]
|
[:div.text-base (some-> tx :transaction/post-date coerce/to-date-time (atime/unparse-local atime/normal-date))]]
|
||||||
]
|
|
||||||
[:div
|
[:div
|
||||||
[:div.text-sm.font-medium.text-gray-500 "Original Description"]
|
[:div.text-sm.font-medium.text-gray-500 "Original Description"]
|
||||||
[:div.text-base (or (:transaction/description-original tx) "-")]
|
[:div.text-base (or (:transaction/description-original tx) "-")]]
|
||||||
]
|
|
||||||
[:div
|
[:div
|
||||||
[:div.text-sm.font-medium.text-gray-500 "Simplified Description"]
|
[:div.text-sm.font-medium.text-gray-500 "Simplified Description"]
|
||||||
[:div.text-base (or (:transaction/description-simple tx) "-")]
|
[:div.text-base (or (:transaction/description-simple tx) "-")]]
|
||||||
]
|
|
||||||
[:div
|
[:div
|
||||||
[:div.text-sm.font-medium.text-gray-500 "Check Number"]
|
[:div.text-sm.font-medium.text-gray-500 "Check Number"]
|
||||||
[:div.text-base (or (:transaction/check-number tx) "-")]
|
[:div.text-base (or (:transaction/check-number tx) "-")]]
|
||||||
]
|
|
||||||
[:div
|
[:div
|
||||||
[:div.text-sm.font-medium.text-gray-500 "Status"]
|
[:div.text-sm.font-medium.text-gray-500 "Status"]
|
||||||
[:div.text-base (or (some-> tx :transaction/status) "-")]
|
[:div.text-base (or (some-> tx :transaction/status) "-")]]
|
||||||
]
|
|
||||||
[:div
|
[:div
|
||||||
[:div.text-sm.font-medium.text-gray-500 "Transaction Type"]
|
[:div.text-sm.font-medium.text-gray-500 "Transaction Type"]
|
||||||
[:div.text-base (or (some-> tx :transaction/type) "-")]
|
[:div.text-base (or (some-> tx :transaction/type) "-")]]]]
|
||||||
]
|
|
||||||
]]
|
|
||||||
|
|
||||||
;; Transaction Links Section
|
;; Transaction Links Section
|
||||||
#_[:div.mb-6.border.rounded-lg.p-4.bg-gray-50
|
#_[:div.mb-6.border.rounded-lg.p-4.bg-gray-50
|
||||||
[:h3.text-lg.font-semibold.mb-2 "Transaction Links"]
|
[:h3.text-lg.font-semibold.mb-2 "Transaction Links"]
|
||||||
(let [tx-id (mm/get-mfs-field multi-form-state :db/id)
|
(let [tx-id (mm/get-mfs-field multi-form-state :db/id)
|
||||||
db-history (dc/history (dc/db conn))
|
db-history (dc/history (dc/db conn))
|
||||||
|
|
||||||
;; Get current and historical payments linked to this transaction
|
;; Get current and historical payments linked to this transaction
|
||||||
current-payment (when-let [payment-id (-> (dc/pull (dc/db conn)
|
current-payment (when-let [payment-id (-> (dc/pull (dc/db conn)
|
||||||
'[:transaction/payment]
|
'[:transaction/payment]
|
||||||
tx-id)
|
tx-id)
|
||||||
:transaction/payment
|
:transaction/payment
|
||||||
:db/id)]
|
:db/id)]
|
||||||
{:entity-id payment-id :active true})
|
{:entity-id payment-id :active true})
|
||||||
historical-payments (when tx-id
|
historical-payments (when tx-id
|
||||||
(->> (dc/q '[:find ?payment ?inst ?added
|
(->> (dc/q '[:find ?payment ?inst ?added
|
||||||
:in $ ?e
|
:in $ ?e
|
||||||
:where
|
:where
|
||||||
[?e :transaction/payment ?payment ?tx ?added]
|
[?e :transaction/payment ?payment ?tx ?added]
|
||||||
[?tx :db/txInstant ?inst]]
|
[?tx :db/txInstant ?inst]]
|
||||||
db-history tx-id)
|
db-history tx-id)
|
||||||
(map (fn [[id date op]] {:entity-id id
|
(map (fn [[id date op]] {:entity-id id
|
||||||
:date date
|
:date date
|
||||||
:op op}))))
|
:op op}))))
|
||||||
|
|
||||||
all-payments historical-payments ]
|
all-payments historical-payments]
|
||||||
|
|
||||||
[:div
|
[:div
|
||||||
;; Payments section
|
;; Payments section
|
||||||
[:div.mb-3
|
[:div.mb-3
|
||||||
[:h4.font-medium.text-gray-700 "Linked Payments:"]
|
[:h4.font-medium.text-gray-700 "Linked Payments:"]
|
||||||
(if (seq all-payments)
|
(if (seq all-payments)
|
||||||
[:ul.list-disc.pl-6.mt-1
|
[:ul.list-disc.pl-6.mt-1
|
||||||
(for [{:keys [entity-id date op]} all-payments
|
(for [{:keys [entity-id date op]} all-payments
|
||||||
:let [payment (dc/pull (dc/db conn)
|
:let [payment (dc/pull (dc/db conn)
|
||||||
'[:db/id :payment/invoice-number
|
'[:db/id :payment/invoice-number
|
||||||
[ :payment/date :xform clj-time.coerce/from-date]
|
[ :payment/date :xform clj-time.coerce/from-date]
|
||||||
{:payment/vendor [:vendor/name]}]
|
{:payment/vendor [:vendor/name]}]
|
||||||
entity-id)]]
|
entity-id)]]
|
||||||
[:li.text-sm.text-gray-600 {:class (when-not op "line-through")}
|
[:li.text-sm.text-gray-600 {:class (when-not op "line-through")}
|
||||||
(if op [:span.text-green-600 "✓ "] "")
|
(if op [:span.text-green-600 "✓ "] "")
|
||||||
(str "Payment #" (:payment/invoice-number payment) " - "
|
(str "Payment #" (:payment/invoice-number payment) " - "
|
||||||
(-> payment :payment/vendor :vendor/name))])]
|
(-> payment :payment/vendor :vendor/name))])]
|
||||||
[:p.text-sm.text-gray-500.italic "No payments linked to this transaction"])]
|
[:p.text-sm.text-gray-500.italic "No payments linked to this transaction"])]])]]
|
||||||
|
|
||||||
|
;; Invoices section
|
||||||
|
|
||||||
;; Invoices section
|
|
||||||
])]]
|
|
||||||
|
|
||||||
;; Hidden ID field
|
;; Hidden ID field
|
||||||
(fc/with-field :db/id
|
(fc/with-field :db/id
|
||||||
(com/hidden {:name (fc/field-name)
|
(com/hidden {:name (fc/field-name)
|
||||||
:value (fc/field-value)}))
|
:value (fc/field-value)}))
|
||||||
|
|
||||||
;; Hidden client field
|
;; Hidden client field
|
||||||
(fc/with-field :transaction/client
|
(fc/with-field :transaction/client
|
||||||
(com/hidden {:name (fc/field-name)
|
(com/hidden {:name (fc/field-name)
|
||||||
:value (or (mm/get-mfs-field multi-form-state :transaction/client)
|
:value (or (mm/get-mfs-field multi-form-state :transaction/client)
|
||||||
(:db/id (:client request)))}))
|
(:db/id (:client request)))}))
|
||||||
|
|
||||||
;; Editable fields section
|
;; Editable fields section
|
||||||
[:div.mt-6
|
[:div.mt-6
|
||||||
[:h3.text-lg.font-semibold.mb-4 "Editable Fields"]]
|
[:h3.text-lg.font-semibold.mb-4 "Editable Fields"]]
|
||||||
|
|
||||||
;; Vendor field
|
;; Vendor field
|
||||||
(fc/with-field :transaction/vendor
|
(fc/with-field :transaction/vendor
|
||||||
(com/validated-field
|
(com/validated-field
|
||||||
{:label "Vendor"
|
{:label "Vendor"
|
||||||
:errors (fc/field-errors)}
|
:errors (fc/field-errors)}
|
||||||
[:div.w-96
|
[:div.w-96
|
||||||
(com/typeahead {:name (fc/field-name)
|
(com/typeahead {:name (fc/field-name)
|
||||||
:error? (fc/error?)
|
:error? (fc/error?)
|
||||||
:class "w-96"
|
:class "w-96"
|
||||||
:placeholder "Search..."
|
:placeholder "Search..."
|
||||||
:url (bidi/path-for ssr-routes/only-routes :vendor-search)
|
:url (bidi/path-for ssr-routes/only-routes :vendor-search)
|
||||||
:value (fc/field-value)
|
:value (fc/field-value)
|
||||||
:content-fn (fn [c] (pull-attr (dc/db conn) :vendor/name c))
|
:content-fn (fn [c] (pull-attr (dc/db conn) :vendor/name c))
|
||||||
:x-model "vendorId"})]))
|
:x-model "vendorId"})]))
|
||||||
[:div.mb-4
|
[:div.mb-4
|
||||||
[:span.text-sm.text-gray-500 "Can't find the vendor? "
|
[:span.text-sm.text-gray-500 "Can't find the vendor? "
|
||||||
(com/link {:href (bidi.bidi/path-for
|
(com/link {:href (bidi.bidi/path-for
|
||||||
client-routes/routes
|
client-routes/routes
|
||||||
:new-vendor)
|
:new-vendor)
|
||||||
:target "new"}
|
:target "new"}
|
||||||
"Add new vendor")
|
"Add new vendor")
|
||||||
" in a new window, then return here."]]
|
" in a new window, then return here."]]
|
||||||
|
|
||||||
;; Memo field
|
;; Memo field
|
||||||
(fc/with-field :transaction/memo
|
(fc/with-field :transaction/memo
|
||||||
(com/validated-field
|
(com/validated-field
|
||||||
{:label "Memo"
|
{:label "Memo"
|
||||||
:errors (fc/field-errors)}
|
:errors (fc/field-errors)}
|
||||||
[:div.w-96
|
[:div.w-96
|
||||||
(com/text-input {:value (-> (fc/field-value))
|
(com/text-input {:value (-> (fc/field-value))
|
||||||
:name (fc/field-name)
|
:name (fc/field-name)
|
||||||
:error? (fc/field-errors)
|
:error? (fc/field-errors)
|
||||||
:placeholder "Optional note"})]))
|
:placeholder "Optional note"})]))
|
||||||
|
|
||||||
;; Approval status field
|
;; Approval status field
|
||||||
(fc/with-field :transaction/approval-status
|
(fc/with-field :transaction/approval-status
|
||||||
(com/validated-field
|
(com/validated-field
|
||||||
{:label "Status"
|
{:label "Status"
|
||||||
:errors (fc/field-errors)}
|
:errors (fc/field-errors)}
|
||||||
(com/radio-card {:options (mapv (fn [[k v]] {:value (name k) :content v})
|
(com/radio-card {:options (mapv (fn [[k v]] {:value (name k) :content v})
|
||||||
transaction-approval-status)
|
transaction-approval-status)
|
||||||
:value (name (or (fc/field-value) :transaction-approval-status/unapproved))
|
:value (name (or (fc/field-value) :transaction-approval-status/unapproved))
|
||||||
:name (fc/field-name)}))))
|
:name (fc/field-name)}))))
|
||||||
:footer
|
:footer
|
||||||
(mm/default-step-footer linear-wizard this :validation-route ::route/edit-wizard-navigate)
|
(mm/default-step-footer linear-wizard this :validation-route ::route/edit-wizard-navigate)
|
||||||
:validation-route ::route/edit-wizard-navigate)))
|
:validation-route ::route/edit-wizard-navigate)))
|
||||||
@@ -533,10 +533,10 @@
|
|||||||
payments (when client-id
|
payments (when client-id
|
||||||
(dc/q '[:find [(pull ?p [:db/id :payment/invoice-number :payment/amount :payment/date
|
(dc/q '[:find [(pull ?p [:db/id :payment/invoice-number :payment/amount :payment/date
|
||||||
{:payment/vendor [:db/id :vendor/name]}]) ...]
|
{:payment/vendor [:db/id :vendor/name]}]) ...]
|
||||||
:in $ ?client
|
:in $ ?client
|
||||||
:where
|
:where
|
||||||
[?p :payment/client ?client]
|
[?p :payment/client ?client]
|
||||||
[?p :payment/status :payment-status/pending]]
|
[?p :payment/status :payment-status/pending]]
|
||||||
(dc/db conn)
|
(dc/db conn)
|
||||||
client-id))]
|
client-id))]
|
||||||
(filter #(dollars= (Math/abs (:transaction/amount tx)) (:payment/amount %)) payments)))
|
(filter #(dollars= (Math/abs (:transaction/amount tx)) (:payment/amount %)) payments)))
|
||||||
@@ -547,33 +547,30 @@
|
|||||||
(if (seq payments)
|
(if (seq payments)
|
||||||
[:div
|
[:div
|
||||||
[:h3.text-lg.font-bold.mb-4 "Available Payments"]
|
[:h3.text-lg.font-bold.mb-4 "Available Payments"]
|
||||||
[:div.space-y-2
|
[:div {:hx-post (bidi/path-for ssr-routes/only-routes ::route/match-payment)
|
||||||
(for [payment payments]
|
:hx-trigger "matchPayment"
|
||||||
[:div.py-2.border-b.border-gray-200 {:hx-post (bidi/path-for ssr-routes/only-routes
|
:hx-target "#modal-holder"
|
||||||
::route/match-payment)
|
:hx-include "this"
|
||||||
:hx-include "this"
|
:hx-swap "outerHTML"}
|
||||||
:hx-trigger "matchPayment"
|
(com/hidden {:name "action"
|
||||||
:hx-params "transaction-id, match-payment-id"
|
:value "link-payment"
|
||||||
:hx-target "#modal-holder"
|
:form ""})
|
||||||
:hx-swap "outerHTML"}
|
(com/hidden {:name "transaction-id"
|
||||||
(com/hidden {:name "transaction-id"
|
:value (-> request :multi-form-state :snapshot :db/id)
|
||||||
:form ""
|
:form ""})
|
||||||
:value (-> request :multi-form-state :snapshot :db/id)})
|
[:div.space-y-2
|
||||||
|
[:label.block.text-sm.font-medium.mb-1 "Select a payment to match:"]
|
||||||
|
(when payments
|
||||||
(com/hidden {:name "match-payment-id"
|
(com/radio-card {:options (for [payment payments]
|
||||||
:value (:db/id payment)
|
{:value (:db/id payment)
|
||||||
:form ""})
|
:content (str (:payment/invoice-number payment) " - "
|
||||||
[:div.flex.justify-between.items-center
|
(-> payment :payment/vendor :vendor/name)
|
||||||
[:div.space-y-1
|
" - Amount: $" (format "%.2f" (:payment/amount payment))
|
||||||
[:div.font-medium
|
" • Date: " (some-> payment :payment/date coerce/to-date-time (atime/unparse-local atime/normal-date)))})
|
||||||
(str (:payment/invoice-number payment) " - " (-> payment :payment/vendor :vendor/name))]
|
:name "payment-id"
|
||||||
[:div.text-sm.text-gray-600
|
:width "w-full"}))
|
||||||
(str "Amount: $" (format "%.2f" (:payment/amount payment)))
|
(com/a-button {"@click" "$dispatch('matchPayment')"} "Match" #_[:button.mt-4.w-full.py-2.bg-blue-500.text-white.rounded.hover:bg-blue-600 "Match"])]] ]
|
||||||
" • "
|
[:div.text-center.py-4.text-gray-500 "No matching payments available for this transaction."]) ]))
|
||||||
(str "Date: " (some-> payment :payment/date coerce/to-date-time (atime/unparse-local atime/normal-date)))]]
|
|
||||||
(com/a-button {:color :primary :size :small "@click" "$dispatch('matchPayment')"} "Match")]])]]
|
|
||||||
[:div.text-center.py-4.text-gray-500 "No matching payments available for this transaction."])]))
|
|
||||||
|
|
||||||
(defn get-available-autopay-invoices [request]
|
(defn get-available-autopay-invoices [request]
|
||||||
(let [tx-id (or (-> request :multi-form-state :snapshot :db/id)
|
(let [tx-id (or (-> request :multi-form-state :snapshot :db/id)
|
||||||
@@ -596,30 +593,24 @@
|
|||||||
(if (seq invoice-matches)
|
(if (seq invoice-matches)
|
||||||
[:div
|
[:div
|
||||||
[:h3.text-lg.font-bold.mb-4 "Available Autopay Invoices"]
|
[:h3.text-lg.font-bold.mb-4 "Available Autopay Invoices"]
|
||||||
[:div.space-y-4
|
[:form {:hx-post (bidi/path-for ssr-routes/only-routes ::route/match-autopay)
|
||||||
(for [match-group invoice-matches]
|
:hx-trigger "submit"
|
||||||
[:div.border.border-gray-200.rounded.p-4 {:hx-post (bidi/path-for ssr-routes/only-routes
|
|
||||||
::route/match-autopay-invoices)
|
|
||||||
:hx-trigger "matchAutopay"
|
|
||||||
:hx-include "this"
|
|
||||||
:hx-params "transaction-id, autopay-invoice-ids"
|
|
||||||
:hx-target "#modal-holder"
|
:hx-target "#modal-holder"
|
||||||
:hx-swap "outerHTML"}
|
:hx-swap "outerHTML"}
|
||||||
[:div.space-y-3
|
|
||||||
[:div.text-sm.font-medium "Match with these invoices:"]
|
|
||||||
(com/hidden {:name "transaction-id"
|
(com/hidden {:name "transaction-id"
|
||||||
:form ""
|
:value (get-in request [:multi-form-state :snapshot :db/id])
|
||||||
:value (-> request :multi-form-state :snapshot :db/id)})
|
:form ""})
|
||||||
|
[:div.space-y-2
|
||||||
|
[:label.block.text-sm.font-medium.mb-1 "Select an autopay invoice to apply:"]
|
||||||
|
(for [match-group invoice-matches]
|
||||||
(for [invoice match-group]
|
(for [invoice match-group]
|
||||||
[:div.flex.justify-between.items-center.py-2.border-b.border-gray-100
|
[:div.flex.items-center
|
||||||
[:div
|
[:input {:type :radio :value (:db/id invoice) :name "autopay-invoice-id"}]
|
||||||
[:div.font-medium (:invoice/invoice-number invoice)]
|
[:div.ml-3
|
||||||
[:div.text-sm.text-gray-600 (-> invoice :invoice/vendor :vendor/name)]]
|
[:span.block.text-sm.font-medium (:invoice/invoice-number invoice)]
|
||||||
[:div.text-right
|
[:span.block.text-sm.text-gray-500 (-> invoice :invoice/vendor :vendor/name)]
|
||||||
[:div.font-medium (format "$%.2f" (:invoice/total invoice))]
|
[:span.block.text-sm.font-medium (format "$%.2f" (:invoice/total invoice))]]]))]
|
||||||
(com/hidden {:name "autopay-invoice-ids" :value (:db/id invoice) :form ""})]])
|
[:button.mt-4.w-full.py-2.bg-blue-500.text-white.rounded.hover:bg-blue-600 "Match"]]]
|
||||||
[:div.flex.justify-end.mt-4
|
|
||||||
(com/a-button {:color :primary :size :small "@click" "$dispatch('matchAutopay')"} "Match")]]])]]
|
|
||||||
[:div.text-center.py-4.text-gray-500 "No matching autopay invoices available for this transaction."])]))
|
[:div.text-center.py-4.text-gray-500 "No matching autopay invoices available for this transaction."])]))
|
||||||
|
|
||||||
(defn get-available-unpaid-invoices [request]
|
(defn get-available-unpaid-invoices [request]
|
||||||
@@ -643,32 +634,24 @@
|
|||||||
(if (seq invoice-matches)
|
(if (seq invoice-matches)
|
||||||
[:div
|
[:div
|
||||||
[:h3.text-lg.font-bold.mb-4 "Available Unpaid Invoices"]
|
[:h3.text-lg.font-bold.mb-4 "Available Unpaid Invoices"]
|
||||||
[:div.space-y-4
|
[:form {:hx-post (bidi/path-for ssr-routes/only-routes ::route/match-unpaid-invoices)
|
||||||
(for [match-group invoice-matches]
|
:hx-trigger "submit"
|
||||||
[:div.border.border-gray-200.rounded.p-4 {:hx-post (bidi/path-for ssr-routes/only-routes
|
:hx-target "#modal-holder"
|
||||||
::route/match-unpaid-invoices)
|
:hx-swap "outerHTML"}
|
||||||
:hx-trigger "matchUnpaidInvoices"
|
(com/hidden {:name "transaction-id"
|
||||||
:hx-include "this"
|
:value (get-in request [:multi-form-state :snapshot :db/id])
|
||||||
:hx-params "transaction-id, unpaid-invoice-ids"
|
:form ""})
|
||||||
:hx-target "#modal-holder"
|
[:div.space-y-2
|
||||||
:hx-swap "outerHTML"}
|
[:label.block.text-sm.font-medium.mb-1 "Select an unpaid invoice to apply:"]
|
||||||
|
(for [match-group invoice-matches]
|
||||||
[:div.space-y-3
|
(for [invoice match-group]
|
||||||
[:div.text-sm.font-medium "Match with these invoices:"]
|
[:div.flex.items-center
|
||||||
(com/hidden {:name "transaction-id"
|
[:input {:type :radio :value (:db/id invoice) :name "unpaid-invoice-id"}]
|
||||||
:form ""
|
[:div.ml-3
|
||||||
:value (-> request :multi-form-state :snapshot :db/id)})
|
[:span.block.text-sm.font-medium (:invoice/invoice-number invoice)]
|
||||||
|
[:span.block.text-sm.text-gray-500 (-> invoice :invoice/vendor :vendor/name)]
|
||||||
(for [invoice match-group]
|
[:span.block.text-sm.font-medium (format "$%.2f" (:invoice/outstanding-balance invoice))]]]))]
|
||||||
[:div.flex.justify-between.items-center.py-2.border-b.border-gray-100
|
[:button.mt-4.w-full.py-2.bg-blue-500.text-white.rounded.hover:bg-blue-600 "Match"]]]
|
||||||
[:div
|
|
||||||
[:div.font-medium (:invoice/invoice-number invoice)]
|
|
||||||
[:div.text-sm.text-gray-600 (-> invoice :invoice/vendor :vendor/name)]]
|
|
||||||
[:div.text-right
|
|
||||||
[:div.font-medium (format "$%.2f" (:invoice/outstanding-balance invoice))]
|
|
||||||
(com/hidden {:name "unpaid-invoice-ids" :value (:db/id invoice) :form ""})]])
|
|
||||||
[:div.flex.justify-end.mt-4
|
|
||||||
(com/a-button {:color :primary :size :small "@click" "$dispatch('matchUnpaidInvoices')"} "Match")]]])]]
|
|
||||||
[:div.text-center.py-4.text-gray-500 "No matching unpaid invoices available for this transaction."])]))
|
[:div.text-center.py-4.text-gray-500 "No matching unpaid invoices available for this transaction."])]))
|
||||||
|
|
||||||
(defn get-available-rules [request]
|
(defn get-available-rules [request]
|
||||||
@@ -685,18 +668,18 @@
|
|||||||
:transaction-rule/client :transaction-rule/bank-account
|
:transaction-rule/client :transaction-rule/bank-account
|
||||||
:transaction-rule/yodlee-merchant])
|
:transaction-rule/yodlee-merchant])
|
||||||
:where
|
:where
|
||||||
[?r :transaction-rule/description]
|
[?r :transaction-rule/description]]
|
||||||
]
|
|
||||||
(dc/db conn))]
|
(dc/db conn))]
|
||||||
(when tx
|
(when tx
|
||||||
(->> patterns
|
(->> patterns
|
||||||
(map first)
|
(map first)
|
||||||
(filter (fn [rule]
|
(filter (fn [rule]
|
||||||
(rm/rule-applies? (-> tx
|
(rm/rule-applies? (-> tx
|
||||||
(update :transaction/date coerce/to-date)
|
(update :transaction/date coerce/to-date))
|
||||||
) (-> rule
|
(-> rule
|
||||||
(update :transaction-rule/description #(some-> % iol-ion.query/->pattern))))))
|
(update :transaction-rule/description #(some-> % iol-ion.query/->pattern))))))))))
|
||||||
))))
|
|
||||||
|
|
||||||
(defn transaction-rules-view [request]
|
(defn transaction-rules-view [request]
|
||||||
(let [matching-rules (get-available-rules request)]
|
(let [matching-rules (get-available-rules request)]
|
||||||
@@ -704,27 +687,22 @@
|
|||||||
(if (seq matching-rules)
|
(if (seq matching-rules)
|
||||||
[:div
|
[:div
|
||||||
[:h3.text-lg.font-bold.mb-4 "Matching Transaction Rules"]
|
[:h3.text-lg.font-bold.mb-4 "Matching Transaction Rules"]
|
||||||
[:div.space-y-2
|
[:form {:hx-post (bidi/path-for ssr-routes/only-routes ::route/apply-rule)
|
||||||
(for [{:keys [:db/id :transaction-rule/note :transaction-rule/description]} matching-rules]
|
:hx-trigger "submit"
|
||||||
[:div.py-2.border-b.border-gray-200 {:hx-post (bidi/path-for ssr-routes/only-routes
|
|
||||||
::route/apply-rule)
|
|
||||||
:hx-params "transaction-id, rule-id"
|
|
||||||
:hx-include "this"
|
|
||||||
:hx-trigger "applyRule"
|
|
||||||
:hx-target "#modal-holder"
|
:hx-target "#modal-holder"
|
||||||
:hx-swap "outerHTML"}
|
:hx-swap "outerHTML"}
|
||||||
(com/hidden {:name "transaction-id"
|
(com/hidden {:name "transaction-id"
|
||||||
:value (get-in request [:multi-form-state :snapshot :db/id])
|
:value (get-in request [:multi-form-state :snapshot :db/id])
|
||||||
:form ""})
|
:form ""})
|
||||||
(com/hidden {:name "rule-id"
|
[:div.space-y-2
|
||||||
:value id
|
[:label.block.text-sm.font-medium.mb-1 "Select a rule to apply:"]
|
||||||
:form ""})
|
(for [{:keys [:db/id :transaction-rule/note :transaction-rule/description]} matching-rules]
|
||||||
[:div.flex.justify-between.items-center
|
[:div.flex.items-center
|
||||||
[:div.space-y-1
|
[:input {:type :radio :value id :name "rule-id"}]
|
||||||
[:div.font-medium note]
|
[:div.ml-3
|
||||||
[:div.text-sm.text-gray-600
|
[:span.block.text-sm.font-medium note]
|
||||||
(str "Pattern: " description)]]
|
[:span.block.text-sm.text-gray-500 description]]])]
|
||||||
(com/a-button {:color :primary :size :small "@click" "$dispatch('applyRule')"} "Apply")]])]]
|
[:button.mt-4.w-full.py-2.bg-blue-500.text-white.rounded.hover:bg-blue-600 "Apply"]]]
|
||||||
[:div.text-center.py-4.text-gray-500 "No matching rules found for this transaction."])]))
|
[:div.text-center.py-4.text-gray-500 "No matching rules found for this transaction."])]))
|
||||||
|
|
||||||
(defn payment-info-view [request]
|
(defn payment-info-view [request]
|
||||||
@@ -734,11 +712,11 @@
|
|||||||
payment (dc/pull
|
payment (dc/pull
|
||||||
(dc/db conn)
|
(dc/db conn)
|
||||||
'[:payment/amount
|
'[:payment/amount
|
||||||
[:payment/date :xform clj-time.coerce/from-date]
|
[:payment/date :xform clj-time.coerce/from-date]
|
||||||
{ [ :payment/status :xform iol-ion.query/ident] [:db/ident]
|
{ [ :payment/status :xform iol-ion.query/ident] [:db/ident]
|
||||||
|
|
||||||
|
:payment/vendor [:vendor/name]}]
|
||||||
|
|
||||||
:payment/vendor [:vendor/name]}
|
|
||||||
]
|
|
||||||
(-> tx :transaction/payment :db/id))]
|
(-> tx :transaction/payment :db/id))]
|
||||||
(when payment
|
(when payment
|
||||||
[:div.my-4.p-4.bg-blue-50.rounded
|
[:div.my-4.p-4.bg-blue-50.rounded
|
||||||
@@ -752,7 +730,7 @@
|
|||||||
[:div (-> payment :payment/vendor :vendor/name)]]
|
[:div (-> payment :payment/vendor :vendor/name)]]
|
||||||
[:div.flex.justify-between
|
[:div.flex.justify-between
|
||||||
[:div.font-medium "Amount"]
|
[:div.font-medium "Amount"]
|
||||||
[:div (some->> (:payment/amount payment) (format "$%.2f" ))]]
|
[:div (some->> (:payment/amount payment) (format "$%.2f"))]]
|
||||||
[:div.flex.justify-between
|
[:div.flex.justify-between
|
||||||
[:div.font-medium "Status"]
|
[:div.font-medium "Status"]
|
||||||
[:div (some-> payment :payment/status name)]]
|
[:div (some-> payment :payment/status name)]]
|
||||||
@@ -887,11 +865,13 @@
|
|||||||
(com/data-grid-cell {:class "text-right"} [:span.font-bold.text-right "TRANSACTION TOTAL"])
|
(com/data-grid-cell {:class "text-right"} [:span.font-bold.text-right "TRANSACTION TOTAL"])
|
||||||
(com/data-grid-cell {:class "text-right"}
|
(com/data-grid-cell {:class "text-right"}
|
||||||
(format "$%,.2f" (Math/abs (:transaction/amount snapshot))))
|
(format "$%,.2f" (Math/abs (:transaction/amount snapshot))))
|
||||||
(com/data-grid-cell {})))))]]] ])
|
(com/data-grid-cell {})))))]]]])
|
||||||
:footer
|
:footer
|
||||||
(mm/default-step-footer linear-wizard this :validation-route ::route/edit-wizard-navigate
|
(mm/default-step-footer linear-wizard this :validation-route ::route/edit-wizard-navigate
|
||||||
:next-button (com/button {:color :primary :x-ref "next" :class "w-32"} "Done"))
|
:next-button (com/button {:color :primary :x-ref "next" :class "w-32"} "Done"))
|
||||||
:validation-route ::route/edit-wizard-navigate)))
|
:validation-route ::route/edit-wizard-navigate)) )
|
||||||
|
|
||||||
|
|
||||||
(defrecord EditWizard [_ current-step]
|
(defrecord EditWizard [_ current-step]
|
||||||
mm/LinearModalWizard
|
mm/LinearModalWizard
|
||||||
(hydrate-from-request
|
(hydrate-from-request
|
||||||
@@ -1106,19 +1086,19 @@
|
|||||||
|
|
||||||
(solr/touch-with-ledger transaction-id)
|
(solr/touch-with-ledger transaction-id)
|
||||||
|
|
||||||
(modal-response
|
(modal-response
|
||||||
(com/success-modal {:title "Transaction linked successfully"}
|
(com/success-modal {:title "Transaction linked successfully"}
|
||||||
|
|
||||||
[:p.text-gray-600.mt-2 "The transaction has been linked to the autopay invoices."]
|
[:p.text-gray-600.mt-2 "The transaction has been linked to the autopay invoices."]
|
||||||
[:p.text-gray-600.mt-2 "To view the new payment, click "
|
[:p.text-gray-600.mt-2 "To view the new payment, click "
|
||||||
(com/link {:href (hu/url (bidi/path-for ssr-routes/only-routes ::payment-route/all-page)
|
(com/link {:href (hu/url (bidi/path-for ssr-routes/only-routes ::payment-route/all-page)
|
||||||
{:exact-match-id (:db/id (pull-attr (dc/db conn)
|
{:exact-match-id (:db/id (pull-attr (dc/db conn)
|
||||||
:transaction/payment
|
:transaction/payment
|
||||||
(:db/id transaction)))})
|
(:db/id transaction)))})
|
||||||
:hx-boost true}
|
:hx-boost true}
|
||||||
"here")
|
"here")
|
||||||
" to view it."])
|
" to view it."])
|
||||||
:headers {"hx-trigger" "invalidated"})))
|
:headers {"hx-trigger" "invalidated"})))
|
||||||
|
|
||||||
(defn apply-rule [{{:keys [transaction-id rule-id]} :form-params :as request}]
|
(defn apply-rule [{{:keys [transaction-id rule-id]} :form-params :as request}]
|
||||||
(let [ transaction (-> (d-transactions/get-by-id transaction-id)
|
(let [ transaction (-> (d-transactions/get-by-id transaction-id)
|
||||||
@@ -1152,12 +1132,12 @@
|
|||||||
(solr/touch-with-ledger transaction-id)
|
(solr/touch-with-ledger transaction-id)
|
||||||
|
|
||||||
(modal-response
|
(modal-response
|
||||||
(com/success-modal {:title "Rule applied successfully"
|
(com/success-modal {:title "Rule applied successfully"}
|
||||||
}
|
|
||||||
[:p.text-gray-600.mt-2 "The selected rule has been applied to this transaction."])
|
[:p.text-gray-600.mt-2 "The selected rule has been applied to this transaction."])
|
||||||
:headers {"hx-trigger" "invalidated"})))
|
:headers {"hx-trigger" "invalidated"})))
|
||||||
|
|
||||||
(defn unlink-payment [{{:keys [transaction-id]} :form-params :as request}]
|
(defn unlink-payment [{{:keys [transaction-id] :as fp} :form-params :as request}]
|
||||||
(let [transaction (dc/pull (dc/db conn)
|
(let [transaction (dc/pull (dc/db conn)
|
||||||
'[:transaction/approval-status
|
'[:transaction/approval-status
|
||||||
|
|
||||||
@@ -1241,6 +1221,25 @@
|
|||||||
(mm/wrap-wizard edit-wizard)
|
(mm/wrap-wizard edit-wizard)
|
||||||
(mm/wrap-init-multi-form-state initial-edit-wizard-state))))
|
(mm/wrap-init-multi-form-state initial-edit-wizard-state))))
|
||||||
|
|
||||||
|
(def save-schema
|
||||||
|
(mc/schema
|
||||||
|
[:and [:map [:action [:enum :apply-rule :unlink-payment :link-unpaid-invoices :link-autopay-invoices :link-payment]]]
|
||||||
|
[:multi {:dispatch :action}
|
||||||
|
[:apply-rule [:map
|
||||||
|
[:transaction-id entity-id]
|
||||||
|
[:rule-id entity-id]]]
|
||||||
|
[:unlink-payment [:map
|
||||||
|
[:transaction-id entity-id]]]
|
||||||
|
[:link-unpaid-invoices [:map
|
||||||
|
[:transaction-id entity-id]
|
||||||
|
[:unpaid-invoice-ids [:vector {:coerce? true} entity-id]]]]
|
||||||
|
[:link-autopay-invoices [:map
|
||||||
|
[:transaction-id entity-id]
|
||||||
|
[:autopay-invoice-ids [:vector {:coerce? true} entity-id]]]]
|
||||||
|
[:link-payment [:map
|
||||||
|
[:transaction-id entity-id]
|
||||||
|
[:payment-id entity-id]]]]]))
|
||||||
|
|
||||||
(def key->handler
|
(def key->handler
|
||||||
(apply-middleware-to-all-handlers
|
(apply-middleware-to-all-handlers
|
||||||
{::route/edit-wizard (-> mm/open-wizard-handler
|
{::route/edit-wizard (-> mm/open-wizard-handler
|
||||||
@@ -1267,20 +1266,19 @@
|
|||||||
(mm/wrap-wizard edit-wizard)
|
(mm/wrap-wizard edit-wizard)
|
||||||
(mm/wrap-decode-multi-form-state))
|
(mm/wrap-decode-multi-form-state))
|
||||||
::route/edit-wizard-new-account (->
|
::route/edit-wizard-new-account (->
|
||||||
(add-new-entity-handler [:step-params :transaction/accounts]
|
(add-new-entity-handler [:step-params :transaction/accounts]
|
||||||
(fn render [cursor request]
|
(fn render [cursor request]
|
||||||
(transaction-account-row*
|
(transaction-account-row*
|
||||||
{:value cursor
|
{:value cursor
|
||||||
:client-id (:client-id (:query-params request))}))
|
:client-id (:client-id (:query-params request))}))
|
||||||
(fn build-new-row [base _]
|
(fn build-new-row [base _]
|
||||||
(assoc base :transaction-account/location "Shared")))
|
(assoc base :transaction-account/location "Shared")))
|
||||||
(wrap-schema-enforce :query-schema [:map
|
(wrap-schema-enforce :query-schema [:map
|
||||||
[:client-id {:optional true}
|
[:client-id {:optional true}
|
||||||
[:maybe entity-id]]]))
|
[:maybe entity-id]]]))
|
||||||
::route/match-payment (-> match-payment
|
::route/match-payment (-> match-payment
|
||||||
(wrap-schema-enforce :form-schema
|
(wrap-schema-enforce :form-schema
|
||||||
[:map [:transaction-id entity-id]
|
save-schema))
|
||||||
[:match-payment-id entity-id]]))
|
|
||||||
::route/match-autopay-invoices (-> match-autopay-invoices
|
::route/match-autopay-invoices (-> match-autopay-invoices
|
||||||
(wrap-schema-enforce :form-schema
|
(wrap-schema-enforce :form-schema
|
||||||
[:map [:transaction-id entity-id]
|
[:map [:transaction-id entity-id]
|
||||||
@@ -1295,7 +1293,10 @@
|
|||||||
[:rule-id entity-id]]))
|
[:rule-id entity-id]]))
|
||||||
::route/unlink-payment (-> unlink-payment
|
::route/unlink-payment (-> unlink-payment
|
||||||
(wrap-schema-enforce :form-schema
|
(wrap-schema-enforce :form-schema
|
||||||
[:map [:transaction-id entity-id]]))}
|
save-schema))}
|
||||||
(fn [h]
|
(fn [h]
|
||||||
(-> h
|
(-> h
|
||||||
(wrap-client-redirect-unauthenticated)))))
|
(wrap-client-redirect-unauthenticated)))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user