Files
Investidor-V2/tests/frontend/iva-act-sale-base.test.mjs
T

305 lines
10 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import {
newActSaleBase,
switchActValuation,
switchActKind,
actSaleKinds,
actSaleTreatments,
actSaleConditions,
evaluateActSale,
recordActSaleBases,
saveActSaleBook,
actSaleBook,
actSaleCurrent,
actSaleReportRows,
} from '../../resources/js/lib/iva-act-sale-base.ts';
import { commissionBaseCurrent } from '../../resources/js/lib/iva-commission-base.ts';
import { publicBaseCurrent } 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-g7c-preserved-inputs.json', import.meta.url),
'utf8',
),
);
const row = (treatment = actSaleTreatments[0].id) => ({
...newActSaleBase('g7c-' + treatment, 'MZN'),
treatment,
actKind: treatment === 'act_sale_amount' ? 'auction' : 'settlement',
description: 'SYNTHETIC QA bens transmitidos pelo acto identificado',
period: '2026-12',
actRef: 'SYNTHETIC QA acto A / bens desta operação',
documentRef: 'SYNTHETIC QA G7C ' + treatment,
documentDate: '2026-11-14',
actValue: '1500.25',
normalValue: '2200.50',
valuationRef: 'SYNTHETIC QA fundamento A do valor escolhido',
valuationScope:
'SYNTHETIC QA bens A, operação A, dezembro2026 e condições declaradas',
compositionRef: 'SYNTHETIC QA composição15(5)/(6),valor líquido de IVA',
evidence: 'SYNTHETIC QA pressupostos expressos,não documento real',
conditions: Object.fromEntries(
actSaleConditions.map(([key]) => [key, 'yes']),
),
});
const draft = () => ({
...structuredClone(frozen),
context_opportunity: 'opportunity:341',
project_title: 'SYNTHETIC QA — G7C / Bases de transmissão por acto',
});
void test('P094 each act and valuation requires own conditions and references; unknown/no never become zero or VAT', () => {
for (const rule of actSaleTreatments)
for (const kind of actSaleKinds) {
const input = { ...row(rule.id), actKind: kind.id },
ok = evaluateActSale(input, 'MZN');
assert.equal(ok.status, 'determined');
assert.equal(
ok.baseAmount,
rule.id === 'act_sale_amount' ? '1500.25' : '2200.50',
);
assert.equal(ok.vat, null);
assert.equal(ok.rate, null);
for (const [key] of actSaleConditions)
for (const value of ['', 'unknown', 'no']) {
const r = evaluateActSale(
{
...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: '' },
{ actRef: '' },
{ documentRef: '' },
{ documentDate: '2026-02-30' },
{ valuationRef: '' },
{ valuationScope: '' },
{ compositionRef: '' },
{ evidence: '' },
{ period: '2025-12' },
{ currency: 'USD' },
{ actKind: 'unknown' },
{ treatment: 'unknown' },
])
assert.equal(
evaluateActSale({ ...input, ...patch }, 'MZN').baseAmount,
null,
);
}
});
void test('selected act value or normal value only; no automatic minimum/fallback, exact decimals and retained edits on switch', () => {
const input = row();
for (const [patch, expected] of [
[{ actValue: '0' }, '0.00'],
[{ actValue: '1.000002' }, '1.000002'],
[{ actValue: '999999999999999.999999' }, '999999999999999.999999'],
[{ normalValue: 'invalid' }, '1500.25'],
[{ treatment: 'act_normal_value', actValue: 'invalid' }, '2200.50'],
])
assert.equal(
evaluateActSale({ ...input, ...patch }, 'MZN').baseAmount,
expected,
);
for (const patch of [
{ actValue: '' },
{ actValue: '-1' },
{ actValue: 'NaN' },
{ actValue: '0.0000001' },
{ treatment: 'act_normal_value', normalValue: '' },
])
assert.equal(
evaluateActSale({ ...input, ...patch }, 'MZN').baseAmount,
null,
);
const switched = switchActValuation(input, 'act_normal_value');
assert.equal(switched.conditions.valuationApplies, '');
for (const field of [
'actValue',
'normalValue',
'actRef',
'valuationRef',
'valuationScope',
'documentRef',
])
assert.equal(switched[field], input[field]);
assert.equal(evaluateActSale(switched, 'MZN').baseAmount, null);
assert.equal(
evaluateActSale(
{
...switched,
conditions: { ...switched.conditions, valuationApplies: 'yes' },
},
'MZN',
).baseAmount,
'2200.50',
);
const changed = switchActKind(input, 'judicial_sale');
assert.equal(changed.conditions.goodsByAct, '');
assert.equal(changed.conditions.valuationApplies, '');
assert.equal(changed.actValue, input.actValue);
assert.equal(changed.documentRef, input.documentRef);
assert.equal(evaluateActSale(changed, 'MZN').baseAmount, null);
});
void test('save/recovery/history/report preserve all80 delivered books and old currentkeys', () => {
const d = draft(),
prior = JSON.parse(d.modelo_a_snapshot);
assert.deepEqual(actSaleBook(d), { rows: [] });
assert.deepEqual(actSaleReportRows(d), []);
let book = recordActSaleBases(d, {
rows: actSaleTreatments.map((rule) => ({
...row(rule.id),
conditions: {},
})),
});
book = recordActSaleBases(d, {
...book,
rows: actSaleTreatments.map((rule) => ({
...row(rule.id),
conditions: { ...row(rule.id).conditions, goodsByAct: 'no' },
})),
});
book = recordActSaleBases(d, {
...book,
rows: actSaleTreatments.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(actSaleCurrent(d, book));
for (const field of [
'treatment',
'actKind',
'actValue',
'normalValue',
'valuationRef',
'valuationScope',
'actRef',
])
assert.ok(
!actSaleCurrent(d, {
...book,
rows: book.rows.map((r) => ({ ...r, [field]: 'changed' })),
}),
);
for (const patch of [
{ context_opportunity: 'opportunity:342' },
{ currency: 'USD' },
{ selection_labels: { province_id: 'Maputo' } },
])
assert.ok(!actSaleCurrent({ ...d, ...patch }, book));
d.modelo_a_snapshot = saveActSaleBook(d, book);
assert.deepEqual(actSaleBook(JSON.parse(JSON.stringify(d))), book);
const next = JSON.parse(d.modelo_a_snapshot);
const { actSaleBases: _actSaleBases, ...untouched } = next;
assert.deepEqual(untouched, prior);
for (const [scope, previous] of Object.entries(prior.importExemptions))
assert.ok(
importExemptionCurrent(
{ ...d, context_opportunity: scope },
previous,
),
);
for (const [scope, previous] of Object.entries(prior.publicAuthorityBases))
assert.ok(
publicBaseCurrent({ ...d, context_opportunity: scope }, previous),
);
for (const [scope, previous] of Object.entries(prior.commissionBases))
assert.ok(
commissionBaseCurrent(
{ ...d, context_opportunity: scope },
previous,
),
);
assert.equal(
next.commissionBases['opportunity:340'].result.rows[0].baseAmount,
'900.15',
);
assert.equal(
next.publicAuthorityBases['opportunity:339'].result.rows[0].vat,
null,
);
assert.equal(
next.importExemptions['opportunity:338'].result.rows[1].importVat,
'400.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 G7C',
}),
);
for (const token of [
'Base P094',
'1500.25',
'2200.50',
'IVA: Indeterminado',
'opportunity:341',
'Manica',
'2026-12',
'acto A',
'15(2)(h)',
'fundamento A',
'dezembro2026',
'campo11',
])
assert.ok(report.includes(token), token);
assert.deepEqual(
actSaleReportRows({ ...d, context_opportunity: 'opportunity:342' }),
[],
);
if (process.env.IVA_G7C_CALCULATED)
fs.writeFileSync(process.env.IVA_G7C_CALCULATED, JSON.stringify(d));
});
void test('corrupt rows/store cannot overwrite originals; duplicate operation identifiers cannot be recorded', () => {
const d = draft(),
original = d.modelo_a_snapshot;
for (const patch of [
{ actValue: 4 },
{ conditions: [] },
{ valuationRef: null },
])
assert.throws(
() => saveActSaleBook(d, { rows: [{ ...row(), ...patch }] }),
/originais preservados/,
);
assert.equal(d.modelo_a_snapshot, original);
assert.throws(
() => recordActSaleBases(d, { rows: [row(), row()] }),
/distintos/,
);
assert.throws(() => recordActSaleBases(d, { rows: [] }), /distintos/);
assert.throws(
() =>
actSaleBook({
...d,
modelo_a_snapshot: JSON.stringify({
...JSON.parse(original),
actSaleBases: [],
}),
}),
/originais preservados/,
);
});