755 lines
25 KiB
JavaScript
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,
|
|
);
|
|
});
|