32 lines
14 KiB
JavaScript
32 lines
14 KiB
JavaScript
import test from 'node:test';import assert from 'node:assert/strict';import fs from 'node:fs';
|
|
import {recordSales,saveSalesBook} from '../../resources/js/lib/iva-operations.ts';
|
|
import {recordAcquisitions,saveAcquisitionBook,acquisitionBook} from '../../resources/js/lib/iva-acquisitions.ts';
|
|
import {closureBook,closureSources,newClosure,recordClosure,saveClosureBook,adoptPreviousCredit,newClosureCredit,closureCurrent} from '../../resources/js/lib/iva-closure.ts';
|
|
import {substitutionBook,substitutionCurrent} from '../../resources/js/lib/iva-substitution.ts';
|
|
import {refundBook,refundSources,newRefund,recordRefund,saveRefundBook,refundCurrent,refundReportRows} from '../../resources/js/lib/iva-refund.ts';
|
|
const seed=JSON.parse(fs.readFileSync(new URL('./fixtures/iva-l3d-preserved.json',import.meta.url),'utf8')),older=JSON.parse(fs.readFileSync(new URL('./fixtures/iva-l3ab-preserved.json',import.meta.url),'utf8'));
|
|
function fixture(){const d=structuredClone(seed),base=JSON.parse(older.modelo_a_snapshot),s=JSON.parse(d.modelo_a_snapshot);for(const[k,v]of Object.entries(base))if(k!=='version')s[k]={...v,...s[k]};d.modelo_a_snapshot=JSON.stringify(s);d.context_opportunity='opportunity:374';
|
|
const sales=structuredClone(s.salesOperations['opportunity:373']);d.modelo_a_snapshot=saveSalesBook(d,recordSales(d,sales));const b=structuredClone(s.books['opportunity:373']);b.rows[0].net='5001000';b.rows[0].vat='800160';b.rows[0].gross='5801160';delete b.result;delete b.history;d.modelo_a_snapshot=saveAcquisitionBook(d,recordAcquisitions(d,b,'2026-10-25T13:00:00Z'));
|
|
const row={...newClosure('refund-Jan',closureSources(d).find(s=>s.period==='2026-01'),'SYNTHETIC QA entidade374'),firstDeclaration:'yes',filing:'timely',submissionDate:'2026-02-15',filingRef:'SYNTHETIC QA prazo declarado',creditsComplete:'yes',interest:'none',interestRef:'SYNTHETIC QA sem juros',destination:'refund',refundAmount:'600000'};
|
|
const ab=recordClosure(d,{version:1,rows:[row]},'2026-10-25T13:01:00Z');d.modelo_a_snapshot=saveClosureBook(d,ab);assert.equal(ab.result.rows[0].status,'determined',ab.result.rows[0].reason);
|
|
const input={...newRefund('refund1',refundSources(d)[0]),ground:'high_credit',originPeriod:'2026-01',entitlementDate:'2026-01-10',groundRef:'SYNTHETIC QA extractos/origem800000 e créditos mais antigos',factsConfirmed:'yes',oldestFirst:'yes',requestReference:'SYNTHETIC QA pedidoR1',requestDate:'2026-02-15',stage:'submitted',decision:'partial',decisionReference:'SYNTHETIC QA decisãoD1',decisionDate:'2026-03-01',decisionIssuer:'SYNTHETIC QA entidade emitente declarada',approvedAmount:'550000',receipts:[{id:'receipt1',reference:'SYNTHETIC QA recebimentoR1a',date:'2026-03-05',amount:'200000'},{id:'receipt2',reference:'SYNTHETIC QA recebimentoR1b',date:'2026-03-10',amount:'250000'}]};return{d,b:{version:1,rows:[input]}};
|
|
}
|
|
const n=v=>v&&`${v.numerator}/${v.denominator}`;
|
|
test('25 is allocated once between26 and27;requested,approved,received and waiting remain distinct',()=>{const{d,b}=fixture(),ab=closureBook(d).result.rows[0];assert.equal(n(ab.fields['25']),'800000/1');assert.equal(n(ab.fields['26']),'200000/1');assert.equal(n(ab.fields['27']),'600000/1');const r=recordRefund(d,b).result.rows[0];assert.equal(r.status,'determined',r.reason);assert.equal(r.eligibility,'compatible');assert.equal(n(r.requested),'600000/1');assert.equal(n(r.reported),'200000/1');assert.equal(n(r.approved),'550000/1');assert.equal(n(r.received),'450000/1');assert.equal(n(r.awaitingReceipt),'100000/1');assert.equal(n(r.notGranted),'50000/1');});
|
|
test('preparation,presented pending,approval and rejection do not automatically create receipt or release credit',()=>{const{d,b}=fixture();for(const stage of ['preparation','submitted']){const v=structuredClone(b);Object.assign(v.rows[0],{stage,decision:'pending',receipts:[]});const r=recordRefund(d,v).result.rows[0];assert.equal(r.status,'determined');assert.equal(r.approved,null);assert.equal(r.received,null);}
|
|
const full=structuredClone(b);Object.assign(full.rows[0],{decision:'approved',approvedAmount:'600000',receipts:[]});let r=recordRefund(d,full).result.rows[0];assert.equal(n(r.received),'0/1');assert.equal(n(r.awaitingReceipt),'600000/1');const rejected=structuredClone(b);Object.assign(rejected.rows[0],{decision:'rejected',approvedAmount:'0',receipts:[]});r=recordRefund(d,rejected).result.rows[0];assert.equal(r.status,'determined');assert.equal(n(r.notGranted),'600000/1');assert.equal(n(closureBook(d).result.rows[0].fields['26']),'200000/1');});
|
|
test('source,request and decision details are required;unknown never becomes entitlement or cash',()=>{const{d,b}=fixture();for(const patch of [{source:null},{requestReference:''},{requestDate:''},{decisionReference:''},{decisionDate:'2026-02-01'},{decisionIssuer:''},{approvedAmount:''},{stage:'preparation'}]){const v=structuredClone(b);Object.assign(v.rows[0],patch);assert.equal(recordRefund(d,v).result.rows[0].status,'indeterminate',JSON.stringify(patch));}
|
|
const forged=structuredClone(b);forged.rows[0].source.requested={numerator:'700000',denominator:'1'};assert.equal(recordRefund(d,forged).result.rows[0].requested,null);});
|
|
test('a documented external decision is recorded without turning an unimplemented/unknown basis into a new approval gate',()=>{const{d,b}=fixture();for(const patch of [{ground:''},{factsConfirmed:''},{oldestFirst:''},{entitlementDate:''},{groundRef:''}]){const v=structuredClone(b);Object.assign(v.rows[0],patch);const r=recordRefund(d,v).result.rows[0];assert.equal(r.status,'determined',r.reason);assert.equal(r.eligibility,'not_demonstrated');assert.equal(n(r.approved),'550000/1');assert.equal(n(r.received),'450000/1');v.rows[0].decision='pending';v.rows[0].receipts=[];const pending=recordRefund(d,v).result.rows[0];assert.equal(pending.status,'indeterminate');assert.equal(pending.approved,null);}});
|
|
test('current four-month/high-credit conditions and ten-year limit are applied without obsolete50percent rule',()=>{const{d,b}=fixture();const v=structuredClone(b);Object.assign(v.rows[0],{ground:'four_months',originPeriod:'2025-09',entitlementDate:'2025-09-10'});assert.equal(recordRefund(d,v).result.rows[0].eligibility,'compatible');v.rows[0].originPeriod='2025-10';v.rows[0].entitlementDate='2025-10-10';assert.equal(recordRefund(d,v).result.rows[0].eligibility,'incompatible');v.rows[0].originPeriod='2015-01';v.rows[0].entitlementDate='2015-01-10';assert.equal(recordRefund(d,v).result.rows[0].eligibility,'incompatible');
|
|
const cessation=structuredClone(b);Object.assign(cessation.rows[0],{ground:'cessation',cessationDate:'2026-01-31'});assert.equal(recordRefund(d,cessation).result.rows[0].eligibility,'compatible');cessation.rows[0].cessationDate='2026-03-01';assert.equal(recordRefund(d,cessation).result.rows[0].eligibility,'incompatible');});
|
|
test('thresholds are strictly greater;fractional credit remains exact after allocation',()=>{for(const [vat,request,ground,expected]of [['500160','100000','high_credit','incompatible'],['100160','50000','four_months','incompatible'],['500161','100000','high_credit','compatible']]){const{d,b}=fixture(),acq=acquisitionBook(d);acq.rows[0].vat=vat;acq.rows[0].gross=String(Number(acq.rows[0].net)+Number(vat));d.modelo_a_snapshot=saveAcquisitionBook(d,recordAcquisitions(d,acq));const ab=closureBook(d);ab.rows[0].source=closureSources(d).filter(s=>s.period==='2026-01').at(-1);ab.rows[0].refundAmount=request;d.modelo_a_snapshot=saveClosureBook(d,recordClosure(d,ab));Object.assign(b.rows[0],{source:refundSources(d)[0],ground,originPeriod:'2025-09',entitlementDate:'2025-09-10',stage:'preparation',decision:'pending',receipts:[]});assert.equal(recordRefund(d,b).result.rows[0].eligibility,expected);}
|
|
const{d}=fixture(),acq=acquisitionBook(d);acq.rows[0].vat='800161';acq.rows[0].gross='5801161';acq.rows[0].d.numerator='1';acq.rows[0].d.denominator='3';d.modelo_a_snapshot=saveAcquisitionBook(d,recordAcquisitions(d,acq));const ab=closureBook(d);ab.rows[0].source=closureSources(d).filter(s=>s.period==='2026-01').at(-1);ab.rows[0].refundAmount='200000';const result=recordClosure(d,ab);assert.equal(n(result.result.rows[0].fields['25']),'799681/3');assert.equal(n(result.result.rows[0].fields['26']),'199681/3');assert.equal(n(result.result.rows[0].fields['27']),'200000/1');});
|
|
test('request/receipt duplication,over-receipt and decisions beyond request are refused',()=>{const{d,b}=fixture();const copy=structuredClone(b);copy.rows.push({...structuredClone(copy.rows[0]),id:'copy'});assert.ok(recordRefund(d,copy).result.rows.every(r=>r.requested===null));for(const patch of [{approvedAmount:'600001'},{decision:'approved',approvedAmount:'550000'},{decision:'rejected',approvedAmount:'1'},{decision:'pending'}]){const v=structuredClone(b);Object.assign(v.rows[0],patch);assert.equal(recordRefund(d,v).result.rows[0].status,'indeterminate');}const receipt=structuredClone(b);receipt.rows[0].receipts.push({...receipt.rows[0].receipts[0],id:'copy'});assert.equal(recordRefund(d,receipt).result.rows[0].received,null);const over=structuredClone(b);over.rows[0].receipts[0].amount='400000';assert.equal(recordRefund(d,over).result.rows[0].status,'indeterminate');});
|
|
test('submitted refund locks the allocation even after current preparation is removed;histories are not erased',()=>{const{d,b}=fixture();const book=recordRefund(d,b);d.modelo_a_snapshot=saveRefundBook(d,book);const ab=closureBook(d);ab.rows[0].destination='report';assert.match(recordClosure(d,ab).result.rows[0].reason,/Crédito ligado/);const removed=recordRefund(d,{...book,rows:[]});d.modelo_a_snapshot=saveRefundBook(d,removed);assert.match(recordClosure(d,ab).result.rows[0].reason,/Crédito ligado/);assert.ok(removed.history.length);});
|
|
test('only residual26 travels to next month;old whole-credit carry requires explicit adoption',()=>{const{d,b}=fixture();let ab=closureBook(d);const full=structuredClone(ab);full.rows[0].destination='report';const before=recordClosure(d,full);const feb={...newClosure('refund-Feb',closureSources(d).find(s=>s.period==='2026-02'),'SYNTHETIC QA entidade374'),firstDeclaration:'yes',filing:'timely',submissionDate:'2026-03-15',filingRef:'SYNTHETIC QA prazo',creditsComplete:'yes',interest:'none',interestRef:'SYNTHETIC QA sem juros',destination:'report',credits:[adoptPreviousCredit(newClosureCredit('carry','previous'),before.result.rows[0])]};ab=recordClosure(d,{...ab,rows:[...ab.rows,feb]});assert.equal(ab.result.rows[1].status,'indeterminate');d.modelo_a_snapshot=saveClosureBook(d,ab);assert.deepEqual(refundSources(d),[]);ab.rows[1].credits[0]=adoptPreviousCredit(ab.rows[1].credits[0],ab.result.rows[0]);ab=recordClosure(d,ab);assert.equal(n(ab.result.rows[1].fields['19']),'200000/1');assert.equal(n(ab.result.rows[1].fields['26']),'199920/1');d.modelo_a_snapshot=saveClosureBook(d,ab);assert.equal(recordRefund(d,b).result.rows[0].status,'determined');});
|
|
test('effective carry already used in a later declaration cannot be reallocated retroactively for a refund',()=>{const{d}=fixture(),acq=acquisitionBook(d);acq.rows[0].origin='effective';d.modelo_a_snapshot=saveAcquisitionBook(d,recordAcquisitions(d,acq));let ab=closureBook(d);Object.assign(ab.rows[0],{origin:'effective',source:closureSources(d).filter(s=>s.period==='2026-01').at(-1),destination:'report'});ab=recordClosure(d,ab);assert.equal(ab.result.rows[0].status,'determined');const feb={...structuredClone(ab.rows[0]),id:'actual-Feb',period:'2026-02',source:closureSources(d).filter(s=>s.period==='2026-02').at(-1),submissionDate:'2026-03-15',credits:[adoptPreviousCredit(newClosureCredit('actual-carry','previous'),ab.result.rows[0])]};ab=recordClosure(d,{...ab,rows:[...ab.rows,feb]});assert.equal(ab.result.rows[1].status,'determined');d.modelo_a_snapshot=saveClosureBook(d,ab);ab.rows[0].destination='refund';ab.rows[0].refundAmount='600000';const changed=recordClosure(d,ab);assert.match(changed.result.rows[0].reason,/Reporte efectivo já utilizado/);assert.equal(changed.result.rows[0].status,'indeterminate');});
|
|
test('unsubmitted allocation may be changed explicitly,but old reported credit cannot fund the old request',()=>{const{d,b}=fixture();b.rows[0].stage='preparation';b.rows[0].decision='pending';b.rows[0].receipts=[];let book=recordRefund(d,b);d.modelo_a_snapshot=saveRefundBook(d,book);const ab=closureBook(d);ab.rows[0].destination='report';d.modelo_a_snapshot=saveClosureBook(d,recordClosure(d,ab));assert.equal(refundCurrent(d,book),false);assert.equal(recordRefund(d,book).result.rows[0].requested,null);});
|
|
test('save/recover preserves all prior L1/L2/AB/C maps and frozen report;recalculation appends history',()=>{const{d,b}=fixture(),before=JSON.parse(d.modelo_a_snapshot);let book=recordRefund(d,b);d.modelo_a_snapshot=saveRefundBook(d,book);const after=JSON.parse(d.modelo_a_snapshot);for(const[k,v]of Object.entries(before))assert.deepEqual(after[k],v,k);assert.ok(refundCurrent(d,refundBook(d)));const cDraft={...d,context_opportunity:'opportunity:373'};assert.ok(substitutionCurrent(cDraft,substitutionBook(cDraft)));assert.ok(closureCurrent(cDraft,closureBook(cDraft)));book.rows[0].requestReference='deliberate edit';assert.equal(refundCurrent(d,book),false);d.modelo_a_snapshot=saveRefundBook(d,book);assert.doesNotMatch(refundReportRows(d).map(r=>r.join(' ')).join(' '),/deliberate edit/);const prior=structuredClone(book.result);book=recordRefund(d,book);assert.deepEqual(book.history.at(-1),prior);});
|
|
test('export synthetic frozen refund reconciliation for guardedAPI and actual component proof',()=>{const{d,b}=fixture();const first=recordRefund(d,b,'2026-10-25T13:02:00Z'),book=recordRefund(d,first,'2026-10-25T13:03:00Z');d.modelo_a_snapshot=saveRefundBook(d,book);assert.ok(refundCurrent(d,book));if(process.env.IVA_L3D_CALCULATED)fs.writeFileSync(process.env.IVA_L3D_CALCULATED,JSON.stringify(d));});
|