Files
Investidor-V2/tests/frontend/irpc-annual-settlement.test.mjs
T

570 lines
21 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import {
normalBook,
normalCurrent,
saveNormalBook,
} from '../../resources/js/lib/irpc-normal.ts';
import {
irpcBook,
newIrpcRow,
saveIrpcBook,
recordIrpc,
} from '../../resources/js/lib/irpc-withholdings.ts';
import {
annualBook,
newAnnualBook,
newAnnualPpc,
saveAnnualBook,
reuseAnnualAssessment,
annualAssessment,
annualWithholdingOptions,
reuseAnnualWithholding,
calculateAnnual,
recordAnnual,
annualCurrent,
annualReportRows,
annualHtml,
} from '../../resources/js/lib/irpc-annual-settlement.ts';
import { createProjectDossierReport } from '../../resources/js/lib/project-dossier-report.ts';
const read = (n) =>
JSON.parse(
fs.readFileSync(new URL('./fixtures/' + n, import.meta.url), 'utf8'),
);
const val = (v) => (v ? Number(v.numerator) / Number(v.denominator) : null);
const now = '2026-10-26T18:00:00+02:00';
const original = () => read('irpc-assessment-preserved.json');
function acceptedL7(expense = false) {
const d = read('irpc-l7-preserved.json')[
expense ? 'expense' : 'acceleration'
];
if (!expense) {
const f = read('irpc-l7-continuation-preserved.json');
d.business_plan_snapshot = f.plan;
d.modelo_a_snapshot = saveNormalBook(d, f.book);
}
return d;
}
function setup(rf = '300', ppc = '500') {
const d = original(),
l5 = irpcBook(d);
if (rf !== null) {
const row = {
...newIrpcRow('L8-received'),
direction: 'received',
origin: 'effective',
counterparty: 'QA pagador A',
documentRef: 'QA rendimento próprio 2026',
base: String(Number(rf) * 5),
currency: 'MZN',
kind: 'royalty',
recipientIrpc: 'yes',
payerIrpc: 'yes',
residence: 'resident',
mozambique: 'yes',
qualificationRef:
'QA royalties próprios incluídos no resultado anual',
specialRelief: 'none',
eventsComplete: 'yes',
events: [
{
kind: 'payment',
date: '2026-10-02',
reference: 'QA rendimento e primeiro evento 2026',
},
],
settlement: 'complete',
settlementRef: 'QA retenção efectivamente sofrida e reconciliada',
allocations: [{ paymentId: 'RF-L8', amount: rf }],
ordinary: {
nonDigital: 'yes',
capitalType: '',
rentOwnPropertyManagement: '',
specialFacts: 'no',
capitalSeatAndManagement: '',
reference: 'QA royalties não digitais de residente',
},
};
l5.rows.push(row);
l5.payments.push({
id: 'RF-L8',
kind: 'withholding_proof',
reference: 'QA-RF-2026-1',
date: '2026-10-02',
amount: rf,
currency: 'MZN',
});
d.modelo_a_snapshot = saveIrpcBook(d, recordIrpc(d, l5, now));
}
let b = reuseAnnualAssessment(d, newAnnualBook());
b.reference =
'QA entidade123456789,exercício2026,declaração integral dos abatimentos';
b.assessmentReference = 'QA colecta válida já demonstrada1408';
b.withholdings = {
state: rf === null ? 'absent' : 'known',
reference:
'QA inventário completo das retenções efectivamente sofridas por este sujeito no exercício',
rows: [],
};
b.ppc = {
state: ppc === null ? 'absent' : 'known',
reference: 'QA inventário completo dos PPC efectuados no exercício',
rows: [],
};
b.pec = { state: 'absent', amount: '', reference: 'QA ausência de PEC' };
b.credits = {
state: 'known',
amount: '0',
reference: 'QA créditos com regras próprias conhecidos zero',
};
if (rf !== null) {
b = reuseAnnualWithholding(d, b, 'L8-received', 'RF-L8', 'claim-rf');
Object.assign(b.withholdings.rows[0], {
incomeIncluded: 'yes',
reference:
'QA próprio beneficiário NUIT123456789;rendimento incluído na base anual2026',
});
}
if (ppc !== null)
b.ppc.rows.push({
...newAnnualPpc('claim-ppc'),
document: 'QA-PPC-2026-1',
issuer: 'QA AT',
date: '2026-06-30',
subjectNuit: b.subjectNuit,
year: b.year,
proofAmount: ppc,
amount: ppc,
effective: 'yes',
reference:
'QA recibo de PPC efectivo e afectação ao sujeito/exercício',
});
return { d, b };
}
const model = (d, b) => calculateAnnual(d, b);
const pending = (d, b) => {
const m = model(d, b);
assert.equal(m.status, 'indeterminate', m.pending.join(' | '));
assert.equal(m.arithmeticBalance, null);
assert.equal(m.payable, null);
assert.equal(m.recoverable, null);
return m;
};
void test('optional L8 leaves accepted assessment, other books and report rows untouched', () => {
const d = original(),
before = JSON.stringify(d),
b = normalBook(d);
assert.equal(normalCurrent(d, b), true);
assert.equal(val(annualAssessment(d).colecta), 1408);
assert.equal(annualBook(d), undefined);
assert.deepEqual(annualReportRows(d), []);
assert.equal(JSON.stringify(d), before);
const old = acceptedL7();
assert.deepEqual(annualReportRows(old), []);
assert.equal(
normalBook(old).result.model.version,
'IRPC-normal-adjustments/13',
);
});
void test('1408 minus actual received300 and provenPPC500 yields608 payable without consuming official balances', () => {
const { d, b } = setup(),
before = d.modelo_a_snapshot,
m = model(d, b);
assert.deepEqual(m.pending, []);
assert.equal(m.status, 'payable');
assert.equal(val(m.knownWithholdings), 300);
assert.equal(val(m.knownPpc), 500);
assert.equal(val(m.payable), 608);
assert.equal(val(m.recoverable), 0);
assert.equal(m.officialBalancesConsumed, false);
assert.equal(m.submitted, false);
assert.equal(d.modelo_a_snapshot, before);
});
void test('1408 minus1000 and600 yields192 recoverable;408 and1000 balances exactly', () => {
for (const [rf, ppc, status, balance] of [
['1000', '600', 'recoverable', -192],
['408', '1000', 'balanced', 0],
]) {
const { d, b } = setup(rf, ppc),
m = model(d, b);
assert.deepEqual(m.pending, []);
assert.equal(m.status, status);
assert.equal(val(m.arithmeticBalance), balance);
if (typeof balance !== 'number')
throw new Error('Invalid balance fixture');
assert.equal(val(m.recoverable), balance < 0 ? -balance : 0);
}
});
void test('declared absence yields1408;known zero has its own proof and missing never becomes zero', () => {
const { d, b } = setup(null, null);
assert.equal(val(model(d, b).payable), 1408);
b.credits.state = '';
assert.match(pending(d, b).pending.join(' '), /Créditos/);
b.credits = { state: 'absent', amount: '', reference: 'QA ausência' };
b.ppc.state = '';
assert.match(pending(d, b).pending.join(' '), /PPC/);
const zero = setup('0', '0');
assert.equal(val(model(zero.d, zero.b).payable), 1408);
assert.equal(model(zero.d, zero.b).input.withholdings.state, 'known');
});
void test('effective received L5 proof is required, not a projection,remittance or definitive withholding', () => {
for (const patch of [
{ origin: 'projected' },
{ direction: 'paid' },
{ residence: 'nonresident', permanentEstablishment: 'no' },
]) {
const { d, b } = setup(),
l5 = irpcBook(d);
Object.assign(
l5.rows.find((r) => r.id === 'L8-received'),
patch,
);
d.modelo_a_snapshot = saveIrpcBook(d, recordIrpc(d, l5, now));
const option = annualWithholdingOptions(d).find(
(o) => o.rowId === 'L8-received',
);
assert.equal(option.ready, false);
pending(d, b);
}
const { d, b } = setup(),
l5 = irpcBook(d);
l5.payments.find((p) => p.id === 'RF-L8').kind = 'remittance';
d.modelo_a_snapshot = saveIrpcBook(d, recordIrpc(d, l5, now));
pending(d, b);
});
void test('beneficiary,income inclusion,proof date,entity,exercise and actualPPC are mandatory', () => {
for (const change of [
(b) => (b.withholdings.rows[0].incomeIncluded = ''),
(b) => (b.withholdings.rows[0].reference = ''),
(b) => (b.withholdings.rows[0].subjectNuit = '999999999'),
(b) => (b.year = '2025'),
(b) => (b.ppc.rows[0].subjectNuit = '999999999'),
(b) => (b.ppc.rows[0].year = '2025'),
(b) => (b.ppc.rows[0].currency = 'USD'),
(b) => (b.ppc.rows[0].date = '2026-02-30'),
(b) => (b.ppc.rows[0].date = '2027-01-01'),
(b) => (b.ppc.rows[0].effective = 'no'),
(b) => (b.ppc.rows[0].reference = ''),
(b) => (b.reference = ''),
]) {
const { d, b } = setup();
change(b);
pending(d, b);
}
});
void test('selected current source and reusing collection never overwrite deliberate entity/year edits', () => {
const { d, b } = setup();
b.subjectNuit = '999999999';
b.year = '2025';
const next = reuseAnnualAssessment(d, b);
assert.equal(next.subjectNuit, '999999999');
assert.equal(next.year, '2025');
pending(d, next);
const other = setup();
other.b.assessmentKey = 'old-source';
pending(other.d, other.b);
});
void test('absence contradiction and incomplete group inventories are indeterminate,not assumed totals', () => {
for (const change of [
(b) => {
b.withholdings = {
state: 'absent',
reference: 'QA ausência contraditória',
rows: [],
};
},
(b) => (b.ppc.rows = []),
(b) => (b.ppc.state = 'absent'),
(b) => (b.withholdings.reference = ''),
]) {
const { d, b } = setup();
change(b);
pending(d, b);
}
});
void test('duplicate pair,duplicatePPC,overclaim and cross-kind physical proof cannot be deducted twice', () => {
for (const change of [
(b) =>
b.withholdings.rows.push({
...b.withholdings.rows[0],
id: 'duplicate',
}),
(b) => b.ppc.rows.push({ ...b.ppc.rows[0], id: 'duplicate' }),
(b) => (b.withholdings.rows[0].amount = '301'),
(b) => (b.ppc.rows[0].amount = '501'),
(b) =>
Object.assign(b.ppc.rows[0], {
document: ' QA-RF-2026-1 ',
issuer: 'QA pagador A',
date: '2026-10-02',
amount: '100',
proofAmount: '300',
}),
]) {
const { d, b } = setup();
change(b);
pending(d, b);
}
const { d, b } = setup();
assert.throws(
() => reuseAnnualWithholding(d, b, 'L8-received', 'RF-L8', 'again'),
/já seleccionada/,
);
});
void test('partial proof allocations allowed only within its available amount across context maps', () => {
const { d, b } = setup(null, '500');
b.ppc.rows[0].amount = '300';
const other = structuredClone(b);
other.ppc.rows[0].id = 'other';
other.ppc.rows[0].amount = '200';
const s = JSON.parse(d.modelo_a_snapshot);
s.irpcAnnual = { 'opportunity:other': other };
d.modelo_a_snapshot = JSON.stringify(s);
assert.equal(val(model(d, b).payable), 1108);
other.ppc.rows[0].amount = '201';
d.modelo_a_snapshot = JSON.stringify(s);
pending(d, b);
other.ppc.rows[0].amount = '100';
other.ppc.rows[0].subjectNuit = '999999999';
d.modelo_a_snapshot = JSON.stringify(s);
pending(d, b);
other.ppc.rows[0].subjectNuit = b.subjectNuit;
other.ppc.rows[0].year = '2025';
d.modelo_a_snapshot = JSON.stringify(s);
pending(d, b);
});
void test('positive PEC or specialcredit stays separated with null final balances,explicitzero permitted', () => {
for (const key of ['pec', 'credits']) {
const { d, b } = setup();
b[key] = {
state: 'known',
amount: '10',
reference: 'QA saldo positivo exige regra própria',
};
const m = pending(d, b);
assert.match(m.pending.join(' '), /fora deste recorte/);
assert.equal(val(m.knownPpc), 500);
b[key].amount = '0';
assert.equal(model(d, b).status, 'payable');
b[key] = {
state: 'absent',
amount: '0',
reference: 'QA ausência contradiz montante',
};
pending(d, b);
}
});
void test('changing receipt date cannot evade identity checks;copied L5 allocation cannot consume larger proof twice', () => {
const p = setup();
p.b.ppc.rows.push({
...p.b.ppc.rows[0],
id: 'other-date',
date: '2026-07-01',
amount: '100',
});
pending(p.d, p.b);
const { d, b } = setup();
const l5 = irpcBook(d);
l5.payments.find((p) => p.id === 'RF-L8').amount = '1000';
d.modelo_a_snapshot = saveIrpcBook(d, recordIrpc(d, l5, now));
b.withholdings.rows = [];
const fresh = reuseAnnualWithholding(d, b, 'L8-received', 'RF-L8', 'rf');
Object.assign(fresh.withholdings.rows[0], {
amount: '200',
incomeIncluded: 'yes',
reference: 'QA próprio beneficiário e rendimento incluído',
});
const other = structuredClone(fresh);
other.withholdings.rows[0].amount = '101';
other.ppc.rows = [];
other.ppc.state = 'absent';
const s = JSON.parse(d.modelo_a_snapshot);
s.irpcAnnual = { 'opportunity:other': other };
s.irpcWithholdings['opportunity:other'] = structuredClone(l5);
d.modelo_a_snapshot = JSON.stringify(s);
d.modelo_a_snapshot = saveIrpcBook(d, recordIrpc(d, irpcBook(d), now));
fresh.withholdings.rows[0].sourceKey = annualWithholdingOptions(d).find(
(o) => o.rowId === 'L8-received',
).key;
other.withholdings.rows[0].sourceKey = fresh.withholdings.rows[0].sourceKey;
const current = JSON.parse(d.modelo_a_snapshot);
current.irpcAnnual['opportunity:other'] = other;
d.modelo_a_snapshot = JSON.stringify(current);
assert.match(pending(d, fresh).pending.join(' '), /Mesma afectação L5/);
other.withholdings.rows[0].amount = '100';
d.modelo_a_snapshot = JSON.stringify(current);
assert.equal(model(d, fresh).status, 'payable');
});
void test('annual accepted L7 accelerated assessment is reused once rather than summed with L6', () => {
const d = read('irpc-l7-preserved.json').acceleration,
b = reuseAnnualAssessment(d, newAnnualBook());
b.reference = 'QA entidade/exercício';
b.assessmentReference = 'QA colecta L7';
for (const key of ['withholdings', 'ppc', 'pec', 'credits']) {
b[key].state = 'absent';
b[key].reference = 'QA ausência declarada';
}
const m = model(d, b);
assert.deepEqual(m.pending, []);
assert.equal(m.assessment.kind, 'special');
assert.equal(val(m.payable), 1996.8);
assert.notEqual(
val(m.payable),
val(normalBook(d).result.model.colecta) + 1996.8,
);
});
void test('100 minimum is applied symmetrically;99.99 kept exact without invented rounding or carry', () => {
for (const [ppc, status, balance] of [
['1308.01', 'below_minimum', 99.99],
['1308', 'payable', 100],
['1507.99', 'below_minimum', -99.99],
['1508', 'recoverable', -100],
]) {
const { d, b } = setup(null, ppc),
m = model(d, b);
assert.equal(m.status, status);
assert.equal(val(m.arithmeticBalance), balance);
if (status === 'below_minimum') {
assert.deepEqual(m.belowMinimum, {
numerator: '9999',
denominator: '100',
});
assert.equal(val(m.payable), 0);
assert.equal(val(m.recoverable), 0);
}
}
const { d, b } = setup();
b.ppc.rows[0].amount = '0.001';
pending(d, b);
});
void test('existing L7 source cannot be bypassed by choosing L6 and positiveautonomous/shortperiod remain scoped', () => {
for (const expense of [false, true]) {
const d = acceptedL7(expense);
let b = reuseAnnualAssessment(d, newAnnualBook());
b.reference = 'QA fecho';
b.assessmentReference = 'QA colecta';
for (const key of ['withholdings', 'ppc', 'pec', 'credits']) {
b[key].state = 'absent';
b[key].reference = 'QA sem movimentos';
}
assert.equal(b.assessment, 'special');
let m = pending(d, b);
if (!expense) assert.match(m.pending.join(' '), /períodos especiais/);
if (expense) assert.match(m.pending.join(' '), /Tributação autónoma/);
b.assessment = 'normal';
assert.match(pending(d, b).pending.join(' '), /não corrente/);
}
});
void test('record/recover/recalculate appends history unchanged,preserves all old books/plan,no double deduction', () => {
const { d, b } = setup(),
before = JSON.parse(d.modelo_a_snapshot),
plan = d.business_plan_snapshot;
d.modelo_a_snapshot = saveAnnualBook(d, recordAnnual(d, b, now));
const once = annualBook(d),
old = structuredClone(once.result);
assert.equal(annualCurrent(d, once), true);
d.modelo_a_snapshot = saveAnnualBook(d, recordAnnual(d, once, now));
const back = annualBook(JSON.parse(JSON.stringify(d)));
assert.deepEqual(back.history, [old]);
assert.equal(val(back.result.model.payable), 608);
assert.equal(d.business_plan_snapshot, plan);
const snapshot = JSON.parse(d.modelo_a_snapshot);
delete snapshot.irpcAnnual;
assert.deepEqual(snapshot, before);
b.ppc.rows[0].amount = '400';
assert.equal(annualCurrent(d, b), false);
});
void test('changed L5 or assessment invalidates current L8 while retaining historical report and references', () => {
const { d, b } = setup();
d.modelo_a_snapshot = saveAnnualBook(d, recordAnnual(d, b, now));
const back = annualBook(d),
old = structuredClone(back.result);
back.reference = 'NEW reference';
d.modelo_a_snapshot = saveAnnualBook(d, back);
assert.equal(annualCurrent(d, back), false);
assert.match(annualReportRows(d).flat().join(' '), /Histórico/);
assert.doesNotMatch(annualReportRows(d).flat().join(' '), /NEW reference/);
assert.throws(() => annualHtml(d), /fontes actuais/);
assert.deepEqual(back.result, old);
const n = normalBook(d);
n.base.identityReference += ' changed';
d.modelo_a_snapshot = saveNormalBook(d, n);
assert.equal(annualCurrent(d, back), false);
pending(d, back);
const changed = setup();
changed.d.modelo_a_snapshot = saveAnnualBook(
changed.d,
recordAnnual(changed.d, changed.b, now),
);
const saved = annualBook(changed.d),
prior = structuredClone(saved.result);
const l5 = irpcBook(changed.d);
l5.payments.find((p) => p.id === 'RF-L8').reference += ' corrected';
changed.d.modelo_a_snapshot = saveIrpcBook(
changed.d,
recordIrpc(changed.d, l5, now),
);
assert.equal(annualCurrent(changed.d, saved), false);
pending(changed.d, saved);
assert.deepEqual(saved.result, prior);
});
void test('malformed optionalbook never resets accepted books;report explicitly preserves missing states', () => {
const { d, b } = setup();
b.credits.state = '';
d.modelo_a_snapshot = saveAnnualBook(d, recordAnnual(d, b, now));
assert.match(annualHtml(d), /Indeterminado/);
assert.match(annualHtml(d), /Informação em falta/);
assert.match(
JSON.stringify(
createProjectDossierReport(d, {
selectedSections: new Set(['finance']),
}),
),
/Saldo anual simulado/,
);
const old = d.modelo_a_snapshot;
b.ppc.rows.push({ ...b.ppc.rows[0] });
assert.throws(() => saveAnnualBook(d, b), /identificador repetido/);
assert.equal(d.modelo_a_snapshot, old);
});
void test('prepare only L8 delta cases for guarded API and report recovery', () => {
const cases = {
payable: setup(),
recoverable: setup('1000', '600'),
balanced: setup('408', '1000'),
absent: setup(null, null),
missing: setup(),
duplicate: setup(),
pec: setup(),
};
cases.missing.b.ppc.rows[0].effective = '';
cases.duplicate.b.ppc.rows.push({
...cases.duplicate.b.ppc.rows[0],
id: 'duplicate',
});
cases.pec.b.pec = {
state: 'known',
amount: '10',
reference: 'QA PEC separado',
};
fs.mkdirSync('outputs/irpc-l8-settlement', { recursive: true });
for (const [name, { d, b }] of Object.entries(cases)) {
d.modelo_a_snapshot = saveAnnualBook(d, recordAnnual(d, b, now));
fs.writeFileSync(
'outputs/irpc-l8-settlement/' + name + '.json',
JSON.stringify(d),
);
fs.writeFileSync(
'outputs/irpc-l8-settlement/' + name + '.html',
annualHtml(d),
);
}
const { d } = setup();
fs.writeFileSync(
'outputs/irpc-l8-settlement/initial.json',
JSON.stringify(d),
);
});