Files
Investidor-V2/tests/frontend/iva-service-normal.test.mjs
T

36 lines
7.4 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import {newServiceNormal,switchServiceTreatment,serviceNormalTreatments,serviceNormalConditions,ownServiceConditions,evaluateServiceNormal,recordServiceNormals,saveServiceNormalBook,serviceNormalBook,serviceNormalCurrent,serviceNormalReportRows} from '../../resources/js/lib/iva-service-normal.ts';
import {valueReviewCurrent} from '../../resources/js/lib/iva-value-review.ts';
import {exclusionCurrent} from '../../resources/js/lib/iva-exclusion-base.ts';
import {createProjectDossierReport} from '../../resources/js/lib/project-dossier-report.ts';
const frozen=JSON.parse(fs.readFileSync(new URL('./fixtures/iva-g7i-preserved-inputs.json',import.meta.url),'utf8'));
const row=(treatment='free_company_service')=>({...newServiceNormal('g7i-'+treatment,'MZN'),treatment,description:'SYNTHETIC QA serviço A',period:'2026-12',operationRef:'SYNTHETIC QA operação A',purposeRef:'SYNTHETIC QA finalidade/destinatário ou sector isento A',assetRef:'SYNTHETIC QA bens/elementos A',deductionRef:'SYNTHETIC QA referência dedução anterior parcial A',documentRef:'SYNTHETIC QA G7I '+treatment,documentDate:'2026-12-12',normalValue:({free_company_service:'500.25',private_company_asset_use:'700.50',exempt_sector_asset_use:'900.75'})[treatment],valuationRef:'SYNTHETIC QA valor normal serviço A15(4)',valuationScope:'SYNTHETIC QA serviço A,estádio/tempo/lugar/concorrência/fornecedor independente,dezembro2026Manica',compositionRef:'SYNTHETIC QA composição15(4)/(5)/(6),sem próprioIVA',evidence:'SYNTHETIC QA pressupostos expressos,não documento real',conditions:Object.fromEntries([...serviceNormalConditions,...ownServiceConditions(treatment)].map(([key])=>[key,'yes']))});
const draft=()=>({...structuredClone(frozen),context_opportunity:'opportunity:347',project_title:'SYNTHETIC QA — G7I / Valor normal de serviços'});
test('P089 three treatments ownconditions and normalvalue references;prior deduction applies only to assetuse',()=>{
for(const t of serviceNormalTreatments){const input=row(t.id);assert.equal(evaluateServiceNormal(input,'MZN').baseAmount,input.normalValue);assert.equal(evaluateServiceNormal(input,'MZN').vat,null);assert.equal(evaluateServiceNormal(input,'MZN').rate,null);
for(const[key]of [...serviceNormalConditions,...ownServiceConditions(t.id)])for(const value of ['','unknown','no']){const r=evaluateServiceNormal({...input,conditions:{...input.conditions,[key]:value}},'MZN');assert.equal(r.status,value==='no'?'not_applicable':'indeterminate');assert.equal(r.baseAmount,null);}
for(const patch of [{description:''},{operationRef:''},{purposeRef:''},{documentRef:''},{documentDate:'2026-02-30'},{valuationRef:''},{valuationScope:''},{compositionRef:''},{evidence:''},{period:'2025-12'},{currency:'USD'},{treatment:''},{normalValue:''}])assert.equal(evaluateServiceNormal({...input,...patch},'MZN').baseAmount,null);
if(t.id!=='free_company_service')for(const patch of [{assetRef:''},{deductionRef:''}])assert.equal(evaluateServiceNormal({...input,...patch},'MZN').baseAmount,null);
}
const free=row();assert.equal(evaluateServiceNormal({...free,assetRef:'',deductionRef:'',conditions:{...free.conditions,priorDeduction:'no'}},'MZN').baseAmount,'500.25');
});
test('normalvalue only,no assetcost/substitution;exactdecimal/zero and switch preserves hiddenassetrefs but clears ownconditions',()=>{
const input=row('private_company_asset_use');for(const normalValue of ['0','1.000002','999999999999999.999999'])assert.equal(evaluateServiceNormal({...input,normalValue},'MZN').baseAmount,normalValue==='0'?'0.00':normalValue);for(const normalValue of ['-1','NaN','0.0000001'])assert.equal(evaluateServiceNormal({...input,normalValue},'MZN').baseAmount,null);
const changed=switchServiceTreatment(input,'free_company_service');assert.equal(changed.conditions.freeByCompany,undefined);assert.equal(changed.conditions.priorDeduction,undefined);for(const field of ['assetRef','deductionRef','normalValue','valuationRef','valuationScope','purposeRef'])assert.equal(changed[field],input[field]);assert.equal(evaluateServiceNormal(changed,'MZN').baseAmount,null);
assert.equal(evaluateServiceNormal({...changed,conditions:{...changed.conditions,freeByCompany:'yes',nonBusinessPurpose:'yes',priorDeduction:'no'}},'MZN').baseAmount,'700.50');
});
test('save/recovery/history/report preserve all86 delivered books/currentkeys including P103/P102/P101/P095',()=>{
const d=draft(),prior=JSON.parse(d.modelo_a_snapshot);assert.deepEqual(serviceNormalBook(d),{rows:[]});assert.deepEqual(serviceNormalReportRows(d),[]);
let book=recordServiceNormals(d,{rows:serviceNormalTreatments.map(t=>({...row(t.id),conditions:{}}))});book=recordServiceNormals(d,{...book,rows:serviceNormalTreatments.map(t=>({...row(t.id),conditions:{...row(t.id).conditions,domesticOperation:'no'}}))});book=recordServiceNormals(d,{...book,rows:serviceNormalTreatments.map(t=>row(t.id))});assert.equal(book.history.length,2);assert.ok(book.history.every(h=>h.rows.every(r=>r.baseAmount===null&&r.vat===null)));assert.ok(serviceNormalCurrent(d,book));
for(const field of ['treatment','normalValue','valuationScope','deductionRef','assetRef','purposeRef'])assert.ok(!serviceNormalCurrent(d,{...book,rows:book.rows.map(r=>({...r,[field]:'changed'}))}));for(const patch of [{context_opportunity:'opportunity:348'},{currency:'USD'},{selection_labels:{province_id:'Maputo'}}])assert.ok(!serviceNormalCurrent({...d,...patch},book));
d.modelo_a_snapshot=saveServiceNormalBook(d,book);assert.deepEqual(serviceNormalBook(JSON.parse(JSON.stringify(d))),book);const next=JSON.parse(d.modelo_a_snapshot);const{serviceNormals,...untouched}=next;assert.deepEqual(untouched,prior);for(const[scope,previous]of Object.entries(prior.valueReviews))assert.ok(valueReviewCurrent({...d,context_opportunity:scope},previous));for(const[scope,previous]of Object.entries(prior.considerationExclusions))assert.ok(exclusionCurrent({...d,context_opportunity:scope},previous));
assert.equal(next.valueReviews['opportunity:346'].result.rows[1].baseAmount,'1400.75');assert.equal(next.considerationExclusions['opportunity:345'].result.rows[0].baseAmount,'799.70');assert.equal(next.regulatedFuelBases['opportunity:342'].result.rows[0].baseAmount,'1400.15');assert.equal(next.books['opportunity:329'].result.periods[0].fields['11'],null);
const report=JSON.stringify(createProjectDossierReport(d,{selectedSections:new Set(['finance']),version:'QA G7I'}));for(const token of ['Base P089','500.25','700.50','900.75','IVA: Indeterminado','opportunity:347','Manica','2026-12','15(2)(c)','4(2)(a)','dedução anterior parcial A','campo11'])assert.ok(report.includes(token),token);assert.deepEqual(serviceNormalReportRows({...d,context_opportunity:'opportunity:348'}),[]);
if(process.env.IVA_G7I_CALCULATED)fs.writeFileSync(process.env.IVA_G7I_CALCULATED,JSON.stringify(d));
});
test('corrupt service rows/store cannot overwrite originals;duplicateoperationids cannotbe recorded',()=>{
const d=draft(),original=d.modelo_a_snapshot;for(const patch of [{normalValue:4},{conditions:[]},{deductionRef:null}])assert.throws(()=>saveServiceNormalBook(d,{rows:[{...row(),...patch}]}),/originais preservados/);assert.equal(d.modelo_a_snapshot,original);assert.throws(()=>recordServiceNormals(d,{rows:[row(),row()]}),/distintos/);assert.throws(()=>recordServiceNormals(d,{rows:[]}),/distintos/);assert.throws(()=>serviceNormalBook({...d,modelo_a_snapshot:JSON.stringify({...JSON.parse(original),serviceNormals:[]})}),/originais preservados/);
});