import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import { recordSales, saveSalesBook, } from '../../resources/js/lib/iva-operations.ts'; import { recordAcquisitions, saveAcquisitionBook, acquisitionBook, } from '../../resources/js/lib/iva-acquisitions.ts'; import { closureBook, closureCurrent, closureSources, closureReportRows, recordClosure, } from '../../resources/js/lib/iva-closure.ts'; import { newSubstitution, previousDeclarations, substitutionBook, recordSubstitution, saveSubstitutionBook, substitutionCurrent, substitutionReportRows, } from '../../resources/js/lib/iva-substitution.ts'; const frozen = JSON.parse( fs.readFileSync( new URL('./fixtures/iva-l3c-preserved.json', import.meta.url), 'utf8', ), ); const old = JSON.parse( fs.readFileSync( new URL('./fixtures/iva-l3ab-preserved.json', import.meta.url), 'utf8', ), ); function fixture() { const d = structuredClone(frozen), base = JSON.parse(old.modelo_a_snapshot), snap = JSON.parse(d.modelo_a_snapshot); for (const [k, v] of Object.entries(base)) if (k !== 'version') snap[k] = { ...v, ...snap[k] }; d.modelo_a_snapshot = JSON.stringify(snap); const sales = snap.salesOperations['opportunity:373']; sales.rows[2].amount = '1375'; d.modelo_a_snapshot = saveSalesBook(d, recordSales(d, sales)); d.modelo_a_snapshot = saveAcquisitionBook( d, recordAcquisitions(d, acquisitionBook(d), '2026-10-25T12:30:00.000Z'), ); const prior = previousDeclarations(d).find((p) => p.period === '2026-03'), source = closureSources(d) .filter((s) => s.period === '2026-03') .at(-1); const row = { ...newSubstitution('sub-Mar'), prior, source, priorReference: 'SYNTHETIC QA declaraçãoMarço', priorDate: '2026-04-15', reference: 'SYNTHETIC QA substituiçãoMarço', date: '2026-05-10', reason: 'SYNTHETIC QA operação de Março corrigida', interest: 'documented', interestAmount: '1', interestRef: 'SYNTHETIC QA juros adicionais identificados', payments: [ { id: 'payment2', reference: 'SYNTHETIC QA reciboP2', date: '2026-05-11', amount: '20', }, ], }; return { d, b: { version: 1, rows: [row] } }; } const value = (r, f) => r.fields[f] && `${r.fields[f].numerator}/${r.fields[f].denominator}`; function alternative(d, debit, credit) { const s = JSON.parse(d.modelo_a_snapshot), calc = structuredClone(s.books['opportunity:373'].result); calc.revision += 10; calc.calculatedAt += '-unit'; const p = calc.periods.find((p) => p.period === '2026-03'); p.fields['17'] = String(debit); p.fields['18'] = String(credit); p.exactFields['17'] = { numerator: String(debit), denominator: '1' }; p.exactFields['18'] = { numerator: String(credit), denominator: '1' }; s.books['opportunity:373'].history.push(calc); d.modelo_a_snapshot = JSON.stringify(s); return closureSources(d).find( (p) => p.revision === calc.revision && p.period === '2026-03', ); } void test('real corrected L2 version gives additional60 plus1;original72/30/42 and credits remain unchanged', () => { const { d, b } = fixture(), before = closureBook(d), r = recordSubstitution(d, b).result.rows[0]; assert.equal(r.status, 'determined', r.reason); assert.equal(value(r, '21'), '60/1'); assert.equal(value(r, '22'), '60/1'); assert.equal(value(r, '24'), '61/1'); assert.deepEqual(r.paid, { numerator: '20', denominator: '1' }); assert.deepEqual(r.outstanding, { numerator: '41', denominator: '1' }); assert.deepEqual(r.totalOutstanding, { numerator: '83', denominator: '1' }); assert.deepEqual(r.totalPaid, { numerator: '50', denominator: '1' }); for (const f of ['19', '20', '25', '26', '27']) assert.equal(r.fields[f], null); d.modelo_a_snapshot = saveSubstitutionBook(d, recordSubstitution(d, b)); assert.deepEqual(closureBook(d), before); assert.ok(closureCurrent(d, before)); assert.match( closureReportRows(d) .map((r) => r.join(' ')) .join(' '), /Calculado24:72\/1;pagamentos registados:30\/1;por liquidar:42\/1/, ); }); void test('quadro9 four sign cases use exact difference;credit waits for communication and never offsets original debt', () => { for (const [priorKind, actual17, actual18, expected] of [ ['debit', 220, 0, '60/1'], ['credit', 20, 0, '120/1'], ['credit', 0, 80, '20/1'], ['debit', 0, 40, '-200/1'], ]) { const { d, b } = fixture(); if (priorKind === 'credit') { const jan = previousDeclarations(d).find( (p) => p.period === '2026-01', ); b.rows[0].prior = { ...jan }; b.rows[0].prior.period = '2026-03'; // use an actual frozen credit-origin version in March below const snap = JSON.parse(d.modelo_a_snapshot), ab = snap.ivaClosures['opportunity:373'], calc = structuredClone(ab.result), r = calc.rows[2]; r.fields['17'] = { numerator: '0', denominator: '1' }; r.fields['18'] = { numerator: '100', denominator: '1' }; r.id = 'unit-credit-Mar'; calc.inputs[2].id = r.id; ab.history.push(calc); d.modelo_a_snapshot = JSON.stringify(snap); b.rows[0].prior = previousDeclarations(d).find( (p) => p.id === 'unit-credit-Mar', ); } b.rows[0].source = alternative(d, actual17, actual18); const r = recordSubstitution(d, b).result.rows[0]; assert.equal(r.status, 'determined', r.reason); assert.equal(value(r, '21'), expected); if (expected.startsWith('-')) { assert.equal(value(r, '22'), '0/1'); assert.deepEqual(r.pendingCredit, { numerator: '200', denominator: '1', }); assert.equal(value(r, '26'), null); assert.equal(r.totalOutstanding.numerator, '42'); } } }); void test('missing facts,wrong period,currency,unknown source and interest never produce presumed zero', () => { const { d, b } = fixture(); for (const patch of [ { prior: null }, { source: null }, { priorReference: '' }, { priorDate: '' }, { date: '2026-04-01' }, { reference: '' }, { reason: '' }, { interest: '' }, ]) { const bad = structuredClone(b); Object.assign(bad.rows[0], patch); assert.equal( recordSubstitution(d, bad).result.rows[0].status, 'indeterminate', JSON.stringify(patch), ); } const bad = structuredClone(b); bad.rows[0].source = closureSources(d).find((s) => s.period === '2026-02'); assert.equal(recordSubstitution(d, bad).result.rows[0].fields['21'], null); assert.equal( recordSubstitution({ ...d, currency: 'USD' }, b).result.rows[0].status, 'indeterminate', ); }); void test('old payment cannot be re-used and two substitutions cannot repeat one predecessor or payment', () => { const { d, b } = fixture(); b.rows[0].payments[0].reference = 'SYNTHETIC QA reciboP1'; assert.match( recordSubstitution(d, b).result.rows[0].reason, /Pagamento já/, ); const f = fixture(); f.b.rows.push({ ...structuredClone(f.b.rows[0]), id: 'copy' }); assert.ok( recordSubstitution(f.d, f.b).result.rows.every( (r) => r.status === 'indeterminate', ), ); const g = fixture(); g.b.rows[0].payments.push({ ...g.b.rows[0].payments[0], id: 'copy' }); assert.equal(recordSubstitution(g.d, g.b).result.rows[0].paid, null); }); void test('successive substitution links frozen predecessor,charges only new difference and preserves all payments', () => { const { d, b } = fixture(); let book = recordSubstitution(d, b, '2026-10-25T12:31:00Z'); d.modelo_a_snapshot = saveSubstitutionBook(d, book); const next = structuredClone(book.rows[0]); next.id = 'sub-Mar-2'; next.prior = previousDeclarations(d).find((p) => p.kind === 'C'); next.source = alternative(d, 250, 0); next.priorReference = next.reference; next.priorDate = next.date; next.reference = 'SYNTHETIC QA segunda substituição'; next.date = '2026-06-10'; next.interest = 'none'; next.interestRef = 'SYNTHETIC QA sem juros adicionais'; next.payments = []; book = recordSubstitution(d, { ...book, rows: [...book.rows, next] }); const r = book.result.rows[1]; assert.equal(r.status, 'determined', r.reason); assert.equal(value(r, '21'), '30/1'); assert.deepEqual(r.totalOutstanding, { numerator: '113', denominator: '1', }); assert.deepEqual(r.totalPaid, { numerator: '50', denominator: '1' }); assert.equal(book.history.length, 1); next.payments = [ { id: 'reuse', reference: 'SYNTHETIC QA reciboP2', date: '2026-06-11', amount: '20', }, ]; assert.equal( recordSubstitution(d, { ...book, rows: [book.rows[0], next] }).result .rows[1].paid, null, ); }); void test('save/recover retains every L1/L2/AB map;report uses frozen inputs and recalculation appends history', () => { const { d, b } = fixture(), before = JSON.parse(d.modelo_a_snapshot); let book = recordSubstitution(d, b); d.modelo_a_snapshot = saveSubstitutionBook(d, book); assert.ok(substitutionCurrent(d, substitutionBook(d))); const stored = JSON.parse(d.modelo_a_snapshot); for (const [k, v] of Object.entries(before)) assert.deepEqual(stored[k], v, k); book.rows[0].reference = 'new deliberate edit'; assert.equal(substitutionCurrent(d, book), false); d.modelo_a_snapshot = saveSubstitutionBook(d, book); const report = substitutionReportRows(d) .map((r) => r.join(' ')) .join(' '); assert.match(report, /SYNTHETIC QA substituiçãoMarço/); assert.doesNotMatch(report, /new deliberate edit/); const oldResult = structuredClone(book.result); book = recordSubstitution(d, book); assert.deepEqual(book.history.at(-1), oldResult); d.modelo_a_snapshot = saveSubstitutionBook(d, book); assert.ok(substitutionCurrent(d, book)); assert.match( substitutionReportRows(d) .map((r) => r.join(' ')) .join(' '), /new deliberate edit/, ); }); void test('fractional delta remains exact and overpayment does not generate communicated credit', () => { const { d, b } = fixture(); const s = alternative(d, 220, 0), snap = JSON.parse(d.modelo_a_snapshot), calc = snap.books['opportunity:373'].history.at(-1); calc.periods[2].exactFields['17'] = { numerator: '661', denominator: '3' }; d.modelo_a_snapshot = JSON.stringify(snap); b.rows[0].source = closureSources(d).find( (x) => x.revision === s.revision && x.period === '2026-03', ); b.rows[0].payments[0].amount = '100'; const r = recordSubstitution(d, b).result.rows[0]; assert.equal(value(r, '21'), '181/3'); assert.deepEqual(r.overpayment, { numerator: '116', denominator: '3' }); assert.equal(r.pendingCredit.numerator, '0'); assert.equal(r.fields['20'], null); }); void test('a payment already frozen in C cannot be added again in A/B;original valid A/B remains current', () => { const { d, b } = fixture(); d.modelo_a_snapshot = saveSubstitutionBook(d, recordSubstitution(d, b)); const ab = closureBook(d); assert.ok(closureCurrent(d, ab)); ab.rows[2].payments.push({ ...b.rows[0].payments[0] }); assert.equal(closureCurrent(d, ab), false); const r = recordClosure(d, ab).result.rows[2]; assert.equal(r.paid, null); assert.match(r.reason, /Pagamento/); }); void test('a newly conflicting payment in recovered AB makes C stale and requires reconciliation', () => { const { d, b } = fixture(); const book = recordSubstitution(d, b); d.modelo_a_snapshot = saveSubstitutionBook(d, book); assert.ok(substitutionCurrent(d, book)); const s = JSON.parse(d.modelo_a_snapshot); s.ivaClosures['opportunity:373'].rows[2].payments.push({ ...b.rows[0].payments[0], }); d.modelo_a_snapshot = JSON.stringify(s); assert.equal(substitutionCurrent(d, book), false); assert.equal(recordSubstitution(d, book).result.rows[0].paid, null); }); void test('a substitution cannot link back through a circular predecessor chain', () => { const { d, b } = fixture(); let book = recordSubstitution(d, b, '2026-10-25T12:31:00Z'); d.modelo_a_snapshot = saveSubstitutionBook(d, book); const child = { ...structuredClone(book.rows[0]), id: 'child', prior: previousDeclarations(d).find((p) => p.kind === 'C'), priorReference: book.rows[0].reference, priorDate: book.rows[0].date, date: '2026-06-10', reference: 'child-ref', payments: [], }; book = recordSubstitution( d, { ...book, rows: [...book.rows, child] }, '2026-10-25T12:32:00Z', ); d.modelo_a_snapshot = saveSubstitutionBook(d, book); book.rows[0].prior = previousDeclarations(d).find( (p) => p.kind === 'C' && p.id === 'child', ); assert.ok( recordSubstitution(d, book).result.rows.every((r) => /circular/.test(r.reason), ), ); }); void test('export synthetic demonstrated correction for guarded private API and actual component proof', () => { const { d, b } = fixture(); const result = recordSubstitution(d, b, '2026-10-25T12:31:00.000Z'); const again = recordSubstitution(d, result, '2026-10-25T12:32:00.000Z'); d.modelo_a_snapshot = saveSubstitutionBook(d, again); assert.ok(substitutionCurrent(d, again)); if (process.env.IVA_L3C_CALCULATED) fs.writeFileSync(process.env.IVA_L3C_CALCULATED, JSON.stringify(d)); });