import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import {newIrpcRow,emptyIrpcOrdinaryFacts,recordIrpc,saveIrpcBook,irpcBook,irpcCurrent,irpcReportRows,irpcPreparationHtml} from '../../resources/js/lib/irpc-withholdings.ts'; import {newIrpcReliefPreparation} from '../../resources/js/lib/irpc-relief-preparation.ts'; import {createProjectDossierReport,createProjectDossierPrintHtml} 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='dispensa')=>({...newIrpcRow(id),counterparty:'QA sociedade',documentRef:'QA-'+id,base:'100000',kind:'services',recipientIrpc:'yes',payerIrpc:'yes',residence:'resident',mozambique:'yes',qualificationRef:'QA contrato e cadastro do titular',specialRelief:'claimed',eventsComplete:'yes',events:[{kind:'cost',date:'2026-10-02',reference:'QA custo'}],ordinary:{...emptyIrpcOrdinaryFacts(),nonDigital:'yes',reference:'QA contrato de serviços'},reliefPreparation:{...newIrpcReliefPreparation(),rule:'cirpc68d',instrument:'CIRPC Lei34/2007',article:'68(d)',validFrom:'2026-01-01',validTo:'2026-12-31',conditions:'QA titular e rendimento sujeitos a IRPC',evidence:'QA cadastro e contrato existentes',facts:{subjectToIrpc:'yes'}}}); const treaty=(id='convention')=>({...row(id),kind:'royalty',residence:'nonresident',permanentEstablishment:'no',reliefPreparation:{...row().reliefPreparation,kind:'treaty',rule:'ptMz12',instrument:'CDT PT-MZ RAR36/1992 e Protocolo2009',article:'12',country:'PT',residenceDocument:'QA certificado fiscal Portugal 2026',facts:{treatyResident:'yes',beneficialOwner:'yes',payerResidentMz:'yes',armLength:'yes',royaltyDefinition:'yes'}}}); const calc=(r)=>recordIrpc(draft(),{version:1,rows:[r],payments:[]}).result.rows[0]; test('68d dispenses resident and attributable EP services/equipment without exempting the income',()=>{ for(const kind of ['services','equipment'])for(const patch of [{},{residence:'nonresident',permanentEstablishment:'yes',attributable:'yes'}]){ const v=calc({...row(),kind,...patch});assert.equal(v.status,'calculated');assert.equal(v.treatment,'dispensed');assert.equal(v.nature,'on_account');assert.equal(v.tax.numerator,'0');assert.equal(v.outstanding.numerator,'0');assert.match(v.reason,/não é isenção/); } }); test('68f requires own-property management and absence of fiscal transparency',()=>{ const r=row();r.kind='rent';r.ordinary.rentOwnPropertyManagement='yes';r.reliefPreparation.rule='cirpc68f';r.reliefPreparation.article='68(f)';r.reliefPreparation.facts.notTransparent='yes';assert.equal(calc(r).treatment,'dispensed'); for(const value of ['', 'no']){r.reliefPreparation.facts.notTransparent=value;assert.equal(calc(r).tax,null);}r.reliefPreparation.facts.notTransparent='yes';r.ordinary.rentOwnPropertyManagement='no';assert.equal(calc(r).tax,null); }); test('definitive nonresident equipment is never dispensed under68d',()=>{ for(const patch of [{permanentEstablishment:'no'},{permanentEstablishment:'yes',attributable:'no'},{permanentEstablishment:''}])assert.equal(calc({...row(),kind:'equipment',residence:'nonresident',...patch}).tax,null); }); test('Portugal royalties and qualified equipment use10% gross definitive limit',()=>{ for(const kind of ['royalty','equipment']){const v=calc({...treaty(),kind});assert.equal(v.status,'calculated');assert.equal(v.treatment,'treaty');assert.equal(v.rate,10);assert.equal(v.tax.numerator,'10000');assert.equal(v.nature,'definitive');} }); test('missing,negative,expired or contradictory evidence remains indeterminate,never zero',()=>{ const samples=[r=>r.reliefPreparation.evidence='',r=>r.reliefPreparation.residenceDocument='',r=>r.reliefPreparation.validTo='2026-09-30',r=>r.reliefPreparation.validFrom='2026-02-30',r=>r.reliefPreparation.article='7',r=>r.reliefPreparation.country='ZA',r=>r.permanentEstablishment='yes',r=>r.kind='digital_services']; for(const fact of ['treatyResident','beneficialOwner','payerResidentMz','armLength','royaltyDefinition'])for(const value of ['', 'no'])samples.push(r=>r.reliefPreparation.facts[fact]=value); for(const mutate of samples){const r=treaty();mutate(r);const v=calc(r);assert.equal(v.status,'indeterminate');assert.equal(v.tax,null);assert.equal(v.rate,null);} }); test('independent rows calculate beside insufficient information; save recover report export and history',()=>{ const d=draft(),prior=irpcBook(d),insufficient=treaty('missing');insufficient.reliefPreparation.residenceDocument=''; const rent=row('property-dispensation');rent.kind='rent';rent.ordinary.rentOwnPropertyManagement='yes';rent.reliefPreparation.rule='cirpc68f';rent.reliefPreparation.article='68(f)';rent.reliefPreparation.facts.notTransparent='yes'; const b=recordIrpc(d,{...prior,rows:[...prior.rows,row(),rent,treaty(),insufficient]});d.modelo_a_snapshot=saveIrpcBook(d,b); const recovered=JSON.parse(JSON.stringify(d)),book=irpcBook(recovered);assert.deepEqual(book,b);assert.equal(irpcCurrent(recovered,book),true);assert.deepEqual(book.history.at(-1),prior.result); assert.deepEqual(book.result.rows.slice(-3).map(r=>r.status),['calculated','calculated','indeterminate']); const report=irpcReportRows(recovered).slice(-3);assert.match(report[0][2],/Dispensa de retenção/);assert.match(report[1][2],/Limite convencional/);assert.match(report[2][2],/Indeterminado/);assert.match(report[1][3],/QA certificado fiscal Portugal 2026/); assert.match(irpcPreparationHtml(recovered),/10000/);book.rows.at(-2).reliefPreparation.facts.beneficialOwner='';assert.equal(irpcCurrent(recovered,book),false);assert.equal(recordIrpc(recovered,book).result.rows.at(-2).tax,null); fs.mkdirSync('outputs/irpc-reliefs',{recursive:true});fs.writeFileSync('outputs/irpc-reliefs/calculated.json',JSON.stringify(recovered));fs.writeFileSync('outputs/irpc-reliefs/preparation.html',irpcPreparationHtml(recovered)); }); test('dispensa with payment attached requires reconciliation and retains the original proof',()=>{ const r=row();r.allocations=[{paymentId:'p',amount:'100'}];const p={id:'p',kind:'remittance',reference:'QA prova',date:'2026-10-20',amount:'100',currency:'MZN'};const b=recordIrpc(draft(),{version:1,rows:[r],payments:[p]});assert.equal(b.result.rows[0].status,'indeterminate');assert.deepEqual(b.payments,[p]); }); test('unsupported relief and malformed persisted conditions are not accepted as a benefit',()=>{ const r=row();r.reliefPreparation.rule='';assert.equal(calc(r).tax,null);r.reliefPreparation.facts.subjectToIrpc='no';assert.equal(calc(r).tax,null);r.reliefPreparation.facts.subjectToIrpc=42;assert.throws(()=>saveIrpcBook(draft(),{version:1,rows:[r],payments:[]})); }); test('financial dossier includes each IRPC result, evidence and insufficient information',async()=>{ const d=draft(),missing=treaty('missing');missing.reliefPreparation.residenceDocument='';d.modelo_a_snapshot=saveIrpcBook(d,recordIrpc(d,{version:1,rows:[row(),treaty(),missing],payments:[]})); const report=createProjectDossierReport(d,{selectedSections:new Set(['finance'])}),text=JSON.stringify(report); for(const label of ['Dispensa de retenção','Limite convencional','Indeterminado','QA certificado fiscal Portugal 2026'])assert.ok(text.includes(label),label); const html=await createProjectDossierPrintHtml(report);assert.match(html,/Dispensa de retenção/);assert.match(html,/prova de residência/); });