281 lines
9.4 KiB
JavaScript
281 lines
9.4 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import {
|
|
newForeignOperation,
|
|
evaluateForeignOperation,
|
|
foreignSources,
|
|
foreignEvents,
|
|
recordForeignOperations,
|
|
saveForeignOperationBook,
|
|
foreignOperationBook,
|
|
foreignOperationCurrent,
|
|
foreignOperationReportRows,
|
|
switchForeignSource,
|
|
} from '../../resources/js/lib/iva-foreign-operations.ts';
|
|
import {
|
|
recordGoodsLocations,
|
|
saveGoodsLocationBook,
|
|
goodsLocationBook,
|
|
} from '../../resources/js/lib/iva-goods-location.ts';
|
|
import {
|
|
recordServiceLocations,
|
|
saveServiceLocationBook,
|
|
} from '../../resources/js/lib/iva-service-location.ts';
|
|
import {
|
|
recordListedServices,
|
|
saveListedServiceBook,
|
|
} from '../../resources/js/lib/iva-listed-services.ts';
|
|
import {
|
|
recordCurrencyEvents,
|
|
saveCurrencyEventBook,
|
|
} from '../../resources/js/lib/iva-currency-events.ts';
|
|
const frozen = JSON.parse(
|
|
fs.readFileSync(
|
|
'tests/frontend/fixtures/iva-foreign-operations-preserved.json',
|
|
'utf8',
|
|
),
|
|
),
|
|
original = JSON.parse(frozen.modelo_a_snapshot);
|
|
function seed() {
|
|
const d = {
|
|
...frozen,
|
|
context_opportunity: 'opportunity:358',
|
|
project_title: 'SYNTHETIC QA — Operações no estrangeiro / Manica',
|
|
};
|
|
for (const [map, record, save] of [
|
|
['goodsLocations', recordGoodsLocations, saveGoodsLocationBook],
|
|
['serviceLocations', recordServiceLocations, saveServiceLocationBook],
|
|
['listedServices', recordListedServices, saveListedServiceBook],
|
|
]) {
|
|
const rows = structuredClone(Object.values(original[map])[0].rows).map(
|
|
(r) => ({ ...r, operationRef: 'SYNTHETIC QA op ' + r.id }),
|
|
);
|
|
if (map === 'serviceLocations') {
|
|
const r = structuredClone(
|
|
rows.find((x) => x.id === 'transport_service-MZ'),
|
|
);
|
|
rows.push({
|
|
...r,
|
|
id: 'transport_partial',
|
|
operationRef: 'SYNTHETIC QA op transport_partial',
|
|
departure: 'MZ',
|
|
arrival: 'foreign',
|
|
nationalDistance: '20',
|
|
totalDistance: '100',
|
|
});
|
|
}
|
|
d.modelo_a_snapshot = save(d, record(d, { rows }));
|
|
}
|
|
const event = structuredClone(
|
|
original.currencyEvents['opportunity:357'].rows.find(
|
|
(r) => r.treatment === 'goods_available',
|
|
),
|
|
);
|
|
event.operationRef = 'SYNTHETIC QA op g8a-goods_without_transport';
|
|
d.modelo_a_snapshot = saveCurrencyEventBook(
|
|
d,
|
|
recordCurrencyEvents(d, { rows: [event] }),
|
|
);
|
|
return d;
|
|
}
|
|
const draft = seed();
|
|
function row(id, kind, source) {
|
|
return {
|
|
...newForeignOperation(id),
|
|
sourceKind: kind,
|
|
sourceRowId: source,
|
|
territorialPart: 'outside',
|
|
hypotheticalTaxable: 'yes',
|
|
hypotheticalRef:
|
|
'SYNTHETIC QA hipótese interna/natureza tributável e19(2),fundamento identificado',
|
|
excluded19_2: 'no',
|
|
scope: 'SYNTHETIC QA conjunto/parcela exterior/período identificado',
|
|
evidence: 'SYNTHETIC QA factos/pressupostos expressos',
|
|
};
|
|
}
|
|
const cases = [
|
|
row('foreign_goods', 'goods', 'g8a-goods_without_transport'),
|
|
row('foreign_services', 'services', 'general_service-foreign'),
|
|
row('foreign_listed', 'listed', 'rights_service-outbound'),
|
|
row('foreign_transport_part', 'services', 'transport_partial'),
|
|
];
|
|
cases[0].eventRowId = 'goods_available';
|
|
for (const r of cases)
|
|
void test(
|
|
'P002 ' + r.id + ' reuses delivered territoriality and potential19only',
|
|
() => {
|
|
const v = evaluateForeignOperation(draft, r);
|
|
assert.equal(v.status, 'determined');
|
|
assert.equal(v.relation19, 'potential');
|
|
assert.equal(
|
|
v.territory,
|
|
r.id === 'foreign_transport_part'
|
|
? 'outside_portion'
|
|
: 'outside',
|
|
);
|
|
assert.equal(v.deduction, null);
|
|
assert.equal(v.vat, null);
|
|
assert.equal(v.baseAmount, null);
|
|
assert.equal(v.source.id, r.sourceRowId);
|
|
assert.equal(v.source.current, true);
|
|
if (r.eventRowId) assert.equal(v.event.eventDate, '2026-12-10');
|
|
},
|
|
);
|
|
void test('P002 unknown/national/partial do not imply foreign or exemption', () => {
|
|
assert.equal(
|
|
evaluateForeignOperation(draft, { ...cases[0], sourceRowId: '' })
|
|
.status,
|
|
'indeterminate',
|
|
);
|
|
assert.equal(
|
|
evaluateForeignOperation(draft, {
|
|
...cases[0],
|
|
sourceRowId: 'g8a-goods_with_transport',
|
|
}).status,
|
|
'not_applicable',
|
|
);
|
|
assert.equal(
|
|
evaluateForeignOperation(draft, { ...cases[3], territorialPart: '' })
|
|
.status,
|
|
'indeterminate',
|
|
);
|
|
assert.equal(
|
|
evaluateForeignOperation(draft, {
|
|
...cases[3],
|
|
territorialPart: 'national',
|
|
}).status,
|
|
'not_applicable',
|
|
);
|
|
for (const patch of [
|
|
{ scope: '' },
|
|
{ evidence: '' },
|
|
{ hypotheticalRef: '' },
|
|
{ hypotheticalTaxable: '' },
|
|
{ excluded19_2: '' },
|
|
])
|
|
assert.equal(
|
|
evaluateForeignOperation(draft, { ...cases[0], ...patch }).status,
|
|
'indeterminate',
|
|
);
|
|
const no = evaluateForeignOperation(draft, {
|
|
...cases[0],
|
|
hypotheticalTaxable: 'no',
|
|
});
|
|
assert.equal(no.relation19, 'not_demonstrated');
|
|
assert.equal(no.deduction, null);
|
|
const excluded = evaluateForeignOperation(draft, {
|
|
...cases[0],
|
|
excluded19_2: 'yes',
|
|
});
|
|
assert.equal(excluded.relation19, 'excluded_19_2');
|
|
assert.equal(excluded.deduction, null);
|
|
});
|
|
void test('P002 stale source and event scope mismatch never overwrite prior acceptance', () => {
|
|
const b = goodsLocationBook(draft),
|
|
changed = {
|
|
...draft,
|
|
modelo_a_snapshot: saveGoodsLocationBook(draft, {
|
|
...b,
|
|
rows: b.rows.map((r) =>
|
|
r.id === cases[0].sourceRowId
|
|
? { ...r, placeCountry: 'MZ' }
|
|
: r,
|
|
),
|
|
}),
|
|
};
|
|
assert.equal(
|
|
evaluateForeignOperation(changed, cases[0]).status,
|
|
'indeterminate',
|
|
);
|
|
assert.equal(
|
|
evaluateForeignOperation(
|
|
{ ...draft, context_opportunity: 'opportunity:359' },
|
|
cases[0],
|
|
).status,
|
|
'indeterminate',
|
|
);
|
|
assert.equal(
|
|
evaluateForeignOperation(draft, {
|
|
...cases[1],
|
|
eventRowId: 'goods_available',
|
|
}).status,
|
|
'indeterminate',
|
|
);
|
|
const sw = switchForeignSource(
|
|
cases[0],
|
|
'services',
|
|
'general_service-foreign',
|
|
);
|
|
assert.equal(sw.hypotheticalRef, cases[0].hypotheticalRef);
|
|
assert.equal(sw.hypotheticalTaxable, '');
|
|
assert.equal(sw.eventRowId, '');
|
|
assert.equal(foreignEvents(draft).length, 1);
|
|
assert.equal(foreignSources(draft, 'goods').length, 3);
|
|
});
|
|
void test('P002 dependency changes invalidate new analysis while snapshot/history stay immutable', () => {
|
|
const a = recordForeignOperations(draft, { rows: cases }),
|
|
d = { ...draft, modelo_a_snapshot: saveForeignOperationBook(draft, a) };
|
|
assert.equal(foreignOperationCurrent(d, a), true);
|
|
const b = goodsLocationBook(d),
|
|
changed = {
|
|
...d,
|
|
modelo_a_snapshot: saveGoodsLocationBook(
|
|
d,
|
|
recordGoodsLocations(d, b),
|
|
),
|
|
};
|
|
assert.equal(foreignOperationCurrent(changed, a), false);
|
|
assert.deepEqual(a.result.inputs, cases);
|
|
assert.equal(foreignOperationReportRows(d).length, 4);
|
|
});
|
|
void test('P002 private save/recovery/report preserve 120delivered books and P104currency facts', () => {
|
|
const rows = [
|
|
...cases,
|
|
row('national', 'goods', 'g8a-goods_with_transport'),
|
|
{
|
|
...cases[1],
|
|
id: 'hypothesis_not_taxable',
|
|
hypotheticalTaxable: 'no',
|
|
},
|
|
{ ...cases[2], id: 'exclusion19_2', excluded19_2: 'yes' },
|
|
],
|
|
a = recordForeignOperations(draft, { rows }),
|
|
d = { ...draft, modelo_a_snapshot: saveForeignOperationBook(draft, a) },
|
|
b = recordForeignOperations(d, a);
|
|
d.modelo_a_snapshot = saveForeignOperationBook(d, b);
|
|
assert.equal(b.history.length, 1);
|
|
assert.equal(foreignOperationReportRows(d).length, 7);
|
|
const store = JSON.parse(d.modelo_a_snapshot);
|
|
delete store.foreignOperations;
|
|
for (const name of [
|
|
'goodsLocations',
|
|
'serviceLocations',
|
|
'listedServices',
|
|
'currencyEvents',
|
|
])
|
|
delete store[name]['opportunity:358'];
|
|
assert.deepEqual(store, original);
|
|
assert.deepEqual(
|
|
foreignOperationBook({ ...d, context_opportunity: 'opportunity:359' }),
|
|
{ rows: [] },
|
|
);
|
|
assert.throws(() =>
|
|
recordForeignOperations(d, { rows: [rows[0], rows[0]] }),
|
|
);
|
|
assert.throws(() =>
|
|
foreignOperationBook({
|
|
...d,
|
|
modelo_a_snapshot: JSON.stringify({
|
|
...store,
|
|
foreignOperations: [],
|
|
}),
|
|
}),
|
|
);
|
|
if (process.env.IVA_FOREIGN_OPERATIONS_FIXTURE)
|
|
fs.writeFileSync(
|
|
process.env.IVA_FOREIGN_OPERATIONS_FIXTURE,
|
|
JSON.stringify(d, null, 2) + '\n',
|
|
);
|
|
});
|