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

435 lines
14 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import {
irpcBook,
newIrpcRow,
recordIrpc,
saveIrpcBook,
reuseIrpcSources,
irpcCurrent,
irpcReportRows,
irpcPreparationHtml,
} from '../../resources/js/lib/irpc-withholdings.ts';
import {
acquisitionBook,
newAcquisition,
saveAcquisitionBook,
} from '../../resources/js/lib/iva-acquisitions.ts';
const seed = JSON.parse(
fs.readFileSync(
new URL('./fixtures/ispc-residual-preserved.json', import.meta.url),
'utf8',
),
);
const draft = () => ({ ...structuredClone(seed), fiscal_component: 'irpc' });
const row = (id = 'digital') => ({
...newIrpcRow(id),
counterparty: 'SYNTHETIC QA fornecedor não residente',
documentRef: 'SYNTHETIC-' + id,
base: '100000',
kind: 'digital_services',
recipientIrpc: 'yes',
payerIrpc: 'yes',
residence: 'nonresident',
permanentEstablishment: 'no',
mozambique: 'yes',
qualificationRef:
'SYNTHETIC QA: SaaS utilizado em Moçambique, devedor nacional,sem EP,Lei12/2025 arts5(5/7)/62(5)b',
specialRelief: 'none',
eventsComplete: 'yes',
events: [
{ kind: 'cost', date: '2026-10-02', reference: 'SYNTHETIC custo' },
{
kind: 'payment',
date: '2026-11-03',
reference: 'SYNTHETIC pagamento do rendimento',
},
],
settlement: 'none',
settlementRef: 'SYNTHETIC sem entrega',
});
const book = (r = row()) => ({ version: 1, rows: [r], payments: [] });
void test('positive digital goods/services and e-money have exact 10 percent and first applicable cost event', () => {
for (const kind of [
'digital_goods',
'digital_services',
'emoney_commission',
]) {
const r = recordIrpc(draft(), book({ ...row(), kind })).result.rows[0];
assert.equal(r.status, 'calculated');
assert.deepEqual(r.tax, { numerator: '10000', denominator: '1' });
assert.equal(r.eventDate, '2026-10-02');
assert.equal(r.nature, 'definitive');
assert.equal(r.period, '2026-10');
}
});
void test('unknown territory, recipient, payer, EP, event completeness, relief never become zero or nonapplicable', () => {
for (const patch of [
{ mozambique: '' },
{ recipientIrpc: '' },
{ payerIrpc: '' },
{ permanentEstablishment: '' },
{ eventsComplete: '' },
{ specialRelief: '' },
]) {
const r = recordIrpc(draft(), book({ ...row(), ...patch })).result
.rows[0];
assert.equal(r.status, 'indeterminate');
assert.equal(r.tax, null);
}
});
void test('resident and income attributable to EP preserve concrete 68d mapping gap; no 10 or20 automatic', () => {
for (const patch of [
{ residence: 'resident' },
{ permanentEstablishment: 'yes', attributable: 'yes' },
]) {
const r = recordIrpc(draft(), book({ ...row(), ...patch })).result
.rows[0];
assert.equal(r.tax, null);
assert.match(r.reason, /68/);
}
});
void test('nonresident with EP and explicitly nonattributable income has definitive positive branch', () =>
assert.equal(
recordIrpc(
draft(),
book({
...row(),
permanentEstablishment: 'yes',
attributable: 'no',
}),
).result.rows[0].status,
'calculated',
));
void test('ordinary and exceptional income need specific amended rule; no legacy2002 rates', () => {
for (const kind of [
'royalty',
'rent',
'capital',
'equipment',
'services',
'other',
]) {
const r = recordIrpc(draft(), book({ ...row(), kind })).result.rows[0];
assert.equal(r.tax, null);
assert.match(r.reason, /20\/2022/);
}
});
void test('event before2026 is not moved to later payment; impossible dates rejected', () => {
for (const date of ['2025-12-31', '2026-02-30']) {
const r = row();
r.events[0].date = date;
assert.equal(
recordIrpc(draft(), book(r)).result.rows[0].status,
'indeterminate',
);
}
});
void test('foreign amount and missing base remain indeterminate, explicit zero is distinct', () => {
for (const patch of [{ currency: 'USD' }, { base: '' }, { base: '-1' }])
assert.equal(
recordIrpc(draft(), book({ ...row(), ...patch })).result.rows[0]
.tax,
null,
);
assert.deepEqual(
recordIrpc(draft(), book({ ...row(), base: '0' })).result.rows[0].tax,
{ numerator: '0', denominator: '1' },
);
});
void test('partial remittance is separate from tax and recipient payment cannot settle tax', () => {
const r = {
...row(),
settlement: 'complete',
allocations: [{ paymentId: 'p1', amount: '6000' }],
};
const b = {
...book(r),
payments: [
{
id: 'p1',
kind: 'remittance',
reference: 'SYNTHETIC-AT1',
date: '2026-10-20',
amount: '6000',
currency: 'MZN',
},
],
};
const out = recordIrpc(draft(), b).result.rows[0];
assert.deepEqual(out.settled, { numerator: '6000', denominator: '1' });
assert.deepEqual(out.outstanding, { numerator: '4000', denominator: '1' });
b.payments[0].kind = 'beneficiary';
assert.equal(recordIrpc(draft(), b).result.rows[0].status, 'indeterminate');
});
void test('received withholding needs matching proof; no yearly credit or buyer debt generated', () => {
const r = {
...row(),
direction: 'received',
settlement: 'complete',
allocations: [{ paymentId: 'p1', amount: '1500' }],
base: '20000',
};
const b = {
...book(r),
payments: [
{
id: 'p1',
kind: 'withholding_proof',
reference: 'SYNTHETIC-PROOF1',
date: '2026-10-20',
amount: '1500',
currency: 'MZN',
},
],
};
const out = recordIrpc(draft(), b).result.rows[0];
assert.equal(out.direction, 'received');
assert.deepEqual(out.outstanding, { numerator: '500', denominator: '1' });
assert.equal('annualCredit' in out, false);
});
void test('unknown settlement stays null and no-payment with allocations is contradictory', () => {
let r = recordIrpc(draft(), book({ ...row(), settlement: '' })).result
.rows[0];
assert.equal(r.settled, null);
assert.equal(r.tax.numerator, '10000');
r = recordIrpc(
draft(),
book({ ...row(), allocations: [{ paymentId: 'p', amount: '1' }] }),
).result.rows[0];
assert.equal(r.status, 'indeterminate');
});
void test('same remittance can be split but never consumed twice, including other opportunities', () => {
const p = {
id: 'p',
kind: 'remittance',
reference: 'SYNTHETIC-SHARED',
date: '2026-10-20',
amount: '10000',
currency: 'MZN',
},
a = {
...row('a'),
settlement: 'complete',
allocations: [{ paymentId: 'p', amount: '5000' }],
},
b = {
...row('b'),
settlement: 'complete',
allocations: [{ paymentId: 'p', amount: '5000' }],
};
let bk = { version: 1, rows: [a, b], payments: [p] };
assert.ok(
recordIrpc(draft(), bk).result.rows.every(
(r) => r.status === 'calculated',
),
);
bk.rows[1].allocations[0].amount = '6000';
assert.ok(
recordIrpc(draft(), bk).result.rows.every(
(r) => r.status === 'indeterminate',
),
);
const d = draft();
d.modelo_a_snapshot = saveIrpcBook(d, {
version: 1,
rows: [a],
payments: [p],
});
d.context_opportunity = 'another';
assert.equal(
recordIrpc(d, {
version: 1,
rows: [{ ...b, allocations: [{ paymentId: 'p', amount: '6000' }] }],
payments: [p],
}).result.rows[0].status,
'indeterminate',
);
});
void test('duplicate document, duplicate proof ID or duplicate reference do not double totals', () => {
const r = row(),
d = draft();
assert.ok(
recordIrpc(d, {
version: 1,
rows: [r, { ...r, id: 'x' }],
payments: [],
}).result.rows.every((r) => r.status === 'indeterminate'),
);
const p = {
id: 'p',
kind: 'remittance',
reference: 'SYNTHETIC-SAME',
date: '2026-10-20',
amount: '1',
currency: 'MZN',
};
assert.throws(
() => saveIrpcBook(d, { ...book(), payments: [p, { ...p, id: 'p2' }] }),
/já registado/,
);
});
void test('reuse is idempotent, inherits supplier/document/amount, preserves deliberate edits and detects source delta', () => {
const d = draft(),
ab = acquisitionBook(d),
a = {
...newAcquisition('L5-supplier', '2026-10', 'MZN'),
supplier: 'SYNTHETIC saved supplier',
documentRef: 'SYNTHETIC invoice',
documentLine: '1',
net: '100000',
description: 'SaaS',
};
d.modelo_a_snapshot = saveAcquisitionBook(d, {
...ab,
rows: [...ab.rows, a],
});
let b = reuseIrpcSources(d, irpcBook(d));
assert.equal(reuseIrpcSources(d, b).rows.length, b.rows.length);
let r = b.rows.find((r) => r.source?.id === 'acquisition:L5-supplier');
assert.equal(r.counterparty, a.supplier);
assert.equal(r.documentRef, 'SYNTHETIC invoice / linha 1');
r.base = '90000';
assert.equal(
reuseIrpcSources(d, b).rows.find((v) => v.id === r.id).base,
'90000',
);
b = {
version: 1,
rows: [{ ...row(), id: r.id, source: r.source }],
payments: [],
};
const current = acquisitionBook(d);
d.modelo_a_snapshot = saveAcquisitionBook(d, {
...current,
rows: current.rows.map((v) =>
v.id === a.id ? { ...v, net: '120000' } : v,
),
});
assert.match(recordIrpc(d, b).result.rows[0].reason, /origem alterado/);
});
void test('save/recover preserves all preexisting maps, history, context separation and frozen report', () => {
const d = draft(),
prior = JSON.parse(d.modelo_a_snapshot),
b = recordIrpc(d, book());
d.modelo_a_snapshot = saveIrpcBook(d, b);
const now = JSON.parse(d.modelo_a_snapshot);
for (const [k, v] of Object.entries(prior)) assert.deepEqual(now[k], v);
assert.deepEqual(irpcBook(d).result, b.result);
assert.equal(irpcCurrent(d, b), true);
b.rows[0].base = '200000';
assert.equal(irpcCurrent(d, b), false);
assert.match(
irpcReportRows({ ...d, modelo_a_snapshot: saveIrpcBook(d, b) })[0][2],
/10000.00/,
);
assert.equal(recordIrpc(d, b).history.length, 1);
d.context_opportunity = 'other';
assert.equal(irpcBook(d).rows.length, 0);
});
void test('malformed store remains recoverable as explicit error, never overwritten blank', () => {
assert.throws(() =>
irpcBook({
modelo_a_snapshot: '{"version":1,"books":{},"irpcWithholdings":[]}',
}),
);
const d = draft(),
b = book();
b.rows[0].events = [{ kind: 'madeup', date: '2026-10-01', reference: 'x' }];
assert.throws(() => saveIrpcBook(d, b));
});
void test('export escapes private inputs, retains period/nature/gaps and excludes submission claims', () => {
const d = draft(),
r = { ...row(), counterparty: '<script>alert(1)</script>' };
d.modelo_a_snapshot = saveIrpcBook(d, recordIrpc(d, book(r)));
const html = irpcPreparationHtml(d);
assert.ok(!html.includes('<script>'));
assert.match(html, /&lt;script&gt;/);
assert.match(html, /sem submissão/);
assert.match(html, /CER/);
assert.match(html, /2026-10/);
assert.match(html, /Retenção definitiva/);
});
if (process.env.IRPC_FIXTURE_OUTPUT) {
const d = draft(),
b = recordIrpc(d, {
version: 1,
rows: [
{
...row(),
settlement: 'complete',
allocations: [{ paymentId: 'p1', amount: '6000' }],
},
{
...row('commission'),
base: '20000',
kind: 'emoney_commission',
direction: 'received',
settlement: 'complete',
allocations: [{ paymentId: 'p2', amount: '1500' }],
},
],
payments: [
{
id: 'p1',
kind: 'remittance',
reference: 'SYNTHETIC-AT1',
date: '2026-10-20',
amount: '6000',
currency: 'MZN',
},
{
id: 'p2',
kind: 'withholding_proof',
reference: 'SYNTHETIC-PROOF1',
date: '2026-10-20',
amount: '1500',
currency: 'MZN',
},
],
});
d.modelo_a_snapshot = saveIrpcBook(d, b);
fs.writeFileSync(
process.env.IRPC_FIXTURE_OUTPUT,
JSON.stringify(d, null, 2) + '\n',
);
}
void test('allocation changes in another opportunity mark previous result stale only for shared payments', () => {
const d = draft(),
p = {
id: 'p',
kind: 'remittance',
reference: 'SYNTHETIC-SHARED',
date: '2026-10-20',
amount: '10000',
currency: 'MZN',
},
r = {
...row(),
settlement: 'complete',
allocations: [{ paymentId: 'p', amount: '6000' }],
},
b = recordIrpc(d, { ...book(r), payments: [p] });
d.modelo_a_snapshot = saveIrpcBook(d, b);
assert.equal(irpcCurrent(d, b), true);
const ctx = d.context_opportunity;
d.context_opportunity = 'other';
d.modelo_a_snapshot = saveIrpcBook(d, book(row('unrelated')));
d.context_opportunity = ctx;
assert.equal(irpcCurrent(d, b), true);
d.context_opportunity = 'other';
d.modelo_a_snapshot = saveIrpcBook(d, {
...book({
...row('shared'),
settlement: 'complete',
allocations: [{ paymentId: 'p', amount: '1000' }],
}),
payments: [p],
});
d.context_opportunity = ctx;
assert.equal(irpcCurrent(d, b), false);
assert.match(irpcReportRows(d)[0][3], /Histórico/);
});