diff --git a/lib/bookkeeping/__tests__/category-mapping.test.ts b/lib/bookkeeping/__tests__/category-mapping.test.ts index 13053cd9..47815646 100644 --- a/lib/bookkeeping/__tests__/category-mapping.test.ts +++ b/lib/bookkeeping/__tests__/category-mapping.test.ts @@ -381,6 +381,64 @@ describe('private transaction accounts by entity type and direction', () => { }) }) +describe('incoming expense refund (positive amount, expense category)', () => { + it('getCategoryAccountMapping swaps accounts: bank debited, expense account credited', () => { + const result = getCategoryAccountMapping('expense_software', 500, true) + expect(result.debitAccount).toBe('1930') + expect(result.creditAccount).toBe('5420') + }) + + it('getCategoryAccountMapping sets vatCreditAccount 2641 and clears vatDebitAccount for refund', () => { + const result = getCategoryAccountMapping('expense_software', 500, true) + expect(result.vatDebitAccount).toBeNull() + expect(result.vatCreditAccount).toBe('2641') + }) + + it('VAT-exempt expense refund (bank_fees) has no VAT accounts', () => { + const result = getCategoryAccountMapping('expense_bank_fees', 100, true) + expect(result.debitAccount).toBe('1930') + expect(result.creditAccount).toBe('6570') + expect(result.vatDebitAccount).toBeNull() + expect(result.vatCreditAccount).toBeNull() + }) + + it('buildMappingResultFromCategory generates credit line on 2641 for expense refund', () => { + const tx = makeTransaction({ amount: 1000 }) + const result = buildMappingResultFromCategory('expense_software', tx, true) + expect(result.vat_lines).toHaveLength(1) + expect(result.vat_lines[0].account_number).toBe('2641') + expect(result.vat_lines[0].credit_amount).toBe(200) + expect(result.vat_lines[0].debit_amount).toBe(0) + }) + + it('buildMappingResultFromCategory uses återföring description for expense refund VAT', () => { + const tx = makeTransaction({ amount: 1000 }) + const result = buildMappingResultFromCategory('expense_software', tx, true) + expect(result.vat_lines[0].description).toBe('Återföring ingående moms 25%') + }) + + it('buildMappingResultFromCategory generates no VAT line for VAT-exempt expense refund', () => { + const tx = makeTransaction({ amount: 100 }) + const result = buildMappingResultFromCategory('expense_bank_fees', tx, true) + expect(result.vat_lines).toHaveLength(0) + }) + + it('buildMappingResultFromCategory maps debit/credit correctly (bank debited, expense credited)', () => { + const tx = makeTransaction({ amount: 1250 }) + const result = buildMappingResultFromCategory('expense_software', tx, true) + expect(result.debit_account).toBe('1930') + expect(result.credit_account).toBe('5420') + }) + + it('vat_amount override on expense refund uses återföring description', () => { + const tx = makeTransaction({ amount: 1250 }) + const result = buildMappingResultFromCategory('expense_software', tx, true, 'enskild_firma', 'standard_25', 200) + expect(result.vat_lines).toHaveLength(1) + expect(result.vat_lines[0].credit_amount).toBe(200) + expect(result.vat_lines[0].description).toBe('Återföring ingående moms (enligt underlag)') + }) +}) + describe('category default → leaf account guarantee', () => { // BAS encodes the parent/leaf distinction in account_name via the // "(gruppkonto)" suffix. Auditors and Skatteverket downstream reporting diff --git a/lib/bookkeeping/category-mapping.ts b/lib/bookkeeping/category-mapping.ts index 73e6d4c0..e34e920b 100644 --- a/lib/bookkeeping/category-mapping.ts +++ b/lib/bookkeeping/category-mapping.ts @@ -141,6 +141,18 @@ export function getCategoryAccountMapping( // Note: income tax deduction was abolished 2017 (IL 16 kap 2 §), but VAT deduction remains. const resolvedVat = vatTreatment ?? (isVatExempt ? null : category === 'expense_representation' ? 'reduced_12' : 'standard_25') + if (amount > 0) { + // Incoming refund: bank receives money, expense account is reduced (credited). + // Ingående moms is reversed — credit 2641 instead of debit. + return { + debitAccount: BANK_ACCOUNT, + creditAccount: expenseAccount, + vatTreatment: resolvedVat, + vatDebitAccount: null, + vatCreditAccount: resolvedVat ? '2641' : null, + } + } + return { debitAccount: expenseAccount, creditAccount: BANK_ACCOUNT, @@ -291,12 +303,16 @@ export function buildMappingResultFromCategory( description: hasVatOverride ? 'Ingående moms (enligt underlag)' : `Ingående moms ${vatRate * 100}%`, }) } else if (vatAmount > 0 && transaction.amount > 0 && mapping.vatCreditAccount) { - // Income: Utgående moms (output VAT) + // Income output VAT, or expense refund reversing ingående moms (both credit vatCreditAccount). + // Distinguish by account: 2641 = reversed ingående moms, 2611/2621/2631 = utgående moms. + const isExpenseRefund = mapping.vatCreditAccount === '2641' vatLines.push({ account_number: mapping.vatCreditAccount, debit_amount: 0, credit_amount: vatAmount, - description: hasVatOverride ? 'Utgående moms (enligt underlag)' : `Utgående moms ${vatRate * 100}%`, + description: isExpenseRefund + ? (hasVatOverride ? 'Återföring ingående moms (enligt underlag)' : `Återföring ingående moms ${vatRate * 100}%`) + : (hasVatOverride ? 'Utgående moms (enligt underlag)' : `Utgående moms ${vatRate * 100}%`), }) } }