Files
Investidor-V2/tests/frontend/iva-export-services.test.mjs
T

37 lines
5.9 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import { exportServiceTreatments } from '../../resources/js/lib/iva-export-services.ts';
import { newSalesOperation,operationTreatments,evaluateSalesOperation,referenceExportOperation,recordSales,saveSalesBook,salesBook,salesCurrent } from '../../resources/js/lib/iva-operations.ts';
import { createProjectDossierReport } from '../../resources/js/lib/project-dossier-report.ts';
const frozen=JSON.parse(fs.readFileSync(new URL('./fixtures/iva-g4b-inputs.json',import.meta.url),'utf8'));
const row=(rule,i=0)=>({...newSalesOperation(`g4c-${i}`,'2026-12','MZN'),description:`SYNTHETIC QA — ${rule.label}`,establishment:'SYNTHETIC QA estabelecimento',amount:'100',basis:i%2?'included':'net',treatment:rule.id,confirmed:rule.conditions.map((_,n)=>n),evidence:'SYNTHETIC QA — documento/pressuposto externo identifica bens,regime/localização,mandante e ligação do serviço;operação do intermediário localizada emMoçambique;sem certificação'});
function fixture(){
const draft={...structuredClone(frozen),context_opportunity:'opportunity:325',project_title:'SYNTHETIC QA — G4C / Serviços ligados'};
const goods={...row(operationTreatments.find(r=>r.id==='export_goods'),9),id:'g4c-goods',description:'SYNTHETIC QA — mercadorias expedidas pelo vendedor',amount:'1000'};
const services=exportServiceTreatments.map(row);services[1]=referenceExportOperation(services[1],goods);services[1].confirmed=exportServiceTreatments[1].conditions.map((_,i)=>i);
const book=recordSales(draft,{currency:'MZN',rows:[goods,...services],periods:[{period:'2026-12',complete:true}]});draft.modelo_a_snapshot=saveSalesBook(draft,book);return{draft,book};
}
test('three services require each legal condition,local service scope and evidence;no automatic classification from client or sector',()=>{
assert.equal(exportServiceTreatments.length,3);assert.match(exportServiceTreatments[0].conditions[2],/admissão temporária/);assert.match(exportServiceTreatments[0].conditions[3],/artigo9/);assert.match(exportServiceTreatments[1].conditions[2],/artigo9/);assert.match(exportServiceTreatments[2].conditions[1],/não actuação em nome próprio/);
for(const [i,rule]of exportServiceTreatments.entries()){const input=row(rule,i);assert.equal(evaluateSalesOperation(input,'MZN').field,'05');assert.equal(evaluateSalesOperation(input,'MZN').vat,'0.00');for(const missing of input.confirmed)assert.equal(evaluateSalesOperation({...input,confirmed:input.confirmed.filter(n=>n!==missing)},'MZN').vat,null);for(const patch of [{amount:''},{evidence:''},{sourceCurrency:'USD'},{period:'2027-01'},{basis:''}])assert.equal(evaluateSalesOperation({...input,...patch},'MZN').vat,null);}
});
test('reference reuse keeps service price/identity/evidence and requires deliberate confirmation of new relationship',()=>{
const service=row(exportServiceTreatments[1]),related={...row(operationTreatments.find(r=>r.id==='export_goods'),8),amount:'1000'},linked=referenceExportOperation(service,related);
assert.deepEqual(linked.relatedExport,{id:related.id,description:related.description,treatment:related.treatment});assert.deepEqual(linked.confirmed,[]);
for(const field of ['id','amount','basis','period','description','evidence','establishment','sourceAmount','sourcePeriod','sourceCurrency','source'])assert.deepEqual(linked[field],service[field],field);
assert.equal(linked.amount,'100');assert.equal(evaluateSalesOperation(linked,'MZN').vat,null);assert.throws(()=>referenceExportOperation(service,service));assert.throws(()=>referenceExportOperation(related,service));
});
test('deleted/changed linked operation blocks current calculation,keeps history and requires explicit reference update',()=>{
const{draft,book}=fixture();assert.equal(book.result.periods[0].fields['05'],'1300.00');
for(const rows of [book.rows.filter(r=>r.id!=='g4c-goods'),book.rows.map(r=>r.id==='g4c-goods'?{...r,description:'Changed goods'}:r)]){const changed=recordSales(draft,{...book,rows});assert.equal(changed.result.rows.find(r=>r.id==='g4c-1').vat,null);assert.match(changed.result.rows.find(r=>r.id==='g4c-1').reason,/removida ou alterada/);assert.equal(changed.result.periods[0].fields['05'],null);assert.deepEqual(changed.history.at(-1),book.result);assert.equal(changed.rows.find(r=>r.id==='g4c-1').amount,'100');}
const changedGoods={...book.rows[0],description:'Reviewed goods'};const linked=referenceExportOperation(book.rows[2],changedGoods);linked.confirmed=exportServiceTreatments[1].conditions.map((_,i)=>i);const next=recordSales(draft,{...book,rows:[changedGoods,book.rows[1],linked,book.rows[3]]});assert.equal(next.result.periods[0].fields['05'],'1300.00');
const invalid=recordSales(draft,{...book,rows:book.rows.map(r=>r.id==='g4c-1'?{...r,relatedExport:{id:r.id,description:r.description,treatment:r.treatment}}:r)});assert.equal(invalid.result.periods[0].fields['05'],null);
});
test('stored service reference survives report/recovery;all16 prior books remain unchanged/current without recalculation',()=>{
const{draft,book}=fixture();assert.ok(salesCurrent(draft,book));assert.deepEqual(salesBook(JSON.parse(JSON.stringify(draft))),JSON.parse(JSON.stringify(book)));
const before=JSON.parse(frozen.modelo_a_snapshot).salesOperations,after=JSON.parse(draft.modelo_a_snapshot).salesOperations;assert.equal(Object.keys(before).length,16);for(const[context,original]of Object.entries(before)){assert.deepEqual(after[context],original);assert.ok(salesCurrent({...draft,context_opportunity:context},after[context]));}
const report=JSON.stringify(createProjectDossierReport(draft,{selectedSections:new Set(['finance']),version:'QA G4C'}));for(const token of ['opportunity:325','Manica','2026-12','1300.00','13(1)(m)','13(1)(n)','13(1)(s)','g4c-goods','Operação relacionada'])assert.ok(report.includes(token),token);
if(process.env.IVA_G4C_CALCULATED)fs.writeFileSync(process.env.IVA_G4C_CALCULATED,JSON.stringify(draft));
});