* fix(reports): paginate 8 more report/ledger queries (1000-row truncation)
Raw .select() without fetchAllRows() silently caps at PostgREST's 1000-row
limit, producing wrong statutory output for high-volume companies. Following
#806 (trial-balance/VAT), wrap the remaining offenders in
fetchAllRows + a stable .order('id') + dedupeBy:
- ink2-engine / ne-engine: INK2 & NE-bilaga tax declarations under-counted
- ar-reconciliation (1510/1513), supplier-reconciliation (2440): phantom
"Ej avstämd" gaps
- full-archive-export: 7-year DR archive (added a unique total order so rows
are not silently skipped/duplicated across pages)
- avgifter-basis, currency-revaluation, vat-declaration
Adds a regression guard test asserting >1000 ledger lines are summed, not
truncated at 1000.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(api): close extension-dispatcher MFA gap, scope /api/events to API key, sweep dead code
Security/correctness:
- ext/[...path] dispatcher now uses requireAuth() instead of inline
supabase.auth.getUser(), enforcing MFA (AAL2) on hosted across the whole
enabled-extension surface (banking sync, document upload/booking, supplier
invoices, migration). Ratchets antipatterns-baseline raw-route-auth 168->165.
- /api/events now filters by the API key's bound company_id instead of the
user's active company (was a cross-company read with a scoped key).
- enable-banking OAuth callback calls ensureInitialized() at module load so
the PSD2 consent audit event (ASVS V16 / GDPR Art.30) isn't dropped on a
cold-start instance.
Dead-code sweep (all confirmed zero importers):
- delete lib/tax/calculator.ts, lib/salary/engangsskatt.ts (+test),
lib/email/resend.ts, lib/salary/salary-transaction-matcher.ts,
lib/webhooks/diff.ts, lib/salary/effective-values.ts,
lib/bookkeeping/template-prompt.ts
- trim unused lib/vat/eu-countries.ts helpers (keep EU_COUNTRIES)
- remove dead getAutomaticStatus() and the abandoned Activepieces CSP entry
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(invoices): fail closed when a payment journal entry doesn't post
Three mark-paid paths (legacy route, v1 API, agent commit) diverged on the
"mark paid but the JE failed" case — two would flip the invoice to paid (or
leave an orphaned posted voucher) with no booking, silently diverging the GL
from the AR/AP sub-ledger. Unify on fail-closed:
- legacy + v1 + agent commitMarkInvoicePaid: never mark paid without a posted
voucher; on a null/failed JE return INVOICE_PAID_BOOK_FAILED before any
state mutation (v1 mirrors the match-invoice strict mode).
- agent path: add the .in('status',[...]).select('id') CAS guard and cancel
the orphaned voucher (cancelOrphanedPaymentEntry) on a lost race or update
error, matching the web route.
- legacy route: cancel the orphan on a non-race update error too (was only
handled on the race branch).
- supplier mark-paid: stop swallowing a failed supplier_invoice_payments
insert — that row drives the reversal amount in payment-sync; roll back the
status flip and cancel the voucher instead.
- pending-ops orchestrator: error-check the terminal 'committed' write so an
op stranded in 'committing' (the expire sweep only targets 'pending') is at
least logged loudly.
Adds a guard test for the legacy fail-closed path. Full unit suite green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(ci): unblock core build + address compliance-review findings
- avgifter-basis.ts: fix the core-build TypeScript error — PostgREST's
type-level select parser models the salary_run embed as an array, which
wasn't assignable to the object-typed generic. Type it `unknown` (rows are
read via an explicit cast), making it robust across postgrest-js versions.
- /api/events: add a non-null companyId guard before the event_log query
(defense-in-depth for the API-key-bound scope) — addresses ASVS V8.2.1 /
ISO A.5.15.
- supplier mark-paid: add a CAS guard (.eq('status', newStatus)) to the
payment-insert-failure rollback so a concurrent settlement can't be
clobbered — addresses ASVS V2.3.
- dispatcher: add an AAL2 regression test asserting a non-MFA session is
rejected (403) and the extension handler never runs — addresses the
GDPR Art.32 review ask for the single extension chokepoint.
Verified deletions are safe: effective-values.ts was a dead duplicate — the
live AGI/payslip path inlines the same `?? override` coalescing
(generate-declaration.ts), so AGI correctness is unaffected.
next build: exit 0. Full unit suite: 6147 passing. ESLint clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>