import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import { offerPreparationBook, saveOfferPreparationBook, recordOfferRequest, emptyOfferRequest, offerScenarios, offerPathReport, recordOfferPreparations, } from '../../resources/js/lib/iva-offer-preparations.ts'; import { createProjectDossierReport } from '../../resources/js/lib/project-dossier-report.ts'; const frozen = JSON.parse( fs.readFileSync( new URL('./fixtures/iva-g6e-inputs.json', import.meta.url), 'utf8', ), ); const draft = () => ({ ...structuredClone(frozen), context_opportunity: 'opportunity:334', project_title: 'SYNTHETIC QA — Offer paths', }); const row84 = () => ({ ...offerPreparationBook(draft()).rows[0], normalVat: '800', normalVatSource: 'SYNTHETIC QA pressuposto expresso, não taxa inferida', conditionalAssumption: 'yes', }); void test('presented request never grants benefit;normal amount requires own source and conditional scenario explicit material assumptions', () => { const row = { ...row84(), request: { ...emptyOfferRequest(), state: 'presented', reference: 'SYNTHETIC QA pedido', presentedOn: '2026-11-20', }, }; assert.deepEqual(offerScenarios(row), { normalVat: '800', conditionalVat: '0.00', conditionalReason: offerScenarios(row).conditionalReason, applied: false, }); assert.match( offerScenarios(row).conditionalReason, /Não é benefício concedido/, ); for (const patch of [ { conditionalAssumption: '' }, { goodsSuitable: '' }, { goodsSuitable: 'no' }, { period: '2027-01' }, { currency: 'USD' }, ]) assert.equal(offerScenarios({ ...row, ...patch }).conditionalVat, null); for (const patch of [ { normalVat: '' }, { normalVatSource: '' }, { normalVat: '-10' }, { normalVat: 'desconhecido' }, ]) assert.equal(offerScenarios({ ...row, ...patch }).normalVat, null); assert.match(offerPathReport(row), /Pedido Apresentado/); assert.match(offerPathReport(row), /apresentar pedido não concede isenção/); }); void test('P084 recorded decisions preserve goods/operation/period/conditions and previous decision when current inputs change', () => { let row = { ...row84(), request: { ...emptyOfferRequest(), state: 'granted', decisionRef: 'SYNTHETIC QA decisão parcial', decisionOn: '2026-11-25', goods: 'SYNTHETIC QA só bens A', operation: 'import', periodFrom: '2026-12', periodTo: '2026-12', conditions: 'SYNTHETIC QA exclusivamente entrega ao destinatário A', }, }; row = recordOfferRequest(row); assert.equal(row.requestRecords.length, 1); const accepted = structuredClone(row.requestRecords[0]); row = { ...row, description: 'Bens B posteriores', operation: 'acquisition', period: '2027-01', request: { ...row.request, state: 'denied', decisionRef: 'SYNTHETIC QA decisão B', goods: 'Bens B', periodFrom: '2027-01', periodTo: '2027-01', operation: 'acquisition', conditions: 'SYNTHETIC QA apenas pedido B', }, }; row = recordOfferRequest(row); assert.deepEqual(row.requestRecords[0], accepted); assert.equal(row.requestRecords.length, 2); assert.equal(offerScenarios(row).applied, false); assert.equal(offerScenarios(row).conditionalVat, null); assert.match(offerPathReport(row), /não estender decisão/); for (const key of [ 'goods', 'operation', 'periodFrom', 'periodTo', 'conditions', ]) assert.throws( () => recordOfferRequest({ ...row, request: { ...row.request, [key]: '' }, }), /âmbito da decisão/, ); const exact = recordOfferRequest({ ...row, request: { ...row.request, periodFrom: '2027-01-10', periodTo: '2027-01-15', }, }); assert.equal(exact.requestRecords.at(-1).request.periodFrom, '2027-01-10'); assert.equal(exact.requestRecords.at(-1).request.periodTo, '2027-01-15'); assert.throws( () => recordOfferRequest({ ...row, request: { ...row.request, periodFrom: '2027-02-30', periodTo: '2027-03-01', }, }), /âmbito da decisão/, ); const literal = recordOfferRequest({ ...row, request: { ...row.request, operation: '', operationScope: 'SYNTHETIC QA aquisição e importação dos bens A', periodFrom: '', periodTo: '', periodScope: 'SYNTHETIC QA desde a decisão até revogação, sem termo', }, }); assert.match(offerPathReport(literal), /até revogação, sem termo/); assert.equal( literal.requestRecords.at(-1).request.operationScope, 'SYNTHETIC QA aquisição e importação dos bens A', ); assert.equal(row.requestRecords[0].request.operation, 'import'); assert.equal(row.operation, 'acquisition'); }); void test('P085 humanitarian purpose/emergency/documents are distinct;missing inputs stay indeterminate and operational states may now be recorded under Tempo25/10', () => { const base = { ...offerPreparationBook(draft()).rows[1], period: '2026-12', beneficiary: 'SYNTHETIC QA destinatário', purpose: 'SYNTHETIC QA atenuar cheias', humanitarianPurpose: 'SYNTHETIC QA água e alimentação para deslocados', availableDocuments: 'SYNTHETIC QA lista de bens\nSYNTHETIC QA guia disponível', normalVat: '160', normalVatSource: 'SYNTHETIC QA previsão explícita', conditionalAssumption: 'yes', }; assert.equal(offerScenarios(base).conditionalVat, '0.00'); assert.equal( offerScenarios({ ...base, humanitarianPurpose: '' }).conditionalVat, null, ); assert.equal( offerScenarios({ ...base, mitigation: 'no' }).conditionalVat, null, ); assert.equal( recordOfferRequest(base).requestRecords.at(-1).request.state, 'preparation', ); assert.match(offerPathReport(base), /finalidade humanitária/); assert.match( offerPathReport(base), /procedimento de emergência\/doação por corresponder/, ); assert.equal(base.request, undefined); }); void test('save/recover/report retain old drafts/histories and all27fiscalbooks;normal and conditional are labelled and field11 stays unchanged', () => { const d = draft(), prior = JSON.parse(frozen.modelo_a_snapshot), old = offerPreparationBook(d); const priorDecision = recordOfferRequest({ ...row84(), request: { ...emptyOfferRequest(), state: 'granted', decisionRef: 'SYNTHETIC QA decisão parcial anterior', goods: 'SYNTHETIC QA apenas bens A', operation: 'import', periodFrom: '2026-11', periodTo: '2026-11', conditions: 'SYNTHETIC QA só a operação anterior A', }, }); const p84 = recordOfferRequest({ ...priorDecision, request: { ...emptyOfferRequest(), state: 'presented', reference: 'SYNTHETIC QA pedido em apreciação', }, }); const p85 = { ...old.rows[1], period: '2026-12', beneficiary: 'SYNTHETIC QA destinatário', purpose: 'SYNTHETIC QA resposta humanitária', humanitarianPurpose: 'SYNTHETIC QA assistência imediata às vítimas das cheias', availableDocuments: 'SYNTHETIC QA bens e destinatário', normalVat: '160', normalVatSource: 'SYNTHETIC QA previsão sem taxa inferida', conditionalAssumption: 'yes', }; const book = recordOfferPreparations({ ...old, rows: [p84, p85] }); assert.deepEqual(book.history.at(-1), old.saved); d.modelo_a_snapshot = saveOfferPreparationBook(d, book); assert.deepEqual(offerPreparationBook(JSON.parse(JSON.stringify(d))), book); const next = JSON.parse(d.modelo_a_snapshot); for (const key of ['books', 'salesOperations', 'importExemptions']) assert.deepEqual(next[key], prior[key]); assert.equal( next.books['opportunity:329'].result.periods[0].fields['11'], null, ); const report = JSON.stringify( createProjectDossierReport(d, { selectedSections: new Set(['finance']), version: 'QA offer paths', }), ); for (const token of [ 'Pedido Apresentado', 'Cenário normal', 'Cenário condicionado à isenção', 'hipótese', 'finalidade humanitária', 'benefício não aplicado', '800', '160', 'apresentar pedido não concede isenção', 'SYNTHETIC QA decisão parcial anterior', 'SYNTHETIC QA apenas bens A', '2026-11', ]) assert.ok(report.includes(token), token); if (process.env.IVA_OFFER_PATHS) fs.writeFileSync(process.env.IVA_OFFER_PATHS, JSON.stringify(d)); });