Files
Investidor-V2/tests/frontend/iva-returnee-baggage.test.mjs
T

234 lines
8.3 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import {
baggageTreatments,
baggageConditions,
} from '../../resources/js/lib/iva-returnee-baggage.ts';
import {
newReturnee,
evaluateReturnee,
recordReturnees,
saveReturneeBook,
returneeBook,
returneeCurrent,
returneeReportRows,
} from '../../resources/js/lib/iva-returnees.ts';
const frozen = JSON.parse(
fs.readFileSync(
'tests/frontend/fixtures/iva-baggage-preserved.json',
'utf8',
),
),
prior = JSON.parse(frozen.modelo_a_snapshot),
draft = {
...frozen,
context_opportunity: 'opportunity:366',
project_title: 'SYNTHETIC QA — Bagagem no regresso / Manica',
};
function row(treatment, id = treatment, patch = {}) {
return {
...newReturnee(id, 'MZN'),
treatment,
description: 'SYNTHETIC QA bagagem ' + id,
beneficiary: 'SYNTHETIC QA viajante ' + id,
period: '2026-10',
origin: 'projected',
scope: 'SYNTHETIC QA bens/operação/período',
evidence: 'SYNTHETIC QA factos/pressupostos expressos',
documentRef: 'SYNTHETIC QA documentos disponíveis',
missionRef: 'SYNTHETIC QA referência de missão pública/estudo',
importDate: '2026-10-10',
arrival: '2026-10-10',
stayStart: '2024-01-01',
stayEnd: '2026-10-09',
missionPlannedEnd: '2027-01-01',
baggageKind: 'personal',
baggageRoute: 'accompanied',
earlyReturnRef: 'SYNTHETIC QA regresso por serviço doEstado',
conditions: {
...Object.fromEntries(baggageConditions.map(([k]) => [k, 'yes'])),
earlyService: 'yes',
},
...patch,
};
}
const rows = [];
for (const treatment of baggageTreatments)
for (const kind of ['personal', 'household']) {
const r = row(treatment.id, treatment.id + '-' + kind, {
baggageKind: kind,
});
rows.push(r);
void test(
treatment.id +
' / ' +
kind +
' sem condições de veículo transplantadas',
() => {
const result = evaluateReturnee(r, 'MZN');
assert.equal(result.status, 'exempt');
assert.equal(result.importVat, '0.00');
assert.equal(result.deduction, null);
assert.equal(r.conditions.national, undefined);
assert.equal(r.previous, '');
assert.equal(r.request, '');
assert.equal(r.historyRef, '');
assert.match(result.source, /57\(2\)c\/d/);
assert.match(result.reason, /57\(4\).*não dispensa/);
},
);
}
void test('P058 bagagem não acompanhada61:180dias próprios,sem60+30deveículos e sem quota5anos', () => {
const r = row('student_baggage', 'unaccompanied', {
arrival: '2026-04-13',
stayEnd: '2026-04-12',
baggageRoute: 'unaccompanied',
});
assert.equal(evaluateReturnee(r, 'MZN').status, 'exempt');
const late = { ...r, arrival: '2026-04-12', stayEnd: '2026-04-11' };
assert.equal(evaluateReturnee(late, 'MZN').status, 'indeterminate');
const advance = { ...r, arrival: '2026-10-20', stayEnd: '2026-10-09' };
assert.equal(evaluateReturnee(advance, 'MZN').status, 'indeterminate');
const authorised = {
...advance,
id: 'advance-authorised',
earlyBaggageRef: 'SYNTHETIC QA estância/âmbito61(2)',
conditions: { ...advance.conditions, advanceClearance: 'yes' },
};
assert.equal(evaluateReturnee(authorised, 'MZN').status, 'exempt');
assert.equal(
evaluateReturnee({ ...authorised, earlyBaggageRef: '' }, 'MZN').status,
'indeterminate',
);
rows.push(r, authorised);
});
void test('P058 missão superior a um ano e regresso antecipado pelo Estado são percursos distintos', () => {
const r = row('civil_baggage');
assert.equal(
evaluateReturnee({ ...r, stayStart: '2025-10-09' }, 'MZN').status,
'not_applicable',
);
assert.equal(
evaluateReturnee({ ...r, stayStart: '2025-10-08' }, 'MZN').status,
'exempt',
);
const early = row('early_state_baggage', 'early-short', {
stayStart: '2026-02-01',
missionPlannedEnd: '2028-02-01',
});
assert.equal(evaluateReturnee(early, 'MZN').status, 'exempt');
assert.equal(
evaluateReturnee(
{
...early,
conditions: { ...early.conditions, earlyService: 'no' },
},
'MZN',
).status,
'not_applicable',
);
for (const patch of [
{ missionPlannedEnd: '2027-02-01' },
{ missionPlannedEnd: '2026-09-01' },
{ earlyReturnRef: '' },
{ conditions: { ...early.conditions, earlyService: '' } },
])
assert.notEqual(
evaluateReturnee({ ...early, ...patch }, 'MZN').status,
'exempt',
);
rows.push(early);
});
void test('P058 bagagem desconhecida/comercial/fora57(3) não vira IVAzero;57(2)b permanece por corresponder', () => {
const r = rows[0];
for (const patch of [
{ missionRef: '' },
{ baggageKind: '' },
{ baggageKind: 'vehicle' },
{ baggageRoute: '' },
{ currency: 'USD' },
{ period: '2027-10' },
{ importDate: '2026-02-30' },
{ documentRef: '' },
{ conditions: { ...r.conditions, mission: '' } },
]) {
const v = evaluateReturnee({ ...r, ...patch }, 'MZN');
assert.equal(v.status, 'indeterminate');
assert.equal(v.importVat, null);
}
for (const [k] of baggageConditions)
assert.equal(
evaluateReturnee(
{ ...r, conditions: { ...r.conditions, [k]: 'no' } },
'MZN',
).status,
'not_applicable',
);
assert.equal(
evaluateReturnee({ ...r, treatment: 'domicile_baggage' }, 'MZN').status,
'indeterminate',
);
});
void test('P058 bagagem não consome quota de veículo;linhas da mesma bagagem não são duplicação do direito de veículo', () => {
const first = rows[0],
second = {
...first,
id: 'same-person-household',
baggageKind: 'household',
};
assert.deepEqual(
recordReturnees(draft, { rows: [first, second] }).result.rows.map(
(r) => r.status,
),
['exempt', 'exempt'],
);
});
void test('P058 gravação/recuperação/histórico/relatório preservam128fechos e anteriores livrosP058/P104/ofertas', () => {
let book = recordReturnees(draft, { rows });
assert.equal(returneeCurrent(draft, book), true);
const before = structuredClone(book.result),
changed = {
...book,
rows: book.rows.map((r, i) =>
i ? r : { ...r, scope: 'SYNTHETIC QA âmbito revisto' },
),
};
assert.equal(returneeCurrent(draft, changed), false);
assert.deepEqual(book.result, before);
book = recordReturnees(draft, changed);
draft.modelo_a_snapshot = saveReturneeBook(draft, book);
assert.deepEqual(returneeBook(draft), book);
assert.deepEqual(book.history[0], before);
assert.equal(
returneeBook({ ...draft, context_opportunity: 'opportunity:999' }).rows
.length,
0,
);
const next = JSON.parse(draft.modelo_a_snapshot);
assert.deepEqual(
Object.fromEntries(
Object.entries(prior).filter(([k]) => k !== 'returneeImports'),
),
Object.fromEntries(
Object.entries(next).filter(([k]) => k !== 'returneeImports'),
),
);
for (const [k, v] of Object.entries(prior.returneeImports ?? {}))
assert.deepEqual(next.returneeImports[k], v);
assert.match(returneeReportRows(draft)[0][3], /missão:SYNTHETIC QA/);
assert.match(returneeReportRows(draft)[0][3], /versão2/);
assert.throws(
() =>
saveReturneeBook(draft, {
rows: [{ ...rows[0], missionRef: { invalid: true } }],
}),
/não recuperáveis/,
);
if (process.env.IVA_BAGGAGE_FIXTURE)
fs.writeFileSync(
'tests/frontend/fixtures/iva-baggage.json',
JSON.stringify(draft),
);
});