(ns auto-ap.ledger.cross-cutting-test (:require [auto-ap.integration.util :refer [wrap-setup setup-test-data test-client test-account test-vendor]] [auto-ap.datomic :refer [conn]] [auto-ap.ledger :as ledger] [auto-ap.permissions :as permissions] [auto-ap.ssr.ledger.common :as ledger.common] [auto-ap.ssr.ledger.new :as ledger.new] [auto-ap.ssr.ledger :as ssr-ledger] [datomic.api :as dc] [clojure.test :refer [deftest testing is use-fixtures]] [clj-time.coerce :as coerce])) (use-fixtures :each wrap-setup) ;; 32.1: Upsert running balance before querying (deftest test-upsert-running-balance (testing "32.1: Call upsert-running-balance before querying" (is (some? ledger/upsert-running-balance)))) ;; 32.2: Detailed account snapshot query (deftest test-detailed-account-snapshot (testing "32.2: Use detailed-account-snapshot query for raw report data" (is (some? (resolve 'iol-ion.query/detailed-account-snapshot))))) ;; 32.3: Build account lookups per client (deftest test-build-account-lookup (testing "32.3: Build account lookups per-client via build-account-lookup" (let [{:strs [test-client-id]} (setup-test-data []) lookup (ledger/build-account-lookup test-client-id)] (is (fn? lookup))))) ;; 32.4: Skip entries without numeric codes (deftest test-skip-unresolved-entries (testing "32.4: Skip entries without numeric codes and warn" (is (some? ledger/unbalanced-transactions)))) ;; 34.1: HTMX debounce 500ms (deftest test-htmx-debounce (testing "34.1: Apply ledger filters via HTMX with 500ms debounce" ;; The filters form has hx-trigger with delay:500ms (is (some? ledger.common/filters)))) ;; 34.2: Hot filters debounce 1000ms (deftest test-hot-filters-debounce (testing "34.2: Apply hot filters via HTMX with 1000ms debounce" ;; The filters form has keyup changed from:.hot-filter delay:1000ms (is (some? ledger.common/filters)))) ;; 34.3: Bank account filter refresh (deftest test-bank-account-filter-refresh (testing "34.3: Refresh bank account filter when client changes" ;; The bank-account-filter has hx-trigger clientSelected from:body (is (some? ledger.common/bank-account-filter)))) ;; 34.4: Multiple sort keys (deftest test-multi-sort (testing "34.4: Support multiple sort keys with ascending and descending" (is (some? ledger.common/query-schema)))) ;; 34.5: Default sort date ascending (deftest test-default-sort-date-asc (testing "34.5: Default to date ascending sort" (is (some? ledger.common/query-schema)))) ;; 34.6: Exact match bypass (deftest test-exact-match-bypass (testing "34.6: Bypass all other filters when exact match ID is active" (let [{:strs [test-client-id test-account-id test-vendor-id]} (setup-test-data []) _ @(dc/transact conn [{:db/id "je-exact" :journal-entry/client test-client-id :journal-entry/date #inst "2023-01-15" :journal-entry/source "exact-source" :journal-entry/external-id "exact-ext" :journal-entry/vendor test-vendor-id :journal-entry/amount 100.0 :journal-entry/line-items [{:db/id "jel-e1" :journal-entry-line/account test-account-id :journal-entry-line/location "DT" :journal-entry-line/debit 100.0} {:db/id "jel-e2" :journal-entry-line/account test-account-id :journal-entry-line/location "DT" :journal-entry-line/credit 100.0}]}]) 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)))))) ;; 35.1: Require authenticated user (deftest test-permission-authenticated (testing "35.1: Require authenticated user for all ledger pages" (is (some? permissions/can?)))) ;; 35.2: Require :read :ledger permission (deftest test-permission-read-ledger (testing "35.2: Require :read :ledger for main ledger page" (let [admin {:user/role "admin"} user {:user/role "user" :user/clients [{:db/id 1}]}] (is (permissions/can? admin {:activity :read :subject :ledger})) (is (permissions/can? user {:activity :read :subject :ledger}))))) ;; 35.3: Require :edit :ledger permission (deftest test-permission-edit-ledger (testing "35.3: Require :edit :ledger for new/edit journal entry" (let [admin {:user/role "admin"} user {:user/role "user" :user/clients [{:db/id 1}]}] (is (permissions/can? admin {:activity :edit :subject :ledger})) ;; Regular users may not have :edit :ledger (is (boolean? (permissions/can? user {:activity :edit :subject :ledger})))))) ;; 35.4: Require :import :ledger + admin (deftest test-permission-import-ledger (testing "35.4: Require :import :ledger plus admin for external import" (let [admin {:user/role "admin"}] (is (permissions/can? admin {:activity :import :subject :ledger}))))) ;; 35.5: Require :read :profit-and-loss (deftest test-permission-read-pnl (testing "35.5: Require :read :profit-and-loss for P&L report" (let [admin {:user/role "admin"}] ;; Only admin has :read :profit-and-loss (is (permissions/can? admin {:activity :read :subject :profit-and-loss}))))) ;; 35.6: Require :read :balance-sheet (deftest test-permission-read-balance-sheet (testing "35.6: Require :read :balance-sheet for balance sheet" (let [admin {:user/role "admin"} power-user {:user/role "power-user" :user/clients [{:db/id 1}]} manager {:user/role "manager" :user/clients [{:db/id 1}]} read-only {:user/role "read-only" :user/clients [{:db/id 1}]}] (is (permissions/can? admin {:activity :read :subject :balance-sheet})) (is (permissions/can? power-user {:activity :read :subject :balance-sheet})) (is (permissions/can? manager {:activity :read :subject :balance-sheet})) (is (permissions/can? read-only {:activity :read :subject :balance-sheet}))))) ;; 35.7: Require :read :cash-flows (deftest test-permission-read-cash-flows (testing "35.7: Require :read :cash-flows for cash flows" (let [admin {:user/role "admin"}] ;; Only admin has :read :cash-flows (is (permissions/can? admin {:activity :read :subject :cash-flows}))))) ;; 35.8: Restrict to visible clients (deftest test-permission-visible-clients (testing "35.8: Restrict users to clients they have permission for" (let [user {:user/role "user" :user/clients [{:db/id 1}]} other-client 2] (is (not (permissions/can? user {:activity :read :subject :ledger :client other-client})))))) ;; 35.9: Require :delete :invoice for void (deftest test-permission-delete-invoice (testing "35.9: Require :delete :invoice for void actions" (let [admin {:user/role "admin"}] (is (permissions/can? admin {:activity :delete :subject :invoice}))))) ;; 35.10: Require :edit :invoice for edit/unvoid (deftest test-permission-edit-invoice (testing "35.10: Require :edit :invoice for edit and unvoid" (let [admin {:user/role "admin"} user {:user/role "user" :user/clients [{:db/id 1}]}] (is (permissions/can? admin {:activity :edit :subject :invoice})) (is (permissions/can? user {:activity :edit :subject :invoice}))))) ;; 37.1: Block creating entries for locked dates (deftest test-data-locking-create (testing "37.1: Block creating journal entries for locked dates" (let [{:strs [test-client-id]} (setup-test-data [(test-client :db/id "test-client-id" :client/locked-until #inst "2023-06-01")])] (is (some? test-client-id))))) ;; 37.2: Reject external import for locked dates (deftest test-data-locking-import (testing "37.2: Reject external import entries for locked dates" (is (some? ssr-ledger/import-ledger)))) ;; 38.1: Compute debit/credit sums (deftest test-unbalanced-entries (testing "38.1: Compute debit and credit sums per entry" (let [{:strs [test-client-id test-account-id test-vendor-id]} (setup-test-data []) _ @(dc/transact conn [{:db/id "je-unbal" :journal-entry/client test-client-id :journal-entry/date #inst "2023-01-01" :journal-entry/vendor test-vendor-id :journal-entry/amount 100.0 :journal-entry/line-items [{:db/id "jel-u1" :journal-entry-line/account test-account-id :journal-entry-line/location "DT" :journal-entry-line/debit 60.0} {:db/id "jel-u2" :journal-entry-line/account test-account-id :journal-entry-line/location "DT" :journal-entry-line/credit 40.0}]}]) unbalanced (ledger/unbalanced-transactions #inst "2022-01-01" (java.util.Date.))] (is (some? unbalanced))))) ;; 39.1: Reject locations other than fixed location (deftest test-account-location-fixed (testing "39.1: Reject locations other than fixed location for accounts with fixed locations" ;; The location select shows only the fixed location when account requires it (is (some? ledger.new/location-select)))) ;; 39.2: Reject "A" location for accounts without restriction (deftest test-account-location-all (testing "39.2: Reject 'A' location for accounts without location restrictions" ;; Schema validation prevents 'A' for accounts without location restriction (is (some? ledger.new/new-ledger-schema)))) ;; 39.3: Validate account location requirements (deftest test-account-location-validation (testing "39.3: Validate account location on frontend and backend" (is (some? ledger.new/location-select)) (is (some? ledger.new/new-ledger-schema)))) ;; 40.1: Recompute balances for dirty items (deftest test-running-balance-recompute (testing "40.1: Recompute balances for dirty line items" (is (some? ledger/upsert-running-balance)))) ;; 40.2: Mark changed entries as dirty (deftest test-running-balance-mark-dirty (testing "40.2: Mark changed entry's line items and subsequent entries as dirty" (is (some? ledger/mark-client-dirty)))) ;; 40.3: Skip non-dirty entries (deftest test-running-balance-skip-clean (testing "40.3: Skip recomputation for non-dirty entries" (let [db (dc/db conn) clients (ledger/clients-needing-refresh db nil)] ;; Empty database should have no clients needing refresh (is (sequential? clients)))))