import test from 'node:test';
import assert from 'node:assert/strict';
import { fixture } from './fixtures/iva-beverage-fixture.mjs';
import {
recordBeverageRestitution,
saveBeverageRestitutionBook,
beverageRestitutionBook,
beverageRestitutionSources,
beverageRestitutionCurrent,
beverageRestitutionReportRows,
} from '../../resources/js/lib/iva-beverage-restitution.ts';
import { beverageDeductionEvidence } from '../../resources/js/lib/iva-beverage-allocation.ts';
import {
acquisitionBook,
recordAcquisitions,
saveAcquisitionBook,
} from '../../resources/js/lib/iva-acquisitions.ts';
import { newAcquisitionAllocation } from '../../resources/js/lib/iva-acquisition-allocation.ts';
import { fiscalCalculationGaps } from '../../resources/js/lib/iva-calculation-gaps.ts';
import {
l3FormPreparations,
l3FormPreparationHtml,
l3FormalitiesDependencies,
} from '../../resources/js/lib/l3-form-preparation.ts';
const n = (v) => v && `${v.numerator}/${v.denominator}`;
const allocation = (from, to, patch = {}) => ({
from,
to,
reference: 'SYNTHETIC QA repartição documento',
deductionFrom: '',
deductionTo: '',
deductionReference: '',
...patch,
});
function pending(row, id, from, to) {
return {
...structuredClone(row),
id,
decision: 'pending',
decisionReference: '',
decisionDate: '',
decisionIssuer: '',
approvedAmount: '',
receipts: [],
allocation: allocation(from, to),
};
}
function partialPrior(numerator = '3', denominator = '8', expected = '6/1') {
const f = fixture(),
a = acquisitionBook(f.d);
a.rows[0].origin = 'effective';
a.rows[0].allocation = 'mixed';
a.rows[0].bc.beveragePath = 'deduction';
a.rows[0].bc.facts.onlyDeduction = 'yes';
a.rows[0].d = {
...newAcquisitionAllocation('2026-12'),
method: 'actual',
numerator,
denominator,
unit: 'unidades físicas documentadas',
scopeRef: 'SYNTHETIC QA parte com direito',
source: 'SYNTHETIC QA critério e registos',
communicationRef: 'SYNTHETIC QA comunicação22(2)',
noExcludedPayments: 'yes',
facts: {
scopeConfirmed: 'yes',
rightBasis: 'yes',
actualScope: 'yes',
communication: 'yes',
conditionsMet: 'yes',
},
};
let actual = recordAcquisitions(f.d, a);
assert.equal(
n(actual.result.rows[0].deductibleExact),
expected,
actual.result.rows[0].reason,
);
f.d.modelo_a_snapshot = saveAcquisitionBook(f.d, actual);
actual.rows[0].bc.beveragePath = 'refund';
actual.rows[0].allocation = 'taxable_only';
delete actual.rows[0].d;
actual = recordAcquisitions(f.d, actual);
f.d.modelo_a_snapshot = saveAcquisitionBook(f.d, actual);
f.b.rows[0].source = beverageRestitutionSources(f.d)[0];
return f;
}
void test('disjoint portions6+10 share one documentary VAT16 without overlap;save/recovery/report preserve prior maps', () => {
const { d, b } = fixture(),
before = JSON.parse(d.modelo_a_snapshot);
b.rows = [
pending(b.rows[0], 'portion6', '0', '6'),
pending(b.rows[0], 'portion10', '6', '16'),
];
const book = recordBeverageRestitution(d, b);
assert.deepEqual(
book.result.rows.map((r) => [r.status, n(r.requested)]),
[
['determined', '6/1'],
['determined', '10/1'],
],
);
d.modelo_a_snapshot = saveBeverageRestitutionBook(d, book);
assert.deepEqual(beverageRestitutionBook(d), book);
for (const [k, v] of Object.entries(before))
assert.deepEqual(JSON.parse(d.modelo_a_snapshot)[k], v, k);
assert.ok(beverageRestitutionCurrent(d, book));
assert.match(
beverageRestitutionReportRows(d).flat().join(' '),
/IVA documental:16.00;parcelaIVA:6.00/,
);
});
void test('overlap,negative/zero/outside bounds,missing support and duplicate legacy whole line are rejected', () => {
const { d, b } = fixture();
for (const pair of [
['0', '0'],
['-1', '6'],
['6', '5'],
['0', '16.000001'],
]) {
const book = { ...b, rows: [pending(b.rows[0], 'bad', ...pair)] };
assert.equal(
recordBeverageRestitution(d, book).result.rows[0].requested,
null,
);
}
let book = {
...b,
rows: [
pending(b.rows[0], 'p1', '0', '10'),
pending(b.rows[0], 'p2', '9', '16'),
],
};
assert.ok(
recordBeverageRestitution(d, book).result.rows.every(
(r) => !r.requested,
),
);
book.rows = [
pending(b.rows[0], 'p1', '0', '6'),
structuredClone(b.rows[0]),
];
assert.ok(
recordBeverageRestitution(d, book).result.rows.every(
(r) => !r.requested,
),
);
book.rows = [pending(b.rows[0], 'p1', '0', '6')];
book.rows[0].allocation.reference = '';
assert.equal(
recordBeverageRestitution(d, book).result.rows[0].requested,
null,
);
});
void test('effective deduction6 must be explicitly reconciled to its own interval;E10 uses only the disjoint remainder', () => {
const { d, b } = partialPrior();
assert.equal(
n(beverageDeductionEvidence(d, b.rows[0].source).amount),
'6/1',
);
b.rows = [pending(b.rows[0], 'remainder', '6', '16')];
assert.equal(
recordBeverageRestitution(d, b).result.rows[0].requested,
null,
);
Object.assign(b.rows[0].allocation, {
deductionFrom: '0',
deductionTo: '6',
deductionReference: 'SYNTHETIC QA correspondência da dedução original',
});
let book = recordBeverageRestitution(d, b);
assert.equal(n(book.result.rows[0].requested), '10/1');
assert.equal(book.result.rows[0].status, 'determined');
assert.ok(
acquisitionBook(d).history.some((c) =>
c.rows.some((r) => n(r.deductibleExact) === '6/1'),
),
);
for (const patch of [
{ deductionTo: '5' },
{ deductionFrom: '4', deductionTo: '10' },
{ deductionReference: '' },
]) {
const changed = structuredClone(b);
Object.assign(changed.rows[0].allocation, patch);
assert.equal(
recordBeverageRestitution(d, changed).result.rows[0].requested,
null,
);
}
});
void test('a rounded tiny deduction,divergent historical amounts and editing away a committed portion never releaseVAT', () => {
const { d, b } = partialPrior(),
a = acquisitionBook(d),
old = structuredClone(
a.history.find((c) =>
c.rows.some((r) => n(r.deductibleExact) === '6/1'),
),
);
old.rows.find((r) => r.id === 'beverage-purchase').deductibleExact = {
numerator: '1',
denominator: '100000000',
};
old.rows.find((r) => r.id === 'beverage-purchase').deductible = '0.00';
a.history.push(old);
d.modelo_a_snapshot = saveAcquisitionBook(d, a);
b.rows = [pending(b.rows[0], 'x', '0', '16')];
assert.equal(
recordBeverageRestitution(d, b).result.rows[0].requested,
null,
);
assert.match(
beverageDeductionEvidence(d, b.rows[0].source).reason,
/divergentes/,
);
const f = fixture();
f.b.rows = [pending(f.b.rows[0], 'committed', '0', '6')];
let book = recordBeverageRestitution(f.d, f.b);
f.d.modelo_a_snapshot = saveBeverageRestitutionBook(f.d, book);
book = recordBeverageRestitution(f.d, { ...book, rows: [] });
f.d.modelo_a_snapshot = saveBeverageRestitutionBook(f.d, book);
const overlap = pending(f.b.rows[0], 'new', '5', '16');
assert.equal(
recordBeverageRestitution(f.d, { ...book, rows: [overlap] }).result
.rows[0].requested,
null,
);
const edited = pending(f.b.rows[0], 'committed', '0', '7');
assert.equal(
recordBeverageRestitution(f.d, { ...book, rows: [edited] }).result
.rows[0].requested,
null,
);
});
void test('gap inventory identifies supplyVAT independently from purchase16 and includes implementation limits of margin/travel/events', () => {
const { d } = fixture(),
gaps = fiscalCalculationGaps(d);
const supply = gaps.find(
(g) =>
g.book === 'salesOperations' && g.reason.includes('Bebidas em13'),
);
assert.ok(supply);
assert.match(supply.resolution, /IVA da aquisição separado/);
const store = JSON.parse(d.modelo_a_snapshot);
store.regulatedSecondhands = {
...store.regulatedSecondhands,
[d.context_opportunity]: {
rows: [{ id: 'm', period: '2026-12' }],
result: {
inputs: [{ id: 'm', period: '2026-12' }],
rows: [
{
id: 'm',
status: 'determined',
baseAmount: '100',
vat: null,
reason: 'Base da margem calculada;IVA não calculado.',
},
],
},
},
};
d.modelo_a_snapshot = JSON.stringify(store);
const margin = fiscalCalculationGaps(d).find(
(g) => g.book === 'regulatedSecondhands' && g.kind === 'implementation',
);
assert.ok(margin);
assert.match(margin.resolution, /liquidação/);
assert.ok(margin.required.length);
assert.ok(
fiscalCalculationGaps({ ...d, context_opportunity: 'other-context' })
.length === 0,
);
});
void test('formal preparation maps only identified L3 fields;E keeps16/0/12/7/5 outside27;HTML escapes documents and lists specific dependencies', () => {
const { d, b } = fixture();
b.rows[0].entityRef = '';
d.modelo_a_snapshot = saveBeverageRestitutionBook(
d,
recordBeverageRestitution(d, b),
);
const forms = l3FormPreparations(d),
e = forms.find((p) => p.branch === 'E');
assert.deepEqual(
e.fields.map((f) => f.value),
['16.00', '16.00', '0.00', '12.00', '7.00', '5.00'],
);
assert.ok(e.fields.every((f) => f.field === 'Dossier'));
const html = l3FormPreparationHtml(e);
assert.ok(!html.includes('