import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import { exportBeneficiaryTreatments as rules } from '../../resources/js/lib/iva-export-beneficiaries.ts'; import { newSalesOperation, evaluateSalesOperation, recordSales, saveSalesBook, salesBook, salesCurrent, } from '../../resources/js/lib/iva-operations.ts'; import { createProjectDossierReport } from '../../resources/js/lib/project-dossier-report.ts'; const frozen = JSON.parse( fs.readFileSync( new URL('./fixtures/iva-g4d-inputs.json', import.meta.url), 'utf8', ), ); const row = (rule, i) => ({ ...newSalesOperation(`g4e-${i}`, '2026-12', 'MZN'), description: `SYNTHETIC QA — ${rule.label}`, establishment: 'SYNTHETIC QA estabelecimento', amount: '100', basis: i % 2 ? 'included' : 'net', treatment: rule.id, confirmed: rule.conditions.map((_, n) => n), evidence: 'SYNTHETIC QA — pressuposto expresso de projecção: intervenientes, destino/saída e regulamentação própria (b); acordo aplicável, disposição, âmbito/período e reconhecimento (j/k); reconhecimento do organismo e prévio direito (l). Sem certificação real.', }); function fixture() { const draft = { ...structuredClone(frozen), context_opportunity: 'opportunity:327', project_title: 'SYNTHETIC QA — G4E / Exportações e beneficiários', }; const book = recordSales(draft, { currency: 'MZN', rows: rules.map(row), periods: [{ period: '2026-12', complete: true }], }); draft.modelo_a_snapshot = saveSalesBook(draft, book); return { draft, book }; } void test('four distinct branches require every applicable condition; missing treaty/recognition/private-use exclusion never produces zero', () => { assert.equal(rules.length, 4); for (const [i, rule] of rules.entries()) { const input = row(rule, i), result = evaluateSalesOperation(input, 'MZN'); assert.equal(result.field, '05'); assert.equal(result.vat, '0.00'); assert.equal(result.net, '100.00'); assert.equal(result.gross, '100.00'); for (const missing of input.confirmed) { const incomplete = evaluateSalesOperation( { ...input, confirmed: input.confirmed.filter((n) => n !== missing), }, 'MZN', ); assert.equal(incomplete.vat, null); assert.equal(incomplete.net, null); } for (const patch of [ { amount: '' }, { evidence: '' }, { sourceCurrency: 'USD' }, { period: '2027-01' }, { basis: '' }, { description: '' }, { establishment: '' }, ]) assert.equal( evaluateSalesOperation({ ...input, ...patch }, 'MZN').vat, null, ); } assert.match(rules[0].conditions.join(' '), /uso privado/); assert.match(rules[0].conditions.join(' '), /regulamentação própria/); for (const rule of rules.slice(1, 3)) assert.match(rule.conditions.join(' '), /acordo ou convénio/); assert.match( rules[3].conditions.join(' '), /Prévio reconhecimento do direito/, ); }); void test('editing legal support preserves price/context/history and makes totals indeterminate until conditions are reviewed', () => { const { draft, book } = fixture(); assert.equal(book.result.periods[0].fields['05'], '400.00'); for (let i = 0; i < 4; i++) { const changed = recordSales(draft, { ...book, rows: book.rows.map((r, n) => n === i ? { ...r, evidence: 'Documento/pressuposto revisto; condições por reconfirmar', confirmed: [], } : r, ), }); assert.equal(changed.rows[i].amount, '100'); assert.equal(changed.rows[i].sourceCurrency, 'MZN'); assert.equal(changed.rows[i].period, '2026-12'); assert.equal(changed.result.periods[0].fields['05'], null); assert.deepEqual(changed.history.at(-1), book.result); const edited = { ...draft, modelo_a_snapshot: saveSalesBook(draft, { ...book, rows: changed.rows, }), }; assert.equal(salesCurrent(edited, salesBook(edited)), false); } }); void test('snapshot/report retain sources,assumptions,opportunity;18 previous books are identical/current without recalculation', () => { const { draft, book } = fixture(); assert.ok(salesCurrent(draft, book)); assert.deepEqual( salesBook(JSON.parse(JSON.stringify(draft))), JSON.parse(JSON.stringify(book)), ); const before = JSON.parse(frozen.modelo_a_snapshot).salesOperations, after = JSON.parse(draft.modelo_a_snapshot).salesOperations; assert.equal(Object.keys(before).length, 18); for (const [context, original] of Object.entries(before)) { assert.deepEqual(after[context], original); assert.ok( salesCurrent( { ...draft, context_opportunity: context }, after[context], ), ); } const report = JSON.stringify( createProjectDossierReport(draft, { selectedSections: new Set(['finance']), version: 'QA G4E', }), ); for (const token of [ 'opportunity:327', 'Manica', '2026-12', '400.00', '13(1)(b)', '13(1)(j)', '13(1)(k)', '13(1)(l)', 'pressuposto expresso', 'QA G4E', ]) assert.ok(report.includes(token), token); if (process.env.IVA_G4E_CALCULATED) fs.writeFileSync(process.env.IVA_G4E_CALCULATED, JSON.stringify(draft)); });