import test from 'node:test'; import assert from 'node:assert/strict'; import { newPecPlan, calculatePecPlan, validatePecPlan, } from '../../resources/js/lib/irpc-pec-plan.ts'; import { calculatePecObligation } from '../../resources/js/lib/irpc-pec-obligation.ts'; import { plan as obligation } from './helpers/irpc-pec-obligation-case.mjs'; import { combinedCase } from './helpers/irpc-investment-combination-case.mjs'; import { recordAnnual, annualCurrent, annualBook, saveAnnualBook, annualHtml, } from '../../resources/js/lib/irpc-annual-settlement.ts'; const f = (n) => ({ numerator: String(n), denominator: '1' }), three = () => ({ ...newPecPlan(), mode: 'three', reference: 'SYNTHETIC proposed allocation29(1)', june: '10000', august: '15000', october: '15000', }), calc = (p, total = f(40000), na = false) => calculatePecPlan(p, total, na, '2026'); test('three explicit positive instalments reconcile exact total without inventing equal allocation', () => { const m = calc(three()); assert.deepEqual(m.pending, []); assert.deepEqual( m.rows.map((r) => r.month), ['2026-06', '2026-08', '2026-10'], ); assert.deepEqual( m.rows.map((r) => r.amount), [f(10000), f(15000), f(15000)], ); assert.equal(m.legalDeadlineValidated, false); assert.equal(m.paymentExecuted, false); assert.equal(m.annualDeductionCreated, false); }); test('single option records chosen month and entire obligation but does not certify deadline', () => { const m = calc({ ...three(), mode: 'single', singleMonth: '2026-06' }); assert.deepEqual(m.pending, []); assert.deepEqual(m.rows, [{ month: '2026-06', amount: f(40000) }]); assert.equal(m.legalDeadlineValidated, false); }); test('unknown,total changed,zero,not applicable,invalid month and mismatched cents never release a reconciled schedule', () => { for (const p of [ { ...three(), june: '' }, { ...three(), june: '9999.99' }, { ...three(), june: '10000.01' }, { ...three(), june: '0' }, { ...three(), reference: '' }, { ...three(), mode: 'single', singleMonth: '2025-06' }, ]) { const m = calc(p); assert.ok(m.pending.length); assert.equal(m.allocationReconciled, false); assert.deepEqual(m.rows, []); } for (const t of [null, f(0), f(50000)]) assert.ok(calc(three(), t).pending.length); assert.ok(calc(three(), f(40000), true).pending.length); assert.throws(() => validatePecPlan({ ...three(), june: null })); }); test('schedule preserves explicit option in native book and invalidates annual snapshot on edits without new paid deductions', () => { const { d, b } = combinedCase(); b.pecObligation = { ...obligation(), subjectNuit: b.subjectNuit }; let current = recordAnnual(d, b, '2026-11-11T11:00:00Z'); current.pecObligation.plan = three(); assert.equal(annualCurrent(d, current), false); current = recordAnnual(d, current, '2026-11-11T11:01:00Z'); d.modelo_a_snapshot = saveAnnualBook(d, current); const back = annualBook(d); assert.equal(annualCurrent(d, back), true); assert.deepEqual(back.result.model.arithmeticBalance, f(138)); assert.deepEqual(back.pecPayments, b.pecPayments); assert.equal(back.history.length, 1); assert.match(annualHtml(d), /PEC — projecção de tesouraria/); back.pecObligation.previousTurnover = '20000000'; assert.ok( calculatePecPlan( back.pecObligation.plan, calculatePecObligation( back.pecObligation, back.subjectNuit, back.year, back.assessment, ).total, false, '2026', ).pending.length, ); assert.equal(annualCurrent(d, back), false); });