(cloud) experimental approach to ensure the ledgers do not get out of sync

This commit is contained in:
2023-04-02 07:45:12 -07:00
parent baa1c2e001
commit 68e809d8fb
10 changed files with 301 additions and 134 deletions

View File

@@ -220,6 +220,82 @@
result result
nil))) nil)))
(defn invoice->journal-entry
([db invoice-id]
(invoice->journal-entry db invoice-id invoice-id))
;; the 3-arity version allows you to pass a potential tempid in instead of the invoice-id,
;; which would be a temporary value after the transaction
([db invoice-id raw-invoice-id]
(let [entity (dc/pull db
'[:invoice/total
:invoice/exclude-from-ledger
:invoice/outstanding-balance
:invoice/date
{:invoice/vendor [:db/id :vendor/name]
:invoice/client [:db/id :client/code]
:invoice/payment [:db/id {:payment/status [:db/ident]}]
:invoice/status [:db/ident]
:invoice/import-status [:db/ident]
:invoice/expense-accounts [:invoice-expense-account/account
:invoice-expense-account/amount
:invoice-expense-account/location]}]
invoice-id)
credit-invoice? (< (:invoice/total entity 0.0) 0.0)]
(when-not (or
(not (:invoice/total entity))
(= true (:invoice/exclude-from-ledger entity))
(= :import-status/pending (:db/ident (:invoice/import-status entity)))
(= :invoice-status/voided (:db/ident (:invoice/status entity)))
(< -0.001 (:invoice/total entity) 0.001))
(remove-nils
{:journal-entry/source "invoice"
:journal-entry/client (:db/id (:invoice/client entity))
:journal-entry/date (:invoice/date entity)
:journal-entry/original-entity raw-invoice-id
:journal-entry/vendor (:db/id (:invoice/vendor entity))
:journal-entry/amount (Math/abs (:invoice/total entity))
:journal-entry/line-items (into [(cond-> {:db/id (str (:db/id entity) "-" 0)
:journal-entry-line/account :account/accounts-payable
:journal-entry-line/location "A"
}
credit-invoice? (assoc :journal-entry-line/debit (Math/abs (:invoice/total entity)))
(not credit-invoice?) (assoc :journal-entry-line/credit (Math/abs (:invoice/total entity))))]
(map-indexed (fn [i ea]
(cond->
{:db/id (str (:db/id entity) "-" (inc i))
:journal-entry-line/account (:db/id (:invoice-expense-account/account ea))
:journal-entry-line/location (or (:invoice-expense-account/location ea) "HQ")
}
credit-invoice? (assoc :journal-entry-line/credit (Math/abs (:invoice-expense-account/amount ea)))
(not credit-invoice?) (assoc :journal-entry-line/debit (Math/abs (:invoice-expense-account/amount ea)))))
(:invoice/expense-accounts entity)))
:journal-entry/cleared (and (< (:invoice/outstanding-balance entity) 0.01)
(every? #(= :payment-status/cleared (:payment/status %)) (:invoice/payments entity))
)})))))
(defn upsert-invoice [db invoice]
(let [
upserted-entity (upsert-entity db invoice)
with-invoice (try (dc/with db {:tx-data upserted-entity})
(catch ClassCastException e
(println "Dev local does not support with in tx functions. :(")
(dc/with (dc/with-db @(resolve 'auto-ap.datomic/conn)) {:tx-data upserted-entity})
))
invoice-id (or (-> with-invoice :tempids (get (:db/id invoice)))
(:db/id invoice))
journal-entry (invoice->journal-entry (:db-after with-invoice)
invoice-id
(:db/id invoice))]
(into upserted-entity
(if journal-entry
(upsert-ledger db journal-entry)
[[:db/retractEntity [:journal-entry/original-entity (:db/id invoice)]]]))))
(defn propose-invoice [db invoice] (defn propose-invoice [db invoice]
(let [existing? (boolean (seq (dc/q '[:find ?i (let [existing? (boolean (seq (dc/q '[:find ?i
:in $ ?invoice-number ?client ?vendor :in $ ?invoice-number ?client ?vendor
@@ -228,10 +304,10 @@
[?i :invoice/client ?client] [?i :invoice/client ?client]
[?i :invoice/vendor ?vendor] [?i :invoice/vendor ?vendor]
(not [?i :invoice/status :invoice-status/voided])] (not [?i :invoice/status :invoice-status/voided])]
db db
(:invoice/invoice-number invoice) (:invoice/invoice-number invoice)
(:invoice/client invoice) (:invoice/client invoice)
(:invoice/vendor invoice))))] (:invoice/vendor invoice))))]
(if existing? (if existing?
[] []
[(remove-nils invoice)]))) (upsert-invoice db invoice))))

