From 3251b364a1235b951de5a9393f3548f9313d0787 Mon Sep 17 00:00:00 2001 From: Bryce Date: Fri, 26 Jun 2026 07:41:50 -0700 Subject: [PATCH] =?UTF-8?q?refactor(ssr):=20Phase=2011=20=E2=80=94=20delet?= =?UTF-8?q?e=20the=20dead=20mm/*=20wizard=20machinery?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With all 11 plan modals migrated onto the session-backed engine, the mm multi-step wizard framework has zero runtime callers (transaction/edit.clj builds its :multi-form-state map from its own wrap-derive-state and never required the namespace). Delete it: - src/.../components/multi_modal.clj — the ModalWizardStep/LinearModalWizard/ Initializable/Discardable protocols, the MultiStepFormState record, and the wrap-wizard / wrap-decode-multi-form-state / default-render-step / encode-step-key middleware + helpers (~22KB). - test/.../transaction/edit_simple_advanced_mode_test.clj — the last importer of mm; it was already broken (refers edit-vendor-changed-handler / edit-wizard-toggle-mode-handler, both removed when Transaction Edit migrated, so it no longer loaded) and tests the old mm interface that no longer exists. - test_server.clj: drop the stale unused mm require. form-cursor (fc/*) stays — still used by ~18 non-wizard forms outside this plan. The alpine-morph focus mechanism stays — it belongs to the whole-form-swap doctrine, not the wizard machinery. Fresh from-disk JVM compiles clean without the namespace; full e2e suite 71/71. Co-Authored-By: Claude Opus 4.8 --- .../ssr-form-migration/reference/scorecard.md | 24 + .../auto_ap/ssr/components/multi_modal.clj | 429 ------ .../edit_simple_advanced_mode_test.clj | 1319 ----------------- test/clj/auto_ap/test_server.clj | 1 - 4 files changed, 24 insertions(+), 1749 deletions(-) delete mode 100644 src/clj/auto_ap/ssr/components/multi_modal.clj delete mode 100644 test/clj/auto_ap/ssr/transaction/edit_simple_advanced_mode_test.clj diff --git a/.claude/skills/ssr-form-migration/reference/scorecard.md b/.claude/skills/ssr-form-migration/reference/scorecard.md index a167ee07..08a85359 100644 --- a/.claude/skills/ssr-form-migration/reference/scorecard.md +++ b/.claude/skills/ssr-form-migration/reference/scorecard.md @@ -328,3 +328,27 @@ against a fresh `TEST_SERVER_PORT=… lein run -m auto-ap.test-server` JVM, not timeline; edit prefill w/ disabled code; bank-accounts card + add affordance; editor open/discard; accept-merge; edit→save round-trip). Engine flow + accept + pass-through + edit init also confirmed at the REPL. + +--- + +## Phase 11 — Cleanup (delete the dead wizard machinery) + +With all 11 plan modals migrated, the `mm` multi-step wizard framework had **zero runtime +callers** (`transaction/edit.clj` reads `:multi-form-state` but builds it from its own +`wrap-derive-state` — it never required the namespace). Deleted: +`auto-ap.ssr.components.multi-modal` (the protocols `ModalWizardStep` / +`LinearModalWizard` / `Initializable` / `Discardable`, the `MultiStepFormState` record, +`wrap-wizard` / `wrap-decode-multi-form-state` / `default-render-step` / `encode-step-key` ++ the rest, ~22KB) and its last importer, the already-broken +`edit_simple_advanced_mode_test.clj` (it `:refer`'d `edit-vendor-changed-handler` / +`edit-wizard-toggle-mode-handler`, both removed when Transaction Edit migrated, so it could +no longer load). Removed a stale unused `mm` require in `test_server.clj`. + +**Not removed:** `form-cursor` (`fc/*`) is still used by ~18 non-wizard forms outside this +plan (accounts, ledger reports, users, imports, …) — out of scope. The Alpine +`alpine-morph` mechanism is still referenced by the whole-form-swap focus infra (the swap +doctrine), not the wizard machinery, so it stays. The lingering `MultiStepFormState` +mentions in migrated files are historical comments, not code. + +**Verification:** a fresh from-disk JVM compiles without the deleted namespace; full e2e +suite **71/71**. diff --git a/src/clj/auto_ap/ssr/components/multi_modal.clj b/src/clj/auto_ap/ssr/components/multi_modal.clj deleted file mode 100644 index cb3b03bd..00000000 --- a/src/clj/auto_ap/ssr/components/multi_modal.clj +++ /dev/null @@ -1,429 +0,0 @@ -(ns auto-ap.ssr.components.multi-modal - (:require [auto-ap.cursor :as cursor] - [auto-ap.ssr-routes :as ssr-routes] - [auto-ap.ssr.components :as com] - [auto-ap.ssr.components.timeline :as timeline] - [auto-ap.ssr.form-cursor :as fc] - [auto-ap.ssr.hx :as hx] - [auto-ap.ssr.nested-form-params :refer [wrap-nested-form-params]] - [auto-ap.ssr.svg :as svg] - [auto-ap.ssr.utils - :refer [assert-schema html-response main-transformer - modal-response wrap-form-4xx-2 wrap-schema-enforce]] - [bidi.bidi :as bidi] - [hiccup.util :as hu] - [malli.core :as mc] - [malli.core :as m])) - -(def default-form-props {:hx-ext "response-targets" - :hx-swap "outerHTML" - :hx-target-400 "#form-errors .error-content" - :hx-trigger "submit" - :hx-target "this"}) - -(defprotocol ModalWizardStep - (step-key [this]) - (edit-path [this request]) - (render-step [this request]) - (step-schema [this]) - (step-name [this])) - -(defprotocol Initializable - (init-step-params [this multi-form-state request])) - -(defprotocol CustomNext - (custom-next-handler [this request])) - -(defprotocol Discardable - (can-discard? [this step-params]) - (discard-changes [this request])) - -(defprotocol LinearModalWizard - (hydrate-from-request [this request]) - (get-current-step [this]) - (navigate [this step-key]) - - (form-schema [this]) - (steps [this]) - (get-step [this step-key]) - (render-wizard [this request]) - (submit [this request])) - -(defrecord MultiStepFormState [snapshot edit-path step-params]) -(defn select-state [multi-form-state edit-path default] - (->MultiStepFormState (:snapshot multi-form-state) - edit-path - (or (get-in (:snapshot multi-form-state) edit-path) - default))) - -(defn merge-multi-form-state [{:keys [snapshot edit-path step-params] :as multi-form-state}] - (let [cursor (cursor/cursor (or snapshot {})) - ;; this hack makes sure that, in the event of a missing vector entry, will make sure to add it first - edit-cursor (cond-> cursor - (seq edit-path) (cursor/ensure-path! edit-path {}) - (seq edit-path) (get-in edit-path {})) - - _ (cursor/transact! edit-cursor (fn [spot] - (merge spot step-params)))] - (assoc multi-form-state - :snapshot @cursor - :edit-path [] - :step-params @cursor))) - -(defn get-mfs-field [mfs k] - (or (get (:step-params mfs) k) - (get-in (:snapshot mfs) (conj (or (:edit-path mfs) []) - k)))) - -(def step-key-schema (mc/schema [:orn {:decode/arbitrary clojure.edn/read-string - :encode/arbitrary pr-str} - [:sub-step [:cat :keyword [:or :int :string]]] - [:step :keyword]])) - -(def encode-step-key - (m/-instrument {:schema [:=> [:cat step-key-schema] :any]} - (fn encode-step-key [sk] - (mc/encode step-key-schema sk main-transformer)))) - -(defn render-timeline [linear-wizard current-step validation-route] - (let [step-names (map #(step-name (get-step linear-wizard %)) (steps linear-wizard)) - active-index (.indexOf step-names (step-name current-step))] - (timeline/vertical-timeline - {} - (for [[n i] (map vector (steps linear-wizard) (range))] - (timeline/vertical-timeline-step (cond-> {} - (= i active-index) (assoc :active? true) - (< i active-index) (assoc :visited? true) - (= i (dec (count step-names))) (assoc :last? true)) - [:a.cursor-pointer.whitespace-nowrap {:x-data (hx/json {:timelineIndex i}) - :hx-put (hu/url (bidi/path-for ssr-routes/only-routes validation-route) - {:from (encode-step-key (step-key current-step)) - :to (encode-step-key (step-key (get-step linear-wizard n)))})} - (step-name (get-step linear-wizard n))]))))) -(defn back-button [linear-wizard step validation-route] - [:a.cursor-pointer.whitespace-nowrap.font-medium.text-blue-600 {:hx-put (hu/url (bidi/path-for ssr-routes/only-routes validation-route) - {:from (encode-step-key (step-key step)) - :to (encode-step-key (->> (partition-all 2 1 (steps linear-wizard)) - (filter (fn [[from to]] - (= to (step-key step)))) - ffirst))}) - :class "dark:text-blue-500"} - "Back"]) - -(defn default-next-button [linear-wizard step validation-route & {:keys [next-button-content]}] - (let [steps (steps linear-wizard) - last? (= (step-key step) (last steps)) - next-step (when-not last? (->> steps - (drop-while #(not= (step-key step) - %)) - (drop 1) - first - (get-step linear-wizard)))] - (com/validated-save-button (cond-> {:errors (seq fc/*form-errors*) - ;;:x-data (hx/json {}) - :x-ref "next" - :class "w-48"} - (not last?) (assoc :hx-put (hu/url (bidi/path-for ssr-routes/only-routes validation-route) - {:from (encode-step-key (step-key step)) - :to (encode-step-key (step-key next-step))}))) - - (or next-button-content - (if next-step - (step-name next-step) - "Save")) - (when-not last? - [:div.w-5.h-5 svg/arrow-right])))) - -(defn default-step-body [params & children] - [:div.space-y-1 {} - children]) - -(defn flatten-form-errors - "Walks a malli-humanized error structure and returns a flat sequence of - human-readable strings, prefixing each leaf message with the nearest - field name for context. Lets the footer's error bar surface every - validation error for the whole form, even ones whose field lives on a - hidden step/tab and so would otherwise be invisible." - ([errors] (flatten-form-errors nil errors)) - ([field errors] - (let [label (cond (keyword? field) (name field) - (string? field) field - :else nil) - decorate (fn [msg] (if label (str label ": " msg) msg))] - (cond - (map? errors) - (mapcat (fn [[k v]] (flatten-form-errors k v)) errors) - - (and (sequential? errors) (every? string? errors)) - (map decorate errors) - - (sequential? errors) - (mapcat #(flatten-form-errors field %) errors) - - (string? errors) - [(decorate errors)] - - :else nil)))) - -(defn default-step-footer [linear-wizard step & {:keys [validation-route - discard-button - next-button - next-button-content]}] - [:div.flex.justify-end - [:div.flex.items-baseline.gap-x-4 - (let [step-errors (:step-params fc/*form-errors*)] - (com/form-errors {:errors (or (:errors step-errors) - (when (sequential? step-errors) step-errors) - (seq (distinct (flatten-form-errors step-errors))))})) - (when (not= (first (steps linear-wizard)) - (step-key step)) - (when validation-route - (back-button linear-wizard step validation-route))) - (when (and (satisfies? Discardable step) (can-discard? step @fc/*current*)) - discard-button) - (cond next-button - next-button - - validation-route - (default-next-button linear-wizard step validation-route - :next-button-content next-button-content) - - :else - [:div "No action possible."])]]) - -(defn default-render-step [linear-wizard step & {:keys [head body footer validation-route discard-route width-height-class side-panel]}] - (let [is-last? (= (step-key step) (last (steps linear-wizard)))] - (com/modal-card-advanced - {"@keydown.enter.prevent.stop" "if ($refs.next ) {$refs.next.click()}" - :class (str - (or width-height-class " md:w-[750px] md:h-[600px] ") - " w-full h-full - group-[.forward]/transition:htmx-swapping:opacity-0 - group-[.forward]/transition:htmx-swapping:-translate-x-1/4 - group-[.forward]/transition:htmx-swapping:scale-75 - group-[.forward]/transition:htmx-swapping:ease-in - group-[.forward]/transition:htmx-added:opacity-0 - group-[.forward]/transition:htmx-added:scale-75 - group-[.forward]/transition:htmx-added:translate-x-1/4 - group-[.forward]/transition:htmx-added:ease-out - - group-[.backward]/transition:htmx-swapping:opacity-0 - group-[.backward]/transition:htmx-swapping:translate-x-1/4 - group-[.backward]/transition:htmx-swapping:scale-75 - group-[.backward]/transition:htmx-swapping:ease-in - group-[.backward]/transition:htmx-added:opacity-0 - group-[.backward]/transition:htmx-added:scale-75 - group-[.backward]/transition:htmx-added:-translate-x-1/4 - group-[.backward]/transition:htmx-added:ease-out - opacity-100 translate-x-0 scale-100" - (when is-last? "last-modal-step") - " transition duration-150 - ") - #_#_":class" (hiccup/raw "{ - \"htmx-added:opacity-0 opacity-100\": $data.transitionType=='forward', - \"htmx-swapping:translate-x-2/3 htmx-swapping:opacity-0 htmx-swapping:scale-0 htmx-added:-translate-x-2/3 htmx-added:opacity-0 htmx-added:scale-0 scale-100 translate-x-0 opacity-100\": $data.transitionType=='backward' - } - ") - "x-data" ""} - (com/modal-header {} - head) - #_(com/modal-header-attachment {}) - [:div.flex.shrink.overflow-auto.grow - (when side-panel - [:div.grow-0.w-64.bg-gray-50.border-r.hidden.md:block.overflow-y-auto - {:class "max-h-full"} - side-panel]) - (when (:render-timeline? linear-wizard) - [:div.grow-0.pr-6.pt-2.bg-gray-100.self-stretch.hidden.md:block #_{:style "margin-left:-20px"} - (render-timeline linear-wizard step validation-route)]) - (com/modal-body {} - body)] - - (com/modal-footer {} - footer)))) - -(defn wrap-ensure-step [handler] - (-> - (fn [{:keys [wizard multi-form-state] :as request}] - (assert-schema (step-schema (get-current-step wizard)) (:step-params multi-form-state)) - (handler request)) - (wrap-form-4xx-2 (fn [{:keys [wizard] :as request}] ;; THIS MAY BE BETTER TO JUST MAKE THE LINEAR WIZARD POPULATE FROM THE REQUEST - (html-response - (render-wizard wizard request) - :headers {"x-transition-type" "none" - "HX-reswap" "outerHTML"}))))) - -(defn get-transition-type [wizard from-step-key to-step-key] - (let [to-step-index (.indexOf (steps wizard) to-step-key) - - from-step-index (.indexOf (steps wizard) - from-step-key)] - (cond (= -1 to-step-index) - nil - (= -1 from-step-index) - nil - (= from-step-index to-step-index) - nil - (> from-step-index to-step-index) - "backward" - :else - "forward"))) - -(defn navigate-handler [{{:keys [wizard] :as request} :request to-step :to-step oob :oob}] - (let [current-step (get-current-step wizard) - wizard (navigate wizard to-step) - new-step (get-current-step wizard) - transition-type (get-transition-type wizard (step-key current-step) to-step)] - (html-response - (render-wizard wizard - (-> request - (assoc :multi-form-state (-> (:multi-form-state request) - (merge-multi-form-state) - (select-state (edit-path new-step request) {}) - (#(cond-> % - (satisfies? Initializable new-step) - (assoc :step-params - (init-step-params new-step % request)))))))) - :headers {"HX-reswap" (when transition-type "outerHTML swap:0.16s") - "x-transition-type" (or transition-type "none")} - :oob (or oob [])))) - -(def next-handler - - (-> (fn [{:keys [wizard] :as request}] - (let [current-step (get-current-step wizard)] - (if (satisfies? CustomNext current-step) - (custom-next-handler current-step request) - (navigate-handler {:request request - :to-step (:to (:query-params request))})))) - (wrap-ensure-step) - (wrap-schema-enforce :query-schema - [:map - [:to {:optional true} [:maybe step-key-schema]]]))) - -(def discard-handler - (-> - (fn [{:keys [wizard multi-form-state] :as request}] - (let [current-step (get-current-step wizard) - to-step (:to (:query-params request)) - wizard (navigate wizard to-step) - transition-type (get-transition-type wizard (step-key current-step) to-step)] - (html-response - (render-wizard wizard - (-> request - (assoc :multi-form-state (discard-changes current-step multi-form-state)))) - :headers {"HX-reswap" (when transition-type "outerHTML swap:0.16s") - "x-transition-type" (or transition-type "none")}))) - (wrap-schema-enforce :query-schema - [:map - [:to step-key-schema]]))) - -(def submit-handler - (-> (fn [{:keys [wizard multi-form-state] :as request}] - (submit wizard (-> request - (assoc :multi-form-state (merge-multi-form-state multi-form-state))))) - (wrap-ensure-step))) - -(defn default-render-wizard [linear-wizard {:keys [multi-form-state form-errors snapshot current-step] :as request} & {:keys [form-params render-timeline?] - :or {render-timeline? true}}] - (let [current-step (get-current-step (assoc linear-wizard :render-timeline? render-timeline?)) - edit-path (edit-path current-step request)] - [:form#wizard-form form-params - (fc/start-form multi-form-state (when form-errors {:step-params form-errors}) - (list - (fc/with-field :snapshot - (com/hidden {:name (fc/field-name) - :value (pr-str (fc/field-value))})) - (fc/with-field :edit-path - (com/hidden {:name (fc/field-name) - :value (pr-str (or edit-path []))})) - (com/hidden {:name "current-step" - :value (pr-str (step-key current-step))}) - - (fc/with-field :step-params - (com/modal - {:id "wizardmodal"} - - (render-step current-step request)))))])) - -(defn wrap-wizard [handler linear-wizard] - (fn [request] - (let [current-step-key (if-let [current-step (get (:form-params request) "current-step")] - (mc/decode step-key-schema current-step main-transformer) - (first (steps linear-wizard))) - current-step (get-step linear-wizard current-step-key) - multi-form-state (-> (:multi-form-state request) - (update :snapshot (fn [snapshot] - (mc/decode (form-schema linear-wizard) - snapshot - main-transformer))) - (update :step-params (fn [step-params] - (or - (mc/decode (step-schema current-step) - step-params - main-transformer) - {} ;; Todo add a defaultable - )))) - request (-> request - (assoc :multi-form-state multi-form-state)) - linear-wizard (navigate linear-wizard current-step-key)] - (handler - (assoc request :wizard (hydrate-from-request linear-wizard request)))))) - -(defn open-wizard-handler [{:keys [wizard current-step query-params] :as request}] - (cond-> - (modal-response - [:div#transitioner.flex-1 {:x-data (hx/json {"transitionType" "none"}) - :x-ref "transitioner" - :class "" - "@htmx:after-request" "if(event.detail.xhr.getResponseHeader('x-transition-type')) { - $refs.transitioner.classList.remove('forward') - $refs.transitioner.classList.remove('backward'); - $refs.transitioner.classList.add('group/transition') - $refs.transitioner.classList.add(event.detail.xhr.getResponseHeader('x-transition-type')); - } else { - - $refs.transitioner.classList.remove('group/transition') - } - "} - (render-wizard wizard request)]) - (get query-params :replace-modal) (assoc-in [:headers "hx-trigger"] "modalswap"))) - -(defn wrap-init-multi-form-state [handler get-multi-form-state] - (-> - (fn init-multi-form [request] - (handler (assoc request :multi-form-state (get-multi-form-state request)))) - (wrap-nested-form-params))) - -(defn wrap-decode-multi-form-state [handler] - (wrap-init-multi-form-state - handler - (fn parse-multi-form-state [request] - (map->MultiStepFormState (mc/decode [:map - [:snapshot {:optional true - :decode/arbitrary - #(clojure.edn/read-string {:readers clj-time.coerce/data-readers - :eof nil} - %)} - [:maybe :any]] - [:edit-path {:optional true :decode/arbitrary (fn [z] - (clojure.edn/read-string z))} [:maybe [:sequential {:min 0} any?]]] - [:step-params {:optional true} - [:maybe - :any]]] - (:form-params request) - main-transformer))))) - -#_(comment - (def f {"snapshot" - "{:invoices [{:invoice_id 17592297837035, :amount 23.0, :invoice {:db/id 17592297837035, :invoice/vendor {:db/id 17592186045722, :vendor/name \"Sysco\"}, :invoice/client {:db/id 17592232555238}, :invoice/outstanding-balance 23.0, :invoice/invoice-number \"702,34\"}} {:invoice_id 17592297837049, :amount 23.0, :invoice {:db/id 17592297837049, :invoice/vendor {:db/id 17592186045722, :vendor/name \"Sysco\"}, :invoice/client {:db/id 17592232555238}, :invoice/outstanding-balance 23.0, :invoice/invoice-number \"80[234234\"}}], :client 17592232555238}", - "edit-path" "[]", - "current-step" ":payment-details", - "mode" "advanced", - "step-params" - {"invoices" - {"0" {"invoice_id" "17592297837035", "amount" "1"}, - "1" {"invoice_id" "17592297837049", "amount" "23.00"}}}}) - (mc/decode [:map [:step-params {:optional true} [:maybe :any]]] - f - main-transformer)) \ No newline at end of file diff --git a/test/clj/auto_ap/ssr/transaction/edit_simple_advanced_mode_test.clj b/test/clj/auto_ap/ssr/transaction/edit_simple_advanced_mode_test.clj deleted file mode 100644 index cf092feb..00000000 --- a/test/clj/auto_ap/ssr/transaction/edit_simple_advanced_mode_test.clj +++ /dev/null @@ -1,1319 +0,0 @@ -(ns auto-ap.ssr.transaction.edit-simple-advanced-mode-test - (:require - [auto-ap.datomic :refer [conn]] - [auto-ap.integration.util :refer [wrap-setup]] - [auto-ap.solr] - [auto-ap.ssr.components.multi-modal :as mm] - [auto-ap.ssr.form-cursor :as fc] - [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]] - [datomic.api :as dc] - [hiccup.core :as hiccup])) - -;; Private save-handler accessible via var reference -(def ^:private save-handler - #'auto-ap.ssr.transaction.edit/save-handler) - -(use-fixtures :each wrap-setup) - -;;; --------------------------------------------------------------------------- -;;; Helpers -;;; --------------------------------------------------------------------------- - -(defn- tempid->id - "Resolve a string temp ID to its numeric Datomic entity ID after transact." - [result temp-id] - (get (:tempids result) temp-id)) - -;;; --------------------------------------------------------------------------- -;;; AC1-3: manual-mode-initial — mode selection on open -;;; --------------------------------------------------------------------------- - -;; manual-mode-initial is private; access via var reference -(def ^:private manual-mode-initial - #'auto-ap.ssr.transaction.edit/manual-mode-initial) - -(deftest manual-mode-initial-test - (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: 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: 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}]}))) - (is (= :advanced (manual-mode-initial {:db/id 123 - :transaction/accounts [{} {} {}]}))))) - -;;; --------------------------------------------------------------------------- -;;; AC4-5: edit-vendor-changed-handler — vendor selection in simple mode -;;; --------------------------------------------------------------------------- - -(deftest edit-vendor-changed-simple-mode-test - (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"} - {:db/id "account-id" - :account/name "Test Account" - :account/type :account-type/expense} - {:db/id "vendor-id" - :vendor/default-account "account-id"} - {:db/id "client-id" - :client/code "TESTCL" - :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") - request {:multi-form-state (mm/->MultiStepFormState - {:db/id tx-id - :transaction/client client-id - :transaction/accounts []} - [] - {:mode "simple" - :transaction/vendor vendor-id - :transaction/accounts []}) - :entity {:db/id tx-id - :transaction/client {:db/id client-id} - :transaction/amount 100.0}} - response (edit-vendor-changed-handler request) - body (:body response)] - ;; Response should contain the manual-coding-section div - (is (re-find #"manual-coding-section" body) - "Response should contain the manual-coding-section element") - ;; The vendor's default account ID should appear in the rendered HTML - (is (re-find (re-pattern (str account-id)) body) - "Response should contain the vendor's default account ID") - ;; The account's name should appear in the rendered HTML - (is (re-find #"Test Account" body) - "Response should contain the vendor's default account name"))) - - (testing "AC5: vendor selection in simple mode DOES overwrite already-set account" - (let [result @(dc/transact conn [{:db/id "vendor-id" - :vendor/name "Test Vendor"} - {:db/id "account-id" - :account/name "Test Account" - :account/type :account-type/expense} - {:db/id "vendor-id" - :vendor/default-account "account-id"} - {:db/id "other-account-id" - :account/name "Other Account" - :account/type :account-type/expense} - {:db/id "client-id" - :client/code "TESTCL2" - :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") - other-account-id (tempid->id result "other-account-id") - client-id (tempid->id result "client-id") - ;; existing-accounts already set — but simple mode should still overwrite - existing-accounts [{:db/id "row-id" - :transaction-account/account other-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 existing-accounts} - [] - {:mode "simple" - :transaction/vendor vendor-id - :transaction/accounts existing-accounts}) - :entity {:db/id tx-id - :transaction/client {:db/id client-id} - :transaction/amount 100.0}} - response (edit-vendor-changed-handler request) - body (:body response)] - ;; The handler returns an html-response; verify the body is HTML - (is (re-find #"manual-coding-section" body) - "Response body should contain the manual-coding-section element") - ;; The vendor's default account SHOULD appear (overwriting previous) - (is (re-find (re-pattern (str account-id)) body) - "Vendor change in simple mode should overwrite with vendor's default account") - ;; The previous account should NOT appear - (is (not (re-find (re-pattern (str other-account-id)) body)) - "Previous account should be replaced by vendor default")))) - -;;; --------------------------------------------------------------------------- -;;; AC6: save round-trip — manual mode saves vendor + account to DB -;;; --------------------------------------------------------------------------- - -(deftest save-manual-round-trip-test - (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" - :account/name "Save Account" - :account/type :account-type/expense} - {:db/id "client-id" - :client/code "SAVECL" - :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") - new-row-id (str (java.util.UUID/randomUUID)) - snapshot {:db/id tx-id - :transaction/client client-id - :action :manual - :transaction/vendor vendor-id - :transaction/amount 100.0 - :transaction/accounts [{:db/id new-row-id - :transaction-account/account account-id - :transaction-account/location "Shared" - :transaction-account/amount 100.0}]} - request {:multi-form-state (mm/->MultiStepFormState snapshot [] snapshot) - :entity {:db/id tx-id - :transaction/client {:db/id client-id} - :transaction/amount 100.0} - :identity {:user/role "admin"}}] - (with-redefs [auto-ap.solr/impl (auto-ap.solr/->InMemSolrClient (atom {}))] - (save-handler request)) - ;; Verify the transaction was saved to DB with vendor and accounts - (let [saved (dc/pull (dc/db conn) - '[:db/id - {:transaction/vendor [:db/id]} - {:transaction/accounts [{:transaction-account/account [:db/id]} - :transaction-account/location - :transaction-account/amount]}] - tx-id)] - (is (= vendor-id (-> saved :transaction/vendor :db/id)) - "Vendor should be saved on the transaction") - (is (= 1 (count (:transaction/accounts saved))) - "Exactly one account row should be saved") - (is (= account-id (-> saved :transaction/accounts first :transaction-account/account :db/id)) - "The correct account should be saved") - ;; "Shared" is spread to client-specific locations by maybe-spread-locations - (is (= "DT" (-> saved :transaction/accounts first :transaction-account/location)) - "The location should be saved (Shared spreads to client locations)") - (is (= 100.0 (-> saved :transaction/accounts first :transaction-account/amount)) - "The amount should be saved"))))) - -;;; --------------------------------------------------------------------------- -;;; AC7: simple mode account typeahead respects :account/default-allowance rules -;;; --------------------------------------------------------------------------- - -(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"]} - {:db/id "account-id" - :account/name "Toggle 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-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 simple-account} - [] - {:mode "simple" - :transaction/accounts simple-account}) - :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 (re-find #"manual-coding-section" body) - "Response body should contain the coding section element") - ;; Advanced mode has Switch to simple mode link (when <=1 row) - (is (re-find #"Switch to simple mode" body) - "After toggling to advanced mode (from simple with 1 row), 'Switch to simple mode' should appear") - ;; The account-grid-body section should be present in the advanced mode HTML - (is (re-find #"account-grid-body" body) - "Advanced mode response should contain the account-grid-body element") - ;; The account ID from the simple-mode row should appear in the advanced table HTML - (is (re-find (re-pattern (str account-id)) body) - "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")))) - -;;; --------------------------------------------------------------------------- -;;; 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 "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)] - ;; 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 "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") - zero-rows [] - one-row [{:db/id "row-1" - :transaction-account/account account-id - :transaction-account/location "DT" - :transaction-account/amount 100.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}}) - 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 "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"]} - {:db/id "account-id" - :account/name "Account1" - :account/type :account-type/expense} - {:db/id "account-id-2" - :account/name "Account2" - :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") - 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 - :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 (not (re-find #"Switch to simple mode" html)) - "In advanced mode with 2+ rows, 'Switch to simple mode' link should be absent")))) - -;;; --------------------------------------------------------------------------- -;;; AC13: advanced → simple (1 row) re-renders in simple 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 "TOGSIMCL" - :client/locations ["DT"]} - {:db/id "account-id" - :account/name "Toggle 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}] - 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)] - ;; 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 (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)] - ;; 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 - ;; NOTE: memo and approval-status fields are rendered by LinksStep/render-step - ;; (outside of #manual-coding-section), so they cannot be tested at the - ;; manual-coding-section* level. This test covers what manual-coding-section* - ;; is directly responsible for. - (testing "AC17: key fields are present in both simple and advanced modes" - (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)))))] - ;; --- Vendor field assertions (both modes) --- - (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") - ;; --- Hidden mode input (proves section is complete) --- - (is (re-find #"name=\"mode\".*value=\"simple\"" html-simple) - "AC17: hidden mode=simple input must be present in simple mode") - (is (re-find #"name=\"mode\".*value=\"advanced\"" html-advanced) - "AC17: hidden mode=advanced input must be present in advanced mode") - ;; --- Simple-mode-specific fields: account typeahead, location select, toggle link --- - (is (re-find #"account/search" html-simple) - "AC17: account typeahead search URL must appear in simple mode") - (is (re-find #"transaction-account/location" html-simple) - "AC17: location select field name must appear in simple mode") - (is (re-find #"Switch to advanced mode" html-simple) - "AC17: toggle link to advanced mode must appear in simple mode") - ;; --- Advanced-mode-specific: account-grid-body and toggle link --- - (is (re-find #"account-grid-body" html-advanced) - "AC17: #account-grid-body must be present in advanced mode") - (is (re-find #"Switch to simple mode" html-advanced) - "AC17: toggle link to simple mode must appear in advanced mode (<=1 row)")))) - -;;; --------------------------------------------------------------------------- -;;; 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 (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" - (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))))] - ;; 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" - (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")))) - -;;; --------------------------------------------------------------------------- -;;; Bug: vendor selection gets erased on vendor-changed HTMX response -;;; --------------------------------------------------------------------------- - -(deftest vendor-selection-preserved-in-htmx-response-test - (testing "BUG: vendor selection should be preserved when HTMX re-renders the edit form" - (let [result @(dc/transact conn [{:db/id "vendor-id" - :vendor/name "Test Vendor"} - {:db/id "account-id" - :account/name "Existing Account" - :account/type :account-type/expense} - {:db/id "client-id" - :client/code "VENDORCL" - :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") - ;; Simulate the request after middleware decoding. - ;; In production, form values arrive as strings. The middleware decodes - ;; step-params with keyword keys but leaves values as strings. - existing-accounts [{: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 existing-accounts} - [] - {:mode "simple" - ;; This is how the vendor ID arrives from the form: - ;; as a string, not a long. - :transaction/vendor (str vendor-id) - :transaction/accounts existing-accounts}) - :entity {:db/id tx-id - :transaction/client {:db/id client-id} - :transaction/amount 100.0}} - ;; The handler should return a successful response with the vendor - ;; preserved. Currently it crashes because the string vendor-id is - ;; not converted to a long before being passed to Datomic. - response (try - (edit-vendor-changed-handler request) - (catch Exception e - {:error e}))] - (is (not (:error response)) - (str "BUG: String vendor-id from form submission should be converted to long. " - "Server crashes with: " (some-> response :error ex-message))) - (when-not (:error response) - (is (= 200 (:status response)) - "Response should be successful") - (is (re-find #"Test Vendor" (:body response)) - "Vendor name should appear in the HTMX response") - (is (re-find (re-pattern (str vendor-id)) (:body response)) - "Vendor ID should be preserved in the response HTML"))))) - -;;; --------------------------------------------------------------------------- -;;; Bug: vendor change does not populate account -;;; --------------------------------------------------------------------------- - -(deftest vendor-change-simple-mode-overwrites-test - (testing "BUG: vendor change in simple mode should overwrite existing account" - ;; When a vendor is changed in simple mode, it should always populate - ;; the vendor's default account, even if an account was already set. - (let [result @(dc/transact conn [{:db/id "vendor-id" - :vendor/name "Vendor With Default"} - {: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 "Previously Selected Account" - :account/type :account-type/expense} - {:db/id "client-id" - :client/code "VENDORCL" - :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") - ;; Simulate form state with an already-selected account (as the form submits) - existing-accounts [{:db/id "row-1" - :transaction-account/account existing-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 existing-accounts} - [] - {:mode "simple" - :transaction/vendor vendor-id - :transaction/accounts existing-accounts}) - :entity {:db/id tx-id - :transaction/client {:db/id client-id} - :transaction/amount 100.0}} - response (edit-vendor-changed-handler request) - body (:body response)] - ;; The vendor's default account SHOULD appear (overwriting the previous) - (is (re-find (re-pattern (str vendor-account-id)) body) - "BUG: Vendor change in simple mode should overwrite with vendor's default account") - ;; The previously selected account should NOT appear - (is (not (re-find (re-pattern (str existing-account-id)) body)) - "Previously selected account should be replaced by vendor default") - (is (re-find #"Vendor Default Account" body) - "Vendor default account name should appear")))) - -(deftest vendor-change-advanced-mode-empty-row-test - (testing "BUG: vendor change in advanced mode should populate empty row" - ;; In advanced mode with 1 empty row, changing vendor should populate it - (let [result @(dc/transact conn [{:db/id "vendor-id" - :vendor/name "Vendor With Default"} - {: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 "client-id" - :client/code "ADVEMPTYCL" - :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") - client-id (tempid->id result "client-id") - ;; Simulate advanced mode with 1 empty row (account=nil, as form submits) - empty-row [{:db/id "row-1" - :transaction-account/account nil - :transaction-account/location "Shared" - :transaction-account/amount 100.0}] - request {:multi-form-state (mm/->MultiStepFormState - {:db/id tx-id - :transaction/client client-id - :transaction/accounts empty-row} - [] - {:mode "advanced" - :transaction/vendor vendor-id - :transaction/accounts empty-row}) - :entity {:db/id tx-id - :transaction/client {:db/id client-id} - :transaction/amount 100.0}} - response (edit-vendor-changed-handler request) - body (:body response)] - ;; The vendor's default account SHOULD appear in the row - (is (re-find (re-pattern (str vendor-account-id)) body) - "BUG: Vendor change in advanced mode with empty row should populate it") - (is (re-find #"Vendor Default Account" body) - "Vendor default account name should appear in the row")))) - -(deftest vendor-change-advanced-mode-filled-row-test - (testing "AC15b: vendor change in advanced mode with filled row should NOT overwrite" - ;; In advanced mode with 1 row that already has an account selected, - ;; changing vendor should NOT overwrite it - (let [result @(dc/transact conn [{:db/id "vendor-id" - :vendor/name "Vendor With Default"} - {: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 "Manually Selected Account" - :account/type :account-type/expense} - {:db/id "client-id" - :client/code "ADVFILLEDCL" - :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") - ;; Advanced mode with 1 row that already has an account - filled-row [{:db/id "row-1" - :transaction-account/account existing-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 filled-row} - [] - {:mode "advanced" - :transaction/vendor vendor-id - :transaction/accounts filled-row}) - :entity {:db/id tx-id - :transaction/client {:db/id client-id} - :transaction/amount 100.0}} - response (edit-vendor-changed-handler request) - body (:body response)] - ;; The existing account should still be there - (is (re-find (re-pattern (str existing-account-id)) body) - "Existing account should remain when vendor changes in advanced mode with filled row") - ;; The vendor's default account should NOT appear - (is (not (re-find (re-pattern (str vendor-account-id)) body)) - "Vendor default should NOT overwrite filled row in advanced mode")))) - -(deftest vendor-change-advanced-mode-two-rows-test - (testing "AC15c: vendor change in advanced mode with 2+ rows should NOT modify any" - ;; In advanced mode with 2 or more rows, vendor change should not touch any row - (let [result @(dc/transact conn [{:db/id "vendor-id" - :vendor/name "Vendor With Default"} - {: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 "account-1" - :account/name "Account One" - :account/type :account-type/expense} - {:db/id "account-2" - :account/name "Account Two" - :account/type :account-type/expense} - {:db/id "client-id" - :client/code "ADVTWOROWCL" - :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") - account-1 (tempid->id result "account-1") - account-2 (tempid->id result "account-2") - client-id (tempid->id result "client-id") - ;; Advanced mode with 2 rows - two-rows [{:db/id "row-1" - :transaction-account/account account-1 - :transaction-account/location "DT" - :transaction-account/amount 50.0} - {:db/id "row-2" - :transaction-account/account account-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/vendor vendor-id - :transaction/accounts two-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)] - ;; Both existing accounts should remain - (is (re-find (re-pattern (str account-1)) body) - "First row account should remain") - (is (re-find (re-pattern (str account-2)) body) - "Second row account should remain") - ;; Vendor default should NOT appear - (is (not (re-find (re-pattern (str vendor-account-id)) body)) - "Vendor default should NOT modify rows when 2+ exist")))) - -(deftest vendor-change-client-specific-override-test - (testing "BUG: vendor change should use client-specific account override if present" - ;; When a vendor has a client-specific account override, changing vendor - ;; should populate the client-specific account, not the global default. - (let [result @(dc/transact conn [{:db/id "global-account-id" - :account/name "Global Default" - :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 "CLIOVERRIDE" - :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") - global-account-id (tempid->id result "global-account-id") - client-specific-account-id (tempid->id result "client-specific-account-id") - ;; Simple mode with empty account row - empty-row [{:db/id "row-1" - :transaction-account/account nil - :transaction-account/location "Shared" - :transaction-account/amount 100.0}] - request {:multi-form-state (mm/->MultiStepFormState - {:db/id 999999 - :transaction/client client-id - :transaction/accounts empty-row} - [] - {:mode "simple" - :transaction/vendor vendor-id - :transaction/accounts empty-row}) - :entity {:db/id 999999 - :transaction/client {:db/id client-id} - :transaction/amount 100.0}} - response (edit-vendor-changed-handler request) - body (:body response)] - ;; The client-specific account should appear, not the global default - (is (re-find (re-pattern (str client-specific-account-id)) body) - "BUG: Vendor change should populate client-specific account override") - (is (re-find #"Client Specific Account" body) - "Client-specific account name should appear") - ;; The global default should NOT appear - (is (not (re-find (re-pattern (str global-account-id)) body)) - "Global vendor default should NOT appear when client override exists")))) - -;;; Update AC5: simple mode SHOULD overwrite existing accounts -(deftest vendor-change-simple-mode-overwrites-ac5-test - (testing "AC5 UPDATED: vendor selection in simple mode DOES overwrite already-set account" - (let [result @(dc/transact conn [{:db/id "vendor-id" - :vendor/name "Test Vendor"} - {:db/id "account-id" - :account/name "Test Account" - :account/type :account-type/expense} - {:db/id "vendor-id" - :vendor/default-account "account-id"} - {:db/id "other-account-id" - :account/name "Other Account" - :account/type :account-type/expense} - {:db/id "client-id" - :client/code "TESTCL2" - :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") - other-account-id (tempid->id result "other-account-id") - client-id (tempid->id result "client-id") - ;; existing-accounts already set — but simple mode should still overwrite - existing-accounts [{:db/id "row-id" - :transaction-account/account other-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 existing-accounts} - [] - {:mode "simple" - :transaction/vendor vendor-id - :transaction/accounts existing-accounts}) - :entity {:db/id tx-id - :transaction/client {:db/id client-id} - :transaction/amount 100.0}} - response (edit-vendor-changed-handler request) - body (:body response)] - ;; The handler returns an html-response; verify the body is HTML - (is (re-find #"manual-coding-section" body) - "Response body should contain the manual-coding-section element") - ;; The vendor's default account SHOULD appear (overwriting previous) - (is (re-find (re-pattern (str account-id)) body) - "BUG: Vendor change in simple mode should overwrite with vendor's default account") - ;; The previous account should NOT appear - (is (not (re-find (re-pattern (str other-account-id)) body)) - "Previous account should be replaced by vendor default")))) diff --git a/test/clj/auto_ap/test_server.clj b/test/clj/auto_ap/test_server.clj index a1549e6f..d0276d1b 100644 --- a/test/clj/auto_ap/test_server.clj +++ b/test/clj/auto_ap/test_server.clj @@ -6,7 +6,6 @@ [auto-ap.integration.util :refer [setup-test-data test-client test-bank-account test-transaction test-payment test-invoice]] [auto-ap.routes.transactions :as route] [auto-ap.ssr.transaction.edit :as edit] - [auto-ap.ssr.components.multi-modal :as mm] [auto-ap.ssr.utils :refer [wrap-entity wrap-schema-enforce]] [auto-ap.permissions :refer [wrap-must]] [auto-ap.datomic.transactions :as d-transactions]