feat(import): import skattekontoutdrag files into the skattekonto pipeline (#1637)
* feat(import): import skattekontoutdrag files into the skattekonto pipeline Users can now upload the kontohändelse export from Skatteverket's skattekonto e-service (current CSV layout, verified against a real 2026-08 export, plus legacy .skv files) instead of needing the paid API connection. Parsed rows land in skattekonto_transactions as booked file_import rows and inherit the existing 1630 rules engine, bulk booking, match-to-verifikat and both UIs unchanged. - Core parser lib/import/skattekonto-file/ with strict detection (orgnr header + saldo markers, or two distinct SKV vocabulary terms plus row shape), sum-integrity check (opening + rows must equal closing) and a wrong-company guard against company_settings. - computeDedupKey moves to core (lib/skatteverket/skattekonto-dedup); the extension re-imports it. File rows hash-key; content-signature partitioning skips rows already booked (either key form) and promotes matching upcoming rows in place. - syncSkattekonto gains a takeover step: an id-keyed API row adopts a matching hash-keyed imported row in place, so journal links survive connecting the API after a file import. Upcoming rows can no longer clobber a booked row on hash collision. - New skattekonto_file_imports table (company-scoped file-hash dedup) plus source/file_import_id provenance columns on skattekonto_transactions. - /import gains a Skattekontoutdrag wizard (upload/preview/result, deep link ?mode=skattekonto); the bank-file flow detects skattekonto files and redirects instead of importing them as bank rows. - /skattekonto renders imported rows for unconnected companies (attn line + import CTA) instead of discarding them behind the StartCard. - Free for everyone: the local-data booking/match routes were already ungated; only API sync/saldo stay capability-gated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(skattekonto): align the EF F-skatt rule with the 2012 -> 2013 decision 20260810120000 established that 2012 is not standard BAS and moved the booking templates to 2013 (owner taxes in an enskild firma are an eget uttag), but the skattekonto_rules seed still booked EF preliminarskatt against 2012. The file importer makes this rule fire for every EF F-skatt row, so bring it onto 2013 too. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(import): apply review findings on the skattekonto file import - Fix the takeover candidate comparator: the single-argument sort was an inconsistent relation and could adopt a stale upcoming row ahead of the booked file row in a 3+ candidate queue (regression test added), and page the candidate scan with fetchAllRows so a multi-year window is not silently capped at 1000 rows. - Fail parsing when a statement HAS saldo markers but not both readable balances: a file cut off before "Utgående saldo" previously skipped the sum check entirely. sum_valid stays null only for marker-less legacy files. - Count a promotion only when the UPDATE matched a row, so a concurrent sync cannot inflate promoted_count; log a failed finalize of the import record instead of discarding the error. - Migration (unshipped, edited in place): user_id is nullable with ON DELETE SET NULL so import records and their file-hash dedup survive user deletion, and the INSERT policy binds user_id to auth.uid() so a member cannot attribute an import to a colleague. pg tests cover both. - Make the upload drop zone keyboard-reachable (role, tabIndex, Enter/ Space) and give the six count-bearing strings ICU plural forms in both locales. Skipped with reasons on the PR: binding execute rows to file bytes and re-checking orgnr in execute (same client-trust model as the shipped bank-file execute; Zod + RLS scope writes to the caller's own company), a 404 test (the route has no not-found path), event-bus clearing in the route test (the route touches no events), and FK NOT VALID (new column referencing a brand-new empty table). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
Jakob Wennberg
parent
dfb34a01d9
commit
4921d1da5e
@@ -1686,6 +1686,65 @@ const BANK_FILE: Record<string, StructuredErrorEntry> = {
|
||||
message_sv: 'Bankfilsimporten misslyckades.',
|
||||
message_en: 'Bank file import failed.',
|
||||
},
|
||||
BANK_FILE_SKATTEKONTO_DETECTED: {
|
||||
httpStatus: 400,
|
||||
message_sv:
|
||||
'Filen ser ut som ett skattekontoutdrag från Skatteverket. Använd Skattekonto-importen i stället.',
|
||||
message_en:
|
||||
'This file looks like a Skatteverket tax account statement. Use the skattekonto import instead.',
|
||||
},
|
||||
}
|
||||
|
||||
const SKATTEKONTO_FILE: Record<string, StructuredErrorEntry> = {
|
||||
SKATTEKONTO_FILE_NO_FILE: {
|
||||
httpStatus: 400,
|
||||
message_sv: 'Ingen fil bifogad i förfrågan.',
|
||||
message_en: 'No file attached to the request.',
|
||||
},
|
||||
SKATTEKONTO_FILE_TOO_LARGE: {
|
||||
httpStatus: 400,
|
||||
message_sv: 'Filen är för stor. Maxstorlek är 10 MB.',
|
||||
message_en: 'File exceeds the 10 MB size limit.',
|
||||
},
|
||||
SKATTEKONTO_FILE_DUPLICATE: {
|
||||
httpStatus: 409,
|
||||
message_sv: 'Det här kontoutdraget har redan importerats.',
|
||||
message_en: 'This tax account statement has already been imported.',
|
||||
},
|
||||
SKATTEKONTO_FILE_PARSE_FAILED: {
|
||||
httpStatus: 500,
|
||||
message_sv: 'Kunde inte tolka skattekontoutdraget.',
|
||||
message_en: 'Failed to parse the tax account statement.',
|
||||
},
|
||||
SKATTEKONTO_FILE_NOT_RECOGNIZED: {
|
||||
httpStatus: 400,
|
||||
message_sv:
|
||||
'Filen känns inte igen som ett skattekontoutdrag. Ladda ner kontohändelserna från Skatteverkets e-tjänst Skattekonto och försök igen.',
|
||||
message_en:
|
||||
'The file was not recognized as a tax account statement. Download the account events from Skatteverket and try again.',
|
||||
},
|
||||
SKATTEKONTO_FILE_SUM_MISMATCH: {
|
||||
httpStatus: 400,
|
||||
message_sv:
|
||||
'Utdraget summerar inte: ingående saldo plus händelser stämmer inte med utgående saldo. Filen kan vara ofullständig.',
|
||||
message_en:
|
||||
'The statement does not sum: opening balance plus events does not equal the closing balance. The file may be incomplete.',
|
||||
},
|
||||
SKATTEKONTO_FILE_NO_ROWS: {
|
||||
httpStatus: 400,
|
||||
message_sv: 'Kontoutdraget innehåller inga händelser att importera.',
|
||||
message_en: 'No account events to import.',
|
||||
},
|
||||
SKATTEKONTO_FILE_IMPORT_RECORD_FAILED: {
|
||||
httpStatus: 500,
|
||||
message_sv: 'Kunde inte skapa importpost.',
|
||||
message_en: 'Failed to create the import record.',
|
||||
},
|
||||
SKATTEKONTO_FILE_EXECUTE_FAILED: {
|
||||
httpStatus: 500,
|
||||
message_sv: 'Importen av skattekontoutdraget misslyckades.',
|
||||
message_en: 'Tax account statement import failed.',
|
||||
},
|
||||
}
|
||||
|
||||
const OPENING_BALANCE_IMPORT: Record<string, StructuredErrorEntry> = {
|
||||
@@ -3584,6 +3643,7 @@ const REGISTRY: Record<string, StructuredErrorEntry> = {
|
||||
...TAX_DECL,
|
||||
...SIE_IMPORT,
|
||||
...BANK_FILE,
|
||||
...SKATTEKONTO_FILE,
|
||||
...OPENING_BALANCE_IMPORT,
|
||||
...REGISTER_IMPORT,
|
||||
...PROVIDER_MIGRATION,
|
||||
|
||||
Reference in New Issue
Block a user