import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import { normalBook, normalCurrent, calculateNormal, recordNormal, saveNormalBook, normalHtml, } from '../../resources/js/lib/irpc-normal.ts'; import { validateSpecial } from '../../resources/js/lib/irpc-special.ts'; import { fraction } from '../../resources/js/lib/fiscal-rational.ts'; import { createProjectDossierReport } from '../../resources/js/lib/project-dossier-report.ts'; const read = (name) => JSON.parse( fs.readFileSync(new URL('./fixtures/' + name, import.meta.url), 'utf8'), ); const val = (v) => (v ? Number(v.numerator) / Number(v.denominator) : null); function accepted() { const d = read('irpc-l7-preserved.json').acceleration, f = read('irpc-l7-monthly-preserved.json'); d.business_plan_snapshot = f.plan; d.modelo_a_snapshot = saveNormalBook(d, f.book); return { d, b: normalBook(d) }; } function setup(month = 7) { const { d, b } = accepted(), p = JSON.parse(d.business_plan_snapshot); p.plan.assetDepreciation = '120'; d.business_plan_snapshot = JSON.stringify(p); const old = normalBook(read('irpc-l6-values-preserved.json')); b.periodsFx = structuredClone(old.periodsFx); b.periodsFx.from = '2026-' + String(month).padStart(2, '0') + '-01'; b.periodsFx.reference = 'QA início documentado em' + b.periodsFx.from + ';meses completos até31/12'; delete b.forms; const months = 13 - month; b.schedules.interest.amount = String(months * 20); Object.assign(b.depreciation.assets[0], { accounting: String(months * 10), start: b.periodsFx.from.slice(0, 8) + '15', timing: 'months', }); for (const part of [ b.special.expenses, b.special.cooperative, b.special.acceleration, ]) Object.assign(part.qualification, { from: b.periodsFx.from, periodReference: b.periodsFx.reference, }); b.special.acceleration.monthly.startPeriod = { version: 1, reference: 'QA início de actividade residente2026,CBF16 e períodoL6 reconciliados', }; return { d, b }; } void test('accepted11 result and currentkey remain exact without startPeriod;10 histories still readable', () => { const { d, b } = accepted(); assert.equal(normalCurrent(d, b), true); assert.deepEqual(calculateNormal(d, b), b.result.model); const old = read('irpc-l7-preserved.json').acceleration; assert.equal(normalCurrent(old, normalBook(old)), true); }); void test('resident July start reuses6 actualmonths,50 normalquota,75 accelerated capped60;only10 additional', () => { const { d, b } = setup(), asset = JSON.stringify(b.depreciation), plan = d.business_plan_snapshot, m = calculateNormal(d, b), s = m.special, a = s.acceleration.monthly.assets[0]; assert.equal(m.version, 'IRPC-normal-adjustments/12'); assert.equal(m.periodsFx.short, true); assert.equal(a.months, 6); assert.equal(val(a.normalQuota), 50); assert.equal(val(a.acceleratedQuota), 75); assert.equal(val(a.acceleratedDeduction), 60); assert.equal(val(s.acceleration.additionalDeduction), 10); assert.equal(val(m.taxableProfit), 3130); assert.equal(val(m.taxableBase), 3130); assert.equal(val(m.colecta), 1001.6); assert.equal(val(s.taxableProfit), 3120); assert.equal(val(s.taxableBase), 3120); assert.equal(val(s.colecta), 998.4); assert.equal(s.finalBalance.payable, null); assert.equal( s.acceleration.accelerated.assets.length, b.depreciation.assets.length, ); assert.equal(JSON.stringify(b.depreciation), asset); assert.equal(d.business_plan_snapshot, plan); assert.match(s.acceleration.source, /início de actividade/); assert.doesNotMatch(s.acceleration.source, /primeiro exercício anual/); }); void test('October and December use3 and1 months without annualisation and retain exact fiscalfraction', () => { for (const [month, months, diff] of [ [10, 3, fraction(5n)], [12, 1, fraction(5n, 3n)], ]) { if (typeof month !== 'number' || typeof months !== 'number') throw new Error('Invalid monthly fixture'); const { d, b } = setup(month), m = calculateNormal(d, b), a = m.special.acceleration.monthly.assets[0]; assert.equal(m.periodsFx.months, months); assert.equal(a.months, months); assert.deepEqual(a.additionalDeduction, diff); assert.deepEqual(a.normalQuota, fraction(BigInt(months) * 25n, 3n)); d.modelo_a_snapshot = saveNormalBook(d, recordNormal(d, b)); assert.match(normalHtml(d), new RegExp('duodécimosL6:' + months)); if (month === 12) assert.match(normalHtml(d), /5\/3/); } }); void test('missing start evidence blocks only L7;absent and negative remain distinct withnormal totals', () => { const missing = setup(); missing.b.special.acceleration.monthly.startPeriod.reference = ''; let m = calculateNormal(missing.d, missing.b); assert.equal(m.special.acceleration.status, 'indeterminate'); assert.equal(m.special.acceleration.additionalDeduction, null); assert.equal(val(m.colecta), 1001.6); assert.match( m.special.acceleration.reason, /Falta suporte do período curto/, ); for (const state of ['negative', 'absent']) { const { d, b } = setup(); if (state === 'negative') b.special.acceleration.rows[0].eligible = 'no'; else { b.special.acceleration.rows = []; Object.assign(b.special.acceleration.qualification, { state: 'absent', eligible: '', }); } m = calculateNormal(d, b); assert.equal(val(m.special.acceleration.additionalDeduction), 0); assert.equal( m.special.acceleration.status, state === 'absent' ? 'absent' : 'calculated', ); assert.equal(val(m.special.colecta), 1001.6); if (state === 'negative') assert.equal( m.special.acceleration.rows[0].status, 'not_applicable', ); } }); void test('cessation,otherperiodkind,nonresident,previousyearentry,priorquotas and exits remain outside newrecorte', () => { for (const mutate of [ (b) => (b.periodsFx.kind = 'same_year'), (b) => { b.periodsFx.kind = 'cessation'; b.periodsFx.from = '2026-01-01'; b.periodsFx.to = '2026-06-30'; }, (b) => (b.scope = 'ep_general'), (b) => (b.depreciation.assets[0].start = '2026-06-01'), (b) => (b.depreciation.assets[0].previousAccounting = '1'), (b) => { b.depreciation.assets[0].exitState = 'known'; b.depreciation.assets[0].exitDate = '2026-12-01'; }, ]) { const { d, b } = setup(); mutate(b); const m = calculateNormal(d, b); assert.equal(m.special.acceleration.additionalDeduction, null); assert.equal(m.special.acceleration.status, 'indeterminate'); assert.notEqual(m.taxableProfit, null); } }); void test('short fiscalperiod remains unsupported in11 unless explicitly prepared;cancel returns same11 decision', () => { const { d, b } = setup(), period = JSON.stringify(b.periodsFx), asset = JSON.stringify(b.depreciation); delete b.special.acceleration.monthly.startPeriod; const m = calculateNormal(d, b); assert.equal(m.version, 'IRPC-normal-adjustments/11'); assert.equal(m.special.acceleration.additionalDeduction, null); assert.match(m.special.acceleration.reason, /exercício anual civil2026/); assert.equal(JSON.stringify(b.periodsFx), period); assert.equal(JSON.stringify(b.depreciation), asset); }); void test('save-recover12 keeps11result verbatim,originalassets,otherbooks and fractional report;recalc is idempotent', () => { const { d, b } = setup(), old = structuredClone(b.result), history = structuredClone(b.history), asset = JSON.stringify(b.depreciation), plan = d.business_plan_snapshot, others = JSON.parse(d.modelo_a_snapshot); d.modelo_a_snapshot = saveNormalBook(d, recordNormal(d, b)); let back = normalBook(JSON.parse(JSON.stringify(d))); assert.equal(normalCurrent(d, back), true); assert.deepEqual(back.history, [...history, old]); assert.deepEqual(back.history.at(-1), old); assert.equal( back.history.at(-1).model.version, 'IRPC-normal-adjustments/11', ); assert.equal(JSON.stringify(back.depreciation), asset); assert.equal(d.business_plan_snapshot, plan); assert.deepEqual( JSON.parse(d.modelo_a_snapshot).irpcWithholdings, others.irpcWithholdings, ); const html = normalHtml(d); assert.match(html, /2026-07-01–2026-12-31/); assert.match(html, /dedução adicional conciliada:10.00/); assert.match(html, /colecta sobre a base:998.40/); assert.match(html, /Saldo final a pagar\/recuperar:Indeterminado/); assert.match( JSON.stringify( createProjectDossierReport(d, { selectedSections: new Set(['finance']), }), ), /início de actividade/, ); d.modelo_a_snapshot = saveNormalBook(d, recordNormal(d, back)); back = normalBook(d); assert.equal( val(back.result.model.special.acceleration.additionalDeduction), 10, ); assert.equal(val(back.result.model.special.colecta), 998.4); back.periodsFx.from = '2026-10-01'; assert.equal(normalCurrent(d, back), false); }); void test('invalid startPeriod child cannot replace originalbooks', () => { for (const child of [ null, { version: 2, reference: 'QA' }, { version: 1, reference: 3 }, ]) { const { b } = setup(); b.special.acceleration.monthly.startPeriod = child; assert.throws(() => validateSpecial(b.special), /não recuperável/); } }); void test('prepare only new guardedAPI cases and reports', () => { const cases = { start: setup(), fraction: setup(12), missing: setup(), negative: setup(), absent: setup(), }; cases.missing.b.special.acceleration.monthly.startPeriod.reference = ''; cases.negative.b.special.acceleration.rows[0].eligible = 'no'; cases.absent.b.special.acceleration.rows = []; Object.assign(cases.absent.b.special.acceleration.qualification, { state: 'absent', eligible: '', }); fs.mkdirSync('outputs/irpc-l7-start-period', { recursive: true }); for (const [name, { d, b }] of Object.entries(cases)) { d.modelo_a_snapshot = saveNormalBook( d, recordNormal(d, b, '2026-10-26T17:00:00+02:00'), ); fs.writeFileSync( 'outputs/irpc-l7-start-period/' + name + '.json', JSON.stringify(d), ); fs.writeFileSync( 'outputs/irpc-l7-start-period/' + name + '.html', normalHtml(d), ); } });