fixes
This commit is contained in:
@@ -25,8 +25,9 @@
|
|||||||
[auto-ap.ssr.svg :as svg]
|
[auto-ap.ssr.svg :as svg]
|
||||||
[auto-ap.ssr.utils
|
[auto-ap.ssr.utils
|
||||||
:refer [->db-id apply-middleware-to-all-handlers check-allowance
|
:refer [->db-id apply-middleware-to-all-handlers check-allowance
|
||||||
check-location-belongs entity-id html-response modal-response
|
check-location-belongs entity-id form-validation-error
|
||||||
ref->enum-schema strip wrap-entity wrap-schema-enforce]]
|
html-response modal-response ref->enum-schema strip wrap-entity
|
||||||
|
wrap-schema-enforce]]
|
||||||
[auto-ap.time :as atime]
|
[auto-ap.time :as atime]
|
||||||
[bidi.bidi :as bidi]
|
[bidi.bidi :as bidi]
|
||||||
[clj-time.coerce :as coerce]
|
[clj-time.coerce :as coerce]
|
||||||
@@ -34,8 +35,7 @@
|
|||||||
[hiccup.util :as hu]
|
[hiccup.util :as hu]
|
||||||
[iol-ion.query :refer [dollars=]]
|
[iol-ion.query :refer [dollars=]]
|
||||||
[iol-ion.tx :refer [random-tempid]]
|
[iol-ion.tx :refer [random-tempid]]
|
||||||
[malli.core :as mc]
|
[malli.core :as mc]))
|
||||||
[malli.util :as mut]))
|
|
||||||
|
|
||||||
(def transaction-approval-status
|
(def transaction-approval-status
|
||||||
{:transaction-approval-status/unapproved "Unapproved"
|
{:transaction-approval-status/unapproved "Unapproved"
|
||||||
@@ -627,45 +627,30 @@
|
|||||||
[:div.font-medium "Date"]
|
[:div.font-medium "Date"]
|
||||||
[:div (some-> payment :payment/date (atime/unparse-local atime/normal-date))]]
|
[:div (some-> payment :payment/date (atime/unparse-local atime/normal-date))]]
|
||||||
[:div.mt-4 {:hx-post (bidi/path-for ssr-routes/only-routes ::route/unlink-payment)
|
[:div.mt-4 {:hx-post (bidi/path-for ssr-routes/only-routes ::route/unlink-payment)
|
||||||
:hx-params "transaction-id, action"
|
|
||||||
:hx-trigger "unlinkPayment"
|
:hx-trigger "unlinkPayment"
|
||||||
:hx-target "#payment-matches"
|
:hx-target "#payment-matches"
|
||||||
:hx-include "this"
|
:hx-include "this"
|
||||||
:hx-swap "outerHTML"
|
:hx-swap "outerHTML"
|
||||||
:hx-confirm "Are you sure you want to unlink this payment?"}
|
:hx-confirm "Are you sure you want to unlink this payment?"}
|
||||||
|
|
||||||
(com/hidden {:name "action"
|
|
||||||
:value "unlink-payment"
|
|
||||||
:form ""})
|
|
||||||
(com/hidden {:name "transaction-id" :value tx-id :form ""})
|
|
||||||
(com/a-button {:color :red :size :small
|
(com/a-button {:color :red :size :small
|
||||||
"@click" "$dispatch('unlinkPayment')"} "Unlink Payment")]]])
|
"@click" "$dispatch('unlinkPayment')"} "Unlink Payment")]]])
|
||||||
(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 {:hx-post (bidi/path-for ssr-routes/only-routes ::route/link-payment)
|
[:div.space-y-2
|
||||||
:hx-trigger "matchPayment"
|
[:label.block.text-sm.font-medium.mb-1 "Select a payment to match:"]
|
||||||
:hx-target "#modal-holder"
|
(when payments
|
||||||
:hx-include "this"
|
(let [payment-id-field (fc/with-field :payment-id (fc/field-name ))]
|
||||||
:hx-swap "outerHTML"}
|
(com/radio-card {:options (for [payment payments]
|
||||||
(com/hidden {:name "action"
|
{:value (:db/id payment)
|
||||||
:value "link-payment"
|
:content (str (:payment/invoice-number payment) " - "
|
||||||
:form ""})
|
(-> payment :payment/vendor :vendor/name)
|
||||||
(com/hidden {:name "transaction-id"
|
" - Amount: $" (format "%.2f" (:payment/amount payment))
|
||||||
:value (-> request :entity :db/id)
|
" • Date: " (some-> payment :payment/date coerce/to-date-time (atime/unparse-local atime/normal-date)))})
|
||||||
:form ""})
|
:name payment-id-field
|
||||||
[:div.space-y-2
|
:width "w-full"})))
|
||||||
[:label.block.text-sm.font-medium.mb-1 "Select a payment to match:"]
|
(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"])]]
|
||||||
(when payments
|
|
||||||
(com/radio-card {:options (for [payment payments]
|
|
||||||
{:value (:db/id payment)
|
|
||||||
:content (str (:payment/invoice-number payment) " - "
|
|
||||||
(-> payment :payment/vendor :vendor/name)
|
|
||||||
" - Amount: $" (format "%.2f" (:payment/amount payment))
|
|
||||||
" • Date: " (some-> payment :payment/date coerce/to-date-time (atime/unparse-local atime/normal-date)))})
|
|
||||||
:name "payment-id"
|
|
||||||
:width "w-full"}))
|
|
||||||
(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."])]))
|
[:div.text-center.py-4.text-gray-500 "No matching payments available for this transaction."])]))
|
||||||
|
|
||||||
(defn count-payment-matches [request]
|
(defn count-payment-matches [request]
|
||||||
@@ -809,8 +794,9 @@
|
|||||||
(-> request :multi-form-state :snapshot :action)))
|
(-> request :multi-form-state :snapshot :action)))
|
||||||
|
|
||||||
(defmethod save-handler
|
(defmethod save-handler
|
||||||
:link-payment [{{:keys [transaction-id payment-id]} :form-params :as request transaction :entity}]
|
:link-payment [{{ {:keys [transaction-id payment-id]} :snapshot} :multi-form-state :as request transaction :entity}]
|
||||||
(let [payment (d-checks/get-by-id payment-id)]
|
(let [_ (println "PAYMENT ID IS")
|
||||||
|
payment (d-checks/get-by-id payment-id)]
|
||||||
|
|
||||||
(exception->4xx #(assert-can-see-client (:identity request) (-> transaction :transaction/client :db/id)))
|
(exception->4xx #(assert-can-see-client (:identity request) (-> transaction :transaction/client :db/id)))
|
||||||
(exception->4xx #(assert-can-see-client (:identity request) (-> payment :payment/client :db/id)))
|
(exception->4xx #(assert-can-see-client (:identity request) (-> payment :payment/client :db/id)))
|
||||||
@@ -818,7 +804,10 @@
|
|||||||
|
|
||||||
(when (not= (-> transaction :transaction/client :db/id)
|
(when (not= (-> transaction :transaction/client :db/id)
|
||||||
(-> payment :payment/client :db/id))
|
(-> payment :payment/client :db/id))
|
||||||
(throw (ex-info "Clients don't match" {:validation-error "Payment and client do not match."})))
|
(form-validation-error "Clients don't match."
|
||||||
|
:payment-client-id (:payment/client payment)
|
||||||
|
:transaction-client-id (:transaction/client transaction))
|
||||||
|
#_(throw (ex-info "Clients don't match" {:validation-error "Payment and client do not match."})))
|
||||||
|
|
||||||
(when-not (dollars= (- (:transaction/amount transaction))
|
(when-not (dollars= (- (:transaction/amount transaction))
|
||||||
(:payment/amount payment))
|
(:payment/amount payment))
|
||||||
@@ -828,7 +817,7 @@
|
|||||||
[{:db/id (:db/id payment)
|
[{:db/id (:db/id payment)
|
||||||
:payment/status :payment-status/cleared
|
:payment/status :payment-status/cleared
|
||||||
:payment/date (coerce/to-date (first (sort [(:payment/date payment)
|
:payment/date (coerce/to-date (first (sort [(:payment/date payment)
|
||||||
(:transaction/date transaction)])))}
|
(coerce/to-date-time (:transaction/date transaction))])))}
|
||||||
|
|
||||||
[:upsert-transaction
|
[:upsert-transaction
|
||||||
{:db/id (:db/id transaction)
|
{:db/id (:db/id transaction)
|
||||||
@@ -1025,86 +1014,74 @@
|
|||||||
[:div]
|
[:div]
|
||||||
:headers {"hx-trigger" "modalclose"}))))
|
:headers {"hx-trigger" "modalclose"}))))
|
||||||
|
|
||||||
(defn unlink-payment [{{:keys [transaction-id] :as fp} :form-params :as request}]
|
(defn unlink-payment [{{{transaction-id :db/id} :snapshot} :multi-form-state :as request}]
|
||||||
(let [transaction (dc/pull (dc/db conn)
|
|
||||||
'[:transaction/approval-status
|
(fc/start-form (:multi-form-state request) (when (:form-errors request) {:step-params (:form-errors request)})
|
||||||
|
(let [transaction (dc/pull (dc/db conn)
|
||||||
|
'[:transaction/approval-status
|
||||||
|
|
||||||
:transaction/date
|
:transaction/date
|
||||||
:transaction/location
|
:transaction/location
|
||||||
:transaction/vendor
|
:transaction/vendor
|
||||||
:transaction/accounts
|
:transaction/accounts
|
||||||
:transaction/status
|
:transaction/status
|
||||||
:transaction/client [:db/id]
|
:transaction/client [:db/id]
|
||||||
{:transaction/payment [:payment/date
|
{:transaction/payment [:payment/date
|
||||||
{[:payment/status :xform iol-ion.query/ident] [:db/ident]} :db/id]}]
|
{[:payment/status :xform iol-ion.query/ident] [:db/ident]} :db/id]}]
|
||||||
transaction-id)
|
transaction-id)
|
||||||
payment (-> transaction :transaction/payment)]
|
payment (-> transaction :transaction/payment)]
|
||||||
|
|
||||||
(exception->4xx #(assert-can-see-client (:identity request) (-> transaction :transaction/client :db/id)))
|
(exception->4xx #(assert-can-see-client (:identity request) (-> transaction :transaction/client :db/id)))
|
||||||
(exception->4xx #(assert-not-locked (-> transaction :transaction/client :db/id) (:transaction/date transaction)))
|
(exception->4xx #(assert-not-locked (-> transaction :transaction/client :db/id) (:transaction/date transaction)))
|
||||||
|
|
||||||
(when (not= :payment-status/cleared (-> payment :payment/status))
|
(when (not= :payment-status/cleared (-> payment :payment/status))
|
||||||
(throw (ex-info "Payment can't be undone because it isn't cleared."
|
(throw (ex-info "Payment can't be undone because it isn't cleared."
|
||||||
{:validation-error "Payment can't be undone because it isn't cleared."})))
|
{:validation-error "Payment can't be undone because it isn't cleared."})))
|
||||||
|
|
||||||
(let [is-autopay-payment? (some->> (dc/q {:find ['?sp]
|
(let [is-autopay-payment? (some->> (dc/q {:find ['?sp]
|
||||||
:in ['$ '?payment]
|
:in ['$ '?payment]
|
||||||
:where ['[?ip :invoice-payment/payment ?payment]
|
:where ['[?ip :invoice-payment/payment ?payment]
|
||||||
'[?ip :invoice-payment/invoice ?i]
|
'[?ip :invoice-payment/invoice ?i]
|
||||||
'[(get-else $ ?i :invoice/scheduled-payment "N/A") ?sp]]}
|
'[(get-else $ ?i :invoice/scheduled-payment "N/A") ?sp]]}
|
||||||
(dc/db conn) (:db/id payment))
|
(dc/db conn) (:db/id payment))
|
||||||
seq
|
seq
|
||||||
(map first)
|
(map first)
|
||||||
(every? #(instance? java.util.Date %)))]
|
(every? #(instance? java.util.Date %)))]
|
||||||
(if is-autopay-payment?
|
(if is-autopay-payment?
|
||||||
(audit-transact
|
(audit-transact
|
||||||
(-> [{:db/id (:db/id payment)
|
(-> [{:db/id (:db/id payment)
|
||||||
:payment/status :payment-status/pending}
|
:payment/status :payment-status/pending}
|
||||||
[:upsert-transaction
|
[:upsert-transaction
|
||||||
{:db/id (:db/id transaction)
|
{:db/id (:db/id transaction)
|
||||||
:transaction/approval-status :transaction-approval-status/unapproved
|
:transaction/approval-status :transaction-approval-status/unapproved
|
||||||
:transaction/payment nil
|
:transaction/payment nil
|
||||||
:transaction/vendor nil
|
:transaction/vendor nil
|
||||||
:transaction/location nil
|
:transaction/location nil
|
||||||
:transaction/accounts nil}]
|
:transaction/accounts nil}]
|
||||||
[:db/retractEntity (:db/id payment)]]
|
[:db/retractEntity (:db/id payment)]]
|
||||||
(into (map (fn [[invoice-payment]]
|
(into (map (fn [[invoice-payment]]
|
||||||
[:db/retractEntity invoice-payment])
|
[:db/retractEntity invoice-payment])
|
||||||
(dc/q {:find ['?ip]
|
(dc/q {:find ['?ip]
|
||||||
:in ['$ '?p]
|
:in ['$ '?p]
|
||||||
:where ['[?ip :invoice-payment/payment ?p]]}
|
:where ['[?ip :invoice-payment/payment ?p]]}
|
||||||
(dc/db conn)
|
(dc/db conn)
|
||||||
(:db/id payment)))))
|
(:db/id payment)))))
|
||||||
(:identity request))
|
(:identity request))
|
||||||
(audit-transact
|
(audit-transact
|
||||||
[{:db/id (:db/id payment)
|
[{:db/id (:db/id payment)
|
||||||
:payment/status :payment-status/pending}
|
:payment/status :payment-status/pending}
|
||||||
[:upsert-transaction
|
[:upsert-transaction
|
||||||
{:db/id (:db/id transaction)
|
{:db/id (:db/id transaction)
|
||||||
:transaction/approval-status :transaction-approval-status/unapproved
|
:transaction/approval-status :transaction-approval-status/unapproved
|
||||||
:transaction/payment nil
|
:transaction/payment nil
|
||||||
:transaction/vendor nil
|
:transaction/vendor nil
|
||||||
:transaction/location nil
|
:transaction/location nil
|
||||||
:transaction/accounts nil}]]
|
:transaction/accounts nil}]]
|
||||||
(:identity request))))
|
(:identity request))))
|
||||||
|
|
||||||
(solr/touch-with-ledger (:db/id transaction))
|
(solr/touch-with-ledger (:db/id transaction))
|
||||||
(html-response (payment-matches-view request)
|
(html-response (fc/with-field :step-params (payment-matches-view request))
|
||||||
:headers {"hx-trigger" "unlinked"})
|
:headers {"hx-trigger" "unlinked"}))))
|
||||||
|
|
||||||
#_(modal-response
|
|
||||||
(com/success-modal {:title "Transaction unlinked successfully"}
|
|
||||||
|
|
||||||
[:p.text-gray-600.mt-2 "The transaction has been unlinked from its payment."]
|
|
||||||
[:p "If you'd like to also void the payment, click "
|
|
||||||
(com/link
|
|
||||||
{:hx-boost true
|
|
||||||
:href (hu/url (bidi/path-for ssr-routes/only-routes ::payment-route/all-page)
|
|
||||||
{:exact-match-id (:db/id payment)})}
|
|
||||||
"here")
|
|
||||||
" to view the payment"])
|
|
||||||
|
|
||||||
:headers {"hx-trigger" "invalidated"})))
|
|
||||||
|
|
||||||
#_(def save-schema
|
#_(def save-schema
|
||||||
(mc/schema
|
(mc/schema
|
||||||
@@ -1143,6 +1120,7 @@
|
|||||||
(form-schema [_]
|
(form-schema [_]
|
||||||
edit-form-schema)
|
edit-form-schema)
|
||||||
(submit [this {:keys [multi-form-state request-method identity] :as request}]
|
(submit [this {:keys [multi-form-state request-method identity] :as request}]
|
||||||
|
(println "SUBMITTING")
|
||||||
(save-handler request)
|
(save-handler request)
|
||||||
))
|
))
|
||||||
|
|
||||||
@@ -1209,9 +1187,11 @@
|
|||||||
[:client-id {:optional true}
|
[:client-id {:optional true}
|
||||||
[:maybe entity-id]]]))
|
[:maybe entity-id]]]))
|
||||||
|
|
||||||
#_#_::route/unlink-payment (-> unlink-payment
|
::route/unlink-payment (-> unlink-payment
|
||||||
(wrap-entity [:form-params :transaction-id] d-transactions/default-read)
|
(wrap-entity [:multi-form-state :snapshot :db/id] d-transactions/default-read)
|
||||||
(wrap-schema-enforce :form-schema
|
(mm/wrap-wizard edit-wizard)
|
||||||
|
(mm/wrap-decode-multi-form-state)
|
||||||
|
#_(wrap-schema-enforce :form-schema
|
||||||
save-schema))}
|
save-schema))}
|
||||||
(fn [h]
|
(fn [h]
|
||||||
(-> h
|
(-> h
|
||||||
|
|||||||
@@ -29,13 +29,10 @@
|
|||||||
[:link {:rel "stylesheet", :href "/output.css"}]
|
[:link {:rel "stylesheet", :href "/output.css"}]
|
||||||
[:script {:src "https://cdn.plaid.com/link/v2/stable/link-initialize.js"}]
|
[:script {:src "https://cdn.plaid.com/link/v2/stable/link-initialize.js"}]
|
||||||
[:script { :src "https://cdn.jsdelivr.net/npm/@ryangjchandler/alpine-tooltip@1.x.x/dist/cdn.min.js" :defer true}]
|
[:script { :src "https://cdn.jsdelivr.net/npm/@ryangjchandler/alpine-tooltip@1.x.x/dist/cdn.min.js" :defer true}]
|
||||||
[:link {:rel "stylesheet" :href "https://unpkg.com/tippy.js@6/dist/tippy.css"}]
|
[:link {:rel "stylesheet" :href "/css/tippy/tippy.css"}]
|
||||||
[:link {:rel "stylesheet" :href "https://unpkg.com/tippy.js@6/themes/light.css"}]
|
[:link {:rel "stylesheet" :href "/css/tippy/light.css"}]
|
||||||
(if (= "dev" (:dd-env env))
|
[:script {:src "/js/htmx.min.js"
|
||||||
[:script {:src "https://unpkg.com/htmx.org@1.9.6/dist/htmx.js"
|
:crossorigin= "anonymous"}]
|
||||||
:crossorigin= "anonymous"}]
|
|
||||||
[:script {:src "https://unpkg.com/htmx.org@1.9.6/dist/htmx.min.js"
|
|
||||||
:crossorigin= "anonymous"}])
|
|
||||||
|
|
||||||
[:script {:src "https://cdn.jsdelivr.net/npm/sortablejs@1.15.2/Sortable.min.js"}]
|
[:script {:src "https://cdn.jsdelivr.net/npm/sortablejs@1.15.2/Sortable.min.js"}]
|
||||||
[:script {:src "/js/htmx-disable.js"}]
|
[:script {:src "/js/htmx-disable.js"}]
|
||||||
|
|||||||
@@ -196,6 +196,7 @@
|
|||||||
:form-errors (assoc-in {} path [m])}))))
|
:form-errors (assoc-in {} path [m])}))))
|
||||||
|
|
||||||
(defn form-validation-error [m & {:as data}]
|
(defn form-validation-error [m & {:as data}]
|
||||||
|
(alog/warn ::form-validaiton-error :data data)
|
||||||
(throw+ (ex-info m (merge data {:type :form-validation
|
(throw+ (ex-info m (merge data {:type :form-validation
|
||||||
:form-validation-errors [m]}))))
|
:form-validation-errors [m]}))))
|
||||||
|
|
||||||
|
|||||||
6
tasks
6
tasks
@@ -8,4 +8,8 @@
|
|||||||
* also add tests
|
* also add tests
|
||||||
* Make sure that "Shared" really shares locations
|
* Make sure that "Shared" really shares locations
|
||||||
* make sure transactions support import-batch-id query parameter
|
* make sure transactions support import-batch-id query parameter
|
||||||
* make locked transactions clearer
|
* make locked transactions clearer
|
||||||
|
* unlinking then linking requires first closing the dialog.
|
||||||
|
* make approved/unapproved be on the actions page
|
||||||
|
* make memo on the acations page
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user