Files
Investidor-V2/tests/frontend/irpc-special-start-period.test.mjs
T

38 lines
8.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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};
}
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);});
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/);});
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)]]){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/);}});
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');}});
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);}});
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);});
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);});
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/);}});
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));}});