import assert from 'node:assert/strict'; import test from 'node:test'; import fs from 'node:fs'; import { operationTreatments, newSalesOperation, evaluateSalesOperation, recordSales, saveSalesBook, salesBook, salesCurrent, } from '../../resources/js/lib/iva-operations.ts'; import { createProjectDossierReport } from '../../resources/js/lib/project-dossier-report.ts'; const ids = [ 'cultural_visits_public', 'cultural_visits_nonprofit', 'religious_personnel_public', 'religious_personnel_nonprofit', 'statutory_membership', ]; function seed() { return JSON.parse( fs.readFileSync( new URL('./fixtures/iva-social.json', import.meta.url), 'utf8', ), ); } function fixture() { const draft = seed(); draft.context_opportunity = 'opportunity:312'; draft.project_title = 'SYNTHETIC QA — G2C social'; let book = { currency: 'MZN', rows: ids.map((id, i) => ({ ...newSalesOperation(`g2c-${i}`, '2026-04', 'MZN'), description: `SYNTHETIC QA ${id}`, establishment: 'SYNTHETIC QA equipamento Manica', amount: '100', basis: i % 2 ? 'included' : 'net', treatment: id, confirmed: operationTreatments .find((t) => t.id === id) .conditions.map((_, n) => n), evidence: 'SYNTHETIC QA — natureza, entidade, reconhecimento e condições cumulativas pressupostos documentados; sem certificação', })), periods: [{ period: '2026-04', complete: true }], }; book = recordSales(draft, book); draft.modelo_a_snapshot = saveSalesBook(draft, book); return { draft, book }; } void test('G2C three branches have five entity-specific treatments and cumulative nonprofit conditions', () => { const { book } = fixture(); for (const row of book.rows) { const rule = operationTreatments.find((t) => t.id === row.treatment), r = evaluateSalesOperation(row, 'MZN'); assert.equal(r.field, '07'); assert.equal(r.net, '100.00'); assert.equal(r.vat, '0.00'); assert.equal(r.gross, '100.00'); assert.match(r.article, /10\(/); assert.ok(rule.conditions.length >= 4); if ( row.treatment.endsWith('_nonprofit') || row.treatment === 'statutory_membership' ) assert.ok(rule.conditions.length >= 7); for (const missing of row.confirmed) assert.equal( evaluateSalesOperation( { ...row, confirmed: row.confirmed.filter((n) => n !== missing), }, 'MZN', ).vat, null, ); assert.equal( evaluateSalesOperation({ ...row, evidence: '' }, 'MZN').vat, null, ); assert.equal( evaluateSalesOperation({ ...row, period: '2025-04' }, 'MZN').vat, null, ); assert.equal(evaluateSalesOperation(row, 'USD').vat, null); } assert.equal(book.result.periods[0].fields['07'], '500.00'); }); void test('new cultural context round-trips and reports only its operations, preserving three accepted books without recalculation', () => { const { draft, book } = fixture(), original = seed(); const old = JSON.parse(original.modelo_a_snapshot).salesOperations; const recovered = JSON.parse(JSON.stringify(draft)), store = JSON.parse(recovered.modelo_a_snapshot).salesOperations; for (const id of [ 'opportunity:309', 'opportunity:310', 'opportunity:311', ]) { assert.deepEqual(store[id], old[id]); assert.equal( salesCurrent({ ...recovered, context_opportunity: id }, store[id]), true, ); } assert.equal(salesCurrent(recovered, salesBook(recovered)), true); const model = JSON.stringify( createProjectDossierReport(recovered, { selectedSections: new Set(['finance']), }), ); for (const token of [ ...ids, '500.00', 'opportunity:312', 'Manica', '2026-04', 'MZN', ]) assert.ok(model.includes(token), token); for (const token of [ 'financial_stamp', 'patient_transport', 'social_assistance_public', ]) assert.equal(model.includes(token), false); assert.equal(book.result.rows.length, 5); assert.equal(draft.currency, original.currency); assert.deepEqual(draft.selection_labels, original.selection_labels); }); if (process.env.IVA_G2C_OUT) fs.writeFileSync( process.env.IVA_G2C_OUT, JSON.stringify(fixture().draft, null, 2), );