import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import { newOfferPreparation, emptyOfferRequest, saveOfferPreparationBook, offerPreparationBook, offerScenarios, offerPathReport, } from '../../resources/js/lib/iva-offer-preparations.ts'; import { emptyOfferWorkflow, recordOfferWorkflow, offerWorkflowApplied, offerWrittenRequest, } from '../../resources/js/lib/iva-offer-workflow.ts'; const frozen = JSON.parse( fs.readFileSync('tests/frontend/fixtures/iva-returnees.json', 'utf8'), ), draft = { ...frozen, context_opportunity: 'opportunity:364', project_title: 'SYNTHETIC QA — Pedidos ofertas / Manica', }, prior = JSON.parse(frozen.modelo_a_snapshot); const attachment = { id: '00000000-0000-4000-8000-000000000001', draftId: '00000000-0000-4000-8000-000000000002', name: 'SYNTHETIC-QA-despacho.txt', sha256: 'a'.repeat(64), scan: 'pending', url: '/api/v1/simulation-drafts/00000000-0000-4000-8000-000000000002/offer-documents/00000000-0000-4000-8000-000000000001', }; function initial(branch) { return { ...newOfferPreparation(branch, branch, 'MZN'), operation: 'acquisition', description: 'SYNTHETIC QA bensA', period: '2026-10', amount: '1000', beneficiary: 'SYNTHETIC QA entidade', purpose: branch === 'P084' ? 'SYNTHETIC QA interesse público/fins sociais' : 'SYNTHETIC QA atenuar cheias', evidence: 'SYNTHETIC QA justificação', nationalInstitution: 'yes', publicSocialInstitution: 'yes', goodsSuitable: 'yes', publicInterestUse: 'yes', mitigation: 'yes', disaster: 'SYNTHETIC QA cheias', humanitarianPurpose: 'SYNTHETIC QA assistência humanitária', normalVat: '160', normalVatSource: 'SYNTHETIC QA hipótese', conditionalAssumption: 'yes', request: emptyOfferRequest(), workflow: emptyOfferWorkflow(), }; } const completed = []; for (const branch of ['P084', 'P085']) void test( branch + ' pedido → apresentação externa → decisão anexa → formulário com despacho', () => { let row = recordOfferWorkflow(initial(branch), 'request'); assert.equal(offerWorkflowApplied(row).applied, false); assert.match( offerWrittenRequest(row, row.workflow), /apresentação ocorre fora/, ); row = { ...row, request: { ...row.request, state: 'presented', reference: 'SYNTHETIC QA pedido1', presentedOn: '2026-10-01', }, }; row = recordOfferWorkflow(row, 'presented'); assert.equal(offerScenarios(row).applied, false); assert.equal(offerScenarios(row).conditionalVat, '0.00'); row = { ...row, request: { ...row.request, state: 'granted', decisionRef: 'SYNTHETIC QA despacho1', decisionOn: '2026-10-03', goods: 'SYNTHETIC QA apenas bensA', operation: 'acquisition', periodFrom: '2026-10', periodTo: '2026-10', conditions: 'SYNTHETIC QA só operaçãoA', }, workflow: { ...row.workflow, decisionIssuer: 'SYNTHETIC QA MF/AT', decisionAttachment: attachment, scopeConfirmed: 'yes', }, }; row = recordOfferWorkflow(row, 'decision'); assert.equal(offerScenarios(row).applied, true); assert.throws( () => recordOfferWorkflow( { ...row, description: 'outros bens' }, 'form', ), /explicitamente/, ); const outside = recordOfferWorkflow( { ...row, request: { ...row.request, periodFrom: '2027-01', periodTo: '2027-01', }, }, 'decision', ); assert.equal(offerWorkflowApplied(outside).applied, false); assert.equal(offerScenarios(row).appliedVat, '0.00'); assert.equal( offerWorkflowApplied({ ...row, description: 'outros bens' }) .applied, false, ); assert.equal( offerWorkflowApplied({ ...row, period: '2026-11' }).applied, false, ); assert.equal( offerWorkflowApplied({ ...row, beneficiary: 'outra entidade' }) .applied, false, ); assert.equal( offerWorkflowApplied({ ...row, request: { ...row.request, state: 'denied' }, }).applied, false, ); row = { ...row, workflow: { ...row.workflow, formName: 'SYNTHETIC QA formulário aplicável', formSource: 'SYNTHETIC QA correspondência documentada apenas operaçãoA', formContent: offerWrittenRequest(row, row.workflow), }, }; row = recordOfferWorkflow(row, 'form'); assert.equal(row.workflow.formDecisionId, attachment.id); assert.equal(row.workflow.formDecisionHash, attachment.sha256); assert.deepEqual( row.workflow.records.map((r) => r.stage), ['request', 'presented', 'decision', 'form'], ); assert.match( offerPathReport(row), /Procedimento operacional fornecido pela Tempo/, ); assert.match(offerPathReport(row), /submissão fora/); assert.match(offerPathReport(row), /SYNTHETIC QA pedido1/); assert.match(offerPathReport(row), /2026-10-01/); completed.push(row); }, ); void test('decisão e formulário incompletos não bloqueiam guardar rascunho nem inventam concessão', () => { const row = initial('P085'); assert.throws( () => recordOfferWorkflow( { ...row, request: { ...row.request, state: 'presented' } }, 'presented', ), /referência/, ); assert.throws( () => recordOfferWorkflow( { ...row, request: { ...row.request, state: 'granted' } }, 'decision', ), /número/, ); const denied = { ...completed[0], request: { ...completed[0].request, state: 'denied' }, }; assert.throws(() => recordOfferWorkflow(denied, 'form'), /indeferimento/); for (const patch of [ { decisionIssuer: '' }, { decisionAttachment: undefined }, { scopeConfirmed: 'no' }, ]) assert.equal( offerWorkflowApplied({ ...completed[0], workflow: { ...completed[0].workflow, ...patch }, }).applied, false, ); const value = saveOfferPreparationBook(draft, { rows: [row] }); assert.equal( offerPreparationBook({ ...draft, modelo_a_snapshot: value }).rows[0] .workflow.records.length, 0, ); }); void test('127 fechos e P058 parcial conservados;histórico e ambos percursos recuperáveis', () => { const old = prior.offerPreparations ?? {}; const value = saveOfferPreparationBook(draft, { rows: completed }); draft.modelo_a_snapshot = value; const next = JSON.parse(value); assert.deepEqual( Object.fromEntries( Object.entries(next).filter(([k]) => k !== 'offerPreparations'), ), Object.fromEntries( Object.entries(prior).filter(([k]) => k !== 'offerPreparations'), ), ); for (const [k, v] of Object.entries(old)) assert.deepEqual(next.offerPreparations[k], v); assert.equal(offerPreparationBook(draft).rows.length, 2); assert.deepEqual(next.returneeImports, prior.returneeImports); if (process.env.IVA_OFFER_WORKFLOW_FIXTURE) fs.writeFileSync( 'tests/frontend/fixtures/iva-offer-workflow.json', JSON.stringify(draft), ); });