Files
Investidor-V2/tests/frontend/irpc-special-months.test.mjs
T
2026-10-26 14:24:28 +02:00

31 lines
10 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} from '../../resources/js/lib/irpc-normal.ts';
import {newSpecialInput,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 frozen=()=>JSON.parse(fs.readFileSync(new URL('./fixtures/irpc-l7-preserved.json',import.meta.url),'utf8'));
const val=v=>v?Number(v.numerator)/Number(v.denominator):null;
const q=state=>({state,eligible:state==='known'?'yes':'',from:'2026-01-01',to:'2026-12-31',reference:'QA condições/ausência documentadas no sujeito/projecto',periodReference:'QA ano civil2026,CBF16 e Decreto72/2013art9'});
function setup(account='60',start='2026-07-15'){
const d=frozen().acceleration,b=normalBook(d),p=JSON.parse(d.business_plan_snapshot);p.plan.assetDepreciation=account;d.business_plan_snapshot=JSON.stringify(p);
Object.assign(b.depreciation.assets[0],{accounting:account,start,timing:'months'});
b.special.acceleration.monthly={version:1,reference:'QA opção de duodécimos desde a entrada emuso,datas/meses do mesmo activoL6'};return{d,b};
}
test('accepted10 books remain current and reproduce exact original expense,cooperative and annualasset models;history is read unchanged',()=>{for(const[name,d]of Object.entries(frozen())){const b=normalBook(d),original=structuredClone(b.result);assert.equal(normalCurrent(d,b),true,name);assert.deepEqual(calculateNormal(d,b),original.model,name);assert.deepEqual(normalBook(d).result,original,name);}});
test('explicit monthly11 reuses6 L6months and limits75quota to60accounted,adding only10deduction',()=>{const{d,b}=setup(),original=JSON.stringify(b.depreciation),m=calculateNormal(d,b),s=m.special,a=s.acceleration.monthly.assets[0];assert.equal(m.version,'IRPC-normal-adjustments/11');assert.equal(a.months,6);assert.equal(val(a.normalRate),10);assert.equal(val(a.acceleratedRate),15);assert.equal(val(a.normalQuota),50);assert.equal(val(a.acceleratedQuota),75);assert.equal(val(a.normalDeduction),50);assert.equal(val(a.acceleratedDeduction),60);assert.equal(val(a.additionalDeduction),10);assert.equal(val(m.taxableProfit),6310);assert.equal(val(m.colecta),2019.2);assert.equal(val(s.taxableProfit),6300);assert.equal(val(s.taxableBase),6300);assert.equal(val(s.colecta),2016);assert.equal(s.finalBalance.payable,null);assert.equal(JSON.stringify(b.depreciation),original);});
test('month timing comes from L6 including midmonth entry,January,October and December;never dailyproration',()=>{for(const[start,account,months,normal,accelerated]of[['2026-01-01','120',12,100,150],['2026-07-31','60',6,50,75],['2026-10-20','60',3,25,37.5],['2026-12-31','20',1,100/12,12.5]]){const{d,b}=setup(account,start),m=calculateNormal(d,b),a=m.special.acceleration.monthly.assets[0];assert.equal(a.months,months);assert.equal(val(a.normalQuota),normal);assert.equal(val(a.acceleratedQuota),accelerated);assert.equal(a.months,m.depreciation.assets[0].months);}});
test('December retains exact fractional difference and report never rounds fiscal amount',()=>{const{d,b}=setup('20','2026-12-15'),m=calculateNormal(d,b),a=m.special.acceleration.monthly.assets[0];assert.deepEqual(a.normalQuota,fraction(25n,3n));assert.deepEqual(a.acceleratedQuota,fraction(25n,2n));assert.deepEqual(a.additionalDeduction,fraction(25n,6n));d.modelo_a_snapshot=saveNormalBook(d,recordNormal(d,b));assert.match(normalHtml(d),/25\/6/);assert.match(normalHtml(d),/≈/);});
test('accounted caps of0,40,60,100 remain binding and no additional quota is invented',()=>{for(const[account,normal,accelerated,diff]of[['0',0,0,0],['40',40,40,0],['60',50,60,10],['100',50,75,25]]){const{d,b}=setup(account),s=calculateNormal(d,b).special,a=s.acceleration.monthly.assets[0];assert.equal(val(a.normalDeduction),normal);assert.equal(val(a.acceleratedDeduction),accelerated);assert.equal(val(s.acceleration.additionalDeduction),diff);}});
test('new project building reuses land/residual basis and existing2percent table rate with monthlycap',()=>{const{d,b}=setup('50');Object.assign(b.depreciation.assets[0],{category:'commercial_building',cost:'5000',land:'1000',rate:'2'});b.special.acceleration.rows[0].kind='new_project_building';const a=calculateNormal(d,b).special.acceleration.monthly.assets[0];assert.equal(val(a.taxBasis),4000);assert.equal(val(a.normalQuota),40);assert.equal(val(a.acceleratedQuota),60);assert.equal(val(a.acceleratedDeduction),50);assert.equal(val(a.additionalDeduction),10);});
test('other existing assets stay on their normal quota;selected asset is neither copied nor deducted twice',()=>{const{d,b}=setup('80'),other=structuredClone(b.depreciation.assets[0]);b.depreciation.assets[0].accounting='60';Object.assign(other,{id:'other',assetKey:'other',document:'QA-other',description:'Mobiliário foraCBF16',category:'furniture',cost:'200',rate:'10',start:'2026-01-01',timing:'annual',accounting:'20'});b.depreciation.assets.push(other);const s=calculateNormal(d,b).special;assert.equal(s.acceleration.normal.assets.length,2);assert.equal(s.acceleration.accelerated.assets.length,2);assert.deepEqual(s.acceleration.normal.assets[1],s.acceleration.accelerated.assets[1]);assert.equal(val(s.acceleration.additionalDeduction),10);});
test('missingmonthlysupport blocks only CBF16 totals;normalreference and storedinput are retained',()=>{const{d,b}=setup();b.special.acceleration.monthly.reference='';const m=calculateNormal(d,b);assert.equal(m.special.acceleration.status,'indeterminate');assert.equal(m.special.acceleration.additionalDeduction,null);assert.equal(val(m.colecta),2019.2);assert.equal(m.special.acceleration.monthly.assets[0].acceleratedQuota,null);assert.match(m.special.acceleration.reason,/Falta suporte/);});
test('negative condition,declaredabsence and missingqualification have distinct monthly outcomes',()=>{const negative=setup();negative.b.special.acceleration.rows[0].eligible='no';let s=calculateNormal(negative.d,negative.b).special;assert.equal(s.acceleration.rows[0].status,'not_applicable');assert.equal(val(s.acceleration.additionalDeduction),0);assert.equal(s.acceleration.monthly.assets[0].applied,false);assert.equal(s.acceleration.monthly.assets[0].acceleratedRate,null);for(const state of['absent','']){const{d,b}=setup();b.special.acceleration.rows=[];b.special.acceleration.qualification=q(state);b.special.acceleration.monthly.reference='';s=calculateNormal(d,b).special;assert.equal(s.acceleration.status,state==='absent'?'absent':'indeterminate');assert.equal(val(s.acceleration.additionalDeduction),state==='absent'?0:null);}});
test('newmonthlyscope excludes priorquotas,annualoption,exits and earlier/laterentry while retaining L6 reference',()=>{for(const mutate of[a=>a.previousAccounting='1',a=>a.timing='annual',a=>{a.exitState='known';a.exitDate='2026-12-01';},a=>a.start='2025-07-01',a=>a.start='2027-01-01']){const{d,b}=setup();mutate(b.depreciation.assets[0]);const m=calculateNormal(d,b);assert.equal(m.special.acceleration.additionalDeduction,null);assert.notEqual(m.taxableProfit,null);assert.equal(m.special.acceleration.status,'indeterminate');}});
test('short fiscalperiod already supported in L6 is not silently included in this annual CBF16 increment',()=>{const d=JSON.parse(fs.readFileSync(new URL('./fixtures/irpc-l6-values-preserved.json',import.meta.url),'utf8')),b=normalBook(d),model=setup().b.special;b.special=structuredClone(model);for(const part of[b.special.expenses,b.special.cooperative,b.special.acceleration])part.qualification={...part.qualification,from:'2026-07-01'};Object.assign(b.depreciation.assets[0],{category:'machine',cost:'1000',rate:'10',accounting:'60',timing:'months',start:'2026-07-01'});const m=calculateNormal(d,b);assert.notEqual(m.colecta,null);assert.equal(m.special.acceleration.additionalDeduction,null);assert.match(m.special.acceleration.reason,/exercício anual civil2026/);});
test('monthlyexplicitoption never selects CBF from location;missingeligibility evidence remains pending',()=>{const{d,b}=setup();d.province_id='QA-ZEE-candidate';b.special.acceleration.recognitionReference='';const m=calculateNormal(d,b);assert.equal(m.special.acceleration.additionalDeduction,null);assert.notEqual(m.colecta,null);assert.match(m.special.acceleration.reason,/reconhecimento10/);});
test('version11 save-recover preserves10history,originalasset,plan and otherbooks;recalculate stays idempotent',()=>{const{d,b}=setup(),old=structuredClone(b.result),asset=JSON.stringify(b.depreciation),plan=d.business_plan_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.at(-1),old);assert.equal(back.history.at(-1).model.version,'IRPC-normal-adjustments/10');assert.equal(JSON.stringify(back.depreciation),asset);assert.equal(d.business_plan_snapshot,plan);assert.ok(JSON.parse(d.modelo_a_snapshot).irpcWithholdings);const html=normalHtml(d);assert.match(html,/duodécimosL6:6/);assert.match(html,/diferença adicional:10.00/);assert.match(html,/Saldo final a pagar\/recuperar:Indeterminado/);assert.match(JSON.stringify(createProjectDossierReport(d,{selectedSections:new Set(['finance'])})),/CBF16 por duodécimos/);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),2016);back.depreciation.assets[0].start='2026-10-01';assert.equal(normalCurrent(d,back),false);});
test('malformed optionalmonthly schema cannot overwrite oldbooks',()=>{for(const value of[null,{version:2,reference:'QA'},{version:1,reference:12}]){const{b}=setup();b.special.acceleration.monthly=value;assert.throws(()=>validateSpecial(b.special),/não recuperável/);}});
test('newmonthlycases only prepare guardedprivateAPI inputs and reports',()=>{const cases={monthly:setup(),fraction:setup('20','2026-12-15'),missing:setup(),negative:setup(),absent:setup()};cases.missing.b.special.acceleration.monthly.reference='';cases.negative.b.special.acceleration.rows[0].eligible='no';cases.absent.b.special.acceleration.qualification=q('absent');cases.absent.b.special.acceleration.rows=[];fs.mkdirSync('outputs/irpc-l7-months',{recursive:true});for(const[name,{d,b}]of Object.entries(cases)){d.modelo_a_snapshot=saveNormalBook(d,recordNormal(d,b,'2026-10-26T16:00:00+02:00'));fs.writeFileSync('outputs/irpc-l7-months/'+name+'.json',JSON.stringify(d));fs.writeFileSync('outputs/irpc-l7-months/'+name+'.html',normalHtml(d));}});