import { calculateNormalBase } from '../../resources/js/lib/irpc-normal-base.ts'; import { fraction } from '../../resources/js/lib/fiscal-rational.ts'; import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import { normalBook, normalCurrent, calculateNormal, recordNormal, saveNormalBook, normalHtml, normalReportRows, } from '../../resources/js/lib/irpc-normal.ts'; import { newNormalCosts, newCostRow, reusableCreditBalance, costRules, normalCostReport, } from '../../resources/js/lib/irpc-normal-costs.ts'; import { createProjectDossierReport } from '../../resources/js/lib/project-dossier-report.ts'; const seed = () => JSON.parse( fs.readFileSync( new URL( './fixtures/irpc-schedules-preserved.json', import.meta.url, ), 'utf8', ), ); const val = (f) => (f ? Number(f.numerator) / Number(f.denominator) : null); function row(id, rule, amount, facts = {}, sourceCode = 'operating_expenses') { return { ...newCostRow(id), rule, amount, sourceCode, document: 'QA-' + id, line: '1', reference: 'QA documento e factos qualificados para o período anual2026; valores sintéticos', conditions: 'yes', facts, }; } function reconcile(b) { const totals = {}; for (const r of b.schedules.rows) { const k = { tax_own: 'expenseCorrections', tax_other: 'expenseCorrections', undocumented: 'expenseCorrections', capital_in: 'positivePatrimonial', patrimonial_gain: 'positivePatrimonial', capital_out: 'negativePatrimonial', patrimonial_cost: 'negativePatrimonial', future_cost: 'timing', future_income: 'timing', current_cost: 'timing', current_income: 'timing', }[r.rule]; totals[k] = (totals[k] ?? 0) + Number(r.amount); } for (const r of b.costs.rows) { const k = costRules[r.rule][1]; totals[k] = (totals[k] ?? 0) + Number(r.amount); } for (const [k, a] of Object.entries(totals)) b.profit.topics[k] = { state: 'known', amount: String(a), reference: 'QA reconciliação aritmética por documento/linha dos mapas', }; } function setup(rows) { const d = seed(), b = normalBook(d); b.costs = newNormalCosts(); b.costs.rows = rows; b.costs.advertisingReference = 'QA publicidade completa e vendas normais12000 integralmente reconciliadas'; b.costs.salesOrdinary = 'yes'; b.costs.creditRegime = 'ordinary'; b.costs.creditBase = '10000'; b.costs.creditPrevious = '550'; b.costs.creditReference = 'QA créditos ordinários finais10000 e provisão fiscal anterior550 documentados, sem regime financeiro'; reconcile(b); return { d, b }; } function joint() { return setup([ row('allowance', 'allowance', '100', { billed: '20', taxedNotBilled: '20', }), row('representation', 'representation', '50'), row('advertising', 'advertising', '80', { campaign: 'no' }), row('credit', 'credit', '200'), row('stock', 'stock', '60', { balanceKey: 'inventory-A', lossObserved: 'yes', observedLoss: '40', previous: '10', }), row('litigation', 'litigation', '90', { balanceKey: 'court-A', caseOngoing: 'yes', chargeDeductible: 'yes', obligation: '100', previous: '20', }), row('fuel', 'fuel', '50', { supportedNormal: '40', passenger: 'no' }), row('prior', 'prior_cost', '80', { originalYear: '2025', unknownAtClose: 'no', closingEvidence: 'QA factura conhecida no fecho2025', }), row( 'work', 'work', '1000', { projectKey: 'project-A', method: 'percentage', price: '2000', totalCost: '1000', incurred: '500', priorRevenue: '600', priorCost: '300', bookCost: '200', costSource: 'cost_of_sales', partialBilling: 'yes', ownFractionalDelivery: 'no', consistentMethod: 'yes', deferredReceipt: '0', }, 'revenue', ), ]); } function model(r) { const { d, b } = setup([r]); return calculateNormal(d, b); } const item = (m) => m.costs.rows[0]; void test('compatible costs/provisions/prior timing/work produce exact profit6730 base4430 colecta1417.60 from existing6240 result', () => { const { d, b } = joint(), m = calculateNormal(d, b); assert.equal(val(m.accountingProfit), 6240); assert.equal(val(m.schedules.net), 520); assert.equal(val(m.costs.net), -250); assert.equal(val(m.taxableProfit), 6730); assert.equal(val(m.taxableBase), 4430); assert.equal(val(m.colecta), 1417.6); assert.equal(m.version, 'IRPC-normal-adjustments/6'); assert.equal(m.tax, null); assert.equal(m.assessment.finalBalance.payable, null); assert.deepEqual( m.costs.rows.map((r) => val(r.adjustment)), [30, 40, 0, 150, 30, 10, 10, 80, -600], ); assert.equal(m.costs.pending.length, 0); }); void test('allowance exceptions are disjoint amounts; accepted70 plus addback30; known zero differs from missing', () => { let m = model( row('e', 'allowance', '100', { billed: '20', taxedNotBilled: '20' }), ); assert.equal(val(item(m).accepted), 70); assert.equal(val(item(m).adjustment), 30); for (const facts of [ { billed: '0', taxedNotBilled: '0' }, { billed: '100', taxedNotBilled: '0' }, ]) assert.notEqual( model(row('e', 'allowance', '100', facts)).taxableProfit, null, ); for (const facts of [ { billed: '', taxedNotBilled: '0' }, { billed: '60', taxedNotBilled: '50' }, ]) assert.equal( model(row('e', 'allowance', '100', facts)).taxableProfit, null, ); }); void test('representation20percent accepted; fractional cents remain exact without fiscal rounding or autonomous tax', () => { const m = model(row('f', 'representation', '0.01')); assert.deepEqual(item(m).accepted, { numerator: '1', denominator: '500' }); assert.deepEqual(item(m).adjustment, { numerator: '1', denominator: '125', }); assert.equal(m.tax, null); assert.equal(m.assessment.finalBalance.scope, 'L8'); }); void test('advertising shares one annual1percent cap across documents and has no per-invoice duplicate allowance', () => { const { d, b } = setup([ row('n1', 'advertising', '100', { campaign: 'no' }), row('n2', 'advertising', '100', { campaign: 'no' }), ]); let m = calculateNormal(d, b); assert.deepEqual( m.costs.rows.map((r) => val(r.accepted)), [60, 60], ); assert.equal(val(m.costs.net), 80); assert.deepEqual( m.costs.rows.map((r) => val(r.limit)), [120, 120], ); b.costs.rows.reverse(); assert.equal(val(calculateNormal(d, b).costs.net), 80); b.costs.salesOrdinary = ''; m = calculateNormal(d, b); assert.equal(m.taxableProfit, null); assert.equal(m.schedules.net.numerator, '520'); const c = setup([ row('ad-a', 'advertising', '100', { campaign: 'no' }), row('ad-b', 'advertising', '250', { campaign: 'no' }), ]); const f = calculateNormal(c.d, c.b); assert.match(JSON.stringify(normalCostReport(f.costs)), /exacto 240\/7/); }); void test('campaign36A never silently receives ordinary advertising treatment', () => { const m = model(row('campaign', 'advertising', '100', { campaign: 'yes' })); assert.equal(m.taxableProfit, null); assert.equal(item(m).accepted, null); assert.match(item(m).reason, /36-A/); }); void test('vehicle costs distinguish ordinary passenger50percent, documented public/rental exception and nonpassenger', () => { for (const [facts, accepted] of [ [{ passenger: 'yes', publicOrRental: 'no' }, 50], [{ passenger: 'yes', publicOrRental: 'yes' }, 100], [{ passenger: 'no' }, 100], ]) assert.equal( val(item(model(row('v', 'vehicle', '100', facts))).accepted), accepted, ); assert.equal( model(row('v', 'vehicle', '100', { passenger: 'yes' })).taxableProfit, null, ); }); void test('fuel and rent cover independent rules; overlapping36h-i and36-4 stays pending without an invented combination', () => { let m = model( row('fuel', 'fuel', '100', { supportedNormal: '80', passenger: 'yes', publicOrRental: 'no', }), ); assert.equal(m.taxableProfit, null); assert.match(item(m).reason, /Articulação/); m = model( row('fuel', 'fuel', '100', { supportedNormal: '100', passenger: 'yes', publicOrRental: 'no', }), ); assert.equal(val(item(m).accepted), 50); m = model( row('rent', 'rental', '100', { nonAcceptedQuota: '20', quotaReference: 'QA quota36h documentada; não calculada automaticamente', passenger: 'yes', publicOrRental: 'yes', }), ); assert.equal(val(item(m).accepted), 80); assert.equal(val(item(m).adjustment), 20); assert.match(item(m).reason, /não apura automaticamente/); assert.equal( model( row('fuel', 'fuel', '100', { supportedNormal: '101', passenger: 'no', }), ).taxableProfit, null, ); assert.equal( model( row('rent', 'rental', '100', { nonAcceptedQuota: '', quotaReference: 'QA', passenger: 'yes', publicOrRental: 'no', }), ).taxableProfit, null, ); }); void test('credit provision caps both annual1.5percent and cumulative6percent; multiple rows share headroom and do not consume source balance', () => { const { d, b } = setup([ row('c1', 'credit', '100'), row('c2', 'credit', '100'), ]); let m = calculateNormal(d, b); assert.deepEqual( m.costs.rows.map((r) => val(r.accepted)), [25, 25], ); assert.equal(val(m.costs.net), 150); assert.equal(b.costs.creditPrevious, '550'); b.costs.creditPrevious = '0'; m = calculateNormal(d, b); assert.equal(val(m.costs.net), 50); b.costs.creditPrevious = '600'; assert.equal(val(calculateNormal(d, b).costs.net), 200); b.costs.creditPrevious = '601'; assert.equal(calculateNormal(d, b).taxableProfit, null); b.costs.creditRegime = 'regulated'; assert.equal(calculateNormal(d, b).taxableProfit, null); b.costs.creditRegime = 'ordinary'; b.costs.creditPrevious = ''; assert.equal(calculateNormal(d, b).taxableProfit, null); }); void test('inventory allowance respects observed loss less existing fiscal provision; absent observation is known rejection, not unknown zero', () => { let m = model( row('s', 'stock', '60', { balanceKey: 'stock', lossObserved: 'yes', observedLoss: '40', previous: '10', }), ); assert.equal(val(item(m).accepted), 30); assert.equal(val(item(m).adjustment), 30); m = model(row('s', 'stock', '60', { lossObserved: 'no' })); assert.equal(val(item(m).accepted), 0); assert.equal(val(item(m).adjustment), 60); m = model( row('s', 'stock', '60', { balanceKey: 'stock', lossObserved: 'yes', observedLoss: '40', previous: '', }), ); assert.equal(m.taxableProfit, null); }); void test('litigation needs current court process and deductible underlying charge; known failure adds back full amount', () => { for (const facts of [ { caseOngoing: 'no' }, { caseOngoing: 'yes', chargeDeductible: 'no' }, ]) { const m = model(row('l', 'litigation', '90', facts)); assert.equal(val(item(m).accepted), 0); assert.equal(val(item(m).adjustment), 90); } const m = model( row('l', 'litigation', '90', { caseOngoing: 'yes', chargeDeductible: 'yes', balanceKey: 'court', obligation: '100', previous: '20', }), ); assert.equal(val(item(m).accepted), 80); assert.equal( model( row('l', 'litigation', '90', { caseOngoing: 'yes', chargeDeductible: '', }), ).taxableProfit, null, ); }); void test('reversal before deducted is taxed once; taxed-provision reversal is excluded only with prior fiscal evidence', () => { let m = model( row( 'r', 'provision_reversal', '50', { balanceKey: 'old', previous: '70', eventEnded: 'yes', inResult: 'yes', }, 'revenue', ), ); assert.equal(val(item(m).adjustment), 0); m = model( row( 'r', 'provision_reversal', '50', { balanceKey: 'old', previous: '70', eventEnded: 'yes', inResult: 'no', }, '', ), ); assert.equal(val(item(m).adjustment), 50); m = model( row( 'r', 'taxed_reversal', '50', { balanceKey: 'old', previous: '70', eventEnded: 'yes', inResult: 'yes', }, 'revenue', ), ); assert.equal(val(item(m).adjustment), -50); assert.equal( model( row( 'r', 'taxed_reversal', '80', { balanceKey: 'old', previous: '70', eventEnded: 'yes', inResult: 'yes', }, 'revenue', ), ).taxableProfit, null, ); }); void test('same fiscal object cannot reuse stock/litigation/reversal balance with another document', () => { const { d, b } = setup([ row('s1', 'stock', '50', { balanceKey: 'same', lossObserved: 'yes', observedLoss: '100', previous: '0', }), row('s2', 'stock', '50', { balanceKey: 'same', lossObserved: 'yes', observedLoss: '100', previous: '0', }), ]); const m = calculateNormal(d, b); assert.equal(m.taxableProfit, null); assert.ok(m.costs.rows.every((r) => r.adjustment === null)); }); void test('prior-period cost and income distinguish knowledge at previous closing; never edit earlier returns or recorded histories', () => { for (const [rule, src, unknown, expected] of [ ['prior_cost', 'operating_expenses', 'yes', 0], ['prior_cost', 'operating_expenses', 'no', 100], ['prior_income', 'revenue', 'yes', 0], ['prior_income', 'revenue', 'no', -100], ]) { const m = model( row( 'p', rule, '100', { originalYear: '2025', unknownAtClose: unknown, closingEvidence: 'QA fecho anterior', }, src, ), ); assert.equal(val(item(m).adjustment), expected); } assert.equal( model( row('p', 'prior_cost', '100', { originalYear: '2025', unknownAtClose: '', closingEvidence: 'QA', }), ).taxableProfit, null, ); }); void test('work percentage uses accumulated performance less prior recognized revenue and cost, no double recognition', () => { const { d, b } = joint(), m = calculateNormal(d, b), w = m.costs.rows.at(-1); assert.equal(val(w.limit), 400); assert.equal(val(w.accepted), 200); assert.equal(val(w.adjustment), -600); assert.match(w.reason, /grau=incorrido/); assert.equal(b.costs.rows.at(-1).facts.priorRevenue, '600'); }); void test('closing work respects mandatory percentage,95percent threshold and missing facts; estimated loss is separate full addback', () => { const { d, b } = joint(), w = b.costs.rows.at(-1); w.facts.method = 'closing'; assert.equal(calculateNormal(d, b).taxableProfit, null); w.facts.partialBilling = 'no'; w.facts.priorRevenue = '0'; w.facts.priorCost = '0'; w.facts.publicReception = 'no'; let m = calculateNormal(d, b); assert.equal(val(m.costs.rows.at(-1).limit), 0); w.facts.incurred = '950'; m = calculateNormal(d, b); assert.equal(val(m.costs.rows.at(-1).limit), 2000); w.facts.deferredReceipt = '1'; assert.equal(calculateNormal(d, b).taxableProfit, null); assert.equal( val( item(model(row('estimate', 'estimated_work_loss', '100'))) .adjustment, ), 100, ); }); void test('cross-map duplicate identity and cumulative source over-allocation block only dependent totals', () => { const { d, b } = joint(); b.costs.rows[0].document = b.rows[0].document + ' '; b.costs.rows[0].line = b.rows[0].line; let m = calculateNormal(d, b); assert.equal(m.taxableProfit, null); assert.match(m.costs.rows[0].reason, /repetidos/); assert.equal(m.costs.rows[0].adjustment, null); assert.equal(val(m.costs.rows[1].adjustment), 40); const c = joint(); c.b.costs.rows[0].amount = '1000'; reconcile(c.b); m = calculateNormal(c.d, c.b); assert.equal(m.taxableProfit, null); assert.match(m.costs.pending.join(' '), /excedem a linha/); }); void test('missing one credit parameter retains known independent corrections and makes downstream totals unknown; upstream records remain', () => { const { d, b } = joint(); b.costs.creditPrevious = ''; const m = calculateNormal(d, b); assert.equal(m.taxableProfit, null); assert.equal(m.taxableBase, null); assert.equal(m.colecta, null); assert.equal(val(m.accountingProfit), 6240); assert.equal(val(m.costs.rows[1].adjustment), 40); assert.equal(val(m.schedules.net), 520); assert.equal(m.schedules.pending.length, 0); }); void test('credit reuse requires known financial closing balances; absent schedule never creates zero', () => { assert.equal(reusableCreditBalance(null), null); assert.equal( reusableCreditBalance({ cashSchedule: [ { salesReceivablesBalance: null, openingReceivablesBalance: '0', }, ], }), null, ); assert.equal( val( reusableCreditBalance({ cashSchedule: [ { salesReceivablesBalance: '100', openingReceivablesBalance: '20', }, ], }), ), 120, ); }); void test('installed5 stays current without recalculation; version6 API-ready snapshots preserve books/history and export exact rules/amounts', () => { const old = seed(), before = normalBook(old); assert.equal(normalCurrent(old, before), true); assert.equal(before.result.model.version, 'IRPC-normal-adjustments/5'); const { d, b } = joint(), store = JSON.parse(d.modelo_a_snapshot); d.modelo_a_snapshot = saveNormalBook( d, recordNormal(d, b, '2026-10-26T00:30:00+02:00'), ); const back = normalBook(JSON.parse(JSON.stringify(d))); assert.equal(normalCurrent(d, back), true); assert.deepEqual(back.history.at(-1), before.result); const saved = JSON.parse(d.modelo_a_snapshot); assert.deepEqual(saved.books, store.books); assert.deepEqual(saved.irpcWithholdings, store.irpcWithholdings); assert.match(normalHtml(d), /1417.60/); assert.match(normalHtml(d), /parcela aceite/); assert.match( JSON.stringify( createProjectDossierReport(d, { selectedSections: new Set(['finance']), }), ), /36\(1\)n/, ); assert.match(JSON.stringify(normalReportRows(d)), /M20 — correspondência/); fs.mkdirSync('outputs/irpc-l6-costs', { recursive: true }); fs.writeFileSync( 'outputs/irpc-l6-costs/calculated.json', JSON.stringify(d), ); fs.writeFileSync('outputs/irpc-l6-costs/preparation.html', normalHtml(d)); const missing = normalBook(d); missing.costs.creditPrevious = ''; d.modelo_a_snapshot = saveNormalBook(d, recordNormal(d, missing)); fs.writeFileSync('outputs/irpc-l6-costs/missing.json', JSON.stringify(d)); const changed = normalBook(d); changed.costs.rows[0].facts.billed = '30'; d.modelo_a_snapshot = saveNormalBook(d, changed); assert.equal(normalCurrent(d, normalBook(d)), false); assert.throws(() => normalHtml(d)); }); void test('malformed versions, unknown rules and non-string facts are refused with saved originals intact', () => { const { d, b } = joint(); for (const change of [ (x) => (x.version = 2), (x) => (x.rows[0].rule = '__proto__'), (x) => (x.rows[0].facts.billed = 20), ]) { const c = structuredClone(b); change(c.costs); assert.throws(() => saveNormalBook(d, c)); } assert.equal(normalCurrent(d, normalBook(d)), true); }); void test('additional categories,shared limits and closing method generate separate recoverable compatible cases', () => { const cases = { additional: setup([ row('vehicle', 'vehicle', '100', { passenger: 'yes', publicOrRental: 'no', }), row('rental', 'rental', '100', { nonAcceptedQuota: '20', quotaReference: 'QA quota36h documentada', passenger: 'yes', publicOrRental: 'yes', }), row('estimate', 'estimated_work_loss', '50'), row( 'reverse', 'provision_reversal', '50', { balanceKey: 'provision-old-deducted', previous: '50', eventEnded: 'yes', inResult: 'no', }, '', ), row( 'taxed-reverse', 'taxed_reversal', '50', { balanceKey: 'provision-old-taxed', previous: '50', eventEnded: 'yes', inResult: 'yes', }, 'revenue', ), row( 'prior-income', 'prior_income', '50', { originalYear: '2025', unknownAtClose: 'no', closingEvidence: 'QA receita conhecida no encerramento anterior', }, 'revenue', ), ]), limits: setup([ row('ad1', 'advertising', '100', { campaign: 'no' }), row('ad2', 'advertising', '100', { campaign: 'no' }), row('cr1', 'credit', '100'), row('cr2', 'credit', '100'), ]), closing: joint(), }; const w = cases.closing.b.costs.rows.at(-1); Object.assign(w.facts, { method: 'closing', partialBilling: 'no', priorRevenue: '0', priorCost: '0', publicReception: 'no', }); const expected = { additional: [7050, 4750, 1520], limits: [7210, 4910, 1571.2], closing: [6530, 4230, 1353.6], }; for (const [name, { d, b }] of Object.entries(cases)) { const m = calculateNormal(d, b); assert.deepEqual( [val(m.taxableProfit), val(m.taxableBase), val(m.colecta)], expected[name], ); d.modelo_a_snapshot = saveNormalBook(d, recordNormal(d, b)); fs.writeFileSync( 'outputs/irpc-l6-costs/' + name + '.json', JSON.stringify(d), ); } }); void test('percentage-generated loss below one cent remains a negative fiscal result, never a silently truncated zero', () => { const { b } = joint(); b.base.lossesState = 'absent'; b.base.losses = []; assert.equal( calculateNormalBase(b.base, fraction(-1n, 1000n), '2026').taxableBase, null, ); assert.equal( val( calculateNormalBase( b.base, fraction(-1n, 1000n), '2026', 'QA perda corrente exacta', ).taxableBase, ), 0, ); assert.equal( val( calculateNormalBase(b.base, fraction(1n, 1000n), '2026') .taxableBase, ), 0.001, ); });