196 lines
7.2 KiB
JavaScript
196 lines
7.2 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { fiscalQaRows } from '../../resources/js/lib/fiscal-qa-preview.ts';
|
|
import {
|
|
exportCompletedArithmeticCsv,
|
|
readArithmeticSnapshot,
|
|
} from '../../resources/js/lib/simulation-form.ts';
|
|
import { createProjectDossierReport } from '../../resources/js/lib/project-dossier-report.ts';
|
|
|
|
function fixture() {
|
|
const snapshot = {
|
|
contract_version: 1,
|
|
scenario_kind: 'fiscal_rule_qa_preview',
|
|
base: '0.03',
|
|
currency: 'MZN',
|
|
indicative_total: '0.0048',
|
|
legal_interpretation: false,
|
|
formula_version: 'investmoz.tax.v1',
|
|
context: {
|
|
reference_date: '2026-10-21',
|
|
currency: 'MZN',
|
|
currency_fraction_digits: 2,
|
|
scenario_id: '61000100-0000-4000-8000-000000000099',
|
|
scenario_version: 1,
|
|
engine_version: 'fiscal-draft-preview-v1',
|
|
ruleset_version: 'IVA-GERAL-BASE-LIQUIDA:1',
|
|
},
|
|
result: {
|
|
status: 'calculated',
|
|
value: '0.0048',
|
|
unit: 'MZN',
|
|
explanation:
|
|
'Cálculo exacto QA; enquadramento declarado; não é saldo a pagar.',
|
|
missing_inputs: [],
|
|
source_ids: ['source-fixture'],
|
|
},
|
|
intermediate: { result: '48/10000' },
|
|
fiscal_provenance: {
|
|
rule: {
|
|
rule_code: 'IVA-GERAL-BASE-LIQUIDA',
|
|
rule_version: 1,
|
|
valid_from: '2023-01-01',
|
|
valid_until: null,
|
|
source_sha256: 'a'.repeat(64),
|
|
source: { diploma: 'Lei 22/2022', article: 'Artigo 17(1)' },
|
|
},
|
|
},
|
|
};
|
|
return {
|
|
id: '61000100-0000-4000-8000-000000000088',
|
|
status: 'completed',
|
|
version_no: 2,
|
|
result_snapshot: snapshot,
|
|
reports: [
|
|
{
|
|
id: '61000100-0000-4000-8000-000000000077',
|
|
version_no: 1,
|
|
status: 'ready',
|
|
report_snapshot: structuredClone(snapshot),
|
|
report_sha256: 'b'.repeat(64),
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
void test('screen rows preserve sub-cent IVA as a string and distinguish calculation from payment', () => {
|
|
const simulation = fixture();
|
|
const before = JSON.stringify(simulation);
|
|
const rows = fiscalQaRows(simulation.result_snapshot);
|
|
assert.equal(
|
|
rows.find(([label]) => label.startsWith('IVA calculado'))[1],
|
|
'0.0048',
|
|
);
|
|
assert.match(
|
|
rows.find(([label]) => label === 'Arredondamento')[1],
|
|
/Não aplicado/,
|
|
);
|
|
assert.match(
|
|
rows.find(([label]) => label === 'Enquadramento')[1],
|
|
/não validado juridicamente/,
|
|
);
|
|
assert.equal(JSON.stringify(simulation), before);
|
|
});
|
|
|
|
void test('CSV preserves the exact amount and refuses a mismatched immutable report', () => {
|
|
const simulation = fixture();
|
|
const csv = exportCompletedArithmeticCsv(
|
|
simulation,
|
|
'2026-10-21T09:00:00Z',
|
|
);
|
|
assert.match(csv, /0\.0048/);
|
|
assert.doesNotMatch(csv, /Resultado fiscal final,Indeterminado/);
|
|
simulation.reports[0].report_snapshot.result.value = '0.00';
|
|
assert.throws(
|
|
() => exportCompletedArithmeticCsv(simulation),
|
|
/relatório preservado/,
|
|
);
|
|
});
|
|
|
|
void test('dossier report uses the same exact snapshot and preserves genuine indetermination', () => {
|
|
const simulation = fixture();
|
|
function report() {
|
|
return createProjectDossierReport(
|
|
{ project_title: 'QA fiscal', currency: 'MZN' },
|
|
{
|
|
selectedSections: new Set(['finance']),
|
|
currentCalculation: {
|
|
status: 'completed',
|
|
version: 2,
|
|
snapshot: simulation.result_snapshot,
|
|
},
|
|
},
|
|
);
|
|
}
|
|
const rows = report().sections.find(
|
|
(section) => section.id === 'finance',
|
|
).rows;
|
|
assert.equal(
|
|
rows.find((row) => row.field.startsWith('IVA calculado')).value,
|
|
'0.0048',
|
|
);
|
|
assert.equal(
|
|
rows.find((row) => row.field === 'Fonte da taxa').value,
|
|
'Lei 22/2022 — Artigo 17(1)',
|
|
);
|
|
simulation.result_snapshot.result.status = 'indeterminate';
|
|
simulation.result_snapshot.result.value = null;
|
|
simulation.result_snapshot.indicative_total = null;
|
|
simulation.result_snapshot.result.missing_inputs = ['eligible_parameter'];
|
|
assert.equal(
|
|
report()
|
|
.sections.find((section) => section.id === 'finance')
|
|
.rows.find((row) => row.field.startsWith('IVA calculado')).value,
|
|
'Indeterminado',
|
|
);
|
|
});
|
|
|
|
void test('published consumer routes the frozen snapshot through screen CSV and dossier without QA labels', () => {
|
|
const simulation = fixture();
|
|
const snapshot = simulation.result_snapshot;
|
|
snapshot.scenario_kind = 'fiscal_rule_published';
|
|
snapshot.context.engine_version = 'fiscal-published-v1';
|
|
snapshot.presentation = {
|
|
value: snapshot.result.value,
|
|
rounding_applied: false,
|
|
};
|
|
snapshot.result.explanation =
|
|
'IVA pela versão publicada; enquadramento declarado.';
|
|
snapshot.fiscal_provenance.rule.status = 'published';
|
|
snapshot.fiscal_provenance.scope_status = 'user_declared';
|
|
snapshot.fiscal_provenance.mode = 'published_version';
|
|
simulation.reports[0].report_snapshot = structuredClone(snapshot);
|
|
const before = JSON.stringify(simulation);
|
|
const rows = fiscalQaRows(snapshot);
|
|
assert.equal(readArithmeticSnapshot(snapshot).integrity, 'versioned');
|
|
assert.match(rows[0][1], /publicada/);
|
|
assert.doesNotMatch(JSON.stringify(rows), /rascunho|não publicada|QA/);
|
|
const csv = exportCompletedArithmeticCsv(
|
|
simulation,
|
|
'2026-10-21T12:00:00Z',
|
|
);
|
|
for (const [label, value] of rows) {
|
|
assert.ok(csv.includes(label));
|
|
assert.ok(csv.includes(value.replaceAll('"', '""')));
|
|
}
|
|
const report = createProjectDossierReport(
|
|
{ project_title: 'Published IVA QA', currency: 'MZN' },
|
|
{
|
|
selectedSections: new Set(['finance']),
|
|
currentCalculation: { status: 'completed', version: 2, snapshot },
|
|
},
|
|
);
|
|
const finance = report.sections.find((section) => section.id === 'finance');
|
|
for (const [field, value] of rows)
|
|
assert.ok(
|
|
finance.rows.some(
|
|
(row) => row.field === field && row.value === value,
|
|
),
|
|
);
|
|
assert.doesNotMatch(JSON.stringify(finance), /rascunho|proveniência QA/);
|
|
assert.equal(JSON.stringify(simulation), before);
|
|
});
|
|
|
|
void test('published consumer indeterminate snapshot is valid and a mismatched presentation is refused', () => {
|
|
const snapshot = fixture().result_snapshot;
|
|
snapshot.scenario_kind = 'fiscal_rule_published';
|
|
snapshot.result.status = 'indeterminate';
|
|
snapshot.result.value = null;
|
|
snapshot.result.missing_inputs = ['operation_scope'];
|
|
snapshot.indicative_total = null;
|
|
snapshot.presentation = { value: null, rounding_applied: false };
|
|
assert.equal(readArithmeticSnapshot(snapshot).integrity, 'versioned');
|
|
snapshot.presentation.value = '0';
|
|
assert.equal(readArithmeticSnapshot(snapshot).integrity, 'invalid');
|
|
});
|