import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import { serializeBusinessPlanSnapshot } from '../../resources/js/lib/business-financial-plan.ts'; import { normalBook, newNormalBook, newNormalAdjustment, normalFinancialSource, calculateNormal, recordNormal, normalCurrent, saveNormalBook, normalReportRows, normalHtml, } from '../../resources/js/lib/irpc-normal.ts'; import { createProjectDossierReport } from '../../resources/js/lib/project-dossier-report.ts'; import { irpcBook } from '../../resources/js/lib/irpc-withholdings.ts'; const plan = () => ({ currency: 'MZN', openingCash: '0', products: [ { label: 'Serviço', monthlyUnits: '10', unitPrice: '100', unitVariableCost: '30', }, ], monthlyCollections: '800', monthlyVariableCostPayments: '250', monthlyOperatingExpenses: '100', monthlyOperatingPayments: '80', monthlyPayrollExpense: '50', monthlyPayrollPayments: '40', assetInvestment: '0', inventory: '0', receivables: '0', payables: '0', equity: '0', shareholderLoans: '0', bankLoanProceeds: '0', grants: '0', assetDepreciation: '0', depreciationLifeMonths: 12, monthlyInterestExpense: '10', monthlyInterestPayment: '5', monthlyPrincipalRepayment: '60', periods: 12, }); const draft = () => ({ ...JSON.parse( fs.readFileSync( new URL('./fixtures/irpc-mrfta-preserved.json', import.meta.url), 'utf8', ), ), fiscal_component: 'irpc', business_plan_snapshot: serializeBusinessPlanSnapshot(plan()), }); const row = (id = 'a') => ({ ...newNormalAdjustment(id), sourceCode: 'operating_expenses', document: 'QA aviso multa sintético', line: id, amount: '100', rule: 'noncontractual_fine', included: 'yes', reference: 'QA pressuposto:multa não contratual incluída no custo operacional', }); const book = () => ({ ...newNormalBook(), year: '2026', mappingReference: 'QA plano meses1–12=Jan–Dez2026', scope: 'resident_general', scopeReference: 'QA residente geral sem regime especial', rows: [ row(), { ...row('b'), sourceCode: 'interest_expense', amount: '20', rule: 'compensatory_interest', document: 'QA liquidação juros sintética', reference: 'QA pressuposto:juros compensatórios incluídos no custo financeiro', }, ], }); void test('L6 reuses reconciled profit and expense sources,without cash repayment or received withholding becoming expense', () => { const d = draft(), s = normalFinancialSource(d), m = calculateNormal(d, book()); assert.equal(s.accountingProfitBeforeTax, '6480.00'); assert.equal(m.accountingProfit.numerator, '6480'); assert.equal(m.additions.numerator, '120'); assert.equal(m.partialAdjustedResult.numerator, '6600'); assert.equal(m.taxableProfit, null); assert.equal(m.taxableBase, null); assert.equal(m.tax, null); assert.equal(m.origin, 'projected'); assert.match(m.pending.join(' '), /L6 parcial/); const p = plan(); p.monthlyPrincipalRepayment = '0'; p.monthlyInterestPayment = '0'; d.business_plan_snapshot = serializeBusinessPlanSnapshot(p); assert.deepEqual( calculateNormal(d, book()).partialAdjustedResult, m.partialAdjustedResult, ); }); void test('missing qualification affects its own adjustment;confirmed additions remain visible without a full adjusted total', () => { const b = book(); b.rows[1].reference = ''; const m = calculateNormal(draft(), b); assert.equal(m.rows[0].status, 'calculated'); assert.equal(m.rows[1].adjustment, null); assert.equal(m.additions.numerator, '100'); assert.equal(m.partialAdjustedResult, null); b.rows[1].rule = 'other'; assert.equal(calculateNormal(draft(), b).rows[1].status, 'outside_scope'); }); void test('duplicate document lines and oversubscribed source costs cannot double count', () => { const b = book(); b.rows.push({ ...b.rows[0], id: 'duplicate' }); let m = calculateNormal(draft(), b); assert.equal(m.rows[0].adjustment, null); assert.equal(m.rows[2].adjustment, null); b.rows = [ { ...row('one'), amount: '700' }, { ...row('two'), amount: '600' }, ]; m = calculateNormal(draft(), b); assert.ok(m.rows.every((r) => r.adjustment === null)); assert.match(m.pending.join(' '), /excedem/); }); void test('already excluded expenses,revenue sources,negative and overprecision amounts do not become additions', () => { for (const patch of [ { included: 'no' }, { sourceCode: 'revenue' }, { amount: '-10' }, { amount: '1.001' }, { rule: '' }, ]) { const b = book(); b.rows = [{ ...row(), ...patch }]; assert.equal(calculateNormal(draft(), b).rows[0].adjustment, null); } }); void test('year,mapping,currency,horizon and regime stay explicit;no global annual assessment inferred', () => { for (const patch of [ { year: '' }, { year: '2025' }, { mappingReference: '' }, { scope: 'other' }, { scopeReference: '' }, ]) assert.equal( calculateNormal(draft(), { ...book(), ...patch }) .partialAdjustedResult, null, ); for (const p of [ { ...plan(), currency: 'USD' }, { ...plan(), periods: 6 }, ]) { const d = draft(); d.business_plan_snapshot = serializeBusinessPlanSnapshot(p); assert.equal(calculateNormal(d, book()).partialAdjustedResult, null); } const d = draft(); d.business_plan_snapshot = ''; assert.equal(calculateNormal(d, book()).accountingProfit, null); assert.equal( calculateNormal(d, { ...book(), rows: [] }).partialAdjustedResult, null, ); }); void test('calculation save recovery report preserves installed books and invalidates history on financial or adjustment changes', () => { const d = draft(), before = JSON.parse(d.modelo_a_snapshot), b = recordNormal(d, book(), '2026-10-25T20:00:00Z'); d.modelo_a_snapshot = saveNormalBook(d, b); const recovered = JSON.parse(JSON.stringify(d)), saved = normalBook(recovered); assert.equal(normalCurrent(recovered, saved), true); assert.deepEqual( JSON.parse(d.modelo_a_snapshot).irpcWithholdings, before.irpcWithholdings, ); assert.ok(irpcBook(d)); assert.match(JSON.stringify(normalReportRows(recovered)), /6600/); assert.match( JSON.stringify( createProjectDossierReport(recovered, { selectedSections: new Set(['finance']), }), ), /6600/, ); assert.match(normalHtml(recovered), /Não é demonstração contabilística/); const p = plan(); p.monthlyOperatingExpenses = '110'; recovered.business_plan_snapshot = serializeBusinessPlanSnapshot(p); assert.equal(normalCurrent(recovered, saved), false); assert.throws(() => normalHtml(recovered)); const next = recordNormal(recovered, saved); assert.deepEqual(next.history[0], b.result); assert.equal(next.result.model.partialAdjustedResult.numerator, '6480'); fs.mkdirSync('outputs/irpc-l6', { recursive: true }); fs.writeFileSync('outputs/irpc-l6/calculated.json', JSON.stringify(d)); fs.writeFileSync('outputs/irpc-l6/preparation.html', normalHtml(d)); }); void test('opportunities remain separate and malformed persisted data is preserved with explicit error', () => { const d = draft(); d.modelo_a_snapshot = saveNormalBook(d, recordNormal(d, book())); d.context_opportunity = 'another'; assert.equal(normalBook(d).rows.length, 0); const b = book(); b.rows[0].amount = 100; assert.throws(() => saveNormalBook(d, b)); const old = d.modelo_a_snapshot; assert.throws(() => saveNormalBook(d, { ...book(), rows: [row(), row()] })); assert.equal(d.modelo_a_snapshot, old); });