43 lines
8.9 KiB
JavaScript
43 lines
8.9 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import {newExclusionBase,newExclusionPart,switchExclusionPart,exclusionTreatments,exclusionConditions,partConditions,evaluateExclusion,recordExclusions,saveExclusionBook,exclusionBook,exclusionCurrent,exclusionReportRows} from '../../resources/js/lib/iva-exclusion-base.ts';
|
|
import {compositionCurrent} from '../../resources/js/lib/iva-composition-base.ts';
|
|
import {fuelBaseCurrent} from '../../resources/js/lib/iva-fuel-base.ts';
|
|
import {createProjectDossierReport} from '../../resources/js/lib/project-dossier-report.ts';
|
|
const frozen=JSON.parse(fs.readFileSync(new URL('./fixtures/iva-g7g-preserved-inputs.json',import.meta.url),'utf8'));
|
|
const part=(treatment='judicial_indemnity',id=treatment)=>({...newExclusionPart(id),treatment,description:'SYNTHETIC QA '+treatment,amount:({judicial_indemnity:'100.10',granted_discount:'50.25',third_party_payment:'30.15',returnable_packaging:'20.05'})[treatment],included:'yes',reference:'SYNTHETIC QA referência '+id+' / declaração judicial e contrato quando aplicável',scope:'SYNTHETIC QA operação A,montante e condições deste tratamento',accountRef:'SYNTHETIC QA contas de terceiros A',beneficiaryRef:'SYNTHETIC QA adquirente B',documentExcerpt:'SYNTHETIC QA factura A,embalagens separadas,devolução acordada expressamente',conditions:Object.fromEntries(partConditions(treatment).map(([key])=>[key,'yes']))});
|
|
const row=()=>({...newExclusionBase('g7g-operation-A','MZN'),description:'SYNTHETIC QA exclusões operação A',period:'2026-12',operationRef:'SYNTHETIC QA operação A',initialAmount:'1000.25',initialRef:'SYNTHETIC QA montante sem próprioIVA,inclusões15(5)consideradas',documentRef:'SYNTHETIC QA G7G documento A',documentDate:'2026-12-12',exclusionRef:'SYNTHETIC QA quatro exclusões reconciliadas,sem duplicação',evidence:'SYNTHETIC QA pressupostos expressos,não documento real',conditions:Object.fromEntries(exclusionConditions.map(([key])=>[key,'yes'])),parts:exclusionTreatments.map(t=>part(t.id))});
|
|
const draft=()=>({...structuredClone(frozen),context_opportunity:'opportunity:345',project_title:'SYNTHETIC QA — G7G / Exclusões da contraprestação'});
|
|
|
|
test('P102 four treatments each own statutoryconditions and references;unknown/no never become automatic exclusion',()=>{
|
|
const input=row(),ok=evaluateExclusion(input,'MZN');assert.equal(ok.baseAmount,'799.70');assert.equal(ok.vat,null);assert.equal(ok.rate,null);
|
|
for(const[key]of exclusionConditions)for(const value of ['','unknown','no']){const r=evaluateExclusion({...input,conditions:{...input.conditions,[key]:value}},'MZN');assert.equal(r.status,value==='no'?'not_applicable':'indeterminate');assert.equal(r.baseAmount,null);}
|
|
for(const t of exclusionTreatments){const p=part(t.id);assert.equal(evaluateExclusion({...input,parts:[p]},'MZN').status,'determined');for(const[key]of partConditions(t.id))for(const value of ['','unknown','no']){const r=evaluateExclusion({...input,parts:[{...p,conditions:{...p.conditions,[key]:value}}]},'MZN');assert.equal(r.status,value==='no'?'not_applicable':'indeterminate');assert.equal(r.baseAmount,null);assert.equal(r.vat,null);}
|
|
for(const patch of [{reference:''},{scope:''},{description:''},{included:''},{amount:''}])assert.equal(evaluateExclusion({...input,parts:[{...p,...patch}]},'MZN').baseAmount,null);
|
|
}
|
|
for(const patch of [{description:''},{operationRef:''},{initialRef:''},{documentRef:''},{documentDate:'2026-02-30'},{exclusionRef:''},{evidence:''},{period:'2025-12'},{currency:'USD'}])assert.equal(evaluateExclusion({...input,...patch},'MZN').baseAmount,null);
|
|
for(const patch of [{accountRef:''},{beneficiaryRef:''}])assert.equal(evaluateExclusion({...input,parts:[{...part('third_party_payment'),...patch}]},'MZN').baseAmount,null);assert.equal(evaluateExclusion({...input,parts:[{...part('returnable_packaging'),documentExcerpt:''}]},'MZN').baseAmount,null);
|
|
});
|
|
|
|
test('subtract included exclusions only,alreadyexcluded retained;exactdecimal/zero/excess;switch retains unrelated references and clearsconditions',()=>{
|
|
const input=row();assert.equal(evaluateExclusion({...input,parts:input.parts.map(p=>({...p,included:'no'}))},'MZN').baseAmount,'1000.25');assert.equal(evaluateExclusion({...input,parts:input.parts.map(p=>({...p,included:p.treatment==='judicial_indemnity'?'no':'yes'}))},'MZN').baseAmount,'899.80');
|
|
assert.equal(evaluateExclusion({...input,initialAmount:'0',parts:[{...part(),amount:'0'}]},'MZN').baseAmount,'0.00');assert.equal(evaluateExclusion({...input,initialAmount:'1.000002',parts:[{...part(),amount:'0.000001'}]},'MZN').baseAmount,'1.000001');assert.equal(evaluateExclusion({...input,initialAmount:'999999999999999.999999',parts:[{...part(),amount:'0.000001'}]},'MZN').baseAmount,'999999999999999.999998');
|
|
for(const amount of ['-1','NaN','0.0000001','','1000.26'])assert.equal(evaluateExclusion({...input,parts:[{...part(),amount}]},'MZN').baseAmount,null);assert.equal(evaluateExclusion({...input,initialAmount:''},'MZN').baseAmount,null);
|
|
const switched=switchExclusionPart(part('third_party_payment'),'returnable_packaging');assert.deepEqual(switched.conditions,{});for(const field of ['amount','reference','scope','accountRef','beneficiaryRef','documentExcerpt','included'])assert.equal(switched[field],part('third_party_payment')[field]);assert.equal(evaluateExclusion({...input,parts:[switched]},'MZN').baseAmount,null);assert.equal(evaluateExclusion({...input,parts:[]},'MZN').baseAmount,null);assert.equal(evaluateExclusion({...input,parts:[part(),part()]},'MZN').baseAmount,null);
|
|
});
|
|
|
|
test('save/recovery/history/report keep one base/four exclusions and all84 previous books/currentkeys unchanged',()=>{
|
|
const d=draft(),prior=JSON.parse(d.modelo_a_snapshot);assert.deepEqual(exclusionBook(d),{rows:[]});assert.deepEqual(exclusionReportRows(d),[]);
|
|
let book=recordExclusions(d,{rows:[{...row(),conditions:{}}]});book=recordExclusions(d,{...book,rows:[{...row(),parts:[{...part('granted_discount'),conditions:{granted:'no'}}]}]});book=recordExclusions(d,{...book,rows:[row()]});assert.equal(book.history.length,2);assert.ok(book.history.every(h=>h.rows.every(r=>r.baseAmount===null&&r.vat===null)));assert.ok(exclusionCurrent(d,book));assert.equal(book.result.rows.length,1);assert.equal(book.result.inputs[0].parts.length,4);
|
|
for(const field of ['amount','included','reference','treatment','scope','accountRef','beneficiaryRef','documentExcerpt'])assert.ok(!exclusionCurrent(d,{...book,rows:book.rows.map(r=>({...r,parts:r.parts.map(p=>({...p,[field]:'changed'}))}))}));for(const patch of [{context_opportunity:'opportunity:346'},{currency:'USD'},{selection_labels:{province_id:'Maputo'}}])assert.ok(!exclusionCurrent({...d,...patch},book));
|
|
d.modelo_a_snapshot=saveExclusionBook(d,book);assert.deepEqual(exclusionBook(JSON.parse(JSON.stringify(d))),book);const next=JSON.parse(d.modelo_a_snapshot);const{considerationExclusions,...untouched}=next;assert.deepEqual(untouched,prior);for(const[scope,previous]of Object.entries(prior.considerationCompositions))assert.ok(compositionCurrent({...d,context_opportunity:scope},previous));for(const[scope,previous]of Object.entries(prior.regulatedFuelBases))assert.ok(fuelBaseCurrent({...d,context_opportunity:scope},previous));
|
|
assert.equal(next.considerationCompositions['opportunity:344'].result.rows[0].baseAmount,'1150.60');assert.equal(next.noncashBases['opportunity:343'].result.rows[1].baseAmount,'2700.75');assert.equal(next.regulatedFuelBases['opportunity:342'].result.rows[0].baseAmount,'1400.15');assert.equal(next.importExemptions['opportunity:338'].result.rows[1].importVat,'400.00');assert.equal(next.books['opportunity:329'].result.periods[0].fields['11'],null);
|
|
const report=JSON.stringify(createProjectDossierReport(d,{selectedSections:new Set(['finance']),version:'QA G7G'}));for(const token of ['Base P102','799.70','IVA: Indeterminado','opportunity:345','Manica','2026-12','operação A','15(6)','100.10','27(5)(b)','contas de terceiros A','devolução acordada','campo11'])assert.ok(report.includes(token),token);assert.equal(exclusionReportRows(d).length,1);assert.deepEqual(exclusionReportRows({...d,context_opportunity:'opportunity:346'}),[]);
|
|
if(process.env.IVA_G7G_CALCULATED)fs.writeFileSync(process.env.IVA_G7G_CALCULATED,JSON.stringify(d));
|
|
});
|
|
|
|
test('corrupt nested exclusions/store cannot overwrite originals;duplicate operation ids cannot be recorded',()=>{
|
|
const d=draft(),original=d.modelo_a_snapshot;for(const patch of [{initialAmount:4},{conditions:[]},{parts:{}},{parts:[{...part(),amount:4}]},{parts:[{...part(),conditions:[]}]},{parts:[null]}])assert.throws(()=>saveExclusionBook(d,{rows:[{...row(),...patch}]}),/originais preservados/);assert.equal(d.modelo_a_snapshot,original);assert.throws(()=>recordExclusions(d,{rows:[row(),row()]}),/distintos/);assert.throws(()=>recordExclusions(d,{rows:[]}),/distintos/);assert.throws(()=>exclusionBook({...d,modelo_a_snapshot:JSON.stringify({...JSON.parse(original),considerationExclusions:[]})}),/originais preservados/);
|
|
});
|