refactor(ssr): collapse 5 manual-coding operation routes into edit-form-changed (heuristic 6)

The 5 manual-coding operations (vendor change, simple/advanced toggle, add row, remove
row, $/% toggle) each had their own route + handler, all doing "mutate form state ->
render-full-form". Fold them into the single edit-form-changed endpoint, which now
dispatches on an `op` form-param to the relevant pure apply-* mutation fn (apply-vendor-
changed / apply-toggle-mode / apply-new-account / apply-remove-account /
apply-toggle-amount-mode) then re-renders. A missing/unknown op (a plain dependent-field
change, e.g. account->location or amount->totals) just re-renders, as before.

- edit.clj: 6 handlers -> 1 dispatcher + 5 pure apply-* fns; markup posts to
  edit-form-changed with :hx-vals {:op "..."}.
- routes/transactions.cljc: remove the 5 now-unused route keys.
- e2e specs: retarget the vendor selector by op (div[hx-vals*="vendor-changed"]) and
  point the toggle-amount-mode / vendor response waits at edit-form-changed, since the
  old per-op route names are gone. (Behavioral assertions unchanged.)

Scorecard: manual-coding routes ~10 -> ~5 (operations now one dispatcher). Parity held:
swap spec 6/6, full suite 32 pass (Shared Location green; no new regression).
This commit is contained in:
2026-06-03 07:07:52 -07:00
parent 1d5a95196f
commit 0f5650b73e
5 changed files with 63 additions and 79 deletions

View File

@@ -39,7 +39,7 @@ Each migration appends one row (after-numbers), referencing the before in the di
| Phase | Modal | LOC | Routes | no-cursor twins | faked roots | snapshot merges | OOB | mixed hx- | cookbook reused / added | | Phase | Modal | LOC | Routes | no-cursor twins | faked roots | snapshot merges | OOB | mixed hx- | cookbook reused / added |
|-------|-------|-----|--------|-----------------|-------------|-----------------|-----|-----------|-------------------------| |-------|-------|-----|--------|-----------------|-------------|-----------------|-----|-----------|-------------------------|
| 1 (baseline) | Transaction Edit `transaction/edit.clj` | 1608 | ~12 | 1 | 2 | ~75 | 0 | 8 | — / seeded 7 entries | | 1 (baseline) | Transaction Edit `transaction/edit.clj` | 1608 | ~12 | 1 | 2 | ~75 | 0 | 8 | — / seeded 7 entries |
| 2 (in progress) | Transaction Edit `transaction/edit.clj` | ~1558 | **~10** | **0** | **0** | ~75 | 0 | 8 | — / 0 | | 2 (in progress) | Transaction Edit `transaction/edit.clj` | ~1530 | **~5** | **0** | **0** | ~75 | 0 | 8 | — / 0 |
> **Phase 2 progress (partial).** Achieved with parity held (swap spec 6/6 + Shared > **Phase 2 progress (partial).** Achieved with parity held (swap spec 6/6 + Shared
> Location green, full suite 31 pass / no regression): deleted the dead `*-no-cursor*` > Location green, full suite 31 pass / no regression): deleted the dead `*-no-cursor*`

View File

