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), ); });