From 12c676629dcc65e14a25251f77956aee6fc62df6 Mon Sep 17 00:00:00 2001 From: Bryce Date: Thu, 14 Sep 2023 21:48:00 -0700 Subject: [PATCH] Bulk coding works on multiple clients --- src/clj/auto_ap/graphql/transactions.clj | 67 ++++++----- src/clj/auto_ap/import/transactions.clj | 2 + .../auto_ap/views/pages/transactions.cljs | 7 +- .../pages/transactions/bulk_updates.cljs | 1 - test/clj/auto_ap/import/transactions_test.clj | 1 + .../integration/graphql/transactions.clj | 110 ++++++++++++++++-- 6 files changed, 145 insertions(+), 43 deletions(-) diff --git a/src/clj/auto_ap/graphql/transactions.clj b/src/clj/auto_ap/graphql/transactions.clj index 9d2d930d..bf2fae76 100644 --- a/src/clj/auto_ap/graphql/transactions.clj +++ b/src/clj/auto_ap/graphql/transactions.clj @@ -24,7 +24,6 @@ [clj-time.coerce :as coerce] [clojure.set :as set] [clojure.string :as str] - [clojure.tools.logging :as log] [datomic.api :as dc] [iol-ion.tx :refer [random-tempid]] [com.brunobonacci.mulog :as mu] @@ -96,7 +95,11 @@ all-ids (->> (get-ids-matching-filters args) all-ids-not-locked)] - (log/info "Unapproving " (count all-ids) args) + (alog/info ::bulk-change-status + :count (count all-ids) + :sample (take 3 all-ids) + :status (:status args) + ) (audit-transact-batch (->> all-ids (mapv (fn [t] @@ -144,28 +147,31 @@ (defn bulk-code-transactions [context args _] (assert-admin (:id context)) - (when-not (:client_id args) + (when-not (seq (:clients context)) (throw (ex-info "Client is required" - {:validation-error "client is required"}))) - (let [args (assoc args :clients [(:client_id args)] :id (:id context)) - locations (pull-attr (dc/db conn) - :client/locations - (:client_id args)) + {:validation-error "Client is required"}))) + (let [args (assoc args :clients (:clients context) :id (:id context)) + client->locations (->> (:clients context) + (map :db/id ) + (dc/q + '[:find (pull ?e [:db/id :client/locations]) + :in $ [?e ...]] + (dc/db conn)) + (map (fn [[client]] + [(:db/id client) (:client/locations client)])) + (into {})) all-ids (all-ids-not-locked (get-ids-matching-filters args)) - transactions (pull-many (dc/db conn) [:db/id :transaction/amount] (vec all-ids)) + transactions (pull-many (dc/db conn) [:db/id :transaction/amount {:transaction/client [:db/id]}] (vec all-ids)) account-total (reduce + 0 (map (fn [x] (:percentage x)) (:accounts args)))] - (alog/info ::bulk-coding-transactions :count (count transactions) :sample (take 3 transactions)) - (when (and (seq (:accounts args)) (not (dollars= 1.0 account-total))) (let [error (str "Account total (" account-total ") does not reach 100%")] (throw (ex-info error {:validation-error error})))) - (doseq [a (:accounts args) :let [{:keys [:account/location :account/name]} (dc/pull (dc/db conn) [:account/location :account/name] @@ -173,20 +179,19 @@ (when (and location (not= location (:location a))) (let [err (str "Account " name " uses location " (:location a) ", but is supposed to be " location)] (throw (ex-info err {:validation-error err}) ))) - - (when (and (not location) - (not (get (into #{"Shared"} locations) - (:location a)))) - (let [err (str "Account " name " uses location " (:location a) ", but doesn't belong to the client.")] - (throw (ex-info err {:validation-error err}) )))) - - (log/info "Bulk coding " (count all-ids) args) + (doseq [[_ locations] client->locations] + (when (and (not location) + (not (get (into #{"Shared"} locations) + (:location a)))) + (let [err (str "Account " name " uses location " (:location a) ", but doesn't belong to the client.")] + (throw (ex-info err {:validation-error err}) ))))) (audit-transact-batch - (map (fn [i] - [:upsert-transaction (cond-> i - (:approval_status args) (assoc :transaction/approval-status (enum->keyword (:approval_status args) "transaction-approval-status")) - (:vendor args) (assoc :transaction/vendor (:vendor args)) - (seq (:accounts args)) (assoc :transaction/accounts (maybe-code-accounts i (:accounts args) locations)))]) + (map (fn [t] + (let [locations (client->locations (-> t :transaction/client :db/id))] + [:upsert-transaction (cond-> t + (:approval_status args) (assoc :transaction/approval-status (enum->keyword (:approval_status args) "transaction-approval-status")) + (:vendor args) (assoc :transaction/vendor (:vendor args)) + (seq (:accounts args)) (assoc :transaction/accounts (maybe-code-accounts t (:accounts args) locations)))])) transactions) (:id context)) {:message (str "Successfully coded " (count all-ids) " transactions.")})) @@ -198,7 +203,9 @@ all-ids (all-ids-not-locked (get-ids-matching-filters args)) db (dc/db conn)] - (log/info "Deleting " (count all-ids) args) + (alog/info ::bulk-delete-transactions + :count (count all-ids) + :sample (take 3 all-ids)) (audit-transact-batch (mapcat (fn [i] (let [transaction (dc/pull db [:transaction/payment @@ -261,7 +268,6 @@ _ (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->> (dc/q {:find ['?sp] :in ['$ '?payment] @@ -274,9 +280,8 @@ (every? #(instance? java.util.Date %))) ] - (log/info (:db/id payment)) + (alog/info ::unlinking :transaction (pr-str transaction) :autopay is-autopay-payment? :payment (pr-str payment)) - (log/info "Unlinking " transaction-id " from payment " payment " - Deleting the payment due to autopay? " is-autopay-payment?) (when (not= :payment-status/cleared (-> payment :payment/status :db/ident)) (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."}))) (if is-autopay-payment? @@ -452,7 +457,8 @@ autopay_invoice_ids) (:db/id (:transaction/bank-account transaction)) (:db/id (:transaction/client transaction)))] - (log/info "Adding a new payment" payment-tx) + (alog/info ::adding-payment-from-autopay-invoice + :payment (pr-str payment-tx)) (audit-transact payment-tx (:id context))) (solr/touch-with-ledger transaction_id) (-> (d-transactions/get-by-id transaction_id) @@ -598,7 +604,6 @@ :bulk_code_transactions {:type :message :args {:filters {:type :transaction_filters} :vendor {:type :id} - :client_id {:type :id} :approval_status {:type :transaction_approval_status} :accounts {:type '(list :edit_percentage_account)} :ids {:type '(list :id)}} diff --git a/src/clj/auto_ap/import/transactions.clj b/src/clj/auto_ap/import/transactions.clj index 679a12b0..19285064 100644 --- a/src/clj/auto_ap/import/transactions.clj +++ b/src/clj/auto_ap/import/transactions.clj @@ -19,6 +19,7 @@ (defn rough-match [client-id bank-account-id amount] (if (and client-id bank-account-id amount) (let [[matching-checks] (d-checks/get-graphql {:client-id client-id + :clients [client-id] :bank-account-id bank-account-id :amount (- amount) :status :payment-status/pending})] @@ -43,6 +44,7 @@ check-number (or (-> (d-checks/get-graphql {:client-id client-id + :clients [client-id] :bank-account-id bank-account-id :check-number check-number :amount (- amount) diff --git a/src/cljs/auto_ap/views/pages/transactions.cljs b/src/cljs/auto_ap/views/pages/transactions.cljs index 7ec355a5..2db451eb 100644 --- a/src/cljs/auto_ap/views/pages/transactions.cljs +++ b/src/cljs/auto_ap/views/pages/transactions.cljs @@ -178,10 +178,9 @@ [:div.buttons [:button.button.is-outlined.is-primary {:on-click (dispatch-event [::manual/opening])} "Manual Yodlee Import"] - (when client - [:button.button.is-warning {:on-click (dispatch-event [::bulk/code-requested checked params total]) - :disabled (not (seq checked))} - "Code"]) + [:button.button.is-warning {:on-click (dispatch-event [::bulk/code-requested checked params total]) + :disabled (not (seq checked))} + "Code"] [:button.button.is-danger {:on-click (dispatch-event [::delete-selected-requested params false]) :disabled (not (seq checked))} "Delete"] diff --git a/src/cljs/auto_ap/views/pages/transactions/bulk_updates.cljs b/src/cljs/auto_ap/views/pages/transactions/bulk_updates.cljs index 0e0eb1ed..7796367d 100644 --- a/src/cljs/auto_ap/views/pages/transactions/bulk_updates.cljs +++ b/src/cljs/auto_ap/views/pages/transactions/bulk_updates.cljs @@ -42,7 +42,6 @@ :operation/name "BulkCodeTransactions"} :venia/queries [[:bulk-code-transactions {:filters (some-> checked-params data-params->query-params) - :client_id (:id client) :ids specific-transactions :vendor (:id (:vendor data)) :approval-status (:transaction-approval-status data) diff --git a/test/clj/auto_ap/import/transactions_test.clj b/test/clj/auto_ap/import/transactions_test.clj index af0a240d..ad982856 100644 --- a/test/clj/auto_ap/import/transactions_test.clj +++ b/test/clj/auto_ap/import/transactions_test.clj @@ -123,6 +123,7 @@ (let [transaction-result (sut/transaction->txs (assoc base-transaction :transaction/description-original "CHECK 10001" + :transaction/check-number 10001 :transaction/amount -30.0) (dc/pull (dc/db conn ) sut/bank-account-pull bank-account-id) noop-rule)] diff --git a/test/clj/auto_ap/integration/graphql/transactions.clj b/test/clj/auto_ap/integration/graphql/transactions.clj index 305b45d8..64325bbd 100644 --- a/test/clj/auto_ap/integration/graphql/transactions.clj +++ b/test/clj/auto_ap/integration/graphql/transactions.clj @@ -48,7 +48,8 @@ :transaction/approval-status :transaction-approval-status/approved :transaction/bank-account "test-bank-account-id")])] (is (= "Succesfully changed 1 transactions to be unapproved." - (:message (sut/bulk-change-status {:id (admin-token)} {:filters {:client_id test-client-id} + (:message (sut/bulk-change-status {:id (admin-token) + :clients [{:db/id test-client-id}]} {:filters {} :status :unapproved} nil)))) (is (= :transaction-approval-status/unapproved (:db/ident (:transaction/approval-status (dc/pull (dc/db conn) '[{:transaction/approval-status [:db/ident]}] transaction-id))))) @@ -68,9 +69,9 @@ :transaction/bank-account "test-bank-account-id" :transaction/amount 40.0)])] (is (= "Successfully coded 1 transactions." - (:message (sut/bulk-code-transactions {:id (admin-token)} + (:message (sut/bulk-code-transactions {:id (admin-token) + :clients [{:db/id test-client-id}]} {:filters {:client_id test-client-id} - :client_id test-client-id :vendor test-vendor-id :approval_status :unapproved :accounts [{:account_id test-account-id @@ -87,16 +88,111 @@ :transaction/accounts [:transaction-account/account :transaction-account/location :transaction-account/amount]}] - transaction-id)))))) + transaction-id))) + + (testing "for more than one client" + (let [{:strs [transaction-id-1 + transaction-id-2 + test-client-id-2 + test-client-id]} (setup-test-data [ + (test-transaction :db/id "transaction-id-1" + :transaction/client "test-client-id" + :transaction/bank-account "test-bank-account-id" + :transaction/amount 40.0) + (test-transaction :db/id "transaction-id-2" + :transaction/client "test-client-id-2" + :transaction/bank-account "test-bank-account-id-2" + :transaction/amount 40.0) + (test-client :db/id "test-client-id-2" + :client/locations ["GR"]) + (test-bank-account :db/id "test-bank-account-id-2")])] + (is (= "Successfully coded 2 transactions." + (:message (sut/bulk-code-transactions {:id (admin-token) + :clients [{:db/id test-client-id} + {:db/id test-client-id-2}]} + {:filters {} + :vendor test-vendor-id + :approval_status :unapproved + :accounts [{:account_id test-account-id + :location "Shared" + :percentage 1.0}]} nil)))) + + (is (= #:transaction{:vendor {:db/id test-vendor-id} + :approval-status {:db/ident :transaction-approval-status/unapproved} + :accounts [#:transaction-account{:account {:db/id test-account-id} + :location "DT" + :amount 40.0}]} + (dc/pull (dc/db conn) '[:transaction/vendor + {:transaction/approval-status [:db/ident] + :transaction/accounts [:transaction-account/account + :transaction-account/location + :transaction-account/amount]}] + transaction-id-1))) + + (is (= #:transaction{:vendor {:db/id test-vendor-id} + :approval-status {:db/ident :transaction-approval-status/unapproved} + :accounts [#:transaction-account{:account {:db/id test-account-id} + :location "GR" + :amount 40.0}]} + (dc/pull (dc/db conn) '[:transaction/vendor + {:transaction/approval-status [:db/ident] + :transaction/accounts [:transaction-account/account + :transaction-account/location + :transaction-account/amount]}] + transaction-id-2)))) + + (testing "should reject a location that doesnt exist" + (let [{:strs [test-client-id-1 + test-client-id-2]} (setup-test-data [ + (test-transaction :db/id "transaction-id-1" + :transaction/client "test-client-id-1" + :transaction/bank-account "test-bank-account-id" + :transaction/amount 40.0) + (test-transaction :db/id "transaction-id-2" + :transaction/client "test-client-id-2" + :transaction/bank-account "test-bank-account-id-2" + :transaction/amount 40.0) + (test-client :db/id "test-client-id-1" + :client/locations ["DT" "BOTH"]) + (test-client :db/id "test-client-id-2" + :client/locations ["GR" "BOTH"]) + (test-bank-account :db/id "test-bank-account-id-2")])] + (is (thrown? Exception (sut/bulk-code-transactions {:id (admin-token) + :clients [{:db/id test-client-id} + {:db/id test-client-id-2}]} + {:filters {} + :vendor test-vendor-id + :approval_status :unapproved + :accounts [{:account_id test-account-id + :location "OG" + :percentage 1.0}]} nil))) + (is (thrown? Exception (sut/bulk-code-transactions {:id (admin-token) + :clients [{:db/id test-client-id} + {:db/id test-client-id-2}]} + {:filters {} + :vendor test-vendor-id + :approval_status :unapproved + :accounts [{:account_id test-account-id + :location "DT" + :percentage 1.0}]} nil))) + (is (sut/bulk-code-transactions {:id (admin-token) + :clients [{:db/id test-client-id-1} + {:db/id test-client-id-2}]} + {:filters {} + :vendor test-vendor-id + :approval_status :unapproved + :accounts [{:account_id test-account-id + :location "BOTH" + :percentage 1.0}]} nil)))))))) (deftest edit-transactions (testing "Should edit transactions" (let [{:strs [transaction-id test-account-id test-vendor-id]} (setup-test-data [(test-transaction :db/id "transaction-id" - :transaction/client "test-client-id" - :transaction/bank-account "test-bank-account-id" - :transaction/amount 40.0)])] + :transaction/client "test-client-id" + :transaction/bank-account "test-bank-account-id" + :transaction/amount 40.0)])] (sut/edit-transaction {:id (admin-token)} {:transaction {:id transaction-id :vendor_id test-vendor-id