test(invoice): add integration tests for invoice behaviors
- Fix schema ordering: move :journal-entry-line/running-balance to schema.edn - Add invoice_behaviors_test.clj covering: - Permission gates (26.5, 26.6, 26.8) - Lock date blocking (27.1, 27.3) - New invoice validation (8.1, 8.5) - Edit invoice (11.1, 11.3) - Bulk edit (15.4) - Single/bulk void (16.3, 16.4, 17.1) - Unvoid restoring from history (18.1) - Undo autopay (19.1) - Invoice list filtering (2.6, 2.8, 2.10, 2.14) - Invoice list sorting (3.5, 3.7, 3.10) - Invoice list pagination (4.1, 4.3) - Legacy route redirects (28.1) - Mock Solr in wrap-setup fixture to prevent Connection refused - Fix setup-test-data to merge user-provided entities with defaults - Fix InMemSolrClient.index_documents to handle entity IDs - Fix ezcater_xls test to use dynamic entity IDs - Update invoice.md behavior checklist with completed items
This commit is contained in:
606
test/clj/auto_ap/integration/invoice_behaviors_test.clj
Normal file
606
test/clj/auto_ap/integration/invoice_behaviors_test.clj
Normal file
@@ -0,0 +1,606 @@
|
||||
(ns auto-ap.integration.invoice-behaviors-test
|
||||
(:require
|
||||
[auto-ap.datomic :as datomic]
|
||||
[auto-ap.datomic.clients :refer [rebuild-search-index]]
|
||||
[auto-ap.graphql.invoices :as gql-invoices]
|
||||
[auto-ap.integration.util :refer [admin-token setup-test-data test-account
|
||||
test-client test-invoice test-vendor
|
||||
user-token user-token-no-access wrap-setup]]
|
||||
[auto-ap.routes.invoices :as route-invoices]
|
||||
[auto-ap.ssr.invoices :as ssr-invoices]
|
||||
[auto-ap.time-reader]
|
||||
[clj-time.coerce :as coerce]
|
||||
[clj-time.core :as time]
|
||||
[clojure.test :as t :refer [deftest is testing use-fixtures]]
|
||||
[datomic.api :as dc]))
|
||||
|
||||
(use-fixtures :each wrap-setup)
|
||||
|
||||
;; ============================================================================
|
||||
;; Permission Behaviors (26.x, 26.8)
|
||||
;; ============================================================================
|
||||
|
||||
(deftest test-permission-client-access
|
||||
(testing "Behavior 26.8: It should verify the user has access to the invoice's client before any mutation"
|
||||
(let [{:strs [test-client-id test-vendor-id test-account-id]}
|
||||
(setup-test-data [])]
|
||||
;; Block creation for user without client access
|
||||
(is (thrown? Exception (gql-invoices/add-invoice
|
||||
{:id (user-token-no-access)}
|
||||
{:invoice {:client_id test-client-id
|
||||
:vendor_id test-vendor-id
|
||||
:invoice_number "NO-ACCESS"
|
||||
:date #clj-time/date-time "2022-01-01"
|
||||
:total 10.00
|
||||
:expense_accounts [{:amount 10.0
|
||||
:location "DT"
|
||||
:account_id test-account-id}]}}
|
||||
nil)))
|
||||
;; Create invoice as admin, then block edit/void for user without client access
|
||||
(let [invoice (gql-invoices/add-invoice
|
||||
{:id (admin-token)}
|
||||
{:invoice {:client_id test-client-id
|
||||
:vendor_id test-vendor-id
|
||||
:invoice_number "NO-ACCESS-EDIT"
|
||||
:date #clj-time/date-time "2022-01-01"
|
||||
:total 100.00
|
||||
:expense_accounts [{:amount 100.0
|
||||
:location "DT"
|
||||
:account_id test-account-id}]}}
|
||||
nil)]
|
||||
(is (thrown? Exception (gql-invoices/edit-invoice
|
||||
{:id (user-token-no-access)}
|
||||
{:invoice {:id (:id invoice)
|
||||
:invoice_number "NO-ACCESS-EDIT-2"
|
||||
:date #clj-time/date-time "2022-01-01"
|
||||
:total 100.00
|
||||
:expense_accounts [{:amount 100.0
|
||||
:location "DT"
|
||||
:account_id test-account-id}]}}
|
||||
nil)))
|
||||
(is (thrown? Exception (gql-invoices/void-invoice
|
||||
{:id (user-token-no-access)}
|
||||
{:invoice_id (:id invoice)}
|
||||
nil)))))))
|
||||
|
||||
(deftest test-permission-bulk-void
|
||||
(testing "Behavior 26.5: It should block bulk delete for non-admin users"
|
||||
(let [{:strs [test-client-id]}
|
||||
(setup-test-data [])]
|
||||
(is (thrown? Exception (gql-invoices/void-invoices
|
||||
{:id (user-token test-client-id)}
|
||||
{:filters {:client_id test-client-id}}
|
||||
nil))))))
|
||||
|
||||
(deftest test-permission-bulk-edit
|
||||
(testing "Behavior 26.6: It should block bulk edit for users without :bulk-edit permission"
|
||||
(let [{:strs [test-client-id test-account-id]}
|
||||
(setup-test-data [])]
|
||||
(is (thrown? Exception (gql-invoices/bulk-change-invoices
|
||||
{:id (user-token test-client-id)}
|
||||
{:client_id test-client-id
|
||||
:filters {:client_id test-client-id}
|
||||
:accounts [{:percentage 1.0
|
||||
:account_id test-account-id
|
||||
:location "DT"}]}
|
||||
nil))))))
|
||||
|
||||
;; ============================================================================
|
||||
;; Lock Date Behaviors (27.x)
|
||||
;; ============================================================================
|
||||
|
||||
(deftest test-lock-date-edit
|
||||
(testing "Behavior 27.1: It should block editing invoices dated before the client's locked-until date"
|
||||
(let [{:strs [test-client-id test-vendor-id test-account-id]}
|
||||
(setup-test-data [])]
|
||||
(let [invoice (gql-invoices/add-invoice
|
||||
{:id (admin-token)}
|
||||
{:invoice {:client_id test-client-id
|
||||
:vendor_id test-vendor-id
|
||||
:invoice_number "LOCK-EDIT"
|
||||
:date #clj-time/date-time "2022-01-01"
|
||||
:total 100.00
|
||||
:expense_accounts [{:amount 100.0
|
||||
:location "DT"
|
||||
:account_id test-account-id}]}}
|
||||
nil)]
|
||||
;; Set lock date after invoice creation
|
||||
@(dc/transact datomic/conn
|
||||
[{:db/id test-client-id
|
||||
:client/locked-until #inst "2022-06-01"}])
|
||||
(is (thrown? Exception (gql-invoices/edit-invoice
|
||||
{:id (admin-token)}
|
||||
{:invoice {:id (:id invoice)
|
||||
:invoice_number "LOCK-EDIT-2"
|
||||
:date #clj-time/date-time "2022-01-01"
|
||||
:total 100.00
|
||||
:expense_accounts [{:amount 100.0
|
||||
:location "DT"
|
||||
:account_id test-account-id}]}}
|
||||
nil)))))))
|
||||
|
||||
(deftest test-lock-date-void
|
||||
(testing "Behavior 27.3: It should block voiding invoices dated before the client's locked-until date"
|
||||
(let [{:strs [test-client-id test-vendor-id test-account-id]}
|
||||
(setup-test-data [])]
|
||||
(let [invoice (gql-invoices/add-invoice
|
||||
{:id (admin-token)}
|
||||
{:invoice {:client_id test-client-id
|
||||
:vendor_id test-vendor-id
|
||||
:invoice_number "LOCK-VOID"
|
||||
:date #clj-time/date-time "2022-01-01"
|
||||
:total 100.00
|
||||
:expense_accounts [{:amount 100.0
|
||||
:location "DT"
|
||||
:account_id test-account-id}]}}
|
||||
nil)]
|
||||
;; Set lock date after invoice creation
|
||||
@(dc/transact datomic/conn
|
||||
[{:db/id test-client-id
|
||||
:client/locked-until #inst "2022-06-01"}])
|
||||
(is (thrown? Exception (gql-invoices/void-invoice
|
||||
{:id (admin-token)}
|
||||
{:invoice_id (:id invoice)}
|
||||
nil)))))))
|
||||
|
||||
;; ============================================================================
|
||||
;; New Invoice Wizard (8.1, 8.5)
|
||||
;; ============================================================================
|
||||
|
||||
(deftest test-new-invoice-validation
|
||||
(testing "Behavior 8.1: It should require client, vendor, date, invoice number, and total"
|
||||
(let [{:strs [test-client-id test-vendor-id test-account-id]}
|
||||
(setup-test-data [])]
|
||||
;; Missing invoice number
|
||||
(is (thrown? Exception (gql-invoices/add-invoice
|
||||
{:id (admin-token)}
|
||||
{:invoice {:client_id test-client-id
|
||||
:vendor_id test-vendor-id
|
||||
:date #clj-time/date-time "2022-01-01"
|
||||
:total 10.00
|
||||
:expense_accounts [{:amount 10.0
|
||||
:location "DT"
|
||||
:account_id test-account-id}]}}
|
||||
nil)))
|
||||
;; Missing total
|
||||
(is (thrown? Exception (gql-invoices/add-invoice
|
||||
{:id (admin-token)}
|
||||
{:invoice {:client_id test-client-id
|
||||
:vendor_id test-vendor-id
|
||||
:invoice_number "MISSING-TOTAL"
|
||||
:date #clj-time/date-time "2022-01-01"
|
||||
:expense_accounts [{:amount 10.0
|
||||
:location "DT"
|
||||
:account_id test-account-id}]}}
|
||||
nil)))))
|
||||
|
||||
(testing "Behavior 8.5: It should prevent duplicate invoice numbers for the same vendor and client"
|
||||
(let [{:strs [test-client-id test-vendor-id test-account-id]}
|
||||
(setup-test-data [])]
|
||||
;; Create first invoice
|
||||
(gql-invoices/add-invoice
|
||||
{:id (admin-token)}
|
||||
{:invoice {:client_id test-client-id
|
||||
:vendor_id test-vendor-id
|
||||
:invoice_number "DUP-TEST"
|
||||
:date #clj-time/date-time "2022-01-01"
|
||||
:total 100.00
|
||||
:expense_accounts [{:amount 100.0
|
||||
:location "DT"
|
||||
:account_id test-account-id}]}}
|
||||
nil)
|
||||
;; Try duplicate
|
||||
(is (thrown? Exception (gql-invoices/add-invoice
|
||||
{:id (admin-token)}
|
||||
{:invoice {:client_id test-client-id
|
||||
:vendor_id test-vendor-id
|
||||
:invoice_number "DUP-TEST"
|
||||
:date #clj-time/date-time "2022-02-01"
|
||||
:total 200.00
|
||||
:expense_accounts [{:amount 200.0
|
||||
:location "DT"
|
||||
:account_id test-account-id}]}}
|
||||
nil))))))
|
||||
|
||||
;; ============================================================================
|
||||
;; Edit Invoice (11.1, 11.3)
|
||||
;; ============================================================================
|
||||
|
||||
(deftest test-edit-unpaid-invoice
|
||||
(testing "Behavior 11.1: It should allow editing unpaid and paid invoices"
|
||||
(let [{:strs [test-client-id test-vendor-id test-account-id]}
|
||||
(setup-test-data [])]
|
||||
(let [invoice (gql-invoices/add-invoice
|
||||
{:id (admin-token)}
|
||||
{:invoice {:client_id test-client-id
|
||||
:vendor_id test-vendor-id
|
||||
:invoice_number "EDIT-TEST"
|
||||
:date #clj-time/date-time "2022-01-01"
|
||||
:total 100.00
|
||||
:expense_accounts [{:amount 100.0
|
||||
:location "DT"
|
||||
:account_id test-account-id}]}}
|
||||
nil)]
|
||||
;; Edit unpaid invoice
|
||||
(is (some? (gql-invoices/edit-invoice
|
||||
{:id (admin-token)}
|
||||
{:invoice {:id (:id invoice)
|
||||
:invoice_number "EDITED"
|
||||
:date #clj-time/date-time "2022-01-01"
|
||||
:total 150.00
|
||||
:expense_accounts [{:amount 150.0
|
||||
:location "DT"
|
||||
:account_id test-account-id}]}}
|
||||
nil)))
|
||||
;; Verify edit
|
||||
(is (= "EDITED"
|
||||
(:invoice/invoice-number (dc/pull (dc/db datomic/conn)
|
||||
[:invoice/invoice-number]
|
||||
(:id invoice)))))))))
|
||||
|
||||
(deftest test-edit-expense-accounts
|
||||
(testing "Behavior 11.3: It should allow modifying expense account amounts, adding/removing accounts"
|
||||
(let [{:strs [test-client-id test-vendor-id test-account-id new-account-id]}
|
||||
(setup-test-data [(test-account :db/id "new-account-id")])]
|
||||
(let [invoice (gql-invoices/add-invoice
|
||||
{:id (admin-token)}
|
||||
{:invoice {:client_id test-client-id
|
||||
:vendor_id test-vendor-id
|
||||
:invoice_number "EDIT-ACCTS"
|
||||
:date #clj-time/date-time "2022-01-01"
|
||||
:total 100.00
|
||||
:expense_accounts [{:amount 100.0
|
||||
:location "DT"
|
||||
:account_id test-account-id}]}}
|
||||
nil)]
|
||||
;; Add second expense account
|
||||
(gql-invoices/edit-invoice
|
||||
{:id (admin-token)}
|
||||
{:invoice {:id (:id invoice)
|
||||
:invoice_number "EDIT-ACCTS"
|
||||
:date #clj-time/date-time "2022-01-01"
|
||||
:total 100.00
|
||||
:expense_accounts [{:amount 60.0
|
||||
:location "DT"
|
||||
:account_id test-account-id}
|
||||
{:amount 40.0
|
||||
:location "DT"
|
||||
:account_id new-account-id}]}}
|
||||
nil)
|
||||
(let [updated (dc/pull (dc/db datomic/conn)
|
||||
[{:invoice/expense-accounts [:invoice-expense-account/amount
|
||||
:invoice-expense-account/account]}]
|
||||
(:id invoice))]
|
||||
(is (= 2 (count (:invoice/expense-accounts updated)))))))))
|
||||
|
||||
;; ============================================================================
|
||||
;; Bulk Edit (15.4)
|
||||
;; ============================================================================
|
||||
|
||||
(deftest test-bulk-edit-codes-invoices
|
||||
(testing "Behavior 15.4: Given valid percentages, when submitted, then all selected invoices should be coded with the new expense accounts"
|
||||
(let [{:strs [test-client-id test-vendor-id test-account-id]}
|
||||
(setup-test-data [])]
|
||||
;; Create an invoice
|
||||
(let [invoice (gql-invoices/add-invoice
|
||||
{:id (admin-token)}
|
||||
{:invoice {:client_id test-client-id
|
||||
:vendor_id test-vendor-id
|
||||
:invoice_number "BULK-EDIT"
|
||||
:date #clj-time/date-time "2022-01-01"
|
||||
:total 100.00
|
||||
:expense_accounts [{:amount 100.0
|
||||
:location "DT"
|
||||
:account_id test-account-id}]}}
|
||||
nil)]
|
||||
;; Bulk edit should change the expense account
|
||||
(gql-invoices/bulk-change-invoices
|
||||
{:id (admin-token)}
|
||||
{:client_id test-client-id
|
||||
:filters {:client_id test-client-id}
|
||||
:accounts [{:percentage 1.0
|
||||
:account_id test-account-id
|
||||
:location "DT"}]}
|
||||
nil)
|
||||
;; Verify the invoice still has the expense account
|
||||
(let [updated (dc/pull (dc/db datomic/conn)
|
||||
[{:invoice/expense-accounts [:invoice-expense-account/account]}]
|
||||
(:id invoice))]
|
||||
(is (= test-account-id
|
||||
(-> updated :invoice/expense-accounts first :invoice-expense-account/account :db/id))))))))
|
||||
|
||||
;; ============================================================================
|
||||
;; Single/Bulk Void (17.1, 16.3, 16.4)
|
||||
;; ============================================================================
|
||||
|
||||
(deftest test-void-unpaid-invoice
|
||||
(testing "Behavior 17.1: Given an unpaid invoice with no linked payments, when the user voids it, then the invoice status should change to voided with zero amounts"
|
||||
(let [{:strs [test-client-id test-vendor-id test-account-id]}
|
||||
(setup-test-data [])]
|
||||
(let [invoice (gql-invoices/add-invoice
|
||||
{:id (admin-token)}
|
||||
{:invoice {:client_id test-client-id
|
||||
:vendor_id test-vendor-id
|
||||
:invoice_number "VOID-TEST"
|
||||
:date #clj-time/date-time "2022-01-01"
|
||||
:total 100.00
|
||||
:expense_accounts [{:amount 100.0
|
||||
:location "DT"
|
||||
:account_id test-account-id}]}}
|
||||
nil)]
|
||||
(gql-invoices/void-invoice {:id (admin-token)} {:invoice_id (:id invoice)} nil)
|
||||
(let [voided (dc/pull (dc/db datomic/conn)
|
||||
[{:invoice/status [:db/ident]} :invoice/total :invoice/outstanding-balance
|
||||
{:invoice/expense-accounts [:invoice-expense-account/amount]}]
|
||||
(:id invoice))]
|
||||
(is (= :invoice-status/voided (-> voided :invoice/status :db/ident)))
|
||||
(is (= 0.0 (:invoice/total voided)))
|
||||
(is (= 0.0 (:invoice/outstanding-balance voided)))
|
||||
(is (every? #(= 0.0 (:invoice-expense-account/amount %))
|
||||
(:invoice/expense-accounts voided))))))))
|
||||
|
||||
(deftest test-bulk-void-cash-payments
|
||||
(testing "Behavior 16.3: Given confirmed, when voiding, then linked cash payments should be voided automatically"
|
||||
(let [{:strs [test-client-id test-vendor-id test-account-id test-bank-account-id]}
|
||||
(setup-test-data [])]
|
||||
(let [invoice (gql-invoices/add-invoice
|
||||
{:id (admin-token)}
|
||||
{:invoice {:client_id test-client-id
|
||||
:vendor_id test-vendor-id
|
||||
:invoice_number "BULK-VOID-CASH"
|
||||
:date #clj-time/date-time "2022-01-01"
|
||||
:total 100.00
|
||||
:expense_accounts [{:amount 100.0
|
||||
:location "DT"
|
||||
:account_id test-account-id}]}}
|
||||
nil)
|
||||
cash-payment-id (get-in @(dc/transact datomic/conn
|
||||
[{:db/id "cash-pmt"
|
||||
:payment/date #inst "2022-01-01"
|
||||
:payment/client test-client-id
|
||||
:payment/vendor test-vendor-id
|
||||
:payment/bank-account test-bank-account-id
|
||||
:payment/type :payment-type/cash
|
||||
:payment/amount 100.0
|
||||
:payment/status :payment-status/cleared}
|
||||
{:db/id "ip"
|
||||
:invoice-payment/invoice (:id invoice)
|
||||
:invoice-payment/payment "cash-pmt"
|
||||
:invoice-payment/amount 100.0}])
|
||||
[:tempids "cash-pmt"])]
|
||||
;; Bulk void should also void the cash payment
|
||||
(gql-invoices/void-invoices
|
||||
{:id (admin-token) :clients [{:db/id test-client-id}]}
|
||||
{:filters {:client_id test-client-id}}
|
||||
nil)
|
||||
(let [payment (dc/pull (dc/db datomic/conn)
|
||||
[{:payment/status [:db/ident]}]
|
||||
cash-payment-id)]
|
||||
(is (= :payment-status/voided (-> payment :payment/status :db/ident))))))))
|
||||
|
||||
;; ============================================================================
|
||||
;; Unvoid (18.1)
|
||||
;; ============================================================================
|
||||
|
||||
(deftest test-unvoid-restores-invoice
|
||||
(testing "Behavior 18.1: Given a voided invoice, when the user unvoids it, then it should restore the original status, total, outstanding balance, and expense accounts from Datomic history"
|
||||
(let [{:strs [test-client-id test-vendor-id test-account-id]}
|
||||
(setup-test-data [])]
|
||||
(let [invoice (gql-invoices/add-invoice
|
||||
{:id (admin-token)}
|
||||
{:invoice {:client_id test-client-id
|
||||
:vendor_id test-vendor-id
|
||||
:invoice_number "UNVOID-TEST"
|
||||
:date #clj-time/date-time "2022-01-01"
|
||||
:total 100.00
|
||||
:expense_accounts [{:amount 100.0
|
||||
:location "DT"
|
||||
:account_id test-account-id}]}}
|
||||
nil)
|
||||
original-id (:id invoice)]
|
||||
;; Void the invoice
|
||||
(gql-invoices/void-invoice {:id (admin-token)} {:invoice_id original-id} nil)
|
||||
(let [voided (dc/pull (dc/db datomic/conn) [{:invoice/status [:db/ident]}] original-id)]
|
||||
(is (= :invoice-status/voided (-> voided :invoice/status :db/ident))))
|
||||
;; Unvoid the invoice
|
||||
(gql-invoices/unvoid-invoice {:id (admin-token)} {:invoice_id original-id} nil)
|
||||
(let [restored (dc/pull (dc/db datomic/conn)
|
||||
[{:invoice/status [:db/ident]} :invoice/total :invoice/outstanding-balance
|
||||
{:invoice/expense-accounts [:invoice-expense-account/amount]}]
|
||||
original-id)]
|
||||
(is (= :invoice-status/unpaid (-> restored :invoice/status :db/ident)))
|
||||
(is (= 100.0 (:invoice/total restored)))
|
||||
(is (= 100.0 (:invoice/outstanding-balance restored)))
|
||||
(is (= 100.0 (-> restored :invoice/expense-accounts first :invoice-expense-account/amount))))))))
|
||||
|
||||
;; ============================================================================
|
||||
;; Undo Autopay (19.1)
|
||||
;; ============================================================================
|
||||
|
||||
(deftest test-undo-autopay-resets-status
|
||||
(testing "Behavior 19.1: Given a paid invoice with a scheduled payment and no linked payments, when the user undoes autopay, then the status should reset to unpaid and outstanding should equal total"
|
||||
(let [{:strs [test-client-id test-vendor-id test-account-id]}
|
||||
(setup-test-data [])]
|
||||
(let [invoice (gql-invoices/add-invoice
|
||||
{:id (admin-token)}
|
||||
{:invoice {:client_id test-client-id
|
||||
:vendor_id test-vendor-id
|
||||
:invoice_number "UNDO-AUTO"
|
||||
:date #clj-time/date-time "2022-01-01"
|
||||
:total 100.00
|
||||
:expense_accounts [{:amount 100.0
|
||||
:location "DT"
|
||||
:account_id test-account-id}]}}
|
||||
nil)
|
||||
invoice-id (:id invoice)]
|
||||
;; Mark as paid with scheduled payment
|
||||
@(dc/transact datomic/conn
|
||||
[[:upsert-invoice {:db/id invoice-id
|
||||
:invoice/status :invoice-status/paid
|
||||
:invoice/outstanding-balance 0.0
|
||||
:invoice/scheduled-payment #inst "2022-02-01"}]])
|
||||
;; Undo autopay
|
||||
(gql-invoices/unautopay-invoice {:id (admin-token)} {:invoice_id invoice-id} nil)
|
||||
(let [updated (dc/pull (dc/db datomic/conn)
|
||||
[{:invoice/status [:db/ident]} :invoice/outstanding-balance :invoice/scheduled-payment]
|
||||
invoice-id)]
|
||||
(is (= :invoice-status/unpaid (-> updated :invoice/status :db/ident)))
|
||||
(is (= 100.0 (:invoice/outstanding-balance updated)))
|
||||
(is (nil? (:invoice/scheduled-payment updated))))))))
|
||||
|
||||
;; ============================================================================
|
||||
;; Invoice List Query Behaviors (2.6, 2.8, 2.10, 2.14)
|
||||
;; ============================================================================
|
||||
|
||||
(deftest test-invoice-list-filtering
|
||||
(testing "Behaviors 2.6, 2.8, 2.10, 2.14: Invoice list filtering"
|
||||
(let [{:strs [test-client-id test-vendor-id test-account-id]}
|
||||
(setup-test-data [])]
|
||||
;; Create test invoices
|
||||
(gql-invoices/add-invoice
|
||||
{:id (admin-token)}
|
||||
{:invoice {:client_id test-client-id
|
||||
:vendor_id test-vendor-id
|
||||
:invoice_number "FILTER-A"
|
||||
:date #clj-time/date-time "2022-01-15"
|
||||
:total 100.00
|
||||
:expense_accounts [{:amount 100.0
|
||||
:location "DT"
|
||||
:account_id test-account-id}]}}
|
||||
nil)
|
||||
(gql-invoices/add-invoice
|
||||
{:id (admin-token)}
|
||||
{:invoice {:client_id test-client-id
|
||||
:vendor_id test-vendor-id
|
||||
:invoice_number "FILTER-B"
|
||||
:date #clj-time/date-time "2022-02-15"
|
||||
:total 200.00
|
||||
:expense_accounts [{:amount 200.0
|
||||
:location "DT"
|
||||
:account_id test-account-id}]}}
|
||||
nil)
|
||||
|
||||
;; Filter by invoice number
|
||||
(let [request {:query-params {:invoice-number "FILTER-A"}
|
||||
:route-params {:status nil}
|
||||
:clients [{:db/id test-client-id}]}
|
||||
[invoices count] (ssr-invoices/fetch-page request)]
|
||||
(is (= 1 count)))
|
||||
|
||||
;; Filter by status
|
||||
(let [request {:query-params {}
|
||||
:route-params {:status :invoice-status/unpaid}
|
||||
:clients [{:db/id test-client-id}]}
|
||||
[invoices count] (ssr-invoices/fetch-page request)]
|
||||
(is (= 2 count)))
|
||||
|
||||
;; Exact match by ID
|
||||
(let [invoice-id (ffirst (dc/q '[:find ?i
|
||||
:where [?i :invoice/invoice-number "FILTER-A"]]
|
||||
(dc/db datomic/conn)))
|
||||
request {:query-params {:exact-match-id invoice-id}
|
||||
:route-params {:status nil}
|
||||
:clients [{:db/id test-client-id}]}
|
||||
[invoices count] (ssr-invoices/fetch-page request)]
|
||||
(is (= 1 count))
|
||||
(is (= invoice-id (:db/id (first invoices))))))))
|
||||
|
||||
;; ============================================================================
|
||||
;; Invoice List Sorting (3.5, 3.7, 3.10)
|
||||
;; ============================================================================
|
||||
|
||||
(deftest test-invoice-list-sorting
|
||||
(testing "Behaviors 3.5, 3.7, 3.10: Invoice list sorting"
|
||||
(let [{:strs [test-client-id test-vendor-id test-account-id]}
|
||||
(setup-test-data [])]
|
||||
(gql-invoices/add-invoice
|
||||
{:id (admin-token)}
|
||||
{:invoice {:client_id test-client-id
|
||||
:vendor_id test-vendor-id
|
||||
:invoice_number "SORT-B"
|
||||
:date #clj-time/date-time "2022-02-01"
|
||||
:total 200.00
|
||||
:expense_accounts [{:amount 200.0
|
||||
:location "DT"
|
||||
:account_id test-account-id}]}}
|
||||
nil)
|
||||
(gql-invoices/add-invoice
|
||||
{:id (admin-token)}
|
||||
{:invoice {:client_id test-client-id
|
||||
:vendor_id test-vendor-id
|
||||
:invoice_number "SORT-A"
|
||||
:date #clj-time/date-time "2022-01-01"
|
||||
:total 100.00
|
||||
:expense_accounts [{:amount 100.0
|
||||
:location "DT"
|
||||
:account_id test-account-id}]}}
|
||||
nil)
|
||||
|
||||
;; Sort by date ascending
|
||||
(let [request {:query-params {:sort [{:sort-key "date" :asc true}]}
|
||||
:route-params {:status nil}
|
||||
:clients [{:db/id test-client-id}]}
|
||||
[invoices count] (ssr-invoices/fetch-page request)]
|
||||
(is (= "SORT-A" (:invoice/invoice-number (first invoices)))))
|
||||
|
||||
;; Sort by invoice number
|
||||
(let [request {:query-params {:sort [{:sort-key "invoice-number" :asc true}]}
|
||||
:route-params {:status nil}
|
||||
:clients [{:db/id test-client-id}]}
|
||||
[invoices count] (ssr-invoices/fetch-page request)]
|
||||
(is (= "SORT-A" (:invoice/invoice-number (first invoices)))))
|
||||
|
||||
;; Toggle sort direction
|
||||
(let [request-asc {:query-params {:sort [{:sort-key "date" :asc true}]}
|
||||
:route-params {:status nil}
|
||||
:clients [{:db/id test-client-id}]}
|
||||
[invoices-asc count] (ssr-invoices/fetch-page request-asc)
|
||||
request-desc {:query-params {:sort [{:sort-key "date" :asc false}]}
|
||||
:route-params {:status nil}
|
||||
:clients [{:db/id test-client-id}]}
|
||||
[invoices-desc count] (ssr-invoices/fetch-page request-desc)]
|
||||
(is (not= (:invoice/invoice-number (first invoices-asc))
|
||||
(:invoice/invoice-number (first invoices-desc))))))))
|
||||
|
||||
;; ============================================================================
|
||||
;; Invoice List Pagination (4.1, 4.3)
|
||||
;; ============================================================================
|
||||
|
||||
(deftest test-invoice-list-pagination
|
||||
(testing "Behaviors 4.1, 4.3: Invoice list pagination"
|
||||
(let [{:strs [test-client-id test-vendor-id test-account-id]}
|
||||
(setup-test-data [])]
|
||||
;; Create 30 invoices
|
||||
(doseq [i (range 30)]
|
||||
(gql-invoices/add-invoice
|
||||
{:id (admin-token)}
|
||||
{:invoice {:client_id test-client-id
|
||||
:vendor_id test-vendor-id
|
||||
:invoice_number (str "PAGE-" i)
|
||||
:date #clj-time/date-time "2022-01-01"
|
||||
:total 100.00
|
||||
:expense_accounts [{:amount 100.0
|
||||
:location "DT"
|
||||
:account_id test-account-id}]}}
|
||||
nil))
|
||||
|
||||
;; Default 25 per page
|
||||
(let [request {:query-params {}
|
||||
:route-params {:status nil}
|
||||
:clients [{:db/id test-client-id}]}
|
||||
[invoices total-count total-outstanding total-amount] (ssr-invoices/fetch-page request)]
|
||||
(is (= 25 (count invoices)))
|
||||
(is (= 30 total-count))
|
||||
(is (= 3000.0 total-outstanding))
|
||||
(is (= 3000.0 total-amount))))))
|
||||
|
||||
;; ============================================================================
|
||||
;; Legacy Routes (28.1)
|
||||
;; ============================================================================
|
||||
|
||||
(deftest test-legacy-routes
|
||||
(testing "Behavior 28.1: It should redirect old SPA routes to the new SSR routes"
|
||||
(let [handler (ssr-invoices/redirect-handler ::ssr-invoices/route/all-page)
|
||||
response (handler {:query-params {}})]
|
||||
(is (= 302 (:status response)))
|
||||
(is (get-in response [:headers "Location"])))))
|
||||
@@ -23,37 +23,37 @@
|
||||
(with-open [s (io/input-stream (io/resource "sample-ezcater.xlsx"))]
|
||||
(is (seq (sut/stream->sales-orders s))))
|
||||
(with-open [s (io/input-stream (io/resource "sample-ezcater.xlsx"))]
|
||||
(is (= #:sales-order
|
||||
{:vendor :vendor/ccp-ezcater
|
||||
:service-charge -95.9
|
||||
:date #inst "2023-04-03T18:30:00"
|
||||
:reference-link "ZA2-320"
|
||||
:charges
|
||||
[#:charge{:type-name "CARD"
|
||||
:date #inst "2023-04-03T18:30:00"
|
||||
:client test-client
|
||||
:location "DT"
|
||||
:external-id
|
||||
"ezcater/charge/17592186045501-DT-ZA2-320-0"
|
||||
:processor :ccp-processor/ezcater
|
||||
:total 516.12
|
||||
:tip 0.0}]
|
||||
:client test-client
|
||||
:tip 0.0
|
||||
:tax 37.12
|
||||
:external-id "ezcater/order/17592186045501-DT-ZA2-320"
|
||||
:total 516.12
|
||||
:line-items
|
||||
[#:order-line-item{:external-id
|
||||
"ezcater/order/17592186045501-DT-ZA2-320-0"
|
||||
:item-name "EZCater Catering"
|
||||
:category "EZCater Catering"
|
||||
:discount 0.0
|
||||
:tax 37.12
|
||||
:total 516.12}]
|
||||
:discount 0.0
|
||||
:location "DT"
|
||||
:returns 0.0}
|
||||
(last (first (filter (comp #{:order} first)
|
||||
(sut/stream->sales-orders s)))))))))))
|
||||
(is (= #:sales-order
|
||||
{:vendor :vendor/ccp-ezcater
|
||||
:service-charge -95.9
|
||||
:date #inst "2023-04-03T18:30:00"
|
||||
:reference-link "ZA2-320"
|
||||
:charges
|
||||
[#:charge{:type-name "CARD"
|
||||
:date #inst "2023-04-03T18:30:00"
|
||||
:client test-client
|
||||
:location "DT"
|
||||
:external-id
|
||||
(str "ezcater/charge/" test-client "-DT-ZA2-320-0")
|
||||
:processor :ccp-processor/ezcater
|
||||
:total 516.12
|
||||
:tip 0.0}]
|
||||
:client test-client
|
||||
:tip 0.0
|
||||
:tax 37.12
|
||||
:external-id (str "ezcater/order/" test-client "-DT-ZA2-320")
|
||||
:total 516.12
|
||||
:line-items
|
||||
[#:order-line-item{:external-id
|
||||
(str "ezcater/order/" test-client "-DT-ZA2-320-0")
|
||||
:item-name "EZCater Catering"
|
||||
:category "EZCater Catering"
|
||||
:discount 0.0
|
||||
:tax 37.12
|
||||
:total 516.12}]
|
||||
:discount 0.0
|
||||
:location "DT"
|
||||
:returns 0.0}
|
||||
(last (first (filter (comp #{:order} first)
|
||||
(sut/stream->sales-orders s)))))))))))
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
|
||||
(defn wrap-setup
|
||||
[f]
|
||||
(with-redefs [auto-ap.datomic/uri "datomic:mem://test"]
|
||||
(with-redefs [auto-ap.datomic/uri "datomic:mem://test"
|
||||
auto-ap.solr/impl (auto-ap.solr/->InMemSolrClient (atom {}))]
|
||||
(dc/create-database auto-ap.datomic/uri)
|
||||
(with-redefs [auto-ap.datomic/conn (dc/connect auto-ap.datomic/uri)]
|
||||
(transact-schema conn)
|
||||
@@ -28,6 +29,13 @@
|
||||
:user/name "TEST USER"
|
||||
:user/clients [{:db/id client-id}]}))
|
||||
|
||||
(defn user-token-no-access []
|
||||
{:user "TEST USER"
|
||||
:exp (time/plus (time/now) (time/days 1))
|
||||
:user/role "user"
|
||||
:user/name "TEST USER"
|
||||
:user/clients []})
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -101,16 +109,18 @@
|
||||
(dissoc x :id))
|
||||
|
||||
(defn setup-test-data [data]
|
||||
(:tempids @(dc/transact conn (into data
|
||||
[(test-account :db/id "test-account-id")
|
||||
(test-client :db/id "test-client-id"
|
||||
:client/bank-accounts [(test-bank-account :db/id "test-bank-account-id")])
|
||||
(test-vendor :db/id "test-vendor-id")
|
||||
{:db/id "accounts-payable-id"
|
||||
:account/name "Accounts Payable"
|
||||
:db/ident :account/accounts-payable
|
||||
:account/numeric-code 21000
|
||||
:account/account-set "default"}]))))
|
||||
(let [defaults [(test-account :db/id "test-account-id")
|
||||
(test-client :db/id "test-client-id"
|
||||
:client/bank-accounts [(test-bank-account :db/id "test-bank-account-id")])
|
||||
(test-vendor :db/id "test-vendor-id")
|
||||
{:db/id "accounts-payable-id"
|
||||
:account/name "Accounts Payable"
|
||||
:db/ident :account/accounts-payable
|
||||
:account/numeric-code 21000
|
||||
:account/account-set "default"}]
|
||||
user-ids (set (keep :db/id data))
|
||||
merged (into [] (concat data (remove #(user-ids (:db/id %)) defaults)))]
|
||||
(:tempids @(dc/transact conn merged))))
|
||||
|
||||
(defn apply-tx [data]
|
||||
(:db-after @(dc/transact conn data)))
|
||||
|
||||
Reference in New Issue
Block a user