Files
Investidor-V2/tests/frontend/iva-value-review.test.mjs
T

344 lines
11 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import {
newValueReview,
switchValueReview,
switchPriceContext,
switchValueOrigin,
valueReviewTreatments,
priceContexts,
valueReviewConditions,
evaluateValueReview,
recordValueReviews,
saveValueReviewBook,
valueReviewBook,
valueReviewCurrent,
valueReviewReportRows,
} from '../../resources/js/lib/iva-value-review.ts';
import { exclusionCurrent } from '../../resources/js/lib/iva-exclusion-base.ts';
import { compositionCurrent } from '../../resources/js/lib/iva-composition-base.ts';
import { createProjectDossierReport } from '../../resources/js/lib/project-dossier-report.ts';
const frozen = JSON.parse(
fs.readFileSync(
new URL('./fixtures/iva-g7h-preserved-inputs.json', import.meta.url),
'utf8',
),
);
const row = (treatment = 'price_comparison') => ({
...newValueReview('g7h-' + treatment, 'MZN'),
treatment,
priceContext: 'retail',
description: 'SYNTHETIC QA operação A',
period: '2026-12',
operationRef: 'SYNTHETIC QA operação A',
documentRef: 'SYNTHETIC QA G7H ' + treatment,
documentDate: '2026-12-12',
declaredAmount: '1000.25',
referenceAmount: '1500.50',
referenceRef: 'SYNTHETIC QA preço corrente A',
referenceScope:
'SYNTHETIC QA bens A,retalho,dezembro2026,Manica e condições comparáveis',
compositionRef:
'SYNTHETIC QA composição equivalente15(5)/(6),sem próprioIVA',
evidence: 'SYNTHETIC QA pressupostos expressos,não documento real',
correctedAmount: '1400.75',
correctionRef: 'SYNTHETIC QA pressuposto condicionado correcção A',
correctionScope: 'SYNTHETIC QA bens A/operação A/dezembro2026/condições A',
correctionDate: '2026-12-20',
correctionApplies: 'yes',
conditions: Object.fromEntries(
valueReviewConditions.map(([key]) => [key, 'yes']),
),
});
const draft = () => ({
...structuredClone(frozen),
context_opportunity: 'opportunity:346',
project_title: 'SYNTHETIC QA — G7H / Comparação e correcção',
});
void test('P103 contexts and ownconditions/references;below comparison never replaces declaredbase automatically', () => {
for (const context of priceContexts) {
const input = { ...row(), priceContext: context.id };
assert.equal(evaluateValueReview(input, 'MZN').baseAmount, '1000.25');
assert.equal(evaluateValueReview(input, 'MZN').comparison, 'below');
for (const [key] of valueReviewConditions)
for (const value of ['', 'unknown', 'no']) {
const r = evaluateValueReview(
{
...input,
conditions: { ...input.conditions, [key]: value },
},
'MZN',
);
assert.equal(
r.status,
value === 'no' ? 'not_applicable' : 'indeterminate',
);
assert.equal(r.baseAmount, null);
assert.equal(r.vat, null);
}
for (const patch of [
{ description: '' },
{ operationRef: '' },
{ documentRef: '' },
{ documentDate: '2026-02-30' },
{ referenceRef: '' },
{ referenceScope: '' },
{ compositionRef: '' },
{ evidence: '' },
{ period: '2025-12' },
{ currency: 'USD' },
{ priceContext: 'unknown' },
{ treatment: '' },
{ declaredAmount: '' },
{ referenceAmount: '' },
])
assert.equal(
evaluateValueReview({ ...input, ...patch }, 'MZN').baseAmount,
null,
);
}
for (const referenceAmount of ['1000.25', '500.00']) {
const r = evaluateValueReview({ ...row(), referenceAmount }, 'MZN');
assert.equal(r.comparison, 'not_below');
assert.equal(r.baseAmount, '1000.25');
}
});
void test('correction amount/reference/scope required;scenario distinct fromeffective;no maximum/default reference;exactdecimal and switches', () => {
const input = row('referenced_correction'),
ok = evaluateValueReview(input, 'MZN');
assert.equal(ok.baseAmount, '1400.75');
assert.equal(ok.comparison, 'below');
assert.match(ok.reason, /Cenário condicionado/);
assert.equal(ok.vat, null);
assert.equal(ok.rate, null);
assert.match(
evaluateValueReview({ ...input, origin: 'effective' }, 'MZN').reason,
/dados efectivos declarados/,
);
for (const patch of [
{ correctedAmount: '' },
{ correctionRef: '' },
{ correctionScope: '' },
{ correctionDate: '2026-02-30' },
{ correctionApplies: '' },
]) {
const r = evaluateValueReview({ ...input, ...patch }, 'MZN');
assert.equal(r.baseAmount, null);
assert.equal(r.comparison, 'below');
}
assert.equal(
evaluateValueReview({ ...input, correctionApplies: 'no' }, 'MZN')
.status,
'not_applicable',
);
assert.equal(
evaluateValueReview({ ...input, referenceAmount: '500' }, 'MZN').status,
'not_applicable',
);
for (const correctedAmount of ['0', '900.15', '999999999999999.999999'])
assert.equal(
evaluateValueReview({ ...input, correctedAmount }, 'MZN')
.baseAmount,
correctedAmount === '0' ? '0.00' : correctedAmount,
);
for (const correctedAmount of ['-1', 'NaN', '0.0000001'])
assert.equal(
evaluateValueReview({ ...input, correctedAmount }, 'MZN')
.baseAmount,
null,
);
assert.equal(
evaluateValueReview(
{ ...row(), declaredAmount: '0', referenceAmount: '0' },
'MZN',
).baseAmount,
'0.00',
);
assert.equal(
evaluateValueReview(
{
...row(),
declaredAmount: '1.000001',
referenceAmount: '1.000002',
},
'MZN',
).comparison,
'below',
);
for (const changed of [
switchValueReview(input, 'price_comparison'),
switchPriceContext(input, 'wholesale'),
switchValueOrigin(input, 'effective'),
]) {
assert.equal(changed.correctionApplies, '');
for (const field of [
'declaredAmount',
'referenceAmount',
'correctedAmount',
'correctionRef',
'correctionScope',
])
assert.equal(changed[field], input[field]);
}
assert.equal(
evaluateValueReview(switchValueOrigin(input, 'effective'), 'MZN')
.baseAmount,
null,
);
assert.equal(
evaluateValueReview(switchPriceContext(input, 'wholesale'), 'MZN')
.baseAmount,
null,
);
});
void test('save/recovery/history/report preserve all85 delivered books/currentkeys including P102/P101/P095', () => {
const d = draft(),
prior = JSON.parse(d.modelo_a_snapshot);
assert.deepEqual(valueReviewBook(d), { rows: [] });
assert.deepEqual(valueReviewReportRows(d), []);
let book = recordValueReviews(d, {
rows: valueReviewTreatments.map((t) => ({
...row(t.id),
conditions: {},
})),
});
book = recordValueReviews(d, {
...book,
rows: valueReviewTreatments.map((t) => ({
...row(t.id),
conditions: { ...row(t.id).conditions, referenceCorresponds: 'no' },
})),
});
book = recordValueReviews(d, {
...book,
rows: valueReviewTreatments.map((t) => row(t.id)),
});
assert.equal(book.history.length, 2);
assert.ok(
book.history.every((h) =>
h.rows.every((r) => r.baseAmount === null && r.vat === null),
),
);
assert.ok(valueReviewCurrent(d, book));
for (const field of [
'priceContext',
'declaredAmount',
'referenceAmount',
'referenceScope',
'correctedAmount',
'correctionRef',
'correctionScope',
'correctionApplies',
'origin',
])
assert.ok(
!valueReviewCurrent(d, {
...book,
rows: book.rows.map((r) => ({ ...r, [field]: 'changed' })),
}),
);
for (const patch of [
{ context_opportunity: 'opportunity:347' },
{ currency: 'USD' },
{ selection_labels: { province_id: 'Maputo' } },
])
assert.ok(!valueReviewCurrent({ ...d, ...patch }, book));
d.modelo_a_snapshot = saveValueReviewBook(d, book);
assert.deepEqual(valueReviewBook(JSON.parse(JSON.stringify(d))), book);
const next = JSON.parse(d.modelo_a_snapshot);
const { valueReviews: _valueReviews, ...untouched } = next;
assert.deepEqual(untouched, prior);
for (const [scope, previous] of Object.entries(
prior.considerationExclusions,
))
assert.ok(
exclusionCurrent({ ...d, context_opportunity: scope }, previous),
);
for (const [scope, previous] of Object.entries(
prior.considerationCompositions,
))
assert.ok(
compositionCurrent({ ...d, context_opportunity: scope }, previous),
);
assert.equal(
next.considerationExclusions['opportunity:345'].result.rows[0]
.baseAmount,
'799.70',
);
assert.equal(
next.considerationCompositions['opportunity:344'].result.rows[0]
.baseAmount,
'1150.60',
);
assert.equal(
next.regulatedFuelBases['opportunity:342'].result.rows[0].baseAmount,
'1400.15',
);
assert.equal(
next.books['opportunity:329'].result.periods[0].fields['11'],
null,
);
const report = JSON.stringify(
createProjectDossierReport(d, {
selectedSections: new Set(['finance']),
version: 'QA G7H',
}),
);
for (const token of [
'Base P103',
'1000.25',
'1400.75',
'1500.50',
'IVA: Indeterminado',
'opportunity:346',
'Manica',
'2026-12',
'15(7)',
'condicionado',
'correcção A',
'campo11',
])
assert.ok(report.includes(token), token);
assert.deepEqual(
valueReviewReportRows({ ...d, context_opportunity: 'opportunity:347' }),
[],
);
if (process.env.IVA_G7H_CALCULATED)
fs.writeFileSync(process.env.IVA_G7H_CALCULATED, JSON.stringify(d));
});
void test('corrupt review rows/store cannot overwrite originals;duplicateoperationids cannotbe recorded', () => {
const d = draft(),
original = d.modelo_a_snapshot;
for (const patch of [
{ declaredAmount: 4 },
{ conditions: [] },
{ correctionScope: null },
])
assert.throws(
() => saveValueReviewBook(d, { rows: [{ ...row(), ...patch }] }),
/originais preservados/,
);
assert.equal(d.modelo_a_snapshot, original);
assert.throws(
() => recordValueReviews(d, { rows: [row(), row()] }),
/distintos/,
);
assert.throws(() => recordValueReviews(d, { rows: [] }), /distintos/);
assert.throws(
() =>
valueReviewBook({
...d,
modelo_a_snapshot: JSON.stringify({
...JSON.parse(original),
valueReviews: [],
}),
}),
/originais preservados/,
);
});