448 lines
15 KiB
JavaScript
448 lines
15 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import {
|
|
recordSales,
|
|
saveSalesBook,
|
|
} from '../../resources/js/lib/iva-operations.ts';
|
|
import {
|
|
recordAcquisitions,
|
|
evaluateAcquisition,
|
|
saveAcquisitionBook,
|
|
acquisitionBook,
|
|
acquisitionCurrent,
|
|
acquisitionReportRows,
|
|
readAcquisitionStore,
|
|
} from '../../resources/js/lib/iva-acquisitions.ts';
|
|
import { saveImportExemptionBook } from '../../resources/js/lib/iva-import-exemptions.ts';
|
|
import {
|
|
newAcquisitionAllocation,
|
|
adoptAllocationReduced,
|
|
} from '../../resources/js/lib/iva-acquisition-allocation.ts';
|
|
import {
|
|
newReducedBase,
|
|
reducedBaseConditions,
|
|
ownReducedConditions,
|
|
recordReducedBases,
|
|
saveReducedBaseBook,
|
|
reducedBaseBook,
|
|
} from '../../resources/js/lib/iva-reduced-bases.ts';
|
|
const frozen = JSON.parse(
|
|
fs.readFileSync(
|
|
new URL('./fixtures/iva-l2d-preserved.json', import.meta.url),
|
|
'utf8',
|
|
),
|
|
);
|
|
export function allocationFixture() {
|
|
const draft = {
|
|
...structuredClone(frozen),
|
|
context_opportunity: 'opportunity:370',
|
|
project_title: 'SYNTHETIC QA — L2-D afectação e proporções',
|
|
};
|
|
const store = JSON.parse(frozen.modelo_a_snapshot);
|
|
draft.modelo_a_snapshot = saveSalesBook(
|
|
draft,
|
|
recordSales(
|
|
draft,
|
|
structuredClone(store.salesOperations['opportunity:369']),
|
|
),
|
|
);
|
|
draft.modelo_a_snapshot = saveImportExemptionBook(
|
|
draft,
|
|
structuredClone(store.importExemptions['opportunity:369']),
|
|
);
|
|
const base = store.books['opportunity:369'];
|
|
const d = (method, patch = {}) => ({
|
|
...newAcquisitionAllocation('2026-12'),
|
|
method,
|
|
numerator: '1',
|
|
denominator: '3',
|
|
unit: 'm2 utilizados',
|
|
scopeRef: 'SYNTHETIC QA entidade/actividades/ano2026',
|
|
source: 'SYNTHETIC QA contabilidade/critério',
|
|
communicationRef: 'SYNTHETIC QA comunicação prévia22(2)',
|
|
noExcludedPayments: 'yes',
|
|
basis: 'prior_year',
|
|
facts: {
|
|
scopeConfirmed: 'yes',
|
|
rightBasis: 'yes',
|
|
actualScope: 'yes',
|
|
communication: 'yes',
|
|
conditionsMet: 'yes',
|
|
annualScope: 'yes',
|
|
netExclusions: 'yes',
|
|
},
|
|
...patch,
|
|
});
|
|
const row = (id, method = 'actual', patch = {}, dpatch = {}) => ({
|
|
...structuredClone(base.rows[3]),
|
|
id,
|
|
description: 'SYNTHETIC QA ' + id,
|
|
documentRef: 'SYNTHETIC QA ' + id,
|
|
deductionLink: undefined,
|
|
allocation: 'mixed',
|
|
d: d(method, dpatch),
|
|
...patch,
|
|
});
|
|
const imported = {
|
|
...structuredClone(base.rows[0]),
|
|
id: 'imported',
|
|
deductionLink: undefined,
|
|
allocation: 'mixed',
|
|
d: d('pro_rata', { numerator: '2', denominator: '3' }),
|
|
};
|
|
const book = {
|
|
currency: 'MZN',
|
|
rightVersion: 'acquisition-operation-right/1',
|
|
taxpayerRegistered: 'yes',
|
|
rows: [
|
|
row('actual'),
|
|
row('pro-rata', 'pro_rata'),
|
|
row('diesel', 'actual', { bc: { ...base.rows[1].bc } }),
|
|
imported,
|
|
],
|
|
periods: structuredClone(base.periods),
|
|
};
|
|
return { draft, book, row, d };
|
|
}
|
|
void test('actual allocation uses exact fraction and prior communication,without inventing approval or bypassing C', () => {
|
|
const { draft, row } = allocationFixture();
|
|
const r = row('actual');
|
|
let value = evaluateAcquisition(r, 'MZN', 'yes', draft);
|
|
assert.deepEqual(value.deductibleExact, {
|
|
numerator: '16',
|
|
denominator: '3',
|
|
});
|
|
assert.deepEqual(value.nonDeductibleExact, {
|
|
numerator: '32',
|
|
denominator: '3',
|
|
});
|
|
assert.equal(value.eligibility.status, 'eligible');
|
|
for (const patch of [
|
|
{ numerator: '' },
|
|
{ denominator: '0' },
|
|
{ denominator: '0.1' },
|
|
{ unit: '' },
|
|
{ communicationRef: '' },
|
|
{ scopeRef: '' },
|
|
{ source: '' },
|
|
{ facts: { ...r.d.facts, communication: '' } },
|
|
{ facts: { ...r.d.facts, conditionsMet: '' } },
|
|
{ facts: { ...r.d.facts, rightBasis: '' } },
|
|
])
|
|
assert.equal(
|
|
evaluateAcquisition(
|
|
{ ...r, d: { ...r.d, ...patch } },
|
|
'MZN',
|
|
'yes',
|
|
draft,
|
|
).deductible,
|
|
null,
|
|
JSON.stringify(patch),
|
|
);
|
|
for (const patch of [
|
|
{ held: '' },
|
|
{ documentRef: '' },
|
|
{ legalDocument: '' },
|
|
{ documentDate: '2026-11-10' },
|
|
])
|
|
assert.equal(
|
|
evaluateAcquisition({ ...r, ...patch }, 'MZN', 'yes', draft)
|
|
.deductible,
|
|
null,
|
|
);
|
|
assert.equal(
|
|
evaluateAcquisition(
|
|
{ ...r, bc: { ...r.bc, expense: 'travel' } },
|
|
'MZN',
|
|
'yes',
|
|
draft,
|
|
).deductible,
|
|
'0.00',
|
|
);
|
|
assert.equal(
|
|
evaluateAcquisition(
|
|
{ ...r, bc: { ...r.bc, reducedSupply: 'yes' } },
|
|
'MZN',
|
|
'yes',
|
|
draft,
|
|
).deductible,
|
|
'0.00',
|
|
);
|
|
});
|
|
void test('pro rata uses annual prior year or declared initial estimate;ceil hundredth is preserved separately from money', () => {
|
|
const { draft, row } = allocationFixture();
|
|
const r = row('pro', 'pro_rata');
|
|
const calc = (patch) =>
|
|
evaluateAcquisition(
|
|
{ ...r, d: { ...r.d, ...patch } },
|
|
'MZN',
|
|
'yes',
|
|
draft,
|
|
);
|
|
const v = calc({});
|
|
assert.deepEqual(v.allocationCalculation.rawRatio, {
|
|
numerator: '1',
|
|
denominator: '3',
|
|
});
|
|
assert.deepEqual(v.allocationCalculation.ratio, {
|
|
numerator: '17',
|
|
denominator: '50',
|
|
});
|
|
assert.equal(v.deductible, '5.44');
|
|
assert.match(v.reason, /34%/);
|
|
assert.equal(calc({ numerator: '0' }).deductible, '0.00');
|
|
assert.equal(calc({ numerator: '3' }).deductible, '16.00');
|
|
assert.equal(calc({ numerator: '1', denominator: '2' }).deductible, '8.00');
|
|
for (const patch of [
|
|
{ referenceYear: '2026' },
|
|
{ actualRequired: 'yes' },
|
|
{ basis: '' },
|
|
{ facts: { ...r.d.facts, annualScope: '' } },
|
|
{ facts: { ...r.d.facts, netExclusions: '' } },
|
|
])
|
|
assert.equal(calc(patch).deductible, null, JSON.stringify(patch));
|
|
const initial = {
|
|
basis: 'initial_changed',
|
|
referenceYear: '2026',
|
|
declarationRef: 'SYNTHETIC QA início/alteração',
|
|
facts: { ...r.d.facts, initialChanged: 'yes' },
|
|
};
|
|
assert.equal(calc(initial).deductible, '5.44');
|
|
assert.equal(calc({ ...initial, declarationRef: '' }).deductible, null);
|
|
});
|
|
void test('F composes C diesel only once with D and import11,keeps exact fractions and unknown dependencies', () => {
|
|
const { draft, book } = allocationFixture();
|
|
const result = recordAcquisitions(draft, book).result;
|
|
assert.deepEqual(result.rows[2].allocationCalculation.before, {
|
|
numerator: '8',
|
|
denominator: '1',
|
|
});
|
|
assert.deepEqual(result.rows[2].deductibleExact, {
|
|
numerator: '8',
|
|
denominator: '3',
|
|
});
|
|
assert.deepEqual(
|
|
['09', '11', '14', '15', '16', '17', '18'].map(
|
|
(k) => result.periods[0].fields[k],
|
|
),
|
|
['13.44', '10.72', '1000.00', '24.16', '160.00', '135.84', '0.00'],
|
|
);
|
|
const incomplete = structuredClone(book);
|
|
incomplete.rows[0].d.denominator = '0';
|
|
assert.equal(
|
|
recordAcquisitions(draft, incomplete).result.periods[0].fields['15'],
|
|
null,
|
|
);
|
|
const coverage = structuredClone(book);
|
|
coverage.periods[0].importsComplete = false;
|
|
assert.equal(
|
|
recordAcquisitions(draft, coverage).result.periods[0].fields['17'],
|
|
null,
|
|
);
|
|
const duplicate = structuredClone(book);
|
|
duplicate.rows.push({ ...book.rows[0], id: 'duplicate' });
|
|
assert.throws(() => recordAcquisitions(draft, duplicate), /repetido/);
|
|
const saved = recordAcquisitions(draft, book);
|
|
draft.modelo_a_snapshot = saveAcquisitionBook(draft, saved);
|
|
addReduced(draft);
|
|
assert.equal(acquisitionCurrent(draft, saved), false);
|
|
const outputGap = recordAcquisitions(draft, saved).result;
|
|
assert.equal(outputGap.periods[0].fields['15'], '24.16');
|
|
assert.equal(outputGap.periods[0].fields['16'], null);
|
|
});
|
|
function addReduced(draft) {
|
|
const rows = [
|
|
'regulated_energy',
|
|
'aeronautical_fees',
|
|
'public_works',
|
|
'public_water',
|
|
].map((t) => ({
|
|
...newReducedBase(t, 'MZN'),
|
|
treatment: t,
|
|
description: 'SYNTHETIC QA ' + t,
|
|
period: '2026-12',
|
|
operationDate: '2026-12-10',
|
|
documentDate: '2026-12-10',
|
|
documentRef: 'SYNTHETIC QA ' + t,
|
|
priceRef: 'SYNTHETIC QA preço/taxa',
|
|
priceFrom: '2026-01-01',
|
|
priceScope: 'SYNTHETIC QA âmbito',
|
|
consideration: '100',
|
|
projectRef: 'SYNTHETIC QA contrato público',
|
|
workCategory: 'roads',
|
|
compositionRef: 'SYNTHETIC QA composição semIVA',
|
|
evidence: 'SYNTHETIC QA condições',
|
|
conditions: Object.fromEntries(
|
|
[...reducedBaseConditions, ...ownReducedConditions(t)].map(
|
|
([key]) => [key, 'yes'],
|
|
),
|
|
),
|
|
}));
|
|
draft.modelo_a_snapshot = saveReducedBaseBook(
|
|
draft,
|
|
recordReducedBases(draft, { rows }),
|
|
);
|
|
}
|
|
void test('18(6) reuses all four demonstrated reduced bases;no double base reduction or invented output tax', () => {
|
|
const { draft, row } = allocationFixture();
|
|
addReduced(draft);
|
|
const expected = {
|
|
regulated_energy: '9.92',
|
|
aeronautical_fees: '13.60',
|
|
public_works: '6.40',
|
|
public_water: '12.00',
|
|
};
|
|
for (const [id, value] of Object.entries(expected)) {
|
|
const r = row('purchase-' + id, 'reduced', {
|
|
allocation: 'taxable_only',
|
|
});
|
|
r.d = adoptAllocationReduced(draft, r.d, id);
|
|
r.d.facts.scopeConfirmed = 'yes';
|
|
const result = evaluateAcquisition(r, 'MZN', 'yes', draft);
|
|
assert.equal(result.deductible, value, id);
|
|
assert.equal(r.net, '100');
|
|
assert.equal(r.vat, '16');
|
|
const book = recordAcquisitions(draft, {
|
|
currency: 'MZN',
|
|
taxpayerRegistered: 'yes',
|
|
rows: [r],
|
|
periods: [
|
|
{
|
|
period: '2026-12',
|
|
complete: true,
|
|
importsComplete: true,
|
|
absent: ['12', '13'],
|
|
},
|
|
],
|
|
});
|
|
assert.equal(book.result.periods[0].fields['15'], value);
|
|
for (const f of ['14', '16', '17', '18'])
|
|
assert.equal(book.result.periods[0].fields[f], null, f);
|
|
const saved = {
|
|
...draft,
|
|
modelo_a_snapshot: saveAcquisitionBook(draft, book),
|
|
};
|
|
assert.ok(acquisitionCurrent(saved, book));
|
|
const removed = {
|
|
...saved,
|
|
modelo_a_snapshot: saveReducedBaseBook(saved, {
|
|
rows: reducedBaseBook(saved).rows,
|
|
}),
|
|
};
|
|
assert.equal(acquisitionCurrent(removed, book), false);
|
|
assert.equal(
|
|
evaluateAcquisition(
|
|
{ ...r, allocation: 'mixed' },
|
|
'MZN',
|
|
'yes',
|
|
draft,
|
|
).deductible,
|
|
null,
|
|
);
|
|
const changed = reducedBaseBook(draft);
|
|
changed.rows.find((x) => x.id === id).description += ' alteração';
|
|
const other = {
|
|
...draft,
|
|
modelo_a_snapshot: saveReducedBaseBook(
|
|
draft,
|
|
recordReducedBases(draft, changed),
|
|
),
|
|
};
|
|
assert.equal(
|
|
evaluateAcquisition(r, 'MZN', 'yes', other).deductible,
|
|
null,
|
|
);
|
|
}
|
|
});
|
|
void test('save recovery/report preserves every prior ABC/L1 book and frozen D histories;no automatic recalculation', () => {
|
|
let { draft, book } = allocationFixture();
|
|
const prior = 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.ok(acquisitionCurrent(recovered, acquisitionBook(recovered)));
|
|
assert.deepEqual(book.history, [first]);
|
|
const current = JSON.parse(draft.modelo_a_snapshot);
|
|
for (const k of Object.keys(prior).filter((k) => k !== 'version'))
|
|
for (const [id, v] of Object.entries(prior[k]))
|
|
assert.deepEqual(current[k][id], v, k + id);
|
|
for (const scope of ['opportunity:368', 'opportunity:369'])
|
|
assert.ok(
|
|
acquisitionCurrent(
|
|
{ ...recovered, context_opportunity: scope },
|
|
prior.books[scope],
|
|
),
|
|
);
|
|
const report = JSON.stringify(acquisitionReportRows(recovered));
|
|
for (const token of [
|
|
'AfectaçãoD',
|
|
'16/3',
|
|
'1/3',
|
|
'34%',
|
|
'24.16',
|
|
'135.84',
|
|
'22(2)',
|
|
])
|
|
assert.ok(report.includes(token), token);
|
|
book.rows[0].d.numerator = '2';
|
|
draft.modelo_a_snapshot = saveAcquisitionBook(draft, book);
|
|
assert.equal(acquisitionCurrent(draft, book), false);
|
|
assert.deepEqual(book.history, [first]);
|
|
assert.match(
|
|
JSON.stringify(acquisitionReportRows(draft)),
|
|
/Histórico desactualizado/,
|
|
);
|
|
const broken = JSON.parse(recovered.modelo_a_snapshot);
|
|
broken.books['opportunity:370'].rows[0].d.facts.scopeConfirmed = 'maybe';
|
|
assert.throws(
|
|
() => readAcquisitionStore(JSON.stringify(broken)),
|
|
/AfectaçãoD/,
|
|
);
|
|
if (process.env.IVA_L2D_CALCULATED) {
|
|
const special = {
|
|
...recovered,
|
|
context_opportunity: 'opportunity:371',
|
|
};
|
|
special.modelo_a_snapshot = saveSalesBook(
|
|
special,
|
|
recordSales(
|
|
special,
|
|
structuredClone(current.salesOperations['opportunity:370']),
|
|
),
|
|
);
|
|
addReduced(special);
|
|
const reduced = allocationFixture().row(
|
|
'reduced-public-works',
|
|
'reduced',
|
|
{ allocation: 'taxable_only' },
|
|
);
|
|
reduced.d = adoptAllocationReduced(special, reduced.d, 'public_works');
|
|
reduced.d.facts.scopeConfirmed = 'yes';
|
|
const reducedBook = recordAcquisitions(special, {
|
|
currency: 'MZN',
|
|
taxpayerRegistered: 'yes',
|
|
rows: [reduced],
|
|
periods: [
|
|
{
|
|
period: '2026-12',
|
|
complete: true,
|
|
importsComplete: true,
|
|
absent: ['12', '13'],
|
|
},
|
|
],
|
|
});
|
|
special.modelo_a_snapshot = saveAcquisitionBook(special, reducedBook);
|
|
fs.writeFileSync(
|
|
process.env.IVA_L2D_CALCULATED,
|
|
JSON.stringify({
|
|
...special,
|
|
context_opportunity: 'opportunity:370',
|
|
}),
|
|
);
|
|
}
|
|
});
|