246 lines
8.9 KiB
JavaScript
246 lines
8.9 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import {
|
|
newReducedBase,
|
|
switchReducedTreatment,
|
|
reducedBaseConditions,
|
|
ownReducedConditions,
|
|
reducedBaseTreatments,
|
|
publicWorkCategories,
|
|
evaluateReducedBase,
|
|
recordReducedBases,
|
|
saveReducedBaseBook,
|
|
reducedBaseBook,
|
|
reducedBaseCurrent,
|
|
reducedBaseReportRows,
|
|
} from '../../resources/js/lib/iva-reduced-bases.ts';
|
|
const frozen = JSON.parse(
|
|
fs.readFileSync(
|
|
'tests/frontend/fixtures/iva-g7-reduced-preserved.json',
|
|
'utf8',
|
|
),
|
|
);
|
|
const draft = {
|
|
...frozen,
|
|
currency: 'MZN',
|
|
context_opportunity: 'opportunity:351',
|
|
project_title: 'SYNTHETIC QA — G7 bases reduzidas / Manica',
|
|
};
|
|
function row(t) {
|
|
const rule = reducedBaseTreatments.find((x) => x.id === t);
|
|
return {
|
|
...newReducedBase(t, 'MZN'),
|
|
treatment: t,
|
|
description: 'SYNTHETIC QA ' + rule.branch,
|
|
period: '2026-12',
|
|
origin: 'projected',
|
|
operationDate: '2026-12-12',
|
|
documentRef: 'SYNTHETIC QA documento/pressuposto',
|
|
documentDate: '2026-12-12',
|
|
priceScope: 'SYNTHETIC QA objecto/operação/local/período',
|
|
priceRef: 'SYNTHETIC QA preço/taxa aplicável',
|
|
priceFrom: '2026-01-01',
|
|
priceTo: '2026-12-31',
|
|
consideration: '1000.25',
|
|
compositionRef: 'SYNTHETIC QA total antes redução,semIVA,15(1)/(5)/(6)',
|
|
evidence: 'SYNTHETIC QA pressupostos expressos',
|
|
projectRef: 'SYNTHETIC QA projecto público/contrato',
|
|
workCategory: 'roads',
|
|
conditions: Object.fromEntries(
|
|
[...reducedBaseConditions, ...ownReducedConditions(t)].map(
|
|
([key]) => [key, 'yes'],
|
|
),
|
|
),
|
|
};
|
|
}
|
|
const expected = ['620.155', '850.2125', '400.10', '750.1875'];
|
|
for (const [i, rule] of reducedBaseTreatments.entries())
|
|
void test(
|
|
rule.branch +
|
|
' ownconditions,exact base and separate deduction proportion',
|
|
() => {
|
|
const r = row(rule.id),
|
|
result = evaluateReducedBase(r, 'MZN');
|
|
assert.equal(result.status, 'determined');
|
|
assert.equal(result.baseAmount, expected[i]);
|
|
assert.equal(result.deductionProportion, rule.percent + '%');
|
|
assert.equal(result.vat, null);
|
|
assert.equal(result.rate, null);
|
|
for (const [key] of [
|
|
...reducedBaseConditions,
|
|
...ownReducedConditions(rule.id),
|
|
]) {
|
|
const missing = structuredClone(r);
|
|
delete missing.conditions[key];
|
|
assert.equal(
|
|
evaluateReducedBase(missing, 'MZN').status,
|
|
'indeterminate',
|
|
);
|
|
missing.conditions[key] = 'no';
|
|
assert.equal(
|
|
evaluateReducedBase(missing, 'MZN').status,
|
|
'not_applicable',
|
|
);
|
|
assert.equal(
|
|
evaluateReducedBase(missing, 'MZN').baseAmount,
|
|
null,
|
|
);
|
|
assert.equal(
|
|
evaluateReducedBase(missing, 'MZN').deductionProportion,
|
|
null,
|
|
);
|
|
}
|
|
for (const field of [
|
|
'description',
|
|
'documentRef',
|
|
'priceScope',
|
|
'compositionRef',
|
|
'evidence',
|
|
'consideration',
|
|
])
|
|
assert.equal(
|
|
evaluateReducedBase({ ...r, [field]: '' }, 'MZN').status,
|
|
'indeterminate',
|
|
field,
|
|
);
|
|
for (const patch of [
|
|
{ operationDate: '2026-11-12' },
|
|
{ documentDate: '2026-02-30' },
|
|
{ period: '2025-12' },
|
|
{ currency: 'USD' },
|
|
{ consideration: '1,5' },
|
|
{ consideration: '-1' },
|
|
{ consideration: '1e3' },
|
|
])
|
|
assert.equal(
|
|
evaluateReducedBase({ ...r, ...patch }, 'MZN').status,
|
|
'indeterminate',
|
|
);
|
|
assert.equal(evaluateReducedBase(r, 'USD').status, 'indeterminate');
|
|
assert.equal(
|
|
evaluateReducedBase({ ...r, consideration: '0' }, 'MZN')
|
|
.baseAmount,
|
|
'0.00',
|
|
);
|
|
if (rule.id === 'public_works') {
|
|
assert.equal(
|
|
evaluateReducedBase(
|
|
{ ...r, priceRef: '', priceFrom: '', priceTo: '' },
|
|
'MZN',
|
|
).baseAmount,
|
|
expected[i],
|
|
);
|
|
for (const c of publicWorkCategories)
|
|
assert.equal(
|
|
evaluateReducedBase({ ...r, workCategory: c.id }, 'MZN')
|
|
.status,
|
|
'determined',
|
|
);
|
|
assert.equal(
|
|
evaluateReducedBase(
|
|
{ ...r, workCategory: 'private_work' },
|
|
'MZN',
|
|
).status,
|
|
'indeterminate',
|
|
);
|
|
assert.equal(
|
|
evaluateReducedBase({ ...r, projectRef: '' }, 'MZN').status,
|
|
'indeterminate',
|
|
);
|
|
assert.match(result.reason, /base40%/);
|
|
} else {
|
|
for (const patch of [
|
|
{ priceRef: '' },
|
|
{ priceFrom: '' },
|
|
{ priceFrom: '2027-01-01' },
|
|
{ priceTo: '2026-11-30' },
|
|
{ priceTo: '2025-12-31' },
|
|
])
|
|
assert.equal(
|
|
evaluateReducedBase({ ...r, ...patch }, 'MZN').status,
|
|
'indeterminate',
|
|
);
|
|
assert.equal(
|
|
evaluateReducedBase({ ...r, priceTo: '' }, 'MZN').status,
|
|
'determined',
|
|
);
|
|
}
|
|
},
|
|
);
|
|
void test('Exactfraction retained,no 60percent works base,no duplicate reduction and no automatic deduction', () => {
|
|
assert.equal(
|
|
evaluateReducedBase(
|
|
{ ...row('regulated_energy'), consideration: '0.000001' },
|
|
'MZN',
|
|
).baseAmount,
|
|
'0.00000062',
|
|
);
|
|
assert.equal(
|
|
evaluateReducedBase(
|
|
{ ...row('public_works'), consideration: '0.000001' },
|
|
'MZN',
|
|
).baseAmount,
|
|
'0.0000004',
|
|
);
|
|
assert.equal(
|
|
evaluateReducedBase(
|
|
{ ...row('public_water'), consideration: '999999999999999.999999' },
|
|
'MZN',
|
|
).baseAmount,
|
|
'749999999999999.99999925',
|
|
);
|
|
const reduced = row('public_works');
|
|
reduced.conditions.fullNetInvoice = 'no';
|
|
assert.equal(evaluateReducedBase(reduced, 'MZN').baseAmount, null);
|
|
const changed = switchReducedTreatment(
|
|
row('regulated_energy'),
|
|
'public_works',
|
|
);
|
|
assert.equal(changed.priceRef, row('regulated_energy').priceRef);
|
|
assert.equal(changed.projectRef, row('regulated_energy').projectRef);
|
|
assert.equal(changed.conditions.publicWorks, undefined);
|
|
assert.equal(evaluateReducedBase(changed, 'MZN').status, 'indeterminate');
|
|
});
|
|
void test('Four branches save/recover/history/report and all previous books are preserved', () => {
|
|
const rows = reducedBaseTreatments.map((t) => row(t.id));
|
|
let book = recordReducedBases(draft, { rows });
|
|
book = recordReducedBases(draft, book);
|
|
assert.equal(book.history.length, 1);
|
|
assert.notEqual(book.result.inputs, book.rows);
|
|
const next = {
|
|
...draft,
|
|
modelo_a_snapshot: saveReducedBaseBook(draft, book),
|
|
};
|
|
assert.deepEqual(reducedBaseBook(next), book);
|
|
assert.ok(reducedBaseCurrent(next, book));
|
|
assert.equal(reducedBaseCurrent({ ...next, currency: 'USD' }, book), false);
|
|
assert.equal(
|
|
reducedBaseCurrent({ ...next, context_opportunity: 'other' }, book),
|
|
false,
|
|
);
|
|
assert.equal(reducedBaseReportRows(next).length, 4);
|
|
for (const t of reducedBaseTreatments)
|
|
assert.ok(
|
|
reducedBaseReportRows(next).some((r) => r[1].includes(t.branch)),
|
|
);
|
|
const store = JSON.parse(next.modelo_a_snapshot);
|
|
delete store.regulatedReducedBases;
|
|
assert.deepEqual(store, JSON.parse(frozen.modelo_a_snapshot));
|
|
assert.throws(() => recordReducedBases(next, { rows: [rows[0], rows[0]] }));
|
|
assert.throws(() =>
|
|
reducedBaseBook({
|
|
...next,
|
|
modelo_a_snapshot: JSON.stringify({
|
|
...JSON.parse(next.modelo_a_snapshot),
|
|
regulatedReducedBases: [],
|
|
}),
|
|
}),
|
|
);
|
|
if (process.env.IVA_REDUCED_FIXTURE)
|
|
fs.writeFileSync(
|
|
process.env.IVA_REDUCED_FIXTURE,
|
|
JSON.stringify(next, null, 2) + '\n',
|
|
);
|
|
});
|