448 lines
31 KiB
Clojure
448 lines
31 KiB
Clojure
(ns auto-ap.integration.graphql.transactions
|
|
(:require
|
|
[auto-ap.datomic :refer [conn]]
|
|
[auto-ap.graphql.transactions :as sut]
|
|
[auto-ap.integration.util
|
|
:refer [admin-token
|
|
setup-test-data
|
|
test-bank-account
|
|
test-client
|
|
test-payment
|
|
test-transaction
|
|
test-transaction-rule
|
|
test-invoice
|
|
user-token
|
|
wrap-setup]]
|
|
[clojure.test :as t :refer [deftest is testing use-fixtures]]
|
|
[datomic.api :as dc]))
|
|
|
|
(use-fixtures :each wrap-setup)
|
|
|
|
(deftest get-transaction-page
|
|
(testing "Should list transactions"
|
|
(let [{:strs [transaction-id
|
|
test-client-id]} (setup-test-data [(test-transaction :db/id "transaction-id"
|
|
:transaction/client "test-client-id"
|
|
:transaction/bank-account "test-bank-account-id")])]
|
|
(is (= 1 (:total (sut/get-transaction-page {:id (admin-token)} {} nil))))
|
|
(is (= transaction-id (:id (first (:data (sut/get-transaction-page {:id (admin-token)} {} nil))))))
|
|
(testing "Should only show transactions you have access to"
|
|
(is (= 1 (:total (sut/get-transaction-page {:id (admin-token)} {} nil))))
|
|
(is (= 1 (:total (sut/get-transaction-page {:id (user-token test-client-id)} {} nil))))
|
|
(is (= 0 (:total (sut/get-transaction-page {:id (user-token 1)} {} nil))))
|
|
|
|
(is (= 1 (:total (sut/get-transaction-page {:id (admin-token)} {:filters {:client_id test-client-id}} nil))))
|
|
(is (= 0 (:total (sut/get-transaction-page {:id (admin-token)} {:filters {:client_id 1}} nil))))
|
|
(is (= 1 (:total (sut/get-transaction-page {:id (user-token test-client-id)} {:filters {:client_id test-client-id}} nil))))
|
|
(is (= 0 (:total (sut/get-transaction-page {:id (user-token 1)} {:filters {:client_id test-client-id}} nil)))))
|
|
|
|
(testing "Should only show potential duplicates if filtered enough"
|
|
(is (thrown? Exception (:total (sut/get-transaction-page {:id (admin-token)} {:filters {:potential_duplicates true}} nil))))))))
|
|
|
|
|
|
(deftest bulk-change-status
|
|
(testing "Should change status of multiple transactions"
|
|
(let [{:strs [transaction-id
|
|
test-client-id]} (setup-test-data [(test-transaction :db/id "transaction-id"
|
|
:transaction/client "test-client-id"
|
|
: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)
|
|
: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)))))
|
|
|
|
(testing "Only admins should be able to change the status"
|
|
(is (thrown? Exception (sut/bulk-change-status {:id (user-token test-client-id)}
|
|
{:filters {:client_id test-client-id}
|
|
:status :unapproved} nil)))))))
|
|
|
|
(deftest bulk-code-transactions
|
|
(testing "Should code transactions"
|
|
(let [{:strs [transaction-id
|
|
test-client-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)])]
|
|
(is (= "Successfully coded 1 transactions."
|
|
(:message (sut/bulk-code-transactions {:id (admin-token)
|
|
:clients [{:db/id test-client-id}]}
|
|
{:filters {:client_id test-client-id}
|
|
:vendor test-vendor-id
|
|
:approval_status :unapproved
|
|
:accounts [{:account_id test-account-id
|
|
:location "DT"
|
|
: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)))
|
|
|
|
(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)])]
|
|
(sut/edit-transaction {:id (admin-token)}
|
|
{:transaction {:id transaction-id
|
|
:vendor_id test-vendor-id
|
|
:approval_status :approved
|
|
:accounts [{:account_id test-account-id
|
|
:location "DT"
|
|
:amount 40.0}]}} nil)
|
|
|
|
(is (= #:transaction{:vendor {:db/id test-vendor-id}
|
|
:approval-status {:db/ident :transaction-approval-status/approved}
|
|
: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)))
|
|
|
|
(testing "Should prevent saves with bad accounts"
|
|
(is (thrown? Exception
|
|
(sut/edit-transaction {:id (admin-token)}
|
|
{:transaction {:id transaction-id
|
|
:vendor_id test-vendor-id
|
|
:approval_status :approved
|
|
:accounts [{:account_id test-account-id
|
|
:location "DT"
|
|
:amount 20.0}]}} nil)))))))
|
|
|
|
|
|
(deftest match-transaction
|
|
(testing "Should link a transaction to a payment, mark it as accounts payable"
|
|
(let [{:strs [transaction-id
|
|
test-vendor-id
|
|
accounts-payable-id
|
|
payment-id]} (setup-test-data [(test-transaction :db/id "transaction-id"
|
|
:transaction/client "test-client-id"
|
|
:transaction/bank-account "test-bank-account-id"
|
|
:transaction/amount -50.0)
|
|
(test-payment :db/id "payment-id"
|
|
:payment/client "test-client-id"
|
|
:payment/vendor "test-vendor-id"
|
|
:payment/bank-account "test-bank-account-id"
|
|
:payment/amount 50.0)])]
|
|
(sut/match-transaction {:id (admin-token)} {:transaction_id transaction-id :payment_id payment-id} nil)
|
|
(is (= #:transaction{:vendor {:db/id test-vendor-id}
|
|
:approval-status {:db/ident :transaction-approval-status/approved}
|
|
:payment {:db/id payment-id}
|
|
:accounts [#:transaction-account{:account {:account/name "Accounts Payable"}
|
|
:location "A"
|
|
:amount 50.0}]}
|
|
(dc/pull (dc/db conn) '[:transaction/vendor
|
|
:transaction/payment
|
|
{:transaction/approval-status [:db/ident]
|
|
:transaction/accounts [{:transaction-account/account [:account/name]}
|
|
:transaction-account/location
|
|
:transaction-account/amount]}]
|
|
transaction-id)))))
|
|
|
|
(testing "Should prevent linking a payment if they don't match"
|
|
(let [{:strs [transaction-id
|
|
mismatched-amount-payment-id
|
|
mismatched-bank-account-payment-id]}
|
|
(setup-test-data [(test-transaction :db/id "transaction-id"
|
|
:transaction/client "test-client-id"
|
|
:transaction/bank-account "test-bank-account-id"
|
|
:transaction/amount -50.0)
|
|
(test-payment :db/id "mismatched-amount-payment-id"
|
|
:payment/client "test-client-id"
|
|
:payment/vendor "test-vendor-id"
|
|
:payment/bank-account "test-bank-account-id"
|
|
:payment/amount 30.0)
|
|
|
|
(test-client :db/id "mismatched-client-id"
|
|
:client/bank-accounts [(test-bank-account :db/id "mismatched-bank-account-id")])
|
|
(test-payment :db/id "mismatched-bank-account-payment-id"
|
|
:payment/client "mismatched-client-id"
|
|
:payment/vendor "test-vendor-id"
|
|
:payment/bank-account "mismatched-bank-account-id"
|
|
:payment/amount 50.0)])]
|
|
(is (thrown? Exception (sut/match-transaction {:id (admin-token)} {:transaction_id transaction-id :payment_id mismatched-amount-payment-id} nil)))
|
|
(is (thrown? Exception (sut/match-transaction {:id (admin-token)} {:transaction_id transaction-id :payment_id mismatched-bank-account-payment-id} nil)))
|
|
)))
|
|
|
|
(deftest match-transaction-autopay-invoices
|
|
(testing "Should link transaction to a set of autopaid invoices"
|
|
(let [{:strs [transaction-id
|
|
test-vendor-id
|
|
invoice-1
|
|
invoice-2
|
|
]} (setup-test-data [(test-transaction :db/id "transaction-id"
|
|
:transaction/amount -50.0)
|
|
(test-invoice :db/id "invoice-1"
|
|
:invoice/total 30.0)
|
|
(test-invoice :db/id "invoice-2"
|
|
:invoice/total 20.0)])]
|
|
(sut/match-transaction-autopay-invoices {:id (admin-token)} {:transaction_id transaction-id :autopay_invoice_ids [invoice-1 invoice-2]} nil)
|
|
(let [result (dc/pull (dc/db conn) '[:transaction/vendor
|
|
{:transaction/payment [:db/id {:payment/status [:db/ident]}]}
|
|
{:transaction/approval-status [:db/ident]
|
|
:transaction/accounts [:transaction-account/account
|
|
:transaction-account/location
|
|
:transaction-account/amount]}
|
|
]
|
|
transaction-id)]
|
|
(testing "should have created a payment"
|
|
(is (some? (:transaction/payment result)))
|
|
(is (= :payment-status/cleared (-> result
|
|
:transaction/payment
|
|
:payment/status
|
|
:db/ident)))
|
|
(is (= :transaction-approval-status/approved (-> result :transaction/approval-status :db/ident))))
|
|
(testing "Should have completed the invoice"
|
|
(is (= :invoice-status/paid (->> invoice-1
|
|
(dc/pull (dc/db conn) '[{:invoice/status [:db/ident]}])
|
|
:invoice/status
|
|
:db/ident)))
|
|
(is (= :invoice-status/paid (->> invoice-2
|
|
(dc/pull (dc/db conn) '[{:invoice/status [:db/ident]}])
|
|
:invoice/status
|
|
:db/ident)))))))
|
|
|
|
(testing "Should prevent a transaction from linking to an incorrectly balanced invoice"
|
|
(let [{:strs [transaction-id
|
|
test-vendor-id
|
|
invoice-1
|
|
invoice-2
|
|
]} (setup-test-data [(test-transaction :db/id "transaction-id"
|
|
:transaction/amount -50.0)
|
|
(test-invoice :db/id "invoice-1"
|
|
:invoice/total 30.0)])]
|
|
(is (thrown? Exception (sut/match-transaction-autopay-invoices {:id (admin-token)} {:transaction_id transaction-id :autopay_invoice_ids [invoice-1 invoice-2]} nil))))))
|
|
|
|
(deftest match-transaction-unpaid-invoices
|
|
(testing "TODO exact same as above, no need for two endpaints"
|
|
(let [{:strs [transaction-id
|
|
test-vendor-id
|
|
invoice-1
|
|
invoice-2
|
|
]} (setup-test-data [(test-transaction :db/id "transaction-id"
|
|
:transaction/amount -50.0)
|
|
(test-invoice :db/id "invoice-1"
|
|
:invoice/outstanding-balance 30.0 ;; TODO this part is a little different
|
|
:invoice/total 30.0)
|
|
(test-invoice :db/id "invoice-2"
|
|
:invoice/outstanding-balance 20.0 ;; TODO this part is a little different
|
|
:invoice/total 20.0)])]
|
|
(sut/match-transaction-unpaid-invoices {:id (admin-token)} {:transaction_id transaction-id :unpaid_invoice_ids [invoice-1 invoice-2]} nil)
|
|
(let [result (dc/pull (dc/db conn) '[:transaction/vendor
|
|
{:transaction/payment [:db/id {:payment/status [:db/ident]}]}
|
|
{:transaction/approval-status [:db/ident]
|
|
:transaction/accounts [:transaction-account/account
|
|
:transaction-account/location
|
|
:transaction-account/amount]}
|
|
]
|
|
transaction-id)]
|
|
(testing "should have created a payment"
|
|
(is (some? (:transaction/payment result)))
|
|
(is (= :payment-status/cleared (-> result
|
|
:transaction/payment
|
|
:payment/status
|
|
:db/ident)))
|
|
(is (= :transaction-approval-status/approved (-> result :transaction/approval-status :db/ident))))
|
|
(testing "Should have completed the invoice"
|
|
(is (= :invoice-status/paid (->> invoice-1
|
|
(dc/pull (dc/db conn) '[{:invoice/status [:db/ident]}])
|
|
:invoice/status
|
|
:db/ident)))
|
|
(is (= :invoice-status/paid (->> invoice-2
|
|
(dc/pull (dc/db conn) '[{:invoice/status [:db/ident]}])
|
|
:invoice/status
|
|
:db/ident)))))))
|
|
|
|
(testing "Should prevent a transaction from linking to an incorrectly balanced invoice"
|
|
(let [{:strs [transaction-id
|
|
test-vendor-id
|
|
invoice-1
|
|
invoice-2
|
|
]} (setup-test-data [(test-transaction :db/id "transaction-id"
|
|
:transaction/amount -50.0)
|
|
(test-invoice :db/id "invoice-1"
|
|
:invoice/total 30.0)])]
|
|
(is (thrown? Exception (sut/match-transaction-autopay-invoices {:id (admin-token)} {:transaction_id transaction-id :autopay_invoice_ids [invoice-1 invoice-2]} nil))))))
|
|
|
|
|
|
(deftest match-transaction-rules
|
|
(testing "Should match transactions without linked payments"
|
|
(let [{:strs [transaction-id
|
|
transaction-rule-id
|
|
]} (setup-test-data [(test-transaction :db/id "transaction-id"
|
|
:transaction/amount -50.0)
|
|
(test-transaction-rule :db/id "transaction-rule-id"
|
|
:transaction-rule/client "test-client-id"
|
|
:transaction-rule/transaction-approval-status :transaction-approval-status/excluded
|
|
:transaction-rule/description ".*"
|
|
)])]
|
|
(is (= transaction-rule-id (-> (sut/match-transaction-rules {:id (admin-token)} {:transaction_ids [transaction-id] :transaction_rule_id transaction-rule-id} nil)
|
|
first
|
|
:matched_rule
|
|
:id)))
|
|
|
|
(testing "Should apply statuses"
|
|
(is (= :excluded
|
|
(-> (sut/match-transaction-rules {:id (admin-token)}
|
|
{:transaction_ids [transaction-id] :transaction_rule_id transaction-rule-id}
|
|
nil)
|
|
first
|
|
:approval_status
|
|
))))))
|
|
|
|
(testing "Should not apply to transactions if they don't match"
|
|
(let [{:strs [transaction-id
|
|
transaction-rule-id]}
|
|
(setup-test-data [(test-transaction :db/id "transaction-id"
|
|
:transaction/amount -50.0)
|
|
(test-transaction-rule :db/id "transaction-rule-id"
|
|
:transaction-rule/description "NOMATCH"
|
|
)])]
|
|
(is (thrown? Exception (-> (sut/match-transaction-rules {:id (admin-token)} {:transaction_ids [transaction-id] :transaction_rule_id transaction-rule-id} nil)
|
|
first
|
|
:matched_rule
|
|
:id)))))
|
|
(testing "Should not apply to transactions if they are already matched"
|
|
(let [{:strs [transaction-id
|
|
transaction-rule-id]}
|
|
(setup-test-data [(test-payment :db/id "extant-payment-id")
|
|
(test-transaction :db/id "transaction-id"
|
|
:transaction/payment {:db/id "extant-payment-id"}
|
|
:transaction/amount -50.0)
|
|
(test-transaction-rule :db/id "transaction-rule-id"
|
|
:transaction-rule/description ".*"
|
|
)])]
|
|
(is (thrown? Exception (-> (sut/match-transaction-rules {:id (admin-token)} {:transaction_ids [transaction-id] :transaction_rule_id transaction-rule-id} nil)
|
|
first
|
|
:matched_rule
|
|
:id)))))
|
|
|
|
(testing "Should apply to all transactions even without a transaction id"
|
|
(let [{:strs [transaction-id
|
|
transaction-rule-id]}
|
|
(setup-test-data [(test-transaction :db/id "transaction-id"
|
|
:transaction/description-original "MATCH"
|
|
:transaction/amount -50.0)
|
|
(test-transaction-rule :db/id "transaction-rule-id"
|
|
:transaction-rule/description ".*"
|
|
)])]
|
|
(sut/match-transaction-rules {:id (admin-token)} {:all true
|
|
:transaction_rule_id transaction-rule-id} nil)
|
|
(= {:transaction/matched-rule {:db/id transaction-rule-id}}
|
|
(dc/pull (dc/db conn) '[:transaction/matched-rule] transaction-id)))))
|
|
|