55 lines
8.4 KiB
JavaScript
55 lines
8.4 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import {newIrpcRow,emptyIrpcOrdinaryFacts,recordIrpc,saveIrpcBook,irpcBook,irpcCurrent,irpcReportRows} from '../../resources/js/lib/irpc-withholdings.ts';
|
|
import {newMrftaInput,prepareMrfta,recordMrfta,mrftaCurrent,mrftaHtml} from '../../resources/js/lib/irpc-mrfta.ts';
|
|
import {createProjectDossierReport} from '../../resources/js/lib/project-dossier-report.ts';
|
|
const draft=()=>JSON.parse(fs.readFileSync(new URL('./fixtures/irpc-grouped-preserved.json',import.meta.url),'utf8'));
|
|
const row=(id='principal')=>({...newIrpcRow(id),counterparty:'QA contraparte',documentRef:'QA-'+id,base:'100000',kind:'royalty',recipientIrpc:'yes',payerIrpc:'yes',residence:'nonresident',permanentEstablishment:'no',mozambique:'yes',qualificationRef:'QA contrato e residência',specialRelief:'none',eventsComplete:'yes',events:[{kind:'cost',date:'2026-10-02',reference:'QA custo'}],settlement:'complete',settlementRef:'QA reconciliação',allocations:[{paymentId:'p',amount:'3000'}],ordinary:{...emptyIrpcOrdinaryFacts(),nonDigital:'yes',reference:'QA royalties não digitais'}});
|
|
const input=()=>({...newMrftaInput(),period:'2026-10',declaration:'initial',timing:'late',nuit:'999999999',name:'QA sujeito passivo',commercialName:'QA',taxArea:'QA área fiscal',address:'QA domicílio',contact:'qa@example.invalid',completeness:'yes',completenessRef:'QA conferência do período e livros',otherRubrics:'none',otherRubricsRef:'QA ausência documentada de IRPS/tributação autónoma/outros valores',codes:[{rowId:'principal',cer:'999',reference:'QA CER fictício, apenas teste'}],interest:{...newMrftaInput().interest,mode:'assessment',period:'2026-10',amount:'500',date:'2026-11-25',reference:'QA liquidação de juros compensatórios'}});
|
|
function book(rows=[row()]){return {...recordIrpc(draft(),{version:1,rows,payments:[{id:'p',kind:'remittance',reference:'QA entrega IRPC',date:'2026-11-15',amount:'3000',currency:'MZN'}]}),mrfta:input()};}
|
|
|
|
test('MRFTA fills one period with principal, documented interest, payment and balance separately',()=>{
|
|
const b=book(),m=prepareMrfta(b,true);assert.deepEqual(m.pending,[]);assert.equal(m.principal.numerator,'20000');assert.equal(m.paid.numerator,'3000');assert.equal(m.interest.numerator,'500');assert.equal(m.total.numerator,'17500');assert.equal(m.lines[0].rate,20);assert.equal(m.lines[0].cer,'999');assert.match(m.interestReason,/transcritos/);
|
|
});
|
|
test('unknown MAIBOR2026 never becomes zero or MIMO; independent principal remains calculated',()=>{
|
|
const b=book();b.mrfta.interest.mode='';const m=prepareMrfta(b,true);assert.equal(m.interest,null);assert.equal(m.total,null);assert.equal(m.principal.numerator,'20000');assert.equal(b.result.rows[0].status,'calculated');assert.match(m.pending.join(' '),/MAIBOR2026/);
|
|
});
|
|
test('insufficient CER or contradictory rate per rubric blocks total without losing rows',()=>{
|
|
const b=book();b.mrfta.codes=[];let m=prepareMrfta(b,true);assert.equal(m.total,null);assert.equal(m.lines.length,1);assert.match(m.pending.join(' '),/CER/);
|
|
const other=structuredClone(b.result.rows[0]);other.id='other';other.rate=10;other.tax={numerator:'10000',denominator:'1'};b.result.rows.push(other);b.result.inputs.push({...row('other'),allocations:[]});b.mrfta.codes=[{rowId:'principal',cer:'999',reference:'QA ref'},{rowId:'other',cer:'999',reference:'QA ref'}];m=prepareMrfta(b,true);assert.equal(m.lines.length,2);assert.match(m.pending.join(' '),/taxas ou CER distintos/);assert.equal(m.total,null);
|
|
});
|
|
test('other months,suffered withholdings and documented dispensas stay outside payable rows',()=>{
|
|
const b=book();for(const[id,patch,original]of [['other-month',{period:'2026-11'},{}],['suffered',{}, {direction:'received'}],['dispensed',{treatment:'dispensed',tax:{numerator:'0',denominator:'1'}},{}]]){b.result.rows.push({...b.result.rows[0],id,...patch});b.result.inputs.push({...row(id),...original});}
|
|
const m=prepareMrfta(b,true);assert.equal(m.lines.length,1);assert.equal(m.excluded.length,3);assert.equal(m.principal.numerator,'20000');assert.equal(m.total.numerator,'17500');
|
|
});
|
|
test('unsupported digital and indeterminate cases retain explicit gaps without guessed rubric',()=>{
|
|
const b=book();b.result.inputs[0].kind='digital_services';let m=prepareMrfta(b,true);assert.equal(m.lines.length,0);assert.equal(m.total,null);assert.match(m.pending.join(' '),/digital/);b.result.inputs[0].kind='royalty';b.result.rows[0].status='indeterminate';b.result.rows[0].tax=null;m=prepareMrfta(b,true);assert.equal(m.total,null);assert.match(m.pending.join(' '),/indeterminado/);
|
|
});
|
|
test('timely remittance only yields zero interest with complete proof and explicit applicable deadline',()=>{
|
|
const r=row();r.base='15000';const b=book([r]);b.mrfta.interest={...b.mrfta.interest,mode:'timely',period:'2026-10',dueDate:'2026-11-20',dueReference:'QA prazo documentado e calendário'};assert.equal(prepareMrfta(b,true).interest.numerator,'0');assert.equal(prepareMrfta(b,true).total.numerator,'0');
|
|
b.mrfta.interest.dueDate='2026-11-10';assert.equal(prepareMrfta(b,true).interest,null);b.mrfta.interest.dueDate='2026-11-20';b.result.rows[0].settled=null;assert.equal(prepareMrfta(b,true).interest,null);
|
|
});
|
|
test('assessment cannot leak into another period or accept negative amount and missing reference',()=>{
|
|
const b=book();for(const patch of [{period:'2026-11'},{amount:'-10'},{reference:''},{date:'2026-02-30'}]){const copy=structuredClone(b);Object.assign(copy.mrfta.interest,patch);assert.equal(prepareMrfta(copy,true).interest,null);assert.equal(prepareMrfta(copy,true).total,null);}
|
|
});
|
|
test('MRFTA save,recover,history and dossier preserve existing tax maps and freeze evidence',()=>{
|
|
const d=draft(),prior=irpcBook(d),original=JSON.parse(d.modelo_a_snapshot);let b=book();b.history=[prior.result];b=recordMrfta(b,true);d.modelo_a_snapshot=saveIrpcBook(d,b);const recovered=JSON.parse(JSON.stringify(d)),saved=irpcBook(recovered);assert.deepEqual(saved,b);assert.equal(irpcCurrent(recovered,saved),true);assert.equal(mrftaCurrent(saved,true),true);
|
|
for(const[k,v]of Object.entries(original))if(k!=='irpcWithholdings')assert.deepEqual(JSON.parse(recovered.modelo_a_snapshot)[k],v,k);
|
|
const report=irpcReportRows(recovered);assert.match(JSON.stringify(report),/17500/);assert.match(JSON.stringify(createProjectDossierReport(recovered,{selectedSections:new Set(['finance'])})),/MRFTA/);
|
|
assert.match(mrftaHtml(saved,true),/Quadro7/);assert.match(mrftaHtml(saved,true),/QA CER fictício/);const before=structuredClone(saved.mrftaResult);saved.mrfta.interest.amount='600';assert.equal(mrftaCurrent(saved,true),false);assert.throws(()=>mrftaHtml(saved,true));const next=recordMrfta(saved,true);assert.deepEqual(next.mrftaHistory.at(-1),before);assert.equal(next.mrftaResult.model.total.numerator,'17600');
|
|
fs.mkdirSync('outputs/irpc-mrfta',{recursive:true});fs.writeFileSync('outputs/irpc-mrfta/calculated.json',JSON.stringify(recovered));fs.writeFileSync('outputs/irpc-mrfta/preparation.html',mrftaHtml(b,true));
|
|
});
|
|
test('malformed persisted MRFTA rejects recovery without replacing originals',()=>{
|
|
const b=book();b.mrfta.codes.push({...b.mrfta.codes[0]});assert.throws(()=>saveIrpcBook(draft(),b));b.mrfta.codes=[];b.mrfta.interest.amount=5;assert.throws(()=>saveIrpcBook(draft(),b));
|
|
});
|
|
test('export escapes identities,documents and assessment references',()=>{
|
|
const b=book();b.mrfta.name='<script>QA</script>';b.mrfta.interest.reference='<img src=x onerror=QA>';const html=mrftaHtml(recordMrfta(b,true),true);assert.ok(!html.includes('<script>QA'));assert.ok(html.includes('<script>'));assert.ok(html.includes('<img'));
|
|
});
|
|
test('other tax rubrics cannot silently become zero and partial rows remain available',()=>{
|
|
const b=book();b.mrfta.otherRubrics='pending';const m=prepareMrfta(b,true);assert.equal(m.total,null);assert.equal(m.principal.numerator,'20000');assert.match(m.pending.join(' '),/IRPS,tributação autónoma/);assert.throws(()=>mrftaHtml(recordMrfta(b,false),false));
|
|
});
|
|
test('same rubric rate and documented CER aggregate without losing document references',()=>{
|
|
const other={...row('other'),allocations:[],settlement:'none'},b=book([row(),other]);b.mrfta.codes.push({rowId:'other',cer:'999',reference:'QA segundo documento de CER fictício'});const m=prepareMrfta(b,true);assert.equal(m.lines.length,1);assert.equal(m.lines[0].base.numerator,'200000');assert.equal(m.principal.numerator,'40000');assert.equal(m.total.numerator,'37500');assert.equal(m.lines[0].references.length,2);assert.equal(m.lines[0].documents.length,2);
|
|
});
|