Files
Investidor-V2/tests/frontend/iva-public-authority-base.test.mjs
T

302 lines
10 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import {
newPublicBase,
publicBaseTreatments,
publicBaseConditions,
evaluatePublicBase,
recordPublicBases,
savePublicBaseBook,
publicBaseBook,
publicBaseCurrent,
publicBaseReportRows,
} from '../../resources/js/lib/iva-public-authority-base.ts';
import { importExemptionCurrent } from '../../resources/js/lib/iva-import-exemptions.ts';
import { createProjectDossierReport } from '../../resources/js/lib/project-dossier-report.ts';
const frozen = JSON.parse(
fs.readFileSync(
new URL('./fixtures/iva-g7a-preserved-inputs.json', import.meta.url),
'utf8',
),
);
const row = (treatment = publicBaseTreatments[0].id) => ({
...newPublicBase('g7a-' + treatment, 'MZN'),
treatment,
description: 'SYNTHETIC QA compensação por acto público',
operationNature:
treatment === 'public_authority_indemnity' ? 'goods' : 'services',
period: '2026-12',
authority: 'SYNTHETIC QA autoridade pública',
actRef: 'SYNTHETIC QA acto A,operação própria',
documentRef: 'SYNTHETIC QA G7A ' + treatment,
documentDate: '2026-11-14',
compensationForm:
treatment === 'public_authority_indemnity' ? 'money' : 'mixed',
moneyAmount:
treatment === 'public_authority_indemnity' ? '1500.25' : '1000.10',
nonMoneyValue: '250.20',
valuationRef:
'SYNTHETIC QA valor normal nos termos15(4),pressuposto explícito',
compositionRef:
'SYNTHETIC QA documento A,composição15(5)/(6),semIVA e sem duplicações',
evidence:
'SYNTHETIC QA pressupostos;operação sujeita e ligada ao acto,não incumprimento contratual judicial',
conditions: Object.fromEntries(
publicBaseConditions.map(([key]) => [key, 'yes']),
),
});
const draft = () => ({
...structuredClone(frozen),
context_opportunity: 'opportunity:339',
project_title: 'SYNTHETIC QA — G7A / Base de actos públicos',
});
void test('P090 bases distinguish indemnity/other compensation from incidence,tax and judicial contract-breach exclusion', () => {
for (const treatment of publicBaseTreatments) {
const input = row(treatment.id),
ok = evaluatePublicBase(input, 'MZN');
assert.equal(ok.status, 'determined');
assert.equal(
ok.baseAmount,
treatment.id === 'public_authority_indemnity'
? '1500.25'
: '1250.30',
);
assert.equal(ok.vat, null);
assert.equal(ok.rate, null);
for (const [key] of publicBaseConditions)
for (const value of ['', 'unknown', 'no']) {
const r = evaluatePublicBase(
{
...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 [
{ authority: '' },
{ actRef: '' },
{ compositionRef: '' },
{ operationNature: '' },
{ documentDate: '2026-02-30' },
{ documentRef: '' },
{ period: '2025-12' },
{ currency: 'USD' },
])
assert.equal(
evaluatePublicBase({ ...input, ...patch }, 'MZN').baseAmount,
null,
);
}
assert.match(
evaluatePublicBase(
{
...row(),
conditions: {
...row().conditions,
notJudicialContractBreach: 'no',
},
},
'MZN',
).reason,
/15\(6\)\(a\)/,
);
});
void test('money,in-kind,mixed compute exact net components;unknown differs from explicit zero and hidden values are retained', () => {
const input = row();
for (const [patch, expected] of [
[{ compensationForm: 'money', moneyAmount: '0' }, '0.00'],
[
{
compensationForm: 'non_money',
moneyAmount: '',
nonMoneyValue: '250.20',
},
'250.20',
],
[
{
compensationForm: 'mixed',
moneyAmount: '0.000001',
nonMoneyValue: '0.000002',
},
'0.000003',
],
[
{
compensationForm: 'mixed',
moneyAmount: '999999999999999.999999',
nonMoneyValue: '999999999999999.999999',
},
'1999999999999999.999998',
],
]) {
const r = evaluatePublicBase({ ...input, ...patch }, 'MZN');
assert.equal(r.baseAmount, expected);
assert.equal(r.vat, null);
}
for (const patch of [
{ compensationForm: '' },
{ moneyAmount: '' },
{ moneyAmount: '-1' },
{ moneyAmount: 'NaN' },
{ moneyAmount: '1.0000001' },
{ compensationForm: 'mixed', nonMoneyValue: '' },
{ compensationForm: 'non_money', valuationRef: '' },
])
assert.equal(
evaluatePublicBase({ ...input, ...patch }, 'MZN').baseAmount,
null,
);
const switched = { ...input, compensationForm: 'non_money' };
assert.equal(switched.moneyAmount, '1500.25');
assert.equal(evaluatePublicBase(switched, 'MZN').baseAmount, '250.20');
assert.equal(
evaluatePublicBase({ ...switched, compensationForm: 'mixed' }, 'MZN')
.baseAmount,
'1750.45',
);
});
void test('save/recovery/report isolate P090 from78delivered branches and partial offers,while keeping exact versions and contextual invalidation', () => {
const d = draft(),
prior = JSON.parse(d.modelo_a_snapshot);
assert.deepEqual(publicBaseBook(d), { rows: [] });
assert.deepEqual(publicBaseReportRows(d), []);
let book = recordPublicBases(d, {
rows: publicBaseTreatments.map((rule) => ({
...row(rule.id),
conditions: {},
})),
});
book = recordPublicBases(d, {
...book,
rows: publicBaseTreatments.map((rule) => ({
...row(rule.id),
conditions: {
...row(rule.id).conditions,
notJudicialContractBreach: 'no',
},
})),
});
book = recordPublicBases(d, {
...book,
rows: publicBaseTreatments.map((rule) => row(rule.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(publicBaseCurrent(d, book));
for (const field of [
'actRef',
'documentRef',
'compositionRef',
'compensationForm',
'valuationRef',
'moneyAmount',
])
assert.ok(
!publicBaseCurrent(d, {
...book,
rows: book.rows.map((r) => ({ ...r, [field]: 'changed' })),
}),
);
for (const patch of [
{ context_opportunity: 'opportunity:340' },
{ currency: 'USD' },
{ selection_labels: { province_id: 'Maputo' } },
])
assert.ok(!publicBaseCurrent({ ...d, ...patch }, book));
d.modelo_a_snapshot = savePublicBaseBook(d, book);
assert.deepEqual(publicBaseBook(JSON.parse(JSON.stringify(d))), book);
const next = JSON.parse(d.modelo_a_snapshot);
const { publicAuthorityBases: _publicAuthorityBases, ...untouched } = next;
assert.deepEqual(untouched, prior);
for (const [scope, previous] of Object.entries(prior.importExemptions))
assert.ok(
importExemptionCurrent(
{ ...d, context_opportunity: scope },
previous,
),
);
assert.equal(
next.importExemptions['opportunity:338'].result.rows[1].importVat,
'400.00',
);
assert.equal(
next.importExemptions['opportunity:336'].result.rows[0].serviceVat,
'0.00',
);
assert.equal(
next.books['opportunity:329'].result.periods[0].fields['11'],
null,
);
const report = JSON.stringify(
createProjectDossierReport(d, {
selectedSections: new Set(['finance']),
version: 'QA G7A',
}),
);
for (const token of [
'Base P090',
'1500.25',
'1250.30',
'IVA: Indeterminado',
'opportunity:339',
'Manica',
'2026-12',
'acto A',
'15(2)(d)',
'composição15(5)/(6)',
'campo11',
])
assert.ok(report.includes(token), token);
assert.deepEqual(
publicBaseReportRows({ ...d, context_opportunity: 'opportunity:340' }),
[],
);
if (process.env.IVA_G7A_CALCULATED)
fs.writeFileSync(process.env.IVA_G7A_CALCULATED, JSON.stringify(d));
});
void test('malformed public-base rows/store cannot replace originals;duplicate identifiers cannot create calculations', () => {
const d = draft(),
original = d.modelo_a_snapshot;
for (const patch of [
{ moneyAmount: 4 },
{ conditions: [] },
{ valuationRef: null },
])
assert.throws(
() => savePublicBaseBook(d, { rows: [{ ...row(), ...patch }] }),
/originais preservados/,
);
assert.equal(d.modelo_a_snapshot, original);
assert.throws(
() => recordPublicBases(d, { rows: [row(), row()] }),
/distintos/,
);
assert.throws(() => recordPublicBases(d, { rows: [] }), /distintos/);
const bad = {
...d,
modelo_a_snapshot: JSON.stringify({
...JSON.parse(original),
publicAuthorityBases: [],
}),
};
assert.throws(() => publicBaseBook(bad), /originais preservados/);
assert.equal(d.modelo_a_snapshot, original);
});