diff --git a/src/clj/auto_ap/graphql/invoices.clj b/src/clj/auto_ap/graphql/invoices.clj index 2cf60bab..bbe4c5bc 100644 --- a/src/clj/auto_ap/graphql/invoices.clj +++ b/src/clj/auto_ap/graphql/invoices.clj @@ -23,7 +23,8 @@ [clj-time.core :as time] [clojure.set :as set] [clojure.tools.logging :as log] - [datomic.api :as d])) + [datomic.api :as d] + [manifold.deferred :as de])) (defn ->graphql [invoice user ] (if (= "admin" (:user/role user)) @@ -127,7 +128,7 @@ set)] (set/difference existing-ids specified-ids))) -(defn assert-valid-expense-accounts [expense_accounts] +(defn assert-valid-expense-accounts [expense_accounts vendor_id] (doseq [expense-account expense_accounts :let [account (d/entity (d/db conn) (:account_id expense-account))]] (when (empty? (:location expense-account)) @@ -140,6 +141,14 @@ (throw (ex-info err {:validation-error err})))) + (when (and (= :allowance/denied + (:account/invoice-allowance account)) + (not= (:db/id (:vendor/default-account (d/entity (d/db conn) vendor_id))) + (:db/id account))) + (let [err (str "Account isn't allowed for invoice use.")] + (throw (ex-info err + {:validation-error err})))) + (when (and (empty? (:account/location account)) (= "A" (:location expense-account))) (let [err (str "Account uses location '" (:location expense-account) "', which is reserved for liabilities, equities, and assets.")] @@ -160,12 +169,12 @@ -(defn add-invoice [context {{:keys [expense_accounts client_id] :as in} :invoice} _] +(defn add-invoice [context {{:keys [expense_accounts client_id vendor_id] :as in} :invoice} _] (assert-no-conflicting in) (assert-can-see-client (:id context) client_id) (assert-not-locked client_id (:date in)) - (assert-valid-expense-accounts expense_accounts) + (assert-valid-expense-accounts expense_accounts vendor_id) (assert-invoice-amounts-add-up in) (let [transaction-result (transact-with-ledger [(add-invoice-transaction in)] (:id context))] @@ -178,12 +187,12 @@ -(defn add-and-print-invoice [context {{:keys [total client_id] :as in} :invoice bank-account-id :bank_account_id type :type} _] +(defn add-and-print-invoice [context {{:keys [total client_id vendor_id] :as in} :invoice bank-account-id :bank_account_id type :type} _] (assert-no-conflicting in) (assert-can-see-client (:id context) client_id) (assert-bank-account-belongs client_id bank-account-id) (assert-not-locked client_id (:date in)) - (assert-valid-expense-accounts (:expense_accounts in)) + (assert-valid-expense-accounts (:expense_accounts in) vendor_id) (assert-invoice-amounts-add-up in) (let [transaction-result (transact-with-ledger [(add-invoice-transaction in)] (:id context))] (-> (gq-checks/print-checks-internal [{:invoice-id (get-in transaction-result [:tempids "invoice"]) @@ -194,7 +203,7 @@ (:id context)) u/->graphql))) -(defn edit-invoice [context {{:keys [id due invoice_number total date expense_accounts scheduled_payment] :as in} :invoice} _] +(defn edit-invoice [context {{:keys [id due invoice_number vendor_id total date expense_accounts scheduled_payment] :as in} :invoice} _] (let [invoice (d-invoices/get-by-id id) _ (when (seq (d-invoices/find-conflicting {:db/id id :invoice/invoice-number invoice_number @@ -206,7 +215,7 @@ _ (assert-can-see-client (:id context) (:db/id (:invoice/client invoice))) deleted (deleted-expense-accounts invoice expense_accounts) _ (assert-not-locked (:db/id (:invoice/client invoice)) (:date in)) - _ (assert-valid-expense-accounts expense_accounts) + _ (assert-valid-expense-accounts expense_accounts vendor_id) _ (assert-invoice-amounts-add-up in) updated-invoice (cond-> {:db/id id diff --git a/test/clj/auto_ap/integration/graphql/invoices.clj b/test/clj/auto_ap/integration/graphql/invoices.clj new file mode 100644 index 00000000..29a9adec --- /dev/null +++ b/test/clj/auto_ap/integration/graphql/invoices.clj @@ -0,0 +1,101 @@ +(ns auto-ap.integration.graphql.invoices + (:require + [auto-ap.datomic :refer [conn uri]] + [auto-ap.datomic.migrate :as m] + [auto-ap.time-reader] + [auto-ap.graphql.invoices :as sut] + [clj-time.core :as time] + [clojure.test :as t :refer [deftest is testing use-fixtures]] + [datomic.api :as d])) + +(defn wrap-setup + [f] + (with-redefs [auto-ap.datomic/uri "datomic:mem://datomic-transactor:4334/invoice"] + (d/create-database uri) + (with-redefs [auto-ap.datomic/conn (d/connect uri)] + (m/migrate conn) + + (f) + (d/release conn) + (d/delete-database uri)))) + +#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]} +(defn admin-token [] + {:user "TEST ADMIN" + :exp (time/plus (time/now) (time/days 1)) + :user/role "admin" + :user/name "TEST ADMIN"}) + +#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]} +(defn user-token [client-id] + {:user "TEST USER" + :exp (time/plus (time/now) (time/days 1)) + :user/role "user" + :user/name "TEST USER" + :user/clients [{:db/id client-id}]}) + + +(use-fixtures :each wrap-setup) + +(deftest test-add-invoice + (testing "It should Add an invoice" + (let [{:strs [vendor-id client-id account-id]} + (:tempids @(d/transact conn [{:client/code "ABC" + :db/id "client-id" + :client/locations ["DT"]} + {:vendor/name "Vendy" + :db/id "vendor-id" + :vendor/default-account "account-id"} + {:account/name "Account" + :account/numeric-code 123 + :account/invoice-allowance :allowance/allowed + :db/id "account-id"}]))] + (is (some? (sut/add-invoice {:id (admin-token)} + {:invoice {:client_id client-id + :vendor_id vendor-id + :invoice_number "123" + :date #clj-time/date-time "2022-01-01" + :total 10.00 + :expense_accounts [{:amount 10.0 + :location "DT" + :account_id account-id}]}} + nil))) + (testing "It should prevent an expense account that isn't allowed" + (let [{:strs [denied-account-id]} + (:tempids @(d/transact conn [ + {:account/name "Account" + :account/numeric-code 123 + :account/invoice-allowance :allowance/denied + :db/id "denied-account-id"}]))] + (is (thrown? Exception (sut/add-invoice {:id (admin-token)} + {:invoice {:client_id client-id + :vendor_id vendor-id + :invoice_number "789" + :date #clj-time/date-time "2022-01-01" + :total 10.00 + :expense_accounts [{:amount 10.0 + :location "DT" + :account_id denied-account-id}]}} + nil))))) + + (testing "It should allow an expense account that is valid for the vendor" + (let [{:strs [vendor-account-id vendor-2]} + (:tempids @(d/transact conn [ + {:account/name "Account" + :account/numeric-code 123 + :account/invoice-allowance :allowance/denied + :account/vendor-allowance :allowance/allowed + :db/id "vendor-account-id"} + {:vendor/name "Testy" + :vendor/default-account "vendor-account-id" + :db/id "vendor-2"}]))] + (is (some? (sut/add-invoice {:id (admin-token)} + {:invoice {:client_id client-id + :vendor_id vendor-2 + :invoice_number "456" + :date #clj-time/date-time "2022-01-01" + :total 10.00 + :expense_accounts [{:amount 10.0 + :location "DT" + :account_id vendor-account-id}]}} + nil))))))))