Files
Investidor-V2/tests/frontend/iva-acquisition-right.test.mjs
T

297 lines
9.6 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import {
newSalesOperation,
recordSales,
saveSalesBook,
salesBook,
operationTreatments,
} from '../../resources/js/lib/iva-operations.ts';
import {
newAcquisition,
evaluateAcquisition,
recordAcquisitions,
saveAcquisitionBook,
acquisitionBook,
acquisitionCurrent,
acquisitionReportRows,
readAcquisitionStore,
} from '../../resources/js/lib/iva-acquisitions.ts';
import {
createAcquisitionRightLink,
linkedOperationRight,
} from '../../resources/js/lib/iva-acquisition-right.ts';
const frozen = JSON.parse(
fs.readFileSync(
new URL('./fixtures/iva-l2a-preserved.json', import.meta.url),
'utf8',
),
);
export function rightFixture() {
const draft = {
...structuredClone(frozen),
context_opportunity: 'opportunity:368',
project_title: 'SYNTHETIC QA — L2-A aquisição e direito',
};
const types = [
'general',
'export_goods',
'listed_maize',
'housing_rent',
'private_health',
];
const operations = {
currency: 'MZN',
periods: [{ period: '2026-12', complete: true }],
rows: types.map((t, i) => ({
...newSalesOperation('operation-' + i, '2026-12', 'MZN'),
description: 'SYNTHETIC QA ' + t,
amount: '100',
basis: 'net',
establishment: 'Mozambique',
treatment: t,
confirmed: operationTreatments
.find((x) => x.id === t)
.conditions.map((_, j) => j),
evidence: 'SYNTHETIC QA fonte e condições',
})),
};
draft.modelo_a_snapshot = saveSalesBook(
draft,
recordSales(draft, operations),
);
const rows = types.map((_, i) => ({
...newAcquisition('purchase-' + i, '2026-12', 'MZN'),
net: '100',
vat: '16',
gross: '116',
description: 'SYNTHETIC QA compra ' + i,
documentType: 'invoice',
documentRef: 'SYNTHETIC QA ' + i,
documentDate: '2026-12-10',
supplier: 'SYNTHETIC QA fornecedor',
supplierRegistered: 'yes',
legalDocument: 'yes',
held: 'yes',
domestic: 'yes',
allocation: 'taxable_only',
exclusion: 'none',
integrity: 'yes',
deductionLink: {
...createAcquisitionRightLink(draft, 'operation-' + i),
scopeConfirmed: 'yes',
allocationRef: 'Afectação exclusiva SYNTHETIC QA ' + i,
noExcludedPayments: 'yes',
},
}));
return {
draft,
book: {
currency: 'MZN',
taxpayerRegistered: 'yes',
rows,
periods: [
{
period: '2026-12',
complete: true,
absent: ['11', '12', '13'],
},
],
},
};
}
void test('right follows classified operation, including exempt with right and taxed17-A without right', () => {
const { draft, book } = rightFixture();
const results = book.rows.map((r) =>
evaluateAcquisition(r, 'MZN', 'yes', draft),
);
assert.deepEqual(
results.map((r) => r.eligibility.status),
['eligible', 'eligible', 'eligible', 'no_right', 'no_right'],
);
assert.deepEqual(
results.map((r) => r.deductible),
['16.00', '16.00', '16.00', '0.00', '0.00'],
);
for (const r of results) {
assert.ok(r.eligibility.reason);
assert.match(r.eligibility.source, /Lei10\/2025/);
}
assert.match(results[4].reason, /17-A/);
});
void test('own documentary conditions, missing data, imports and mixed use remain distinct from operation right', () => {
const { draft, book } = rightFixture(),
row = book.rows[0];
for (const patch of [
{ held: '' },
{ documentRef: '' },
{ documentDate: '2026-11-10' },
{ allocation: 'mixed' },
{ domestic: 'no' },
{ exclusion: 'special' },
{ sourceCurrency: 'USD' },
{ vat: '0', gross: '100', held: '' },
{ sourceMonth: 1, sourceAmount: '999', sourceBasis: 'net' },
]) {
const r = evaluateAcquisition(
{ ...row, ...patch },
'MZN',
'yes',
draft,
);
assert.equal(
r.eligibility.status,
'indeterminate',
JSON.stringify(patch),
);
assert.equal(r.deductible, null);
}
for (const patch of [
{ exclusion: 'excluded' },
{ supplierRegistered: 'no' },
{ integrity: 'no' },
])
assert.equal(
evaluateAcquisition({ ...row, ...patch }, 'MZN', 'yes', draft)
.eligibility.status,
'no_right',
);
assert.equal(
evaluateAcquisition(
{
...row,
deductionLink: { ...row.deductionLink, scopeConfirmed: '' },
},
'MZN',
'yes',
draft,
).deductible,
null,
);
assert.equal(
evaluateAcquisition(
{
...row,
deductionLink: {
...row.deductionLink,
noExcludedPayments: 'no',
},
},
'MZN',
'yes',
draft,
).deductible,
'0.00',
);
assert.equal(evaluateAcquisition(row, 'MZN', 'yes').deductible, null);
});
void test('totals F include only eligible amounts; unknown never becomes zero; no duplicated projected/effective invoice', () => {
const { draft, book } = rightFixture();
const result = recordAcquisitions(draft, book).result;
assert.equal(result.periods[0].fields['09'], '48.00');
assert.equal(result.periods[0].fields['15'], '48.00');
assert.deepEqual(
['14', '15', '16', '17', '18'].map((f) => result.periods[0].fields[f]),
['500.00', '48.00', '21.00', '0.00', '27.00'],
);
const cleared = structuredClone(book);
cleared.rightVersion = 'acquisition-operation-right/1';
cleared.rows = cleared.rows.map(
({ deductionLink: _deductionLink, ...row }) => row,
);
const empty = recordAcquisitions(draft, cleared).result;
assert.ok(
empty.rows.every((r) => r.eligibility.status === 'indeterminate'),
);
assert.equal(empty.periods[0].fields['15'], null);
const unknown = structuredClone(book);
unknown.rows[0].held = '';
assert.equal(
recordAcquisitions(draft, unknown).result.periods[0].fields['15'],
null,
);
const duplicate = structuredClone(book);
duplicate.rows.push({
...duplicate.rows[0],
id: 'copy',
origin: 'effective',
});
assert.throws(() => recordAcquisitions(draft, duplicate), /dupla dedução/);
});
void test('source edits require explicit adoption; save recovery report and historical versions preserve old books byte for byte', () => {
let { draft, book } = rightFixture();
const before = JSON.parse(frozen.modelo_a_snapshot);
book = recordAcquisitions(draft, book);
draft.modelo_a_snapshot = saveAcquisitionBook(draft, book);
const first = structuredClone(book.result);
book = recordAcquisitions(draft, book);
draft.modelo_a_snapshot = saveAcquisitionBook(draft, book);
const recovered = JSON.parse(JSON.stringify(draft));
assert.deepEqual(acquisitionBook(recovered), book);
assert.ok(acquisitionCurrent(recovered, book));
assert.deepEqual(book.history, [first]);
const after = JSON.parse(draft.modelo_a_snapshot);
for (const key of Object.keys(before)) {
if (key === 'version') continue;
for (const [id, value] of Object.entries(before[key]))
assert.deepEqual(after[key][id], value, key + id);
}
const report = JSON.stringify(acquisitionReportRows(recovered));
for (const word of [
'Elegível',
'Sem direito',
'Lei10/2025',
'48.00',
'operation-4',
])
assert.ok(report.includes(word), word);
const ops = salesBook(draft);
ops.rows[0].description = 'Alteração deliberada';
draft.modelo_a_snapshot = saveSalesBook(draft, recordSales(draft, ops));
assert.equal(acquisitionCurrent(draft, book), false);
assert.equal(
linkedOperationRight(draft, book.rows[0].deductionLink).status,
'indeterminate',
);
assert.deepEqual(book.history[0], first);
assert.deepEqual(
book.result,
recovered ? acquisitionBook(recovered).result : null,
);
const adopted = createAcquisitionRightLink(
draft,
book.rows[0].deductionLink.operationId,
);
assert.equal(adopted.scopeConfirmed, '');
assert.equal(adopted.allocationRef, '');
assert.equal(linkedOperationRight(draft, adopted).status, 'eligible');
assert.equal(
evaluateAcquisition(
{ ...book.rows[0], deductionLink: adopted },
'MZN',
'yes',
draft,
).deductible,
null,
);
assert.equal(
linkedOperationRight(
{ ...draft, context_opportunity: 'opportunity:329' },
adopted,
).status,
'indeterminate',
);
const malformed = JSON.parse(draft.modelo_a_snapshot);
malformed.books['opportunity:368'].rows[0].deductionLink.version = 9;
assert.throws(
() => readAcquisitionStore(JSON.stringify(malformed)),
/Vínculo/,
);
if (process.env.IVA_L2A_CALCULATED)
fs.writeFileSync(
process.env.IVA_L2A_CALCULATED,
JSON.stringify(recovered),
);
});