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:
co-authored by
Jakob Wennberg
Claude Fable 5
parent
799fa1246a
commit
ddbe9b1379
@@ -97,13 +97,32 @@ describe('generateSIEExport', () => {
|
||||
const lines = output.split('\r\n')
|
||||
|
||||
expect(lines[0]).toBe('#FLAGGA 0')
|
||||
expect(lines[1]).toBe('#SIETYP 4')
|
||||
expect(lines[2]).toMatch(/^#PROGRAM "ERPBase" "1\.0"$/)
|
||||
expect(lines[3]).toMatch(/^#GEN \d{8}$/)
|
||||
expect(lines[4]).toBe('#ORGNR 556677-8899')
|
||||
expect(lines[5]).toBe('#FNAMN "Test AB"')
|
||||
expect(lines[6]).toBe('#RAR 0 20240101 20241231')
|
||||
expect(output).not.toContain('#FORMAT PC8')
|
||||
expect(lines[1]).toBe('#FORMAT PC8')
|
||||
expect(lines[2]).toBe('#SIETYP 4')
|
||||
expect(lines[3]).toMatch(/^#PROGRAM "ERPBase" "1\.0"$/)
|
||||
expect(lines[4]).toMatch(/^#GEN \d{8}$/)
|
||||
expect(lines[5]).toBe('#ORGNR 556677-8899')
|
||||
expect(lines[6]).toBe('#FNAMN "Test AB"')
|
||||
expect(lines[7]).toBe('#RAR 0 20240101 20241231')
|
||||
})
|
||||
|
||||
it('hyphenates a bare 10-digit org_number in #ORGNR', async () => {
|
||||
results = [
|
||||
{ data: { id: 'period-1', period_start: '2024-01-01', period_end: '2024-12-31' }, error: null },
|
||||
{ data: null, error: null }, // prevPeriod
|
||||
{ data: [], error: null }, // accounts
|
||||
{ data: [], error: null }, // journal_entries (no entries -> line fetch skipped)
|
||||
{ data: [], error: null }, // dimensions
|
||||
{ data: [], error: null }, // dimension_values
|
||||
{ data: [], error: null }, // opening balances RPC
|
||||
]
|
||||
|
||||
const output = await generateSIEExport(supabase, 'company-1', {
|
||||
...baseOptions,
|
||||
org_number: '5566778899',
|
||||
})
|
||||
|
||||
expect(output).toContain('#ORGNR 556677-8899')
|
||||
})
|
||||
|
||||
it('omits #ORGNR when org_number is null', async () => {
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { SupabaseClient } from '@supabase/supabase-js'
|
||||
import { fetchAllRows } from '@/lib/supabase/fetch-all'
|
||||
import { fetchLinesByEntryIds } from '@/lib/bookkeeping/entry-lines'
|
||||
import { getBranding } from '@/lib/branding/service'
|
||||
import { formatOrgNumber } from '@/lib/utils'
|
||||
import { createLogger } from '@/lib/logger'
|
||||
import { getOpeningBalances } from './opening-balances'
|
||||
import type { SIEExportOptions, JournalEntry, JournalEntryLine, BASAccount } from '@/types'
|
||||
@@ -158,14 +159,19 @@ export async function generateSIEExport(
|
||||
|
||||
// === Header ===
|
||||
lines.push('#FLAGGA 0')
|
||||
if (options.emit_format_pc8) lines.push('#FORMAT PC8')
|
||||
// #FORMAT is compulsory in every SIE type and PC8 is its only legal value.
|
||||
// Strict importers (e.g. Visma Spiris) reject files without it, and they
|
||||
// detect the actual byte encoding themselves, so it is emitted even when
|
||||
// the output is served as UTF-8.
|
||||
lines.push('#FORMAT PC8')
|
||||
lines.push('#SIETYP 4')
|
||||
const programName = sanitizeProgramName(options.program_name || getBranding().appName)
|
||||
lines.push(`#PROGRAM "${programName}" "1.0"`)
|
||||
lines.push(`#GEN ${formatSIEDate(now)}`)
|
||||
|
||||
if (options.org_number) {
|
||||
lines.push(`#ORGNR ${options.org_number}`)
|
||||
// Spec format is nnnnnn-nnnn; company_settings may store it without the hyphen.
|
||||
lines.push(`#ORGNR ${formatOrgNumber(options.org_number)}`)
|
||||
}
|
||||
|
||||
lines.push(`#FNAMN "${escapeQuotes(options.company_name)}"`)
|
||||
|
||||
Reference in New Issue
Block a user