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

58 lines
6.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'}]}};
}
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']);
});
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);
});
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/);
});
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/);
});