217 lines
7.5 KiB
JavaScript
217 lines
7.5 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import {
|
|
specialRegimeTreatments as rules,
|
|
specialRegimeOptions,
|
|
specialRegimesFor,
|
|
specialRegimeIssue,
|
|
} from '../../resources/js/lib/iva-special-regimes.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-g4e-inputs.json', import.meta.url),
|
|
'utf8',
|
|
),
|
|
);
|
|
const regimes = [
|
|
'economic_zone',
|
|
'customs_warehouse',
|
|
'free_zone',
|
|
'duty_free_shop',
|
|
'transit',
|
|
'temporary_import',
|
|
];
|
|
const row = (rule, i) => ({
|
|
...newSalesOperation(`g5a-${i}`, '2026-12', 'MZN'),
|
|
description: `SYNTHETIC QA — ${rule.label}`,
|
|
establishment: 'SYNTHETIC QA estabelecimento',
|
|
amount: '100',
|
|
basis: i % 2 ? 'included' : 'net',
|
|
treatment: rule.id,
|
|
specialRegime: regimes[i],
|
|
confirmed: rule.conditions.map((_, n) => n),
|
|
evidence:
|
|
'SYNTHETIC QA — pressuposto expresso de projecção: bens e operação identificados; regime e controlo/documento, destino, permanência no período, ausência de utilização/consumo final e ligação directa do serviço/transporte identificados. Cada linha tem preço próprio, sem duplicar bens. Sem certificação real.',
|
|
});
|
|
function fixture() {
|
|
const draft = {
|
|
...structuredClone(frozen),
|
|
context_opportunity: 'opportunity:328',
|
|
project_title: 'SYNTHETIC QA — G5A / Regimes especiais',
|
|
};
|
|
const book = recordSales(draft, {
|
|
currency: 'MZN',
|
|
rows: rules.map(row),
|
|
periods: [{ period: '2026-12', complete: true }],
|
|
});
|
|
draft.modelo_a_snapshot = saveSalesBook(draft, book);
|
|
return { draft, book };
|
|
}
|
|
void test('three branches/six goods-service treatments require own conditions,no final use and identified regime', () => {
|
|
assert.equal(rules.length, 6);
|
|
for (const [i, rule] of rules.entries()) {
|
|
const input = row(rule, i),
|
|
result = evaluateSalesOperation(input, 'MZN');
|
|
assert.equal(result.field, '05');
|
|
assert.equal(result.vat, '0.00');
|
|
assert.equal(result.net, '100.00');
|
|
assert.equal(result.gross, '100.00');
|
|
assert.match(result.article, /19\(1\)\(b\)\(iv\)/);
|
|
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: '' },
|
|
{ description: '' },
|
|
{ establishment: '' },
|
|
])
|
|
assert.equal(
|
|
evaluateSalesOperation({ ...input, ...patch }, 'MZN').vat,
|
|
null,
|
|
);
|
|
assert.match(
|
|
rule.conditions.join(' '),
|
|
/não têm utilização nem consumo finais/,
|
|
);
|
|
}
|
|
});
|
|
void test('regimes from another paragraph or unknown are indeterminate;destination and service conditions remain distinct', () => {
|
|
for (const [i, rule] of rules.entries()) {
|
|
for (const [regime] of specialRegimeOptions) {
|
|
const result = evaluateSalesOperation(
|
|
{ ...row(rule, i), specialRegime: regime },
|
|
'MZN',
|
|
);
|
|
const allowed = specialRegimesFor(rule.id).some(
|
|
([id]) => id === regime,
|
|
);
|
|
assert.equal(result.vat, allowed ? '0.00' : null);
|
|
if (!allowed) {
|
|
assert.equal(result.net, null);
|
|
assert.equal(result.field, null);
|
|
}
|
|
}
|
|
for (const regime of [undefined, '', null, 'other'])
|
|
assert.equal(
|
|
evaluateSalesOperation(
|
|
{ ...row(rule, i), specialRegime: regime },
|
|
'MZN',
|
|
).vat,
|
|
null,
|
|
);
|
|
}
|
|
assert.match(
|
|
rules[0].conditions.join(' '),
|
|
/loja franca por si não comprova/,
|
|
);
|
|
assert.equal(
|
|
evaluateSalesOperation(
|
|
{
|
|
...row(rules[0], 0),
|
|
specialRegime: 'duty_free_shop',
|
|
confirmed: [0, 1, 3],
|
|
},
|
|
'MZN',
|
|
).vat,
|
|
null,
|
|
);
|
|
assert.equal(
|
|
specialRegimeIssue('regime_internal_goods', 'duty_free_shop'),
|
|
null,
|
|
);
|
|
assert.equal(specialRegimeIssue('general', undefined), null);
|
|
assert.deepEqual(specialRegimesFor('general'), []);
|
|
});
|
|
void test('change of regime or ending eligibility preserves price,context/history and blocks presumptive totals', () => {
|
|
const { draft, book } = fixture();
|
|
assert.equal(book.result.periods[0].fields['05'], '600.00');
|
|
for (const patch of [
|
|
{ specialRegime: 'transit', confirmed: [] },
|
|
{ specialRegime: '', confirmed: [] },
|
|
{ confirmed: [], evidence: 'Regime terminado; rever enquadramento' },
|
|
]) {
|
|
const changed = recordSales(draft, {
|
|
...book,
|
|
rows: book.rows.map((r, i) => (i === 0 ? { ...r, ...patch } : r)),
|
|
});
|
|
assert.equal(changed.rows[0].amount, '100');
|
|
assert.equal(changed.rows[0].sourceCurrency, 'MZN');
|
|
assert.equal(changed.rows[0].period, '2026-12');
|
|
assert.equal(changed.result.periods[0].fields['05'], null);
|
|
assert.deepEqual(changed.history.at(-1), book.result);
|
|
const edited = {
|
|
...draft,
|
|
modelo_a_snapshot: saveSalesBook(draft, {
|
|
...book,
|
|
rows: changed.rows,
|
|
}),
|
|
};
|
|
assert.equal(salesCurrent(edited, salesBook(edited)), false);
|
|
}
|
|
});
|
|
void test('snapshot/report retain regime,source and assumptions;all19 previous books are identical/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, 19);
|
|
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 G5A',
|
|
}),
|
|
);
|
|
for (const token of [
|
|
'opportunity:328',
|
|
'Manica',
|
|
'2026-12',
|
|
'600.00',
|
|
'14(1)(b)',
|
|
'14(1)(c)',
|
|
'14(1)(d)',
|
|
'19(1)(b)(iv)',
|
|
'Regime especial',
|
|
'Importação temporária',
|
|
'Loja franca',
|
|
'pressuposto expresso',
|
|
'QA G5A',
|
|
])
|
|
assert.ok(report.includes(token), token);
|
|
if (process.env.IVA_G5A_CALCULATED)
|
|
fs.writeFileSync(process.env.IVA_G5A_CALCULATED, JSON.stringify(draft));
|
|
});
|