fix: validate account mappings and handle reverse charge in categorization

Add validation for non-empty debit/credit accounts before creating journal
entries. Generate fiktiv moms lines (2645/2614) for EU reverse charge expenses.
Change default unmapped expense account from 6900 to 6991. Add tests for
reverse charge handling and exhaustive category mapping coverage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Emil
2026-02-23 12:58:50 +01:00
co-authored by Claude Opus 4.6
parent 3c036059ab
commit 2f953c1031
6 changed files with 133 additions and 4 deletions
@@ -228,6 +228,33 @@ describe('POST /api/transactions/[id]/categorize', () => {
expect(body.error).toBe('Failed to update transaction')
})
it('returns 400 when mapping result has empty debit_account', async () => {
const tx = makeTransaction({
id: 'tx-1',
amount: -500,
journal_entry_id: null,
})
enqueue({ data: tx, error: null })
enqueue({ data: { entity_type: 'enskild_firma', fiscal_year_start_month: 1 }, error: null })
mockBuildMappingResultFromCategory.mockReturnValue({
...defaultMappingResult,
debit_account: '',
})
const request = createMockRequest('/api/transactions/tx-1/categorize', {
method: 'POST',
body: { is_business: true, category: 'expense_software' },
})
const response = await POST(request, createMockRouteParams({ id: 'tx-1' }))
const { status, body } = await parseJsonResponse<{ error: string }>(response)
expect(status).toBe(400)
expect(body.error).toBe('Invalid account mapping: debit and credit accounts are required')
expect(mockCreateTransactionJournalEntry).not.toHaveBeenCalled()
})
it('categorizes as private when is_business is false', async () => {
const tx = makeTransaction({
id: 'tx-1',
@@ -199,6 +199,14 @@ export async function POST(
}
}
// Validate that both accounts are present before proceeding
if (!mappingResult.debit_account || !mappingResult.credit_account) {
return NextResponse.json(
{ error: 'Invalid account mapping: debit and credit accounts are required' },
{ status: 400 }
)
}
// Ensure fiscal period exists for the transaction date
await ensureFiscalPeriod(supabase, user.id, transaction.date, fiscalYearStartMonth)