149 lines
5.3 KiB
JavaScript
149 lines
5.3 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import { directExportTreatments } from '../../resources/js/lib/iva-export-operations.ts';
|
|
import {
|
|
newSalesOperation,
|
|
evaluateSalesOperation,
|
|
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-g3-inputs.json', import.meta.url),
|
|
'utf8',
|
|
),
|
|
);
|
|
function row(rule, index = 0) {
|
|
return {
|
|
...newSalesOperation(`g4a-${index}`, '2026-11', 'MZN'),
|
|
description: `SYNTHETIC QA — ${rule.label}`,
|
|
establishment: 'SYNTHETIC QA estabelecimento',
|
|
amount: '100',
|
|
basis: index % 2 ? 'included' : 'net',
|
|
treatment: rule.id,
|
|
confirmed: rule.conditions.map((_, i) => i),
|
|
evidence:
|
|
'SYNTHETIC QA — intervenientes,origem/destino e ligação descritos como pressupostos da projecção; não certificação documental',
|
|
};
|
|
}
|
|
function fixture() {
|
|
const draft = {
|
|
...structuredClone(frozen),
|
|
context_opportunity: 'opportunity:323',
|
|
project_title: 'SYNTHETIC QA — G4A / Exportações directas',
|
|
};
|
|
const book = recordSales(draft, {
|
|
currency: 'MZN',
|
|
rows: directExportTreatments.map(row),
|
|
periods: [{ period: '2026-11', complete: true }],
|
|
});
|
|
draft.modelo_a_snapshot = saveSalesBook(draft, book);
|
|
return { draft, book };
|
|
}
|
|
void test('five autonomous article13 rules classify field05 only after their own facts and evidence are determined', () => {
|
|
assert.equal(directExportTreatments.length, 5);
|
|
for (const rule of directExportTreatments) {
|
|
const input = row(rule),
|
|
result = evaluateSalesOperation(input, 'MZN');
|
|
assert.equal(result.field, '05');
|
|
assert.equal(result.net, '100.00');
|
|
assert.equal(result.vat, '0.00');
|
|
assert.equal(result.gross, '100.00');
|
|
assert.match(result.source, /13\(1\)/);
|
|
for (const missing of input.confirmed) {
|
|
const r = evaluateSalesOperation(
|
|
{
|
|
...input,
|
|
confirmed: input.confirmed.filter((i) => i !== missing),
|
|
},
|
|
'MZN',
|
|
);
|
|
assert.equal(r.status, 'indeterminate');
|
|
assert.equal(r.vat, null);
|
|
}
|
|
for (const patch of [
|
|
{ amount: '' },
|
|
{ basis: '' },
|
|
{ evidence: '' },
|
|
{ description: '' },
|
|
{ establishment: '' },
|
|
{ sourceCurrency: 'USD' },
|
|
{ period: '2025-11' },
|
|
])
|
|
assert.equal(
|
|
evaluateSalesOperation({ ...input, ...patch }, 'MZN').vat,
|
|
null,
|
|
);
|
|
assert.equal(
|
|
evaluateSalesOperation({ ...input, amount: '0' }, 'MZN').net,
|
|
'0.00',
|
|
);
|
|
}
|
|
assert.match(
|
|
directExportTreatments[0].conditions[1],
|
|
/Caminhos de Ferro de Moçambique/,
|
|
);
|
|
assert.match(directExportTreatments[1].conditions[2], /nacionalidade/);
|
|
assert.match(directExportTreatments[2].conditions[3], /não estabelecido/);
|
|
assert.match(
|
|
directExportTreatments[3].conditions[2],
|
|
/Banco de Moçambique/,
|
|
);
|
|
assert.match(directExportTreatments[4].conditions[2], /afectação/);
|
|
});
|
|
void test('missing evidence leaves period indeterminate and recalculation preserves the previous result', () => {
|
|
const { draft, book } = fixture();
|
|
const next = recordSales(draft, {
|
|
...book,
|
|
rows: book.rows.map((r, i) => (i === 2 ? { ...r, evidence: '' } : r)),
|
|
});
|
|
assert.equal(next.result.periods[0].fields['05'], null);
|
|
assert.deepEqual(next.history.at(-1), book.result);
|
|
assert.equal(book.result.periods[0].fields['05'], '500.00');
|
|
assert.equal(book.result.periods[0].fields['01'], '0.00');
|
|
});
|
|
void test('snapshot/report retain context and sources while all14 delivered books stay byte-equivalent and current', () => {
|
|
const { draft, book } = fixture();
|
|
assert.equal(salesCurrent(draft, book), true);
|
|
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, 14);
|
|
for (const [context, original] of Object.entries(before)) {
|
|
assert.deepEqual(after[context], original);
|
|
assert.equal(
|
|
salesCurrent(
|
|
{ ...draft, context_opportunity: context },
|
|
after[context],
|
|
),
|
|
true,
|
|
);
|
|
}
|
|
const model = createProjectDossierReport(draft, {
|
|
selectedSections: new Set(['finance']),
|
|
version: 'QA G4A',
|
|
});
|
|
for (const value of [
|
|
'500.00',
|
|
'opportunity:323',
|
|
'Manica',
|
|
'2026-11',
|
|
'MZN',
|
|
'13(1)(o)',
|
|
'13(1)(p)',
|
|
'13(1)(q)',
|
|
'13(1)(r)',
|
|
'13(1)(t)',
|
|
])
|
|
assert.ok(JSON.stringify(model).includes(value), value);
|
|
if (process.env.IVA_G4A_CALCULATED)
|
|
fs.writeFileSync(process.env.IVA_G4A_CALCULATED, JSON.stringify(draft));
|
|
});
|