refactor(ssr): Phase 4 — full Selmer migration of POS Sales Summary; remove the wizard; fix add-item + totals
Migrates the POS Sales Summary edit modal off the wizard to a plain Selmer form, building on the parity gate committed earlier. Largest migration so far and the first with no prior test coverage. What changed - Wizard removed: deleted MainStep/EditWizard records, MultiStepFormState, the step-params[...] prefix, the EDN snapshot round-trip, and all mm/* middleware. Replaced with a plain handler + flat wrap-decode/wrap-derive-state. The 51 fc/ cursor refs are de-cursored into explicit data + Selmer templates. - db/id-keyed item merge: wrap-derive-state overlays posted items onto the persisted items by :db/id, so read-only fields the form doesn't post (ledger-side, amount) survive a re-render and the debit/credit split + totals stay correct. New manual rows (temp db/id) ride through as-is. - Inline click-to-edit account cell preserved as three small targeted .account-cell-swap routes (edit/save/cancel-item-account), ported to Selmer with the new field-name scheme. - 100% Selmer modal render path (the remaining Hiccup / hx-swap-oob / "hx-" strings are all grid-page code — grid render lambdas, the filters form, and the submit response-header map — not the modal). - Routes: dropped edit-wizard-navigate + new-summary-item; added form-changed. Fixes (two pre-existing bugs, per request) - "New Summary Item" add button (was throwing `newRowIndex is not defined` and targeting a non-existent `.new-row`) is now a whole-form-swap op=new-item that adds an editable manual row (category + account typeahead + debit/credit money inputs + remove). - The dead totals/balance display (malformed Hiccup that discarded its labels) is replaced by a proper #summary-totals block showing running Total + Balanced/Unbalanced, refreshed via a Rule-4 targeted swap on manual amount edits. Scorecard delta (pos/sales_summaries.clj): LOC 790->732, mm coupling 20->0, wizard records 4->0, fc/ cursor 51->0, step-params 27->0 (2 comments), modal routes 8->6. (hx-swap-oob 1 and mixed-hx live in the grid page, not the modal.) Verification: sales-summary spec 7/7 (incl. the two fixes); full Playwright suite 46/46; cljfmt clean. Skill fed: scorecard row + narrative; gotchas (parity-gate- first, characterize-then-fix, keyup-trigger tests); cookbook (inline click-to-edit cell, db/id-keyed item merge). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -224,6 +224,35 @@ on each post (`[:vector {:coerce? true} entity-id]` + the `coerce-vector` transf
|
||||
— you code exactly the rows the user saw, immune to data changing between open and submit. This
|
||||
is heuristic 2 → 0 for a multi-select modal.
|
||||
|
||||
## No parity gate? Build one first — seed + characterization spec, before touching code
|
||||
|
||||
A modal with **no e2e coverage** (and no test-server seed for its domain) cannot be migrated
|
||||
safely — "behavior parity is proven by tests, not by reading" is the skill's #1 non-negotiable.
|
||||
Phase 4 (POS Sales Summary) had zero coverage. The fix: (1) seed a representative entity in
|
||||
`test_server.clj`'s `seed-test-data` and surface its id via `/test-info`; (2) write a
|
||||
characterization spec against the **unmodified** modal and confirm it green; (3) commit the gate
|
||||
*separately, ahead of the rewrite*. Reach the modal the real way (grid → row's edit button), not
|
||||
a direct fragment URL. To discover the actual rendered structure (field names, ids, swap targets)
|
||||
— especially when the code has dead/buggy render fns — dump the live modal HTML with a throwaway
|
||||
spec first; assert against what *renders*, not what the code looks like.
|
||||
|
||||
## Characterize before you fix; never assert a bug as working
|
||||
|
||||
Writing the gate often surfaces pre-existing bugs (Phase 4: a "New Summary Item" button that
|
||||
threw `newRowIndex is not defined`, and a totals display whose malformed Hiccup discarded its
|
||||
own labels). Do **not** assert the broken behavior as if it works, and do **not** silently "fix"
|
||||
it mid-refactor — surface it and let the user decide fix-vs-preserve. If they choose *fix*: the
|
||||
spec first documents the break (a passing test of the *current* inert behavior or an explicit
|
||||
note), then is rewritten to assert the *fixed* behavior as part of the migration commit.
|
||||
|
||||
## htmx `keyup`-triggered inputs need real keystrokes in tests
|
||||
|
||||
A money/text input wired `hx-trigger="keyup changed delay:300ms"` does **not** fire on Playwright
|
||||
`.fill()` + `dispatchEvent('change')` — `fill` sets the value without keyup events. Use
|
||||
`.click()` then `.pressSequentially('500')` (types char-by-char, firing keyup) so the targeted
|
||||
swap actually triggers. (A `change`-triggered control is the opposite — `dispatchEvent('change')`
|
||||
is fine there.)
|
||||
|
||||
## Scorecard exceptions (ratchet violations with a reason)
|
||||
|
||||
**Heuristic 4 (LOC net ↓) — exception (Phase 3, Transaction Bulk Code: 420→506).** When the
|
||||
|
||||
Reference in New Issue
Block a user