* fix(vat): keep the momsrapport intact after a manual nollställning (#984)
The momsrapport already excludes settlement verifikat tagged with
source_type 'vat_settlement' (#983), but settlements booked any other
way still zeroed every ruta the moment they were posted: manual
momsomföringar booked before the tagged flow existed (the report in
issue #984), SIE-imported settlements, and storno reversals of a
settlement, which inverted the sign instead and silently doubled the
rutor after an annullera.
Exclude settlement entries by SHAPE as well: an entry with at least one
line on a declaration account (ACCOUNT_RUTA) and at least one on a
settlement net account (2650/1650) is bookkeeping about the declaration,
not VAT-bearing activity, in both the web projection
(fetchVatAccountTotals) and the MCP twin (computeVatReport).
Opening-balance entries are exempt: carried-in 26xx balances are
unsettled VAT that belongs in the next declaration.
Shape-detected POSTED settlements now also gate the "Skapa verifikat"
button through existing_entries, since the proposal re-clears the full
period and booking it on top of a manual settlement would corrupt the
26xx balances. Stornos never gate, so annullera still re-enables
booking.
Fixes#984
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(mcp): stable id order for the paginated VAT-report line fetch (CodeRabbit)
fetchAllRows pages with .range(); without a unique .order() rows can
shift across page boundaries once a period exceeds 1000 lines, skipping
or double-counting journal lines in the rutor. Same discipline as the
web projection (fetch-all.ts). Pre-existing, but the query was already
being touched for #984.
Also documents the shape-rule triage from the compliance-bot review in
DECISIONS.md: compound business-VAT-plus-2650 verifikat stay a known
accepted residual (a direction guard would break the storno exclusion),
and the opening-balance concern is false for app flows (SIE import and
set_opening_balances both tag source_type 'opening_balance').
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat(vat): book the momsrapport as an editable settlement verifikat (#980)
Adds a "Bokfor momsrapporten" card under the VAT declaration that builds
an editable verifikat proposal from the report and books it through the
ordinary journal entry form:
- lib/reports/vat-settlement.ts: proposal builder. Clears each 26xx
account at exact ore, books the net on 2650 (att betala) or 1650 (att
aterfa) at the filed whole-krona amount (buildFiledAmounts, oretal
faller bort per SFL 22 kap 1 par), balances the gap on 3740. Surfaces
existing vat_settlement entries in the period so the UI can warn
before a double booking.
- GET /api/reports/vat-declaration/settlement-proposal: same period
params as the sibling report routes.
- VatBookingCard (reports view): fetches the proposal, warns when the
period already has a posted settlement or draft, and opens the
JournalEntryForm (bare, prefilled, source_type vat_settlement) in a
dialog so every line is editable before committing. Booking uses the
existing engine path: balance validation, period locks, voucher
series per source type.
- vat_settlement entries are excluded from the declaration projection
(calculateVatDeclaration via new shared fetchVatAccountTotals, and
the MCP computeVatReport for parity): a pure-projection report would
otherwise read zero, and a later Skatteverket submission would file
zeros, the moment the settlement is booked.
No migration needed: the vat_settlement source type shipped in
20260708100000.
Closes#980
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(vat): block re-booking a settled period, fail loud on lookup errors (CodeRabbit)
The proposal is not delta-aware (it re-clears the FULL period), so a
second booking while a posted settlement exists would corrupt the 26xx
balances: disable "Skapa verifikat" until that verifikat is annulled
(storno restores the balances). And since the existing-settlement
lookup now gates that button, a swallowed query error would silently
re-enable it: throw instead.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>