View File

@@ -4,6 +4,7 @@
iol-ion.tx/upsert-ledger iol-ion.tx/upsert-ledger
iol-ion.tx/min-by iol-ion.tx/min-by
iol-ion.tx/propose-invoice iol-ion.tx/propose-invoice
iol-ion.tx/upsert-invoice
iol-ion.query/dollars= iol-ion.query/dollars=
iol-ion.query/dollars-0?] iol-ion.query/dollars-0?]

View File

@@ -1,8 +1,9 @@
(ns auto-ap.graphql.checks (ns auto-ap.graphql.checks
(:require (:require
[amazonica.aws.s3 :as s3] [amazonica.aws.s3 :as s3]
[auto-ap.datomic :refer [conn remove-nils plus pull-many]] [auto-ap.datomic :refer [conn remove-nils plus pull-many audit-transact]]
[auto-ap.datomic.accounts :as a] [auto-ap.datomic.accounts :as a]
[iol-ion.tx :refer [upsert-invoice]]
[auto-ap.datomic.bank-accounts :as d-bank-accounts] [auto-ap.datomic.bank-accounts :as d-bank-accounts]
[auto-ap.datomic.checks :as d-checks] [auto-ap.datomic.checks :as d-checks]
[auto-ap.datomic.clients :as d-clients] [auto-ap.datomic.clients :as d-clients]
@@ -18,7 +19,6 @@
assert-failure assert-failure
assert-not-locked assert-not-locked
enum->keyword]] enum->keyword]]
[auto-ap.ledger :refer [transact-with-ledger]]
[auto-ap.numeric :refer [num->words]] [auto-ap.numeric :refer [num->words]]
[auto-ap.time :refer [iso-date local-now parse]] [auto-ap.time :refer [iso-date local-now parse]]
[auto-ap.utils :refer [by dollars-0?]] [auto-ap.utils :refer [by dollars-0?]]
@@ -415,7 +415,7 @@
(make-pdfs (filter #(and (= :payment-type/check (:payment/type %)) (make-pdfs (filter #(and (= :payment-type/check (:payment/type %))
(> (:payment/amount %) 0.0)) (> (:payment/amount %) 0.0))
checks)))) checks))))
(transact-with-ledger (map #(if (map? %) (audit-transact (map #(if (map? %)
(dissoc % :payment/pdf-data) (dissoc % :payment/pdf-data)
%) checks ) id) %) checks ) id)
@@ -476,7 +476,7 @@
0 0
invoice-payment-lookup)] invoice-payment-lookup)]
(transact-with-ledger (audit-transact
(into [(assoc base-payment (into [(assoc base-payment
:payment/type :payment-type/check :payment/type :payment-type/check
:payment/status :payment-status/pending :payment/status :payment-status/pending
@@ -501,60 +501,60 @@
new-balance (+ (:invoice/outstanding-balance invoice) new-balance (+ (:invoice/outstanding-balance invoice)
(:invoice-payment/amount x))] (:invoice-payment/amount x))]
[[:db.fn/retractEntity (:db/id x)] [[:db.fn/retractEntity (:db/id x)]
{:db/id (:db/id invoice) `(upsert-invoice ~{:db/id (:db/id invoice)
:invoice/outstanding-balance new-balance :invoice/outstanding-balance new-balance
:invoice/status (if (dollars-0? new-balance) :invoice/status (if (dollars-0? new-balance)
(:invoice/status invoice) (:invoice/status invoice)
:invoice-status/unpaid)}])) :invoice-status/unpaid)})]))
(:payment/invoices check)) (:payment/invoices check))
updated-payment {:db/id id updated-payment {:db/id id
:payment/amount 0.0 :payment/amount 0.0
:payment/status :payment-status/voided}] :payment/status :payment-status/voided}]
(transact-with-ledger (conj removing-payments updated-payment) (audit-transact (conj removing-payments updated-payment)
(:id context))) (:id context)))
(-> (d-checks/get-by-id id) (-> (d-checks/get-by-id id)
(->graphql)))) (->graphql))))
(defn void-payments-internal [all-ids id] (defn void-payments-internal [all-ids id]
(transact-with-ledger (->> all-ids (audit-transact (->> all-ids
(dc/q '[:find (pull ?p [:db/id (dc/q '[:find (pull ?p [:db/id
{:invoice-payment/_payment [:invoice-payment/amount {:invoice-payment/_payment [:invoice-payment/amount
:db/id :db/id
{:invoice-payment/invoice [:db/id :invoice/outstanding-balance]}]}]) {:invoice-payment/invoice [:db/id :invoice/outstanding-balance]}]}])
:in $ [?p ...] :in $ [?p ...]
:where :where
(not [_ :transaction/payment ?p]) (not [_ :transaction/payment ?p])
(not [?p :payment/status :payment-status/voided]) (not [?p :payment/status :payment-status/voided])
[?p :payment/client ?c] [?p :payment/client ?c]
[(get-else $ ?c :client/locked-until #inst "2000-01-01") ?lu] [(get-else $ ?c :client/locked-until #inst "2000-01-01") ?lu]
[?p :payment/date ?d] [?p :payment/date ?d]
[(>= ?d ?lu)] [(>= ?d ?lu)]
] ]
(dc/db conn)) (dc/db conn))
(map first) (map first)
(mapcat (fn [{:keys [:db/id] (mapcat (fn [{:keys [:db/id]
invoices :invoice-payment/_payment}] invoices :invoice-payment/_payment}]
(into (into
[{:db/id id [{:db/id id
:payment/amount 0.0 :payment/amount 0.0
:payment/status :payment-status/voided}] :payment/status :payment-status/voided}]
(->> invoices (->> invoices
(mapcat (fn [{:keys [:invoice-payment/invoice :db/id :invoice-payment/amount]}] (mapcat (fn [{:keys [:invoice-payment/invoice :db/id :invoice-payment/amount]}]
(let [new-balance (+ (:invoice/outstanding-balance invoice) (let [new-balance (+ (:invoice/outstanding-balance invoice)
amount)] amount)]
[[:db.fn/retractEntity id] [[:db.fn/retractEntity id]
{:db/id (:db/id invoice) `(upsert-invoice ~{:db/id (:db/id invoice)
:invoice/outstanding-balance new-balance :invoice/outstanding-balance new-balance
:invoice/status (if (dollars-0? new-balance) :invoice/status (if (dollars-0? new-balance)
(:invoice/status invoice) (:invoice/status invoice)
:invoice-status/unpaid)}])))))))) :invoice-status/unpaid)})]))))))))
id)) id))
(defn void-payments [context args _] (defn void-payments [context args _]
(assert-admin (:id context)) (assert-admin (:id context))
(let [args (assoc args :id (:id context)) (let [args (assoc args :id (:id context))
ids (some-> args ids (some-> args
:filters :filters
(assoc :id (:id context)) (assoc :id (:id context))
(<-graphql) (<-graphql)
@@ -649,10 +649,7 @@
:payment/type :payment-type/balance-credit :payment/type :payment-type/balance-credit
:payment/status :payment-status/cleared}] :payment/status :payment-status/cleared}]
(audit-transact (-> []
(transact-with-ledger (-> []
(conj payment) (conj payment)
(into (invoice-payments invoices invoice-amounts))) (:id context)) (into (invoice-payments invoices invoice-amounts))) (:id context))
(->graphql {:invoices (d-invoices/get-multi (map :db/id invoices))}))) (->graphql {:invoices (d-invoices/get-multi (map :db/id invoices))})))

