Files
accounted/lib/bokslut/arsredovisning/__tests__/pdf-format.test.ts
T
MattssonandClaude Fable 5 89ce837947 fix(arsredovisning): losses lost their minus sign in the PDF (#2013)
* fix(arsredovisning): losses lost their minus sign in the PDF

toLocaleString('sv-SE') formats negatives with U+2212 MINUS SIGN, which
react-pdf's built-in Helvetica (WinAnsi encoding) has no glyph for, so the
sign was silently dropped: a loss rendered as a profit on 'Arets resultat'
and 'Summa fritt eget kapital' while all totals stayed correct. Reported by
a user whose -4 684,24 kr loss displayed as +4 684 kr.

Add formatPdfKronor (absolute value + ASCII hyphen-minus) and route the K2
and K3 PDF templates and the PDF-bound note builders through it. Display
only; no ledger or iXBRL changes (the iXBRL writer already handles signs
via the sign attribute and its own presentational minus).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012CBPBCdBr5P75MQCt4qcnE

* fix(arsredovisning): pin PDF group separator to U+00A0 across ICU versions

Newer CLDR data groups sv-SE thousands with U+202F NARROW NO-BREAK SPACE,
which WinAnsi Helvetica also lacks: digits would silently run together the
same way the minus sign was dropped. Normalize the separator to U+00A0 in
formatPdfKronor so the rendering does not depend on the runtime's ICU.

Addresses the PR Agent reviewer-guide finding on #2013.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012CBPBCdBr5P75MQCt4qcnE

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 16:55:41 +02:00

44 lines
1.6 KiB
TypeScript

import { describe, it, expect } from 'vitest'
import { formatPdfKronor } from '../pdf-format'
// Build the special characters from code points so the assertions cannot be
// corrupted by editor/encoding round-trips: U+00A0 is the sv-SE thousands
// separator, U+2212 is the locale minus that WinAnsi Helvetica cannot render.
const NBSP = String.fromCharCode(0x00a0)
const U2212 = String.fromCharCode(0x2212)
describe('formatPdfKronor', () => {
it('formats a loss with an ASCII hyphen-minus, never U+2212', () => {
const out = formatPdfKronor(-4684.24)
expect(out).toBe(`-4${NBSP}684`)
expect(out).not.toContain(U2212)
})
it('keeps sv-SE thousands grouping for positive amounts', () => {
expect(formatPdfKronor(1234567.89)).toBe(`1${NBSP}234${NBSP}568`)
})
it('pins the group separator to U+00A0 regardless of the ICU CLDR choice', () => {
// Newer CLDR data groups sv-SE with U+202F NARROW NO-BREAK SPACE, which
// is also missing from WinAnsi; the formatter must normalize to U+00A0.
const U202F = String.fromCharCode(0x202f)
const out = formatPdfKronor(-9876543)
expect(out).toBe(`-9${NBSP}876${NBSP}543`)
expect(out).not.toContain(U202F)
})
it('rounds to whole kronor and never renders "-0"', () => {
expect(formatPdfKronor(-0.4)).toBe('0')
expect(formatPdfKronor(-0.6)).toBe('-1')
expect(formatPdfKronor(0)).toBe('0')
})
it('always uses ASCII hyphen for negative amounts', () => {
for (const n of [-1, -999, -1000, -4684.24, -1_000_000.5]) {
const out = formatPdfKronor(n)
expect(out).not.toContain(U2212)
expect(out.startsWith('-')).toBe(true)
}
})
})