feat(transactions): port manual bank-transaction import to SSR #8
Reference in New Issue
Block a user
Delete Branch "integreat-add-transaction-manual"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Ports the master-branch manual bank-transaction import to the SSR/alpinejs/htmx stack. The route names
::external-import-page/parse/importwere already declared inroutes/transactions.cljcbut no handler served them — this fills that gap, modeled on the existing SSR ledger import.Flow: a dedicated admin page (
/transaction2/external-import-new) where you paste the exact same Yodlee positional-column TSV as before → parsed rows render in an editable review grid with per-row error/warning badges → import. Every master validation is preserved, and the existingimport.transactionsengine is reused unchanged (viaimport.manual/import-batch).What changed
auto-ap.ssr.transaction.import— page, clipboard/paste form, parse handler, editable form-cursor grid, two-tier validation, and import handler. Wired intossr/transaction.cljkey->handler; admin-only viawrap-must {:activity :import :subject :transaction}+assert-admin.[message status]per row. Hard errors (bad date, unparseable amount, unknown client/bank-account code, missing fields) block the whole batch (throw+ :field-validationre-renders the grid). Warnings (before bank-account start-date, before client locked-until, already-imported) skip just that row. Warn conditions are computed from the engine's owncategorize-transaction, so the grid preview matches the import result.ssr/components/aside.clj.e2e/transaction-import.spec.ts(3 Playwright tests, committed failing-first then made green) andssr/transaction/import_test.clj(10 clojure.test deftests, 33 assertions, against in-memory Datomic). Deterministic bank-account code added to the test-server seed.Validation
transaction-navigationdate-range persistence,transaction-editshared-location) were verified pre-existing — they fail on the baseline with this branch's changes stashed.Residual Review Findings
From the automated code review (advisory; not blocking this PR):
Engineering follow-ups
client-codeis an editable grid column the import never consumes (client is derived from bank-account-code) — remove or make read-only. (maintainability + correctness)wrap-nested-form-params → :tablecoercion on the edit-then-import path (covered by e2e end-to-end, not at unit level). (testing + reliability)classify-tablere-runs the two full-table lookups thatimport-batchalso runs (~4 queries/cycle); thread resolved lookups through. Minor at admin scale. (maintainability + performance)classify-tableDB queries lack try/catch → raw 500 on Datomic failure (mirrors ledger's existing pattern). (reliability)import.manual/tabulate-data; privateimport-transactionshelper name collides with theimport.transactions(t) alias. (maintainability)Product decisions (master-faithful as shipped)
Plan
docs/plans/2026-06-01-001-feat-manual-transaction-import-ssr-plan.md🤖 Generated with Claude Code
When a transaction is saved via the edit modal, return the updated row HTML with {:flash? true} instead of an empty div. This makes the row flash with the live-added animation, matching the behavior of other pages like invoices and accounts. Uses hx-retarget to swap the specific row in the table while also triggering modalclose to close the modal.When editing a transaction manually, users can now toggle between viewing account amounts as dollar values or percentages. The toggle appears in the table header as a radio button group ($ / %). Key features: - Global toggle switches all accounts simultaneously - $→%: amounts are converted to percentages of the transaction total - %→$: uses percentages->dollars with spread-cents for accurate cent distribution - Form state preserves vendor, memo, approval status when toggling - Save handler converts % back to $ before persisting to Datomic - Uses HTMX to re-render only the account grid body on toggle New route: /toggle-amount-mode New functions: ->percentage, percentages->dollars, convert-accounts-mode, account-grid-body*, toggle-amount-modeWhen a transaction is pre-coded, the snapshot stores :transaction-account/account as a Datomic ref map {:db/id N} rather than a bare integer. simple-mode-fields* and the simpleAccountId Alpine initializer both need the integer id, not the map, to correctly populate the account typeahead value and the x-hx-val binding.Pull request closed