fix(reports): always emit compulsory #FORMAT PC8 in SIE export (#1466)

#FORMAT is a compulsory record in every SIE type and PC8 is its only
legal value. We only emitted it when the caller opted into cp437 byte
encoding, so the default UTF-8 download had no #FORMAT line and strict
importers (Visma Spiris) rejected the file with 'Etiketten #FORMAT
saknas i filen'. Cloud exporters (Fortnox, Bokio) ship UTF-8 bytes with
#FORMAT PC8 and importers detect the real encoding from the bytes, so
the tag is now unconditional.

Also formats #ORGNR as nnnnnn-nnnn per spec; company_settings stores
the org number without a hyphen.

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:
Jakob Wennberg
2026-08-08 15:04:07 +02:00
committed by GitHub
co-authored by Jakob Wennberg Claude Fable 5
parent 799fa1246a
commit ddbe9b1379
6 changed files with 36 additions and 14 deletions
-1
View File
@@ -36,7 +36,6 @@ export const GET = withRouteContext(
company_name: company.company_name || 'Unknown',
org_number: company.org_number,
exclude_year_end_closing: excludeClosing,
emit_format_pc8: useCP437,
})
const body = useCP437 ? Buffer.from(encodeSIEToCP437(sieContent)) : sieContent
@@ -28,7 +28,7 @@ registerEndpoint({
pitfalls: [
'`period_id` is required.',
'The response is text/plain with Content-Disposition: attachment: clients should treat as a binary download. Filename uses the pattern `export_{period_id}.se`.',
'Default encoding is UTF-8 (no #FORMAT PC8 tag). Pass `encoding=cp437` to get a spec-compliant CP437-encoded file with #FORMAT PC8, required by some legacy desktop bookkeeping software.',
'The compulsory #FORMAT PC8 tag is always present, but default byte encoding is UTF-8 (the de-facto cloud convention; importers detect encoding from the bytes). Pass `encoding=cp437` for actual CP437 bytes, required by some legacy desktop bookkeeping software.',
'Only `posted` entries are exported; drafts and reversed entries\' originals are included but marked accordingly.',
],
example: {
@@ -76,7 +76,6 @@ export const GET = withApiV1<{ params: Promise<{ companyId: string }> }>(
company_name: (company as { company_name: string | null }).company_name || 'Unknown',
org_number: (company as { org_number: string | null }).org_number,
exclude_year_end_closing: excludeClosing,
emit_format_pc8: useCP437,
}),
{ log: ctx.log, requestId: ctx.requestId, reportName: 'sie-export' },
)