diff --git a/lib/reports/ink2/__tests__/sru-generator.test.ts b/lib/reports/ink2/__tests__/sru-generator.test.ts index 7adb25ab..22197872 100644 --- a/lib/reports/ink2/__tests__/sru-generator.test.ts +++ b/lib/reports/ink2/__tests__/sru-generator.test.ts @@ -20,9 +20,9 @@ function makeDeclaration(overrides?: Partial): INK2Declaration '7360': 0, '7361': 0, '7362': 0, '7363': 0, '7364': 0, '7365': 30000, '7366': 0, '7367': 0, '7369': 70000, '7368': 0, '7370': 0, '7410': 500000, '7411': 0, '7412': 0, '7413': 0, - '7511': 0, '7512': 0, '7513': -100000, '7514': -80000, '7515': -10000, '7516': 0, '7517': -5000, + '7511': 0, '7512': 0, '7513': 100000, '7514': 80000, '7515': 10000, '7516': 0, '7517': 5000, '7414': 0, '7415': 0, '7423': 0, '7416': 0, '7417': 0, - '7521': 0, '7522': -3000, + '7521': 0, '7522': 3000, '7524': 0, '7419': 0, '7420': 0, '7525': 0, '7421': 0, '7422': 0, '7528': 0, '7450': 302000, '7550': 0, @@ -209,13 +209,31 @@ describe('INK2 SRU Generator', () => { } }) - it('handles negative values correctly', () => { + it('reports cost fields as positive values per Skatteverket convention', () => { const declaration = makeDeclaration() const submission = generateSRUSubmission(declaration) const ink2rBlock = extractBlock(submission.blanketterSru, 'INK2R') - expect(ink2rBlock).toContain('#UPPGIFT 7513 -100000') - expect(ink2rBlock).toContain('#UPPGIFT 7522 -3000') + expect(ink2rBlock).toContain('#UPPGIFT 7513 100000') + expect(ink2rBlock).toContain('#UPPGIFT 7522 3000') + }) + + it('handles bokslutsdispositioner fields with correct sign', () => { + const declaration = makeDeclaration({ + ink2r: { + ...makeDeclaration().ink2r, + '7524': 50000, // Lämnade koncernbidrag (debit-normal cost, positive) + '7525': 30000, // Avsättning periodiseringsfond (debit-normal cost, positive) + '7419': 20000, // Mottagna koncernbidrag (credit-normal income, positive) + }, + }) + const submission = generateSRUSubmission(declaration) + const ink2rBlock = extractBlock(submission.blanketterSru, 'INK2R') + + // All bokslutsdispositioner fields are positive in the SRU output + expect(ink2rBlock).toContain('#UPPGIFT 7524 50000') + expect(ink2rBlock).toContain('#UPPGIFT 7525 30000') + expect(ink2rBlock).toContain('#UPPGIFT 7419 20000') }) it('includes INK2S with överskott/underskott', () => { diff --git a/lib/reports/ink2/ink2-engine.ts b/lib/reports/ink2/ink2-engine.ts index cf21261c..c4221cee 100644 --- a/lib/reports/ink2/ink2-engine.ts +++ b/lib/reports/ink2/ink2-engine.ts @@ -13,6 +13,10 @@ import type { INK2AccountMapping, INK2RSRUCode, } from './types' +import { + INK2R_ASSET_CODES, + INK2R_EQUITY_LIABILITY_CODES, +} from './types' /** * INK2 Declaration Engine @@ -667,24 +671,9 @@ function createEmptyINK2RRutor(): INK2RRutor { } } -/** All INK2R asset codes for summing */ -const ASSET_CODES: INK2RSRUCode[] = [ - '7201', '7202', '7214', '7215', '7216', '7217', - '7230', '7231', '7233', '7232', '7234', '7235', - '7241', '7242', '7243', '7244', '7245', '7246', - '7251', '7252', '7261', '7262', '7263', - '7270', '7271', '7281', -] - -/** All INK2R equity/liability codes for summing */ -const EQUITY_LIABILITY_CODES: INK2RSRUCode[] = [ - '7301', '7302', - '7321', '7322', '7323', - '7331', '7332', '7333', - '7350', '7351', '7352', '7353', '7354', - '7360', '7361', '7362', '7363', '7364', '7365', '7366', '7367', '7369', '7368', - '7370', -] +// Reuse canonical code arrays from types.ts (single source of truth) +const ASSET_CODES = INK2R_ASSET_CODES +const EQUITY_LIABILITY_CODES = INK2R_EQUITY_LIABILITY_CODES /** * Generate INK2 declaration for a fiscal period @@ -791,15 +780,16 @@ export async function generateINK2Declaration( let amount: number if (mapping.section === 'income_statement') { - // Income statement: supply the signed value as-is from accounting - // Revenue (credit normal): balance is negative (credit > debit), negate for positive revenue - // Cost (debit normal): balance is positive (debit > credit), keep as-is for negative cost + // Income statement sign convention per Skatteverket INK2R: + // All amounts are reported as positive values on the form. + // Revenue (credit normal): balance is negative in ledger, negate → positive + // Cost (debit normal): balance is positive in ledger, keep → positive // Net: negate so positive = income, negative = cost if (mapping.normalBalance === 'credit') { amount = -balance } else if (mapping.normalBalance === 'debit') { - // Costs: debit balance is positive in ledger, but on the form they appear negative - amount = -balance + // Costs: debit balance is positive in ledger, keep positive (Skatteverket convention) + amount = balance } else { // Net: negate to match accounting convention amount = -balance @@ -847,26 +837,27 @@ export async function generateINK2Declaration( const totalAssets = ASSET_CODES.reduce((sum, code) => sum + ink2r[code], 0) const totalEquityLiabilities = EQUITY_LIABILITY_CODES.reduce((sum, code) => sum + ink2r[code], 0) - // Operating result: revenue + costs (costs are already negative) + // Operating result: revenue minus costs (costs are positive per Skatteverket convention) const operatingResult = - ink2r['7410'] + ink2r['7411'] + ink2r['7412'] + ink2r['7413'] + - ink2r['7511'] + ink2r['7512'] + ink2r['7513'] + ink2r['7514'] + - ink2r['7515'] + ink2r['7516'] + ink2r['7517'] + ink2r['7410'] + ink2r['7411'] + ink2r['7412'] + ink2r['7413'] + - ink2r['7511'] - ink2r['7512'] - ink2r['7513'] - ink2r['7514'] + - ink2r['7515'] - ink2r['7516'] - ink2r['7517'] - // Financial items + // Financial items: income minus costs const financialItems = - ink2r['7414'] + ink2r['7415'] + ink2r['7423'] + ink2r['7416'] + ink2r['7417'] + - ink2r['7521'] + ink2r['7522'] + ink2r['7414'] + ink2r['7415'] + ink2r['7423'] + ink2r['7416'] + ink2r['7417'] + - ink2r['7521'] - ink2r['7522'] - // Bokslutsdispositioner + // Bokslutsdispositioner: subtract debit-normal, add credit-normal and net const bokslutsdispositioner = - ink2r['7524'] + ink2r['7419'] + ink2r['7420'] + ink2r['7525'] + ink2r['7421'] + ink2r['7422'] + - ink2r['7524'] + ink2r['7419'] + ink2r['7420'] - ink2r['7525'] + + ink2r['7421'] + ink2r['7422'] // Result before tax const resultBeforeTax = operatingResult + financialItems + bokslutsdispositioner - // Result after tax - const resultAfterFinancial = resultBeforeTax + ink2r['7528'] + // Result after tax (7528 is positive, subtract it) + const resultAfterFinancial = resultBeforeTax - ink2r['7528'] // Set årets resultat: vinst (7450) or förlust (7550) if (resultAfterFinancial >= 0) { @@ -888,7 +879,8 @@ export async function generateINK2Declaration( // Build INK2 (huvudblankett) // Auto-derive from INK2S result (simplified: result + non-deductible tax) - const taxAmount = Math.abs(ink2r['7528']) + // 7528 is already positive per Skatteverket convention + const taxAmount = ink2r['7528'] const taxableResult = resultAfterFinancial + taxAmount const ink2: INK2Rutor = { diff --git a/lib/reports/ink2/sru-generator.ts b/lib/reports/ink2/sru-generator.ts index 3acc3ce9..6322d024 100644 --- a/lib/reports/ink2/sru-generator.ts +++ b/lib/reports/ink2/sru-generator.ts @@ -4,6 +4,11 @@ import type { INK2SRutor, SRUSubmission, } from './types' +import { + INK2R_ASSET_CODES, + INK2R_EQUITY_LIABILITY_CODES, + INK2R_INCOME_CODES, +} from './types' /** * SRU File Generator for INK2 (Aktiebolag) @@ -170,8 +175,12 @@ function generateBlanketterSru(declaration: INK2Declaration, now: Date): string lines.push(`#UPPGIFT 7011 ${declaration.ink2['7011']}`) lines.push(`#UPPGIFT 7012 ${declaration.ink2['7012']}`) - // All INK2R fields — emit non-zero values only - const ink2rCodes: INK2RSRUCode[] = Object.keys(declaration.ink2r) as INK2RSRUCode[] + // All INK2R fields in canonical Skatteverket order — emit non-zero values only + const ink2rCodes: INK2RSRUCode[] = [ + ...INK2R_ASSET_CODES, + ...INK2R_EQUITY_LIABILITY_CODES, + ...INK2R_INCOME_CODES, + ] for (const code of ink2rCodes) { const value = declaration.ink2r[code] if (value !== 0) {