Files
accounted/lib/reports/__tests__/vat-declaration-checks.test.ts
T

630 lines
23 KiB
TypeScript

import { describe, it, expect } from 'vitest'
import type { VatDeclarationRutor } from '@/types'
import { runVatDeclarationChecks } from '../vat-declaration-checks'
const emptyRutor: VatDeclarationRutor = {
ruta05: 0, ruta06: 0, ruta07: 0, ruta08: 0,
ruta10: 0, ruta11: 0, ruta12: 0,
ruta20: 0, ruta21: 0, ruta22: 0, ruta23: 0, ruta24: 0,
ruta30: 0, ruta31: 0, ruta32: 0,
ruta35: 0, ruta36: 0, ruta37: 0, ruta38: 0,
ruta39: 0, ruta40: 0, ruta41: 0, ruta42: 0,
ruta48: 0, ruta49: 0,
ruta50: 0, ruta60: 0, ruta61: 0, ruta62: 0,
}
describe('runVatDeclarationChecks', () => {
it('returns empty findings for a balanced sales-only declaration', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta05: 100000,
ruta10: 25000,
ruta49: 25000,
}
expect(runVatDeclarationChecks(rutor)).toEqual([])
})
it('returns empty findings for a balanced declaration with RC basis + output VAT', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta21: 10000, // EU services basis
ruta30: 2500, // RC output VAT
ruta48: 2500, // matching input VAT
ruta49: 0,
}
expect(runVatDeclarationChecks(rutor)).toEqual([])
})
// FK004 mirror: SKV's primary rejection signal we want to catch locally.
it('flags ERROR when ruta 30-32 populated but ruta 20-24 is empty', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta05: 78852,
ruta10: 19713,
ruta30: 2500,
ruta48: 2500,
ruta49: 19713,
}
const findings = runVatDeclarationChecks(rutor)
const fk004 = findings.find((f) => f.code === 'RC_BASIS_MISSING')
expect(fk004).toBeDefined()
expect(fk004?.status).toBe('ERROR')
expect(fk004?.message).toMatch(/ruta 30-32/)
expect(fk004?.message).toMatch(/ruta 20-24/)
})
// Regression (2026-07-24): the check compared presence, not proportion, so
// correcting ONE voucher out of ~39 cleared the error while ~51 tkr of
// basis was still missing and the declaration claimed to be ready.
it('flags ERROR when RC basis is only partially reported', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta21: 2109.16, // one corrected voucher
ruta30: 13446.18, // fiktiv moms for ~39 vouchers → expects ~53 785 kr basis
ruta48: 13446.18,
ruta49: 0,
}
const finding = runVatDeclarationChecks(rutor).find((f) => f.code === 'RC_BASIS_MISSING')
expect(finding?.status).toBe('ERROR')
expect(finding?.message).toMatch(/saknas/)
})
it('does not flag RC basis inside the per-voucher rounding tolerance', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta21: 9990, // expected 10000, tolerance max(1, 0.5%) = 50
ruta30: 2500,
ruta48: 2500,
ruta49: 0,
}
const findings = runVatDeclarationChecks(rutor)
expect(findings.find((f) => f.code === 'RC_BASIS_MISSING')).toBeUndefined()
expect(findings.find((f) => f.code === 'RC_OUTPUT_MISSING')).toBeUndefined()
})
it('flags ERROR when a whole voucher of basis is missing beyond the tolerance', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta21: 9000, // expected 10000: one ~1000 kr voucher missing
ruta30: 2500,
ruta48: 2500,
ruta49: 0,
}
expect(
runVatDeclarationChecks(rutor).find((f) => f.code === 'RC_BASIS_MISSING')?.status,
).toBe('ERROR')
})
it('flags ERROR when basis exceeds what the output VAT accounts for', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta21: 60000, // expected only 10000 from ruta30: fiktiv moms missing
ruta30: 2500,
ruta48: 2500,
ruta49: 0,
}
expect(
runVatDeclarationChecks(rutor).find((f) => f.code === 'RC_OUTPUT_MISSING')?.status,
).toBe('ERROR')
})
it('flags ERROR when basis is present but no output RC VAT', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta21: 10000,
ruta48: 2500,
ruta49: -2500,
}
const findings = runVatDeclarationChecks(rutor)
expect(findings.find((f) => f.code === 'RC_OUTPUT_MISSING')?.status).toBe('ERROR')
})
it('warns when input VAT is materially smaller than RC output VAT', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta21: 10000,
ruta30: 2500,
ruta48: 100, // Calculated input VAT missing: should be ~2500
ruta49: 2400,
}
const findings = runVatDeclarationChecks(rutor)
const mismatch = findings.find((f) => f.code === 'RC_INPUT_VAT_MISMATCH')
expect(mismatch?.status).toBe('WARNING')
})
// The finding this pair of tests exists for: ruta 48 aggregates 2641 + 2642 +
// 2645 + 2646 + 2647 + 2649, so ordinary debiterad ingående moms masked a
// completely missing RC input. 50 000 kr of RC output with nothing on 2645,
// next to 60 000 kr of ordinary 2641, left ruta 48 (60 000) above the RC
// output (50 000) and the aggregate comparison silent, while the company paid
// in 50 000 kr it was entitled to deduct.
describe('RC_INPUT_VAT_MISMATCH against the RC input accounts', () => {
const rcTotals = (accounts: Record<string, number>) =>
new Map(Object.entries(accounts).map(([acc, debit]) => [acc, { debit, credit: 0 }]))
it('warns when RC output has no 2645/2647 behind it even though ruta 48 is larger', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta21: 200000, // 50 000 / 0.25: basis correctly booked, so no FK004
ruta30: 50000, // fiktiv utgående moms
ruta48: 60000, // ordinary 2641 only: no RC input at all
ruta49: -10000,
}
const totals = rcTotals({ '2641': 60000 })
// Aggregate-only comparison: silent, which is the bug.
expect(
runVatDeclarationChecks(rutor).find((f) => f.code === 'RC_INPUT_VAT_MISMATCH'),
).toBeUndefined()
const finding = runVatDeclarationChecks(rutor, totals)
.find((f) => f.code === 'RC_INPUT_VAT_MISMATCH')
expect(finding?.status).toBe('WARNING')
// \s, not a literal space: sv-SE groups thousands with a no-break space.
expect(finding?.message).toMatch(/50\s000 kr/)
expect(finding?.message).toMatch(/saknas/)
})
it('does not warn for a correct RC declaration where 2645 mirrors the output', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta21: 200000,
ruta30: 50000,
ruta48: 110000, // 60 000 ordinary 2641 + 50 000 RC input
ruta49: -60000,
}
const totals = rcTotals({ '2641': 60000, '2645': 50000 })
expect(
runVatDeclarationChecks(rutor, totals).find((f) => f.code === 'RC_INPUT_VAT_MISMATCH'),
).toBeUndefined()
})
it('accepts 2647 (domestic reverse charge) as the mirror', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta24: 200000, // domestic RC services, ML 16 kap
ruta30: 50000,
ruta48: 50000,
ruta49: 0,
}
expect(
runVatDeclarationChecks(rutor, rcTotals({ '2647': 50000 }))
.find((f) => f.code === 'RC_INPUT_VAT_MISMATCH'),
).toBeUndefined()
})
it('warns on a partial RC input shortfall the aggregate cannot see', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta21: 200000,
ruta30: 50000,
ruta48: 80000, // 60 000 ordinary + only 20 000 of the 50 000 RC input
ruta49: -30000,
}
const totals = rcTotals({ '2641': 60000, '2645': 20000 })
expect(
runVatDeclarationChecks(rutor).find((f) => f.code === 'RC_INPUT_VAT_MISMATCH'),
).toBeUndefined()
const finding = runVatDeclarationChecks(rutor, totals)
.find((f) => f.code === 'RC_INPUT_VAT_MISMATCH')
expect(finding?.status).toBe('WARNING')
expect(finding?.message).toMatch(/30\s000 kr saknas/)
})
it('leaves a declaration with no reverse charge at all untouched', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta05: 400000,
ruta10: 100000,
ruta48: 60000, // ordinary input VAT only, no rutor 30-32
ruta49: 40000,
}
const totals = rcTotals({ '2641': 60000 })
expect(runVatDeclarationChecks(rutor, totals)).toEqual([])
expect(runVatDeclarationChecks(rutor)).toEqual([])
})
// 2649 carries the deductible portion of shared costs in general, not RC
// input; counting it would put the masking back.
it('does not count 2649 (blandad verksamhet) as reverse-charge input', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta21: 200000,
ruta30: 50000,
ruta48: 50000,
ruta49: 0,
}
expect(
runVatDeclarationChecks(rutor, rcTotals({ '2649': 50000 }))
.find((f) => f.code === 'RC_INPUT_VAT_MISMATCH')?.status,
).toBe('WARNING')
})
// A credit on 2645 (e.g. a storno of the fiktiv-moms pair) must reduce the
// RC input, not be ignored: the debit balance is what ruta 48 receives.
it('nets credits on the RC input accounts against their debits', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta21: 200000,
ruta30: 50000,
ruta48: 20000,
ruta49: 30000,
}
const totals = new Map([['2645', { debit: 50000, credit: 30000 }]])
const finding = runVatDeclarationChecks(rutor, totals)
.find((f) => f.code === 'RC_INPUT_VAT_MISMATCH')
expect(finding?.status).toBe('WARNING')
expect(finding?.message).toMatch(/30\s000 kr saknas/)
})
})
// FK009 detection: if our calculator and SKV's recomputed sum disagree
// we flag locally so we never submit a drift.
it('flags ERROR when ruta49 drifts from the canonical formula', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta10: 100,
ruta48: 20,
ruta49: 99, // wrong: should be 80
}
const findings = runVatDeclarationChecks(rutor)
const drift = findings.find((f) => f.code === 'SUMMA_MOMS_DRIFT')
expect(drift?.status).toBe('ERROR')
})
it('ignores fractional-öre drift (≤ 0.5 SEK)', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta10: 100.30,
ruta48: 20.10,
ruta49: 80.20, // canonical formula exactly, only fractional öre
}
const findings = runVatDeclarationChecks(rutor)
expect(findings.find((f) => f.code === 'SUMMA_MOMS_DRIFT')).toBeUndefined()
})
// SKV §4.1.1.4 rule 1: taxable sales base without output VAT.
it('flags ERROR when taxable sales (ruta 05) booked without output VAT', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta05: 10000,
// ruta 10/11/12 all zero: SKV rule 1 violation
ruta49: 0,
}
const findings = runVatDeclarationChecks(rutor)
const finding = findings.find((f) => f.code === 'TAXABLE_SALES_WITHOUT_OUTPUT')
expect(finding?.status).toBe('ERROR')
expect(finding?.message).toMatch(/försäljning/)
expect(finding?.message).toMatch(/utgående moms/)
})
it('flags ERROR for ruta 06 (uttag) without output VAT', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta06: 5000,
ruta49: 0,
}
const findings = runVatDeclarationChecks(rutor)
expect(findings.find((f) => f.code === 'TAXABLE_SALES_WITHOUT_OUTPUT')?.status).toBe('ERROR')
})
it('does not flag taxable sales without output VAT when output VAT is present', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta05: 10000,
ruta10: 2500,
ruta49: 2500,
}
const findings = runVatDeclarationChecks(rutor)
expect(findings.find((f) => f.code === 'TAXABLE_SALES_WITHOUT_OUTPUT')).toBeUndefined()
expect(findings.find((f) => f.code === 'SALES_OUTPUT_VAT_SHORTFALL')).toBeUndefined()
})
// Regression (2026-07-27): the binary sales check cleared as soon as ANY
// output VAT existed, so 2 000 kr of missing utgående moms on a 400 000 kr
// base rendered "Inga fel hittades" with Skicka enabled. The proportional
// form warns (never blocks: periodisering legitimately drifts this way).
describe('SALES_OUTPUT_VAT_SHORTFALL (proportional, warning tier)', () => {
it('warns when the sales base implies more output VAT than declared', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta05: 400000,
ruta10: 98000, // 100 000 kr expected at 25%: 2 000 kr moms missing
ruta49: 98000,
}
const findings = runVatDeclarationChecks(rutor)
const finding = findings.find((f) => f.code === 'SALES_OUTPUT_VAT_SHORTFALL')
expect(finding?.status).toBe('WARNING')
expect(finding?.message).toMatch(/saknar/)
expect(finding?.message).toMatch(/periodisering/)
// Never a filing blocker: no ERROR may fire on this declaration, so
// isFilingBlocked (ERROR-only) keeps Skicka enabled while the banner
// stops claiming "Inga fel hittades".
expect(findings.find((f) => f.code === 'TAXABLE_SALES_WITHOUT_OUTPUT')).toBeUndefined()
expect(findings.every((f) => f.status === 'WARNING')).toBe(true)
})
it('stays green for an exact mixed-rate declaration', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta05: 170000, // 100 000 @25% + 50 000 @12% + 20 000 @6%
ruta10: 25000,
ruta11: 6000,
ruta12: 1200,
ruta49: 32200,
}
expect(runVatDeclarationChecks(rutor)).toEqual([])
})
it('absorbs drift inside the 0.5% tolerance', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta05: 100040, // implied base 100 000, tolerance 500
ruta10: 25000,
ruta49: 25000,
}
expect(
runVatDeclarationChecks(rutor).find((f) => f.code === 'SALES_OUTPUT_VAT_SHORTFALL'),
).toBeUndefined()
})
it('warns just outside the 0.5% tolerance', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta05: 100600, // implied base 100 000, tolerance 500
ruta10: 25000,
ruta49: 25000,
}
expect(
runVatDeclarationChecks(rutor).find((f) => f.code === 'SALES_OUTPUT_VAT_SHORTFALL')?.status,
).toBe('WARNING')
})
it('applies the 1 kr tolerance floor at a small base', () => {
const inside: VatDeclarationRutor = {
...emptyRutor,
ruta05: 100.8, // implied 100, floor tolerance 1 kr
ruta10: 25,
ruta49: 25,
}
expect(
runVatDeclarationChecks(inside).find((f) => f.code === 'SALES_OUTPUT_VAT_SHORTFALL'),
).toBeUndefined()
const outside: VatDeclarationRutor = {
...emptyRutor,
ruta05: 104,
ruta10: 25,
ruta49: 25,
}
expect(
runVatDeclarationChecks(outside).find((f) => f.code === 'SALES_OUTPUT_VAT_SHORTFALL')?.status,
).toBe('WARNING')
})
it('does not fire when output VAT is absent entirely (binary ERROR owns that case)', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta05: 10000,
ruta49: 0,
}
const findings = runVatDeclarationChecks(rutor)
expect(findings.find((f) => f.code === 'TAXABLE_SALES_WITHOUT_OUTPUT')?.status).toBe('ERROR')
expect(findings.find((f) => f.code === 'SALES_OUTPUT_VAT_SHORTFALL')).toBeUndefined()
})
})
// Mirror: output VAT without taxable sales base.
it('flags ERROR when output VAT booked without taxable sales base', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
// No ruta 05/06/07/08
ruta10: 2500,
ruta49: 2500,
}
const findings = runVatDeclarationChecks(rutor)
const finding = findings.find((f) => f.code === 'OUTPUT_VAT_WITHOUT_SALES_BASE')
expect(finding?.status).toBe('ERROR')
expect(finding?.message).toContain('momspliktiga intäktskonton')
expect(finding?.message).toContain('Standard moms')
expect(finding?.message).not.toContain('3001/3002/3003')
})
// SKV §4.1.1.4 rule 5: import base without import output VAT.
it('flags ERROR when import base (ruta 50) without import output VAT', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta50: 10000,
// ruta 60/61/62 all zero
ruta48: 0,
ruta49: 0,
}
const findings = runVatDeclarationChecks(rutor)
expect(findings.find((f) => f.code === 'IMPORT_BASE_WITHOUT_OUTPUT')?.status).toBe('ERROR')
})
// SKV §4.1.1.4 rule 6: import output VAT without import base.
it('flags ERROR when import output VAT (ruta 60) without ruta 50', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta60: 2500,
ruta48: 2500,
ruta49: 0,
}
const findings = runVatDeclarationChecks(rutor)
expect(findings.find((f) => f.code === 'IMPORT_OUTPUT_WITHOUT_BASE')?.status).toBe('ERROR')
})
it('does not flag import checks when both base and output VAT are present', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta50: 10000,
ruta60: 2500,
ruta48: 2500,
ruta49: 0,
}
const findings = runVatDeclarationChecks(rutor)
expect(findings.find((f) => f.code === 'IMPORT_BASE_WITHOUT_OUTPUT')).toBeUndefined()
expect(findings.find((f) => f.code === 'IMPORT_OUTPUT_WITHOUT_BASE')).toBeUndefined()
})
it('does not flag import checks for a mixed-rate import declaration', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta50: 170000, // 100 000 @ 25% + 50 000 @ 12% + 20 000 @ 6%
ruta60: 25000,
ruta61: 6000,
ruta62: 1200,
ruta48: 32200,
ruta49: 0,
}
const findings = runVatDeclarationChecks(rutor)
expect(findings.find((f) => f.code === 'IMPORT_BASE_WITHOUT_OUTPUT')).toBeUndefined()
expect(findings.find((f) => f.code === 'IMPORT_OUTPUT_WITHOUT_BASE')).toBeUndefined()
})
// The binary presence test passed this: SOME import output VAT existed, so
// 100 000 kr of tullvärdesunderlag went out without its 25 000 kr importmoms.
it('flags ERROR when only part of the import base carries output VAT', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta50: 500000, // declared base
ruta60: 100000, // only accounts for 400 000 kr of it
ruta48: 100000,
ruta49: 0,
}
const finding = runVatDeclarationChecks(rutor)
.find((f) => f.code === 'IMPORT_BASE_WITHOUT_OUTPUT')
expect(finding?.status).toBe('ERROR')
expect(finding?.message).toMatch(/saknar utgående moms/)
})
// Mirror shape, also passed by the old binary test: ruta 50 was non-zero.
it('flags ERROR when the import base is only partially reported', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta50: 400000,
ruta60: 125000, // implies a 500 000 kr base: 100 000 kr missing
ruta48: 125000,
ruta49: 0,
}
const finding = runVatDeclarationChecks(rutor)
.find((f) => f.code === 'IMPORT_OUTPUT_WITHOUT_BASE')
expect(finding?.status).toBe('ERROR')
expect(finding?.message).toMatch(/saknas/)
})
// Tolerance boundary: max(1, 0.5% of the implied base) = 2 500 kr at 500 000.
it('does not flag import base inside the per-voucher rounding tolerance', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta50: 497501, // 2 499 kr short of the implied 500 000
ruta60: 125000,
ruta48: 125000,
ruta49: 0,
}
const findings = runVatDeclarationChecks(rutor)
expect(findings.find((f) => f.code === 'IMPORT_OUTPUT_WITHOUT_BASE')).toBeUndefined()
expect(findings.find((f) => f.code === 'IMPORT_BASE_WITHOUT_OUTPUT')).toBeUndefined()
})
it('flags ERROR just outside the import tolerance', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta50: 497000, // 3 000 kr short: beyond the 2 500 kr tolerance
ruta60: 125000,
ruta48: 125000,
ruta49: 0,
}
expect(
runVatDeclarationChecks(rutor).find((f) => f.code === 'IMPORT_OUTPUT_WITHOUT_BASE')?.status,
).toBe('ERROR')
})
it('does not flag an import base excess inside the tolerance', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta50: 502499, // 2 499 kr above the implied 500 000
ruta60: 125000,
ruta48: 125000,
ruta49: 0,
}
expect(
runVatDeclarationChecks(rutor).find((f) => f.code === 'IMPORT_BASE_WITHOUT_OUTPUT'),
).toBeUndefined()
})
it('flags ERROR just outside the import base excess tolerance', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta50: 503000, // 3 000 kr above the implied 500 000: beyond the 2 500 kr tolerance
ruta60: 125000,
ruta48: 125000,
ruta49: 0,
}
expect(
runVatDeclarationChecks(rutor).find((f) => f.code === 'IMPORT_BASE_WITHOUT_OUTPUT')?.status,
).toBe('ERROR')
})
// At a small import base the tolerance is the 1 kr floor, not 0.5% (which
// would be 0.50 kr on a 100 kr base). Every other import case above has a
// basis large enough that the percentage branch wins, so the floor branch of
// Math.max would otherwise go untested.
it('applies the 1 kr tolerance floor at a small import base', () => {
const withinFloor: VatDeclarationRutor = {
...emptyRutor,
ruta50: 100.5, // 0.50 kr over the implied 100 kr: inside the 1 kr floor
ruta60: 25,
ruta48: 25,
ruta49: 0,
}
expect(
runVatDeclarationChecks(withinFloor).find((f) => f.code === 'IMPORT_BASE_WITHOUT_OUTPUT'),
).toBeUndefined()
const outsideFloor: VatDeclarationRutor = { ...withinFloor, ruta50: 102 }
expect(
runVatDeclarationChecks(outsideFloor).find((f) => f.code === 'IMPORT_BASE_WITHOUT_OUTPUT')
?.status,
).toBe('ERROR')
})
// The sales pair stays binary on purpose: rutor 07/08 have no source accounts
// in ACCOUNT_RUTA while their output VAT (2613/2616 etc.) still feeds ruta 10,
// and periodiserade invoice lines credit 29xx with the moms left on 2611. A
// proportional check would block filing on correct declarations.
it('does not flag the sales pair when output VAT exceeds what ruta 05-08 implies', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta05: 100000,
ruta10: 125000, // e.g. VMB (2616) or frivillig uthyrning (2613) on top
ruta48: 0,
ruta49: 125000,
}
const findings = runVatDeclarationChecks(rutor)
expect(findings.find((f) => f.code === 'TAXABLE_SALES_WITHOUT_OUTPUT')).toBeUndefined()
expect(findings.find((f) => f.code === 'OUTPUT_VAT_WITHOUT_SALES_BASE')).toBeUndefined()
// The proportional warning checks the OPPOSITE direction only: excess
// output (VMB, uthyrning, overrides) must never trigger it either.
expect(findings.find((f) => f.code === 'SALES_OUTPUT_VAT_SHORTFALL')).toBeUndefined()
})
// Multiple findings should surface together so the user sees the whole picture.
it('reports multiple distinct findings for a deeply broken declaration', () => {
const rutor: VatDeclarationRutor = {
...emptyRutor,
ruta05: 10000, // taxable sales but no output VAT
ruta30: 2500, // RC output but no RC basis
ruta50: 5000, // import base but no import output
ruta48: 0,
ruta49: 2500,
}
const findings = runVatDeclarationChecks(rutor)
const codes = findings.map((f) => f.code).sort()
expect(codes).toContain('TAXABLE_SALES_WITHOUT_OUTPUT')
expect(codes).toContain('RC_BASIS_MISSING')
expect(codes).toContain('IMPORT_BASE_WITHOUT_OUTPUT')
})
})