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

755 lines
25 KiB
JavaScript

import { calculateNormalBase } from '../../resources/js/lib/irpc-normal-base.ts';
import { fraction } from '../../resources/js/lib/fiscal-rational.ts';
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import {
normalBook,
normalCurrent,
calculateNormal,
recordNormal,
saveNormalBook,
normalHtml,
normalReportRows,
} from '../../resources/js/lib/irpc-normal.ts';
import {
newNormalCosts,
newCostRow,
reusableCreditBalance,
costRules,
normalCostReport,
} from '../../resources/js/lib/irpc-normal-costs.ts';
import { createProjectDossierReport } from '../../resources/js/lib/project-dossier-report.ts';
const seed = () =>
JSON.parse(
fs.readFileSync(
new URL(
'./fixtures/irpc-schedules-preserved.json',
import.meta.url,
),
'utf8',
),
);
const val = (f) => (f ? Number(f.numerator) / Number(f.denominator) : null);
function row(id, rule, amount, facts = {}, sourceCode = 'operating_expenses') {
return {
...newCostRow(id),
rule,
amount,
sourceCode,
document: 'QA-' + id,
line: '1',
reference:
'QA documento e factos qualificados para o período anual2026; valores sintéticos',
conditions: 'yes',
facts,
};
}
function reconcile(b) {
const totals = {};
for (const r of b.schedules.rows) {
const k = {
tax_own: 'expenseCorrections',
tax_other: 'expenseCorrections',
undocumented: 'expenseCorrections',
capital_in: 'positivePatrimonial',
patrimonial_gain: 'positivePatrimonial',
capital_out: 'negativePatrimonial',
patrimonial_cost: 'negativePatrimonial',
future_cost: 'timing',
future_income: 'timing',
current_cost: 'timing',
current_income: 'timing',
}[r.rule];
totals[k] = (totals[k] ?? 0) + Number(r.amount);
}
for (const r of b.costs.rows) {
const k = costRules[r.rule][1];
totals[k] = (totals[k] ?? 0) + Number(r.amount);
}
for (const [k, a] of Object.entries(totals))
b.profit.topics[k] = {
state: 'known',
amount: String(a),
reference:
'QA reconciliação aritmética por documento/linha dos mapas',
};
}
function setup(rows) {
const d = seed(),
b = normalBook(d);
b.costs = newNormalCosts();
b.costs.rows = rows;
b.costs.advertisingReference =
'QA publicidade completa e vendas normais12000 integralmente reconciliadas';
b.costs.salesOrdinary = 'yes';
b.costs.creditRegime = 'ordinary';
b.costs.creditBase = '10000';
b.costs.creditPrevious = '550';
b.costs.creditReference =
'QA créditos ordinários finais10000 e provisão fiscal anterior550 documentados, sem regime financeiro';
reconcile(b);
return { d, b };
}
function joint() {
return setup([
row('allowance', 'allowance', '100', {
billed: '20',
taxedNotBilled: '20',
}),
row('representation', 'representation', '50'),
row('advertising', 'advertising', '80', { campaign: 'no' }),
row('credit', 'credit', '200'),
row('stock', 'stock', '60', {
balanceKey: 'inventory-A',
lossObserved: 'yes',
observedLoss: '40',
previous: '10',
}),
row('litigation', 'litigation', '90', {
balanceKey: 'court-A',
caseOngoing: 'yes',
chargeDeductible: 'yes',
obligation: '100',
previous: '20',
}),
row('fuel', 'fuel', '50', { supportedNormal: '40', passenger: 'no' }),
row('prior', 'prior_cost', '80', {
originalYear: '2025',
unknownAtClose: 'no',
closingEvidence: 'QA factura conhecida no fecho2025',
}),
row(
'work',
'work',
'1000',
{
projectKey: 'project-A',
method: 'percentage',
price: '2000',
totalCost: '1000',
incurred: '500',
priorRevenue: '600',
priorCost: '300',
bookCost: '200',
costSource: 'cost_of_sales',
partialBilling: 'yes',
ownFractionalDelivery: 'no',
consistentMethod: 'yes',
deferredReceipt: '0',
},
'revenue',
),
]);
}
function model(r) {
const { d, b } = setup([r]);
return calculateNormal(d, b);
}
const item = (m) => m.costs.rows[0];
void test('compatible costs/provisions/prior timing/work produce exact profit6730 base4430 colecta1417.60 from existing6240 result', () => {
const { d, b } = joint(),
m = calculateNormal(d, b);
assert.equal(val(m.accountingProfit), 6240);
assert.equal(val(m.schedules.net), 520);
assert.equal(val(m.costs.net), -250);
assert.equal(val(m.taxableProfit), 6730);
assert.equal(val(m.taxableBase), 4430);
assert.equal(val(m.colecta), 1417.6);
assert.equal(m.version, 'IRPC-normal-adjustments/6');
assert.equal(m.tax, null);
assert.equal(m.assessment.finalBalance.payable, null);
assert.deepEqual(
m.costs.rows.map((r) => val(r.adjustment)),
[30, 40, 0, 150, 30, 10, 10, 80, -600],
);
assert.equal(m.costs.pending.length, 0);
});
void test('allowance exceptions are disjoint amounts; accepted70 plus addback30; known zero differs from missing', () => {
let m = model(
row('e', 'allowance', '100', { billed: '20', taxedNotBilled: '20' }),
);
assert.equal(val(item(m).accepted), 70);
assert.equal(val(item(m).adjustment), 30);
for (const facts of [
{ billed: '0', taxedNotBilled: '0' },
{ billed: '100', taxedNotBilled: '0' },
])
assert.notEqual(
model(row('e', 'allowance', '100', facts)).taxableProfit,
null,
);
for (const facts of [
{ billed: '', taxedNotBilled: '0' },
{ billed: '60', taxedNotBilled: '50' },
])
assert.equal(
model(row('e', 'allowance', '100', facts)).taxableProfit,
null,
);
});
void test('representation20percent accepted; fractional cents remain exact without fiscal rounding or autonomous tax', () => {
const m = model(row('f', 'representation', '0.01'));
assert.deepEqual(item(m).accepted, { numerator: '1', denominator: '500' });
assert.deepEqual(item(m).adjustment, {
numerator: '1',
denominator: '125',
});
assert.equal(m.tax, null);
assert.equal(m.assessment.finalBalance.scope, 'L8');
});
void test('advertising shares one annual1percent cap across documents and has no per-invoice duplicate allowance', () => {
const { d, b } = setup([
row('n1', 'advertising', '100', { campaign: 'no' }),
row('n2', 'advertising', '100', { campaign: 'no' }),
]);
let m = calculateNormal(d, b);
assert.deepEqual(
m.costs.rows.map((r) => val(r.accepted)),
[60, 60],
);
assert.equal(val(m.costs.net), 80);
assert.deepEqual(
m.costs.rows.map((r) => val(r.limit)),
[120, 120],
);
b.costs.rows.reverse();
assert.equal(val(calculateNormal(d, b).costs.net), 80);
b.costs.salesOrdinary = '';
m = calculateNormal(d, b);
assert.equal(m.taxableProfit, null);
assert.equal(m.schedules.net.numerator, '520');
const c = setup([
row('ad-a', 'advertising', '100', { campaign: 'no' }),
row('ad-b', 'advertising', '250', { campaign: 'no' }),
]);
const f = calculateNormal(c.d, c.b);
assert.match(JSON.stringify(normalCostReport(f.costs)), /exacto 240\/7/);
});
void test('campaign36A never silently receives ordinary advertising treatment', () => {
const m = model(row('campaign', 'advertising', '100', { campaign: 'yes' }));
assert.equal(m.taxableProfit, null);
assert.equal(item(m).accepted, null);
assert.match(item(m).reason, /36-A/);
});
void test('vehicle costs distinguish ordinary passenger50percent, documented public/rental exception and nonpassenger', () => {
for (const [facts, accepted] of [
[{ passenger: 'yes', publicOrRental: 'no' }, 50],
[{ passenger: 'yes', publicOrRental: 'yes' }, 100],
[{ passenger: 'no' }, 100],
])
assert.equal(
val(item(model(row('v', 'vehicle', '100', facts))).accepted),
accepted,
);
assert.equal(
model(row('v', 'vehicle', '100', { passenger: 'yes' })).taxableProfit,
null,
);
});
void test('fuel and rent cover independent rules; overlapping36h-i and36-4 stays pending without an invented combination', () => {
let m = model(
row('fuel', 'fuel', '100', {
supportedNormal: '80',
passenger: 'yes',
publicOrRental: 'no',
}),
);
assert.equal(m.taxableProfit, null);
assert.match(item(m).reason, /Articulação/);
m = model(
row('fuel', 'fuel', '100', {
supportedNormal: '100',
passenger: 'yes',
publicOrRental: 'no',
}),
);
assert.equal(val(item(m).accepted), 50);
m = model(
row('rent', 'rental', '100', {
nonAcceptedQuota: '20',
quotaReference:
'QA quota36h documentada; não calculada automaticamente',
passenger: 'yes',
publicOrRental: 'yes',
}),
);
assert.equal(val(item(m).accepted), 80);
assert.equal(val(item(m).adjustment), 20);
assert.match(item(m).reason, /não apura automaticamente/);
assert.equal(
model(
row('fuel', 'fuel', '100', {
supportedNormal: '101',
passenger: 'no',
}),
).taxableProfit,
null,
);
assert.equal(
model(
row('rent', 'rental', '100', {
nonAcceptedQuota: '',
quotaReference: 'QA',
passenger: 'yes',
publicOrRental: 'no',
}),
).taxableProfit,
null,
);
});
void test('credit provision caps both annual1.5percent and cumulative6percent; multiple rows share headroom and do not consume source balance', () => {
const { d, b } = setup([
row('c1', 'credit', '100'),
row('c2', 'credit', '100'),
]);
let m = calculateNormal(d, b);
assert.deepEqual(
m.costs.rows.map((r) => val(r.accepted)),
[25, 25],
);
assert.equal(val(m.costs.net), 150);
assert.equal(b.costs.creditPrevious, '550');
b.costs.creditPrevious = '0';
m = calculateNormal(d, b);
assert.equal(val(m.costs.net), 50);
b.costs.creditPrevious = '600';
assert.equal(val(calculateNormal(d, b).costs.net), 200);
b.costs.creditPrevious = '601';
assert.equal(calculateNormal(d, b).taxableProfit, null);
b.costs.creditRegime = 'regulated';
assert.equal(calculateNormal(d, b).taxableProfit, null);
b.costs.creditRegime = 'ordinary';
b.costs.creditPrevious = '';
assert.equal(calculateNormal(d, b).taxableProfit, null);
});
void test('inventory allowance respects observed loss less existing fiscal provision; absent observation is known rejection, not unknown zero', () => {
let m = model(
row('s', 'stock', '60', {
balanceKey: 'stock',
lossObserved: 'yes',
observedLoss: '40',
previous: '10',
}),
);
assert.equal(val(item(m).accepted), 30);
assert.equal(val(item(m).adjustment), 30);
m = model(row('s', 'stock', '60', { lossObserved: 'no' }));
assert.equal(val(item(m).accepted), 0);
assert.equal(val(item(m).adjustment), 60);
m = model(
row('s', 'stock', '60', {
balanceKey: 'stock',
lossObserved: 'yes',
observedLoss: '40',
previous: '',
}),
);
assert.equal(m.taxableProfit, null);
});
void test('litigation needs current court process and deductible underlying charge; known failure adds back full amount', () => {
for (const facts of [
{ caseOngoing: 'no' },
{ caseOngoing: 'yes', chargeDeductible: 'no' },
]) {
const m = model(row('l', 'litigation', '90', facts));
assert.equal(val(item(m).accepted), 0);
assert.equal(val(item(m).adjustment), 90);
}
const m = model(
row('l', 'litigation', '90', {
caseOngoing: 'yes',
chargeDeductible: 'yes',
balanceKey: 'court',
obligation: '100',
previous: '20',
}),
);
assert.equal(val(item(m).accepted), 80);
assert.equal(
model(
row('l', 'litigation', '90', {
caseOngoing: 'yes',
chargeDeductible: '',
}),
).taxableProfit,
null,
);
});
void test('reversal before deducted is taxed once; taxed-provision reversal is excluded only with prior fiscal evidence', () => {
let m = model(
row(
'r',
'provision_reversal',
'50',
{
balanceKey: 'old',
previous: '70',
eventEnded: 'yes',
inResult: 'yes',
},
'revenue',
),
);
assert.equal(val(item(m).adjustment), 0);
m = model(
row(
'r',
'provision_reversal',
'50',
{
balanceKey: 'old',
previous: '70',
eventEnded: 'yes',
inResult: 'no',
},
'',
),
);
assert.equal(val(item(m).adjustment), 50);
m = model(
row(
'r',
'taxed_reversal',
'50',
{
balanceKey: 'old',
previous: '70',
eventEnded: 'yes',
inResult: 'yes',
},
'revenue',
),
);
assert.equal(val(item(m).adjustment), -50);
assert.equal(
model(
row(
'r',
'taxed_reversal',
'80',
{
balanceKey: 'old',
previous: '70',
eventEnded: 'yes',
inResult: 'yes',
},
'revenue',
),
).taxableProfit,
null,
);
});
void test('same fiscal object cannot reuse stock/litigation/reversal balance with another document', () => {
const { d, b } = setup([
row('s1', 'stock', '50', {
balanceKey: 'same',
lossObserved: 'yes',
observedLoss: '100',
previous: '0',
}),
row('s2', 'stock', '50', {
balanceKey: 'same',
lossObserved: 'yes',
observedLoss: '100',
previous: '0',
}),
]);
const m = calculateNormal(d, b);
assert.equal(m.taxableProfit, null);
assert.ok(m.costs.rows.every((r) => r.adjustment === null));
});
void test('prior-period cost and income distinguish knowledge at previous closing; never edit earlier returns or recorded histories', () => {
for (const [rule, src, unknown, expected] of [
['prior_cost', 'operating_expenses', 'yes', 0],
['prior_cost', 'operating_expenses', 'no', 100],
['prior_income', 'revenue', 'yes', 0],
['prior_income', 'revenue', 'no', -100],
]) {
const m = model(
row(
'p',
rule,
'100',
{
originalYear: '2025',
unknownAtClose: unknown,
closingEvidence: 'QA fecho anterior',
},
src,
),
);
assert.equal(val(item(m).adjustment), expected);
}
assert.equal(
model(
row('p', 'prior_cost', '100', {
originalYear: '2025',
unknownAtClose: '',
closingEvidence: 'QA',
}),
).taxableProfit,
null,
);
});
void test('work percentage uses accumulated performance less prior recognized revenue and cost, no double recognition', () => {
const { d, b } = joint(),
m = calculateNormal(d, b),
w = m.costs.rows.at(-1);
assert.equal(val(w.limit), 400);
assert.equal(val(w.accepted), 200);
assert.equal(val(w.adjustment), -600);
assert.match(w.reason, /grau=incorrido/);
assert.equal(b.costs.rows.at(-1).facts.priorRevenue, '600');
});
void test('closing work respects mandatory percentage,95percent threshold and missing facts; estimated loss is separate full addback', () => {
const { d, b } = joint(),
w = b.costs.rows.at(-1);
w.facts.method = 'closing';
assert.equal(calculateNormal(d, b).taxableProfit, null);
w.facts.partialBilling = 'no';
w.facts.priorRevenue = '0';
w.facts.priorCost = '0';
w.facts.publicReception = 'no';
let m = calculateNormal(d, b);
assert.equal(val(m.costs.rows.at(-1).limit), 0);
w.facts.incurred = '950';
m = calculateNormal(d, b);
assert.equal(val(m.costs.rows.at(-1).limit), 2000);
w.facts.deferredReceipt = '1';
assert.equal(calculateNormal(d, b).taxableProfit, null);
assert.equal(
val(
item(model(row('estimate', 'estimated_work_loss', '100')))
.adjustment,
),
100,
);
});
void test('cross-map duplicate identity and cumulative source over-allocation block only dependent totals', () => {
const { d, b } = joint();
b.costs.rows[0].document = b.rows[0].document + ' ';
b.costs.rows[0].line = b.rows[0].line;
let m = calculateNormal(d, b);
assert.equal(m.taxableProfit, null);
assert.match(m.costs.rows[0].reason, /repetidos/);
assert.equal(m.costs.rows[0].adjustment, null);
assert.equal(val(m.costs.rows[1].adjustment), 40);
const c = joint();
c.b.costs.rows[0].amount = '1000';
reconcile(c.b);
m = calculateNormal(c.d, c.b);
assert.equal(m.taxableProfit, null);
assert.match(m.costs.pending.join(' '), /excedem a linha/);
});
void test('missing one credit parameter retains known independent corrections and makes downstream totals unknown; upstream records remain', () => {
const { d, b } = joint();
b.costs.creditPrevious = '';
const m = calculateNormal(d, b);
assert.equal(m.taxableProfit, null);
assert.equal(m.taxableBase, null);
assert.equal(m.colecta, null);
assert.equal(val(m.accountingProfit), 6240);
assert.equal(val(m.costs.rows[1].adjustment), 40);
assert.equal(val(m.schedules.net), 520);
assert.equal(m.schedules.pending.length, 0);
});
void test('credit reuse requires known financial closing balances; absent schedule never creates zero', () => {
assert.equal(reusableCreditBalance(null), null);
assert.equal(
reusableCreditBalance({
cashSchedule: [
{
salesReceivablesBalance: null,
openingReceivablesBalance: '0',
},
],
}),
null,
);
assert.equal(
val(
reusableCreditBalance({
cashSchedule: [
{
salesReceivablesBalance: '100',
openingReceivablesBalance: '20',
},
],
}),
),
120,
);
});
void test('installed5 stays current without recalculation; version6 API-ready snapshots preserve books/history and export exact rules/amounts', () => {
const old = seed(),
before = normalBook(old);
assert.equal(normalCurrent(old, before), true);
assert.equal(before.result.model.version, 'IRPC-normal-adjustments/5');
const { d, b } = joint(),
store = JSON.parse(d.modelo_a_snapshot);
d.modelo_a_snapshot = saveNormalBook(
d,
recordNormal(d, b, '2026-10-26T00:30:00+02:00'),
);
const back = normalBook(JSON.parse(JSON.stringify(d)));
assert.equal(normalCurrent(d, back), true);
assert.deepEqual(back.history.at(-1), before.result);
const saved = JSON.parse(d.modelo_a_snapshot);
assert.deepEqual(saved.books, store.books);
assert.deepEqual(saved.irpcWithholdings, store.irpcWithholdings);
assert.match(normalHtml(d), /1417.60/);
assert.match(normalHtml(d), /parcela aceite/);
assert.match(
JSON.stringify(
createProjectDossierReport(d, {
selectedSections: new Set(['finance']),
}),
),
/36\(1\)n/,
);
assert.match(JSON.stringify(normalReportRows(d)), /M20 — correspondência/);
fs.mkdirSync('outputs/irpc-l6-costs', { recursive: true });
fs.writeFileSync(
'outputs/irpc-l6-costs/calculated.json',
JSON.stringify(d),
);
fs.writeFileSync('outputs/irpc-l6-costs/preparation.html', normalHtml(d));
const missing = normalBook(d);
missing.costs.creditPrevious = '';
d.modelo_a_snapshot = saveNormalBook(d, recordNormal(d, missing));
fs.writeFileSync('outputs/irpc-l6-costs/missing.json', JSON.stringify(d));
const changed = normalBook(d);
changed.costs.rows[0].facts.billed = '30';
d.modelo_a_snapshot = saveNormalBook(d, changed);
assert.equal(normalCurrent(d, normalBook(d)), false);
assert.throws(() => normalHtml(d));
});
void test('malformed versions, unknown rules and non-string facts are refused with saved originals intact', () => {
const { d, b } = joint();
for (const change of [
(x) => (x.version = 2),
(x) => (x.rows[0].rule = '__proto__'),
(x) => (x.rows[0].facts.billed = 20),
]) {
const c = structuredClone(b);
change(c.costs);
assert.throws(() => saveNormalBook(d, c));
}
assert.equal(normalCurrent(d, normalBook(d)), true);
});
void test('additional categories,shared limits and closing method generate separate recoverable compatible cases', () => {
const cases = {
additional: setup([
row('vehicle', 'vehicle', '100', {
passenger: 'yes',
publicOrRental: 'no',
}),
row('rental', 'rental', '100', {
nonAcceptedQuota: '20',
quotaReference: 'QA quota36h documentada',
passenger: 'yes',
publicOrRental: 'yes',
}),
row('estimate', 'estimated_work_loss', '50'),
row(
'reverse',
'provision_reversal',
'50',
{
balanceKey: 'provision-old-deducted',
previous: '50',
eventEnded: 'yes',
inResult: 'no',
},
'',
),
row(
'taxed-reverse',
'taxed_reversal',
'50',
{
balanceKey: 'provision-old-taxed',
previous: '50',
eventEnded: 'yes',
inResult: 'yes',
},
'revenue',
),
row(
'prior-income',
'prior_income',
'50',
{
originalYear: '2025',
unknownAtClose: 'no',
closingEvidence:
'QA receita conhecida no encerramento anterior',
},
'revenue',
),
]),
limits: setup([
row('ad1', 'advertising', '100', { campaign: 'no' }),
row('ad2', 'advertising', '100', { campaign: 'no' }),
row('cr1', 'credit', '100'),
row('cr2', 'credit', '100'),
]),
closing: joint(),
};
const w = cases.closing.b.costs.rows.at(-1);
Object.assign(w.facts, {
method: 'closing',
partialBilling: 'no',
priorRevenue: '0',
priorCost: '0',
publicReception: 'no',
});
const expected = {
additional: [7050, 4750, 1520],
limits: [7210, 4910, 1571.2],
closing: [6530, 4230, 1353.6],
};
for (const [name, { d, b }] of Object.entries(cases)) {
const m = calculateNormal(d, b);
assert.deepEqual(
[val(m.taxableProfit), val(m.taxableBase), val(m.colecta)],
expected[name],
);
d.modelo_a_snapshot = saveNormalBook(d, recordNormal(d, b));
fs.writeFileSync(
'outputs/irpc-l6-costs/' + name + '.json',
JSON.stringify(d),
);
}
});
void test('percentage-generated loss below one cent remains a negative fiscal result, never a silently truncated zero', () => {
const { b } = joint();
b.base.lossesState = 'absent';
b.base.losses = [];
assert.equal(
calculateNormalBase(b.base, fraction(-1n, 1000n), '2026').taxableBase,
null,
);
assert.equal(
val(
calculateNormalBase(
b.base,
fraction(-1n, 1000n),
'2026',
'QA perda corrente exacta',
).taxableBase,
),
0,
);
assert.equal(
val(
calculateNormalBase(b.base, fraction(1n, 1000n), '2026')
.taxableBase,
),
0.001,
);
});