Files
Investidor-V2/tests/frontend/financial-fiscal-bridge.test.mjs
T
nala 1106509116
tests / ci (push) Canceled after 0s
vrsao 2
2026-10-08 14:02:01 +02:00

172 lines
6.1 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { buildFinancialFiscalBridge } from '../../resources/js/lib/financial-fiscal-bridge.ts';
import { forecastBusinessPlan } from '../../resources/js/lib/business-financial-plan.ts';
const plan = {
currency: 'MZN',
openingCash: '0',
products: [
{
label: 'Serviço',
monthlyUnits: '10',
unitPrice: '100',
unitVariableCost: '30',
},
],
monthlyCollections: '800',
monthlyVariableCostPayments: '250',
monthlyOperatingExpenses: '100',
monthlyOperatingPayments: '80',
monthlyPayrollExpense: '50',
monthlyPayrollPayments: '40',
assetInvestment: '0',
inventory: '0',
receivables: '0',
payables: '0',
equity: '0',
shareholderLoans: '0',
bankLoanProceeds: '0',
grants: '0',
assetDepreciation: '0',
depreciationLifeMonths: 12,
monthlyInterestExpense: '10',
monthlyInterestPayment: '5',
monthlyPrincipalRepayment: '60',
periods: 2,
};
void test('financial to fiscal bridge reconciles accounting result while leaving tax base indeterminate', () => {
const forecast = forecastBusinessPlan(plan);
const bridge = buildFinancialFiscalBridge(forecast);
const byCode = Object.fromEntries(
bridge.accountingLines.map((line) => [line.code, line]),
);
const cents = (value) => {
const [whole, fraction] = value.split('.');
return BigInt(whole) * 100n + BigInt(fraction);
};
const accountingProfit = [
byCode.revenue.total,
...Object.values(byCode)
.filter((line) => line.accountingEffect === 'expense')
.map((line) => `-${line.total}`),
].reduce((sum, value) => sum + cents(value), 0n);
assert.equal(bridge.status, 'incomplete');
assert.equal(bridge.accountingProfitBeforeTax, '1080.00');
assert.equal(accountingProfit, cents(bridge.accountingProfitBeforeTax));
assert.equal(bridge.taxableBase.status, 'indeterminate');
assert.equal(bridge.taxableBase.amount, null);
assert.ok(
bridge.taxableBase.missingInputs.includes(
'expense_deductibility_classification',
),
);
assert.equal(
bridge.accountingLines.every(
(line) => line.taxTreatment === 'unclassified',
),
true,
);
assert.equal(byCode.revenue.monthlyAmounts[0].amount, '1000.00');
assert.equal(byCode.cost_of_sales.monthlyAmounts[0].amount, '300.00');
assert.equal(bridge.cashSchedule[0].collections, '800.00');
assert.equal(bridge.cashSchedule[0].collectionMode, 'manual');
assert.equal(bridge.cashSchedule[0].salesCollections, null);
assert.equal(bridge.cashSchedule[0].openingReceivablesCollections, '0.00');
assert.equal(bridge.cashSchedule[0].otherCollections, '800.00');
assert.equal(bridge.cashSchedule[0].collectionsTotal, '800.00');
assert.equal(bridge.cashSchedule[0].principalRepayments, '60.00');
assert.equal(
bridge.accountingLines.some(
(line) => line.code === 'principal_repayment',
),
false,
);
assert.notEqual(
bridge.cashSchedule[0].collections,
byCode.revenue.monthlyAmounts[0].amount,
);
});
void test('financial bridge separates opening receivables and scheduled sales collections', () => {
const forecast = forecastBusinessPlan({
...plan,
receivables: '200.00',
monthlyCollections: '0',
salesCollectionLagMonths: 0,
salesCollectionPercent: '75',
openingReceivablesCollectionMonth: 1,
openingReceivablesCollectionPercent: '50',
periods: 1,
});
const bridge = buildFinancialFiscalBridge(forecast);
assert.deepEqual(bridge.cashSchedule[0], {
period: 1,
collectionMode: 'scheduled',
salesCollections: '750.00',
openingReceivablesBalance: '100.00',
openingReceivablesCollections: '100.00',
otherCollections: '0.00',
collections: '850.00',
collectionsTotal: '850.00',
salesReceivablesBalance: '250.00',
supplierPayments: '250.00',
operatingPayments: '80.00',
personnelPayments: '40.00',
interestPayments: '5.00',
principalRepayments: '60.00',
netCashFlow: '415.00',
closingCash: '215.00',
});
assert.equal(
bridge.accountingLines.find((line) => line.code === 'revenue').total,
'1000.00',
);
assert.equal(bridge.taxableBase.status, 'indeterminate');
});
void test('bridge maps monthly recognised expenses but keeps scheduled payments in cash only', () => {
const forecast = forecastBusinessPlan({
...plan,
periods: 2,
monthlyOperatingExpensesByPeriod: ['100', '250'],
monthlyOperatingPaymentsByPeriod: ['20', '300'],
monthlyPayrollExpenseByPeriod: ['50', '75'],
monthlyPayrollPaymentsByPeriod: ['0', '100'],
});
const bridge = buildFinancialFiscalBridge(forecast);
const byCode = Object.fromEntries(
bridge.accountingLines.map((line) => [line.code, line]),
);
assert.deepEqual(byCode.operating_expenses.monthlyAmounts, [
{ period: 1, amount: '100.00' },
{ period: 2, amount: '250.00' },
]);
assert.deepEqual(byCode.personnel.monthlyAmounts, [
{ period: 1, amount: '50.00' },
{ period: 2, amount: '75.00' },
]);
assert.equal(bridge.cashSchedule[0].operatingPayments, '20.00');
assert.equal(bridge.cashSchedule[0].personnelPayments, '0.00');
assert.equal(bridge.cashSchedule[1].operatingPayments, '300.00');
assert.equal(bridge.cashSchedule[1].personnelPayments, '100.00');
assert.equal(bridge.taxableBase.status, 'indeterminate');
});
void test('financial bridge rejects a corrupted monthly cash schedule', () => {
const forecast = forecastBusinessPlan(plan);
const corrupted = {
...forecast,
rows: forecast.rows.map((row, index) =>
index === 0 ? { ...row, closingCash: '999999.00' } : row,
),
};
assert.throws(
() => buildFinancialFiscalBridge(corrupted),
/saldo de caixa mensal não reconcilia/,
);
});