bb28968151
* fix(import): chunk SIE account creation and close the import row on every exit
A full-BAS Bokio SIE file creates 1 200+ chart_of_accounts rows in one
INSERT. PostgREST runs it under the authenticated role's 8 s
statement_timeout, and with four row-level triggers plus the RLS WITH
CHECK that single statement measured 6.5 s to 8.2 s on prod: it was
cancelled for one company and passed for the next (2026-09-09).
- syncMappedAccounts inserts in chunks of 100 rows (INSERT_CHUNK_SIZE),
so every statement stays an order of magnitude inside the limit. A
failed chunk leaves the earlier ones committed; the next attempt reads
the chart again and inserts only what is still missing.
- executeSIEImport closes its pending sie_imports row in a finally
block. Every early `return result` after createPendingImportRecord
(account sync failure, missing fiscal year, overlapping import,
vouchers outside the year) used to leave the row 'pending'. That row
holds the (company_id, file_hash) slot in the partial unique index,
so a retry inside the five-minute cleanup gate failed on the index
instead of on the real error.
- The slot-held message no longer names "gnubok", an "Ersätt import"
button the import history has never had, or Fortnox; it says the
same file is being imported or was interrupted moments ago and to
retry in a few minutes. The thrown-error prefix is Swedish
("Importen misslyckades:") and the two undo hints name
accounted_undo_sie_import.
Tests: chunk sizes and first-failing-chunk behaviour in
account-sync.test.ts; failed-row finalize on two early exits and the
new slot message in sie-import.account-names.test.ts.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WBTRraVXkCnx93Pa9wxJ6G
* fix(import): write account chunks as ignore-duplicates upserts with an exact count
Review follow-up on #2451. A plain INSERT per chunk still had the old
race: a concurrent import (or the replace flow) creating one account
between our read and write raised a duplicate-key error that the code
swallowed as success, while PostgREST had rolled back the whole chunk,
so every other account in it was silently missing.
Each chunk is now an upsert with onConflict (company_id, account_number)
and ignoreDuplicates, selecting the landed rows: the race becomes a
skipped row, `created` counts exactly what was written (also across a
mid-loop failure, which the compliance review flagged), and the
"duplicate" string special-case is gone.
Tests: conflict-skipped row not counted; a race yields no error; a
failing later chunk reports the rows the earlier chunks committed.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WBTRraVXkCnx93Pa9wxJ6G
---------
Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>