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 = [ 'public_security', 'official_military_supplies', 'sisa_subject_operation', 'exempt_activity_asset_sale', 'no_deduction_right_asset_sale', 'excluded_deduction_asset_sale', ]; function seed() { return JSON.parse( fs.readFileSync( new URL('./fixtures/iva-services-nature.json', import.meta.url), 'utf8', ), ); } function fixture() { const draft = seed(); draft.context_opportunity = 'opportunity:316'; draft.project_title = 'SYNTHETIC QA — G2G social'; let book = { currency: 'MZN', rows: ids.map((id, i) => ({ ...newSalesOperation(`g2g-${i}`, '2026-08', '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-08', complete: true }], }; book = recordSales(draft, book); draft.modelo_a_snapshot = saveSalesBook(draft, book); return { draft, book }; } void test('G2G four branches with six conditional treatments require their own operation and eligibility 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, /9\((2|11|12)\)/); assert.ok(rule.conditions.length >= 3); 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-08' }, 'MZN').vat, null, ); assert.equal(evaluateSalesOperation(row, 'USD').vat, null); } assert.equal(book.result.periods[0].fields['07'], '600.00'); assert.equal(book.result.periods[0].fields['05'], '0.00'); assert.ok(book.result.rows.every((r) => r.reason.includes('sem direito'))); }); void test('new conditional context round-trips and reports only its operations, preserving seven 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', 'opportunity:312', 'opportunity:313', 'opportunity:314', 'opportunity:315', ]) { 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:316', 'Manica', '2026-08', 'MZN', ]) assert.ok(model.includes(token), token); for (const token of [ 'financial_stamp', 'patient_transport', 'social_assistance_public', 'statutory_membership', 'author_rights', 'industrial_feed', 'special_tax_games', ]) 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_G2G_OUT) fs.writeFileSync( process.env.IVA_G2G_OUT, JSON.stringify(fixture().draft, null, 2), );