156 lines
5.1 KiB
JavaScript
156 lines
5.1 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import {
|
|
guidanceFinance,
|
|
guidanceFinancialTools,
|
|
} from '../../resources/js/lib/investor-guidance-finance.ts';
|
|
import { buildProjectDossierRows } from '../../resources/js/lib/project-dossier.ts';
|
|
import { serializeFinancialToolsSnapshot } from '../../resources/js/lib/financial-calculators.ts';
|
|
import {
|
|
forecastBusinessPlan,
|
|
serializeBusinessPlanSnapshot,
|
|
compareBusinessScenarios,
|
|
} from '../../resources/js/lib/business-financial-plan.ts';
|
|
|
|
const assumptions = {
|
|
currency: 'MZN',
|
|
openingCash: '0',
|
|
products: [
|
|
{
|
|
label: 'Serviço',
|
|
monthlyUnits: '100',
|
|
unitPrice: '1000',
|
|
unitVariableCost: '400',
|
|
},
|
|
],
|
|
monthlyCollections: '90000',
|
|
salesCollectionLagMonths: null,
|
|
salesCollectionPercent: '100',
|
|
salesRecoverySchedule: [],
|
|
monthlyVariableCostPayments: '42000',
|
|
monthlyVariableCostPaymentsByPeriod: null,
|
|
monthlyInventoryPurchases: '40000',
|
|
inventoryPurchaseLeadMonths: 0,
|
|
inventoryPurchaseSchedule: null,
|
|
useInventoryPurchaseSchedule: false,
|
|
useProductInventorySchedule: false,
|
|
useSupplierPaymentTerms: false,
|
|
monthlyOperatingExpenses: '20000',
|
|
monthlyOperatingPayments: '18000',
|
|
monthlyPayrollExpense: '15000',
|
|
monthlyPayrollPayments: '15000',
|
|
assetInvestment: '100000',
|
|
inventory: '20000',
|
|
receivables: '10000',
|
|
openingReceivablesCollectionMonth: null,
|
|
openingReceivablesCollectionPercent: '100',
|
|
openingReceivablesInstallments: [],
|
|
openingPayablesInstallments: [],
|
|
payables: '5000',
|
|
equity: '80000',
|
|
shareholderLoans: '0',
|
|
bankLoanProceeds: '50000',
|
|
grants: '0',
|
|
assetDepreciation: '100000',
|
|
depreciationLifeMonths: 60,
|
|
monthlyInterestExpense: '1200',
|
|
monthlyInterestPayment: '1000',
|
|
monthlyPrincipalRepayment: '5000',
|
|
periods: 12,
|
|
};
|
|
|
|
void test('guidance reuses the dossier calculator report and preserves partial indeterminacy', () => {
|
|
const source = {
|
|
budgetPeriod: 'Mensal',
|
|
loan: {
|
|
principal: '',
|
|
rate: '',
|
|
months: '',
|
|
rateType: 'nominal',
|
|
periodMonths: '',
|
|
gracePeriods: '',
|
|
upfrontFee: '',
|
|
recurringFee: '',
|
|
},
|
|
breakEven: { fixed: '', price: '', variable: '' },
|
|
funding: { total: '1000', secured: '400' },
|
|
incomes: [{ label: 'Receita QA', amount: '300' }],
|
|
expenses: [{ label: 'Custo QA', amount: '200' }],
|
|
};
|
|
const draft = {
|
|
financial_tools_snapshot: serializeFinancialToolsSnapshot(source),
|
|
currency: 'MZN',
|
|
};
|
|
const result = guidanceFinancialTools(draft);
|
|
assert.deepEqual(
|
|
result.rows,
|
|
buildProjectDossierRows(draft).filter(
|
|
(row) => row[0] === 'Plano financeiro',
|
|
),
|
|
);
|
|
assert.deepEqual(result.assumptions, source);
|
|
assert.equal(
|
|
guidanceFinancialTools({ financial_tools_snapshot: '{"version":999}' }),
|
|
null,
|
|
);
|
|
assert.equal(guidanceFinancialTools(null), null);
|
|
});
|
|
|
|
void test('guidance keeps missing, malformed and unsupported financial snapshots indeterminate', () => {
|
|
for (const snapshot of [
|
|
undefined,
|
|
'',
|
|
'{}',
|
|
'{invalid',
|
|
'{"version":999,"plan":{}}',
|
|
]) {
|
|
const view = guidanceFinance(
|
|
snapshot ? { business_plan_snapshot: snapshot } : null,
|
|
);
|
|
assert.equal(view.status, 'indeterminate');
|
|
assert.equal(view.result, null);
|
|
}
|
|
});
|
|
|
|
void test('guidance uses exact deterministic outputs and preserves negative cash and original assumptions', () => {
|
|
const plan = { ...assumptions, monthlyCollections: '0' };
|
|
const snapshot = serializeBusinessPlanSnapshot(plan);
|
|
const view = guidanceFinance({ business_plan_snapshot: snapshot });
|
|
assert.equal(view.status, 'calculated');
|
|
assert.deepEqual(view.result, forecastBusinessPlan(view.assumptions));
|
|
assert.equal(view.result.endingCash.startsWith('-'), true);
|
|
assert.equal(view.tax, 'indeterminate');
|
|
assert.equal(view.scenarios, null);
|
|
assert.equal(snapshot, serializeBusinessPlanSnapshot(plan));
|
|
});
|
|
|
|
void test('guidance scenarios are the existing deterministic scenario tool without success probabilities', () => {
|
|
const scenario = {
|
|
...assumptions,
|
|
scenarioAdjustments: {
|
|
favorable: {
|
|
unitVolumePercent: '10',
|
|
unitPricePercent: '5',
|
|
unitCostPercent: '0',
|
|
operatingCostsPercent: '0',
|
|
cashCollectionsPercent: '10',
|
|
},
|
|
unfavorable: {
|
|
unitVolumePercent: '-10',
|
|
unitPricePercent: '-5',
|
|
unitCostPercent: '10',
|
|
operatingCostsPercent: '10',
|
|
cashCollectionsPercent: '-10',
|
|
},
|
|
},
|
|
};
|
|
const view = guidanceFinance({
|
|
business_plan_snapshot: serializeBusinessPlanSnapshot(scenario),
|
|
});
|
|
assert.deepEqual(
|
|
view.scenarios,
|
|
compareBusinessScenarios(view.assumptions),
|
|
);
|
|
assert.equal('success_probability' in view, false);
|
|
});
|