View File

@@ -1,8 +1,8 @@
(ns auto-ap.graphql.invoices (ns auto-ap.graphql.invoices
(:require (:require
[auto-ap.datomic [auto-ap.datomic
:refer [conn pull-attr pull-many pull-ref random-tempid ]] :refer [conn pull-attr pull-many pull-ref random-tempid audit-transact audit-transact-batch]]
[iol-ion.tx :refer [upsert-entity]] [iol-ion.tx :refer [upsert-entity upsert-invoice]]
[auto-ap.datomic.clients :as d-clients] [auto-ap.datomic.clients :as d-clients]
[auto-ap.datomic.invoices :as d-invoices] [auto-ap.datomic.invoices :as d-invoices]
[auto-ap.datomic.vendors :as d-vendors] [auto-ap.datomic.vendors :as d-vendors]
@@ -18,7 +18,7 @@
attach-tracing-resolvers attach-tracing-resolvers
enum->keyword]] enum->keyword]]
[auto-ap.ledger [auto-ap.ledger
:refer [transact-batch-with-ledger transact-with-ledger]] :refer [transact-batch-with-ledger]]
[auto-ap.rule-matching :as rm] [auto-ap.rule-matching :as rm]
[auto-ap.utils :refer [dollars=]] [auto-ap.utils :refer [dollars=]]
[clj-time.coerce :as coerce] [clj-time.coerce :as coerce]
@@ -62,8 +62,10 @@
(assert-power-user (:id context)) (assert-power-user (:id context))
(doseq [i invoices] (doseq [i invoices]
(assert-can-see-client (:id context) (:db/id (:invoice/client (dc/pull (dc/db conn) [{:invoice/client [:db/id]}] i))))) (assert-can-see-client (:id context) (:db/id (:invoice/client (dc/pull (dc/db conn) [{:invoice/client [:db/id]}] i)))))
(let [transactions (map (fn [i] [:db/retractEntity i]) invoices)] (let [transactions (mapcat (fn [i] [[:db/retractEntity i]
(transact-with-ledger transactions (:id context)) [:db/retractEntity [:journal-entry/original-invoice i]]])
invoices)]
(audit-transact transactions (:id context))
invoices)) invoices))
(defn approve-invoices [context {:keys [invoices]} _] (defn approve-invoices [context {:keys [invoices]} _]
@@ -74,8 +76,8 @@
i)]] i)]]
(assert-can-see-client (:id context) (-> invoice :invoice/client :db/id)) (assert-can-see-client (:id context) (-> invoice :invoice/client :db/id))
(assert-not-locked (-> invoice :invoice/client :db/id) (-> invoice :invoice/date))) (assert-not-locked (-> invoice :invoice/client :db/id) (-> invoice :invoice/date)))
(let [transactions (map (fn [i] {:db/id i :invoice/import-status :import-status/imported}) invoices)] (let [transactions (map (fn [i] `(upsert-invoice ~{:db/id i :invoice/import-status :import-status/imported})) invoices)]
(transact-with-ledger transactions (:id context)) (audit-transact transactions (:id context))
invoices)) invoices))
(defn assert-no-conflicting [{:keys [invoice_number client_id vendor_id]}] (defn assert-no-conflicting [{:keys [invoice_number client_id vendor_id]}]
@@ -109,19 +111,19 @@
_ (when-not (:db/id account) _ (when-not (:db/id account)
(throw (ex-info (str "Vendor '" (:vendor/name vendor) "' does not have a default expense acount.") {:vendor-id vendor_id})))] (throw (ex-info (str "Vendor '" (:vendor/name vendor) "' does not have a default expense acount.") {:vendor-id vendor_id})))]
`(upsert-entity ~{:db/id "invoice" `(upsert-invoice ~{:db/id "invoice"
:invoice/invoice-number invoice_number :invoice/invoice-number invoice_number
:invoice/client client_id :invoice/client client_id
:invoice/vendor vendor_id :invoice/vendor vendor_id
:invoice/import-status :import-status/imported :invoice/import-status :import-status/imported
:invoice/total total :invoice/total total
:invoice/outstanding-balance total :invoice/outstanding-balance total
:invoice/status :invoice-status/unpaid :invoice/status :invoice-status/unpaid
:invoice/date (coerce/to-date date) :invoice/date (coerce/to-date date)
:invoice/expense-accounts (map expense-account->entity :invoice/expense-accounts (map expense-account->entity
expense_accounts) expense_accounts)
:invoice/due (coerce/to-date due) :invoice/due (coerce/to-date due)
:invoice/scheduled-payment (coerce/to-date scheduled_payment)}))) :invoice/scheduled-payment (coerce/to-date scheduled_payment)})))
(defn assert-valid-expense-accounts [expense_accounts vendor_id] (defn assert-valid-expense-accounts [expense_accounts vendor_id]
(doseq [expense-account expense_accounts (doseq [expense-account expense_accounts
@@ -176,7 +178,7 @@
(assert-valid-expense-accounts expense_accounts vendor_id) (assert-valid-expense-accounts expense_accounts vendor_id)
(assert-invoice-amounts-add-up in) (assert-invoice-amounts-add-up in)
(let [transaction-result (transact-with-ledger [(add-invoice-transaction in)] (:id context))] (let [transaction-result (audit-transact [(add-invoice-transaction in)] (:id context))]
(-> (d-invoices/get-by-id (get-in transaction-result [:tempids "invoice"])) (-> (d-invoices/get-by-id (get-in transaction-result [:tempids "invoice"]))
(->graphql (:id context))))) (->graphql (:id context)))))
@@ -195,7 +197,7 @@
(assert-not-locked client_id (:date in)) (assert-not-locked client_id (:date in))
(assert-valid-expense-accounts (:expense_accounts in) vendor_id) (assert-valid-expense-accounts (:expense_accounts in) vendor_id)
(assert-invoice-amounts-add-up in))) (assert-invoice-amounts-add-up in)))
(let [transaction-result (transact-with-ledger [(add-invoice-transaction in)] (:id context))] (let [transaction-result (audit-transact [(add-invoice-transaction in)] (:id context))]
(mu/trace ::printing-checks (mu/trace ::printing-checks
[] []
(-> (gq-checks/print-checks-internal [{:invoice-id (get-in transaction-result [:tempids "invoice"]) (-> (gq-checks/print-checks-internal [{:invoice-id (get-in transaction-result [:tempids "invoice"])
@@ -230,7 +232,7 @@
expense_accounts) expense_accounts)
:invoice/due (coerce/to-date due) :invoice/due (coerce/to-date due)
:invoice/scheduled-payment (coerce/to-date scheduled_payment)}] :invoice/scheduled-payment (coerce/to-date scheduled_payment)}]
(transact-with-ledger [`(upsert-entity ~updated-invoice)] (audit-transact [`(upsert-invoice ~updated-invoice)]
(:id context)) (:id context))
(-> (d-invoices/get-by-id id) (-> (d-invoices/get-by-id id)
(->graphql (:id context))))) (->graphql (:id context)))))
@@ -239,13 +241,13 @@
(let [invoice (d-invoices/get-by-id id)] (let [invoice (d-invoices/get-by-id id)]
(assert-can-see-client (:id context) (:db/id (:invoice/client invoice))) (assert-can-see-client (:id context) (:db/id (:invoice/client invoice)))
(assert-not-locked (:db/id (:invoice/client invoice)) (:invoice/date invoice)) (assert-not-locked (:db/id (:invoice/client invoice)) (:invoice/date invoice))
(transact-with-ledger [{:db/id id (audit-transact [`(upsert-invoice ~{:db/id id
:invoice/total 0.0 :invoice/total 0.0
:invoice/outstanding-balance 0.0 :invoice/outstanding-balance 0.0
:invoice/status :invoice-status/voided :invoice/status :invoice-status/voided
:invoice/expense-accounts (map (fn [ea] {:db/id (:db/id ea) :invoice/expense-accounts (map (fn [ea] {:db/id (:db/id ea)
:invoice-expense-account/amount 0.0}) :invoice-expense-account/amount 0.0})
(:invoice/expense-accounts invoice))}] (:invoice/expense-accounts invoice))})]
(:id context)) (:id context))
(-> (d-invoices/get-by-id id) (->graphql (:id context))))) (-> (d-invoices/get-by-id id) (->graphql (:id context)))))
@@ -296,7 +298,7 @@
(gq-checks/void-payments-internal voidable-cash-payments (:id context)) (gq-checks/void-payments-internal voidable-cash-payments (:id context))
(log/info "Voiding " (count all-ids) args) (log/info "Voiding " (count all-ids) args)
(transact-with-ledger (audit-transact
(->> all-ids (->> all-ids
(dc/q '[:find (pull ?i [:db/id :invoice/date {:invoice/expense-accounts [:db/id]}]) (dc/q '[:find (pull ?i [:db/id :invoice/date {:invoice/expense-accounts [:db/id]}])
:in $ [?i ...] :in $ [?i ...]
@@ -306,20 +308,17 @@
[?i :invoice/date ?d] [?i :invoice/date ?d]
[(>= ?d ?lu)]] [(>= ?d ?lu)]]
(dc/db conn)) (dc/db conn))
(map first) (map
(mapcat (fn [[i]]
(fn [i] `(upsert-invoice ~{:db/id (:db/id i)
(into :invoice/total 0.0
[{:db/id (:db/id i) :invoice/outstanding-balance 0.0
:invoice/total 0.0 :invoice/status :invoice-status/voided
:invoice/outstanding-balance 0.0 :invoice/expense-accounts (map
:invoice/status :invoice-status/voided (fn [iea]
}] {:db/id (:db/id iea)
(map :invoice-expense-account/amount 0.0})
(fn [iea] (:invoice/expense-accounts i))}))))
{:db/id (:db/id iea)
:invoice-expense-account/amount 0.0})
(:invoice/expense-accounts i))))))
(:id context)) (:id context))
{:message (str "Succesfully voided " (count all-ids))})) {:message (str "Succesfully voided " (count all-ids))}))
@@ -337,17 +336,17 @@
:in ['$ '?e]} :in ['$ '?e]}
:args [history id]}) :args [history id]})
[last-transaction] (->> txs (sort-by first) (last))] [last-transaction] (->> txs (sort-by first) (last))]
(mu/log ::here (audit-transact [`(upsert-invoice
:txes txs) ~(->> txs
(transact-with-ledger [(->> txs (filter (fn [[tx]] (= tx last-transaction)))
(filter (fn [[tx]] (= tx last-transaction))) (reduce (fn [new-transaction [_ entity original-status original-outstanding total expense-account expense-account-amount]]
(reduce (fn [new-transaction [_ entity original-status original-outstanding total expense-account expense-account-amount]] (-> new-transaction
(-> new-transaction (assoc :db/id entity
(assoc :db/id entity :invoice/total total
:invoice/total total :invoice/status original-status
:invoice/status original-status :invoice/outstanding-balance original-outstanding)
:invoice/outstanding-balance original-outstanding) (update :invoice/expense-accounts conj {:db/id expense-account :invoice-expense-account/amount expense-account-amount})))
(update :invoice/expense-accounts conj {:db/id expense-account :invoice-expense-account/amount expense-account-amount}))) {}))] {})))]
(:id context)) (:id context))
(-> (d-invoices/get-by-id id) (-> (d-invoices/get-by-id id)
@@ -363,9 +362,11 @@
(assert-can-see-client (:id context) (:db/id (:invoice/client invoice))) (assert-can-see-client (:id context) (:db/id (:invoice/client invoice)))
(assert-not-locked (:db/id (:invoice/client invoice)) (:invoice/date invoice)) (assert-not-locked (:db/id (:invoice/client invoice)) (:invoice/date invoice))
(assert (not (seq (:invoice-payment/_invoice invoice)))) (assert (not (seq (:invoice-payment/_invoice invoice))))
(transact-with-ledger [[:db/add id :invoice/status :invoice-status/unpaid] (audit-transact [`(upsert-invoice
[:db/add id :invoice/outstanding-balance (:invoice/total invoice)] ~{:db/id id
[:db/retract id :invoice/scheduled-payment (:invoice/scheduled-payment invoice)]] :invoice/status :invoice-status/unpaid
:invoice/outstanding-balance (:invoice/total invoice)
:invoice/scheduled-payment nil})]
(:id context)) (:id context))
(-> (d-invoices/get-by-id id) (-> (d-invoices/get-by-id id)
@@ -378,11 +379,11 @@
_ (assert-not-locked (:db/id (:invoice/client invoice)) (:invoice/date invoice)) _ (assert-not-locked (:db/id (:invoice/client invoice)) (:invoice/date invoice))
_ (assert-valid-expense-accounts (:expense_accounts args) (:db/id (:invoice/vendor invoice )))] _ (assert-valid-expense-accounts (:expense_accounts args) (:db/id (:invoice/vendor invoice )))]
(transact-with-ledger [`(upsert-entity ~{:db/id invoice-id (audit-transact [`(upsert-invoice ~{:db/id invoice-id
:invoice/expense-accounts (map :invoice/expense-accounts (map
expense-account->entity expense-account->entity
(:expense_accounts args))})] (:expense_accounts args))})]
(:id context)) (:id context))
(->graphql (->graphql
(d-invoices/get-by-id (:invoice_id args)) (d-invoices/get-by-id (:invoice_id args))
(:id context)))) (:id context))))
@@ -468,9 +469,9 @@
(let [err (str "Account " name " uses location " (:location a) ", but doesn't belong to the client.")] (let [err (str "Account " name " uses location " (:location a) ", but doesn't belong to the client.")]
(throw (ex-info err {:validation-error err}) )))) (throw (ex-info err {:validation-error err}) ))))
(log/info "Bulk coding " (count all-ids) args) (log/info "Bulk coding " (count all-ids) args)
(transact-batch-with-ledger (audit-transact-batch
(map (fn [i] (map (fn [i]
`(upsert-entity ~{:db/id (:db/id i) `(upsert-invoice ~{:db/id (:db/id i)
:invoice/expense-accounts (maybe-code-accounts i (:accounts args) locations)})) :invoice/expense-accounts (maybe-code-accounts i (:accounts args) locations)}))
invoices) invoices)
(:id context)) (:id context))

View File

@@ -2,9 +2,8 @@
(:gen-class) (:gen-class)
(:require (:require
[amazonica.aws.s3 :as s3] [amazonica.aws.s3 :as s3]
[auto-ap.datomic :refer [conn pull-attr]] [auto-ap.datomic :refer [audit-transact conn pull-attr]]
[auto-ap.jobs.core :refer [execute]] [auto-ap.jobs.core :refer [execute]]
[auto-ap.ledger :refer [transact-with-ledger]]
[auto-ap.time :as atime] [auto-ap.time :as atime]
[auto-ap.utils :refer [dollars=]] [auto-ap.utils :refer [dollars=]]
[clj-time.coerce :as coerce] [clj-time.coerce :as coerce]
@@ -14,7 +13,8 @@
[clojure.tools.logging :as log] [clojure.tools.logging :as log]
[config.core :refer [env]] [config.core :refer [env]]
[datomic.client.api :as dc]) [datomic.client.api :as dc])
(:import [java.util UUID])) (:import
(java.util UUID)))
(def bucket (:data-bucket env)) (def bucket (:data-bucket env))
@@ -141,8 +141,8 @@
(log/info "contains " (count data) " rows") (log/info "contains " (count data) " rows")
(doseq [n (partition-all 50 (register-invoice-import* data))] (doseq [n (partition-all 50 (register-invoice-import* data))]
(log/info "transacting" n) (log/info "transacting" n)
(transact-with-ledger n {:user/name "register-invoice-import" (audit-transact n {:user/name "register-invoice-import"
:user/role "admin"})))) :user/role "admin"}))))
(defn -main [& _] (defn -main [& _]
(execute "register-invoice-import" #(register-invoice-import (:args env)))) (execute "register-invoice-import" #(register-invoice-import (:args env))))

View File

@@ -1,8 +1,7 @@
(ns auto-ap.routes.invoices (ns auto-ap.routes.invoices
(:require (:require
[amazonica.aws.s3 :as s3] [amazonica.aws.s3 :as s3]
[auto-ap.datomic :refer [conn remove-nils uri]] [auto-ap.datomic :refer [audit-transact conn remove-nils]]
[iol-ion.tx :refer [propose-invoice]]
[auto-ap.datomic.accounts :as a] [auto-ap.datomic.accounts :as a]
[auto-ap.datomic.clients :as d-clients] [auto-ap.datomic.clients :as d-clients]
[auto-ap.datomic.invoices :as d-invoices] [auto-ap.datomic.invoices :as d-invoices]
@@ -10,7 +9,6 @@
[auto-ap.graphql.utils :refer [assert-admin assert-can-see-client]] [auto-ap.graphql.utils :refer [assert-admin assert-can-see-client]]
[auto-ap.import.manual :as manual] [auto-ap.import.manual :as manual]
[auto-ap.import.manual.common :as c] [auto-ap.import.manual.common :as c]
[auto-ap.ledger :refer [transact-with-ledger]]
[auto-ap.parse :as parse] [auto-ap.parse :as parse]
[auto-ap.routes.utils :refer [wrap-secure]] [auto-ap.routes.utils :refer [wrap-secure]]
[auto-ap.utils :refer [by]] [auto-ap.utils :refer [by]]
@@ -22,6 +20,7 @@
[config.core :refer [env]] [config.core :refer [env]]
[datomic.client.api :as dc] [datomic.client.api :as dc]
[digest] [digest]
[iol-ion.tx :refer [propose-invoice]]
[ring.middleware.json :refer [wrap-json-response]] [ring.middleware.json :refer [wrap-json-response]]
[unilog.context :as lc]) [unilog.context :as lc])
(:import (:import
@@ -278,7 +277,7 @@
(mapv (fn [i] `(propose-invoice ~i))))] (mapv (fn [i] `(propose-invoice ~i))))]
(log/info "creating invoice" potential-invoices) (log/info "creating invoice" potential-invoices)
(let [tx (transact-with-ledger potential-invoices user)] (let [tx (audit-transact potential-invoices user)]
(when-not (seq (dc/q '[:find ?i (when-not (seq (dc/q '[:find ?i
:in $ [?i ...] :in $ [?i ...]
:where [?i :invoice/invoice-number]] :where [?i :invoice/invoice-number]]
@@ -395,7 +394,7 @@
conj conj
[] []
rows)] rows)]
(transact-with-ledger txes nil))) (audit-transact txes nil)))
(defn batch-upload-transactions [{{:keys [data]} :edn-params user :identity}] (defn batch-upload-transactions [{{:keys [data]} :edn-params user :identity}]
(assert-admin user) (assert-admin user)
@@ -479,7 +478,7 @@
(not= "Cash" (:check %)))) (not= "Cash" (:check %))))
(map :vendor-name) (map :vendor-name)
set) set)
_ (transact-with-ledger (invoice-rows->transaction (:new grouped-rows) _ (audit-transact (invoice-rows->transaction (:new grouped-rows)
user) user)
user)] user)]
{:status 200 {:status 200

View File

@@ -146,13 +146,13 @@
(is (= #:transaction{:vendor {:db/id test-vendor-id} (is (= #:transaction{:vendor {:db/id test-vendor-id}
:approval-status {:db/ident :transaction-approval-status/approved} :approval-status {:db/ident :transaction-approval-status/approved}
:payment {:db/id payment-id} :payment {:db/id payment-id}
:accounts [#:transaction-account{:account {:db/id accounts-payable-id} :accounts [#:transaction-account{:account {:account/name "Accounts Payable"}
:location "A" :location "A"
:amount 50.0}]} :amount 50.0}]}
(dc/pull (dc/db conn) '[:transaction/vendor (dc/pull (dc/db conn) '[:transaction/vendor
:transaction/payment :transaction/payment
{:transaction/approval-status [:db/ident] {:transaction/approval-status [:db/ident]
:transaction/accounts [:transaction-account/account :transaction/accounts [{:transaction-account/account [:account/name]}
:transaction-account/location :transaction-account/location
:transaction-account/amount]}] :transaction-account/amount]}]
transaction-id))))) transaction-id)))))

View File

@@ -108,5 +108,10 @@
:client/bank-accounts [(test-bank-account :db/id "test-bank-account-id")]) :client/bank-accounts [(test-bank-account :db/id "test-bank-account-id")])
(test-vendor :db/id "test-vendor-id") (test-vendor :db/id "test-vendor-id")
{:db/id "accounts-payable-id" {:db/id "accounts-payable-id"
:account/name "Accounts Payable"
:db/ident :account/accounts-payable
:account/numeric-code 21000 :account/numeric-code 21000
:account/account-set "default"}])}))) :account/account-set "default"}])})))
(defn apply-tx [data]
(:db-after (dc/transact conn {:tx-data data})))

View File

@@ -0,0 +1,84 @@
(ns iol-ion.integration.tx
(:require
[auto-ap.datomic :refer [conn]]
[auto-ap.integration.util
:refer [setup-test-data test-invoice wrap-setup apply-tx]]
[clojure.test :as t :refer [deftest is use-fixtures testing]]
[datomic.client.api :as dc]
[iol-ion.tx :as sut]))
(use-fixtures :each wrap-setup)
(def journal-pull [:journal-entry/date
:journal-entry/original-entity
:journal-entry/client
:journal-entry/source
:journal-entry/cleared
:journal-entry/amount
:journal-entry/vendor
{:journal-entry/line-items [{:journal-entry-line/account '[:account/name]}
:journal-entry-line/location
:journal-entry-line/credit
:journal-entry-line/dirty
:journal-entry-line/debit]}])
(deftest upsert-invoice
(testing "Importing should create a journal entry"
(let [{:strs [invoice-id
test-client-id
test-vendor-id
]} (setup-test-data
[(test-invoice :db/id "invoice-id"
:invoice/import-status :import-status/pending
:invoice/total 200.0
)])]
(is (nil? (:db/id (dc/pull (dc/db conn) journal-pull
[:journal-entry/original-entity invoice-id]))))
(let [db-after (apply-tx (sut/upsert-invoice
(dc/with-db conn)
{:db/id invoice-id
:invoice/import-status :import-status/imported}))]
(is (= #:journal-entry{:date #inst "2022-01-01T00:00:00.000-00:00",
:original-entity #:db{:id invoice-id},
:client #:db{:id test-client-id},
:line-items
[#:journal-entry-line{:account
#:account{:name
"Accounts Payable"},
:credit 200.0,
:location "A",
:dirty true}
#:journal-entry-line{:account
#:account{:name "Account"},
:location "DT",
:dirty true,
:debit 100.0}],
:source "invoice",
:cleared false,
:amount 200.0,
:vendor #:db{:id test-vendor-id}}
(dc/pull db-after journal-pull
[:journal-entry/original-entity invoice-id])))
(testing "voiding an invoice should remove the journal entry"
(let [db-after (apply-tx (sut/upsert-invoice
(dc/with-db conn)
{:db/id invoice-id
:invoice/status :invoice-status/voided}))]
(is (= nil
(dc/pull db-after journal-pull
[:journal-entry/original-entity invoice-id])))))
(testing "invoice should remove the journal entry"
(let [db-after (apply-tx (sut/upsert-invoice
(dc/db conn)
{:db/id invoice-id
:invoice/status :invoice-status/unpaid
:invoice/import-status :import-status/pending}))]
(is (= nil
(dc/pull db-after journal-pull
[:journal-entry/original-entity invoice-id])))))))))

View File

@@ -1,8 +1,13 @@
* try again to see if we can get upsert-ledger into the same transaction, making it all or nothing
* look for all usages of transact-with-ledger
* propose-invoice should use upsert invoice as well
it looks like there are a bbunch of orrphaned customizations for accounts, breaking indexes it looks like there are a bbunch of orrphaned customizations for accounts, breaking indexes
upsertledger - matching transaction rule might not assign an account. Other things might not assign accounts. This is an assertion that is commented out. Determine consequence of disabling upsertledger - matching transaction rule might not assign an account. Other things might not assign accounts. This is an assertion that is commented out. Determine consequence of disabling
Double check each job still functions in the new system Double check each job still functions in the new system
Make reports just be based on running-balances Make reports just be based on running-balances
Test exports Test exports
Move pay into iol-ion.tx, make sure to use upsert-invoice
Some jobs just aren't so big they need to be jobs anymore: Some jobs just aren't so big they need to be jobs anymore:
Refreshing running balance for journal entry lines Refreshing running balance for journal entry lines
@@ -12,7 +17,6 @@ Closing auto invoices
Running Balance Cache Running Balance Cache
* Add tests for upsert-ledger * Add tests for upsert-ledger
* try again to see if we can get upsert-ledger into the same transaction, making it all or nothing
* Make a new way to reset the entire cache for a client * Make a new way to reset the entire cache for a client
Address memory Address memory