19 lines
7.8 KiB
JavaScript
19 lines
7.8 KiB
JavaScript
import test from 'node:test';import assert from 'node:assert/strict';import fs from 'node:fs';
|
|
import {normalBook,normalFinancialSource,recordNormal,calculateNormal,normalCurrent,saveNormalBook,normalHtml,normalReportRows}from'../../resources/js/lib/irpc-normal.ts';
|
|
import {newNormalProfitInput,profitTopics}from'../../resources/js/lib/irpc-normal-profit.ts';
|
|
import {restoreBusinessPlanSnapshot,serializeBusinessPlanSnapshot}from'../../resources/js/lib/business-financial-plan.ts';
|
|
import {createProjectDossierReport}from'../../resources/js/lib/project-dossier-report.ts';
|
|
import {irpcBook,saveIrpcBook}from'../../resources/js/lib/irpc-withholdings.ts';
|
|
const seed=()=>JSON.parse(fs.readFileSync(new URL('./fixtures/irpc-normal-preserved.json',import.meta.url),'utf8'));
|
|
function example(){const d=seed(),b=normalBook(d),profit=newNormalProfitInput();b.rows[1].amount='120';b.rows[1].reference='QA todos120MZN de juros reconhecidos são compensatórios documentados;nenhuma taxa L5 calculada';profit.completenessReference='QA plano cobre sujeito residente/exercício2026 completo,sem outros estabelecimentos,receitas,custos ou operações omitidos';profit.lines=normalFinancialSource(d).accountingLines.map(l=>({code:l.code,state:l.code==='depreciation'?'absent':'known',reference:'QA qualificação ordinária/exercício/reconciliação de '+l.origin+';restante custo indispensável sem outras exclusões'}));for(const[k]of profitTopics)profit.topics[k]={state:'absent',amount:'',reference:'QA ausência verificada de '+k};profit.topics.incomeCorrections={state:'known',amount:'0',reference:'QA revisão de correcções ao rendimento:valor conhecido zero'};b.profit=profit;return{d,b};}
|
|
test('documented financial result plus supported addbacks reaches scoped simulated taxable profit without tax/base',()=>{const{d,b}=example(),m=calculateNormal(d,b);assert.equal(m.accountingProfit.numerator,'6480');assert.equal(m.additions.numerator,'220');assert.equal(m.taxableProfit.numerator,'6700');assert.equal(m.taxableBase,null);assert.equal(m.tax,null);assert.equal(m.profit.pending.length,0);assert.equal(m.profit.lines.find(l=>l.code==='interest_expense').remainder.numerator,'0');assert.equal(m.profit.lines.find(l=>l.code==='operating_expenses').remainder.numerator,'1100');});
|
|
test('known zero,declared absence,and missing information keep distinct saved evidence and effects',()=>{const{d,b}=example();b.profit.topics.incomeCorrections={state:'known',amount:'0',reference:'QA total revisto de correcções ao rendimento é zero'};let m=calculateNormal(d,b);assert.equal(m.taxableProfit.numerator,'6700');assert.equal(m.profit.topics.find(t=>t.key==='incomeCorrections').state,'known');assert.equal(m.profit.topics.find(t=>t.key==='positivePatrimonial').state,'absent');b.profit.topics.negativePatrimonial={state:'',amount:'',reference:''};m=calculateNormal(d,b);assert.equal(m.taxableProfit,null);assert.equal(m.additions.numerator,'220');assert.equal(m.profit.topics.find(t=>t.key==='negativePatrimonial').value,null);assert.match(m.profit.pending.join(' '),/sem zero presumido/);});
|
|
test('absence cannot override known nonzero financial values or silently discard an entered amount',()=>{const{d,b}=example();b.profit.lines.find(l=>l.code==='operating_expenses').state='absent';assert.equal(calculateNormal(d,b).taxableProfit,null);b.profit.lines.find(l=>l.code==='operating_expenses').state='known';b.profit.topics.positivePatrimonial.amount='100';assert.equal(calculateNormal(d,b).taxableProfit,null);});
|
|
test('known unimplemented variation,depreciation and remaining financial interest stay visible without presumed treatment',()=>{for(const patch of ['variation','depreciation','interest']){const{d,b}=example();if(patch==='variation')b.profit.topics.positivePatrimonial={state:'known',amount:'500',reference:'QA variação500,regra específica por corresponder'};if(patch==='depreciation'){const p=restoreBusinessPlanSnapshot(d.business_plan_snapshot);p.assetDepreciation='120';d.business_plan_snapshot=serializeBusinessPlanSnapshot(p);b.profit.lines.find(l=>l.code==='depreciation').state='known';}if(patch==='interest')b.rows[1].amount='20';const m=calculateNormal(d,b);assert.equal(m.taxableProfit,null);assert.ok(m.profit.pending.length>0);if(patch==='variation')assert.equal(m.profit.topics[0].value.numerator,'500');}});
|
|
test('complete line classifications and subject-year completeness are required,not a bare project subtotal',()=>{for(const patch of ['line','reference','completeness','scope','year']){const{d,b}=example();if(patch==='line')b.profit.lines.pop();if(patch==='reference')b.profit.lines[0].reference='';if(patch==='completeness')b.profit.completenessReference='';if(patch==='scope')b.scope='other';if(patch==='year')b.year='2025';assert.equal(calculateNormal(d,b).taxableProfit,null);}});
|
|
test('declared no addbacks can close only after full documented review,and fiscal losses are not clamped to zero',()=>{const{d,b}=example();let p=restoreBusinessPlanSnapshot(d.business_plan_snapshot);p.monthlyInterestExpense='0';d.business_plan_snapshot=serializeBusinessPlanSnapshot(p);b.rows=[];b.profit.lines.find(l=>l.code==='interest_expense').state='absent';assert.equal(calculateNormal(d,b).taxableProfit.numerator,'6600');p.products[0].unitPrice='1';d.business_plan_snapshot=serializeBusinessPlanSnapshot(p);assert.equal(calculateNormal(d,b).taxableProfit.numerator,'-5280');});
|
|
test('L5 uncertainty does not block independent known expense classification or annual profit',()=>{const{d,b}=example(),s=JSON.parse(d.modelo_a_snapshot);for(const v of Object.values(s.irpcWithholdings??{})){if(v.mrfta){v.mrfta.codes=[];v.mrfta.interest.mode='';v.mrfta.interest.calculation=undefined;}}d.modelo_a_snapshot=JSON.stringify(s);assert.equal(calculateNormal(d,b).taxableProfit.numerator,'6700');});
|
|
test('old result remains current and unchanged;new closure history and stale-input guards survive roundtrip and dossier',()=>{const legacy=seed(),old=structuredClone(normalBook(legacy));assert.equal(normalCurrent(legacy,old),true);assert.equal(old.result.model.version,'IRPC-normal-adjustments/1');const{d,b}=example();const saved=recordNormal(d,b,'2026-10-25T22:00:00Z');assert.deepEqual(saved.history.at(-1),old.result);const before=JSON.parse(d.modelo_a_snapshot);d.modelo_a_snapshot=saveNormalBook(d,saved);assert.deepEqual(JSON.parse(d.modelo_a_snapshot).irpcWithholdings,before.irpcWithholdings);const back=JSON.parse(JSON.stringify(d));assert.equal(normalCurrent(back,normalBook(back)),true);assert.match(normalHtml(back),/lucro tributável simulado:6700/);assert.match(JSON.stringify(createProjectDossierReport(back,{selectedSections:new Set(['finance'])})),/6700/);assert.match(JSON.stringify(normalReportRows(back)),/Ausência declarada/);const l5=irpcBook(back);back.modelo_a_snapshot=saveIrpcBook(back,l5);assert.deepEqual(normalBook(back),saved);normalBook(back).profit.lines[0].reference='';const changed=normalBook(back);changed.profit.lines[0].reference='';back.modelo_a_snapshot=saveNormalBook(back,changed);assert.equal(normalCurrent(back,normalBook(back)),false);assert.throws(()=>normalHtml(back));
|
|
fs.mkdirSync('outputs/irpc-l6-profit',{recursive:true});fs.writeFileSync('outputs/irpc-l6-profit/calculated.json',JSON.stringify(d));fs.writeFileSync('outputs/irpc-l6-profit/preparation.html',normalHtml(d));const missing=normalBook(d);missing.profit.topics.negativePatrimonial={state:'',amount:'',reference:''};const md=structuredClone(d);md.modelo_a_snapshot=saveNormalBook(md,recordNormal(md,missing));fs.writeFileSync('outputs/irpc-l6-profit/missing.json',JSON.stringify(md));});
|
|
test('malformed added evidence and unsupported stored versions fail without resetting the book',()=>{const{d,b}=example();b.profit.topics.positivePatrimonial.amount=100;assert.throws(()=>saveNormalBook(d,b));b.profit.topics.positivePatrimonial.amount='';b.profit.lines.push({...b.profit.lines[0]});assert.throws(()=>saveNormalBook(d,b));});
|