From 37ea2c7e9740ffb64b73736887579df3a925f9b3 Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Mon, 14 Mar 2022 11:07:41 -0700 Subject: [PATCH] supports locking transactions --- src/clj/auto_ap/graphql/transactions.clj | 44 ++++++++++++++----- src/clj/auto_ap/import/transactions.clj | 7 ++- .../auto_ap/views/pages/transactions.cljs | 2 + test/clj/auto_ap/import/transactions_test.clj | 9 ++++ 4 files changed, 49 insertions(+), 13 deletions(-) diff --git a/src/clj/auto_ap/graphql/transactions.clj b/src/clj/auto_ap/graphql/transactions.clj index a3647490..ed234116 100644 --- a/src/clj/auto_ap/graphql/transactions.clj +++ b/src/clj/auto_ap/graphql/transactions.clj @@ -13,6 +13,7 @@ <-graphql assert-admin assert-can-see-client + assert-not-locked assert-power-user enum->keyword ident->enum-f @@ -69,18 +70,29 @@ specific-ids (d-transactions/filter-ids (:ids args))] (into (set ids) specific-ids))) +(defn all-ids-not-locked [all-ids] + (->> all-ids + (d/q '[:find [?t ...] + :in $ [?t ...] + :where + [?t :transaction/client ?c] + [(get-else $ ?c :client/locked-until #inst "2000-01-01") ?lu] + [?t :transaction/date ?d] + [(>= ?d ?lu)]] + (d/db conn)))) (defn bulk-change-status [context args _] (let [_ (assert-admin (:id context)) args (assoc args :id (:id context)) - all-ids (get-ids-matching-filters args)] + all-ids (->> (get-ids-matching-filters args) + all-ids-not-locked)] (log/info "Unapproving " (count all-ids) args) - (audit-transact-batch - (mapv (fn [i] - {:db/id i - :transaction/approval-status (enum->keyword (:status args) "transaction-approval-status")}) - all-ids) + (->> all-ids + (mapv (fn [t] + {:db/id t + :transaction/approval-status (enum->keyword (:status args) "transaction-approval-status")}))) + (:id context)) {:message (str "Succesfully changed " (count all-ids) " transactions to be " (name (:status args) ) ".")})) @@ -89,7 +101,6 @@ (with-precision 2 (let [accounts (vec (mapcat (fn [ar] - (println ar) (let [cents-to-distribute (int (Math/round (Math/abs (* (:percentage ar) (:transaction/amount transaction) 100))))] @@ -127,7 +138,7 @@ locations (:client/locations (d/pull (d/db conn) [:client/locations] (:client_id (:filters args)))) - all-ids (get-ids-matching-filters args) + all-ids (all-ids-not-locked (get-ids-matching-filters args)) transactions (d/pull-many (d/db conn) '[:db/id :transaction/amount] (vec all-ids)) account-total (reduce + 0 (map (fn [x] (:percentage x)) (:accounts args)))] (when @@ -166,7 +177,7 @@ (defn delete-transactions [context args _] (let [_ (assert-admin (:id context)) args (assoc args :id (:id context)) - all-ids (get-ids-matching-filters args) + all-ids (all-ids-not-locked (get-ids-matching-filters args)) db (d/db conn)] (log/info "Deleting " (count all-ids) args) @@ -219,13 +230,17 @@ transaction (d/pull (d/db conn) [:transaction/approval-status :transaction/status + :transaction/date :transaction/location :transaction/vendor :transaction/accounts :transaction/client [:db/id] - {:transaction/payment [{:payment/status [:db/ident]} :db/id]} ] + {:transaction/payment [:payment/date {:payment/status [:db/ident]} :db/id]} ] transaction-id) _ (assert-can-see-client (:id context) (:transaction/client transaction) ) + _ (assert-not-locked (:db/id (:transaction/client transaction)) (:transaction/date transaction)) + _ (when (:transaction/payment transaction) + (assert-not-locked (:db/id (:transaction/client transaction)) (-> transaction :transaction/payment :payment/date))) _ (log/info "Unlinking" transaction) payment (-> transaction :transaction/payment ) is-autopay-payment? (some->> (doto (d/query {:query {:find ['?sp] @@ -330,6 +345,7 @@ (let [existing-transaction (d-transactions/get-by-id id) _ (assert-can-see-client (:id context) (:transaction/client existing-transaction) ) _ (assert-valid-expense-accounts accounts) + _ (assert-not-locked (:db/id (:transaction/client existing-transaction)) (:transaction/date existing-transaction)) deleted (deleted-accounts existing-transaction accounts) account-total (reduce + 0 (map (fn [x] (:amount x)) accounts)) missing-locations (seq (set/difference @@ -378,7 +394,8 @@ (let [transaction (d-transactions/get-by-id transaction_id) payment (d-checks/get-by-id payment_id) _ (assert-can-see-client (:id context) (:transaction/client transaction) ) - _ (assert-can-see-client (:id context) (:payment/client payment) )] + _ (assert-can-see-client (:id context) (:payment/client payment) ) + _ (assert-not-locked (:db/id (:transaction/client transaction)) (:transaction/date transaction))] (when (not= (:db/id (:transaction/client transaction)) (:db/id (:payment/client payment))) (throw (ex-info "Clients don't match" {:validation-error "Payment and client do not match."}))) @@ -413,7 +430,8 @@ _ (assert-can-see-client (:id context) (:transaction/client transaction) ) db (d/db conn) invoice-clients (set (map (comp :db/id :invoice/client #(d/entity db %)) autopay_invoice_ids)) - invoice-amount (reduce + 0.0 (map (comp :invoice/total #(d/entity db %)) autopay_invoice_ids))] + invoice-amount (reduce + 0.0 (map (comp :invoice/total #(d/entity db %)) autopay_invoice_ids)) + _ (assert-not-locked (:db/id (:transaction/client transaction)) (:transaction/date transaction))] (when (:transaction/payment transaction) (throw (ex-info "Transaction already linked" {:validation-error "Transaction already linked"}))) (when (or (> (count invoice-clients) 1) @@ -452,6 +470,7 @@ transaction (d-transactions/get-by-id transaction_id) _ (assert-can-see-client (:id context) (:transaction/client transaction) ) + _ (assert-not-locked (:db/id (:transaction/client transaction)) (:transaction/date transaction)) db (d/db conn) invoice-clients (set (map (comp :db/id :invoice/client #(d/entity db %)) unpaid_invoice_ids)) invoice-amount (reduce + 0.0 (map (comp :invoice/outstanding-balance #(d/entity db %)) unpaid_invoice_ids))] @@ -494,6 +513,7 @@ transaction_ids) + transaction_ids (all-ids-not-locked transaction_ids) transactions (transduce (comp (map d-transactions/get-by-id) diff --git a/src/clj/auto_ap/import/transactions.clj b/src/clj/auto_ap/import/transactions.clj index 3ceb3ca3..d888acfe 100644 --- a/src/clj/auto_ap/import/transactions.clj +++ b/src/clj/auto_ap/import/transactions.clj @@ -188,6 +188,11 @@ (-> bank-account :bank-account/start-date coerce/to-date-time)))) :not-ready + (and (:client/locked-until (:client/_bank-accounts bank-account)) + (not (t/after? (coerce/to-date-time (:transaction/date transaction)) + (coerce/to-date-time (:client/locked-until (:client/_bank-accounts bank-account)))))) + :not-ready + :else :import)) @@ -291,7 +296,7 @@ :db/id :bank-account/locations :bank-account/start-date - {:client/_bank-accounts [:client/code :client/locations :db/id]} ] + {:client/_bank-accounts [:client/code :client/locked-until :client/locations :db/id]} ] (:transaction/bank-account transaction)) extant (get (swap! extant-cache cache/through-cache (:transaction/bank-account transaction) get-existing) (:transaction/bank-account transaction)) diff --git a/src/cljs/auto_ap/views/pages/transactions.cljs b/src/cljs/auto_ap/views/pages/transactions.cljs index eee88869..bb97007e 100644 --- a/src/cljs/auto_ap/views/pages/transactions.cljs +++ b/src/cljs/auto_ap/views/pages/transactions.cljs @@ -119,6 +119,8 @@ (str "extant " extant)) (when-let [suppressed (:import-batch/suppressed result)] (str "suppressed " suppressed)) + (when-let [not-ready (:import-batch/not-ready result)] + (str "too early " not-ready)) (when-let [error (:validation-error result)] (str "errored " error))]) " transactions." diff --git a/test/clj/auto_ap/import/transactions_test.clj b/test/clj/auto_ap/import/transactions_test.clj index 6ce3aaf5..cca532c9 100644 --- a/test/clj/auto_ap/import/transactions_test.clj +++ b/test/clj/auto_ap/import/transactions_test.clj @@ -43,6 +43,15 @@ (sut/categorize-transaction (assoc base-transaction :transaction/date #inst "2020-01-02") (assoc bank-account :bank-account/start-date #inst "2020-01-01") {})))) + (t/testing "Should exclude a transaction before locked-until" + (t/is (= :not-ready + (sut/categorize-transaction (assoc base-transaction :transaction/date #inst "2020-01-01") + (assoc-in bank-account [:client/_bank-accounts :client/locked-until] #inst "2030-01-01") + {}))) + (t/is (= :import + (sut/categorize-transaction (assoc base-transaction :transaction/date #inst "2020-01-01") + (assoc-in bank-account [:client/_bank-accounts :client/locked-until] #inst "2010-01-01") + {})))) (t/testing "Should error a transaction without a client" (t/is (= :error (sut/categorize-transaction (dissoc base-transaction :transaction/client)