271 lines
9.4 KiB
JavaScript
271 lines
9.4 KiB
JavaScript
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));
|
|
});
|