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 = [ 'social_assistance_public', 'social_assistance_nonprofit', 'social_care_public', 'social_care_nonprofit', 'activity_facilities_public', 'activity_facilities_nonprofit', ]; function seed() { return JSON.parse( fs.readFileSync( new URL('./fixtures/iva-health-goods.json', import.meta.url), 'utf8', ), ); } function fixture() { const draft = seed(); draft.context_opportunity = 'opportunity:311'; draft.project_title = 'SYNTHETIC QA — G2B social'; let book = { currency: 'MZN', rows: ids.map((id, i) => ({ ...newSalesOperation(`g2b-${i}`, '2026-03', '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-03', complete: true }], }; book = recordSales(draft, book); draft.modelo_a_snapshot = saveSalesBook(draft, book); return { draft, book }; } void test('G2B three branches have six 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.equal( rule.conditions.length, row.treatment.endsWith('_nonprofit') ? 7 : 4, ); 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(book.result.periods[0].fields['07'], '600.00'); }); void test('new social context round-trips and reports only its operations, preserving both 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']) { 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, '600.00', 'opportunity:311', 'Manica', '2026-03', 'MZN', ]) assert.ok(model.includes(token), token); for (const token of ['financial_stamp', 'patient_transport']) assert.equal(model.includes(token), false); assert.equal(book.result.rows.length, 6); assert.equal(draft.currency, original.currency); assert.deepEqual(draft.selection_labels, original.selection_labels); }); if (process.env.IVA_G2B_OUT) fs.writeFileSync( process.env.IVA_G2B_OUT, JSON.stringify(fixture().draft, null, 2), );