Files
Investidor-V2/tests/frontend/monthly-iva.test.mjs
T

234 lines
8.0 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import {
monthlyProjectedSales,
forecastBusinessPlan,
serializeBusinessPlanSnapshot,
} from '../../resources/js/lib/business-financial-plan.ts';
import {
monthlyIvaInput,
monthlyIvaKey,
monthlySalesFromDraft,
readMonthlyIvaRecord,
monthlyIvaCashTreatment,
} from '../../resources/js/lib/monthly-iva.ts';
import {
partitionSimulationDraft,
restoreSimulationDraft,
readArithmeticSnapshot,
} from '../../resources/js/lib/simulation-form.ts';
import { createProjectDossierReport } from '../../resources/js/lib/project-dossier-report.ts';
function plan() {
const fields = [
'openingCash',
'monthlyCollections',
'monthlyVariableCostPayments',
'monthlyInventoryPurchases',
'monthlyOperatingExpenses',
'monthlyOperatingPayments',
'monthlyPayrollExpense',
'monthlyPayrollPayments',
'assetInvestment',
'inventory',
'receivables',
'payables',
'equity',
'shareholderLoans',
'bankLoanProceeds',
'grants',
'assetDepreciation',
'monthlyInterestExpense',
'monthlyInterestPayment',
'monthlyPrincipalRepayment',
];
return {
...Object.fromEntries(fields.map((key) => [key, '0'])),
currency: 'MZN',
periods: 4,
depreciationLifeMonths: 60,
products: [
{
label: 'Produto QA',
monthlyUnits: '1',
unitPrice: '1000.03',
unitVariableCost: '0',
monthlyUnitsByPeriod: ['1', '2', '0', '3'],
},
],
};
}
const settings = { start: '2026-01', basis: 'net', declared: true, auto: true };
function draft(value = plan()) {
return {
currency: 'MZN',
business_plan_snapshot: serializeBusinessPlanSnapshot(value),
monthly_iva_settings: JSON.stringify(settings),
};
}
function snapshot(input) {
return {
contract_version: 1,
scenario_kind: 'fiscal_rule_published',
base: null,
currency: 'MZN',
indicative_total: null,
legal_interpretation: false,
formula_version: 'investmoz.tax.v1',
context: {
reference_date: '2026-01-01',
currency: 'MZN',
currency_fraction_digits: 2,
scenario_id: '61000100-0000-4000-8000-000000000099',
scenario_version: 1,
engine_version: 'fiscal-published-v1',
ruleset_version: 'IVA-GERAL-BASE-LIQUIDA:1',
},
result: {
status: 'indeterminate',
value: null,
unit: 'MZN',
explanation: 'IVA mensal; não é imposto a pagar.',
missing_inputs: ['monthly_sales'],
source_ids: ['qa-source'],
},
presentation: { value: null, rounding_applied: false },
fiscal_provenance: {
rule: {
rule_code: 'IVA-GERAL-BASE-LIQUIDA',
rule_version: 1,
valid_from: '2023-01-01',
valid_until: null,
source: { diploma: 'Fonte sintética QA', article: 'Teste' },
},
scope_status: 'user_declared',
},
monthly_projection: {
input,
cash_treatment: monthlyIvaCashTreatment,
rows: [
{
date: '2026-01-01',
sales_declared: '1000.03',
net_sales: '1000.03',
rate: '16',
vat: '160.0048',
gross_sales: '1160.0348',
status: 'calculated',
},
{
date: '2026-02-01',
sales_declared: '0',
net_sales: '0',
rate: '16',
vat: '0',
gross_sales: '0',
status: 'calculated',
},
{
date: '2026-03-01',
sales_declared: null,
net_sales: null,
rate: null,
vat: null,
gross_sales: null,
status: 'missing_sales',
},
],
},
};
}
void test('monthly IVA reuses exactly the existing per-product sales projection without repeating entry', () => {
const value = plan();
assert.deepEqual(
monthlyProjectedSales(value),
forecastBusinessPlan(value).rows.map((row) => row.revenue),
);
assert.deepEqual(monthlySalesFromDraft(draft()).amounts, [
'1000.03',
'2000.06',
'0.00',
'3000.09',
]);
});
void test('a missing month remains unknown while an explicit zero and other months stay available', () => {
const value = plan();
value.products[0].monthlyUnitsByPeriod = ['1', '', '0', '2'];
assert.deepEqual(monthlyProjectedSales(value), [
'1000.03',
null,
'0.00',
'2000.06',
]);
const input = monthlyIvaInput(draft(value), settings);
assert.equal(input.monthly_sales[1].amount, null);
assert.equal(input.monthly_sales[2].amount, '0.00');
assert.equal('rate' in input, false);
assert.equal('monthlyTaxAssessed' in input, false);
});
void test('gross basis is preserved and old fiscal results become stale when sales, currency or basis change', () => {
const value = draft();
const initial = monthlyIvaInput(value, settings);
const frozen = JSON.stringify(initial);
const gross = monthlyIvaInput(value, { ...settings, basis: 'included' });
assert.equal(gross.monthly_sales[0].amount, '1000.03');
assert.notEqual(monthlyIvaKey(gross), monthlyIvaKey(initial));
const changed = plan();
changed.products[0].monthlyUnitsByPeriod[0] = '2';
assert.notEqual(
monthlyIvaKey(monthlyIvaInput(draft(changed), settings)),
monthlyIvaKey(initial),
);
assert.equal(JSON.stringify(initial), frozen);
assert.throws(
() => monthlyIvaInput(value, { ...settings, start: '2026-12' }),
/cobertura/,
);
});
void test('monthly recovery and finance report preserve exact values and mark stale history without recomputing VAT', () => {
const value = plan();
value.products[0].monthlyUnitsByPeriod = ['1', '0', '', '2'];
const original = draft(value);
const input = monthlyIvaInput(original, settings);
const result = snapshot(input);
original.monthly_iva_snapshot = JSON.stringify({
input,
execution: {
id: '61000100-0000-4000-8000-000000000077',
status: 'completed',
version_no: 2,
result_snapshot: result,
},
});
const partition = partitionSimulationDraft(original);
const recovered = restoreSimulationDraft(
partition.economic_snapshot,
partition.personal_snapshot,
);
assert.equal(recovered.monthly_iva_snapshot, original.monthly_iva_snapshot);
assert.equal(
readMonthlyIvaRecord(recovered.monthly_iva_snapshot).execution
.result_snapshot.monthly_projection.rows[0].vat,
'160.0048',
);
assert.equal(readArithmeticSnapshot(result).integrity, 'versioned');
let report = createProjectDossierReport(recovered, {
selectedSections: new Set(['finance']),
});
let text = JSON.stringify(report);
assert.match(text, /160\.0048/);
assert.match(text, /1160\.0348/);
assert.match(text, /sem informação de vendas/);
assert.match(text, /não é imposto a pagar/);
value.products[0].monthlyUnitsByPeriod[0] = '2';
recovered.business_plan_snapshot = serializeBusinessPlanSnapshot(value);
report = createProjectDossierReport(recovered, {
selectedSections: new Set(['finance']),
});
text = JSON.stringify(report);
assert.match(text, /Histórico desactualizado/);
assert.match(text, /160\.0048/);
assert.match(monthlyIvaCashTreatment, /não é somado novamente/);
assert.match(monthlyIvaCashTreatment, /nem deduzido como custo/);
});