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

366 lines
13 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)
void 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);
});
void 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');
});
void 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',
);
});
void 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',
);
});
void 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, {});
});
void 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' }] } },
}),
}),
);
});
void 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'],
);
});
void 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',
);
});