* feat(transactions): show all library templates in picker; fix PSD2 seed-row collision
- Booking template picker: surface every active library template, not
only the convertible 2-account shapes. Multi-leg/complex templates
route to the manual journal editor pre-filled via applyTemplate
instead of being hidden. Drop direction filtering for user
templates (inferred direction is unreliable); the curated static
catalog still respects it. Add an "Aktivera och bokfor" recovery
toast for TX_CATEGORIZE_INVALID_ACCOUNT mirroring the existing
ACCOUNTS_NOT_IN_CHART flow. CreateTemplateForm reflows to one card
per line so trash buttons stop colliding on narrow screens.
- cash_accounts.upsertFromPsd2: the seed_default_cash_account
migration plants a manual (bank_connection_id IS NULL) row on the
same ledger_account, so the first PSD2 sync's upsert on
(company_id, bank_connection_id, external_uid) cannot match it
(NULL != NULL) and falls through to INSERT, tripping the
(company_id, ledger_account) UNIQUE constraint. Look up and
promote the seed row in place first.
- Tests: pin the TX_CATEGORIZE_INVALID_ACCOUNT error shape the
recovery toast parses; cover applyTemplate on shapes the
converter rejects (split-expense and all-'business'-typed).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(transactions): PR review — currency metadata, MRU ordering, observable promote, BAS validation
- TransactionBookingDialog.buildInitialLinesFromTemplate: attach
buildCurrencyMetadata to settlement lines for foreign-currency
transactions so the journal entry retains the original currency,
amount, and exchange_rate. Without this, non-SEK transactions
routed through a non-convertible template were recorded in SEK
only with no foreign-currency annotation. (Greptile #2)
- TemplatePicker.handleSelectLibraryRaw: only bump the MRU after
confirming the click will actually do something (i.e. converted
OR a callback is wired). Future consumers that omit
onPickLibraryTemplate would otherwise corrupt MRU ordering for
templates the user never successfully applied. (Greptile #1)
- cash_accounts.upsertFromPsd2 promote-seed: add .select('id') so
a zero-row UPDATE is observable. If the seed row vanishes
between the SELECT and UPDATE (concurrent ops), fall through to
the normal upsert instead of silently returning success without
persisting anything. (Greptile #3, compliance A.8.9)
- transactions/page.tsx TX_CATEGORIZE_INVALID_ACCOUNT toast:
validate accountNumber against /^\d{4}$/ before embedding in any
fetch URL/body. Defense-in-depth against a malformed server
error envelope. (compliance V8.2.1)
- categorize route test: switch the not-in-chart fixture from
'4535' (Inköp av varor från annat EU-land — reverse-charge) to
'5420' (Programvaror) so the example doesn't imply a domestic
override against an EU-reverse-charge account would be valid
without its paired moms legs. (Swedish compliance review #3)
Other review items deliberately not addressed in this PR:
- Validate-on-save that every VAT-rated template has a 'vat'
line — overrides existing CreateTemplateForm UX, separate PR.
- vat_rate enum guard in applyTemplate — defensive; the editor
dropdown only surfaces legal rates and is the only write path
in production today.
- Imbalance UI warning — already handled: JournalEntryForm
computes isBalanced and gates submission; DB trigger
check_journal_entry_balance enforces BFL 5 kap server-side.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>