import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import { normalBook, normalCurrent, calculateNormal, recordNormal, saveNormalBook, normalHtml, normalFinancialSource, } from '../../resources/js/lib/irpc-normal.ts'; import { newNormalFormPreparation, reuseDirectFormAllocations, normalFormRepresentation, prepareNormalForms, } from '../../resources/js/lib/irpc-normal-form-preparation.ts'; import { fraction } from '../../resources/js/lib/fiscal-rational.ts'; import { createProjectDossierReport } from '../../resources/js/lib/project-dossier-report.ts'; const seed = () => JSON.parse( fs.readFileSync( new URL( './fixtures/irpc-periods-fx-preserved.json', import.meta.url, ), 'utf8', ), ), val = (f) => (f ? Number(f.numerator) / Number(f.denominator) : null); function setup() { const d = seed(), b = normalBook(d); b.forms = { ...newNormalFormPreparation(), edition: 'accepted_pdf_and_m20a1', reference: 'QA PDFM22/Q10–12 e M20A.1/Q4;edições inventariadas distintas', completenessReference: 'QA contas completas da oportunidade/sujeito no período;sem outros custos ou proveitos omitidos', allocations: [ ...reuseDirectFormAllocations(normalFinancialSource(d)), { id: 'revenue', sourceCode: 'revenue', account: '7.2', amount: '6000', reference: 'QA prestação de serviços documentada,sem vendas de bens', }, { id: 'opex', sourceCode: 'operating_expenses', account: '6.3', amount: '600', reference: 'QA fornecimentos e serviços de terceiros completos no período', }, ], incomeTax: { state: 'absent', amount: '', placement: '', reference: 'QA ausência documentada de imposto contabilizado;não inferir gasto da colecta', }, reducedRate: { state: 'absent', reference: 'QA sujeito/período sem parcela de imposto a taxa reduzida', }, }; return { d, b }; } const field = (m, code) => m.formPreparation.fields.find((f) => f.code === code); void test('exact representation keeps signedfraction,approximation,zero,missing and parentheses distinct without fiscal rounding', () => { for (const f of [ fraction(1n, 3n), fraction(-1n, 300n), fraction(123456789012345678901n, 100n), ]) { const r = normalFormRepresentation(f, 'whole'); assert.equal(r.exact, f.numerator + '/' + f.denominator + ' MZN'); assert.equal(r.represented, null); assert.equal(r.ready, false); } assert.match(normalFormRepresentation(fraction(1n, 3n)).display, /≈/); assert.equal(normalFormRepresentation(fraction(0n)).represented, '0'); assert.equal(normalFormRepresentation(null).display, 'Indeterminado'); assert.equal( normalFormRepresentation(fraction(-20n), 'whole', true).represented, '(20)', ); assert.match( normalFormRepresentation(fraction(1n, 2n)).reason, /exacto ao cêntimo/, ); }); void test('optional9 prepares M20 classifications/netresult and M22 mappedadjustments while preserving all existing totals', () => { const { d, b } = setup(), before = normalBook(d).result.model, m = calculateNormal(d, b); assert.equal(m.version, 'IRPC-normal-adjustments/9'); for (const k of [ 'accountingProfit', 'taxableProfit', 'taxableBase', 'colecta', ]) assert.deepEqual(m[k], before[k]); assert.equal(val(m.formPreparation.netResult), 3120); assert.equal(val(field(m, '200').value), 3120); assert.equal(field(m, '200').transcriptionReady, false); assert.equal(val(field(m, '213').value), 0); assert.equal(val(field(m, '206').value), 10); assert.equal(val(field(m, '238').value), 3130); assert.equal(val(field(m, '258').value), 0); assert.equal(m.formPreparation.reconciliation.matches, true); assert.equal(field(m, '259').application, 'not_applicable'); assert.equal(field(m, '260').transcriptionReady, true); assert.equal(val(field(m, '302').value), 1001.6); assert.equal(field(m, '302').representation.ready, true); assert.equal(field(m, '309–316').value, null); assert.equal(m.assessment.finalBalance.payable, null); }); void test('missingorcontradictory booktax blocks only netresult and its fieldreconciliation; knownnormalprofit/base/colecta preserved', () => { for (const tax of [ { state: '', amount: '', placement: '', reference: '' }, { state: 'absent', amount: '320', placement: 'outside', reference: 'QA', }, { state: 'known', amount: '320', placement: 'included', reference: 'QA', }, ]) { const { d, b } = setup(); b.forms.incomeTax = tax; const m = calculateNormal(d, b); assert.equal(m.formPreparation.netResult, null); assert.equal(field(m, '200').value, null); assert.equal(val(field(m, '260').value), 3130); assert.equal(val(field(m, '300').value), 1001.6); assert.equal(val(m.colecta), 1001.6); } }); void test('incomeTaxalreadyincluded reconciles account8.5 and mappedtax without subtracting it again', () => { const { d, b } = setup(); b.forms.incomeTax = { state: 'known', amount: '320', placement: 'included', reference: 'QA imposto reconhecido no plano', }; b.forms.allocations.find((a) => a.id === 'opex').amount = '280'; b.forms.allocations.push({ id: 'tax', sourceCode: 'operating_expenses', account: '8.5', amount: '320', reference: 'QA imposto já no custo de origem', }); b.schedules.rows.push({ id: 'tax', rule: 'tax_own', amount: '320', sourceCode: 'operating_expenses', document: 'QA-tax', line: '1', reference: 'QA imposto sobre lucro já incluído', fiscalYear: '', conditions: 'yes', }); Object.assign(b.profit.topics.expenseCorrections, { state: 'known', amount: '320', reference: 'QA corresponde ao imposto contabilizado já no resultado', }); const m = calculateNormal(d, b); assert.equal(val(m.formPreparation.netResult), 3120); assert.equal(val(field(m, '213').value), 0); assert.equal(val(m.taxableProfit), 3450); assert.equal(field(m, '238').value, null); assert.equal(m.formPreparation.reconciliation.matches, false); assert.ok(m.formPreparation.unmapped.some((e) => e.reason.includes('213'))); }); void test('incomplete revenueclassification does not erase directM20 fields or independentM22 calculations', () => { const { d, b } = setup(); b.forms.allocations.find((a) => a.id === 'revenue').amount = '5999'; const m = calculateNormal(d, b); assert.equal( m.formPreparation.m20.find((f) => f.account === '7.2').value, null, ); assert.equal( val(m.formPreparation.m20.find((f) => f.account === '6.5').value), 60, ); assert.equal(m.formPreparation.netResult, null); assert.equal(val(m.taxableProfit), 3130); assert.equal(val(field(m, '300').value), 1001.6); }); void test('fractional M20 allocations remain exact; no truncation or rounding changes source or net/taxable totals', () => { const { d, b } = setup(); b.forms.allocations.find((a) => a.id === 'opex').amount = '300.50'; b.forms.allocations.push({ id: 'other', sourceCode: 'operating_expenses', account: '6.8', amount: '299.50', reference: 'QA gastos operacionais restantes documentados', }); const m = calculateNormal(d, b), f = m.formPreparation.m20.find((f) => f.account === '6.3'); assert.deepEqual(f.value, fraction(601n, 2n)); assert.equal(f.representation.ready, false); assert.equal(f.representation.represented, null); assert.equal(val(m.formPreparation.netResult), 3120); assert.equal(val(m.colecta), 1001.6); }); void test('lossuses positiveM22 lossfield and M20parentheses; no loss in profitfield and no negative taxablebase', () => { const { d, b } = setup(), p = JSON.parse(d.business_plan_snapshot); p.plan.products[0].unitPrice = '20'; b.schedules.currentLossReference = 'QA perda fiscal corrente reconciliada no sujeito/período,sem reporte automático'; d.business_plan_snapshot = JSON.stringify(p); b.forms.allocations.find((a) => a.id === 'revenue').amount = '1200'; const m = calculateNormal(d, b); assert.equal(val(m.taxableProfit), -1670); assert.equal(val(field(m, '259').value), 1670); assert.equal(field(m, '260').application, 'not_applicable'); assert.equal(m.formPreparation.netRepresentation.represented, '(1680)'); assert.equal(val(m.taxableBase), 0); assert.equal(val(m.colecta), 0); }); void test('unmapped priorquota deduction keeps demonstrated effect and existing260/278/300; no guessedblankfield', () => { const { d, b } = setup(), p = JSON.parse(d.business_plan_snapshot); p.plan.assetDepreciation = '48'; d.business_plan_snapshot = JSON.stringify(p); Object.assign(b.depreciation.assets[0], { accounting: '24', start: '2025-01-01', previousAccounting: '120', previousFiscal: '80', priorAvailable: '40', }); b.forms.allocations.find((a) => a.sourceCode === 'depreciation').amount = '24'; const m = calculateNormal(d, b); assert.equal(val(m.depreciation.adjustment), -26); assert.equal(val(m.colecta), 1001.6); assert.ok( m.formPreparation.unmapped.some((e) => e.origin.startsWith('Mapa de amortizações'), ), ); assert.equal(field(m, '258').value, null); assert.equal(field(m, '238').value, null); assert.equal(val(field(m, '260').value), 3130); assert.equal( m.formPreparation.entries.find((e) => e.origin.startsWith('Mapa de amortizações'), ).code, null, ); }); void test('reducedrate unknown or knowninL7 blocks only302,notnormal300 and not finalL8 assumptions', () => { for (const state of ['', 'known']) { const { d, b } = setup(); b.forms.reducedRate.state = state; const m = calculateNormal(d, b); assert.equal(field(m, '302').value, null); assert.equal(val(field(m, '300').value), 1001.6); assert.equal(val(m.colecta), 1001.6); assert.equal(m.assessment.finalBalance.recoverable, null); } }); void test('save/recover/report preserves8history and originalplan and never claims officialrounding or crosseditiontransfer', () => { const { d, b } = setup(), original = d.business_plan_snapshot, old = structuredClone(b.result); assert.equal(normalCurrent(d, normalBook(d)), true); d.modelo_a_snapshot = saveNormalBook( d, recordNormal(d, b, '2026-10-26T10:00:00+02:00'), ); const back = normalBook(JSON.parse(JSON.stringify(d))); assert.equal(normalCurrent(d, back), true); assert.deepEqual(back.history.at(-1), old); assert.equal(d.business_plan_snapshot, original); assert.match(normalHtml(d), /campo213/); assert.match(normalHtml(d), /transporte automático M20/); assert.match(normalHtml(d), /exacto ao cêntimo/); assert.match( JSON.stringify( createProjectDossierReport(d, { selectedSections: new Set(['finance']), }), ), /campo302/, ); back.forms.allocations[0].reference += 'changed'; assert.equal(normalCurrent(d, back), false); }); void test('fivenewcases write privateAPI proof inputs and reports without rerunning accepted functionalsuites', () => { const cases = { mapped: setup(), fractional: setup(), missing: setup(), unmapped: setup(), booktax: setup(), }; cases.booktax.b.forms.incomeTax = { state: 'known', amount: '320', placement: 'outside', reference: 'QA gasto contabilístico exterior ao plano;campo próprio pendente', }; cases.fractional.b.forms.allocations.find((a) => a.id === 'opex').amount = '300.50'; cases.fractional.b.forms.allocations.push({ id: 'other', sourceCode: 'operating_expenses', account: '6.8', amount: '299.50', reference: 'QA parcela contabilística com centavos', }); cases.missing.b.forms.incomeTax = { state: '', amount: '', placement: '', reference: '', }; const u = cases.unmapped, p = JSON.parse(u.d.business_plan_snapshot); p.plan.assetDepreciation = '48'; u.d.business_plan_snapshot = JSON.stringify(p); Object.assign(u.b.depreciation.assets[0], { accounting: '24', start: '2025-01-01', previousAccounting: '120', previousFiscal: '80', priorAvailable: '40', }); u.b.forms.allocations.find((a) => a.sourceCode === 'depreciation').amount = '24'; fs.mkdirSync('outputs/irpc-l6-values-forms', { recursive: true }); for (const [name, { d, b }] of Object.entries(cases)) { d.modelo_a_snapshot = saveNormalBook( d, recordNormal(d, b, '2026-10-26T10:00:00+02:00'), ); fs.writeFileSync( 'outputs/irpc-l6-values-forms/' + name + '.json', JSON.stringify(d), ); fs.writeFileSync( 'outputs/irpc-l6-values-forms/' + name + '.html', normalHtml(d), ); } }); void test('newlyidentified includedtax mismatch blocks only dependent213/reconciliation without revising demonstratedtax', () => { const { d, b } = setup(); b.forms.incomeTax = { state: 'known', amount: '320', placement: 'included', reference: 'QA imposto reconhecido no plano', }; b.forms.allocations.find((a) => a.id === 'opex').amount = '280'; b.forms.allocations.push({ id: 'tax', sourceCode: 'operating_expenses', account: '8.5', amount: '320', reference: 'QA imposto reconhecido no custo da origem', }); const m = calculateNormal(d, b); assert.equal(val(m.colecta), 1001.6); assert.equal(val(m.formPreparation.netResult), 3120); assert.equal(val(field(m, '213').value), 0); assert.equal(field(m, '238').value, null); assert.equal(val(field(m, '260').value), 3130); assert.ok(m.formPreparation.pending.some((s) => s.includes('tax_own'))); }); void test('positive and negative asseteffects remain separate; netdepreciation is never all assignedto206', () => { const { d, b } = setup(), a = structuredClone(b.depreciation.assets[0]); Object.assign(b.depreciation.assets[0], { cost: '60', accounting: '36' }); Object.assign(a, { id: 'other', assetKey: 'other', document: 'QA-other-asset', accounting: '24', start: '2025-01-01', previousAccounting: '120', previousFiscal: '80', priorAvailable: '40', }); b.depreciation.assets.push(a); const m = calculateNormal(d, b); assert.equal(val(m.depreciation.adjustment), 4); assert.equal(val(field(m, '206').value), 30); assert.equal(val(m.taxableProfit), 3124); assert.equal(m.formPreparation.unmapped.length, 1); assert.equal(val(m.formPreparation.unmapped[0].value), -26); assert.equal(field(m, '238').value, null); }); void test('acceptedcostproof feeds independentM22 labels without applying printedpercentages again', () => { const d = JSON.parse( fs.readFileSync( new URL( './fixtures/irpc-costs-preserved.json', import.meta.url, ), 'utf8', ), ), b = normalBook(d), m = b.result.model, { b: input } = setup(); const mapped = prepareNormalForms(input.forms, m, normalFinancialSource(d)); for (const [rule, code] of [ ['allowance', '219'], ['representation', '220'], ['advertising', '221'], ['credit', '209'], ]) { const row = m.costs.rows.find((r) => r.rule === rule); assert.ok(row); const sum = m.costs.rows .filter((r) => r.rule === rule) .reduce((s, r) => s + val(r.adjustment), 0); const value = mapped.fields.find((f) => f.code === code).value; if (code !== '209') assert.equal(val(value), sum); else assert.equal( val(value), m.costs.rows .filter((r) => ['credit', 'stock', 'litigation'].includes(r.rule), ) .reduce((s, r) => s + val(r.adjustment), 0), ); } assert.deepEqual(b.result.model, m); }); void test('absence of booktax cannot erase previouslyidentified ownincomeTax cost; independentknowncolecta remains', () => { const { d, b } = setup(); b.schedules.rows.push({ id: 'tax', rule: 'tax_own', amount: '320', sourceCode: 'operating_expenses', document: 'QA-tax', line: '1', reference: 'QA imposto já incluído', fiscalYear: '', conditions: 'yes', }); Object.assign(b.profit.topics.expenseCorrections, { state: 'known', amount: '320', reference: 'QA mapa do imposto próprio', }); b.forms.incomeTax = { state: 'absent', amount: '', placement: '', reference: 'QA ausência a conferir', }; const m = calculateNormal(d, b); assert.equal(m.formPreparation.netResult, null); assert.ok( m.formPreparation.pending.some((s) => s.includes('contradiz imposto próprio'), ), ); assert.equal(val(field(m, '300').value), 1104); });