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

346 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import {
normalBook,
normalCurrent,
calculateNormal,
recordNormal,
saveNormalBook,
normalHtml,
} from '../../resources/js/lib/irpc-normal.ts';
import { validateSpecial } from '../../resources/js/lib/irpc-special.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);
function accepted() {
const d = read('irpc-l7-preserved.json').acceleration,
f = read('irpc-l7-continuation-preserved.json');
d.business_plan_snapshot = f.plan;
d.modelo_a_snapshot = saveNormalBook(d, f.book);
return { d, b: normalBook(d) };
}
function setup(expense = false) {
const { d, b } = accepted();
const i = b.special;
Object.assign(i.cooperative, {
kind: 'agrarian',
allIncomeEligible: 'yes',
withheldIncome: 'absent',
});
Object.assign(i.cooperative.qualification, {
state: 'known',
eligible: 'yes',
reference:
'QA cooperativa agrária12,base ordinária integralmente elegível;sem rendimentos sujeitos a retenção',
});
i.acceleration.rows[0].kind = 'agroindustrial_equipment';
i.combinations = {
version: 1,
reference:
'QA mesmo sujeito/projecto/período;reduçãoCIRPC12 eCBF16 genérico,efeitos em etapas distintas',
specificState: 'absent',
specificReference:
'QA sem estatuto/benefício específicoCBF20–48 neste projecto/período;CBF13',
specificException: '',
otherState: 'absent',
otherReference:
'QA sem outros benefícios além dos tratamentosL7 registados',
};
if (expense) {
const old = normalBook(read('irpc-l7-preserved.json').expense);
b.schedules.rows.push(structuredClone(old.schedules.rows[0]));
b.profit.topics.expenseCorrections = structuredClone(
old.profit.topics.expenseCorrections,
);
i.expenses = structuredClone(old.special.expenses);
Object.assign(i.expenses.qualification, {
from: b.periodsFx.from,
to: b.periodsFx.to,
});
}
return { d, b };
}
void test('accepted13 model,key and allhistorical balances reproduce exactly without optionalcombination', () => {
const { d, b } = accepted();
assert.equal(normalCurrent(d, b), true);
assert.deepEqual(calculateNormal(d, b), b.result.model);
assert.deepEqual(normalBook(d).history, b.history);
});
void test('qualified cooperative12 and genericCBF16 combine in order,additional25 once then16percent,prior15 already included', () => {
const { d, b } = setup(),
assets = JSON.stringify(b.depreciation),
m = calculateNormal(d, b),
s = m.special,
c = s.combinations;
assert.equal(m.version, 'IRPC-normal-adjustments/14');
assert.equal(c.status, 'compatible');
assert.equal(val(m.taxableProfit), 3130);
assert.equal(val(m.colecta), 1001.6);
assert.equal(val(s.acceleration.additionalDeduction), 25);
assert.equal(val(s.taxableProfit), 3105);
assert.equal(val(s.taxableBase), 3105);
assert.equal(val(s.cooperative.rate), 16);
assert.equal(val(s.colecta), 496.8);
assert.equal(val(s.combinedColecta), 496.8);
assert.equal(val(c.assetClaims[0].acceleratedDeduction), 75);
assert.equal(val(c.assetClaims[0].priorRecoveredIncluded), 15);
assert.equal(val(c.assetClaims[0].additionalDeduction), 25);
assert.equal(JSON.stringify(b.depreciation), assets);
assert.equal(s.finalBalance.payable, null);
assert.doesNotMatch(s.acceleration.source, /Sem cumulação[.]/);
});
void test('autonomousexpense35percent is not halved by cooperative rate or added again to the taxableprofit', () => {
const { d, b } = setup(true),
m = calculateNormal(d, b),
s = m.special;
assert.equal(val(m.taxableProfit), 3230);
assert.equal(val(s.taxableProfit), 3205);
assert.equal(val(s.taxableBase), 3205);
assert.equal(val(s.expenses.rows[0].l6Adjustment), 100);
assert.equal(val(s.expenses.rows[0].additionalProfitAdjustment), 0);
assert.equal(val(s.expenses.autonomousTax), 35);
assert.equal(val(s.colecta), 512.8);
assert.equal(val(s.combinedColecta), 547.8);
assert.equal(val(s.combinations.limits.autonomousRate), 35);
});
void test('specificCBF22 with generic16 and declaredabsence of exception is incompatible,neither benefit automatically selected', () => {
const { d, b } = setup();
Object.assign(b.special.combinations, {
specificState: 'known',
specificReference:
'QA CBF22 benefício específico infra-estruturas declarado no mesmo projecto/período',
specificException: 'absent',
});
const input = structuredClone(b.special),
m = calculateNormal(d, b),
s = m.special;
assert.equal(s.combinations.status, 'incompatible');
assert.equal(s.acceleration.additionalDeduction, null);
assert.equal(s.taxableProfit, null);
assert.equal(s.colecta, null);
assert.equal(s.combinedColecta, null);
assert.equal(val(m.colecta), 1001.6);
assert.deepEqual(b.special, input);
assert.match(s.pending.join(' '), /IncompatibilidadeCBF4\/13/);
});
void test('claimedexception,unknownspecificpresence,otherbenefit or missingsupport remain indeterminate without provisionalrate', () => {
for (const patch of [
{
specificState: 'known',
specificReference:
'QA excepção invocada,artigo/limite não correspondidos',
specificException: 'claimed',
},
{ specificState: '' },
{
otherState: 'known',
otherReference: 'QA outro benefício fora dos ramos calculados',
},
{ reference: '' },
{ specificReference: '' },
{ otherReference: '' },
]) {
const { d, b } = setup();
Object.assign(b.special.combinations, patch);
const m = calculateNormal(d, b);
assert.equal(m.special.combinations.status, 'indeterminate');
assert.equal(m.special.colecta, null);
assert.equal(val(m.colecta), 1001.6);
}
});
void test('missingcooperativeperiod or mixedincome blocks its dependenttotal without choosing eligibility from location', () => {
for (const patch of [
{ allIncomeEligible: 'no' },
{ withheldIncome: 'known' },
]) {
const { d, b } = setup();
Object.assign(b.special.cooperative, patch);
d.province_id = 'QA-ZEE-address';
const s = calculateNormal(d, b).special;
assert.equal(s.colecta, null);
assert.match(s.cooperative.reason, /mistas\/retidas/);
}
const { d, b } = setup();
b.special.cooperative.qualification.from = '2026-02-01';
assert.equal(calculateNormal(d, b).special.colecta, null);
});
void test('duplicateassetqualification cannot duplicate CBF16 deduction;remainingfiscal20 cap keepsadditionalzero', () => {
const { d, b } = setup();
b.special.acceleration.rows.push({
...b.special.acceleration.rows[0],
id: 'same-asset-again',
});
let s = calculateNormal(d, b).special;
assert.equal(s.acceleration.additionalDeduction, null);
assert.match(s.acceleration.reason, /Activo repetido/);
const cap = setup();
Object.assign(cap.b.depreciation.assets[0], {
previousAccounting: '1000',
previousFiscal: '980',
priorAvailable: '20',
});
s = calculateNormal(cap.d, cap.b).special;
assert.equal(val(s.acceleration.additionalDeduction), 0);
assert.equal(val(s.combinations.assetClaims[0].acceleratedDeduction), 20);
assert.equal(val(s.combinations.assetClaims[0].remainingFiscal), 0);
});
void test('negativecooperative or absentCBF16 retains the other independentlyqualified treatment', () => {
const negative = setup();
negative.b.special.cooperative.qualification.eligible = 'no';
let s = calculateNormal(negative.d, negative.b).special;
assert.equal(s.cooperative.status, 'not_applicable');
assert.equal(val(s.colecta), 993.6);
const absent = setup();
absent.b.special.acceleration.rows = [];
Object.assign(absent.b.special.acceleration.qualification, {
state: 'absent',
eligible: '',
});
s = calculateNormal(absent.d, absent.b).special;
assert.equal(s.acceleration.status, 'absent');
assert.equal(val(s.acceleration.additionalDeduction), 0);
assert.equal(val(s.colecta), 500.8);
});
void test('save-recover14 appends13 verbatim,leaves inputbalances/plan/otherbooks unchanged,and report explains order and no double deduction', () => {
const { d, b } = setup(true),
old = structuredClone(b.result),
history = structuredClone(b.history),
assets = JSON.stringify(b.depreciation),
plan = d.business_plan_snapshot,
other = JSON.parse(d.modelo_a_snapshot).irpcWithholdings;
d.modelo_a_snapshot = saveNormalBook(d, recordNormal(d, b));
let back = normalBook(JSON.parse(JSON.stringify(d)));
assert.equal(normalCurrent(d, back), true);
assert.deepEqual(back.history, [...history, old]);
assert.equal(old.model.version, 'IRPC-normal-adjustments/13');
assert.equal(JSON.stringify(back.depreciation), assets);
assert.equal(d.business_plan_snapshot, plan);
assert.deepEqual(JSON.parse(d.modelo_a_snapshot).irpcWithholdings, other);
const html = normalHtml(d);
assert.match(html, /Combinação permitida no recorte/);
assert.match(html, /parcela anterior já incluída na dedução CBF16:15.00/);
assert.match(html, /única diferença adicional:25.00/);
assert.match(html, /imposto autónomo independente:35.00/);
assert.match(html, /colecta combinada:547.80/);
assert.match(html, /Saldo final a pagar\/recuperar:Indeterminado/);
assert.match(
JSON.stringify(
createProjectDossierReport(d, {
selectedSections: new Set(['finance']),
}),
),
/Cumulações — qualificação/,
);
d.modelo_a_snapshot = saveNormalBook(d, recordNormal(d, back));
back = normalBook(d);
assert.equal(val(back.result.model.special.combinedColecta), 547.8);
assert.equal(back.depreciation.assets[0].priorAvailable, '50');
back.special.combinations.otherState = 'known';
assert.equal(normalCurrent(d, back), false);
});
void test('positive unclassified L6benefit is preserved and blocks another deduction until reconciled;zero base never offsets autonomous35', () => {
const extra = setup();
extra.b.base.benefits = {
state: 'known',
amount: '100',
reference: 'QA dedução já declarada sem correspondência à combinação',
};
let m = calculateNormal(extra.d, extra.b);
assert.equal(m.special.combinations.status, 'indeterminate');
assert.equal(m.special.colecta, null);
assert.notEqual(m.taxableProfit, null);
assert.equal(extra.b.base.benefits.amount, '100');
assert.match(m.special.pending.join(' '), /já registado emL6/);
const loss = setup(true),
p = JSON.parse(loss.d.business_plan_snapshot);
p.plan.products[0].unitPrice = '10';
loss.d.business_plan_snapshot = JSON.stringify(p);
loss.b.schedules.currentLossReference =
'QA perda própria do período reconciliada;sem transporte oficial automático';
m = calculateNormal(loss.d, loss.b);
assert.ok(val(m.special.taxableProfit) < 0);
assert.equal(val(m.special.taxableBase), 0);
assert.equal(val(m.special.colecta), 0);
assert.equal(val(m.special.expenses.autonomousTax), 35);
assert.equal(val(m.special.combinedColecta), 35);
});
void test('malformedoptionalcombination cannot overwrite acceptedbooks', () => {
for (const value of [
null,
{ version: 2 },
{
version: 1,
reference: 'QA',
specificState: 'unknown',
specificReference: 'QA',
specificException: '',
otherState: 'absent',
otherReference: 'QA',
},
]) {
const { b } = setup();
b.special.combinations = value;
assert.throws(() => validateSpecial(b.special), /não recuperável/);
}
});
void test('prepare only new guardedAPI cases;accepted13 proofs remain separate', () => {
const cases = {
combined: setup(),
autonomous: setup(true),
incompatible: setup(),
missing: setup(),
exception: setup(),
negative: setup(),
absent: setup(),
};
Object.assign(cases.incompatible.b.special.combinations, {
specificState: 'known',
specificReference: 'QA CBF22 no mesmo projecto/período',
specificException: 'absent',
});
cases.missing.b.special.combinations.specificReference = '';
Object.assign(cases.exception.b.special.combinations, {
specificState: 'known',
specificReference:
'QA excepção expressa invocada sem correspondência de artigo/limites',
specificException: 'claimed',
});
cases.negative.b.special.cooperative.qualification.eligible = 'no';
cases.absent.b.special.acceleration.rows = [];
Object.assign(cases.absent.b.special.acceleration.qualification, {
state: 'absent',
eligible: '',
});
fs.mkdirSync('outputs/irpc-l7-combinations', { recursive: true });
for (const [name, { d, b }] of Object.entries(cases)) {
d.modelo_a_snapshot = saveNormalBook(
d,
recordNormal(d, b, '2026-10-26T16:00:00+02:00'),
);
fs.writeFileSync(
'outputs/irpc-l7-combinations/' + name + '.json',
JSON.stringify(d),
);
fs.writeFileSync(
'outputs/irpc-l7-combinations/' + name + '.html',
normalHtml(d),
);
}
});