From a462c56595239a4252c75ccdd10dc2dad596627e Mon Sep 17 00:00:00 2001 From: Bryce Date: Tue, 7 Oct 2025 21:52:46 -0700 Subject: [PATCH] fixes bugs, makes checks printable for different dates. --- src/clj/auto_ap/graphql/checks.clj | 189 +++++++++++++-------------- src/clj/auto_ap/graphql/invoices.clj | 3 +- src/clj/auto_ap/ssr/invoices.clj | 52 +++++--- 3 files changed, 127 insertions(+), 117 deletions(-) diff --git a/src/clj/auto_ap/graphql/checks.clj b/src/clj/auto_ap/graphql/checks.clj index 5dca3903..abe0e26b 100644 --- a/src/clj/auto_ap/graphql/checks.clj +++ b/src/clj/auto_ap/graphql/checks.clj @@ -1,29 +1,27 @@ (ns auto-ap.graphql.checks (:require [amazonica.aws.s3 :as s3] - [auto-ap.datomic :refer [conn remove-nils pull-many audit-transact]] + [auto-ap.datomic :refer [audit-transact conn pull-many remove-nils]] [auto-ap.datomic.accounts :as a] [auto-ap.datomic.bank-accounts :as d-bank-accounts] [auto-ap.datomic.checks :as d-checks] [auto-ap.datomic.clients :as d-clients] [auto-ap.datomic.invoices :as d-invoices] [auto-ap.datomic.transactions :as d-transactions] - [auto-ap.logging :as alog] [auto-ap.datomic.vendors :as d-vendors] [auto-ap.graphql.utils - :refer [->graphql - <-graphql - attach-tracing-resolvers - assert-admin - assert-can-see-client - assert-failure - assert-not-locked + :refer [->graphql <-graphql assert-admin assert-can-see-client + assert-failure assert-not-locked attach-tracing-resolvers enum->keyword]] + [auto-ap.logging :as alog] [auto-ap.numeric :refer [num->words]] - [auto-ap.time :refer [iso-date local-now parse]] + [auto-ap.solr :as solr] + [auto-ap.time :refer [iso-date parse]] + [auto-ap.time :as atime] [auto-ap.utils :refer [by dollars-0?]] [clj-pdf.core :as pdf] [clj-time.coerce :as c] + [clj-time.coerce :as coerce] [clj-time.core :as time] [clj-time.format :as f] [clojure.edn :as edn] @@ -31,12 +29,9 @@ [clojure.set :as set] [clojure.string :as str] [com.brunobonacci.mulog :as mu] - [com.walmartlabs.lacinia.util :refer [attach-resolvers]] [config.core :refer [env]] - [clj-time.coerce :as coerce] [datomic.api :as dc] - [digest] - [auto-ap.solr :as solr]) + [digest]) (:import (java.io ByteArrayOutputStream) (java.text DecimalFormat) @@ -215,7 +210,7 @@ (str "https://" (:data-bucket env) "/merged-checks/" uuid ".pdf"))) #_{:clj-kondo/ignore [:unused-binding]} -(defmulti invoices->entities (fn [invoices vendor-id client bank-account type index invoice-amounts] +(defmulti invoices->entities (fn [invoices vendor-id client bank-account type index invoice-amounts date] type)) (defn invoice-payments [invoices invoice-amounts] (->> (for [invoice invoices @@ -228,16 +223,16 @@ [:pay (:db/id invoice) invoice-amount]]) (reduce into []))) -(defn base-payment [invoices vendor client bank-account _ _ invoice-amounts] +(defn base-payment [invoices vendor client bank-account _ _ invoice-amounts date] {:db/id (str (:db/id vendor)) :payment/bank-account (:db/id bank-account) :payment/amount (reduce + 0 (map (comp invoice-amounts :db/id) invoices)) :payment/vendor (:db/id vendor) :payment/client (:db/id client) - :payment/date (c/to-date (time/now)) + :payment/date (c/to-date (or date (time/now))) :payment/invoices (map :db/id invoices)}) -(defmethod invoices->entities :payment-type/check [invoices vendor client bank-account type index invoice-amounts] +(defmethod invoices->entities :payment-type/check [invoices vendor client bank-account type index invoice-amounts date] (when (<= (->> invoices (map (comp invoice-amounts :db/id)) (reduce + 0.0)) @@ -245,51 +240,52 @@ (throw (ex-info "The selected invoices do not have an outstanding balance." {:validation-error "The selected invoices do not have an outstanding balance."}))) (let [uuid (str (UUID/randomUUID)) + date (or date (time/now)) memo (str "Invoice #'s: " (str/join ", " (map (fn [i] (str (:invoice/invoice-number i) "(" (invoice-amounts (:db/id i)) ")")) invoices))) - base-payment (base-payment invoices vendor client bank-account type index invoice-amounts) + base-payment (base-payment invoices vendor client bank-account type index invoice-amounts date) payment (remove-nils - (assoc base-payment - :payment/s3-uuid (when (> (:payment/amount base-payment) 0) uuid) - :payment/s3-key (when (> (:payment/amount base-payment) 0) (str "checks/" uuid ".pdf")) - :payment/s3-url (when (> (:payment/amount base-payment) 0) (str "https://" (:data-bucket env) "/checks/" uuid ".pdf")) - :payment/check-number (+ index (:bank-account/check-number bank-account)) - :payment/type :payment-type/check - :payment/memo memo - :payment/status :payment-status/pending - :payment/pdf-data (pr-str {:vendor vendor - :paid-to (or (:vendor/paid-to vendor) (:vendor/name vendor)) - :amount (reduce + 0 (map (comp invoice-amounts :db/id) invoices)) - :check (str (+ index (:bank-account/check-number bank-account))) - :memo memo - :date (date->str (local-now)) - :client (dissoc client :client/bank-accounts :client/square-integration-status :client/ezcater-locations :client/locked-until :client/emails :client/square-auth-token :client/square-locations) - :bank-account (dissoc bank-account :bank-account/start-date :bank-account/integration-status) - #_#_:client {:name (:name client) - :address (:address client) - :signature-file (:signature-file client) - :bank {:name (:bank-account/bank-name bank-account) - :acct (:bank-account/bank-code bank-account) - :routing (:bank-account/routing bank-account) - :acct-number (:bank-account/number bank-account)}}})))] + (cond-> (assoc base-payment + :payment/s3-uuid (when (> (:payment/amount base-payment) 0) uuid) + :payment/s3-key (when (> (:payment/amount base-payment) 0) (str "checks/" uuid ".pdf")) + :payment/s3-url (when (> (:payment/amount base-payment) 0) (str "https://" (:data-bucket env) "/checks/" uuid ".pdf")) + :payment/check-number (+ index (:bank-account/check-number bank-account)) + :payment/type :payment-type/check + :payment/memo memo + :payment/status :payment-status/pending + :payment/pdf-data (pr-str {:vendor vendor + :paid-to (or (:vendor/paid-to vendor) (:vendor/name vendor)) + :amount (reduce + 0 (map (comp invoice-amounts :db/id) invoices)) + :check (str (+ index (:bank-account/check-number bank-account))) + :memo memo + :date (date->str date) + :client (dissoc client :client/bank-accounts :client/square-integration-status :client/ezcater-locations :client/locked-until :client/emails :client/square-auth-token :client/square-locations) + :bank-account (dissoc bank-account :bank-account/start-date :bank-account/integration-status) + #_#_:client {:name (:name client) + :address (:address client) + :signature-file (:signature-file client) + :bank {:name (:bank-account/bank-name bank-account) + :acct (:bank-account/bank-code bank-account) + :routing (:bank-account/routing bank-account) + :acct-number (:bank-account/number bank-account)}}}))))] (-> [] (conj payment) (into (invoice-payments invoices invoice-amounts))))) -(defmethod invoices->entities :payment-type/debit [invoices vendor client bank-account type index invoice-amounts] +(defmethod invoices->entities :payment-type/debit [invoices vendor client bank-account type index invoice-amounts date] (when (<= (->> invoices (map (comp invoice-amounts :db/id)) (reduce + 0.0)) 0.001) (throw (ex-info "The selected invoices do not have an outstanding balance." {:validation-error "The selected invoices do not have an outstanding balance."}))) - (let [payment (assoc (base-payment invoices vendor client bank-account type index invoice-amounts) + (let [payment (assoc (base-payment invoices vendor client bank-account type index invoice-amounts date) :payment/type :payment-type/debit :payment/memo (str "Debit Invoice #'s: " (str/join ", " @@ -310,14 +306,14 @@ (throw (ex-info "The selected invoices do not have an outstanding balance." {:validation-error "The selected invoices do not have an outstanding balance."})))) -(defmethod invoices->entities :payment-type/credit [invoices vendor client bank-account type index invoice-amounts] +(defmethod invoices->entities :payment-type/credit [invoices vendor client bank-account type index invoice-amounts date] (when (>= (->> invoices (map (comp invoice-amounts :db/id)) (reduce + 0.0)) 0.001) (throw (ex-info "The selected invoices do not have an outstanding balance." {:validation-error "The selected invoices do not have an outstanding balance."}))) - (let [payment (assoc (base-payment invoices vendor client bank-account type index invoice-amounts) + (let [payment (assoc (base-payment invoices vendor client bank-account type index invoice-amounts date) :payment/type :payment-type/credit :payment/memo (str "Debit Invoice #'s: " (str/join ", " @@ -329,14 +325,14 @@ (conj payment) (into (invoice-payments invoices invoice-amounts))))) -(defmethod invoices->entities :payment-type/cash [invoices vendor client bank-account type index invoice-amounts] +(defmethod invoices->entities :payment-type/cash [invoices vendor client bank-account type index invoice-amounts date] (when (<= (->> invoices (map (comp invoice-amounts :db/id)) (reduce + 0.0)) 0.001) (throw (ex-info "The selected invoices do not have an outstanding balance." {:validation-error "The selected invoices do not have an outstanding balance."}))) - (let [base-payment (base-payment invoices vendor client bank-account type index invoice-amounts) + (let [base-payment (base-payment invoices vendor client bank-account type index invoice-amounts date) transaction-id (str (UUID/randomUUID)) memo (str "Cash Invoice #'s: " (str/join ", " @@ -381,50 +377,51 @@ :client-id client-id :invoices (map :invoice/invoice-number invoices)})))) -(defn print-checks-internal [invoice-payments client-id bank-account-id type id] - (let [type (keyword "payment-type" (name type)) - invoices (d-invoices/get-multi (map :invoice-id invoice-payments)) - client (d-clients/get-by-id client-id) - invoice-amounts (by :invoice-id :amount invoice-payments) - invoices-grouped-by-vendor (group-by (comp :db/id :invoice/vendor) invoices) - vendors (->> (pull-many (dc/db conn) d-vendors/default-read (keys invoices-grouped-by-vendor)) - (by :db/id)) - bank-account (d-bank-accounts/get-by-id bank-account-id) - _ (validate-belonging client-id invoices bank-account) - _ (when (and (nil? (:bank-account/check-number bank-account)) - (= type :payment-type/check)) - (let [message (str "The bank account " (:bank-account/name bank-account) " does not have a starting check number. Please ask the integreat staff to initialize it.")] - (throw (ex-info message - {:validation-error message})))) - checks (mu/trace ::build-checks [] - (->> (for [[[vendor-id invoices] index] (map vector invoices-grouped-by-vendor (range))] - (invoices->entities invoices (vendors vendor-id) client bank-account type index invoice-amounts)) - (reduce into []) - doall)) - checks (if (= type :payment-type/check) - (conj checks [:plus (:db/id bank-account) :bank-account/check-number (count invoices-grouped-by-vendor)]) - checks)] - (when (= type :payment-type/check) - (mu/trace ::making-pdfs [:checks checks] - (make-pdfs (filter #(and (= :payment-type/check (:payment/type %)) - (> (:payment/amount %) 0.0)) - checks)))) - (let [result (audit-transact (map #(if (map? %) - (dissoc % :payment/pdf-data) - %) checks) id)] - - (try - (doseq [[_ i] (:tempids result)] - (solr/touch-with-ledger i)) - (catch Exception e - (alog/error ::cant-save-solr - :error e)))) - {:invoices (d-invoices/get-multi (map :invoice-id invoice-payments)) - :pdf-url (if (= type :payment-type/check) - (mu/trace ::merge-pdfs - [] - (merge-pdfs (filter identity (map :payment/s3-key checks)))) - nil)})) +(defn print-checks-internal + ([invoice-payments client-id bank-account-id type id date] + (let [type (keyword "payment-type" (name type)) + invoices (d-invoices/get-multi (map :invoice-id invoice-payments)) + client (d-clients/get-by-id client-id) + invoice-amounts (by :invoice-id :amount invoice-payments) + invoices-grouped-by-vendor (group-by (comp :db/id :invoice/vendor) invoices) + vendors (->> (pull-many (dc/db conn) d-vendors/default-read (keys invoices-grouped-by-vendor)) + (by :db/id)) + bank-account (d-bank-accounts/get-by-id bank-account-id) + _ (validate-belonging client-id invoices bank-account) + _ (when (and (nil? (:bank-account/check-number bank-account)) + (= type :payment-type/check)) + (let [message (str "The bank account " (:bank-account/name bank-account) " does not have a starting check number. Please ask the integreat staff to initialize it.")] + (throw (ex-info message + {:validation-error message})))) + checks (mu/trace ::build-checks [] + (->> (for [[[vendor-id invoices] index] (map vector invoices-grouped-by-vendor (range))] + (invoices->entities invoices (vendors vendor-id) client bank-account type index invoice-amounts date)) + (reduce into []) + doall)) + checks (if (= type :payment-type/check) + (conj checks [:plus (:db/id bank-account) :bank-account/check-number (count invoices-grouped-by-vendor)]) + checks)] + (when (= type :payment-type/check) + (mu/trace ::making-pdfs [:checks checks] + (make-pdfs (filter #(and (= :payment-type/check (:payment/type %)) + (> (:payment/amount %) 0.0)) + checks)))) + (let [result (audit-transact (map #(if (map? %) + (dissoc % :payment/pdf-data) + %) checks) id)] + + (try + (doseq [[_ i] (:tempids result)] + (solr/touch-with-ledger i)) + (catch Exception e + (alog/error ::cant-save-solr + :error e)))) + {:invoices (d-invoices/get-multi (map :invoice-id invoice-payments)) + :pdf-url (if (= type :payment-type/check) + (mu/trace ::merge-pdfs + [] + (merge-pdfs (filter identity (map :payment/s3-key checks)))) + nil)}))) (defn get-payment-page [context args _] (let [[payments checks-count] (d-checks/get-graphql (-> args @@ -474,14 +471,15 @@ bank-account :payment-type/check 0 - invoice-payment-lookup)] + invoice-payment-lookup + (parse (:date args) iso-date))] (let [result (audit-transact (into [(assoc base-payment :payment/type :payment-type/check :payment/status :payment-status/pending :payment/check-number (:check_number args) - :payment/date (c/to-date (parse (:date args) iso-date)))] + #_#_:payment/date (c/to-date (parse (:date args) iso-date)))] (invoice-payments invoices invoice-payment-lookup)) (:id context))] (doseq [[_ i] (:tempids result)] @@ -586,7 +584,8 @@ (:client_id args) (:bank_account_id args) (:type args) - (:id context)))) + (:id context) + (time/now)))) (defn pay-invoices-from-balance [context {invoices :invoices client-id :client_id} _] diff --git a/src/clj/auto_ap/graphql/invoices.clj b/src/clj/auto_ap/graphql/invoices.clj index ea305baa..d6ab1e3a 100644 --- a/src/clj/auto_ap/graphql/invoices.clj +++ b/src/clj/auto_ap/graphql/invoices.clj @@ -212,7 +212,8 @@ client_id bank-account-id type - (:id context)) + (:id context) + (time/now)) u/->graphql)))) (defn edit-invoice [context {{:keys [id due invoice_number vendor_id total date expense_accounts scheduled_payment] :as in} :invoice} _] diff --git a/src/clj/auto_ap/ssr/invoices.clj b/src/clj/auto_ap/ssr/invoices.clj index e1cb236a..735d0ddb 100644 --- a/src/clj/auto_ap/ssr/invoices.clj +++ b/src/clj/auto_ap/ssr/invoices.clj @@ -1165,7 +1165,7 @@ :name (fc/field-name) :error? (fc/field-errors) :placeholder "10001"})))) - (when (= :handwrite-check (:method (:snapshot (:multi-form-state request)))) + (when (#{:handwrite-check :print-check} (:method (:snapshot (:multi-form-state request)))) (fc/with-field :handwritten-date (com/validated-field {:errors (fc/field-errors) @@ -1186,6 +1186,7 @@ (format "Pay in full ($%,.2f)" total)))} {:value "advanced" :content "Customize payments"}]}) + [:div.space-y-4 (fc/with-field :invoices (com/validated-field @@ -1244,13 +1245,13 @@ bank-account :payment-type/check 0 - invoice-payment-lookup)] + invoice-payment-lookup + (:handwritten-date snapshot))] (let [result (audit-transact (into [(assoc base-payment :payment/type :payment-type/check :payment/status :payment-status/pending - :payment/check-number (:check-number snapshot) - :payment/date (coerce/to-date (:handwritten-date snapshot)))] + :payment/check-number (:check-number snapshot))] (invoice-payments invoices invoice-payment-lookup)) (:identity request))] (try @@ -1344,24 +1345,33 @@ (= "" (:check-number snapshot))) (throw (Exception. "Check number is required"))) true)) + result (exception->4xx - #(if (= :handwrite-check (:method snapshot)) - (add-handwritten-check request this snapshot) - (print-checks-internal (map (fn [i] {:invoice-id (:invoice-id i) - :amount (:amount i)}) - (:invoices snapshot)) - (:client snapshot) - (:bank-account snapshot) - (cond (= :print-check (:method snapshot)) - :payment-type/check - (= :debit (:method snapshot)) - :payment-type/debit - (= :cash (:method snapshot)) - :payment-type/cash - (= :credit (:method snapshot)) - :payment-type/credit - :else :payment-type/debit) - identity)))] + #(do + (when (:handwritten-date snapshot) + (let [invoices (d-invoices/get-multi (map :invoice-id (:invoices snapshot)))] + (assert-not-locked (:db/id (:invoice/client (first invoices))) (:handwritten-date snapshot)))) + (if (= :handwrite-check (:method snapshot)) + (add-handwritten-check request this snapshot) + (try + (print-checks-internal (map (fn [i] {:invoice-id (:invoice-id i) + :amount (:amount i)}) + (:invoices snapshot)) + (:client snapshot) + (:bank-account snapshot) + (cond (= :print-check (:method snapshot)) + :payment-type/check + (= :debit (:method snapshot)) + :payment-type/debit + (= :cash (:method snapshot)) + :payment-type/cash + (= :credit (:method snapshot)) + :payment-type/credit + :else :payment-type/debit) + identity + (:handwritten-date snapshot)) + (catch Exception e + (println e))))))] (modal-response (com/modal {} (com/modal-card-advanced