Files
Investidor-V2/tests/frontend/ispc-residuals.test.mjs
T

529 lines
20 KiB
JavaScript

import {
ispcEligibilityBook,
saveIspcEligibilityBook,
} from '../../resources/js/lib/ispc-eligibility.ts';
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import {
ispcResidualBook,
saveIspcResidualBook,
recordIspcResidual,
ispcResidualCurrent,
ispcResidualReportRows,
ispcFormalPreparationHtml,
} from '../../resources/js/lib/ispc-residuals.ts';
import {
ispcWithholdingBook,
saveIspcWithholdingBook,
} from '../../resources/js/lib/ispc-withholding.ts';
import { createProjectDossierReport } from '../../resources/js/lib/project-dossier-report.ts';
const seed = JSON.parse(
fs.readFileSync(
new URL('./fixtures/ispc-quarter-preserved.json', import.meta.url),
'utf8',
),
);
function own(p = {}) {
const d = structuredClone(seed),
b = ispcResidualBook(d, 'occasional');
b.input = {
...b.input,
personType: 'singular',
currency: 'MZN',
origin: 'projected',
operationDate: '2026-06-15',
quarter: '2',
noHabitual: 'yes',
territory: 'yes',
sporadic: 'yes',
activity: 'agriculture',
reference: 'SYNTHETIC QA colheita não reiterada',
amount: '100000',
allQuarterOperations: 'yes',
priorYearTurnover: '0',
priorYearRef: 'SYNTHETIC QA2025zeroexpresso',
organizedRequired: 'no',
option: 'preparation',
optionRef: '',
optionDate: '',
settlement: 'self',
annual2026: '100000',
annualOrigin: 'forecast',
annualRef: 'SYNTHETIC QA2026',
priorInYear: '0',
...p,
};
return { d, b };
}
function acq(p = {}, wpatch = {}) {
const d = structuredClone(seed),
w = ispcWithholdingBook(d);
w.input = {
...w.input,
applicable: 'yes',
quarter: '2',
base: '200000',
priorAnnualPurchases: '2400000',
naturalOccasional: 'yes',
unregistered: 'yes',
related: 'yes',
activity: 'agriculture',
reference: 'SYNTHETIC QA aquisição/ordem e acumulado',
...wpatch,
};
d.modelo_a_snapshot = saveIspcWithholdingBook(d, w);
const b = ispcResidualBook(d, 'acquisitions');
b.input = { ...b.input, ...p };
return { d, b };
}
const calc = ({ d, b }) => recordIspcResidual(d, b, '2026-10-25T15:40:00Z');
const number = (x) => (x ? Number(x.numerator) / Number(x.denominator) : null);
void test('positive ownoccasional agricultural isolated8h route;self3/4/5 andnoautomaticIRPS20', () => {
for (const [annual, n] of [
['100000', 3],
['2000000', 4],
['3000000', 5],
]) {
const r = calc(own({ annual2026: annual })).result;
assert.equal(r.status, 'calculated', r.missing.join(';'));
assert.equal(number(r.amounts.tax), 1000 * n);
assert.match(r.source, /8\(3h,4\)/);
assert.equal(r.details.IRPS.includes('Não se infere'), true);
}
});
void test('unknown notnonapplicable;known exclusions andactivityfamilyownscope', () => {
assert.equal(calc(own({ personType: '' })).result.status, 'indeterminate');
for (const p of [
{ personType: 'collective' },
{ noHabitual: 'no' },
{ sporadic: 'no' },
{ territory: 'no' },
{ activity: 'other' },
])
assert.equal(calc(own(p)).result.status, 'not_applicable');
for (const p of [
{ territory: '' },
{ currency: 'USD' },
{ quarter: '3' },
{ reference: '' },
{ priorYearTurnover: '' },
{ organizedRequired: '' },
])
assert.equal(calc(own(p)).result.status, 'indeterminate');
});
void test('ownminimum500withoutlosingdeclaration;zeroexplicit and4mexcessnotreused', () => {
let r = calc(own({ amount: '1000' })).result;
assert.equal(number(r.amounts.tax), 30);
assert.equal(number(r.amounts.payment), 0);
assert.match(r.details.declaration, /Mantida/);
r = calc(own({ amount: '0' })).result;
assert.equal(number(r.amounts.tax), 0);
assert.equal(calc(own({ amount: '' })).result.status, 'indeterminate');
r = calc(
own({
amount: '200000',
priorInYear: '3900000',
annual2026: '4100000',
}),
).result;
assert.equal(number(r.amounts.ordinary), 100000);
assert.equal(number(r.amounts.excess), 100000);
assert.equal(number(r.amounts.tax), 25000);
});
void test('documentedliberatory5meansno secondselftax;smallwithholdingnotminimum500;divergenceunknown', () => {
const p = {
settlement: 'withheld',
origin: 'effective',
buyerIspc: 'yes',
unregistered: 'yes',
buyerAnnualPurchases: '200000',
withheldActual: '5000',
withheldRef: 'SYNTHETIC QA recibo5',
};
let r = calc(own(p)).result;
assert.equal(r.status, 'settled_by_withholding', r.missing.join(';'));
assert.equal(number(r.amounts.withheld), 5000);
assert.equal(r.amounts.payment, undefined);
assert.match(r.reason, /não somar nova liquidação/);
r = calc(own({ ...p, amount: '100', withheldActual: '5' })).result;
assert.equal(r.status, 'settled_by_withholding');
assert.equal(number(r.amounts.tax), 5);
assert.equal(
calc(own({ ...p, withheldActual: '4000' })).result.status,
'indeterminate',
);
assert.equal(
calc(own({ ...p, buyerAnnualPurchases: '2500000.000001' })).result
.status,
'indeterminate',
);
});
void test('selfeffective requiresapplicableoption;forecastcannot masqueradeasactual;nonoptionnoISPC', () => {
assert.equal(
calc(own({ origin: 'effective' })).result.status,
'indeterminate',
);
assert.equal(
calc(own({ option: 'not_opted' })).result.status,
'not_applicable',
);
const r = calc(
own({
origin: 'effective',
operationDate: '2026-12-31',
quarter: '4',
option: 'opted',
optionRef: 'QA opção',
optionDate: '2026-01-01',
annualOrigin: 'closed_year',
}),
).result;
assert.equal(r.status, 'calculated', r.missing.join(';'));
assert.equal(
calc(
own({
origin: 'effective',
option: 'opted',
optionRef: 'QA',
optionDate: '2026-07-01',
}),
).result.status,
'indeterminate',
);
});
void test('above2.5m splits100kcovered/100kexcess;5kknownbuttotalnullwithunknownroute', () => {
const r = calc(acq()).result;
assert.equal(r.status, 'partial');
assert.equal(number(r.amounts.covered), 100000);
assert.equal(number(r.amounts.coveredTax), 5000);
assert.equal(number(r.amounts.excess), 100000);
assert.equal(r.amounts.excessTax, null);
assert.equal(r.amounts.total, null);
});
void test('positiveIRPS57k20onidentifiedgrossact—separatefromISPCexcess20andownbuyerpayment', () => {
const p = {
route: 'irps_isolated',
noIspcOnExcess: 'yes',
isolatedAct: 'yes',
incomeInMoz: 'yes',
grossExcess: '100000',
grossRef: 'SYNTHETIC QA base ilíquida',
reference: 'SYNTHETIC QA8h/57k sem opção5',
};
const r = calc(acq(p)).result;
assert.equal(r.status, 'calculated', r.missing.join(';'));
assert.equal(number(r.amounts.excessTax), 20000);
assert.equal(number(r.amounts.total), 25000);
assert.match(r.reason, /não são crédito/);
for (const patch of [
{ grossExcess: '' },
{ grossExcess: '80000' },
{ noIspcOnExcess: '' },
{ isolatedAct: '' },
{ incomeInMoz: 'no' },
{ reference: '' },
]) {
const r = calc(acq({ ...p, ...patch })).result;
assert.equal(r.status, 'partial');
assert.equal(r.amounts.total, null);
}
});
void test('supplierISPCnotautomaticallyIRPS;crossingexactboundaryandprioroverlimit;noimplicit5onexcess', () => {
let r = calc(
acq({
route: 'ispc_supplier',
supplierOptionRef: 'QA opção aplicável',
}),
).result;
assert.equal(r.status, 'partial');
assert.equal(r.amounts.excessTax, null);
assert.match(r.reason, /art5impede/);
r = calc(acq({}, { base: '100000' })).result;
assert.equal(r.status, 'not_applicable');
assert.equal(number(r.amounts.excess), 0);
r = calc(acq({}, { priorAnnualPurchases: '2500001' })).result;
assert.equal(number(r.amounts.covered), 0);
assert.equal(number(r.amounts.coveredTax), 0);
assert.equal(number(r.amounts.excess), 200000);
});
void test('newbooks preservehabitualandL3exactly;dependenciesstale withoutrewritingfrozenresults', () => {
let { d, b } = acq();
const old = JSON.parse(d.modelo_a_snapshot);
b = calc({ d, b });
d.modelo_a_snapshot = saveIspcResidualBook(d, b);
for (const [k, v] of Object.entries(old))
assert.deepEqual(JSON.parse(d.modelo_a_snapshot)[k], v);
assert.equal(ispcResidualCurrent(d, b), true);
const w = ispcWithholdingBook(d);
w.input.base = '300000';
d.modelo_a_snapshot = saveIspcWithholdingBook(d, w);
assert.equal(ispcResidualCurrent(d, b), false);
assert.equal(number(b.result.amounts.excess), 100000);
const next = recordIspcResidual(d, b);
assert.equal(next.history.length, 1);
assert.equal(number(next.result.amounts.excess), 200000);
assert.equal(number(next.history[0].amounts.excess), 100000);
});
void test('formalBGeneral mappingfromexistingcurrentquarterwithoutrecalculation;monthnotinferred;old30notused', () => {
const d = structuredClone(seed),
b = ispcResidualBook(d, 'formalities'),
before = d.modelo_a_snapshot;
const result = recordIspcResidual(d, b);
assert.equal(result.result.status, 'partial');
assert.equal(number(result.result.amounts.tax), 24000);
assert.equal(number(result.result.amounts.payment), 24000);
assert.match(result.result.details.formPeriod, /Indeterminado/);
assert.equal(result.result.details.paymentMonth, 'Julho2026');
assert.equal(result.input.formMonth, '');
d.modelo_a_snapshot = saveIspcResidualBook(d, result);
const html = ispcFormalPreparationHtml(d);
assert.match(html, /24000.00/);
assert.match(html, /Quadro8/);
assert.match(html, /Quadro10/);
assert.match(html, /não é formulário oficial/);
assert.deepEqual(
JSON.parse(d.modelo_a_snapshot).ispcQuarters,
JSON.parse(before).ispcQuarters,
);
});
void test('formalinputsguardedandexternalactsdeclaredonly;signed/officialareasnotautofilled;HTMLescaped', () => {
let d = structuredClone(seed),
b = ispcResidualBook(d, 'formalities');
b.input = {
...b.input,
name: 'QA <script>bad()</script>',
nuit: '123456789',
fiscalArea: 'QAUGC',
address: 'QAendereço',
formMonth: '06',
formYear: '2026',
formPeriodRef: 'SYNTHETIC QA correspondência período',
interestSituation: 'none',
interestRef: 'SYNTHETIC QA sem juros',
};
b = recordIspcResidual(d, b);
assert.equal(b.result.status, 'calculated', b.result.missing.join(';'));
d.modelo_a_snapshot = saveIspcResidualBook(d, b);
const html = ispcFormalPreparationHtml(d);
assert.ok(html.includes('&lt;script&gt;'));
assert.ok(!html.includes('<script>bad'));
assert.match(html, /não preencher.*assinatura/i);
b.input.externalStatus = 'paid';
assert.equal(recordIspcResidual(d, b).result.status, 'partial');
});
void test('documentedwithholdingownroutepreventsduplicatedformalselfpayment', () => {
let { d, b } = own({
settlement: 'withheld',
buyerIspc: 'yes',
unregistered: 'yes',
buyerAnnualPurchases: '200000',
withheldActual: '5000',
withheldRef: 'QA recibo5',
});
b = recordIspcResidual(d, b);
d.modelo_a_snapshot = saveIspcResidualBook(d, b);
const f = ispcResidualBook(d, 'formalities');
f.input.sourceKind = 'occasional';
const r = recordIspcResidual(d, f).result;
assert.equal(r.status, 'not_applicable');
assert.match(r.reason, /duplicada/);
});
void test('invalidstoredmaps/enumspreserveerrors;noemptyfallback', () => {
const d = structuredClone(seed),
s = JSON.parse(d.modelo_a_snapshot);
s.ispcResiduals = [];
assert.throws(() =>
ispcResidualBook(
{ ...d, modelo_a_snapshot: JSON.stringify(s) },
'occasional',
),
);
const b = ispcResidualBook(d, 'occasional');
b.input.activity = 'services12';
assert.throws(() => saveIspcResidualBook(d, b));
});
void test('exports separate seller376/buyer375 and report;habitualbooksunchanged', () => {
let { d, b } = own();
d.context_opportunity = 'opportunity:376';
d.person_type = 'singular';
d.project_title = 'SYNTHETIC QA — ISPC residuais';
const e = ispcEligibilityBook(d);
e.input.mode = 'occasional';
e.input.personType = 'singular';
d.modelo_a_snapshot = saveIspcEligibilityBook(d, e);
b = recordIspcResidual(d, b, '2026-10-25T15:40:00Z');
d.modelo_a_snapshot = saveIspcResidualBook(d, b);
d.context_opportunity = 'opportunity:375';
d.person_type = 'collective';
const a = acq({
route: 'irps_isolated',
noIspcOnExcess: 'yes',
isolatedAct: 'yes',
incomeInMoz: 'yes',
grossExcess: '100000',
grossRef: 'QA base ilíquida',
reference: 'QA acto57k',
});
const aw = ispcWithholdingBook(a.d);
d.modelo_a_snapshot = saveIspcWithholdingBook(d, aw);
d.modelo_a_snapshot = saveIspcResidualBook(
d,
recordIspcResidual(d, a.b, '2026-10-25T15:41:00Z'),
);
const f = ispcResidualBook(d, 'formalities');
d.modelo_a_snapshot = saveIspcResidualBook(
d,
recordIspcResidual(d, f, '2026-10-25T15:42:00Z'),
);
const report = createProjectDossierReport(d, {
selectedSections: new Set(['finance']),
version: 'QA residuais',
generatedAt: '2026-10-25T15:43:00Z',
});
assert.match(JSON.stringify(report), /25000.00/);
assert.match(JSON.stringify(report), /24000.00/);
assert.match(JSON.stringify(ispcResidualReportRows(d)), /IRPS57/);
d.context_opportunity = 'opportunity:376';
assert.equal(ispcResidualBook(d, 'occasional').result.status, 'calculated');
assert.equal(
number(ispcResidualBook(d, 'occasional').result.amounts.tax),
3000,
);
d.context_opportunity = 'opportunity:375';
if (process.env.ISPC_RESIDUAL_OUTPUT)
fs.writeFileSync(
process.env.ISPC_RESIDUAL_OUTPUT,
JSON.stringify(d, null, 2),
);
});
void test('new uniqueformsource preserves deliberatechoice;documentedoptionwithannualforecast staysscenario', () => {
let { d, b } = own({
origin: 'effective',
option: 'opted',
optionRef: 'QA opção',
optionDate: '2026-01-01',
});
b = recordIspcResidual(d, b);
assert.equal(b.result.status, 'calculated');
assert.equal(b.result.details.projected, 'yes');
assert.match(b.result.reason, /Cenário declarado/);
const e = ispcEligibilityBook(d);
e.input.mode = 'occasional';
d.modelo_a_snapshot = saveIspcEligibilityBook(d, e);
d.modelo_a_snapshot = saveIspcResidualBook(d, b);
let f = ispcResidualBook(d, 'formalities');
assert.equal(f.input.sourceKind, 'occasional');
f.input.sourceKind = 'quarter';
d.modelo_a_snapshot = saveIspcResidualBook(d, f);
assert.equal(
ispcResidualBook(d, 'formalities').input.sourceKind,
'quarter',
);
});
void test('acquisitionformalpreparation distinguishesISPC5andIRPS20andkeepsspecificexternalmodelpending', () => {
let { d, b } = acq({
route: 'irps_isolated',
noIspcOnExcess: 'yes',
isolatedAct: 'yes',
incomeInMoz: 'yes',
grossExcess: '100000',
grossRef: 'QA base ilíquida',
reference: 'QA57k',
});
b = recordIspcResidual(d, b);
d.modelo_a_snapshot = saveIspcResidualBook(d, b);
let f = ispcResidualBook(d, 'formalities');
f.input.sourceKind = 'acquisitions';
f = recordIspcResidual(d, f);
assert.equal(number(f.result.amounts.tax), 25000);
assert.ok(f.result.missing.some((s) => s.includes('Modelo/código')));
assert.equal(f.result.status, 'partial');
d.modelo_a_snapshot = saveIspcResidualBook(d, f);
const html = ispcFormalPreparationHtml(d);
assert.match(html, /ISPC—retenção12/);
assert.match(html, /IRPS—acto isolado57/);
assert.match(html, /5000.00/);
assert.match(html, /20000.00/);
assert.match(html, /Por corresponder/);
});
void test('knownownaccess exclusions are concrete notrequestsforrevalidation;unknownstillindeterminate', () => {
for (const patch of [
{ organizedRequired: 'yes' },
{ priorYearTurnover: '4000000.000001' },
]) {
const r = calc(own(patch)).result;
assert.equal(r.status, 'not_applicable');
assert.equal(r.amounts.payment, undefined);
assert.match(r.reason, /fora/i);
}
assert.equal(
calc(own({ organizedRequired: '' })).result.status,
'indeterminate',
);
});
void test('below500paymentdispensa iscalculatedbutformalrepresentationnotinventedaszerocalculatedtax', () => {
let { d, b } = own({ amount: '1000' });
b = recordIspcResidual(d, b);
d.modelo_a_snapshot = saveIspcResidualBook(d, b);
let f = ispcResidualBook(d, 'formalities');
f.input = {
...f.input,
sourceKind: 'occasional',
name: 'QA vendedor',
nuit: '123456789',
fiscalArea: 'QAUGC',
address: 'QA endereço',
formMonth: '06',
formYear: '2026',
formPeriodRef: 'QA correspondência',
interestSituation: 'none',
interestRef: 'QA sem juros',
};
f = recordIspcResidual(d, f);
assert.equal(number(f.result.amounts.tax), 30);
assert.equal(number(f.result.amounts.payment), 0);
assert.equal(f.result.status, 'partial');
assert.ok(f.result.missing.some((s) => s.includes('dispensa')));
d.modelo_a_snapshot = saveIspcResidualBook(d, f);
const html = ispcFormalPreparationHtml(d);
assert.match(html, /Imposto calculado:30.00;prestação legal:0.00/);
});
void test('switchfromdocumentedliberatoryroute cannotgenerate secondduewithoutconcretereconciliation', () => {
let { d, b } = own({
settlement: 'withheld',
buyerIspc: 'yes',
unregistered: 'yes',
buyerAnnualPurchases: '200000',
withheldActual: '5000',
withheldRef: 'QA retenção definitiva',
});
b = recordIspcResidual(d, b);
assert.equal(b.result.status, 'settled_by_withholding');
b.input.settlement = 'self';
let next = recordIspcResidual(d, b);
assert.equal(next.result.status, 'partial');
assert.equal(next.result.amounts.payment, null);
assert.equal(next.result.amounts.tax, null);
assert.equal(number(next.result.amounts.withheld), 5000);
assert.equal(next.history[0].status, 'settled_by_withholding');
const fresh = structuredClone(b);
fresh.input.reference = 'QA outra operação';
assert.equal(recordIspcResidual(d, fresh).result.status, 'calculated');
b.input.withholdingCorrected = 'yes';
b.input.withholdingCorrectionRef = 'QA documento externo de rectificação';
next = recordIspcResidual(d, b);
assert.equal(next.result.status, 'calculated');
assert.equal(number(next.result.amounts.payment), 3000);
assert.equal(
next.result.details.priorWithholdingRef,
'QA retenção definitiva',
);
assert.equal(next.history[0].status, 'settled_by_withholding');
});