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

45 lines
9.3 KiB
JavaScript

import test from 'node:test';import assert from 'node:assert/strict';import fs from 'node:fs';
import {digitalTreatments,digitalConditions,newDigitalRow,evaluateDigital,recordDigital,saveDigitalBook,digitalBook,digitalCurrent,digitalReportRows,switchDigitalKind,switchDigitalTreatment} from '../../resources/js/lib/iva-digital.ts';
import {newForeignOperation,recordForeignOperations,saveForeignOperationBook,foreignOperationCurrent,foreignSources} from '../../resources/js/lib/iva-foreign-operations.ts';
const frozen=JSON.parse(fs.readFileSync('tests/frontend/fixtures/iva-digital-preserved.json','utf8')),prior=JSON.parse(frozen.modelo_a_snapshot);
const draft={...frozen,context_opportunity:'opportunity:359',project_title:'SYNTHETIC QA — Digital / Manica'};
function row(rule,direction='inbound'){return{...newDigitalRow(rule.id+'-'+direction),kind:rule.kind,treatment:rule.id,description:'SYNTHETIC QA '+rule.label,period:'2026-12',origin:'projected',operationDate:'2026-12-10',operationRef:'SYNTHETIC QA op '+rule.id+'-'+direction,documentRef:'SYNTHETIC QA contrato/previsão',documentDate:'2026-12-01',subjectRef:'SYNTHETIC QA objecto/direitos/funcionalidade delimitados',useRef:'SYNTHETIC QA destinatário e utilização',natureRef:'SYNTHETIC QA natureza contratual '+rule.kind,scope:'SYNTHETIC QA operação/dezembro2026/âmbito identificado',evidence:'SYNTHETIC QA factos ou pressupostos expressos',providerPlace:'SYNTHETIC QA estabelecimento fornecedor',providerCountry:direction==='inbound'?'foreign':'MZ',providerRef:'SYNTHETIC QA contrato/estabelecimento fornecedor',buyerPlace:'SYNTHETIC QA domicílio adquirente',buyerCountry:direction==='outbound'?'foreign':'MZ',buyerRef:'SYNTHETIC QA contrato/domicílio adquirente',buyerTaxable:'yes',buyerTaxRef:'SYNTHETIC QA sujeito passivo nacional, incluindo isento sem dedução',providerTaxable:'yes',providerTaxRef:'SYNTHETIC QA sujeito passivo fornecedor',goodsPlace:'MZ',goodsPlaceRef:'SYNTHETIC QA factos local6(1),não inferido de IP/servidor',conditions:Object.fromEntries(digitalConditions(rule.kind).map(([k])=>[k,'yes']))};}
const cases=digitalTreatments.flatMap(rule=>['inbound','outbound','domestic'].map(direction=>row(rule,direction)));
for(const r of cases)test(`${r.kind==='goods'?'P130':'P121'} ${r.id} classification and responsibility`,()=>{
const v=evaluateDigital(r);assert.equal(v.status,'determined');assert.equal(v.territory,r.buyerCountry==='foreign'?'outside':'national');assert.equal(v.responsible,r.buyerCountry==='foreign'?'outside':r.providerCountry==='foreign'?'acquirer':'provider');
assert.equal(v.declarationReferenceDate,r.providerCountry==='foreign'&&r.buyerCountry==='MZ'?'2027-01-10':null);assert.equal(v.procedure,r.buyerCountry==='foreign'?'outside':r.providerCountry==='foreign'?'regulation_pending':'general');for(const k of ['vat','baseAmount','rate','deduction'])assert.equal(v[k],null);
});
test('Unknown facts do not become national, exempt or zero; no name-only kind inference',()=>{
const r=cases[0];for(const patch of [{kind:''},{kind:'goods'},{period:'2025-12'},{operationDate:'2026-02-30'},{operationDate:'2026-11-10'},{origin:'unknown'},{documentDate:'2026-02-30'},{subjectRef:''},{useRef:''},{natureRef:''},{scope:''},{evidence:''},{buyerCountry:''},{buyerRef:''},{providerRef:''}])assert.equal(evaluateDigital({...r,...patch}).status,'indeterminate');
for(const[k]of digitalConditions(r.kind)){assert.equal(evaluateDigital({...r,conditions:{...r.conditions,[k]:''}}).status,'indeterminate');assert.equal(evaluateDigital({...r,conditions:{...r.conditions,[k]:'no'}}).status,'not_applicable');}
const outbound={...cases[1],providerCountry:'',providerRef:'',providerPlace:'',buyerTaxable:'',buyerTaxRef:''};assert.equal(evaluateDigital(outbound).territory,'outside');
});
test('26(4) quality/reference independent of national territory; consumer never automatic withholding',()=>{
for(const r of [cases[0],cases.find(r=>r.kind==='goods')])for(const patch of [{buyerTaxable:''},{buyerTaxRef:''},{buyerTaxable:'no'}]){const v=evaluateDigital({...r,...patch});assert.equal(v.territory,'national');assert.equal(v.status,'determined');assert.equal(v.responsible,'indeterminate');assert.equal(v.declarationReferenceDate,null);}
const r={...cases[0],buyerTaxable:'no',buyerTaxRef:'SYNTHETIC QA consumidor',representative:'yes',representativeRef:'SYNTHETIC QA residente/procuração/poderes',communicationPrior:'yes',communicationRef:'SYNTHETIC QA comunicação anterior'};
assert.equal(evaluateDigital(r).responsible,'representative');assert.equal(evaluateDigital(r).declarationReferenceDate,null);
for(const patch of [{providerTaxRef:''},{representativeRef:''},{communicationPrior:'no'},{communicationRef:''}])assert.equal(evaluateDigital({...r,...patch}).responsible,'indeterminate');
const business={...r,representative:'no',businessUse:'yes',businessRef:'SYNTHETIC QA actividade/aquisição profissional'};assert.equal(evaluateDigital(business).responsible,'acquirer');assert.equal(evaluateDigital({...business,businessUse:'no'}).responsible,'indeterminate');assert.equal(evaluateDigital({...business,businessRef:''}).responsible,'indeterminate');
});
test('Domestic digital goods need demonstrated6(1) location, not consumer address/server; domestic services6(3)',()=>{
const r=cases.find(r=>r.kind==='goods'&&r.id.endsWith('domestic'));assert.equal(evaluateDigital({...r,goodsPlace:''}).territory,null);assert.equal(evaluateDigital({...r,goodsPlaceRef:''}).status,'indeterminate');assert.equal(evaluateDigital({...r,goodsPlace:'foreign'}).territory,'outside');
assert.equal(evaluateDigital({...cases[2],goodsPlace:'',goodsPlaceRef:''}).territory,'national');assert.equal(evaluateDigital({...r,providerTaxable:''}).responsible,'indeterminate');
});
test('Switching nature/treatment preserves deliberate references and hidden facts, rechecks specific conditions',()=>{
const r=cases[0],sw=switchDigitalKind(r,'goods');assert.equal(sw.treatment,'');assert.deepEqual(sw.conditions,{});for(const k of ['natureRef','buyerTaxRef','goodsPlaceRef','scope','evidence'])assert.equal(sw[k],r[k]);assert.deepEqual(switchDigitalTreatment(r,'teaching').conditions,{});
});
test('Opportunity isolation, stable history and malformed-store guard; all prior books including P002 immutable',()=>{
const a=recordDigital(draft,{rows:cases}),d={...draft,modelo_a_snapshot:saveDigitalBook(draft,a)},b=recordDigital(d,a);d.modelo_a_snapshot=saveDigitalBook(d,b);assert.equal(digitalCurrent(d,b),true);assert.equal(b.history.length,1);assert.equal(digitalReportRows(d).length,42);assert.deepEqual(digitalBook({...d,context_opportunity:'opportunity:358'}),{rows:[]});
const changed={...b,rows:b.rows.map((r,i)=>i===0?{...r,buyerCountry:'foreign'}:r)};assert.equal(digitalCurrent(d,changed),false);assert.equal(b.history[0].inputs[0].buyerCountry,'MZ');assert.equal(b.history[0].rows[0].territory,'national');
const store=JSON.parse(d.modelo_a_snapshot);delete store.digitalOperations;assert.deepEqual(store,prior);assert.throws(()=>recordDigital(d,{rows:[cases[0],cases[0]]}));assert.throws(()=>digitalBook({...d,modelo_a_snapshot:JSON.stringify({...store,digitalOperations:[]})}));assert.throws(()=>digitalBook({...d,modelo_a_snapshot:JSON.stringify({...store,digitalOperations:{x:{rows:[{id:'incomplete'}]}}})}));
});
test('P002 reuses new digital location in same opportunity, guards stale source and keeps accepted P002 snapshots',()=>{
const a=recordDigital(draft,{rows:cases}),d={...draft,modelo_a_snapshot:saveDigitalBook(draft,a)},r={...newForeignOperation('digital-outside'),sourceKind:'digital',sourceRowId:cases[1].id,hypotheticalTaxable:'yes',hypotheticalRef:'SYNTHETIC QA hipótese interna',excluded19_2:'no',scope:'SYNTHETIC QA operação digital exterior',evidence:'SYNTHETIC QA pressupostos'};
assert.equal(foreignSources(d,'digital').length,42);const b=recordForeignOperations(d,{rows:[r]});assert.equal(b.result.rows[0].relation19,'potential');assert.equal(b.result.rows[0].deduction,null);d.modelo_a_snapshot=saveForeignOperationBook(d,b);assert.equal(foreignOperationCurrent(d,b),true);
const next={...d,modelo_a_snapshot:saveDigitalBook(d,recordDigital(d,a))};assert.equal(foreignOperationCurrent(next,b),false);assert.equal(b.result.rows[0].source.revision,1);assert.deepEqual(JSON.parse(d.modelo_a_snapshot).foreignOperations['opportunity:358'],prior.foreignOperations['opportunity:358']);
});
test('Fixture for private API includes42 examples and unresolved/representative/business cases',()=>{
const inbound=cases[0],extra=[{...inbound,id:'consumer',buyerTaxable:'no',buyerTaxRef:'SYNTHETIC QA consumidor'},{...inbound,id:'unknown_quality',buyerTaxable:'',buyerTaxRef:''},{...inbound,id:'representative',buyerTaxable:'no',buyerTaxRef:'SYNTHETIC QA não sujeito passivo',representative:'yes',representativeRef:'SYNTHETIC QA representante/procuração',communicationPrior:'yes',communicationRef:'SYNTHETIC QA comunicação anterior'},{...inbound,id:'business_26_3',buyerTaxable:'no',buyerTaxRef:'SYNTHETIC QA não sujeito passivo',representative:'no',businessUse:'yes',businessRef:'SYNTHETIC QA actividade profissional'}],a=recordDigital(draft,{rows:[...cases,...extra]}),d={...draft,modelo_a_snapshot:saveDigitalBook(draft,a)},b=recordDigital(d,a);d.modelo_a_snapshot=saveDigitalBook(d,b);
assert.equal(b.rows.length,46);assert.equal(b.history.length,1);if(process.env.IVA_DIGITAL_FIXTURE)fs.writeFileSync(process.env.IVA_DIGITAL_FIXTURE,JSON.stringify(d,null,2)+'\n');
});