@@ -48,7 +48,7 @@ async function openManualAdvanced(page: any, transactionIndex = 0) {
// (Solr is unavailable in tests), click it, and wait for the whole-form swap. // (Solr is unavailable in tests), click it, and wait for the whole-form swap.
async function selectVendor(page: any, vendorId: number, label: string) { async function selectVendor(page: any, vendorId: number, label: string) {
const vendor = page const vendor = page
.locator('div[hx-post*="edit-vendor-changed"]') .locator('div[hx-vals*="vendor-changed"]')
.first() .first()
.locator('div.relative[x-data]') .locator('div.relative[x-data]')
.first(); .first();
@@ -62,7 +62,7 @@ async function selectVendor(page: any, vendorId: number, label: string) {
const swap = page.waitForResponse( const swap = page.waitForResponse(
(r: any) => (r: any) =>
r.url().includes('edit-vendor-changed') && r.url().includes('edit-form-changed') &&
r.request().method() === 'POST' && r.request().method() === 'POST' &&
r.status() === 200 r.status() === 200
); );
@@ -303,7 +303,7 @@ test.describe('Transaction Edit whole-form swap', () => {
await page.locator('button[hx-get*="/transaction2/"][hx-get*="/edit"]').nth(0).click(); await page.locator('button[hx-get*="/transaction2/"][hx-get*="/edit"]').nth(0).click();
await page.waitForSelector('#wizardmodal'); await page.waitForSelector('#wizardmodal');
await page.click('button:has-text("Manual")'); await page.click('button:has-text("Manual")');
await page.waitForSelector('div[hx-post*="edit-vendor-changed"]'); await page.waitForSelector('div[hx-vals*="vendor-changed"]');
const testInfo = await (await page.request.get('/test-info')).json(); const testInfo = await (await page.request.get('/test-info')).json();
const vendorId: number = testInfo.accounts.vendor; const vendorId: number = testInfo.accounts.vendor;
@@ -311,7 +311,7 @@ test.describe('Transaction Edit whole-form swap', () => {
// Drive the vendor typeahead like a user: open dropdown, inject a result // Drive the vendor typeahead like a user: open dropdown, inject a result
// (Solr is unavailable in tests), click it. // (Solr is unavailable in tests), click it.
const vendor = page.locator('div[hx-post*="edit-vendor-changed"]').first().locator('div.relative[x-data]').first(); const vendor = page.locator('div[hx-vals*="vendor-changed"]').first().locator('div.relative[x-data]').first();
await vendor.locator('a[x-ref="input"]').click(); await vendor.locator('a[x-ref="input"]').click();
const search = page.locator('[data-tippy-root] input[x-model="search"]').first(); const search = page.locator('[data-tippy-root] input[x-model="search"]').first();
await search.waitFor({ state: 'visible' }); await search.waitFor({ state: 'visible' });
@@ -322,7 +322,7 @@ test.describe('Transaction Edit whole-form swap', () => {
const swap = page.waitForResponse( const swap = page.waitForResponse(
(r: any) => (r: any) =>
r.url().includes('edit-vendor-changed') && r.url().includes('edit-form-changed') &&
r.request().method() === 'POST' && r.request().method() === 'POST' &&
r.status() === 200 r.status() === 200
); );
@@ -352,7 +352,7 @@ test.describe('Transaction Edit whole-form swap', () => {
await page.locator('button[hx-get*="/transaction2/"][hx-get*="/edit"]').nth(0).click(); await page.locator('button[hx-get*="/transaction2/"][hx-get*="/edit"]').nth(0).click();
await page.waitForSelector('#wizardmodal'); await page.waitForSelector('#wizardmodal');
await page.click('button:has-text("Manual")'); await page.click('button:has-text("Manual")');
await page.waitForSelector('div[hx-post*="edit-vendor-changed"]'); await page.waitForSelector('div[hx-vals*="vendor-changed"]');
const testInfo = await (await page.request.get('/test-info')).json(); const testInfo = await (await page.request.get('/test-info')).json();
const vendor1: number = testInfo.accounts.vendor; const vendor1: number = testInfo.accounts.vendor;
@@ -361,7 +361,7 @@ test.describe('Transaction Edit whole-form swap', () => {
const account2: number = testInfo.accounts['second-account']; const account2: number = testInfo.accounts['second-account'];
const vendorLabel = page const vendorLabel = page
.locator('div[hx-post*="edit-vendor-changed"] span[x-text="value.label"]') .locator('div[hx-vals*="vendor-changed"] span[x-text="value.label"]')
.first(); .first();
const accountHidden = page const accountHidden = page
.locator('input[type="hidden"][name*="transaction-account/account"]') .locator('input[type="hidden"][name*="transaction-account/account"]')

View File

@@ -150,7 +150,7 @@ async function toggleToPercentMode(page: any) {
// Wait for HTMX to swap the grid body // Wait for HTMX to swap the grid body
await page.waitForResponse(response => await page.waitForResponse(response =>
response.url().includes('/toggle-amount-mode') && response.status() === 200 response.url().includes('/edit-form-changed') && response.status() === 200
); );
await page.waitForTimeout(200); await page.waitForTimeout(200);
} }
@@ -161,7 +161,7 @@ async function toggleToDollarMode(page: any) {
// Wait for HTMX to swap the grid body // Wait for HTMX to swap the grid body
await page.waitForResponse(response => await page.waitForResponse(response =>
response.url().includes('/toggle-amount-mode') && response.status() === 200 response.url().includes('/edit-form-changed') && response.status() === 200
); );
await page.waitForTimeout(200); await page.waitForTimeout(200);
} }
@@ -359,7 +359,7 @@ async function selectVendorFromTypeahead(page: any, vendorName: string) {
throw new Error(`Could not find vendor with name ${vendorName}`); throw new Error(`Could not find vendor with name ${vendorName}`);
} }
const vendorContainer = page.locator('div[hx-post*="edit-vendor-changed"]').first(); const vendorContainer = page.locator('div[hx-vals*="vendor-changed"]').first();
const vendorHidden = vendorContainer.locator('input[type="hidden"]').first(); const vendorHidden = vendorContainer.locator('input[type="hidden"]').first();
await vendorHidden.evaluate((el: HTMLInputElement, value: string) => { await vendorHidden.evaluate((el: HTMLInputElement, value: string) => {
@@ -374,7 +374,7 @@ async function selectVendorFromTypeahead(page: any, vendorName: string) {
el.dispatchEvent(new Event('change', { bubbles: true })); el.dispatchEvent(new Event('change', { bubbles: true }));
}); });
await page.waitForResponse(response => response.url().includes('/edit-vendor-changed') && response.status() === 200); await page.waitForResponse(response => response.url().includes('/edit-form-changed') && response.status() === 200);
await page.waitForTimeout(500); await page.waitForTimeout(500);
} }
@@ -434,11 +434,11 @@ test.describe('Transaction Edit Vendor Pre-population', () => {
// `elements` instead of being fetched. Everything else -- the dropdown's own // `elements` instead of being fetched. Everything else -- the dropdown's own
// search input firing a native `change` on blur, the `value = element` click // search input firing a native `change` on blur, the `value = element` click
// handler, the Alpine reactivity, and the HTMX round-trip to // handler, the Alpine reactivity, and the HTMX round-trip to
// `edit-vendor-changed` -- runs exactly as in production. This is the flow that // `edit-form-changed` (op=vendor-changed) -- runs exactly as in production. This is the flow that
// regressed: a stale native `change` from the search input used to win the race // regressed: a stale native `change` from the search input used to win the race
// and revert the vendor to its previous value. // and revert the vendor to its previous value.
async function selectVendorViaDropdown(page: any, vendorId: number, vendorName: string) { async function selectVendorViaDropdown(page: any, vendorId: number, vendorName: string) {
const wrapper = page.locator('div[hx-post*="edit-vendor-changed"]').first(); const wrapper = page.locator('div[hx-vals*="vendor-changed"]').first();
const typeahead = wrapper.locator('div.relative[x-data]').first(); const typeahead = wrapper.locator('div.relative[x-data]').first();
// Open the dropdown (tippy renders the popper into [data-tippy-root]). // Open the dropdown (tippy renders the popper into [data-tippy-root]).
@@ -466,7 +466,7 @@ async function selectVendorViaDropdown(page: any, vendorId: number, vendorName:
await page.waitForResponse( await page.waitForResponse(
(response: any) => (response: any) =>
response.url().includes('/edit-vendor-changed') && response.status() === 200 response.url().includes('/edit-form-changed') && response.status() === 200
); );
await page.waitForTimeout(500); await page.waitForTimeout(500);
} }
@@ -485,7 +485,7 @@ async function openManualVendorSection(page: any, transactionIndex: number) {
await page.waitForSelector('#modal-holder[x-show="open"]', { state: 'visible' }); await page.waitForSelector('#modal-holder[x-show="open"]', { state: 'visible' });
await page.waitForSelector('#wizardmodal'); await page.waitForSelector('#wizardmodal');
await page.click('button:has-text("Manual")'); await page.click('button:has-text("Manual")');
await page.waitForSelector('div[hx-post*="edit-vendor-changed"]'); await page.waitForSelector('div[hx-vals*="vendor-changed"]');
} }
test.describe('Transaction Edit Vendor Selection', () => { test.describe('Transaction Edit Vendor Selection', () => {
@@ -501,14 +501,14 @@ test.describe('Transaction Edit Vendor Selection', () => {
// round-trip. Before the fix this reverted to blank because a stale // round-trip. Before the fix this reverted to blank because a stale
// `change` event submitted the previous vendor and its response won. // `change` event submitted the previous vendor and its response won.
const label = page const label = page
.locator('div[hx-post*="edit-vendor-changed"] span[x-text="value.label"]') .locator('div[hx-vals*="vendor-changed"] span[x-text="value.label"]')
.first(); .first();
await expect(label).toHaveText('Test Vendor'); await expect(label).toHaveText('Test Vendor');
// The server-rendered hidden input must carry the newly selected vendor id. // The server-rendered hidden input must carry the newly selected vendor id.
const hidden = page const hidden = page
.locator( .locator(
'div[hx-post*="edit-vendor-changed"] input[type="hidden"][name="step-params[transaction/vendor]"]' 'div[hx-vals*="vendor-changed"] input[type="hidden"][name="step-params[transaction/vendor]"]'
) )
.first(); .first();
await expect(hidden).toHaveValue(vendorId.toString()); await expect(hidden).toHaveValue(vendorId.toString());

View File

@@ -263,7 +263,8 @@
:value total})]] :value total})]]
[:div.mt-1 [:div.mt-1
[:a.text-sm.text-blue-600.hover:underline.cursor-pointer [:a.text-sm.text-blue-600.hover:underline.cursor-pointer
{:hx-post (bidi/path-for ssr-routes/only-routes ::route/edit-wizard-toggle-mode) {:hx-post (bidi/path-for ssr-routes/only-routes ::route/edit-form-changed)
:hx-vals (hx/json {:op "toggle-mode"})
:hx-include "closest form" :hx-include "closest form"
:hx-target "#wizard-form" :hx-target "#wizard-form"
:hx-select "#wizard-form" :hx-select "#wizard-form"
@@ -344,8 +345,8 @@
(com/text-input (assoc amount-attrs :type "number" :step "0.01")) (com/text-input (assoc amount-attrs :type "number" :step "0.01"))
(com/money-input amount-attrs)))))) (com/money-input amount-attrs))))))
(com/data-grid-cell {:class "align-top"} (com/data-grid-cell {:class "align-top"}
(com/a-icon-button {:hx-post (bidi/path-for ssr-routes/only-routes ::route/edit-wizard-remove-account) (com/a-icon-button {:hx-post (bidi/path-for ssr-routes/only-routes ::route/edit-form-changed)
:hx-vals (hx/json {:row-index (or index 0)}) :hx-vals (hx/json {:op "remove-account" :row-index (or index 0)})
:hx-target "#wizard-form" :hx-target "#wizard-form"
:hx-select "#wizard-form" :hx-select "#wizard-form"
:hx-swap "outerHTML" :hx-swap "outerHTML"
@@ -428,7 +429,8 @@
:value amount-mode :value amount-mode
:name "step-params[amount-mode]" :name "step-params[amount-mode]"
:orientation :horizontal :orientation :horizontal
:hx-post (bidi/path-for ssr-routes/only-routes ::route/toggle-amount-mode) :hx-vals (hx/json {:op "toggle-amount-mode"})
:hx-post (bidi/path-for ssr-routes/only-routes ::route/edit-form-changed)
:hx-target "#wizard-form" :hx-target "#wizard-form"
:hx-select "#wizard-form" :hx-select "#wizard-form"
:hx-swap "outerHTML" :hx-swap "outerHTML"
@@ -468,7 +470,8 @@
(com/data-grid-row {:class "new-row"} (com/data-grid-row {:class "new-row"}
(com/data-grid-cell {:colspan 4} (com/data-grid-cell {:colspan 4}
(com/a-button {:hx-post (bidi/path-for ssr-routes/only-routes ::route/edit-wizard-new-account) (com/a-button {:hx-post (bidi/path-for ssr-routes/only-routes ::route/edit-form-changed)
:hx-vals (hx/json {:op "new-account"})
:hx-target "#wizard-form" :hx-target "#wizard-form"
:hx-select "#wizard-form" :hx-select "#wizard-form"
:hx-swap "outerHTML" :hx-swap "outerHTML"
@@ -490,7 +493,8 @@
[:div#manual-coding-section [:div#manual-coding-section
(com/hidden {:name "step-params[mode]" :value (name mode)}) (com/hidden {:name "step-params[mode]" :value (name mode)})
[:div {:hx-trigger "change" [:div {:hx-trigger "change"
:hx-post (bidi/path-for ssr-routes/only-routes ::route/edit-vendor-changed) :hx-post (bidi/path-for ssr-routes/only-routes ::route/edit-form-changed)
:hx-vals (hx/json {:op "vendor-changed"})
:hx-target "#wizard-form" :hx-target "#wizard-form"
:hx-select "#wizard-form" :hx-select "#wizard-form"
:hx-swap "outerHTML" :hx-swap "outerHTML"
@@ -516,7 +520,8 @@
(when (<= row-count 1) (when (<= row-count 1)
[:div.mb-2 [:div.mb-2
[:a.text-sm.text-blue-600.hover:underline.cursor-pointer [:a.text-sm.text-blue-600.hover:underline.cursor-pointer
{:hx-post (bidi/path-for ssr-routes/only-routes ::route/edit-wizard-toggle-mode) {:hx-post (bidi/path-for ssr-routes/only-routes ::route/edit-form-changed)
:hx-vals (hx/json {:op "toggle-mode"})
:hx-include "closest form" :hx-include "closest form"
:hx-target "#wizard-form" :hx-target "#wizard-form"
:hx-select "#wizard-form" :hx-select "#wizard-form"
@@ -528,17 +533,17 @@
[:div#account-grid-body [:div#account-grid-body
(account-grid-body* request)]))])])) (account-grid-body* request)]))])]))
(defn toggle-amount-mode [request] (defn apply-toggle-amount-mode
"edit-form-changed op: convert account amounts between $ and % and record the new mode."
[request]
(let [snapshot (-> request :multi-form-state :snapshot) (let [snapshot (-> request :multi-form-state :snapshot)
old-mode (or (:amount-mode snapshot) "$") old-mode (or (:amount-mode snapshot) "$")
new-mode (or (get-in request [:multi-form-state :step-params :amount-mode]) "$") new-mode (or (get-in request [:multi-form-state :step-params :amount-mode]) "$")
total (Math/abs (or (:transaction/amount snapshot) 0.0)) total (Math/abs (or (:transaction/amount snapshot) 0.0))
accounts (convert-accounts-mode (:transaction/accounts snapshot) old-mode new-mode total) accounts (convert-accounts-mode (:transaction/accounts snapshot) old-mode new-mode total)]
updated-request (-> request (-> request
(assoc-in [:multi-form-state :snapshot :transaction/accounts] accounts) (assoc-in [:multi-form-state :snapshot :transaction/accounts] accounts)
(assoc-in [:multi-form-state :snapshot :amount-mode] new-mode))] (assoc-in [:multi-form-state :snapshot :amount-mode] new-mode))))
(html-response
(render-full-form updated-request))))
(defn transaction-details-panel [tx] (defn transaction-details-panel [tx]
[:div.p-4.space-y-4 [:div.p-4.space-y-4
@@ -1372,14 +1377,7 @@
[request] [request]
(mm/render-wizard edit-wizard request)) (mm/render-wizard edit-wizard request))
(defn edit-form-changed-handler (defn apply-vendor-changed [request]
"Generic handler that re-renders the whole form. Used when any field changes
and we need the server to re-compute dependent fields."
[request]
(html-response
(render-full-form request)))
(defn edit-vendor-changed-handler [request]
(let [multi-form-state (:multi-form-state request) (let [multi-form-state (:multi-form-state request)
snapshot (:snapshot multi-form-state) snapshot (:snapshot multi-form-state)
step-params (:step-params multi-form-state) step-params (:step-params multi-form-state)
@@ -1421,10 +1419,9 @@
(assoc-in [:multi-form-state :step-params :transaction/accounts] [new-account]))) (assoc-in [:multi-form-state :step-params :transaction/accounts] [new-account])))
request) request)
(assoc-in [:multi-form-state :step-params :transaction/vendor] vendor-id))] (assoc-in [:multi-form-state :step-params :transaction/vendor] vendor-id))]
(html-response render-request))
(render-full-form render-request))))
(defn edit-wizard-toggle-mode-handler [request] (defn apply-toggle-mode [request]
(let [step-params (-> request :multi-form-state :step-params) (let [step-params (-> request :multi-form-state :step-params)
snapshot (-> request :multi-form-state :snapshot) snapshot (-> request :multi-form-state :snapshot)
current-mode (keyword (or (:mode step-params) "simple")) current-mode (keyword (or (:mode step-params) "simple"))
@@ -1453,11 +1450,10 @@
(if first-row [first-row] [])) (if first-row [first-row] []))
(assoc-in [:multi-form-state :step-params :mode] (assoc-in [:multi-form-state :step-params :mode]
(name target-mode)))))] (name target-mode)))))]
(html-response render-request))
(render-full-form render-request))))
(defn edit-wizard-new-account-handler (defn apply-new-account
"Adds a new account row and re-renders the whole form." "edit-form-changed op: append a fresh account row."
[request] [request]
(let [snapshot (-> request :multi-form-state :snapshot) (let [snapshot (-> request :multi-form-state :snapshot)
amount-mode (or (:amount-mode snapshot) "$") amount-mode (or (:amount-mode snapshot) "$")
@@ -1471,12 +1467,10 @@
updated-request (-> request updated-request (-> request
(assoc-in [:multi-form-state :snapshot :transaction/accounts] updated-accounts) (assoc-in [:multi-form-state :snapshot :transaction/accounts] updated-accounts)
(assoc-in [:multi-form-state :step-params :transaction/accounts] updated-accounts))] (assoc-in [:multi-form-state :step-params :transaction/accounts] updated-accounts))]
(html-response updated-request))
(render-full-form updated-request))))
(defn edit-wizard-remove-account-handler (defn apply-remove-account
"Removes an account row and re-renders the whole form. "edit-form-changed op: remove the account row at form-param row-index."
Expects a row-index in the form params."
[request] [request]
(let [row-index (some-> request :form-params (get "row-index") Integer/parseInt) (let [row-index (some-> request :form-params (get "row-index") Integer/parseInt)
snapshot (-> request :multi-form-state :snapshot) snapshot (-> request :multi-form-state :snapshot)
@@ -1488,8 +1482,24 @@
updated-request (-> request updated-request (-> request
(assoc-in [:multi-form-state :snapshot :transaction/accounts] updated-accounts) (assoc-in [:multi-form-state :snapshot :transaction/accounts] updated-accounts)
(assoc-in [:multi-form-state :step-params :transaction/accounts] updated-accounts))] (assoc-in [:multi-form-state :step-params :transaction/accounts] updated-accounts))]
updated-request))
(defn edit-form-changed-handler
"Single whole-form re-render endpoint. Dispatches on the `op` form-param to apply the
relevant state mutation (vendor change, mode toggle, add/remove row, $/% toggle), then
re-renders the whole form. A missing/unknown op (a plain dependent-field change) just
re-renders. Replaces the per-operation edit-wizard-* / toggle-amount-mode routes."
[request]
(let [op (get-in request [:form-params "op"])
request' (case op
"vendor-changed" (apply-vendor-changed request)
"toggle-mode" (apply-toggle-mode request)
"new-account" (apply-new-account request)
"remove-account" (apply-remove-account request)
"toggle-amount-mode" (apply-toggle-amount-mode request)
request)]
(html-response (html-response
(render-full-form updated-request)))) (render-full-form request'))))
(def key->handler (def key->handler
(apply-middleware-to-all-handlers (apply-middleware-to-all-handlers
@@ -1509,10 +1519,6 @@
(wrap-entity [:multi-form-state :snapshot :db/id] d-transactions/default-read) (wrap-entity [:multi-form-state :snapshot :db/id] d-transactions/default-read)
(mm/wrap-wizard edit-wizard) (mm/wrap-wizard edit-wizard)
(mm/wrap-decode-multi-form-state)) (mm/wrap-decode-multi-form-state))
::route/edit-vendor-changed (-> edit-vendor-changed-handler
(mm/wrap-wizard edit-wizard)
(wrap-entity [:multi-form-state :snapshot :db/id] d-transactions/default-read)
(mm/wrap-decode-multi-form-state))
::route/location-select (-> location-select ::route/location-select (-> location-select
(wrap-schema-enforce :query-schema [:map (wrap-schema-enforce :query-schema [:map
[:name :string] [:name :string]
@@ -1524,23 +1530,6 @@
(mm/wrap-wizard edit-wizard) (mm/wrap-wizard edit-wizard)
(wrap-entity [:multi-form-state :snapshot :db/id] d-transactions/default-read) (wrap-entity [:multi-form-state :snapshot :db/id] d-transactions/default-read)
(mm/wrap-decode-multi-form-state)) (mm/wrap-decode-multi-form-state))
::route/toggle-amount-mode (-> toggle-amount-mode
(mm/wrap-wizard edit-wizard)
(wrap-entity [:multi-form-state :snapshot :db/id] d-transactions/default-read)
(mm/wrap-decode-multi-form-state))
::route/edit-wizard-toggle-mode (-> edit-wizard-toggle-mode-handler
(mm/wrap-wizard edit-wizard)
(wrap-entity [:multi-form-state :snapshot :db/id] d-transactions/default-read)
(mm/wrap-decode-multi-form-state))
::route/edit-wizard-new-account (-> edit-wizard-new-account-handler
(mm/wrap-wizard edit-wizard)
(wrap-entity [:multi-form-state :snapshot :db/id] d-transactions/default-read)
(mm/wrap-decode-multi-form-state))
::route/edit-wizard-remove-account (-> edit-wizard-remove-account-handler
(mm/wrap-wizard edit-wizard)
(wrap-entity [:multi-form-state :snapshot :db/id] d-transactions/default-read)
(mm/wrap-decode-multi-form-state))
::route/unlink-payment (-> unlink-payment ::route/unlink-payment (-> unlink-payment
(wrap-must {:activity :edit :subject :transaction} (fn get-client [request] (-> request :entity :transaction/client))) (wrap-must {:activity :edit :subject :transaction} (fn get-client [request] (-> request :entity :transaction/client)))
(wrap-entity [:multi-form-state :snapshot :db/id] d-transactions/default-read) (wrap-entity [:multi-form-state :snapshot :db/id] d-transactions/default-read)

View File

@@ -28,13 +28,8 @@
["/" [#"\d+" :db/id]] {"/edit" {:get ::edit-wizard}} ["/" [#"\d+" :db/id]] {"/edit" {:get ::edit-wizard}}
"/edit-submit" ::edit-submit "/edit-submit" ::edit-submit
"/edit-vendor-changed" ::edit-vendor-changed
"/location-select" ::location-select "/location-select" ::location-select
"/toggle-amount-mode" ::toggle-amount-mode
"/edit-form-changed" ::edit-form-changed "/edit-form-changed" ::edit-form-changed
"/edit-wizard-new-account" ::edit-wizard-new-account
"/edit-wizard-remove-account" ::edit-wizard-remove-account
"/edit-wizard-toggle-mode" ::edit-wizard-toggle-mode
"/match-payment" ::link-payment "/match-payment" ::link-payment
"/match-autopay-invoices" ::link-autopay-invoices "/match-autopay-invoices" ::link-autopay-invoices
"/match-unpaid-invoices" ::link-unpaid-invoices "/match-unpaid-invoices" ::link-unpaid-invoices