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'; import { productionMinimumTermEnd, productionReturnEffective, productionOptionCheck, } from '../../resources/js/lib/iva-production-option.ts'; const ids = [ 'production_agriculture', 'production_forestry', 'production_forestry_opted', 'production_livestock', 'production_livestock_opted', 'production_fishing', 'production_fishing_opted', ]; function seed() { return JSON.parse( fs.readFileSync( new URL('./fixtures/iva-listed.json', import.meta.url), 'utf8', ), ); } function fixture() { const draft = seed(); draft.context_opportunity = 'opportunity:318'; draft.project_title = 'SYNTHETIC QA — G2I social'; let book = { currency: 'MZN', rows: ids.map((id, i) => ({ ...newSalesOperation(`g2i-${i}`, '2026-10', 'MZN'), description: `SYNTHETIC QA ${id}`, establishment: 'SYNTHETIC QA equipamento Manica', amount: id.endsWith('_opted') && i % 3 === 2 ? '116' : '100', basis: i % 3 === 2 ? 'included' : 'net', treatment: id, ...(id.endsWith('_opted') ? { taxOption: { mode: 'annual', declarationDate: '2024-12-15', startupDate: '', returnStatus: 'none', returnDeclarationDate: '', operationDate: '2026-10-15', }, } : {}), 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-10', complete: true }], }; book = recordSales(draft, book); draft.modelo_a_snapshot = saveSalesBook(draft, book); return { draft, book }; } void test('G2I four production branches with seven exemption or opted 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, row.treatment.endsWith('_opted') ? '01' : row.treatment === 'production_agriculture' ? '05' : '07', ); assert.equal(r.net, '100.00'); assert.equal( r.vat, row.treatment.endsWith('_opted') ? '16.00' : '0.00', ); assert.equal( r.gross, row.treatment.endsWith('_opted') ? '116.00' : '100.00', ); assert.match(r.article, /9\(7\)/); 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, treatment: 'listed_banana' }, 'MZN', ).vat, null, ); assert.equal( evaluateSalesOperation({ ...row, period: '2025-10' }, 'MZN').vat, null, ); assert.equal(evaluateSalesOperation(row, 'USD').vat, null); } assert.equal(book.result.periods[0].fields['07'], '300.00'); assert.equal(book.result.periods[0].fields['05'], '100.00'); assert.equal(book.result.periods[0].fields['01'], '300.00'); assert.equal(book.result.periods[0].fields['02'], '48.00'); }); void test('new production context round-trips and reports only its operations, preserving nine 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', 'opportunity:316', 'opportunity:317', ]) { 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, '48.00', 'opportunity:318', 'Manica', '2026-10', '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', 'public_security', 'listed_maize', ]) assert.equal(model.includes(token), false); assert.equal(book.result.rows.length, 7); assert.equal(draft.currency, original.currency); assert.deepEqual(draft.selection_labels, original.selection_labels); }); void test('article11 distinguishes annual effects, startup, five-year minimum and effective return without automatic expiry', () => { const { draft, book } = fixture(), row = book.rows.find( (r) => r.treatment === 'production_forestry_opted', ); const check = (patch) => evaluateSalesOperation( { ...row, taxOption: { ...row.taxOption, ...('returnDeclarationDate' in patch ? { returnStatus: patch.returnDeclarationDate ? 'declared' : 'none', } : {}), ...patch, }, }, 'MZN', ); assert.equal(check({ declarationDate: '2026-01-01' }).vat, null); assert.match(check({ declarationDate: '2026-01-01' }).reason, /2027-01-01/); assert.equal( check({ mode: 'startup', declarationDate: '2026-09-20', startupDate: '2026-10-01', }).vat, '16.00', ); assert.equal( check({ mode: 'startup', declarationDate: '2026-10-02', startupDate: '2026-10-01', }).vat, null, ); assert.equal( check({ declarationDate: '2020-01-01', returnDeclarationDate: '2025-12-31', }).vat, null, ); assert.equal( check({ declarationDate: '2019-12-31', returnDeclarationDate: '2025-01-01', }).vat, null, ); assert.match( check({ declarationDate: '2019-12-31', returnDeclarationDate: '2025-01-01', }).reason, /2026-01-01/, ); assert.equal( check({ declarationDate: '2020-01-01', returnDeclarationDate: '2026-01-01', }).vat, '16.00', ); assert.equal( check({ declarationDate: '2019-12-31', returnDeclarationDate: '' }).vat, '16.00', ); for (const patch of [ { mode: '' }, { returnStatus: '' }, { returnStatus: 'declared', returnDeclarationDate: '' }, { returnStatus: 'none', returnDeclarationDate: '2026-01-01' }, { declarationDate: '' }, { declarationDate: '2026-99-99' }, { declarationDate: '2024-02-30' }, { operationDate: '2026-11-01' }, { operationDate: '' }, { returnDeclarationDate: 'not-a-date' }, ]) assert.equal(check(patch).vat, null); assert.equal( check({ mode: 'startup', declarationDate: '2020-02-28', startupDate: '2020-02-29', returnDeclarationDate: '2025-02-28', }).vat, null, ); assert.match( check({ mode: 'startup', declarationDate: '2020-02-28', startupDate: '2020-02-29', returnDeclarationDate: '2025-02-28', }).reason, /regresso.*2026-01-01/, ); assert.match( check({ mode: 'startup', declarationDate: '2020-02-28', startupDate: '2020-02-29', returnDeclarationDate: '2025-03-01', }).reason, /declaração tardia/, ); assert.equal( check({ mode: 'startup', declarationDate: '2020-02-28', startupDate: '2020-02-29', returnDeclarationDate: '2026-03-01', }).vat, null, ); assert.equal( operationTreatments.some( (t) => t.id === 'production_agriculture_opted', ), false, ); const changed = { ...book, rows: book.rows.map((r) => r.id === row.id ? { ...r, taxOption: { ...r.taxOption, declarationDate: '2025-12-31', }, } : r, ), }; assert.equal(salesCurrent(draft, changed), false); assert.deepEqual(changed.result, book.result); const report = JSON.stringify( createProjectDossierReport(draft, { selectedSections: new Set(['finance']), }), ); for (const token of [ '2024-12-15', '2025-01-01', '2026-10-15', 'ausência de declaração de regresso', ]) assert.ok(report.includes(token), token); }); void test('Civil Code 279(c): calendar term at 24:00 is separate from article11 return effects', () => { assert.equal(productionMinimumTermEnd('2024-02-29'), '2029-02-28'); assert.equal(productionMinimumTermEnd('2023-03-01'), '2028-03-01'); assert.equal(productionMinimumTermEnd('2096-02-29'), '2101-02-28'); assert.equal(productionMinimumTermEnd('2024-12-31'), '2029-12-31'); assert.equal(productionReturnEffective('2029-02-28'), '2030-01-01'); const option = { mode: 'startup', declarationDate: '2024-02-28', startupDate: '2024-02-29', returnStatus: 'declared', returnDeclarationDate: '2029-02-28', operationDate: '2029-02-28', }; // Schedule checks only: these dates do not extend the VAT calculator beyond its admitted 2026 period. for (const date of ['2029-02-28', '2029-03-01', '2029-12-31']) { const result = productionOptionCheck( { ...option, operationDate: date }, date.slice(0, 7), ); assert.equal(result.error, undefined); assert.match(result.summary, /24 horas de 2029-02-28/); assert.match(result.summary, /efeitos 2030-01-01/); } for (const date of ['2030-01-01', '2030-01-02']) assert.match( productionOptionCheck( { ...option, operationDate: date }, date.slice(0, 7), ).error, /cessou.*2030-01-01/, ); assert.equal( productionOptionCheck( { ...option, returnDeclarationDate: '2029-02-27' }, '2029-02', ).error, undefined, ); assert.match( productionOptionCheck( { ...option, returnDeclarationDate: '2028-12-31' }, '2029-02', ).error, /antes de terminado o prazo mínimo/, ); assert.match( productionOptionCheck( { ...option, returnDeclarationDate: '2029-03-01' }, '2029-02', ).error, /declaração tardia/, ); const { draft, book } = fixture(); const row = book.rows.find( (r) => r.treatment === 'production_forestry_opted', ); const leap = { ...row, taxOption: { mode: 'startup', declarationDate: '2020-02-28', startupDate: '2020-02-29', returnStatus: 'declared', returnDeclarationDate: '2025-02-28', operationDate: '2026-10-15', }, }; assert.match( evaluateSalesOperation(leap, 'MZN').reason, /regresso.*2026-01-01/, ); const report = JSON.stringify( createProjectDossierReport(draft, { selectedSections: new Set(['finance']), }), ); assert.ok(report.includes('24 horas de 2030-01-01')); assert.ok(report.includes('production-option/2')); }); if (process.env.IVA_G2I_OUT) fs.writeFileSync( process.env.IVA_G2I_OUT, JSON.stringify(fixture().draft, null, 2), );