import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import { newIrpcRow, emptyIrpcOrdinaryFacts, recordIrpc, saveIrpcBook, irpcBook, irpcCurrent, irpcReportRows, } from '../../resources/js/lib/irpc-withholdings.ts'; import { newMrftaInput, prepareMrfta, recordMrfta, mrftaCurrent, mrftaHtml, } from '../../resources/js/lib/irpc-mrfta.ts'; import { createProjectDossierReport } from '../../resources/js/lib/project-dossier-report.ts'; const draft = () => JSON.parse( fs.readFileSync( new URL('./fixtures/irpc-grouped-preserved.json', import.meta.url), 'utf8', ), ); const row = (id = 'principal') => ({ ...newIrpcRow(id), counterparty: 'QA contraparte', documentRef: 'QA-' + id, base: '100000', kind: 'royalty', recipientIrpc: 'yes', payerIrpc: 'yes', residence: 'nonresident', permanentEstablishment: 'no', mozambique: 'yes', qualificationRef: 'QA contrato e residência', specialRelief: 'none', eventsComplete: 'yes', events: [{ kind: 'cost', date: '2026-10-02', reference: 'QA custo' }], settlement: 'complete', settlementRef: 'QA reconciliação', allocations: [{ paymentId: 'p', amount: '3000' }], ordinary: { ...emptyIrpcOrdinaryFacts(), nonDigital: 'yes', reference: 'QA royalties não digitais', }, }); const input = () => ({ ...newMrftaInput(), period: '2026-10', declaration: 'initial', timing: 'late', nuit: '999999999', name: 'QA sujeito passivo', commercialName: 'QA', taxArea: 'QA área fiscal', address: 'QA domicílio', contact: 'qa@example.invalid', completeness: 'yes', completenessRef: 'QA conferência do período e livros', otherRubrics: 'none', otherRubricsRef: 'QA ausência documentada de IRPS/tributação autónoma/outros valores', codes: [ { rowId: 'principal', cer: '999', reference: 'QA CER fictício, apenas teste', }, ], interest: { ...newMrftaInput().interest, mode: 'assessment', period: '2026-10', amount: '500', date: '2026-11-25', reference: 'QA liquidação de juros compensatórios', }, }); function book(rows = [row()]) { return { ...recordIrpc(draft(), { version: 1, rows, payments: [ { id: 'p', kind: 'remittance', reference: 'QA entrega IRPC', date: '2026-11-15', amount: '3000', currency: 'MZN', }, ], }), mrfta: input(), }; } void test('MRFTA fills one period with principal, documented interest, payment and balance separately', () => { const b = book(), m = prepareMrfta(b, true); assert.deepEqual(m.pending, []); assert.equal(m.principal.numerator, '20000'); assert.equal(m.paid.numerator, '3000'); assert.equal(m.interest.numerator, '500'); assert.equal(m.total.numerator, '17500'); assert.equal(m.lines[0].rate, 20); assert.equal(m.lines[0].cer, '999'); assert.match(m.interestReason, /transcritos/); }); void test('unknown MAIBOR2026 never becomes zero or MIMO; independent principal remains calculated', () => { const b = book(); b.mrfta.interest.mode = ''; const m = prepareMrfta(b, true); assert.equal(m.interest, null); assert.equal(m.total, null); assert.equal(m.principal.numerator, '20000'); assert.equal(b.result.rows[0].status, 'calculated'); assert.match(m.pending.join(' '), /MAIBOR2026/); }); void test('insufficient CER or contradictory rate per rubric blocks total without losing rows', () => { const b = book(); b.mrfta.codes = []; let m = prepareMrfta(b, true); assert.equal(m.total, null); assert.equal(m.lines.length, 1); assert.match(m.pending.join(' '), /CER/); const other = structuredClone(b.result.rows[0]); other.id = 'other'; other.rate = 10; other.tax = { numerator: '10000', denominator: '1' }; b.result.rows.push(other); b.result.inputs.push({ ...row('other'), allocations: [] }); b.mrfta.codes = [ { rowId: 'principal', cer: '999', reference: 'QA ref' }, { rowId: 'other', cer: '999', reference: 'QA ref' }, ]; m = prepareMrfta(b, true); assert.equal(m.lines.length, 2); assert.match(m.pending.join(' '), /taxas ou CER distintos/); assert.equal(m.total, null); }); void test('other months,suffered withholdings and documented dispensas stay outside payable rows', () => { const b = book(); for (const [id, patch, original] of [ ['other-month', { period: '2026-11' }, {}], ['suffered', {}, { direction: 'received' }], [ 'dispensed', { treatment: 'dispensed', tax: { numerator: '0', denominator: '1' }, }, {}, ], ]) { b.result.rows.push({ ...b.result.rows[0], id, ...patch }); b.result.inputs.push({ ...row(id), ...original }); } const m = prepareMrfta(b, true); assert.equal(m.lines.length, 1); assert.equal(m.excluded.length, 3); assert.equal(m.principal.numerator, '20000'); assert.equal(m.total.numerator, '17500'); }); void test('unsupported digital and indeterminate cases retain explicit gaps without guessed rubric', () => { const b = book(); b.result.inputs[0].kind = 'digital_services'; let m = prepareMrfta(b, true); assert.equal(m.lines.length, 0); assert.equal(m.total, null); assert.match(m.pending.join(' '), /digital/); b.result.inputs[0].kind = 'royalty'; b.result.rows[0].status = 'indeterminate'; b.result.rows[0].tax = null; m = prepareMrfta(b, true); assert.equal(m.total, null); assert.match(m.pending.join(' '), /indeterminado/); }); void test('timely remittance only yields zero interest with complete proof and explicit applicable deadline', () => { const r = row(); r.base = '15000'; const b = book([r]); b.mrfta.interest = { ...b.mrfta.interest, mode: 'timely', period: '2026-10', dueDate: '2026-11-20', dueReference: 'QA prazo documentado e calendário', }; assert.equal(prepareMrfta(b, true).interest.numerator, '0'); assert.equal(prepareMrfta(b, true).total.numerator, '0'); b.mrfta.interest.dueDate = '2026-11-10'; assert.equal(prepareMrfta(b, true).interest, null); b.mrfta.interest.dueDate = '2026-11-20'; b.result.rows[0].settled = null; assert.equal(prepareMrfta(b, true).interest, null); }); void test('assessment cannot leak into another period or accept negative amount and missing reference', () => { const b = book(); for (const patch of [ { period: '2026-11' }, { amount: '-10' }, { reference: '' }, { date: '2026-02-30' }, ]) { const copy = structuredClone(b); Object.assign(copy.mrfta.interest, patch); assert.equal(prepareMrfta(copy, true).interest, null); assert.equal(prepareMrfta(copy, true).total, null); } }); void test('MRFTA save,recover,history and dossier preserve existing tax maps and freeze evidence', () => { const d = draft(), prior = irpcBook(d), original = JSON.parse(d.modelo_a_snapshot); let b = book(); b.history = [prior.result]; b = recordMrfta(b, true); d.modelo_a_snapshot = saveIrpcBook(d, b); const recovered = JSON.parse(JSON.stringify(d)), saved = irpcBook(recovered); assert.deepEqual(saved, b); assert.equal(irpcCurrent(recovered, saved), true); assert.equal(mrftaCurrent(saved, true), true); for (const [k, v] of Object.entries(original)) if (k !== 'irpcWithholdings') assert.deepEqual(JSON.parse(recovered.modelo_a_snapshot)[k], v, k); const report = irpcReportRows(recovered); assert.match(JSON.stringify(report), /17500/); assert.match( JSON.stringify( createProjectDossierReport(recovered, { selectedSections: new Set(['finance']), }), ), /MRFTA/, ); assert.match(mrftaHtml(saved, true), /Quadro7/); assert.match(mrftaHtml(saved, true), /QA CER fictício/); const before = structuredClone(saved.mrftaResult); saved.mrfta.interest.amount = '600'; assert.equal(mrftaCurrent(saved, true), false); assert.throws(() => mrftaHtml(saved, true)); const next = recordMrfta(saved, true); assert.deepEqual(next.mrftaHistory.at(-1), before); assert.equal(next.mrftaResult.model.total.numerator, '17600'); fs.mkdirSync('outputs/irpc-mrfta', { recursive: true }); fs.writeFileSync( 'outputs/irpc-mrfta/calculated.json', JSON.stringify(recovered), ); fs.writeFileSync('outputs/irpc-mrfta/preparation.html', mrftaHtml(b, true)); }); void test('malformed persisted MRFTA rejects recovery without replacing originals', () => { const b = book(); b.mrfta.codes.push({ ...b.mrfta.codes[0] }); assert.throws(() => saveIrpcBook(draft(), b)); b.mrfta.codes = []; b.mrfta.interest.amount = 5; assert.throws(() => saveIrpcBook(draft(), b)); }); void test('export escapes identities,documents and assessment references', () => { const b = book(); b.mrfta.name = ''; b.mrfta.interest.reference = ''; const html = mrftaHtml(recordMrfta(b, true), true); assert.ok(!html.includes('