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

223 lines
8.0 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import { exportServiceTreatments } from '../../resources/js/lib/iva-export-services.ts';
import {
newSalesOperation,
operationTreatments,
evaluateSalesOperation,
referenceExportOperation,
recordSales,
saveSalesBook,
salesBook,
salesCurrent,
} from '../../resources/js/lib/iva-operations.ts';
import { createProjectDossierReport } from '../../resources/js/lib/project-dossier-report.ts';
const frozen = JSON.parse(
fs.readFileSync(
new URL('./fixtures/iva-g4b-inputs.json', import.meta.url),
'utf8',
),
);
const row = (rule, i = 0) => ({
...newSalesOperation(`g4c-${i}`, '2026-12', 'MZN'),
description: `SYNTHETIC QA — ${rule.label}`,
establishment: 'SYNTHETIC QA estabelecimento',
amount: '100',
basis: i % 2 ? 'included' : 'net',
treatment: rule.id,
confirmed: rule.conditions.map((_, n) => n),
evidence:
'SYNTHETIC QA — documento/pressuposto externo identifica bens,regime/localização,mandante e ligação do serviço;operação do intermediário localizada emMoçambique;sem certificação',
});
function fixture() {
const draft = {
...structuredClone(frozen),
context_opportunity: 'opportunity:325',
project_title: 'SYNTHETIC QA — G4C / Serviços ligados',
};
const goods = {
...row(
operationTreatments.find((r) => r.id === 'export_goods'),
9,
),
id: 'g4c-goods',
description: 'SYNTHETIC QA — mercadorias expedidas pelo vendedor',
amount: '1000',
};
const services = exportServiceTreatments.map(row);
services[1] = referenceExportOperation(services[1], goods);
services[1].confirmed = exportServiceTreatments[1].conditions.map(
(_, i) => i,
);
const book = recordSales(draft, {
currency: 'MZN',
rows: [goods, ...services],
periods: [{ period: '2026-12', complete: true }],
});
draft.modelo_a_snapshot = saveSalesBook(draft, book);
return { draft, book };
}
void test('three services require each legal condition,local service scope and evidence;no automatic classification from client or sector', () => {
assert.equal(exportServiceTreatments.length, 3);
assert.match(
exportServiceTreatments[0].conditions[2],
/admissão temporária/,
);
assert.match(exportServiceTreatments[0].conditions[3], /artigo9/);
assert.match(exportServiceTreatments[1].conditions[2], /artigo9/);
assert.match(
exportServiceTreatments[2].conditions[1],
/não actuação em nome próprio/,
);
for (const [i, rule] of exportServiceTreatments.entries()) {
const input = row(rule, i);
assert.equal(evaluateSalesOperation(input, 'MZN').field, '05');
assert.equal(evaluateSalesOperation(input, 'MZN').vat, '0.00');
for (const missing of input.confirmed)
assert.equal(
evaluateSalesOperation(
{
...input,
confirmed: input.confirmed.filter((n) => n !== missing),
},
'MZN',
).vat,
null,
);
for (const patch of [
{ amount: '' },
{ evidence: '' },
{ sourceCurrency: 'USD' },
{ period: '2027-01' },
{ basis: '' },
])
assert.equal(
evaluateSalesOperation({ ...input, ...patch }, 'MZN').vat,
null,
);
}
});
void test('reference reuse keeps service price/identity/evidence and requires deliberate confirmation of new relationship', () => {
const service = row(exportServiceTreatments[1]),
related = {
...row(
operationTreatments.find((r) => r.id === 'export_goods'),
8,
),
amount: '1000',
},
linked = referenceExportOperation(service, related);
assert.deepEqual(linked.relatedExport, {
id: related.id,
description: related.description,
treatment: related.treatment,
});
assert.deepEqual(linked.confirmed, []);
for (const field of [
'id',
'amount',
'basis',
'period',
'description',
'evidence',
'establishment',
'sourceAmount',
'sourcePeriod',
'sourceCurrency',
'source',
])
assert.deepEqual(linked[field], service[field], field);
assert.equal(linked.amount, '100');
assert.equal(evaluateSalesOperation(linked, 'MZN').vat, null);
assert.throws(() => referenceExportOperation(service, service));
assert.throws(() => referenceExportOperation(related, service));
});
void test('deleted/changed linked operation blocks current calculation,keeps history and requires explicit reference update', () => {
const { draft, book } = fixture();
assert.equal(book.result.periods[0].fields['05'], '1300.00');
for (const rows of [
book.rows.filter((r) => r.id !== 'g4c-goods'),
book.rows.map((r) =>
r.id === 'g4c-goods' ? { ...r, description: 'Changed goods' } : r,
),
]) {
const changed = recordSales(draft, { ...book, rows });
assert.equal(
changed.result.rows.find((r) => r.id === 'g4c-1').vat,
null,
);
assert.match(
changed.result.rows.find((r) => r.id === 'g4c-1').reason,
/removida ou alterada/,
);
assert.equal(changed.result.periods[0].fields['05'], null);
assert.deepEqual(changed.history.at(-1), book.result);
assert.equal(changed.rows.find((r) => r.id === 'g4c-1').amount, '100');
}
const changedGoods = { ...book.rows[0], description: 'Reviewed goods' };
const linked = referenceExportOperation(book.rows[2], changedGoods);
linked.confirmed = exportServiceTreatments[1].conditions.map((_, i) => i);
const next = recordSales(draft, {
...book,
rows: [changedGoods, book.rows[1], linked, book.rows[3]],
});
assert.equal(next.result.periods[0].fields['05'], '1300.00');
const invalid = recordSales(draft, {
...book,
rows: book.rows.map((r) =>
r.id === 'g4c-1'
? {
...r,
relatedExport: {
id: r.id,
description: r.description,
treatment: r.treatment,
},
}
: r,
),
});
assert.equal(invalid.result.periods[0].fields['05'], null);
});
void test('stored service reference survives report/recovery;all16 prior books remain unchanged/current without recalculation', () => {
const { draft, book } = fixture();
assert.ok(salesCurrent(draft, book));
assert.deepEqual(
salesBook(JSON.parse(JSON.stringify(draft))),
JSON.parse(JSON.stringify(book)),
);
const before = JSON.parse(frozen.modelo_a_snapshot).salesOperations,
after = JSON.parse(draft.modelo_a_snapshot).salesOperations;
assert.equal(Object.keys(before).length, 16);
for (const [context, original] of Object.entries(before)) {
assert.deepEqual(after[context], original);
assert.ok(
salesCurrent(
{ ...draft, context_opportunity: context },
after[context],
),
);
}
const report = JSON.stringify(
createProjectDossierReport(draft, {
selectedSections: new Set(['finance']),
version: 'QA G4C',
}),
);
for (const token of [
'opportunity:325',
'Manica',
'2026-12',
'1300.00',
'13(1)(m)',
'13(1)(n)',
'13(1)(s)',
'g4c-goods',
'Operação relacionada',
])
assert.ok(report.includes(token), token);
if (process.env.IVA_G4C_CALCULATED)
fs.writeFileSync(process.env.IVA_G4C_CALCULATED, JSON.stringify(draft));
});