Allows paying from credit
This commit is contained in:
@@ -212,3 +212,4 @@
|
||||
"hidden" (boolean (:vendor/hidden result))}))))
|
||||
|
||||
#_(rebuild-search-index)
|
||||
|
||||
|
||||
@@ -1,58 +1,60 @@
|
||||
(ns auto-ap.ssr.invoices
|
||||
(:require [auto-ap.client-routes :as client-routes]
|
||||
[auto-ap.datomic
|
||||
(:require
|
||||
[auto-ap.client-routes :as client-routes]
|
||||
[auto-ap.datomic
|
||||
:refer [add-sorter-fields apply-pagination apply-sort-3
|
||||
audit-transact conn merge-query observable-query
|
||||
pull-many]]
|
||||
[auto-ap.datomic.accounts :as d-accounts]
|
||||
[auto-ap.datomic.bank-accounts :as d-bank-accounts]
|
||||
[auto-ap.datomic.invoices :as d-invoices]
|
||||
[auto-ap.query-params :refer [wrap-copy-qp-pqp]]
|
||||
[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
|
||||
assert-not-locked exception->4xx
|
||||
exception->notification
|
||||
extract-client-ids notify-if-locked]]
|
||||
[auto-ap.logging :as alog]
|
||||
[auto-ap.permissions :refer [can?]]
|
||||
[auto-ap.routes.invoice :as route]
|
||||
[auto-ap.routes.payments :as payment-route]
|
||||
[auto-ap.routes.utils
|
||||
[auto-ap.datomic.accounts :as d-accounts]
|
||||
[auto-ap.datomic.bank-accounts :as d-bank-accounts]
|
||||
[auto-ap.datomic.clients :as d-clients]
|
||||
[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 assert-not-locked
|
||||
exception->4xx exception->notification
|
||||
extract-client-ids notify-if-locked]]
|
||||
[auto-ap.logging :as alog]
|
||||
[auto-ap.permissions :refer [can?]]
|
||||
[auto-ap.query-params :refer [wrap-copy-qp-pqp]]
|
||||
[auto-ap.routes.invoice :as route]
|
||||
[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]]
|
||||
[auto-ap.ssr.components.multi-modal :as mm]
|
||||
[auto-ap.ssr.form-cursor :as fc]
|
||||
[auto-ap.ssr.grid-page-helper :as helper :refer [wrap-apply-sort]]
|
||||
[auto-ap.ssr.hiccup-helper :as hh]
|
||||
[auto-ap.ssr.hx :as hx]
|
||||
[auto-ap.ssr.invoice.common :refer [default-read]]
|
||||
[auto-ap.ssr.invoice.new-invoice-wizard :as new-invoice-wizard]
|
||||
[auto-ap.ssr.invoice.import :as invoice-import]
|
||||
[auto-ap.ssr.pos.common :refer [date-range-field*]]
|
||||
[auto-ap.ssr.svg :as svg]
|
||||
[auto-ap.ssr.utils
|
||||
:refer [apply-middleware-to-all-handlers clj-date-schema
|
||||
dissoc-nil-transformer entity-id html-response
|
||||
main-transformer modal-response money ref->enum-schema
|
||||
round-money strip wrap-entity wrap-implied-route-param
|
||||
wrap-merge-prior-hx wrap-schema-enforce form-validation-error assert-schema]]
|
||||
[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]
|
||||
[malli.core :as mc]
|
||||
[malli.transform :as mt]
|
||||
[malli.util :as mut]))
|
||||
[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]]
|
||||
[auto-ap.ssr.components.multi-modal :as mm]
|
||||
[auto-ap.ssr.form-cursor :as fc]
|
||||
[auto-ap.ssr.grid-page-helper :as helper :refer [wrap-apply-sort]]
|
||||
[auto-ap.ssr.hiccup-helper :as hh]
|
||||
[auto-ap.ssr.hx :as hx]
|
||||
[auto-ap.ssr.invoice.common :refer [default-read]]
|
||||
[auto-ap.ssr.invoice.import :as invoice-import]
|
||||
[auto-ap.ssr.invoice.new-invoice-wizard :as new-invoice-wizard]
|
||||
[auto-ap.ssr.pos.common :refer [date-range-field*]]
|
||||
[auto-ap.ssr.svg :as svg]
|
||||
[auto-ap.ssr.utils
|
||||
:refer [apply-middleware-to-all-handlers assert-schema
|
||||
clj-date-schema dissoc-nil-transformer entity-id
|
||||
html-response main-transformer modal-response money
|
||||
ref->enum-schema round-money strip wrap-entity
|
||||
wrap-implied-route-param wrap-merge-prior-hx
|
||||
wrap-schema-enforce]]
|
||||
[auto-ap.time :as atime]
|
||||
[auto-ap.utils :refer [by dollars-0? dollars=]]
|
||||
[bidi.bidi :as bidi]
|
||||
[clj-time.coerce :as coerce]
|
||||
[clj-time.coerce :as c]
|
||||
[clj-time.core :as time]
|
||||
[clojure.string :as str]
|
||||
[datomic.api :as dc]
|
||||
[hiccup.util :as hu]
|
||||
[malli.core :as mc]
|
||||
[malli.transform :as mt]
|
||||
[malli.util :as mut]))
|
||||
|
||||
|
||||
(defn exact-match-id* [request]
|
||||
@@ -361,29 +363,41 @@
|
||||
ids))
|
||||
|
||||
0)
|
||||
outstanding-balances (if (seq ids)
|
||||
(->>
|
||||
(dc/q '[:find ?i ?v ?ob
|
||||
:in $ [?i ...]
|
||||
:where [?i :invoice/vendor ?v]
|
||||
[?i :invoice/outstanding-balance ?ob]]
|
||||
(dc/db conn)
|
||||
ids)))
|
||||
|
||||
vendor-totals (if (seq ids)
|
||||
(->>
|
||||
(dc/q '[:find ?i ?v ?ob
|
||||
:in $ [?i ...]
|
||||
:where [?i :invoice/vendor ?v]
|
||||
[?i :invoice/outstanding-balance ?ob]]
|
||||
(dc/db conn)
|
||||
ids)
|
||||
outstanding-balances
|
||||
(reduce (fn [acc [_ v ob]]
|
||||
(update acc v (fnil + 0) ob))
|
||||
{})
|
||||
(vals)))
|
||||
all-credits-or-debits (or (every? #(<= % 0.0) vendor-totals)
|
||||
(every? #(>= % 0.0) vendor-totals))
|
||||
total (reduce + 0.0 vendor-totals)]
|
||||
at-least-one-positive-payment (some (fn [[_ _ ob]]
|
||||
(> ob 0.001))
|
||||
outstanding-balances)
|
||||
total (reduce + 0.0 vendor-totals)
|
||||
paying-credit? (and (> (count ids) 1)
|
||||
(= 1 (count vendor-totals))
|
||||
at-least-one-positive-payment
|
||||
(dollars-0? total))]
|
||||
|
||||
|
||||
[:div {:hx-target "this"
|
||||
:hx-get (bidi/path-for ssr-routes/only-routes
|
||||
::route/pay-wizard)
|
||||
:hx-trigger "click from:#pay-button"
|
||||
:x-tooltip "{allowHTML: true, content: () => $refs.template.innerHTML, appendTo: $root}"
|
||||
}
|
||||
[:div (cond-> {:hx-target "this"
|
||||
|
||||
:hx-trigger "click from:#pay-button"
|
||||
:x-tooltip "{allowHTML: true, content: () => $refs.template.innerHTML, appendTo: $root}"}
|
||||
paying-credit? (assoc :hx-post (bidi/path-for ssr-routes/only-routes ::route/pay-using-credit))
|
||||
(not paying-credit? ) (assoc :hx-get (bidi/path-for ssr-routes/only-routes
|
||||
::route/pay-wizard)))
|
||||
(com/button {:color :primary
|
||||
:id "pay-button"
|
||||
:disabled (or (= (count (:ids params)) 0)
|
||||
@@ -397,15 +411,22 @@
|
||||
:x-ref "source"
|
||||
:minimal-loading? true
|
||||
:class "relative"}
|
||||
(if (> (count ids) 0)
|
||||
|
||||
(cond
|
||||
paying-credit?
|
||||
"Pay invoices using credit"
|
||||
|
||||
(> (count ids) 0)
|
||||
|
||||
(format "Pay %d invoices ($%,.2f)"
|
||||
(count ids)
|
||||
(or total 0.0))
|
||||
"Pay")
|
||||
(when (or (= 0 (count ids))
|
||||
(> selected-client-count 1))
|
||||
(com/badge {} "!")))
|
||||
|
||||
|
||||
(or (= 0 (count ids))
|
||||
(> selected-client-count 1))
|
||||
(list "Pay " (com/badge {} "!"))
|
||||
:else
|
||||
"Pay"))
|
||||
[:template {:x-ref "template"}
|
||||
(cond
|
||||
(not all-credits-or-debits)
|
||||
@@ -790,8 +811,95 @@
|
||||
updated-count
|
||||
(count ids))})})))
|
||||
|
||||
;; TODO
|
||||
;; Allow for paying balances from set of invoices for one vendor
|
||||
#_(defn pay-invoices-from-balance [context {invoices :invoices
|
||||
client-id :client_id} _]
|
||||
)
|
||||
|
||||
(defn pay-using-credit [request]
|
||||
(alog/peek (:form-params request))
|
||||
(let [invoices (selected->ids request (:form-params request))
|
||||
|
||||
_ (alog/peek invoices)
|
||||
invoices (d-invoices/get-multi invoices)
|
||||
client->invoices (group-by (comp :db/id :invoice/client)
|
||||
invoices)
|
||||
|
||||
client-id (first (keys client->invoices))
|
||||
_ (when (> (count (keys client->invoices)) 1)
|
||||
(throw (ex-info "Can only pay from one customer's balance at a time" {:type :form-validation})))
|
||||
_ (when-not (can? (:identity request) {:activity :pay :subject :invoice
|
||||
:client client-id})
|
||||
(throw (ex-info "You can't pay these invoices" {:type :form-validation})))
|
||||
client (d-clients/get-by-id client-id)
|
||||
|
||||
_ (when (> (count (set (map :invoice/vendor invoices))) 1)
|
||||
(throw (ex-info "Balance payments can only be for one vendor at a time." {:type :form-validation})))
|
||||
_ (when (> (reduce + 0 (map :invoice/outstanding-balance invoices)) 0.001)
|
||||
(throw (ex-info "There isn't a positive balance to pay from" {:type :form-validation})))
|
||||
invoices-to-be-paid (filter
|
||||
(fn [i]
|
||||
(> (:invoice/outstanding-balance i)
|
||||
0.001))
|
||||
invoices)
|
||||
credit-invoices (filter
|
||||
(fn [i]
|
||||
(< (:invoice/outstanding-balance i)
|
||||
0.001))
|
||||
invoices)
|
||||
|
||||
|
||||
total-to-pay (reduce + 0 (map :invoice/outstanding-balance invoices-to-be-paid))
|
||||
_ (when (<= total-to-pay 0.001)
|
||||
(throw (ex-info "Select some invoices that need to be paid" {:type :form-validation})))
|
||||
|
||||
invoice-amounts (->> invoices-to-be-paid
|
||||
(map (fn [i]
|
||||
[(:db/id i)
|
||||
(:invoice/outstanding-balance i)]))
|
||||
(concat (->> credit-invoices
|
||||
(reduce
|
||||
(fn [[remaining-to-pay invoice-amounts] invoice]
|
||||
|
||||
(cond (dollars-0? (+ remaining-to-pay (:invoice/outstanding-balance invoice)))
|
||||
(reduced (conj invoice-amounts
|
||||
[(:db/id invoice)
|
||||
(:invoice/outstanding-balance invoice)]))
|
||||
|
||||
(< (+ remaining-to-pay (:invoice/outstanding-balance invoice)) 0.0)
|
||||
(reduced (conj invoice-amounts
|
||||
[(:db/id invoice)
|
||||
(- remaining-to-pay)]))
|
||||
|
||||
:else
|
||||
[(+ remaining-to-pay (:invoice/outstanding-balance invoice))
|
||||
(conj invoice-amounts [(:db/id invoice)
|
||||
(:invoice/outstanding-balance invoice)])]))
|
||||
[total-to-pay []])))
|
||||
(into {}))
|
||||
|
||||
|
||||
|
||||
vendor-id (:db/id (:invoice/vendor (first invoices)))
|
||||
payment {:db/id (str vendor-id)
|
||||
:payment/amount total-to-pay
|
||||
:payment/vendor vendor-id
|
||||
:payment/client (:db/id client)
|
||||
:payment/date (c/to-date (time/now))
|
||||
:payment/invoices (map :db/id invoices)
|
||||
:payment/type :payment-type/balance-credit
|
||||
:payment/status :payment-status/cleared}
|
||||
result (audit-transact (-> []
|
||||
(conj payment)
|
||||
(into (invoice-payments invoices invoice-amounts))) (:identity request))]
|
||||
|
||||
(doseq [[_ n] (:tempids result)]
|
||||
(solr/touch-with-ledger n))
|
||||
(html-response [:div]
|
||||
:headers {"hx-trigger" (hx/json {:modalclose ""
|
||||
:invalidated ""
|
||||
:notification (format "Successfully paid %d invoices."
|
||||
(count invoices))})})))
|
||||
|
||||
|
||||
(defn does-amount-exceed-outstanding? [amount outstanding-balance]
|
||||
(let [outstanding-balance (round-money outstanding-balance)
|
||||
@@ -1325,6 +1433,9 @@
|
||||
(wrap-admin))
|
||||
::route/bulk-delete (-> bulk-delete-dialog
|
||||
(wrap-admin))
|
||||
|
||||
::route/pay-using-credit (-> pay-using-credit
|
||||
(wrap-schema-enforce :form-schema query-schema))
|
||||
::route/pay-wizard (-> mm/open-wizard-handler
|
||||
|
||||
(mm/wrap-wizard pay-wizard)
|
||||
@@ -1334,6 +1445,7 @@
|
||||
|
||||
(mm/wrap-wizard pay-wizard)
|
||||
(mm/wrap-decode-multi-form-state))
|
||||
|
||||
::route/pay-wizard-navigate
|
||||
(-> mm/next-handler
|
||||
(mm/wrap-wizard pay-wizard)
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
(def search (wrap-json-response search))
|
||||
|
||||
#_(comment
|
||||
(solr/delete solr/impl "vendors")
|
||||
|
||||
(count (let [valid-ids (->> (dc/q '[:find ?v
|
||||
:in $
|
||||
:where [?v :vendor/name]]
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"/pay-button" ::pay-button
|
||||
|
||||
"/pay" {:get ::pay-wizard
|
||||
"/using-credit" ::pay-using-credit
|
||||
|
||||
"/navigate" ::pay-wizard-navigate
|
||||
:post ::pay-submit}
|
||||
|
||||
Reference in New Issue
Block a user