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

55 lines
6.9 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import {newSalesOperation,recordSales,saveSalesBook,salesBook,operationTreatments} from '../../resources/js/lib/iva-operations.ts';
import {newAcquisition,evaluateAcquisition,recordAcquisitions,saveAcquisitionBook,acquisitionBook,acquisitionCurrent,acquisitionReportRows,readAcquisitionStore} from '../../resources/js/lib/iva-acquisitions.ts';
import {createAcquisitionRightLink,linkedOperationRight} from '../../resources/js/lib/iva-acquisition-right.ts';
const frozen=JSON.parse(fs.readFileSync(new URL('./fixtures/iva-l2a-preserved.json',import.meta.url),'utf8'));
export function rightFixture(){
const draft={...structuredClone(frozen),context_opportunity:'opportunity:368',project_title:'SYNTHETIC QA — L2-A aquisição e direito'};
const types=['general','export_goods','listed_maize','housing_rent','private_health'];
const operations={currency:'MZN',periods:[{period:'2026-12',complete:true}],rows:types.map((t,i)=>({...newSalesOperation('operation-'+i,'2026-12','MZN'),description:'SYNTHETIC QA '+t,amount:'100',basis:'net',establishment:'Mozambique',treatment:t,confirmed:operationTreatments.find(x=>x.id===t).conditions.map((_,j)=>j),evidence:'SYNTHETIC QA fonte e condições'}))};
draft.modelo_a_snapshot=saveSalesBook(draft,recordSales(draft,operations));
const rows=types.map((_,i)=>({...newAcquisition('purchase-'+i,'2026-12','MZN'),net:'100',vat:'16',gross:'116',description:'SYNTHETIC QA compra '+i,documentType:'invoice',documentRef:'SYNTHETIC QA '+i,documentDate:'2026-12-10',supplier:'SYNTHETIC QA fornecedor',supplierRegistered:'yes',legalDocument:'yes',held:'yes',domestic:'yes',allocation:'taxable_only',exclusion:'none',integrity:'yes',deductionLink:{...createAcquisitionRightLink(draft,'operation-'+i),scopeConfirmed:'yes',allocationRef:'Afectação exclusiva SYNTHETIC QA '+i,noExcludedPayments:'yes'}}));
return {draft,book:{currency:'MZN',taxpayerRegistered:'yes',rows,periods:[{period:'2026-12',complete:true,absent:['11','12','13']}]}};
}
test('right follows classified operation, including exempt with right and taxed17-A without right',()=>{
const{draft,book}=rightFixture();
const results=book.rows.map(r=>evaluateAcquisition(r,'MZN','yes',draft));
assert.deepEqual(results.map(r=>r.eligibility.status),['eligible','eligible','eligible','no_right','no_right']);
assert.deepEqual(results.map(r=>r.deductible),['16.00','16.00','16.00','0.00','0.00']);
for(const r of results){assert.ok(r.eligibility.reason);assert.match(r.eligibility.source,/Lei10\/2025/);}
assert.match(results[4].reason,/17-A/);
});
test('own documentary conditions, missing data, imports and mixed use remain distinct from operation right',()=>{
const{draft,book}=rightFixture(),row=book.rows[0];
for(const patch of [{held:''},{documentRef:''},{documentDate:'2026-11-10'},{allocation:'mixed'},{domestic:'no'},{exclusion:'special'},{sourceCurrency:'USD'},{vat:'0',gross:'100',held:''},{sourceMonth:1,sourceAmount:'999',sourceBasis:'net'}]){
const r=evaluateAcquisition({...row,...patch},'MZN','yes',draft);assert.equal(r.eligibility.status,'indeterminate',JSON.stringify(patch));assert.equal(r.deductible,null);
}
for(const patch of [{exclusion:'excluded'},{supplierRegistered:'no'},{integrity:'no'}])assert.equal(evaluateAcquisition({...row,...patch},'MZN','yes',draft).eligibility.status,'no_right');
assert.equal(evaluateAcquisition({...row,deductionLink:{...row.deductionLink,scopeConfirmed:''}},'MZN','yes',draft).deductible,null);
assert.equal(evaluateAcquisition({...row,deductionLink:{...row.deductionLink,noExcludedPayments:'no'}},'MZN','yes',draft).deductible,'0.00');
assert.equal(evaluateAcquisition(row,'MZN','yes').deductible,null);
});
test('totals F include only eligible amounts; unknown never becomes zero; no duplicated projected/effective invoice',()=>{
const{draft,book}=rightFixture();const result=recordAcquisitions(draft,book).result;
assert.equal(result.periods[0].fields['09'],'48.00');assert.equal(result.periods[0].fields['15'],'48.00');
assert.deepEqual(['14','15','16','17','18'].map(f=>result.periods[0].fields[f]),['500.00','48.00','21.00','0.00','27.00']);
const cleared=structuredClone(book);cleared.rightVersion='acquisition-operation-right/1';cleared.rows=cleared.rows.map(({deductionLink,...row})=>row);const empty=recordAcquisitions(draft,cleared).result;assert.ok(empty.rows.every(r=>r.eligibility.status==='indeterminate'));assert.equal(empty.periods[0].fields['15'],null);
const unknown=structuredClone(book);unknown.rows[0].held='';assert.equal(recordAcquisitions(draft,unknown).result.periods[0].fields['15'],null);
const duplicate=structuredClone(book);duplicate.rows.push({...duplicate.rows[0],id:'copy',origin:'effective'});assert.throws(()=>recordAcquisitions(draft,duplicate),/dupla dedução/);
});
test('source edits require explicit adoption; save recovery report and historical versions preserve old books byte for byte',()=>{
let{draft,book}=rightFixture();const before=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.deepEqual(acquisitionBook(recovered),book);assert.ok(acquisitionCurrent(recovered,book));assert.deepEqual(book.history,[first]);
const after=JSON.parse(draft.modelo_a_snapshot);for(const key of Object.keys(before)){if(key==='version')continue;for(const [id,value]of Object.entries(before[key]))assert.deepEqual(after[key][id],value,key+id);}
const report=JSON.stringify(acquisitionReportRows(recovered));for(const word of ['Elegível','Sem direito','Lei10/2025','48.00','operation-4'])assert.ok(report.includes(word),word);
const ops=salesBook(draft);ops.rows[0].description='Alteração deliberada';draft.modelo_a_snapshot=saveSalesBook(draft,recordSales(draft,ops));assert.equal(acquisitionCurrent(draft,book),false);
assert.equal(linkedOperationRight(draft,book.rows[0].deductionLink).status,'indeterminate');assert.deepEqual(book.history[0],first);assert.deepEqual(book.result,recovered?acquisitionBook(recovered).result:null);
const adopted=createAcquisitionRightLink(draft,book.rows[0].deductionLink.operationId);assert.equal(adopted.scopeConfirmed,'');assert.equal(adopted.allocationRef,'');assert.equal(linkedOperationRight(draft,adopted).status,'eligible');assert.equal(evaluateAcquisition({...book.rows[0],deductionLink:adopted},'MZN','yes',draft).deductible,null);
assert.equal(linkedOperationRight({...draft,context_opportunity:'opportunity:329'},adopted).status,'indeterminate');
const malformed=JSON.parse(draft.modelo_a_snapshot);malformed.books['opportunity:368'].rows[0].deductionLink.version=9;assert.throws(()=>readAcquisitionStore(JSON.stringify(malformed)),/Vínculo/);
if(process.env.IVA_L2A_CALCULATED)fs.writeFileSync(process.env.IVA_L2A_CALCULATED,JSON.stringify(recovered));
});