Files
Investidor-V2/tests/frontend/iva-acquisition-allocation.test.mjs
T

67 lines
11 KiB
JavaScript

import test from 'node:test';import assert from 'node:assert/strict';import fs from 'node:fs';
import {recordSales,saveSalesBook} from '../../resources/js/lib/iva-operations.ts';
import {recordAcquisitions,evaluateAcquisition,saveAcquisitionBook,acquisitionBook,acquisitionCurrent,acquisitionReportRows,readAcquisitionStore} from '../../resources/js/lib/iva-acquisitions.ts';
import {saveImportExemptionBook} from '../../resources/js/lib/iva-import-exemptions.ts';
import {newAcquisitionAllocation,acquisitionAllocationDecision,adoptAllocationReduced} from '../../resources/js/lib/iva-acquisition-allocation.ts';
import {newReducedBase,reducedBaseConditions,ownReducedConditions,recordReducedBases,saveReducedBaseBook,reducedBaseBook} from '../../resources/js/lib/iva-reduced-bases.ts';
const frozen=JSON.parse(fs.readFileSync(new URL('./fixtures/iva-l2d-preserved.json',import.meta.url),'utf8'));
export function allocationFixture(){
const draft={...structuredClone(frozen),context_opportunity:'opportunity:370',project_title:'SYNTHETIC QA — L2-D afectação e proporções'};const store=JSON.parse(frozen.modelo_a_snapshot);
draft.modelo_a_snapshot=saveSalesBook(draft,recordSales(draft,structuredClone(store.salesOperations['opportunity:369'])));
draft.modelo_a_snapshot=saveImportExemptionBook(draft,structuredClone(store.importExemptions['opportunity:369']));
const base=store.books['opportunity:369'];
const d=(method,patch={})=>({...newAcquisitionAllocation('2026-12'),method,numerator:'1',denominator:'3',unit:'m2 utilizados',scopeRef:'SYNTHETIC QA entidade/actividades/ano2026',source:'SYNTHETIC QA contabilidade/critério',communicationRef:'SYNTHETIC QA comunicação prévia22(2)',noExcludedPayments:'yes',basis:'prior_year',facts:{scopeConfirmed:'yes',rightBasis:'yes',actualScope:'yes',communication:'yes',conditionsMet:'yes',annualScope:'yes',netExclusions:'yes'},...patch});
const row=(id,method='actual',patch={},dpatch={})=>({...structuredClone(base.rows[3]),id,description:'SYNTHETIC QA '+id,documentRef:'SYNTHETIC QA '+id,deductionLink:undefined,allocation:'mixed',d:d(method,dpatch),...patch});
const imported={...structuredClone(base.rows[0]),id:'imported',deductionLink:undefined,allocation:'mixed',d:d('pro_rata',{numerator:'2',denominator:'3'})};
const book={currency:'MZN',rightVersion:'acquisition-operation-right/1',taxpayerRegistered:'yes',rows:[row('actual'),row('pro-rata','pro_rata'),row('diesel','actual',{bc:{...base.rows[1].bc}}),imported],periods:structuredClone(base.periods)};
return{draft,book,row,d};
}
test('actual allocation uses exact fraction and prior communication,without inventing approval or bypassing C',()=>{
const{draft,row}=allocationFixture();const r=row('actual');let value=evaluateAcquisition(r,'MZN','yes',draft);assert.deepEqual(value.deductibleExact,{numerator:'16',denominator:'3'});assert.deepEqual(value.nonDeductibleExact,{numerator:'32',denominator:'3'});assert.equal(value.eligibility.status,'eligible');
for(const patch of [{numerator:''},{denominator:'0'},{denominator:'0.1'},{unit:''},{communicationRef:''},{scopeRef:''},{source:''},{facts:{...r.d.facts,communication:''}},{facts:{...r.d.facts,conditionsMet:''}},{facts:{...r.d.facts,rightBasis:''}}])assert.equal(evaluateAcquisition({...r,d:{...r.d,...patch}},'MZN','yes',draft).deductible,null,JSON.stringify(patch));
for(const patch of [{held:''},{documentRef:''},{legalDocument:''},{documentDate:'2026-11-10'}])assert.equal(evaluateAcquisition({...r,...patch},'MZN','yes',draft).deductible,null);
assert.equal(evaluateAcquisition({...r,bc:{...r.bc,expense:'travel'}},'MZN','yes',draft).deductible,'0.00');
assert.equal(evaluateAcquisition({...r,bc:{...r.bc,reducedSupply:'yes'}},'MZN','yes',draft).deductible,'0.00');
});
test('pro rata uses annual prior year or declared initial estimate;ceil hundredth is preserved separately from money',()=>{
const{draft,row}=allocationFixture();const r=row('pro','pro_rata');const calc=patch=>evaluateAcquisition({...r,d:{...r.d,...patch}},'MZN','yes',draft);
const v=calc({});assert.deepEqual(v.allocationCalculation.rawRatio,{numerator:'1',denominator:'3'});assert.deepEqual(v.allocationCalculation.ratio,{numerator:'17',denominator:'50'});assert.equal(v.deductible,'5.44');assert.match(v.reason,/34%/);
assert.equal(calc({numerator:'0'}).deductible,'0.00');assert.equal(calc({numerator:'3'}).deductible,'16.00');
assert.equal(calc({numerator:'1',denominator:'2'}).deductible,'8.00');
for(const patch of [{referenceYear:'2026'},{actualRequired:'yes'},{basis:''},{facts:{...r.d.facts,annualScope:''}},{facts:{...r.d.facts,netExclusions:''}}])assert.equal(calc(patch).deductible,null,JSON.stringify(patch));
const initial={basis:'initial_changed',referenceYear:'2026',declarationRef:'SYNTHETIC QA início/alteração',facts:{...r.d.facts,initialChanged:'yes'}};assert.equal(calc(initial).deductible,'5.44');assert.equal(calc({...initial,declarationRef:''}).deductible,null);
});
test('F composes C diesel only once with D and import11,keeps exact fractions and unknown dependencies',()=>{
const{draft,book}=allocationFixture();const result=recordAcquisitions(draft,book).result;assert.deepEqual(result.rows[2].allocationCalculation.before,{numerator:'8',denominator:'1'});assert.deepEqual(result.rows[2].deductibleExact,{numerator:'8',denominator:'3'});
assert.deepEqual(['09','11','14','15','16','17','18'].map(k=>result.periods[0].fields[k]),['13.44','10.72','1000.00','24.16','160.00','135.84','0.00']);
const incomplete=structuredClone(book);incomplete.rows[0].d.denominator='0';assert.equal(recordAcquisitions(draft,incomplete).result.periods[0].fields['15'],null);
const coverage=structuredClone(book);coverage.periods[0].importsComplete=false;assert.equal(recordAcquisitions(draft,coverage).result.periods[0].fields['17'],null);
const duplicate=structuredClone(book);duplicate.rows.push({...book.rows[0],id:'duplicate'});assert.throws(()=>recordAcquisitions(draft,duplicate),/repetido/);
const saved=recordAcquisitions(draft,book);draft.modelo_a_snapshot=saveAcquisitionBook(draft,saved);addReduced(draft);assert.equal(acquisitionCurrent(draft,saved),false);const outputGap=recordAcquisitions(draft,saved).result;assert.equal(outputGap.periods[0].fields['15'],'24.16');assert.equal(outputGap.periods[0].fields['16'],null);
});
function addReduced(draft){const rows=['regulated_energy','aeronautical_fees','public_works','public_water'].map(t=>({...newReducedBase(t,'MZN'),treatment:t,description:'SYNTHETIC QA '+t,period:'2026-12',operationDate:'2026-12-10',documentDate:'2026-12-10',documentRef:'SYNTHETIC QA '+t,priceRef:'SYNTHETIC QA preço/taxa',priceFrom:'2026-01-01',priceScope:'SYNTHETIC QA âmbito',consideration:'100',projectRef:'SYNTHETIC QA contrato público',workCategory:'roads',compositionRef:'SYNTHETIC QA composição semIVA',evidence:'SYNTHETIC QA condições',conditions:Object.fromEntries([...reducedBaseConditions,...ownReducedConditions(t)].map(([key])=>[key,'yes']))}));draft.modelo_a_snapshot=saveReducedBaseBook(draft,recordReducedBases(draft,{rows}));}
test('18(6) reuses all four demonstrated reduced bases;no double base reduction or invented output tax',()=>{
const{draft,row}=allocationFixture();addReduced(draft);
const expected={regulated_energy:'9.92',aeronautical_fees:'13.60',public_works:'6.40',public_water:'12.00'};
for(const[id,value]of Object.entries(expected)){
const r=row('purchase-'+id,'reduced',{allocation:'taxable_only'});r.d=adoptAllocationReduced(draft,r.d,id);r.d.facts.scopeConfirmed='yes';const result=evaluateAcquisition(r,'MZN','yes',draft);assert.equal(result.deductible,value,id);assert.equal(r.net,'100');assert.equal(r.vat,'16');
const book=recordAcquisitions(draft,{currency:'MZN',taxpayerRegistered:'yes',rows:[r],periods:[{period:'2026-12',complete:true,importsComplete:true,absent:['12','13']}]});assert.equal(book.result.periods[0].fields['15'],value);for(const f of ['14','16','17','18'])assert.equal(book.result.periods[0].fields[f],null,f);
const saved={...draft,modelo_a_snapshot:saveAcquisitionBook(draft,book)};assert.ok(acquisitionCurrent(saved,book));const removed={...saved,modelo_a_snapshot:saveReducedBaseBook(saved,{rows:reducedBaseBook(saved).rows})};assert.equal(acquisitionCurrent(removed,book),false);
assert.equal(evaluateAcquisition({...r,allocation:'mixed'},'MZN','yes',draft).deductible,null);
const changed=reducedBaseBook(draft);changed.rows.find(x=>x.id===id).description+=' alteração';const other={...draft,modelo_a_snapshot:saveReducedBaseBook(draft,recordReducedBases(draft,changed))};assert.equal(evaluateAcquisition(r,'MZN','yes',other).deductible,null);
}
});
test('save recovery/report preserves every prior ABC/L1 book and frozen D histories;no automatic recalculation',()=>{
let{draft,book}=allocationFixture();const prior=JSON.parse(frozen.modelo_a_snapshot);book=recordAcquisitions(draft,book);draft.modelo_a_snapshot=saveAcquisitionBook(draft,book);const first=structuredClone(book.result);book=recordAcquisitions(draft,book);draft.modelo_a_snapshot=saveAcquisitionBook(draft,book);const recovered=JSON.parse(JSON.stringify(draft));assert.ok(acquisitionCurrent(recovered,acquisitionBook(recovered)));assert.deepEqual(book.history,[first]);
const current=JSON.parse(draft.modelo_a_snapshot);for(const k of Object.keys(prior).filter(k=>k!=='version'))for(const[id,v]of Object.entries(prior[k]))assert.deepEqual(current[k][id],v,k+id);
for(const scope of ['opportunity:368','opportunity:369'])assert.ok(acquisitionCurrent({...recovered,context_opportunity:scope},prior.books[scope]));
const report=JSON.stringify(acquisitionReportRows(recovered));for(const token of ['AfectaçãoD','16/3','1/3','34%','24.16','135.84','22(2)'])assert.ok(report.includes(token),token);
book.rows[0].d.numerator='2';draft.modelo_a_snapshot=saveAcquisitionBook(draft,book);assert.equal(acquisitionCurrent(draft,book),false);assert.deepEqual(book.history,[first]);assert.match(JSON.stringify(acquisitionReportRows(draft)),/Histórico desactualizado/);
const broken=JSON.parse(recovered.modelo_a_snapshot);broken.books['opportunity:370'].rows[0].d.facts.scopeConfirmed='maybe';assert.throws(()=>readAcquisitionStore(JSON.stringify(broken)),/AfectaçãoD/);
if(process.env.IVA_L2D_CALCULATED){
const special={...recovered,context_opportunity:'opportunity:371'};special.modelo_a_snapshot=saveSalesBook(special,recordSales(special,structuredClone(current.salesOperations['opportunity:370'])));addReduced(special);
const reduced=allocationFixture().row('reduced-public-works','reduced',{allocation:'taxable_only'});reduced.d=adoptAllocationReduced(special,reduced.d,'public_works');reduced.d.facts.scopeConfirmed='yes';const reducedBook=recordAcquisitions(special,{currency:'MZN',taxpayerRegistered:'yes',rows:[reduced],periods:[{period:'2026-12',complete:true,importsComplete:true,absent:['12','13']}]});special.modelo_a_snapshot=saveAcquisitionBook(special,reducedBook);
fs.writeFileSync(process.env.IVA_L2D_CALCULATED,JSON.stringify({...special,context_opportunity:'opportunity:370'}));
}
});