test: add missing AC tests and fix AC numbering

This commit is contained in:
2026-05-27 23:36:08 -07:00
parent ebd91f1911
commit 1be83a01f5

View File

@@ -8,6 +8,7 @@
[auto-ap.ssr.transaction.edit :refer [clientize-vendor
edit-vendor-changed-handler
edit-wizard-toggle-mode-handler
location-select*
manual-coding-section*
vendor-default-account]]
[clojure.test :refer [deftest is testing use-fixtures]]
@@ -18,6 +19,10 @@
(def ^:private save-handler
#'auto-ap.ssr.transaction.edit/save-handler)
;; Private simple-mode-fields* accessible via var reference
(def ^:private simple-mode-fields*-fn
#'auto-ap.ssr.transaction.edit/simple-mode-fields*)
(use-fixtures :each wrap-setup)
;;; ---------------------------------------------------------------------------
@@ -30,7 +35,7 @@
(get (:tempids result) temp-id))
;;; ---------------------------------------------------------------------------
;;; AC 1-3: manual-mode-initial — mode selection on open
;;; AC1-3: manual-mode-initial — mode selection on open
;;; ---------------------------------------------------------------------------
;; manual-mode-initial is private; access via var reference
@@ -38,17 +43,17 @@
#'auto-ap.ssr.transaction.edit/manual-mode-initial)
(deftest manual-mode-initial-test
(testing "AC1: no accounts simple mode"
(testing "AC1: uncoded transaction (no accounts) opens in simple mode"
(is (= :simple (manual-mode-initial {:db/id 123})))
(is (= :simple (manual-mode-initial {:db/id 123 :transaction/accounts []}))))
(testing "AC2: exactly one account → simple mode"
(testing "AC2: single-account transaction opens in simple mode"
(is (= :simple (manual-mode-initial {:db/id 123
:transaction/accounts [{:transaction-account/account 456
:transaction-account/location "Shared"
:transaction-account/amount 100.0}]}))))
(testing "AC3: two or more accounts → advanced mode"
(testing "AC3: multi-account (2+) transaction opens in advanced mode"
(is (= :advanced (manual-mode-initial {:db/id 123
:transaction/accounts [{:transaction-account/account 1}
{:transaction-account/account 2}]})))
@@ -56,11 +61,11 @@
:transaction/accounts [{} {} {}]})))))
;;; ---------------------------------------------------------------------------
;;; AC 4-5: edit-vendor-changed-handler — vendor selection in simple mode
;;; AC4-5: edit-vendor-changed-handler — vendor selection in simple mode
;;; ---------------------------------------------------------------------------
(deftest edit-vendor-changed-simple-mode-test
(testing "AC4: selecting vendor with no existing account populates account/location from vendor default"
(testing "AC4: vendor selection in simple mode populates account/location from vendor default"
;; Set up vendor with a default account
(let [result @(dc/transact conn [{:db/id "vendor-id"
:vendor/name "Test Vendor"}
@@ -105,7 +110,7 @@
(is (re-find #"Test Account" body)
"Response should contain the vendor's default account name")))
(testing "AC5: selecting vendor when account already set does NOT overwrite existing account"
(testing "AC5: vendor selection in simple mode does NOT overwrite already-set account"
(let [result @(dc/transact conn [{:db/id "vendor-id"
:vendor/name "Test Vendor"}
{:db/id "account-id"
@@ -158,11 +163,11 @@
"Response should NOT contain the vendor's default account ID when existing account is set"))))
;;; ---------------------------------------------------------------------------
;;; AC 6: save round-trip — manual mode saves vendor + account to DB
;;; AC6: save round-trip — manual mode saves vendor + account to DB
;;; ---------------------------------------------------------------------------
(deftest save-manual-round-trip-test
(testing "AC6: submitting simple-mode form saves vendor and single account/location to DB"
(testing "AC6: save in simple mode persists vendor/account/location — re-opening shows same values"
(let [result @(dc/transact conn [{:db/id "vendor-id"
:vendor/name "Save Vendor"}
{:db/id "account-id"
@@ -218,11 +223,86 @@
"The amount should be saved")))))
;;; ---------------------------------------------------------------------------
;;; AC 7-9: edit-wizard-toggle-mode-handler — mode toggling
;;; AC7: simple mode account typeahead respects :account/default-allowance rules
;;; ---------------------------------------------------------------------------
(deftest toggle-mode-test
(testing "AC7: simple → advanced toggle re-renders in advanced mode with accounts preserved"
(deftest simple-mode-account-typeahead-allowance-test
(testing "AC7: simple mode account typeahead URL includes purpose=transaction"
;; The account-typeahead* always passes purpose=transaction to the search URL,
;; and the schema validates accounts using check-allowance with :account/default-allowance.
;; We test the rendering side: the typeahead URL in simple-mode-fields* must contain purpose=transaction.
(let [result @(dc/transact conn [{:db/id "client-id"
:client/code "ALLOWCL"
:client/locations ["DT"]}
{:db/id "account-id"
:account/name "Allowed Account"
:account/type :account-type/expense}
{:db/id "transaction-id"
:transaction/amount 100.0
:transaction/date #inst "2023-01-01"
:transaction/id (str (java.util.UUID/randomUUID))
:transaction/client "client-id"}])
tx-id (tempid->id result "transaction-id")
account-id (tempid->id result "account-id")
client-id (tempid->id result "client-id")
one-account [{:db/id "row-1"
:transaction-account/account account-id
:transaction-account/location "DT"
:transaction-account/amount 100.0}]
request {:multi-form-state (mm/->MultiStepFormState
{:db/id tx-id
:transaction/client client-id
:transaction/accounts one-account}
[]
{:mode "simple"
:transaction/accounts one-account})
:entity {:db/id tx-id
:transaction/client {:db/id client-id}
:transaction/amount 100.0}}
html (hiccup/html
(fc/start-form (:multi-form-state request) nil
(fc/with-field :step-params
(manual-coding-section* :simple request))))]
(is (re-find #"purpose=transaction" html)
"Simple mode account typeahead URL must include purpose=transaction to enforce allowance rules"))))
;;; ---------------------------------------------------------------------------
;;; AC8: location dropdown — fixed location vs full client list
;;; ---------------------------------------------------------------------------
(deftest location-select-options-test
(testing "AC8: when account has a fixed location, dropdown shows only that location"
(let [html (hiccup/html
(location-select* {:name "location"
:account-location "HQ"
:client-locations ["DT" "NY"]
:value "HQ"}))]
(is (re-find #"HQ" html)
"Fixed account location 'HQ' should appear in dropdown")
(is (not (re-find #"DT" html))
"Client location 'DT' should NOT appear when account has fixed location")
(is (not (re-find #"Shared" html))
"'Shared' option should not appear when account has fixed location")))
(testing "AC8: when account has no fixed location, dropdown shows full client location list"
(let [html (hiccup/html
(location-select* {:name "location"
:account-location nil
:client-locations ["DT" "NY"]
:value "Shared"}))]
(is (re-find #"Shared" html)
"'Shared' option should appear when no fixed account location")
(is (re-find #"DT" html)
"Client location 'DT' should appear when no fixed account location")
(is (re-find #"NY" html)
"Client location 'NY' should appear when no fixed account location"))))
;;; ---------------------------------------------------------------------------
;;; AC9: simple → advanced toggle with pre-populated row
;;; ---------------------------------------------------------------------------
(deftest toggle-simple-to-advanced-test
(testing "AC9: clicking 'Switch to advanced mode' from simple mode re-renders in advanced with one row pre-populated"
(let [result @(dc/transact conn [{:db/id "client-id"
:client/code "TOGGLECL"
:client/locations ["DT"]}
@@ -267,14 +347,58 @@
"Advanced mode response should contain the account ID from the simple-mode row")
;; The account name should appear in the advanced table HTML
(is (re-find #"Toggle Account" body)
"Advanced mode response should contain the account name from the simple-mode row")))
"Advanced mode response should contain the account name from the simple-mode row"))))
(testing "AC8: advanced → simple (1 row) re-renders in simple mode"
;;; ---------------------------------------------------------------------------
;;; AC10: blank simple → advanced gives empty table
;;; ---------------------------------------------------------------------------
(deftest toggle-blank-simple-to-advanced-test
(testing "AC10: 'Switch to advanced mode' from blank simple mode gives advanced with empty table"
(let [result @(dc/transact conn [{:db/id "client-id"
:client/code "TOGSIMCL"
:client/code "BLNKTOGLCL"
:client/locations ["DT"]}
{:db/id "transaction-id"
:transaction/amount 100.0
:transaction/date #inst "2023-01-01"
:transaction/id (str (java.util.UUID/randomUUID))
:transaction/client "client-id"}])
tx-id (tempid->id result "transaction-id")
client-id (tempid->id result "client-id")
request {:multi-form-state (mm/->MultiStepFormState
{:db/id tx-id
:transaction/client client-id
:transaction/accounts []}
[]
{:mode "simple"
:transaction/accounts []})
:entity {:db/id tx-id
:transaction/client {:db/id client-id}
:transaction/amount 100.0}}
response (edit-wizard-toggle-mode-handler request)
body (:body response)]
(is (string? body))
;; Should render in advanced mode
(is (re-find #"account-grid-body" body)
"Blank simple → advanced should produce account-grid-body")
;; Should NOT have any account-row elements (empty grid)
(is (not (re-find #"account-row" body))
"Blank simple → advanced should have NO account rows")
;; Should show 'Switch to simple mode' because 0 rows <= 1
(is (re-find #"Switch to simple mode" body)
"Advanced mode with 0 rows should show 'Switch to simple mode' link"))))
;;; ---------------------------------------------------------------------------
;;; AC11-12: "Switch to simple mode" visibility based on row count
;;; ---------------------------------------------------------------------------
(deftest advanced-mode-switch-link-visibility-test
(testing "AC11: 'Switch to simple mode' link is visible in advanced mode with 0 or 1 rows"
(let [result @(dc/transact conn [{:db/id "client-id"
:client/code "VISLINKCL"
:client/locations ["DT"]}
{:db/id "account-id"
:account/name "Toggle Account"
:account/name "Visibility Account"
:account/type :account-type/expense}
{:db/id "transaction-id"
:transaction/amount 100.0
@@ -284,28 +408,36 @@
tx-id (tempid->id result "transaction-id")
account-id (tempid->id result "account-id")
client-id (tempid->id result "client-id")
zero-rows []
one-row [{:db/id "row-1"
:transaction-account/account account-id
:transaction-account/location "DT"
:transaction-account/amount 100.0}]
request {:multi-form-state (mm/->MultiStepFormState
{:db/id tx-id
:transaction/client client-id
:transaction/accounts one-row}
[]
{:mode "advanced"
:transaction/accounts one-row})
:entity {:db/id tx-id
:transaction/client {:db/id client-id}
:transaction/amount 100.0}}
response (edit-wizard-toggle-mode-handler request)
body (:body response)]
(is (string? body))
;; After toggling from advanced to simple, show "Switch to advanced mode" link
(is (re-find #"Switch to advanced mode" body)
"After toggling to simple mode, 'Switch to advanced mode' should be visible")))
make-req (fn [accounts]
{:multi-form-state (mm/->MultiStepFormState
{:db/id tx-id
:transaction/client client-id
:transaction/accounts accounts}
[]
{:mode "advanced"
:transaction/accounts accounts})
:entity {:db/id tx-id
:transaction/client {:db/id client-id}
:transaction/amount 100.0}})
html-zero-rows (hiccup/html
(fc/start-form (:multi-form-state (make-req zero-rows)) nil
(fc/with-field :step-params
(manual-coding-section* :advanced (make-req zero-rows)))))
html-one-row (hiccup/html
(fc/start-form (:multi-form-state (make-req one-row)) nil
(fc/with-field :step-params
(manual-coding-section* :advanced (make-req one-row)))))]
(is (re-find #"Switch to simple mode" html-zero-rows)
"In advanced mode with 0 rows, 'Switch to simple mode' should be visible")
(is (re-find #"Switch to simple mode" html-one-row)
"In advanced mode with 1 row, 'Switch to simple mode' should be visible")))
(testing "AC9: In advanced mode with 2+ rows, 'Switch to simple mode' should be absent"
(testing "AC12: 'Switch to simple mode' link is absent in advanced mode with 2+ rows"
(let [result @(dc/transact conn [{:db/id "client-id"
:client/code "TWOROWCL"
:client/locations ["DT"]}
@@ -342,7 +474,6 @@
:entity {:db/id tx-id
:transaction/client {:db/id client-id}
:transaction/amount 100.0}}
;; Render advanced mode directly with 2 rows using manual-coding-section*
html (hiccup/html
(fc/start-form (:multi-form-state request) nil
(fc/with-field :step-params
@@ -351,175 +482,16 @@
"In advanced mode with 2+ rows, 'Switch to simple mode' link should be absent"))))
;;; ---------------------------------------------------------------------------
;;; AC 9 (direct): "Switch to simple mode" hidden/visible based on row count
;;; AC13: advanced → simple (1 row) re-renders in simple mode
;;; ---------------------------------------------------------------------------
(deftest advanced-mode-switch-link-visibility-test
(testing "AC9a: 'Switch to simple mode' link visible when 1 row in advanced mode"
(deftest toggle-advanced-to-simple-test
(testing "AC13: 'Switch to simple mode' from advanced (1 row) gives simple mode with that row's values"
(let [result @(dc/transact conn [{:db/id "client-id"
:client/code "VISLINKCL"
:client/code "TOGSIMCL"
:client/locations ["DT"]}
{:db/id "account-id"
:account/name "Visibility Account"
:account/type :account-type/expense}
{:db/id "transaction-id"
:transaction/amount 100.0
:transaction/date #inst "2023-01-01"
:transaction/id (str (java.util.UUID/randomUUID))
:transaction/client "client-id"}])
tx-id (tempid->id result "transaction-id")
account-id (tempid->id result "account-id")
client-id (tempid->id result "client-id")
one-row [{:db/id "row-1"
:transaction-account/account account-id
:transaction-account/location "DT"
:transaction-account/amount 100.0}]
two-rows [{:db/id "row-1"
:transaction-account/account account-id
:transaction-account/location "DT"
:transaction-account/amount 50.0}
{:db/id "row-2"
:transaction-account/account account-id
:transaction-account/location "DT"
:transaction-account/amount 50.0}]
make-req (fn [accounts]
{:multi-form-state (mm/->MultiStepFormState
{:db/id tx-id
:transaction/client client-id
:transaction/accounts accounts}
[]
{:mode "advanced"
:transaction/accounts accounts})
:entity {:db/id tx-id
:transaction/client {:db/id client-id}
:transaction/amount 100.0}})
;; Render advanced mode with 1 row
html-one-row (hiccup/html
(fc/start-form (:multi-form-state (make-req one-row)) nil
(fc/with-field :step-params
(manual-coding-section* :advanced (make-req one-row)))))
;; Render advanced mode with 2 rows
html-two-rows (hiccup/html
(fc/start-form (:multi-form-state (make-req two-rows)) nil
(fc/with-field :step-params
(manual-coding-section* :advanced (make-req two-rows)))))]
(is (re-find #"Switch to simple mode" html-one-row)
"In advanced mode with 1 row, 'Switch to simple mode' should be visible")
(is (not (re-find #"Switch to simple mode" html-two-rows))
"In advanced mode with 2+ rows, 'Switch to simple mode' should be absent"))))
;;; ---------------------------------------------------------------------------
;;; AC 10: vendor-default-account uses clientized vendor logic
;;; ---------------------------------------------------------------------------
(deftest vendor-default-account-clientize-test
(testing "AC10: client-specific account override takes precedence over global vendor default"
(let [result @(dc/transact conn [{:db/id "global-account-id"
:account/name "Global Default Account"
:account/type :account-type/expense}
{:db/id "client-specific-account-id"
:account/name "Client Specific Account"
:account/type :account-type/expense}
{:db/id "client-id"
:client/code "CLACCTEST"
:client/locations ["DT"]}
{:db/id "vendor-id"
:vendor/name "Clientized Vendor"
:vendor/default-account "global-account-id"
:vendor/account-overrides [{:vendor-account-override/client "client-id"
:vendor-account-override/account "client-specific-account-id"}]}])
vendor-id (tempid->id result "vendor-id")
client-id (tempid->id result "client-id")
client-specific-account-id (tempid->id result "client-specific-account-id")
result-account (vendor-default-account vendor-id client-id)]
(is (some? result-account)
"Should return a default account")
(is (= client-specific-account-id (:db/id result-account))
"Client-specific account override should take precedence over global vendor default")))
(testing "AC10: falls back to global vendor default when no client override"
(let [result @(dc/transact conn [{:db/id "global-account-id"
:account/name "Global Default Account"
:account/type :account-type/expense}
{:db/id "vendor-id"
:vendor/name "Vendor No Override"
:vendor/default-account "global-account-id"}
{:db/id "client-id"
:client/code "NOOVCL"
:client/locations ["DT"]}])
vendor-id (tempid->id result "vendor-id")
client-id (tempid->id result "client-id")
global-account-id (tempid->id result "global-account-id")
result-account (vendor-default-account vendor-id client-id)]
(is (= global-account-id (:db/id result-account))
"Should use global vendor default when no client-specific override exists")))
(testing "AC10: clientize-vendor applies both terms and account overrides"
(let [vendor {:db/id "vendor-id"
:vendor/name "Test Vendor"
:vendor/default-account {:db/id "global-account-id"}
:vendor/terms "NET30"
:vendor/automatically-paid-when-due []
:vendor/terms-overrides [{:vendor-terms-override/client {:db/id "client-id"}
:vendor-terms-override/terms "NET60"}]
:vendor/account-overrides [{:vendor-account-override/client {:db/id "client-id"}
:vendor-account-override/account {:db/id "client-specific-account-id"}}]}
clientized (clientize-vendor vendor "client-id")]
(is (= "NET60" (:vendor/terms clientized))
"Terms override should be applied")
(is (= "client-specific-account-id" (-> clientized :vendor/default-account :db/id))
"Account override should be applied"))))
;;; ---------------------------------------------------------------------------
;;; AC 1-2 (rendering): manual-coding-section* renders correct mode
;;; ---------------------------------------------------------------------------
(deftest manual-coding-section-renders-correct-mode-test
(testing "AC1/AC2: simple mode renders with 'Switch to advanced mode' link (with one account present)"
(let [result @(dc/transact conn [{:db/id "client-id"
:client/code "SIMMODECL"
:client/locations ["DT"]}
{:db/id "account-id"
:account/name "Simple Mode Account"
:account/type :account-type/expense}
{:db/id "transaction-id"
:transaction/amount 100.0
:transaction/date #inst "2023-01-01"
:transaction/id (str (java.util.UUID/randomUUID))
:transaction/client "client-id"}])
tx-id (tempid->id result "transaction-id")
account-id (tempid->id result "account-id")
client-id (tempid->id result "client-id")
;; Simple mode: one account (renders correctly with one row cursor available)
one-account [{:db/id "row-1"
:transaction-account/account account-id
:transaction-account/location "DT"
:transaction-account/amount 100.0}]
request {:multi-form-state (mm/->MultiStepFormState
{:db/id tx-id
:transaction/client client-id
:transaction/accounts one-account}
[]
{:mode "simple"
:transaction/accounts one-account})
:entity {:db/id tx-id
:transaction/client {:db/id client-id}
:transaction/amount 100.0}}
html (hiccup/html
(fc/start-form (:multi-form-state request) nil
(fc/with-field :step-params
(manual-coding-section* :simple request))))]
(is (re-find #"Switch to advanced mode" html)
"Simple mode should show 'Switch to advanced mode' link")
(is (not (re-find #"Switch to simple mode" html))
"Simple mode should NOT show 'Switch to simple mode' link")))
(testing "AC3: advanced mode renders with 'Switch to simple mode' link when 1 row"
(let [result @(dc/transact conn [{:db/id "client-id"
:client/code "ADVMODECL"
:client/locations ["DT"]}
{:db/id "account-id"
:account/name "Advanced Mode Account"
:account/name "Toggle Account"
:account/type :account-type/expense}
{:db/id "transaction-id"
:transaction/amount 100.0
@@ -543,11 +515,425 @@
:entity {:db/id tx-id
:transaction/client {:db/id client-id}
:transaction/amount 100.0}}
response (edit-wizard-toggle-mode-handler request)
body (:body response)]
(is (string? body))
;; After toggling from advanced to simple, show "Switch to advanced mode" link
(is (re-find #"Switch to advanced mode" body)
"After toggling to simple mode, 'Switch to advanced mode' should be visible")
;; The row's account ID should appear in the simple mode form
(is (re-find (re-pattern (str account-id)) body)
"Simple mode should display the account from the advanced mode row"))))
;;; ---------------------------------------------------------------------------
;;; AC14: vendor change in advanced mode with no rows creates one row
;;; ---------------------------------------------------------------------------
(deftest edit-vendor-changed-advanced-mode-no-rows-test
(testing "AC14: vendor change in advanced mode with no rows creates one row with vendor's default account"
(let [result @(dc/transact conn [{:db/id "vendor-id"
:vendor/name "Advanced Vendor"}
{:db/id "account-id"
:account/name "Vendor Default Account"
:account/type :account-type/expense}
{:db/id "vendor-id"
:vendor/default-account "account-id"}
{:db/id "client-id"
:client/code "ADVVCL"
:client/locations ["DT"]}
{:db/id "transaction-id"
:transaction/amount 200.0
:transaction/date #inst "2023-01-01"
:transaction/id (str (java.util.UUID/randomUUID))
:transaction/client "client-id"}])
tx-id (tempid->id result "transaction-id")
vendor-id (tempid->id result "vendor-id")
account-id (tempid->id result "account-id")
client-id (tempid->id result "client-id")
;; Advanced mode with no accounts
request {:multi-form-state (mm/->MultiStepFormState
{:db/id tx-id
:transaction/client client-id
:transaction/accounts []}
[]
{:mode "advanced"
:transaction/vendor vendor-id
:transaction/accounts []})
:entity {:db/id tx-id
:transaction/client {:db/id client-id}
:transaction/amount 200.0}}
response (edit-vendor-changed-handler request)
body (:body response)]
(is (string? body))
(is (re-find #"manual-coding-section" body))
;; Should have account-grid-body (advanced mode)
(is (re-find #"account-grid-body" body)
"Advanced mode vendor change should show account-grid-body")
;; The vendor's default account ID should appear in the new row
(is (re-find (re-pattern (str account-id)) body)
"Advanced mode: vendor default account should appear in the newly created row")
(is (re-find #"Vendor Default Account" body)
"Advanced mode: vendor default account name should appear in the new row"))))
;;; ---------------------------------------------------------------------------
;;; AC15: vendor change in advanced mode with existing rows does NOT modify them
;;; ---------------------------------------------------------------------------
(deftest edit-vendor-changed-advanced-mode-existing-rows-test
(testing "AC15: vendor change in advanced mode with existing rows does NOT modify existing rows"
(let [result @(dc/transact conn [{:db/id "vendor-id"
:vendor/name "New Vendor"}
{:db/id "vendor-account-id"
:account/name "Vendor Default Account"
:account/type :account-type/expense}
{:db/id "vendor-id"
:vendor/default-account "vendor-account-id"}
{:db/id "existing-account-id"
:account/name "Existing Account"
:account/type :account-type/expense}
{:db/id "client-id"
:client/code "ADVEXCL"
:client/locations ["DT"]}
{:db/id "transaction-id"
:transaction/amount 100.0
:transaction/date #inst "2023-01-01"
:transaction/id (str (java.util.UUID/randomUUID))
:transaction/client "client-id"}])
tx-id (tempid->id result "transaction-id")
vendor-id (tempid->id result "vendor-id")
vendor-account-id (tempid->id result "vendor-account-id")
existing-account-id (tempid->id result "existing-account-id")
client-id (tempid->id result "client-id")
existing-rows [{:db/id "row-1"
:transaction-account/account existing-account-id
:transaction-account/location "DT"
:transaction-account/amount 100.0}]
;; Advanced mode with existing accounts — vendor change should NOT touch them
request {:multi-form-state (mm/->MultiStepFormState
{:db/id tx-id
:transaction/client client-id
:transaction/accounts existing-rows}
[]
{:mode "advanced"
:transaction/vendor vendor-id
:transaction/accounts existing-rows})
:entity {:db/id tx-id
:transaction/client {:db/id client-id}
:transaction/amount 100.0}}
response (edit-vendor-changed-handler request)
body (:body response)]
(is (string? body))
;; The original (existing) account should still be shown
(is (re-find (re-pattern (str existing-account-id)) body)
"Existing row account should remain unchanged when vendor changes in advanced mode")
;; The vendor's default account should NOT appear — existing rows are untouched
(is (not (re-find (re-pattern (str vendor-account-id)) body))
"Vendor default account should NOT replace existing rows in advanced mode"))))
;;; ---------------------------------------------------------------------------
;;; AC16: vendor-default-account uses clientized vendor logic
;;; ---------------------------------------------------------------------------
(deftest vendor-default-account-clientize-test
(testing "AC16: client-specific account override takes precedence over global vendor default"
(let [result @(dc/transact conn [{:db/id "global-account-id"
:account/name "Global Default Account"
:account/type :account-type/expense}
{:db/id "client-specific-account-id"
:account/name "Client Specific Account"
:account/type :account-type/expense}
{:db/id "client-id"
:client/code "CLACCTEST"
:client/locations ["DT"]}
{:db/id "vendor-id"
:vendor/name "Clientized Vendor"
:vendor/default-account "global-account-id"
:vendor/account-overrides [{:vendor-account-override/client "client-id"
:vendor-account-override/account "client-specific-account-id"}]}])
vendor-id (tempid->id result "vendor-id")
client-id (tempid->id result "client-id")
client-specific-account-id (tempid->id result "client-specific-account-id")
result-account (vendor-default-account vendor-id client-id)]
(is (some? result-account)
"Should return a default account")
(is (= client-specific-account-id (:db/id result-account))
"Client-specific account override should take precedence over global vendor default")))
(testing "AC16: falls back to global vendor default when no client override"
(let [result @(dc/transact conn [{:db/id "global-account-id"
:account/name "Global Default Account"
:account/type :account-type/expense}
{:db/id "vendor-id"
:vendor/name "Vendor No Override"
:vendor/default-account "global-account-id"}
{:db/id "client-id"
:client/code "NOOVCL"
:client/locations ["DT"]}])
vendor-id (tempid->id result "vendor-id")
client-id (tempid->id result "client-id")
global-account-id (tempid->id result "global-account-id")
result-account (vendor-default-account vendor-id client-id)]
(is (= global-account-id (:db/id result-account))
"Should use global vendor default when no client-specific override exists")))
(testing "AC16: clientize-vendor applies both terms and account overrides"
(let [vendor {:db/id "vendor-id"
:vendor/name "Test Vendor"
:vendor/default-account {:db/id "global-account-id"}
:vendor/terms "NET30"
:vendor/automatically-paid-when-due []
:vendor/terms-overrides [{:vendor-terms-override/client {:db/id "client-id"}
:vendor-terms-override/terms "NET60"}]
:vendor/account-overrides [{:vendor-account-override/client {:db/id "client-id"}
:vendor-account-override/account {:db/id "client-specific-account-id"}}]}
clientized (clientize-vendor vendor "client-id")]
(is (= "NET60" (:vendor/terms clientized))
"Terms override should be applied")
(is (= "client-specific-account-id" (-> clientized :vendor/default-account :db/id))
"Account override should be applied"))))
;;; ---------------------------------------------------------------------------
;;; AC17: approval status, memo, vendor fields present in both modes
;;; ---------------------------------------------------------------------------
(deftest fields-present-in-both-modes-test
(testing "AC17: vendor field is present in simple mode"
(let [result @(dc/transact conn [{:db/id "client-id"
:client/code "FLDTESTCL"
:client/locations ["DT"]}
{:db/id "account-id"
:account/name "Fields Account"
:account/type :account-type/expense}
{:db/id "transaction-id"
:transaction/amount 100.0
:transaction/date #inst "2023-01-01"
:transaction/id (str (java.util.UUID/randomUUID))
:transaction/client "client-id"}])
tx-id (tempid->id result "transaction-id")
account-id (tempid->id result "account-id")
client-id (tempid->id result "client-id")
one-account [{:db/id "row-1"
:transaction-account/account account-id
:transaction-account/location "DT"
:transaction-account/amount 100.0}]
make-req (fn [mode accounts]
{:multi-form-state (mm/->MultiStepFormState
{:db/id tx-id
:transaction/client client-id
:transaction/accounts accounts}
[]
{:mode (name mode)
:transaction/accounts accounts})
:entity {:db/id tx-id
:transaction/client {:db/id client-id}
:transaction/amount 100.0}})
html-simple (hiccup/html
(fc/start-form (:multi-form-state (make-req :simple one-account)) nil
(fc/with-field :step-params
(manual-coding-section* :simple (make-req :simple one-account)))))
html-advanced (hiccup/html
(fc/start-form (:multi-form-state (make-req :advanced one-account)) nil
(fc/with-field :step-params
(manual-coding-section* :advanced (make-req :advanced one-account)))))]
(is (re-find #"Vendor" html-simple)
"AC17: Vendor field label must appear in simple mode")
(is (re-find #"vendor/search" html-simple)
"AC17: Vendor typeahead/search URL must appear in simple mode")
(is (re-find #"transaction/vendor" html-simple)
"AC17: Vendor field name must appear in simple mode")
(is (re-find #"Vendor" html-advanced)
"AC17: Vendor field label must appear in advanced mode")
(is (re-find #"vendor/search" html-advanced)
"AC17: Vendor typeahead/search URL must appear in advanced mode")
(is (re-find #"transaction/vendor" html-advanced)
"AC17: Vendor field name must appear in advanced mode"))))
;;; ---------------------------------------------------------------------------
;;; AC18: switching modes mid-edit then saving produces a valid transaction
;;; ---------------------------------------------------------------------------
(deftest switch-modes-then-save-test
(testing "AC18: switching simple→advanced→save produces a single valid transaction row in DB"
(let [result @(dc/transact conn [{:db/id "vendor-id"
:vendor/name "Switch Vendor"}
{:db/id "account-id"
:account/name "Switch Account"
:account/type :account-type/expense}
{:db/id "client-id"
:client/code "SWITCHCL"
:client/locations ["DT"]}
{:db/id "transaction-id"
:transaction/amount 100.0
:transaction/date #inst "2023-01-01"
:transaction/id (str (java.util.UUID/randomUUID))
:transaction/client "client-id"}])
tx-id (tempid->id result "transaction-id")
vendor-id (tempid->id result "vendor-id")
account-id (tempid->id result "account-id")
client-id (tempid->id result "client-id")
simple-row [{:db/id "row-1"
:transaction-account/account account-id
:transaction-account/location "DT"
:transaction-account/amount 100.0}]
;; Step 1: Start in simple mode
simple-request {:multi-form-state (mm/->MultiStepFormState
{:db/id tx-id
:transaction/client client-id
:transaction/accounts simple-row}
[]
{:mode "simple"
:transaction/accounts simple-row})
:entity {:db/id tx-id
:transaction/client {:db/id client-id}
:transaction/amount 100.0}}
;; Step 2: Toggle to advanced mode
toggle-response (edit-wizard-toggle-mode-handler simple-request)
;; After toggle, build a save request in advanced mode with one row
advanced-snapshot {:db/id tx-id
:transaction/client client-id
:action :manual
:transaction/vendor vendor-id
:transaction/amount 100.0
:transaction/accounts [{:db/id "row-1"
:transaction-account/account account-id
:transaction-account/location "DT"
:transaction-account/amount 100.0}]}
save-request {:multi-form-state (mm/->MultiStepFormState advanced-snapshot [] advanced-snapshot)
:entity {:db/id tx-id
:transaction/client {:db/id client-id}
:transaction/amount 100.0}
:identity {:user/role "admin"}}]
;; Verify the toggle worked
(is (string? (:body toggle-response)))
(is (re-find #"account-grid-body" (:body toggle-response))
"After toggle, response should be in advanced mode")
;; Now save
(with-redefs [auto-ap.solr/impl (auto-ap.solr/->InMemSolrClient (atom {}))]
(save-handler save-request))
;; Verify exactly one account row was saved (no orphaned rows)
(let [saved (dc/pull (dc/db conn)
'[:db/id
{:transaction/accounts [{:transaction-account/account [:db/id]}
:transaction-account/location
:transaction-account/amount]}]
tx-id)]
(is (= 1 (count (:transaction/accounts saved)))
"AC18: Exactly one account row should be saved after mode-switch + save (no orphaned rows)")
(is (= account-id (-> saved :transaction/accounts first :transaction-account/account :db/id))
"AC18: The correct account should be saved")))))
;;; ---------------------------------------------------------------------------
;;; AC19: split transaction re-opens in advanced mode with all splits
;;; ---------------------------------------------------------------------------
(deftest split-transaction-reopens-advanced-test
(testing "AC19: split transaction (2+ accounts) re-opens in advanced mode with all splits intact"
;; Test manual-mode-initial with 2-row snapshot
(is (= :advanced (manual-mode-initial {:db/id 123
:transaction/accounts [{:transaction-account/account 1
:transaction-account/amount 50.0}
{:transaction-account/account 2
:transaction-account/amount 50.0}]}))
"AC19: 2-account snapshot should return :advanced mode")
(let [result @(dc/transact conn [{:db/id "client-id"
:client/code "SPLITCL"
:client/locations ["DT"]}
{:db/id "account-id-1"
:account/name "Split Account 1"
:account/type :account-type/expense}
{:db/id "account-id-2"
:account/name "Split Account 2"
:account/type :account-type/expense}
{:db/id "transaction-id"
:transaction/amount 100.0
:transaction/date #inst "2023-01-01"
:transaction/id (str (java.util.UUID/randomUUID))
:transaction/client "client-id"}])
tx-id (tempid->id result "transaction-id")
account-id-1 (tempid->id result "account-id-1")
account-id-2 (tempid->id result "account-id-2")
client-id (tempid->id result "client-id")
two-rows [{:db/id "row-1"
:transaction-account/account account-id-1
:transaction-account/location "DT"
:transaction-account/amount 50.0}
{:db/id "row-2"
:transaction-account/account account-id-2
:transaction-account/location "DT"
:transaction-account/amount 50.0}]
request {:multi-form-state (mm/->MultiStepFormState
{:db/id tx-id
:transaction/client client-id
:transaction/accounts two-rows}
[]
{:mode "advanced"
:transaction/accounts two-rows})
:entity {:db/id tx-id
:transaction/client {:db/id client-id}
:transaction/amount 100.0}}
html (hiccup/html
(fc/start-form (:multi-form-state request) nil
(fc/with-field :step-params
(manual-coding-section* :advanced request))))]
(is (re-find #"Switch to simple mode" html)
"Advanced mode with 1 row should show 'Switch to simple mode' link")
(is (not (re-find #"Switch to advanced mode" html))
"Advanced mode should NOT show 'Switch to advanced mode' link"))))
;; Should show both account IDs
(is (re-find (re-pattern (str account-id-1)) html)
"AC19: First split account should appear in advanced mode rendering")
(is (re-find (re-pattern (str account-id-2)) html)
"AC19: Second split account should appear in advanced mode rendering")
;; Should NOT show 'Switch to simple mode' with 2 rows
(is (not (re-find #"Switch to simple mode" html))
"AC19: Split transaction (2 rows) in advanced mode should NOT show 'Switch to simple mode'"))))
;;; ---------------------------------------------------------------------------
;;; AC20: single-account transaction re-opens in simple mode
;;; ---------------------------------------------------------------------------
(deftest single-account-reopens-simple-test
(testing "AC20: single-account transaction re-opens in simple mode with account/location pre-populated"
;; Test manual-mode-initial returns :simple for single-account snapshot
(is (= :simple (manual-mode-initial {:db/id 123
:transaction/accounts [{:transaction-account/account 456
:transaction-account/location "DT"
:transaction-account/amount 100.0}]}))
"AC20: Single-account snapshot should return :simple mode")
(let [result @(dc/transact conn [{:db/id "client-id"
:client/code "SINGCL"
:client/locations ["DT"]}
{:db/id "account-id"
:account/name "Single Account"
:account/type :account-type/expense}
{:db/id "transaction-id"
:transaction/amount 100.0
:transaction/date #inst "2023-01-01"
:transaction/id (str (java.util.UUID/randomUUID))
:transaction/client "client-id"}])
tx-id (tempid->id result "transaction-id")
account-id (tempid->id result "account-id")
client-id (tempid->id result "client-id")
one-account [{:db/id "row-1"
:transaction-account/account account-id
:transaction-account/location "DT"
:transaction-account/amount 100.0}]
request {:multi-form-state (mm/->MultiStepFormState
{:db/id tx-id
:transaction/client client-id
:transaction/accounts one-account}
[]
{:mode "simple"
:transaction/accounts one-account})
:entity {:db/id tx-id
:transaction/client {:db/id client-id}
:transaction/amount 100.0}}
html (hiccup/html
(fc/start-form (:multi-form-state request) nil
(fc/with-field :step-params
(manual-coding-section* :simple request))))]
;; Simple mode should show the account typeahead pre-populated with the account
(is (re-find (re-pattern (str account-id)) html)
"AC20: Simple mode should show the account pre-populated")
;; Should show 'Switch to advanced mode' (confirming it's in simple mode)
(is (re-find #"Switch to advanced mode" html)
"AC20: Simple mode should show 'Switch to advanced mode' link")
;; Should NOT show 'Switch to simple mode'
(is (not (re-find #"Switch to simple mode" html))
"AC20: Simple mode should NOT show 'Switch to simple mode' link"))))