Switching to radio.

This commit is contained in:
2025-03-10 09:26:29 -07:00
parent 0a797fea3a
commit 8429d8516c
2 changed files with 258 additions and 256 deletions

View File

@@ -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-"])

View File

@@ -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)))))