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('