Files
Investidor-V2/tests/frontend/irpc-normal-assessment.test.mjs
T
2026-10-25 21:58:59 +02:00

17 lines
6.0 KiB
JavaScript

import test from'node:test';import assert from'node:assert/strict';import fs from'node:fs';
import{normalBook,normalCurrent,calculateNormal,recordNormal,saveNormalBook,normalHtml,normalReportRows}from'../../resources/js/lib/irpc-normal.ts';
import{newNormalAssessmentInput,calculateNormalAssessment}from'../../resources/js/lib/irpc-normal-assessment.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-base-preserved.json',import.meta.url),'utf8'));
const input=()=>({...newNormalAssessmentInput(),treatment:'general_61_1',reference:'QA sujeito residente,actividade ordinária,sem taxa reduzida/benefício ou tributação autónoma neste recorte'});
const context=()=>({year:'2026',scope:'resident_general',currency:'MZN'});
test('current base4400 times statutory general32percent yields1408 colecta with explicit2026 period and L8 unknown balance',()=>{const d=seed(),b=normalBook(d);b.assessment=input();const m=calculateNormal(d,b);assert.equal(m.taxableProfit.numerator,'6700');assert.equal(m.taxableBase.numerator,'4400');assert.equal(m.colecta.numerator,'1408');assert.equal(m.tax,null);assert.equal(m.assessment.rate.numerator,'32');assert.deepEqual(m.assessment.period,{year:'2026',from:'2026-01-01',to:'2026-12-31'});assert.match(m.assessment.classification,/artigo61\(1\)/);assert.equal(m.assessment.finalBalance.payable,null);assert.equal(m.assessment.finalBalance.recoverable,null);assert.equal(m.assessment.finalBalance.scope,'L8');});
test('missing classification and special cases do not silently inherit32percent;known upstream base remains',()=>{for(const patch of[{treatment:''},{treatment:'other'},{reference:''}]){const m=calculateNormalAssessment({...input(),...patch},fraction(4400n),context());assert.equal(m.colecta,null);assert.equal(m.taxableBase.numerator,'4400');assert.ok(m.pending.length>0);if(patch.treatment==='other')assert.equal(m.rate,null);}});
test('rate is scoped by period,residence regime and currency,not carried across years or contexts',()=>{for(const patch of[{year:'2025'},{year:'2027'},{scope:'other'},{currency:'USD'}]){const m=calculateNormalAssessment(input(),fraction(4400n),{...context(),...patch});assert.equal(m.colecta,null);if(patch.year)assert.equal(m.rate,null);}});
test('unknown base is not replaced with6700profit;known zero stays zero and fractional cents remain exact',()=>{assert.equal(calculateNormalAssessment(input(),null,context()).colecta,null);assert.equal(calculateNormalAssessment(input(),fraction(0n),context()).colecta.numerator,'0');const m=calculateNormalAssessment(input(),fraction(1n,100n),context());assert.deepEqual(m.colecta,{numerator:'2',denominator:'625'});assert.equal(m.centExact,false);assert.equal(calculateNormalAssessment(input(),fraction(-1n),context()).colecta,null);});
test('an incomplete base calculation only blocks colecta;profit and valid source amounts remain visible',()=>{const d=seed(),b=normalBook(d);b.assessment=input();b.base.benefits={state:'',amount:'',reference:''};const m=calculateNormal(d,b);assert.equal(m.taxableProfit.numerator,'6700');assert.equal(m.taxableBase,null);assert.equal(m.colecta,null);assert.equal(m.assessment.rate.numerator,'32');});
test('retentions,payments and annual credits are not subtracted or resolved by this L6 scope',()=>{const d=seed(),b=normalBook(d);b.assessment=input();const m=calculateNormal(d,b);d.annual_withholdings='999999';d.annual_payments='999999';d.annual_credits='999999';const s=JSON.parse(d.modelo_a_snapshot);for(const w of Object.values(s.irpcWithholdings??{})){w.payments=[];if(w.mrfta)w.mrfta.codes=[];}d.modelo_a_snapshot=JSON.stringify(s);const changed=calculateNormal(d,b);assert.deepEqual(changed.colecta,m.colecta);assert.deepEqual(changed.assessment.finalBalance,m.assessment.finalBalance);assert.equal(changed.tax,null);});
test('legacy3 remains current without recalculation;version4 roundtrip/history/report/dossier preserve prior books and stale guards',()=>{const d=seed(),old=normalBook(d);assert.equal(normalCurrent(d,old),true);assert.equal(old.result.model.version,'IRPC-normal-adjustments/3');const originalStore=JSON.parse(d.modelo_a_snapshot);const b=recordNormal(d,{...old,assessment:input()},'2026-10-25T23:30:00Z');assert.deepEqual(b.history.at(-1),old.result);assert.equal(b.result.model.version,'IRPC-normal-adjustments/4');d.modelo_a_snapshot=saveNormalBook(d,b);assert.deepEqual(JSON.parse(d.modelo_a_snapshot).irpcWithholdings,originalStore.irpcWithholdings);const back=JSON.parse(JSON.stringify(d));assert.equal(normalCurrent(back,normalBook(back)),true);const html=normalHtml(back);assert.match(html,/colecta simulada:1408/);assert.match(html,/saldo final a pagar ou recuperar:Indeterminado \(L8\)/);assert.match(html,/taxa:32%/);assert.match(JSON.stringify(createProjectDossierReport(back,{selectedSections:new Set(['finance'])})),/1408/);assert.match(JSON.stringify(normalReportRows(back)),/Saldo final — âmbito L8/);const changed=normalBook(back);changed.assessment.reference='';back.modelo_a_snapshot=saveNormalBook(back,changed);assert.equal(normalCurrent(back,normalBook(back)),false);assert.throws(()=>normalHtml(back));const again=recordNormal(back,normalBook(back));assert.equal(again.result.model.colecta,null);assert.deepEqual(again.history.at(-1),b.result);
fs.mkdirSync('outputs/irpc-l6-colecta',{recursive:true});fs.writeFileSync('outputs/irpc-l6-colecta/calculated.json',JSON.stringify(d));fs.writeFileSync('outputs/irpc-l6-colecta/preparation.html',normalHtml(d));const md=structuredClone(d),missing=normalBook(md);missing.assessment.treatment='';md.modelo_a_snapshot=saveNormalBook(md,recordNormal(md,missing));fs.writeFileSync('outputs/irpc-l6-colecta/missing.json',JSON.stringify(md));});
test('unsupported input versions and malformed classification fail without replacing originals',()=>{const d=seed(),b=normalBook(d);b.assessment=input();b.assessment.version=2;assert.throws(()=>saveNormalBook(d,b));b.assessment.version=1;b.assessment.treatment='general20';assert.throws(()=>saveNormalBook(d,b));});