import assert from 'node:assert/strict'; import test from 'node:test'; import { forecastBusinessPlan } from '../../resources/js/lib/business-financial-plan.ts'; import { projectMonthlyCashflow } from '../../resources/js/lib/monthly-cashflow.ts'; import { defaultCashflowDraftInputs, serializeCashflowDraftSnapshot, restoreCashflowDraftSnapshot, } from '../../resources/js/lib/monthly-cashflow.ts'; import { serializeFinancialToolsSnapshot } from '../../resources/js/lib/financial-calculators.ts'; import { buildProjectDossierRows } from '../../resources/js/lib/project-dossier.ts'; import { createProjectDossierReport } from '../../resources/js/lib/project-dossier-report.ts'; void test('cashflow draft recovery preserves unanswered months and invalid editable values without using zero', () => { const inputs = { ...defaultCashflowDraftInputs(), openingCash: '77.01', monthlyTaxSchedule: ['10', '', '20', ...Array(9).fill('0')], }; const stored = serializeCashflowDraftSnapshot('schedule', inputs, 'MZN'); const restored = restoreCashflowDraftSnapshot(stored); assert.equal(restored.taxMode, 'schedule'); assert.deepEqual(restored.inputs, inputs); assert.equal(restored.inputs.monthlyTaxSchedule[1], ''); const rows = buildProjectDossierRows({ currency: 'MZN', cashflow_snapshot: stored, }); assert.equal( rows.find( (row) => row[0] === 'Tesouraria' && row[1] === 'Saldo final total', )[2], 'Indeterminado', ); assert.ok( rows .find((row) => row[0] === 'Tesouraria' && row[1] === 'Mês 2')[2] .includes('Imposto assumido: Indeterminado'), ); const report = createProjectDossierReport( { currency: 'MZN', cashflow_snapshot: stored, }, { selectedSections: new Set(['summary', 'finance']), version: 'SINTETICO QA', }, ); assert.equal( report.sections .find((section) => section.id === 'summary') .rows.find( (row) => row.field === 'Saldo final de tesouraria — 12 meses', ).value, 'Indeterminado', ); const invalid = { ...inputs, monthlyRevenue: 'corrigir' }; assert.equal( restoreCashflowDraftSnapshot( serializeCashflowDraftSnapshot('fixed', invalid), ).inputs.monthlyRevenue, 'corrigir', ); assert.equal(restoreCashflowDraftSnapshot('{"version":2}'), null); assert.equal( restoreCashflowDraftSnapshot( stored.replace('"version":2', '"version":3'), ), null, ); }); void test('credit report preserves the complete reproducible recipe alongside the result', () => { const rows = buildProjectDossierRows({ purpose: 'simulate_only', journey_type: 'small_business', currency: 'MZN', financial_tools_snapshot: serializeFinancialToolsSnapshot({ budgetPeriod: 'Mensal', loan: { principal: '1000', rate: '12', months: '12', rateType: 'nominal', periodMonths: '1', gracePeriods: '0', upfrontFee: '0', recurringFee: '0', }, breakEven: { fixed: '100', price: '20', variable: '10' }, funding: { total: '1000', secured: '0' }, incomes: [{ label: 'QA', amount: '100' }], expenses: [{ label: 'QA', amount: '50' }], }), }); const recipe = rows.find( (row) => row[1] === 'Método e pressupostos substituídos', )[2]; for (const text of [ '1000 MZN', '12% nominal anual', '12 meses', 'periodicidade 1 meses', 'carência 0', 'comissão inicial 0 MZN', 'comissão periódica 0 MZN', 'duas casas decimais', 'metade para cima', 'saldo residual', ]) assert.ok(recipe.includes(text), text); assert.equal( rows.find((row) => row[1] === 'Prestação do primeiro período')[2], '88.85 MZN', ); }); // Synthetic QA control ledger, independently calculated before invoking the engines. // Sales 200; variable expense 80; operations 20; payroll 10; // depreciation 10; interest expense 5 => profit before tax 75/month. // Cash: receipts 180; inventory 70; operations 25; payroll 10; // interest paid 7; principal paid 12 => cash increases 56/month. // Initial funding 1200; asset 600 + inventory 100 + receivables 50 - payables 20 = 730; // opening cash 470 => closing 638 after three months. No tax rate is assumed. void test('independent three-month ledger separates profit working capital and cash', () => { const result = forecastBusinessPlan({ currency: 'MZN', openingCash: '0', products: [ { label: 'SINTETICO QA', monthlyUnits: '10', unitPrice: '20', unitVariableCost: '8', }, ], monthlyCollections: '180', monthlyVariableCostPayments: '70', monthlyInventoryPurchases: '70', monthlyOperatingExpenses: '20', monthlyOperatingPayments: '25', monthlyPayrollExpense: '10', monthlyPayrollPayments: '10', assetInvestment: '600', inventory: '100', receivables: '50', payables: '20', equity: '1000', shareholderLoans: '0', bankLoanProceeds: '200', grants: '0', assetDepreciation: '600', depreciationLifeMonths: 60, monthlyInterestExpense: '5', monthlyInterestPayment: '7', monthlyPrincipalRepayment: '12', periods: 3, }); assert.equal(result.workingCapital, '130.00'); assert.equal(result.initialUses, '730.00'); assert.equal(result.openingCashAfterInitialUses, '470.00'); assert.equal(result.totalProfitBeforeTax, '225.00'); assert.equal(result.endingCash, '638.00'); assert.equal(result.totalCashCollections, '540.00'); }); void test('independent cash ledger excludes VAT stages and preserves the unknown tax month', () => { const input = { currency: 'MZN', openingCash: '100', monthlyRevenue: '200', monthlyOperatingCosts: '150', monthlyDebtService: '20', monthlyTaxSchedule: ['10', '', '20'], vatCreditEstimated: '1000', vatRefundRequested: '900', vatRefundRecognized: '800', vatRefundExpectedReceipt: '50', vatRefundExpectedMonth: '2', periods: 3, }; const result = projectMonthlyCashflow(input); // Known cash: 100 + 20 + 80 + 10 = 210; month 2 tax is unknown. assert.equal(result.rows[0].closingCash, '120.00'); assert.equal(result.rows[1].closingCash, null); assert.equal(result.endingCash, null); assert.equal(result.endingCashBeforeUnassessedTax, '210.00'); assert.equal( projectMonthlyCashflow({ ...input, monthlyTaxSchedule: ['10', '0', '20'], }).endingCash, '210.00', ); assert.equal(input.monthlyTaxSchedule[1], ''); });