240 lines
7.7 KiB
JavaScript
240 lines
7.7 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import {
|
|
newReturnee,
|
|
returneeConditions,
|
|
returneeTreatments,
|
|
evaluateReturnee,
|
|
recordReturnees,
|
|
saveReturneeBook,
|
|
returneeBook,
|
|
returneeCurrent,
|
|
returneeReportRows,
|
|
} from '../../resources/js/lib/iva-returnees.ts';
|
|
const frozen = JSON.parse(
|
|
fs.readFileSync(
|
|
'tests/frontend/fixtures/iva-returnees-preserved.json',
|
|
'utf8',
|
|
),
|
|
),
|
|
prior = JSON.parse(frozen.modelo_a_snapshot),
|
|
draft = {
|
|
...frozen,
|
|
context_opportunity: 'opportunity:363',
|
|
project_title: 'SYNTHETIC QA — Regresso definitivo / Manica',
|
|
};
|
|
function row(treatment, id = treatment) {
|
|
return {
|
|
...newReturnee(id, 'MZN'),
|
|
treatment,
|
|
description: 'SYNTHETIC QA veículo ' + id,
|
|
beneficiary: 'SYNTHETIC QA beneficiário ' + id,
|
|
period: '2026-10',
|
|
origin: 'projected',
|
|
scope: 'SYNTHETIC QA veículo/operação/período',
|
|
evidence: 'SYNTHETIC QA pressupostos expressos',
|
|
documentRef: 'SYNTHETIC QA propriedade/residência/declaração uso',
|
|
arrival: '2026-09-01',
|
|
request: '2026-09-20',
|
|
stayStart: '2024-09-01',
|
|
stayEnd: '2026-08-31',
|
|
ownershipAssessment: '2026-09-01',
|
|
ownedSince: treatment === 'owned' ? '2025-01-01' : '2026-08-01',
|
|
importDate: '2026-10-10',
|
|
previous: 'none',
|
|
historyRef: 'SYNTHETIC QA histórico completo incluindo outros dossiers',
|
|
decisionRef: 'SYNTHETIC QA pressuposto de substituição63(3)b',
|
|
normalVat: '160.123456',
|
|
normalVatRef: 'SYNTHETIC QA IVA normal informado',
|
|
conditions: {
|
|
...Object.fromEntries(returneeConditions.map(([k]) => [k, 'yes'])),
|
|
substitution: 'yes',
|
|
},
|
|
};
|
|
}
|
|
const rows = returneeTreatments
|
|
.filter((t) => ['owned', 'recent', 'separate'].includes(t.id))
|
|
.map((t) => row(t.id));
|
|
for (const r of rows)
|
|
void test(
|
|
'P058 veículo ' + r.treatment + ' sem dedução nem taxa presumida',
|
|
() => {
|
|
const v = evaluateReturnee(r, 'MZN');
|
|
assert.equal(
|
|
v.status,
|
|
r.treatment === 'owned' ? 'exempt' : 'reduced',
|
|
);
|
|
assert.equal(
|
|
v.importVat,
|
|
{ owned: '0.00', recent: '32.0246912', separate: '80.061728' }[
|
|
r.treatment
|
|
],
|
|
);
|
|
assert.equal(v.deduction, null);
|
|
},
|
|
);
|
|
void test('P058 desconhecido,âmbito e condições recusadas conservam distinção', () => {
|
|
const r = rows[0];
|
|
for (const patch of [
|
|
{ period: '2025-10' },
|
|
{ currency: 'USD' },
|
|
{ importDate: '2026-02-30' },
|
|
{ description: '' },
|
|
{ documentRef: '' },
|
|
{ historyRef: '' },
|
|
{ scope: '' },
|
|
{ evidence: '' },
|
|
{ previous: '' },
|
|
{ conditions: { ...r.conditions, national: '' } },
|
|
])
|
|
assert.equal(
|
|
evaluateReturnee({ ...r, ...patch }, 'MZN').status,
|
|
'indeterminate',
|
|
);
|
|
for (const [k] of returneeConditions)
|
|
assert.equal(
|
|
evaluateReturnee(
|
|
{ ...r, conditions: { ...r.conditions, [k]: 'no' } },
|
|
'MZN',
|
|
).status,
|
|
'not_applicable',
|
|
);
|
|
});
|
|
void test('P058 prazo60/90dias e excepções referenciadas distintas do regime mineiros', () => {
|
|
const r = rows[0];
|
|
assert.equal(
|
|
evaluateReturnee({ ...r, request: '2026-10-31' }, 'MZN').status,
|
|
'exempt',
|
|
);
|
|
assert.equal(
|
|
evaluateReturnee({ ...r, request: '2026-11-01' }, 'MZN').status,
|
|
'indeterminate',
|
|
);
|
|
const extended = {
|
|
...r,
|
|
request: '2026-11-30',
|
|
extensionRef: 'SYNTHETIC QA prazo concedido',
|
|
conditions: { ...r.conditions, extension: 'yes' },
|
|
};
|
|
assert.equal(evaluateReturnee(extended, 'MZN').status, 'exempt');
|
|
assert.equal(
|
|
evaluateReturnee({ ...extended, request: '2026-12-01' }, 'MZN').status,
|
|
'indeterminate',
|
|
);
|
|
assert.equal(
|
|
evaluateReturnee({ ...extended, extensionRef: '' }, 'MZN').status,
|
|
'indeterminate',
|
|
);
|
|
const sep = rows[2];
|
|
const exceptional = {
|
|
...sep,
|
|
stayStart: '2026-02-01',
|
|
exceptionRef: 'SYNTHETIC QA Ministro/âmbito',
|
|
conditions: { ...sep.conditions, exception: 'yes' },
|
|
};
|
|
assert.equal(evaluateReturnee(exceptional, 'MZN').status, 'reduced');
|
|
assert.equal(
|
|
evaluateReturnee({ ...exceptional, exceptionRef: '' }, 'MZN').status,
|
|
'indeterminate',
|
|
);
|
|
assert.equal(
|
|
evaluateReturnee({ ...sep, decisionRef: '' }, 'MZN').status,
|
|
'indeterminate',
|
|
);
|
|
assert.equal(
|
|
evaluateReturnee({ ...r, stayStart: '2025-08-31' }, 'MZN').status,
|
|
'indeterminate',
|
|
);
|
|
});
|
|
void test('P058 propriedade180dias sem preencher lacuna e redução80% não é taxa deIVA', () => {
|
|
const r = rows[0];
|
|
const d = new Date(r.arrival + 'T00:00:00Z');
|
|
d.setUTCDate(d.getUTCDate() - 180);
|
|
assert.equal(
|
|
evaluateReturnee(
|
|
{ ...r, ownedSince: d.toISOString().slice(0, 10) },
|
|
'MZN',
|
|
).status,
|
|
'indeterminate',
|
|
);
|
|
assert.equal(
|
|
evaluateReturnee({ ...r, ownedSince: '2026-08-01' }, 'MZN').status,
|
|
'not_applicable',
|
|
);
|
|
assert.equal(
|
|
evaluateReturnee({ ...rows[1], normalVat: '0.000001' }, 'MZN')
|
|
.importVat,
|
|
'0.0000002',
|
|
);
|
|
assert.equal(
|
|
evaluateReturnee({ ...rows[1], normalVatRef: '' }, 'MZN').status,
|
|
'indeterminate',
|
|
);
|
|
});
|
|
void test('P058 histórico global e um veículo em cinco anos;sem dupla utilização', () => {
|
|
const r = rows[0];
|
|
assert.equal(
|
|
evaluateReturnee(
|
|
{ ...r, previous: 'known', previousDate: '2021-10-10' },
|
|
'MZN',
|
|
).status,
|
|
'exempt',
|
|
);
|
|
assert.equal(
|
|
evaluateReturnee(
|
|
{ ...r, previous: 'known', previousDate: '2021-10-11' },
|
|
'MZN',
|
|
).status,
|
|
'indeterminate',
|
|
);
|
|
const b = recordReturnees(draft, {
|
|
rows: [r, { ...r, id: 'duplicate', treatment: 'separate' }],
|
|
});
|
|
assert.deepEqual(
|
|
b.result.rows.map((r) => r.status),
|
|
['indeterminate', 'indeterminate'],
|
|
);
|
|
assert.equal(b.rows.length, 2);
|
|
});
|
|
void test('P058 gravação/histórico/recuperação/relatório conservam127fechos e limitações', () => {
|
|
let book = recordReturnees(draft, { rows });
|
|
assert.equal(returneeCurrent(draft, book), true);
|
|
const changed = {
|
|
...book,
|
|
rows: book.rows.map((r, i) =>
|
|
i ? r : { ...r, scope: 'SYNTHETIC QA âmbito revisto' },
|
|
),
|
|
};
|
|
assert.equal(returneeCurrent(draft, changed), false);
|
|
book = recordReturnees(draft, changed);
|
|
draft.modelo_a_snapshot = saveReturneeBook(draft, book);
|
|
assert.deepEqual(returneeBook(draft), book);
|
|
assert.equal(book.history.length, 1);
|
|
assert.equal(
|
|
returneeBook({ ...draft, context_opportunity: 'opportunity:999' }).rows
|
|
.length,
|
|
0,
|
|
);
|
|
assert.deepEqual(
|
|
prior,
|
|
Object.fromEntries(
|
|
Object.entries(JSON.parse(draft.modelo_a_snapshot)).filter(
|
|
([k]) => k !== 'returneeImports',
|
|
),
|
|
),
|
|
);
|
|
assert.throws(() =>
|
|
returneeBook({
|
|
...draft,
|
|
modelo_a_snapshot: '{"version":1,"books":{},"returneeImports":[]}',
|
|
}),
|
|
);
|
|
assert.match(returneeReportRows(draft)[0][3], /versão2/);
|
|
if (process.env.IVA_RETURNEE_FIXTURE)
|
|
fs.writeFileSync(
|
|
'tests/frontend/fixtures/iva-returnees.json',
|
|
JSON.stringify(draft),
|
|
);
|
|
});
|