423 lines
14 KiB
JavaScript
423 lines
14 KiB
JavaScript
import { createProjectDossierReport } from '../../resources/js/lib/project-dossier-report.ts';
|
|
import assert from 'node:assert/strict';
|
|
|
|
void test('legacy draft without a compatible source keeps its shape and identity', () => {
|
|
const legacy = {
|
|
activity_id: 'legacy-activity',
|
|
infrastructure_needs: ['energia'],
|
|
};
|
|
assert.equal(inheritSimulatorFields(legacy), legacy);
|
|
assert.equal(
|
|
Object.hasOwn(
|
|
inheritSimulatorFields(legacy),
|
|
'compatible_fields_snapshot',
|
|
),
|
|
false,
|
|
);
|
|
});
|
|
import test from 'node:test';
|
|
import {
|
|
inheritCompatibleFields,
|
|
inheritSimulatorFields,
|
|
reusedInvestmentValue,
|
|
} from '../../resources/js/lib/context-inheritance.ts';
|
|
import { serializeBusinessPlanSnapshot } from '../../resources/js/lib/business-financial-plan.ts';
|
|
import { monthlySalesFromDraft } from '../../resources/js/lib/monthly-iva.ts';
|
|
import {
|
|
restoreSimulationDraft,
|
|
partitionSimulationDraft,
|
|
} from '../../resources/js/lib/simulation-form.ts';
|
|
|
|
void test('known investment questions become editable summaries after save/recovery, while missing and deliberate edits remain questions', () => {
|
|
const scope = 'dossier:A:simulator';
|
|
const source = {
|
|
project_title: 'Florestas — Beira',
|
|
project_summary: 'Preparar investimento florestal',
|
|
province_id: 'p1',
|
|
province_id_label: 'Sofala',
|
|
sector_id: '3',
|
|
sector_id_label: 'Agronegócio',
|
|
};
|
|
const reused = inheritCompatibleFields(scope, {}, source, null);
|
|
const draft = {
|
|
...reused.patch,
|
|
context_reuse: reused.metadata,
|
|
purpose: 'simulate_only',
|
|
journey_type: 'investor',
|
|
};
|
|
const saved = partitionSimulationDraft(draft);
|
|
const recovered = restoreSimulationDraft(
|
|
saved.personal_snapshot,
|
|
saved.economic_snapshot,
|
|
);
|
|
assert.equal(
|
|
reusedInvestmentValue(scope, 'project_title', recovered),
|
|
source.project_title,
|
|
);
|
|
assert.equal(
|
|
reusedInvestmentValue(scope, 'province_id', recovered),
|
|
'Sofala',
|
|
);
|
|
assert.equal(
|
|
reusedInvestmentValue(scope, 'sector_id', recovered),
|
|
'Agronegócio',
|
|
);
|
|
assert.equal(reusedInvestmentValue(scope, 'district_id', recovered), null);
|
|
assert.equal(
|
|
reusedInvestmentValue(
|
|
'dossier:B:simulator',
|
|
'project_title',
|
|
recovered,
|
|
),
|
|
null,
|
|
);
|
|
assert.equal(
|
|
reusedInvestmentValue(scope, 'project_title', {
|
|
...recovered,
|
|
project_title: 'Edição deliberada',
|
|
}),
|
|
null,
|
|
);
|
|
assert.equal(
|
|
reusedInvestmentValue(scope, 'project_title', {
|
|
...recovered,
|
|
project_title: '',
|
|
}),
|
|
null,
|
|
);
|
|
assert.equal(
|
|
reusedInvestmentValue(scope, 'province_id', {
|
|
...recovered,
|
|
province_id_label: '',
|
|
}),
|
|
null,
|
|
);
|
|
assert.equal(
|
|
reusedInvestmentValue(scope, 'currency', {
|
|
...recovered,
|
|
currency: 'MZN',
|
|
}),
|
|
null,
|
|
);
|
|
assert.equal(
|
|
reusedInvestmentValue(scope, 'project_title', {
|
|
...recovered,
|
|
context_reuse: 'invalid',
|
|
}),
|
|
null,
|
|
);
|
|
});
|
|
|
|
void test('local geography and sector keep their labels when the dossier proposes different identifiers', () => {
|
|
const scope = 'dossier:A:simulator';
|
|
const source = {
|
|
province_id: 'p1',
|
|
province_id_label: 'Sofala',
|
|
district_id: 'd1',
|
|
district_id_label: 'Dondo',
|
|
sector_id: '3',
|
|
sector_id_label: 'Agronegócio',
|
|
};
|
|
const local = {
|
|
province_id: 'p2',
|
|
province_id_label: 'Gaza',
|
|
district_id: 'd2',
|
|
district_id_label: 'Outro distrito',
|
|
sector_id: '4',
|
|
sector_id_label: 'Turismo',
|
|
};
|
|
const reconciliation = inheritCompatibleFields(scope, local, source, null);
|
|
assert.deepEqual(reconciliation.patch, {});
|
|
assert.ok(reconciliation.conflicts.includes('province_id'));
|
|
const first = inheritCompatibleFields(scope, {}, source, null);
|
|
const changed = inheritCompatibleFields(
|
|
scope,
|
|
{ ...first.patch, context_reuse: first.metadata },
|
|
{
|
|
...source,
|
|
province_id: 'p3',
|
|
province_id_label: 'Manica',
|
|
district_id: '',
|
|
district_id_label: '',
|
|
},
|
|
first.metadata,
|
|
);
|
|
assert.equal(changed.patch.province_id, 'p3');
|
|
assert.equal(changed.patch.province_id_label, 'Manica');
|
|
assert.equal(changed.patch.district_id, '');
|
|
assert.equal(changed.patch.district_id_label, '');
|
|
const stale = inheritCompatibleFields(
|
|
scope,
|
|
{ province_id_label: 'Nome antigo' },
|
|
source,
|
|
null,
|
|
);
|
|
assert.equal(stale.patch.province_id, 'p1');
|
|
assert.equal(stale.patch.province_id_label, 'Sofala');
|
|
});
|
|
|
|
void test('investment entry selects the existing simulation route without replacing an earlier deliberate purpose', () => {
|
|
const scope = 'dossier:A:simulator';
|
|
const source = {
|
|
journey_type: 'investor',
|
|
journey_type_label: 'Avaliar o meu investimento',
|
|
purpose: 'simulate_only',
|
|
purpose_label: 'Apenas simular',
|
|
};
|
|
const first = inheritCompatibleFields(scope, {}, source, null);
|
|
const values = { ...first.patch, context_reuse: first.metadata };
|
|
assert.equal(
|
|
reusedInvestmentValue(scope, 'journey_type', values),
|
|
'Avaliar o meu investimento',
|
|
);
|
|
assert.equal(
|
|
reusedInvestmentValue(scope, 'purpose', values),
|
|
'Apenas simular',
|
|
);
|
|
const old = inheritCompatibleFields(
|
|
scope,
|
|
{ journey_type: 'small_business', purpose: 'seek_partner' },
|
|
source,
|
|
null,
|
|
);
|
|
assert.deepEqual(old.patch, {});
|
|
assert.equal(
|
|
reusedInvestmentValue(scope, 'purpose', {
|
|
purpose: 'seek_partner',
|
|
context_reuse: old.metadata,
|
|
}),
|
|
null,
|
|
);
|
|
});
|
|
|
|
void test('inherited source updates follow through recovery while local edits and deliberate clears survive', () => {
|
|
const first = inheritCompatibleFields(
|
|
'dossier:A:simulator',
|
|
{},
|
|
{ currency: 'MZN', project_title: 'A' },
|
|
null,
|
|
);
|
|
const saved = { ...first.patch, context_reuse: first.metadata };
|
|
const changed = inheritCompatibleFields(
|
|
'dossier:A:simulator',
|
|
saved,
|
|
{ currency: 'USD', project_title: 'A2' },
|
|
saved.context_reuse,
|
|
);
|
|
assert.deepEqual(changed.patch, { currency: 'USD', project_title: 'A2' });
|
|
const edited = inheritCompatibleFields(
|
|
'dossier:A:simulator',
|
|
{ ...saved, currency: 'EUR', project_title: '' },
|
|
{ currency: 'USD', project_title: 'A2' },
|
|
saved.context_reuse,
|
|
);
|
|
assert.deepEqual(edited.patch, {});
|
|
assert.deepEqual(edited.conflicts, ['currency', 'project_title']);
|
|
const recovered = JSON.parse(
|
|
JSON.stringify({ ...saved, currency: 'EUR', project_title: '' }),
|
|
);
|
|
assert.deepEqual(
|
|
inheritCompatibleFields(
|
|
'dossier:A:simulator',
|
|
recovered,
|
|
{ currency: 'USD', project_title: 'A2' },
|
|
recovered.context_reuse,
|
|
).patch,
|
|
{},
|
|
);
|
|
assert.ok(!first.metadata.includes('MZN'));
|
|
assert.ok(!first.metadata.includes('project_title":"A'));
|
|
});
|
|
void test('a different dossier cannot take ownership of existing values using another dossier metadata', () => {
|
|
const first = inheritCompatibleFields(
|
|
'dossier:A:simulator',
|
|
{},
|
|
{ currency: 'MZN' },
|
|
null,
|
|
);
|
|
const second = inheritCompatibleFields(
|
|
'dossier:B:simulator',
|
|
first.patch,
|
|
{ currency: 'USD' },
|
|
first.metadata,
|
|
);
|
|
assert.deepEqual(second.patch, {});
|
|
assert.deepEqual(second.conflicts, ['currency']);
|
|
});
|
|
void test('a local geographic override protects the entire hierarchy', () => {
|
|
const first = inheritCompatibleFields(
|
|
'A',
|
|
{},
|
|
{ province_id: 'p1', district_id: 'd1' },
|
|
null,
|
|
);
|
|
const update = inheritCompatibleFields(
|
|
'A',
|
|
{ ...first.patch, province_id: 'p-local' },
|
|
{ province_id: 'p2', district_id: 'd2' },
|
|
first.metadata,
|
|
);
|
|
assert.deepEqual(update.patch, {});
|
|
});
|
|
void test('compatible currency and date reuse survives backend partition and new session recovery without invented tax scope', () => {
|
|
const initial = inheritSimulatorFields({
|
|
currency: 'USD',
|
|
expected_start_date: '2026-03-01',
|
|
purpose: 'simulate_only',
|
|
});
|
|
assert.equal(initial.fiscal_currency, 'USD');
|
|
assert.equal(initial.published_iva_currency, 'USD');
|
|
assert.equal(initial.opportunity_currency, 'USD');
|
|
assert.equal(JSON.parse(initial.monthly_iva_settings).start, '2026-03');
|
|
assert.equal(JSON.parse(initial.monthly_iva_settings).declared, false);
|
|
assert.equal(initial.published_iva_date, undefined);
|
|
assert.equal(
|
|
inheritSimulatorFields({
|
|
expected_start_date: '2026-01-01',
|
|
monthly_iva_settings: '[invalid stored input',
|
|
}).monthly_iva_settings,
|
|
'[invalid stored input',
|
|
);
|
|
const saved = partitionSimulationDraft(initial, true);
|
|
const recovered = restoreSimulationDraft(
|
|
saved.economic_snapshot,
|
|
saved.personal_snapshot,
|
|
);
|
|
assert.equal(recovered.published_iva_currency, 'USD');
|
|
assert.strictEqual(inheritSimulatorFields(recovered), recovered);
|
|
const changed = inheritSimulatorFields({ ...recovered, currency: 'EUR' });
|
|
assert.equal(changed.fiscal_currency, 'EUR');
|
|
const override = inheritSimulatorFields({
|
|
...changed,
|
|
fiscal_currency: 'MZN',
|
|
published_iva_currency: '',
|
|
});
|
|
assert.equal(override.fiscal_currency, 'MZN');
|
|
assert.equal(override.published_iva_currency, '');
|
|
});
|
|
void test('land consumer reuses geography inside an empty parcel and preserves all other parcel data', () => {
|
|
const first = inheritCompatibleFields(
|
|
'land',
|
|
{
|
|
parcels: [
|
|
{ area: '12', province_id: '' },
|
|
{ area: '20', province_id: 'other' },
|
|
],
|
|
},
|
|
{ parcels: [{ province_id: 'P', district_id: 'D' }] },
|
|
null,
|
|
);
|
|
assert.equal(first.patch.parcels[0].area, '12');
|
|
assert.equal(first.patch.parcels[0].district_id, 'D');
|
|
assert.equal(first.patch.parcels[1].province_id, 'other');
|
|
const changed = inheritCompatibleFields(
|
|
'land',
|
|
{
|
|
parcels: [
|
|
{ ...first.patch.parcels[0], area: '13' },
|
|
first.patch.parcels[1],
|
|
],
|
|
},
|
|
{ parcels: [{ province_id: 'P2' }] },
|
|
first.metadata,
|
|
);
|
|
assert.equal(changed.patch.parcels[0].area, '13');
|
|
assert.equal(changed.patch.parcels[0].district_id, '');
|
|
});
|
|
void test('clearing an inherited zone reference clears only the inherited value, preserving a deliberate override', () => {
|
|
const first = inheritCompatibleFields(
|
|
'A',
|
|
{},
|
|
{ special_zone_id: 'zone' },
|
|
null,
|
|
);
|
|
assert.equal(
|
|
inheritCompatibleFields(
|
|
'A',
|
|
first.patch,
|
|
{ special_zone_id: '' },
|
|
first.metadata,
|
|
).patch.special_zone_id,
|
|
'',
|
|
);
|
|
assert.deepEqual(
|
|
inheritCompatibleFields(
|
|
'A',
|
|
{ special_zone_id: 'local' },
|
|
{ special_zone_id: '' },
|
|
first.metadata,
|
|
).patch,
|
|
{},
|
|
);
|
|
});
|
|
void test('conflicting saved sales currency is explained instead of relabelled or converted', () => {
|
|
const fields = [
|
|
'openingCash',
|
|
'monthlyCollections',
|
|
'monthlyVariableCostPayments',
|
|
'monthlyInventoryPurchases',
|
|
'monthlyOperatingExpenses',
|
|
'monthlyOperatingPayments',
|
|
'monthlyPayrollExpense',
|
|
'monthlyPayrollPayments',
|
|
'assetInvestment',
|
|
'inventory',
|
|
'receivables',
|
|
'payables',
|
|
'equity',
|
|
'shareholderLoans',
|
|
'bankLoanProceeds',
|
|
'grants',
|
|
'assetDepreciation',
|
|
'monthlyInterestExpense',
|
|
'monthlyInterestPayment',
|
|
'monthlyPrincipalRepayment',
|
|
];
|
|
const plan = {
|
|
...Object.fromEntries(fields.map((key) => [key, '0'])),
|
|
currency: 'MZN',
|
|
periods: 4,
|
|
depreciationLifeMonths: 60,
|
|
products: [
|
|
{
|
|
label: 'QA',
|
|
monthlyUnits: '1',
|
|
unitPrice: '100',
|
|
unitVariableCost: '0',
|
|
},
|
|
],
|
|
};
|
|
assert.throws(
|
|
() =>
|
|
monthlySalesFromDraft({
|
|
currency: 'USD',
|
|
business_plan_snapshot: serializeBusinessPlanSnapshot(plan),
|
|
}),
|
|
/projecção de vendas está em MZN.*projecto está em USD/,
|
|
);
|
|
});
|
|
|
|
void test('report preserves consulted zone version and makes unsupported benefit explicit without inventing a fiscal result', () => {
|
|
const context = {
|
|
version: 1,
|
|
context_revision: 3,
|
|
zone: { title: 'Referência QA', version: 7, href: '/zonas/qa' },
|
|
fields: { currency: 'USD' },
|
|
};
|
|
const draft = {
|
|
project_title: 'QA',
|
|
currency: 'USD',
|
|
context_applicability_snapshot: JSON.stringify(context),
|
|
};
|
|
const saved = JSON.stringify(draft);
|
|
const report = createProjectDossierReport(draft, {
|
|
selectedSections: new Set(['sources']),
|
|
});
|
|
assert.match(JSON.stringify(report), /Referência QA, versão 7/);
|
|
assert.match(
|
|
JSON.stringify(report),
|
|
/Benefício fiscal da zona indeterminado/,
|
|
);
|
|
assert.equal(JSON.stringify(draft), saved);
|
|
});
|