242 lines
8.7 KiB
JavaScript
242 lines
8.7 KiB
JavaScript
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),
|
|
);
|
|
});
|