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};} 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'); }); 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));