* fix(bookkeeping): revive counterparty template learning, dead since the multi-tenant refactor (#865)
The learning half of counterparty templates has written nothing since
2026-03-30 (prod: 750 SIE imports, zero new templates). Two stacked bugs:
- The multi-tenant refactor re-scoped categorization_templates to
company_id and the lib stopped writing user_id, but user_id kept its
NOT NULL: every insert failed with a null violation that supabase-js
returns rather than throws, so nothing was ever logged. Migration
20260711100000 drops the NOT NULL and the dead user_id indexes.
- Four of six learning call sites (both categorize routes,
categorize-core, the MCP server) passed the auth user id as companyId,
so even with the column fixed the writes would fail FK/RLS and
corrections could never find the template they were correcting.
Hardening while in here:
- insertOrUpdateTemplate now checks every write result, logs failures,
and returns whether a row was written; populateTemplatesFromSieVouchers
reports only templates actually persisted.
- Sign-mismatched matches (an incoming refund matching an expense-learned
template) previously booked backwards: debit expense / credit bank for
money coming IN. They are now mirrored into the correct refund shape
(VAT leg reversed for deductible input VAT), flagged requires_review,
and excluded from template/rule learning so a refund can never flip a
learned template.
- Template amounts are computed from the SEK-resolved amount, so
foreign-currency transactions no longer produce unbalanced multi-line
entries (or VAT computed on foreign units).
- SIE extraction no longer hardcodes 25% for 2641 (rate-agnostic in BAS):
the rate is inferred from voucher amounts and snapped to 25/12/6%, and
reverse-charge counterparties learn vat_treatment='reverse_charge'
instead of losing the RC legs (which also no longer poison the ratio
base).
- New pg-real test locks the exact insert column set against the real
schema, so a schema/code drift like this can't ship green again.
Closes#865
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(bookkeeping): mirror fiktiv-moms legs on RC credit notes, exclude import VAT accounts from ratio base
Compliance-review follow-ups on #989:
- REVERSE_CHARGE_VAT_ACCOUNTS gains the import output-VAT accounts
(2615/2625/2635), which pair with 2645 in import vouchers exactly like
the RC pairs and must not shrink the business ratio base.
- A sign-mismatched match against a reverse_charge template (an RC
supplier's credit note) now mirrors both fiktiv legs (credit 2645 /
debit 2614) instead of booking gross, so Ruta 30/48 net back to zero.
The income line-builder nets VAT credits against debit legs to keep
the mirrored pair balance-neutral (identical result for all existing
credit-only output-VAT paths).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(types): CategorizationTemplate.user_id is nullable since 20260711100000 (CodeRabbit)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(bookkeeping): use roundOre for the VAT netting, keep the ore-round ratchet at baseline
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(bookkeeping): review-gate stale 12% templates across the livsmedel transition, pattern-aware direction guard
Compliance-review round 2 on #989:
- Livsmedel VAT dropped 12% -> 6% on 2026-04-01 (Prop. 2025/26:55) while
restaurang/hotell stay at 12%. A reduced_12 template whose
last_seen_date predates the transition can no longer be trusted
unreviewed: its match is flagged requires_review until a
post-transition approval refreshes it (re-approval keeps 12%, a
correction relearns 6%). Actively-confirmed 12% counterparties flow
without friction.
- The opposite-direction correction guard now falls back to the line
pattern's business sides when the legacy fields are both
settlement-ish and cannot classify a multi-line template.
- Documented the accepted import-RC mirroring limitation (2614 vs 2615
ruta attribution) and the netted-vatCredit precondition.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>