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

282 lines
10 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import {
operationTreatments,
newSalesOperation,
calculateSales,
recordSales,
salesBaseFields,
salesBaseMembership,
salesBook,
salesCurrent,
salesReportRows,
saveSalesBook,
modeloA06Correspondence,
} from '../../resources/js/lib/iva-operations.ts';
import {
calculateAcquisitions,
recordAcquisitions,
saveAcquisitionBook,
acquisitionBook,
acquisitionCurrent,
} from '../../resources/js/lib/iva-acquisitions.ts';
import { modeloAFields } from '../../resources/js/lib/iva-modelo-a.ts';
const seed = JSON.parse(
fs.readFileSync(
new URL('./fixtures/iva-l3e-preserved.json', import.meta.url),
'utf8',
),
);
function fixture() {
const d = structuredClone(seed);
d.context_opportunity = 'opportunity:375';
d.project_title = 'SYNTHETIC QA — P001 correspondência05/06';
const choices = [
['general', '100'],
['private_health', '200'],
['export_goods', '300'],
['housing_rent', '400'],
];
const b = {
currency: 'MZN',
rows: choices.map(([treatment, amount], index) => {
const rule = operationTreatments.find((t) => t.id === treatment);
return {
...newSalesOperation(`p001-${index}`, '2026-01', 'MZN'),
description: `SYNTHETIC QA ${treatment}`,
establishment: 'SYNTHETIC QA estabelecimento',
amount,
basis: 'net',
treatment,
confirmed: rule.conditions.map((_, i) => i),
evidence:
'SYNTHETIC QA pressupostos do tratamento,sem declaração submetida',
};
}),
periods: [{ period: '2026-01', complete: true }],
};
const a = {
currency: 'MZN',
taxpayerRegistered: 'yes',
rows: [],
periods: [
{ period: '2026-01', complete: true, absent: ['11', '12', '13'] },
],
};
return { d, b, a };
}
function recorded() {
const f = fixture();
f.b = recordSales(f.d, f.b, '2026-10-25T16:00:00Z');
f.d.modelo_a_snapshot = saveSalesBook(f.d, f.b);
f.a = recordAcquisitions(f.d, f.a, '2026-10-25T16:00:01Z');
f.d.modelo_a_snapshot = saveAcquisitionBook(f.d, f.a);
return f;
}
void test('whole current catalogue has exclusive field mappings;05and07 keep treatment references;no foreign=06 or invented06 treatment', () => {
assert.equal(
new Set(operationTreatments.map((t) => t.id)).size,
operationTreatments.length,
);
assert.ok(operationTreatments.filter((t) => t.field === '05').length > 0);
assert.ok(operationTreatments.filter((t) => t.field === '07').length > 0);
assert.equal(operationTreatments.filter((t) => t.field === '06').length, 0);
for (const rule of operationTreatments) {
assert.ok(
rule.field === null || salesBaseFields.includes(rule.field),
rule.id,
);
if (rule.field === '05' || rule.field === '07') {
assert.ok(rule.source);
assert.ok(rule.article);
}
}
assert.equal(
operationTreatments.find((t) => t.id === 'foreign_operation').field,
null,
);
assert.equal(
operationTreatments.find((t) => t.id === 'foreign_operation').rate,
null,
);
});
void test('implemented classified operations feed05or07once and14sums01+03+05+06+07 without duplicating the export300', () => {
const { d, b, a } = recorded(),
p = b.result.periods[0].fields;
assert.deepEqual(
Object.fromEntries(
['01', '03', '05', '06', '07'].map((f) => [f, p[f]]),
),
{
'01': '100.00',
'03': '200.00',
'05': '300.00',
'06': '0.00',
'07': '400.00',
},
);
assert.equal(a.result.periods[0].fields['14'], '1000.00');
assert.doesNotMatch(
a.result.periods[0].reasons.join(' '),
/01\/02: vendas ou IVA mensal em falta/,
);
const membership = salesBaseMembership(b.result.rows);
assert.equal(membership['05'][0].id, 'p001-2');
assert.equal(membership['06'].length, 0);
assert.equal(Object.values(membership).flat().length, b.rows.length);
assert.ok(salesCurrent(d, salesBook(d)));
assert.ok(acquisitionCurrent(d, acquisitionBook(d)));
});
void test('mechanical aggregation rejects one operation in05and06or twice in05;distinct06probe is not a new tax classification', () => {
const { b } = recorded(),
r = b.result.rows.find((r) => r.field === '05');
assert.throws(
() => salesBaseMembership([r, { ...r, field: '06' }]),
/Mesma operação/,
);
assert.throws(() => salesBaseMembership([r, { ...r }]), /Mesma operação/);
const grouped = salesBaseMembership([
r,
{ ...r, id: 'aggregation-only-06', field: '06', net: '50.00' },
]);
assert.deepEqual(
grouped['05'].map((r) => r.net),
['300.00'],
);
assert.deepEqual(
grouped['06'].map((r) => r.net),
['50.00'],
);
const f = fixture();
f.b.rows.push({ ...f.b.rows[2], treatment: 'foreign_operation' });
assert.throws(() => calculateSales(f.d, f.b), /duplicados/);
});
void test('foreign or unknown operation never becomes06orzero;declared absence cannot override that unknown in14', () => {
for (const treatment of ['foreign_operation', 'other']) {
const { d, b, a } = fixture();
b.rows[2].treatment = treatment;
const value = recordSales(d, b);
assert.equal(value.result.rows[2].field, null);
assert.equal(value.result.periods[0].fields['06'], null);
d.modelo_a_snapshot = saveSalesBook(d, value);
a.periods[0].absent.push('06');
assert.equal(calculateAcquisitions(d, a).periods[0].fields['06'], null);
assert.equal(calculateAcquisitions(d, a).periods[0].fields['14'], null);
}
});
void test('06zero needs complete classified scope;incomplete scope stays unknown;exact included5percent remains exact in14', () => {
const { d, b, a } = fixture();
b.periods[0].complete = false;
assert.equal(calculateSales(d, b).periods[0].fields['06'], null);
b.periods[0].complete = true;
b.rows[1].basis = 'included';
b.rows[1].amount = '100';
const calc = recordSales(d, b);
d.modelo_a_snapshot = saveSalesBook(d, calc);
const totals = calculateAcquisitions(d, a);
assert.deepEqual(totals.periods[0].exactFields['14'], {
numerator: '18800',
denominator: '21',
});
assert.deepEqual(calc.result.periods[0].exactFields['06'], {
numerator: '0',
denominator: '1',
});
assert.equal(totals.periods[0].fields['14'], '895.238095');
});
void test('deliberate edits do not rewrite05/06history;save/recovery preserves every prior map and report uses frozen inputs', () => {
let { d, b, a } = recorded();
const prior = JSON.parse(seed.modelo_a_snapshot),
first = structuredClone(b.result);
b.rows[2].description = 'Deliberate edit after calculation';
d.modelo_a_snapshot = saveSalesBook(d, b);
assert.equal(salesCurrent(d, salesBook(d)), false);
assert.doesNotMatch(
salesReportRows(d).flat().join(' '),
/Deliberate edit after calculation/,
);
b = recordSales(d, b);
assert.deepEqual(b.history.at(-1), first);
d.modelo_a_snapshot = saveSalesBook(d, b);
const recovered = JSON.parse(JSON.stringify(d));
assert.deepEqual(salesBook(recovered), b);
const current = JSON.parse(recovered.modelo_a_snapshot);
for (const [k, v] of Object.entries(prior))
assert.deepEqual(
k === 'version'
? current[k]
: Object.fromEntries(
Object.keys(v).map((key) => [key, current[k][key]]),
),
v,
k,
);
assert.equal(a.result.periods[0].fields['14'], '1000.00');
});
void test('confirmed form labels replace the generic model-search blocker without changing foreign semantics', () => {
assert.match(
modeloA06Correspondence,
/Correspondência confirmada pela Tempo/,
);
assert.match(modeloA06Correspondence, /Campo14=01\+03\+05\+06\+07/);
assert.doesNotMatch(
modeloA06Correspondence,
/Não há correspondência unívoca|formulário antigo/,
);
assert.equal(
modeloAFields.find((f) => f.field === '05').purpose,
'Vendas isentas com direito à dedução',
);
assert.equal(
modeloAFields.find((f) => f.field === '07').purpose,
'Operações que não conferem direito à dedução',
);
assert.match(
modeloAFields.find((f) => f.field === '06').purpose,
/artigo18/,
);
});
void test('export guarded API and component proof plus full current treatment-to-field inventory', () => {
const { d, b, a } = recorded();
assert.equal(b.result.periods[0].fields['05'], '300.00');
assert.equal(a.result.periods[0].fields['14'], '1000.00');
if (process.env.P001_RECONCILIATION_OUTPUT) {
fs.writeFileSync(
process.env.P001_RECONCILIATION_OUTPUT,
JSON.stringify(d),
);
fs.writeFileSync(
process.env.P001_RECONCILIATION_OUTPUT.replace(
'calculated.json',
'treatment-field-inventory.json',
),
JSON.stringify(
{
origin: 'User-confirmed received form;implemented catalogue only',
labels: Object.fromEntries(
modeloAFields
.filter((f) =>
['05', '06', '07', '14'].includes(f.field),
)
.map((f) => [f.field, f.purpose]),
),
treatments: operationTreatments.map(
({ id, label, field, article, source }) => ({
id,
label,
field,
article,
source,
}),
),
field06_treatments: [],
limit: 'No new positive06tax classification inferred;mechanical duplicate guards separately tested.',
},
null,
2,
),
);
}
});