(ns auto-ap.ledger.grid-test (:require [auto-ap.integration.util :refer [wrap-setup setup-test-data test-client test-account test-vendor test-bank-account]] [auto-ap.datomic :refer [conn]] [auto-ap.ssr.ledger.common :as ledger.common] [auto-ap.datomic.ledger :as d-ledger] [datomic.api :as dc] [clojure.test :refer [deftest testing is use-fixtures]])) (use-fixtures :each wrap-setup) (defn setup-journal-entries "Create test journal entries and return relevant IDs" [{:keys [client-id account-id vendor-id bank-account-id]}] (let [tx-result @(dc/transact conn [{:db/id "je-1" :journal-entry/client client-id :journal-entry/date #inst "2023-01-15" :journal-entry/source "test-source" :journal-entry/external-id "test-ext-123" :journal-entry/vendor vendor-id :journal-entry/amount 100.0 :journal-entry/line-items [{:db/id "jel-1" :journal-entry-line/account account-id :journal-entry-line/location "DT" :journal-entry-line/debit 100.0} {:db/id "jel-2" :journal-entry-line/account account-id :journal-entry-line/location "DT" :journal-entry-line/credit 100.0}]} {:db/id "je-2" :journal-entry/client client-id :journal-entry/date #inst "2023-02-20" :journal-entry/source "another-source" :journal-entry/external-id "test-ext-456" :journal-entry/vendor vendor-id :journal-entry/amount 200.0 :journal-entry/alternate-description "Alt Description" :journal-entry/line-items [{:db/id "jel-3" :journal-entry-line/account account-id :journal-entry-line/location "DT" :journal-entry-line/debit 200.0} {:db/id "jel-4" :journal-entry-line/account account-id :journal-entry-line/location "DT" :journal-entry-line/credit 200.0}]}])] tx-result)) ;; 1.2: Client column visibility (deftest test-display-client-column-visibility (testing "Client column hidden when single client with single location" (let [client-header (first (filter #(= "client" (:key %)) (:headers ledger.common/grid-page))) hide-fn (:hide? client-header)] (is (fn? hide-fn)) (is (hide-fn {:clients [{:db/id 1}] :client {:client/locations ["DT"]}})) (is (not (hide-fn {:clients [{:db/id 1}] :client {:client/locations ["DT" "MH"]}}))) (is (not (hide-fn {:clients [{:db/id 1} {:db/id 2}] :client {:client/locations ["DT"]}})))))) ;; 1.3: Vendor column with alternate-description fallback (deftest test-display-vendor-column-fallback (testing "Vendor column shows vendor name when present" (let [vendor-header (first (filter #(= "vendor" (:key %)) (:headers ledger.common/grid-page))) render-fn (:render vendor-header)] (is (= "Test Vendor" (render-fn {:journal-entry/vendor {:vendor/name "Test Vendor"}}))))) (testing "Vendor column falls back to alternate-description" (let [vendor-header (first (filter #(= "vendor" (:key %)) (:headers ledger.common/grid-page))) render-fn (:render vendor-header) result (render-fn {:journal-entry/vendor nil :journal-entry/alternate-description "Fallback Description"})] (is (vector? result)) (is (re-find #"Fallback Description" (str result)))))) ;; 2.1: Filter by vendor (deftest test-filtering-by-vendor (testing "Filter entries by vendor" (let [{:strs [test-client-id test-account-id test-vendor-id]} (setup-test-data []) _ (setup-journal-entries {:client-id test-client-id :account-id test-account-id :vendor-id test-vendor-id}) result-without-filter (ledger.common/fetch-ids (dc/db conn) {:clients [{:db/id test-client-id}] :query-params {}}) result-with-filter (ledger.common/fetch-ids (dc/db conn) {:clients [{:db/id test-client-id}] :query-params {:vendor {:db/id test-vendor-id}}})] (is (= 2 (:count result-without-filter))) (is (= 2 (:count result-with-filter)))))) ;; 2.2: Filter by account (deftest test-filtering-by-account (testing "Filter entries by account" (let [{:strs [test-client-id test-account-id test-vendor-id]} (setup-test-data []) _ (setup-journal-entries {:client-id test-client-id :account-id test-account-id :vendor-id test-vendor-id}) result (ledger.common/fetch-ids (dc/db conn) {:clients [{:db/id test-client-id}] :query-params {:account {:db/id test-account-id}}})] (is (= 2 (:count result)))))) ;; 2.3: Filter by bank account (deftest test-filtering-by-bank-account (testing "Filter entries by bank account" (let [{:strs [test-client-id test-account-id test-vendor-id test-bank-account-id]} (setup-test-data []) _ @(dc/transact conn [{:db/id "je-bank" :journal-entry/client test-client-id :journal-entry/date #inst "2023-03-01" :journal-entry/source "bank-source" :journal-entry/external-id "bank-ext-1" :journal-entry/vendor test-vendor-id :journal-entry/amount 50.0 :journal-entry/line-items [{:db/id "jel-bank" :journal-entry-line/account test-bank-account-id :journal-entry-line/location "DT" :journal-entry-line/debit 50.0} {:db/id "jel-bank-2" :journal-entry-line/account test-account-id :journal-entry-line/location "DT" :journal-entry-line/credit 50.0}]}]) result (ledger.common/fetch-ids (dc/db conn) {:clients [{:db/id test-client-id}] :query-params {:bank-account {:db/id test-bank-account-id}}})] (is (= 1 (:count result)))))) ;; 2.5: Filter by date range (deftest test-filtering-by-date-range (testing "Filter entries by date range" (let [{:strs [test-client-id test-account-id test-vendor-id]} (setup-test-data []) _ (setup-journal-entries {:client-id test-client-id :account-id test-account-id :vendor-id test-vendor-id}) result (ledger.common/fetch-ids (dc/db conn) {:clients [{:db/id test-client-id}] :query-params {:start-date #inst "2023-01-01" :end-date #inst "2023-01-31"}})] (is (= 1 (:count result)))))) ;; 2.6: Filter by invoice number (deftest test-filtering-by-invoice-number (testing "Filter entries by invoice number" (let [{:strs [test-client-id test-account-id test-vendor-id]} (setup-test-data []) _ @(dc/transact conn [{:db/id "inv-1" :invoice/client test-client-id :invoice/date #inst "2023-01-01" :invoice/vendor test-vendor-id :invoice/invoice-number "INV-TEST-123" :invoice/total 100.0 :invoice/outstanding-balance 100.0 :invoice/status :invoice-status/unpaid :invoice/import-status :import-status/imported} {:db/id "je-inv" :journal-entry/client test-client-id :journal-entry/date #inst "2023-01-01" :journal-entry/original-entity "inv-1" :journal-entry/amount 100.0 :journal-entry/line-items [{:db/id "jel-inv-1" :journal-entry-line/account test-account-id :journal-entry-line/location "DT" :journal-entry-line/debit 100.0} {:db/id "jel-inv-2" :journal-entry-line/account test-account-id :journal-entry-line/location "DT" :journal-entry-line/credit 100.0}]}]) result (ledger.common/fetch-ids (dc/db conn) {:clients [{:db/id test-client-id}] :query-params {:invoice-number "INV-TEST-123"}})] (is (= 1 (:count result)))))) ;; 2.7: Filter by account code range (deftest test-filtering-by-account-code-range (testing "Filter entries by account code range" (let [{:strs [test-client-id test-account-id test-vendor-id]} (setup-test-data [(test-account :db/id "test-account-id" :account/numeric-code 50000)]) _ (setup-journal-entries {:client-id test-client-id :account-id test-account-id :vendor-id test-vendor-id}) result (ledger.common/fetch-ids (dc/db conn) {:clients [{:db/id test-client-id}] :query-params {:numeric-code-gte 40000 :numeric-code-lte 60000}})] (is (= 2 (:count result)))))) ;; 2.8: Filter by amount range (deftest test-filtering-by-amount-range (testing "Filter entries by amount range" (let [{:strs [test-client-id test-account-id test-vendor-id]} (setup-test-data []) _ (setup-journal-entries {:client-id test-client-id :account-id test-account-id :vendor-id test-vendor-id}) result-gte (ledger.common/fetch-ids (dc/db conn) {:clients [{:db/id test-client-id}] :query-params {:amount-gte 150.0}}) result-lte (ledger.common/fetch-ids (dc/db conn) {:clients [{:db/id test-client-id}] :query-params {:amount-lte 150.0}})] (is (= 1 (:count result-gte))) (is (= 1 (:count result-lte)))))) ;; 2.9: Filter unbalanced entries (deftest test-filtering-unbalanced (testing "Filter to show only unbalanced entries" (let [{:strs [test-client-id test-account-id test-vendor-id]} (setup-test-data []) ;; Create an unbalanced entry: debits (60) != credits (40) _ @(dc/transact conn [{:db/id "je-unbal" :journal-entry/client test-client-id :journal-entry/date #inst "2023-01-01" :journal-entry/source "unbal-source" :journal-entry/external-id "unbal-ext" :journal-entry/vendor test-vendor-id :journal-entry/amount 100.0 :journal-entry/line-items [{:db/id "jel-unbal-1" :journal-entry-line/account test-account-id :journal-entry-line/location "DT" :journal-entry-line/debit 60.0} {:db/id "jel-unbal-2" :journal-entry-line/account test-account-id :journal-entry-line/location "DT" :journal-entry-line/credit 40.0}]}]) _ (setup-journal-entries {:client-id test-client-id :account-id test-account-id :vendor-id test-vendor-id}) result (ledger.common/fetch-ids (dc/db conn) {:clients [{:db/id test-client-id}] :query-params {:only-unbalanced true}})] (is (= 1 (:count result)))))) ;; 2.10: Exact match ID filter (deftest test-filtering-by-exact-match-id (testing "Exact match ID filter bypasses other filters" (let [{:strs [test-client-id test-account-id test-vendor-id]} (setup-test-data []) _ (setup-journal-entries {:client-id test-client-id :account-id test-account-id :vendor-id test-vendor-id}) all-ids (:ids (ledger.common/fetch-ids (dc/db conn) {:clients [{:db/id test-client-id}] :query-params {}})) exact-id (first all-ids) result (ledger.common/fetch-ids (dc/db conn) {:clients [{:db/id test-client-id}] :query-params {:exact-match-id exact-id :source "non-existent"}})] (is (= 1 (:count result))) (is (= [exact-id] (vec (:ids result))))))) ;; 2.12: Combined filters (deftest test-filtering-combined-filters (testing "Combined filters refresh together" (let [{:strs [test-client-id test-account-id test-vendor-id]} (setup-test-data []) _ (setup-journal-entries {:client-id test-client-id :account-id test-account-id :vendor-id test-vendor-id}) result (ledger.common/fetch-ids (dc/db conn) {:clients [{:db/id test-client-id}] :query-params {:source "test-source" :start-date #inst "2023-01-01" :end-date #inst "2023-01-31"}})] (is (= 1 (:count result)))))) ;; 3.1-3.7, 3.11: Sorting (deftest test-sorting-by-date (testing "3.5: Sort by date ascending/descending" (let [{:strs [test-client-id test-account-id test-vendor-id]} (setup-test-data []) _ (setup-journal-entries {:client-id test-client-id :account-id test-account-id :vendor-id test-vendor-id}) result-asc (ledger.common/fetch-ids (dc/db conn) {:clients [{:db/id test-client-id}] :query-params {:sort [{:name "Date" :sort-key "date" :asc? true}]}}) result-desc (ledger.common/fetch-ids (dc/db conn) {:clients [{:db/id test-client-id}] :query-params {:sort [{:name "Date" :sort-key "date" :asc? false}]}})] (is (= 2 (:count result-asc))) (is (= 2 (:count result-desc)))))) (deftest test-sorting-by-amount (testing "3.6: Sort by amount ascending/descending" (let [{:strs [test-client-id test-account-id test-vendor-id]} (setup-test-data []) _ (setup-journal-entries {:client-id test-client-id :account-id test-account-id :vendor-id test-vendor-id}) result (ledger.common/fetch-ids (dc/db conn) {:clients [{:db/id test-client-id}] :query-params {:sort [{:name "Amount" :sort-key "amount" :asc? true}]}})] (is (= 2 (:count result)))))) ;; 3.8: Default sort (deftest test-default-sort (testing "3.8: Default sort is date ascending" (let [{:strs [test-client-id test-account-id test-vendor-id]} (setup-test-data []) _ (setup-journal-entries {:client-id test-client-id :account-id test-account-id :vendor-id test-vendor-id}) result (ledger.common/fetch-ids (dc/db conn) {:clients [{:db/id test-client-id}] :query-params {}})] (is (= 2 (:count result)))))) ;; 3.9: Group by vendor (deftest test-sort-group-by-vendor (testing "3.9: Sort by vendor groups rows with break headers" (let [break-fn (:break-table ledger.common/grid-page) mock-entity {:journal-entry/vendor {:vendor/name "Test Vendor"}}] (is (= "Test Vendor" (break-fn {:query-params {:sort [{:name "Vendor"}]}} mock-entity)))))) ;; 3.10: Group by source (deftest test-sort-group-by-source (testing "3.10: Sort by source groups rows with break headers" (let [break-fn (:break-table ledger.common/grid-page) mock-entity {:journal-entry/source "Some Source"}] (is (= "Some Source" (break-fn {:query-params {:sort [{:name "Source"}]}} mock-entity)))))) ;; 3.11: Sort toggle (deftest test-sort-toggle (testing "3.11: Sort direction toggles" (let [{:strs [test-client-id test-account-id test-vendor-id]} (setup-test-data []) _ (setup-journal-entries {:client-id test-client-id :account-id test-account-id :vendor-id test-vendor-id}) result-asc (ledger.common/fetch-ids (dc/db conn) {:clients [{:db/id test-client-id}] :query-params {:sort [{:name "Amount" :sort-key "amount" :asc? true}]}}) result-desc (ledger.common/fetch-ids (dc/db conn) {:clients [{:db/id test-client-id}] :query-params {:sort [{:name "Amount" :sort-key "amount" :asc? false}]}})] (is (= 2 (:count result-asc))) (is (= 2 (:count result-desc)))))) ;; 4.1: Default per page (deftest test-pagination-default (testing "4.1: Default 25 entries per page" (is (some? (:query-schema ledger.common/grid-page))))) ;; 4.2: Change per page (deftest test-pagination-change (testing "4.2: Changing per-page count" (let [{:strs [test-client-id test-account-id test-vendor-id]} (setup-test-data []) _ (setup-journal-entries {:client-id test-client-id :account-id test-account-id :vendor-id test-vendor-id}) result (ledger.common/fetch-ids (dc/db conn) {:clients [{:db/id test-client-id}] :query-params {:per-page 1 :start 0}})] (is (= 2 (:count result))) (is (= 1 (count (:ids result))))))) ;; 6.2: CSV export columns (deftest test-csv-export-columns (testing "6.2: CSV export includes correct columns" (let [csv-headers (filter #(contains? (:render-for % #{:html :csv}) :csv) (:headers ledger.common/grid-page)) csv-keys (set (map :key csv-headers))] (is (contains? csv-keys "id")) (is (contains? csv-keys "client")) (is (contains? csv-keys "vendor")) (is (contains? csv-keys "source")) (is (contains? csv-keys "external-id")) (is (contains? csv-keys "date")) (is (contains? csv-keys "amount")) (is (contains? csv-keys "account")) (is (contains? csv-keys "debit")) (is (contains? csv-keys "credit"))))) ;; 6.1: CSV export line items (deftest test-csv-export-line-items (testing "6.1: CSV export has line-item-level rows" (let [page->csv-fn (:page->csv-entities ledger.common/grid-page) mock-entries [[{:db/id 1 :journal-entry/line-items [{:db/id 11} {:db/id 12}]}]]] (is (= 2 (count (page->csv-fn mock-entries)))))))