125 lines
24 KiB
TypeScript
125 lines
24 KiB
TypeScript
import type {AcquisitionCalculation} from './iva-acquisitions.ts';
|
||
import {addFractions,decimalFraction,fraction,subtractFractions,positiveFraction,fractionLabel,fractionPresentation,type FiscalFraction} from './fiscal-rational.ts';
|
||
|
||
export const closureVersion='iva-closure-ab/1';
|
||
export const closureSource='L3-A/B: CIVA21(2/5),Lei13/2016 PDF15/p1426(273),23(1),PDF16/p1426(274);RCIVA12/15,Decreto7/2008 PDF3/p101;ModeloA/VAT Return9c0b99a8,p1–2,quadros8/10/11. Crédito anterior/comunicado19/20;22=max(17−19−20,0);25=max(18+19+20−17,0);24=22+23;reporte integral26=25 quando expressamente escolhido. Sem substituição21,reembolso27 ou restituição13(2). Cabeçalho remete18/19 mas caixas são19/20;divergência conservada,distinta deP00105/06.';
|
||
type Choice=''|'yes'|'no';
|
||
export type ClosureSource={revision:number;calculatedAt:string;fingerprint:string;opportunity:string;currency:string;period:string;location:string;projected:boolean;debit:FiscalFraction|null;credit:FiscalFraction|null};
|
||
export type ClosureCredit={id:string;kind:'opening'|'communication'|'previous';period:string;reference:string;date:string;amount:string;previousId:string;carryKey:string};
|
||
export type ClosurePayment={id:string;reference:string;date:string;amount:string};
|
||
export type ClosureInput={id:string;period:string;entityRef:string;origin:'projected'|'effective';source:ClosureSource|null;firstDeclaration:Choice;filing:''|'timely'|'late';submissionDate:string;filingRef:string;creditsComplete:Choice;credits:ClosureCredit[];interest:''|'none'|'documented';interestAmount:string;interestRef:string;destination:''|'report'|'refund';refundAmount?:string;payments:ClosurePayment[]};
|
||
export type ClosureResult={id:string;period:string;entityRef:string;origin:ClosureInput['origin'];status:'determined'|'indeterminate';reason:string;inputKey:string;financialKey:string;carryKey:string;fields:Record<string,FiscalFraction|null>;paid:FiscalFraction|null;outstanding:FiscalFraction|null;overpayment:FiscalFraction|null;source:string};
|
||
export type ClosureBook={version:1;rows:ClosureInput[];result?:{calculatedAt:string;key:string;inputs:ClosureInput[];rows:ClosureResult[]};history?:Array<NonNullable<ClosureBook['result']>>};
|
||
const scope=(d:Record<string,unknown>)=>String(d.context_opportunity||'general');
|
||
const zero=()=>fraction(0n);
|
||
export function closureFingerprint(v:unknown):string{const raw=JSON.stringify(v);let h=14695981039346656037n;for(let i=0;i<raw.length;i++)h=BigInt.asUintN(64,(h^BigInt(raw.charCodeAt(i)))*1099511628211n);return`${raw.length}:${h.toString(16)}`;}
|
||
function store(d:Record<string,unknown>):Record<string,unknown>{const s=JSON.parse(String(d.modelo_a_snapshot||'{"version":1,"books":{}}'));if(s.version!==1||!s.books)throw Error('Livros fiscais não recuperáveis;originais preservados.');return s;}
|
||
function maps(d:Record<string,unknown>):Record<string,ClosureBook>{const v=store(d).ivaClosures;if(v===undefined)return{};if(!v||typeof v!=='object'||Array.isArray(v))throw Error('Mapa de fechos não recuperável;originais preservados.');return v as Record<string,ClosureBook>;}
|
||
function validate(b:ClosureBook):void{
|
||
if(!b||b.version!==1||!Array.isArray(b.rows)||b.history!==undefined&&!Array.isArray(b.history)||b.result!==undefined&&(!Array.isArray(b.result.rows)||!Array.isArray(b.result.inputs)||typeof b.result.key!=='string'))throw Error('FechoL3 não recuperável;originais preservados.');
|
||
if(b.result?.rows.some(r=>!r||typeof r.id!=='string'||!r.fields||typeof r.fields!=='object'||!['determined','indeterminate'].includes(r.status)||Object.values(r.fields).some(v=>v!==null&&(!v||!/^\d{1,80}$/.test(v.numerator)||!/^\d{1,80}$/.test(v.denominator)||BigInt(v.denominator)===0n))))throw Error('ResultadoL3 não recuperável;originais preservados.');
|
||
for(const r of b.rows){if(!r||!['id','period','entityRef','submissionDate','filingRef','interestAmount','interestRef'].every(k=>typeof r[k as keyof ClosureInput]==='string')||!['projected','effective'].includes(r.origin)||!['','yes','no'].includes(r.firstDeclaration)||!['','yes','no'].includes(r.creditsComplete)||!['','timely','late'].includes(r.filing)||!['','none','documented'].includes(r.interest)||!['','report','refund'].includes(r.destination)||r.refundAmount!==undefined&&typeof r.refundAmount!=='string'||!Array.isArray(r.credits)||!Array.isArray(r.payments)||r.credits.some(c=>!c||!['opening','communication','previous'].includes(c.kind)||!['id','period','reference','date','amount','previousId','carryKey'].every(k=>typeof c[k as keyof ClosureCredit]==='string'))||r.payments.some(p=>!p||!['id','reference','date','amount'].every(k=>typeof p[k as keyof ClosurePayment]==='string'))||r.source!==null&&(!r.source||!Number.isInteger(r.source.revision)||!['calculatedAt','fingerprint','opportunity','currency','period','location'].every(k=>typeof r.source![k as keyof ClosureSource]==='string')))throw Error('EntradasL3 não recuperáveis;originais preservados,sem substituir por zero.');}
|
||
}
|
||
export function closureBook(d:Record<string,unknown>):ClosureBook{const b=maps(d)[scope(d)]??{version:1,rows:[]};validate(b);return b;}
|
||
export function saveClosureBook(d:Record<string,unknown>,b:ClosureBook):string{validate(b);const s=store(d);return JSON.stringify({...s,ivaClosures:{...maps(d),[scope(d)]:b}});}
|
||
export function closureSources(d:Record<string,unknown>):ClosureSource[]{const b=(store(d).books as Record<string,{result?:AcquisitionCalculation;history?:AcquisitionCalculation[]}>)[scope(d)];return b?[...(b.history??[]),...(b.result?[b.result]:[])].flatMap(calc=>calc.periods.map(p=>({revision:calc.revision,calculatedAt:calc.calculatedAt,fingerprint:closureFingerprint(calc),opportunity:calc.opportunity,currency:calc.currency,period:p.period,location:calc.location,projected:calc.inputs.length===0||calc.inputs.some(r=>r.origin==='projected'),debit:p.exactFields?.['17']??(p.fields['17']===null?null:decimalFraction(p.fields['17'])),credit:p.exactFields?.['18']??(p.fields['18']===null?null:decimalFraction(p.fields['18']))}))).filter((r,i,a)=>a.findIndex(s=>s.fingerprint===r.fingerprint&&s.period===r.period)===i):[];}
|
||
export function newClosure(id:string,source:ClosureSource|null,entityRef=''):ClosureInput{return{id,period:source?.period??'',entityRef,origin:'projected',source:source?structuredClone(source):null,firstDeclaration:'',filing:'',submissionDate:'',filingRef:'',creditsComplete:'',credits:[],interest:'',interestAmount:'',interestRef:'',destination:'',payments:[]};}
|
||
export function newClosureCredit(id:string,kind:ClosureCredit['kind']):ClosureCredit{return{id,kind,period:'',reference:'',date:'',amount:'',previousId:'',carryKey:''};}
|
||
const priorPeriod=(p:string)=>{const d=new Date(p+'-01T00:00:00Z');d.setUTCMonth(d.getUTCMonth()-1);return d.toISOString().slice(0,7);};
|
||
const validDate=(d:string)=>/^20\d{2}-(0[1-9]|1[0-2])-\d{2}$/.test(d)&&!Number.isNaN(Date.parse(d))&&new Date(d).toISOString().slice(0,10)===d;
|
||
const normalize=(s:string)=>s.trim().toLocaleLowerCase();
|
||
function creditIdentity(r:ClosureInput,c:ClosureCredit){return closureFingerprint([normalize(r.entityRef),r.origin,c.kind==='previous'?'carry':'document',c.kind==='previous'?c.previousId:normalize(c.reference),c.period]);}
|
||
function paymentIdentity(r:ClosureInput,p:ClosurePayment){return closureFingerprint([normalize(r.entityRef),r.origin,normalize(p.reference)]);}
|
||
function inputKey(r:ClosureInput){return closureFingerprint(r);}
|
||
function financialKey(r:ClosureInput){const{payments:_,...rest}=r;return closureFingerprint(rest);}
|
||
function otherUses(d:Record<string,unknown>,b:ClosureBook):ClosureInput[]{return[...b.rows,...Object.entries(maps(d)).filter(([id])=>id!==scope(d)).flatMap(([,book])=>{validate(book);return book.rows.filter(r=>r.origin==='effective');})];}
|
||
function duplicateCredits(d:Record<string,unknown>,b:ClosureBook):Set<string>{const uses=new Map<string,number>();for(const r of otherUses(d,b))for(const c of r.credits){const k=creditIdentity(r,c);uses.set(k,(uses.get(k)??0)+1);}return new Set([...uses].filter(([,n])=>n>1).map(([k])=>k));}
|
||
function substitutionPayments(d:Record<string,unknown>):string[]{const m=store(d).ivaSubstitutions as Record<string,{result?:{inputs:Array<{id:string;prior:{entityRef:string;origin:string}|null;payments:ClosurePayment[]}>;rows:Array<{id:string;status:string;paid:FiscalFraction|null}>};history?:Array<{inputs:Array<{id:string;prior:{entityRef:string;origin:string}|null;payments:ClosurePayment[]}>;rows:Array<{id:string;status:string;paid:FiscalFraction|null}>}>}>|undefined;return Object.entries(m??{}).flatMap(([id,b])=>[...(b.history??[]),...(b.result?[b.result]:[])].flatMap(c=>c.inputs.filter(r=>r.prior&&(id===scope(d)||r.prior.origin==='effective')&&c.rows.some(v=>v.id===r.id&&v.status==='determined'&&v.paid)).flatMap(r=>r.payments.map(p=>closureFingerprint([normalize(r.prior!.entityRef),r.prior!.origin,normalize(p.reference)])))));}
|
||
function bookKey(d:Record<string,unknown>,b:ClosureBook){const sources=closureSources(d);const values:unknown[]=[b.rows,b.rows.map(r=>sources.some(s=>JSON.stringify(s)===JSON.stringify(r.source))),otherUses(d,b).map(r=>[normalize(r.entityRef),r.origin,r.period,r.credits.map(c=>creditIdentity(r,c)),r.payments.map(p=>paymentIdentity(r,p))])];const reserved=substitutionPayments(d),conflicts=b.rows.flatMap(r=>r.payments.map(p=>paymentIdentity(r,p))).filter(k=>reserved.includes(k));if(conflicts.length)values.push(conflicts);return closureFingerprint(values);}
|
||
export function closureCurrent(d:Record<string,unknown>,b:ClosureBook):boolean{return !!b.result&&b.result.key===bookKey(d,b)&&b.result.rows.length===b.rows.length&&b.rows.every(r=>b.result!.rows.filter(x=>x.id===r.id&&x.inputKey===inputKey(r)).length===1);}
|
||
export function adoptPreviousCredit(c:ClosureCredit,previous:ClosureResult):ClosureCredit{if(previous.status!=='determined'||!previous.fields['26'])throw Error('Fecho anterior indeterminado;sem crédito presumido.');return{...c,kind:'previous',period:previous.period,reference:`Fecho ${previous.id}`,previousId:previous.id,carryKey:previous.carryKey,amount:fractionPresentation(previous.fields['26']).decimal,date:''};}
|
||
function refundLockConflict(d:Record<string,unknown>,row:ClosureInput,fields:Record<string,FiscalFraction|null>):boolean{
|
||
const books=store(d).ivaRefunds as Record<string,{rows:Array<{source:{closureId:string;entityRef:string;origin:string;period:string;credit:FiscalFraction;carry:FiscalFraction;requested:FiscalFraction}|null;stage:string;decision:string;receipts:unknown[]}>;result?:{inputs:Array<{source:{closureId:string;entityRef:string;origin:string;period:string;credit:FiscalFraction;carry:FiscalFraction;requested:FiscalFraction}|null;stage:string;decision:string;receipts:unknown[]}>};history?:Array<{inputs:Array<{source:{closureId:string;entityRef:string;origin:string;period:string;credit:FiscalFraction;carry:FiscalFraction;requested:FiscalFraction}|null;stage:string;decision:string;receipts:unknown[]}>}>}>|undefined;
|
||
return Object.entries(books??{}).some(([context,book])=>[...book.rows,...[...(book.history??[]),...(book.result?[book.result]:[])].flatMap(c=>c.inputs)].some(r=>r.source&&(context===scope(d)||r.source.origin==='effective')&&r.source.closureId===row.id&&normalize(r.source.entityRef)===normalize(row.entityRef)&&r.source.origin===row.origin&&r.source.period===row.period&&(r.stage==='submitted'||r.decision!=='pending'||r.receipts.length>0)&&[['25',r.source.credit],['26',r.source.carry],['27',r.source.requested]].some(([f,v])=>JSON.stringify(fields[f as string])!==JSON.stringify(v))));
|
||
}
|
||
function effectiveCarryAlreadyUsed(d:Record<string,unknown>,row:ClosureInput,fields:Record<string,FiscalFraction|null>):boolean{
|
||
if(row.origin!=='effective'||row.destination!=='refund')return false;
|
||
const books=maps(d),own=books[scope(d)];if(!own)return false;
|
||
const prior=[...(own.history??[]),...(own.result?[own.result]:[])].flatMap(c=>c.rows).filter(r=>r.id===row.id&&r.origin==='effective'&&r.fields['26']&&fields['26']&&BigInt(subtractFractions(r.fields['26'],fields['26']).numerator)>0n);
|
||
return prior.some(p=>Object.values(books).some(b=>[...(b.history??[]),...(b.result?[b.result]:[])].some(c=>c.inputs.some(input=>input.origin==='effective'&&normalize(input.entityRef)===normalize(row.entityRef)&&input.period>row.period&&input.credits.some(credit=>credit.kind==='previous'&&credit.previousId===p.id&&credit.carryKey===p.carryKey)&&c.rows.some(r=>r.id===input.id&&r.status==='determined')))));
|
||
}
|
||
export function recordClosure(d:Record<string,unknown>,b:ClosureBook,now=new Date().toISOString()):ClosureBook{
|
||
validate(b);if(new Set(b.rows.map(r=>r.id)).size!==b.rows.length)throw Error('Identidade de fecho repetida.');
|
||
const contexts=b.rows.map(r=>JSON.stringify([normalize(r.entityRef),r.origin,r.period]));if(new Set(contexts).size!==contexts.length)throw Error('Mesmo âmbito/período repetido neste cenário;sem duplicar o fecho.');
|
||
const duplicate=duplicateCredits(d,b),puses=new Map<string,number>();for(const r of otherUses(d,b))for(const p of r.payments){const k=paymentIdentity(r,p);puses.set(k,(puses.get(k)??0)+1);}
|
||
const results:ClosureResult[]=[];
|
||
for(const row of [...b.rows].sort((a,z)=>a.period.localeCompare(z.period))){
|
||
const fields:Record<string,FiscalFraction|null>=Object.fromEntries(['17','18','19','20','21','22','23','24','25','26','27'].map(f=>[f,null]));
|
||
const r:ClosureResult={id:row.id,period:row.period,entityRef:row.entityRef,origin:row.origin,status:'indeterminate',reason:'',inputKey:inputKey(row),financialKey:financialKey(row),carryKey:'',fields,paid:null,outstanding:null,overpayment:null,source:closureSource};
|
||
const stop=(reason:string)=>{r.reason=reason;results.push(r);};
|
||
if(!/^2026-(0[1-9]|1[0-2])$/.test(row.period)||!row.entityRef.trim()||d.currency!=='MZN'){stop('Identifique âmbito fiscal e período2026/MZN;sem consolidar oportunidades automaticamente.');continue;}
|
||
if(row.origin==='effective'&&otherUses(d,b).some(other=>other!==row&&other.origin==='effective'&&normalize(other.entityRef)===normalize(row.entityRef)&&other.period===row.period)){stop('Mesmo âmbito fiscal/período efectivo reservado noutra oportunidade recuperada;reconcilie a declaração única,sem duplicar ou consolidar dossiers silenciosamente.');continue;}
|
||
const source=closureSources(d).find(s=>JSON.stringify(s)===JSON.stringify(row.source));
|
||
if(!source||source.period!==row.period||source.opportunity!==scope(d)||source.currency!=='MZN'||!source.debit||!source.credit){stop('ResultadoL2 congelado correspondente ausente ou17/18 indeterminados;sem recalcular nem presumir impostozero.');continue;}
|
||
fields['17']=source.debit;fields['18']=source.credit;
|
||
if(BigInt(source.debit.numerator)<0n||BigInt(source.credit.numerator)<0n||BigInt(source.debit.numerator)>0n&&BigInt(source.credit.numerator)>0n){stop('Sinais17/18 incompatíveis;resolver origemL2.');continue;}
|
||
if(row.origin==='effective'&&source.projected){stop('OrigemL2 contém pressupostos projectados;não os apresentar como declaração efectiva.');continue;}
|
||
if(row.firstDeclaration!=='yes'){stop('Este percursoA/B exige primeira declaração;substituição21 permaneceL3-C.');continue;}
|
||
if(!validDate(row.submissionDate)||row.submissionDate.slice(0,7)<row.period||!row.filingRef.trim()||row.filing!=='timely'){stop(row.filing==='late'?'Declaração fora do prazo:RCIVA15 e exemplar não permitem uso19/20 neste percurso;conservar conta de crédito e tratar procedimento próprio,sem o apagar.':'Indique data e fundamento da entrega em prazo,efectivos ou pressupostos;sem calendário/prazo presumido.');continue;}
|
||
if(row.creditsComplete!=='yes'){stop('Reconcilie todos os créditos aplicáveis;lista vazia só significa ausência quando expressamente confirmada.');continue;}
|
||
const credits19:FiscalFraction[]=[],credits20:FiscalFraction[]=[];let invalid='';
|
||
for(const c of row.credits){
|
||
if(!c.reference.trim()||!/^20\d{2}-(0[1-9]|1[0-2])$/.test(c.period)||c.period>row.period||c.kind!=='communication'&&c.period===row.period||duplicate.has(creditIdentity(row,c))){invalid='Crédito sem origem/período aplicável ou já utilizado/reservado noutra linha/âmbito recuperado;sem utilização duplicada.';break;}
|
||
if(c.kind==='previous'){
|
||
const p=results.find(p=>p.id===c.previousId);if(!p||p.status!=='determined'||p.period!==priorPeriod(row.period)||p.entityRef.trim().toLowerCase()!==row.entityRef.trim().toLowerCase()||p.origin!==row.origin||p.period!==c.period||p.carryKey!==c.carryKey||!p.fields['26']){invalid='Fecho anterior alterado,indeterminado,fora do mês imediatamente anterior ou de outro âmbito;não adoptar novo crédito silenciosamente.';break;}credits19.push(p.fields['26']);
|
||
}else{
|
||
let v:FiscalFraction;try{v=decimalFraction(c.amount);}catch{invalid='Montante de crédito em falta/inválido;não substituir porzero.';break;}
|
||
if(c.kind==='opening'&&c.period!==priorPeriod(row.period)){invalid='Saldo de abertura exige referência ao saldo disponível do mês imediatamente anterior;origem mais antiga deve ser reconciliada nesse saldo.';break;}
|
||
if(c.kind==='opening'&&b.rows.some(p=>p.id!==row.id&&p.entityRef.trim().toLowerCase()===row.entityRef.trim().toLowerCase()&&p.origin===row.origin&&p.period===priorPeriod(row.period))){invalid='Há fecho do período anterior neste livro;reutilize26 expressamente em vez de introduzir outra abertura.';break;}
|
||
if(c.kind==='communication'&&(!validDate(c.date)||c.date>row.submissionDate)){invalid='Crédito20 exige comunicação recebida,referência e data não posterior à entrega declarada;pedido não é comunicação.';break;}
|
||
(c.kind==='opening'?credits19:credits20).push(v);
|
||
}
|
||
}
|
||
if(invalid){stop(invalid);continue;}
|
||
fields['19']=addFractions(credits19);fields['20']=addFractions(credits20);
|
||
const balance=subtractFractions(addFractions([source.credit,fields['19'],fields['20']]),source.debit);
|
||
fields['22']=positiveFraction(fraction(-BigInt(balance.numerator),BigInt(balance.denominator)));fields['25']=positiveFraction(balance);
|
||
if(row.interest==='none'&&row.interestRef.trim())fields['23']=zero();
|
||
else if(row.interest==='documented'&&row.interestRef.trim()){try{fields['23']=decimalFraction(row.interestAmount);}catch{/* Preserve unknown. */}}
|
||
fields['24']=fields['23']?addFractions([fields['22'],fields['23']]):null;
|
||
if(row.destination==='report'){fields['26']=fields['25'];fields['27']=zero();}
|
||
else if(row.destination==='refund'){
|
||
let amount:FiscalFraction;try{amount=decimalFraction(row.refundAmount??'');}catch{stop('Identifique a parcela destinada ao pedido;sem montante presumido.');continue;}
|
||
if(BigInt(amount.numerator)===0n||BigInt(subtractFractions(fields['25'],amount).numerator)<0n){stop('Parcela do pedido deve ser positiva e não exceder o crédito25;sem dupla utilização.');continue;}
|
||
fields['27']=amount;fields['26']=subtractFractions(fields['25'],amount);r.source+=' D:afectação expressa26+27=25,sem concessão/recebimento;CIVA21 eModeloA11.';
|
||
}else{stop('Escolha reporte integral ou parcela para preparação de reembolsoD;sem destino presumido.');continue;}
|
||
if(refundLockConflict(d,row,fields)){stop('Crédito ligado a pedido apresentado/decisão/recebimento;conserve a afectação e reconcilie o procedimento,sem o voltar a reportar ou alterar silenciosamente.');continue;}
|
||
if(effectiveCarryAlreadyUsed(d,row,fields)){stop('Reporte efectivo já utilizado numa declaração posterior recuperada;prepare o pedido sobre o saldo disponível do período reconciliado,sem reafectar retroactivamente crédito consumido.');continue;}
|
||
if(!fields['24']){stop('Juros23 por identificar:indique montante documentado/pressuposto ou inexistência expressa com motivo;sem taxa presumida.');continue;}
|
||
r.status='determined';r.carryKey=closureFingerprint([r.id,r.financialKey,fields['26'],scope(d)]);
|
||
const payments:FiscalFraction[]=[];
|
||
for(const p of row.payments){if(!p.reference.trim()||!validDate(p.date)||(puses.get(paymentIdentity(row,p))??0)>1||substitutionPayments(d).includes(paymentIdentity(row,p))){invalid='Pagamento sem referência/data ou repetido noutro período/declaração;conserve o documento e reconcilie a imputação.';break;}try{payments.push(decimalFraction(p.amount));}catch{invalid='Montante de pagamento por identificar;sem baixa presumida.';break;}}
|
||
if(invalid){stop(invalid);continue;}
|
||
r.paid=addFractions(payments);r.outstanding=positiveFraction(subtractFractions(fields['24'],r.paid));r.overpayment=positiveFraction(subtractFractions(r.paid,fields['24']));
|
||
r.status='determined';r.carryKey=closureFingerprint([r.id,r.financialKey,fields['26'],scope(d)]);r.reason='Saldo calculado, pagamentos registados e crédito transportado distintos. Pagamento não altera22/25/26;excedente pago não cria crédito20 sem comunicação.21 não aplicável à primeira declaração;'+(row.destination==='refund'?'26/27 separados pela afectação;27 é parcela destinada a pedidoD,sem concessão nem recebimento. ':'27zero pela opção expressa de reporte integral. ')+(row.origin==='projected'?'Pressupostos de simulação,sem declaração ou pagamento efectuados pelo portal.':'Dados efectivos declarados;registo não certifica cobrança nem submissãoAT.');results.push(r);
|
||
}
|
||
const result={calculatedAt:now,key:bookKey(d,b),inputs:structuredClone(b.rows),rows:results};return{...b,...(b.result?{history:[...(b.history??[]),structuredClone(b.result)]}:{}),result};
|
||
}
|
||
export function closureReportRows(d:Record<string,unknown>):Array<[string,string,string,string]>{
|
||
try{
|
||
const b=closureBook(d);if(!b.rows.length)return[];
|
||
const hasRefund=b.result?.rows.some(r=>r.fields['27']&&BigInt(r.fields['27'].numerator)>0n);
|
||
const description=hasRefund?closureSource.replace('Sem substituição21,reembolso27 ou restituição13(2).','ExtensãoD:26+27=25;27é parcela destinada a pedido,não decisão/recebimento. Substituição21 e restituição13(2) mantêm percursos próprios.'):closureSource;
|
||
const out:Array<[string,string,string,string]>=[['IVA mensal','L3-A/B — fecho por período',closureCurrent(d,b)?'Resultado correspondente às entradas':'Histórico preservado;rever entradas/origens antes de utilizar',description]];
|
||
for(const r of b.result?.rows??[]){
|
||
const input=b.result?.inputs.find(x=>x.id===r.id),source=input?.source;
|
||
const context=`${r.entityRef};${scope(d)};MZN;${r.origin==='projected'?'Pressuposto de simulação':'Dados efectivos declarados'};versão guardada${b.result!.calculatedAt}`;
|
||
out.push(['IVA mensal',`Fecho A/B — ${r.period}`,Object.entries(r.fields).map(([f,v])=>`${f}:${v?fractionPresentation(v).decimal:f==='21'&&input?.firstDeclaration==='yes'?'Não aplicável (primeira declaração)':'Indeterminado'}`).join('; '),`${context};${r.reason}`]);
|
||
const credits=(input?.credits??[]).map(c=>`${c.kind==='communication'?'Comunicação20':c.kind==='previous'?'Transporte19':'Abertura19'}: ${c.reference};origem${c.period};montante apresentado${c.amount}MZN${c.date?';recebida'+c.date:''}`).join(' | ')||'Sem créditos registados';
|
||
const payments=(input?.payments??[]).map(p=>`${p.reference};${p.date};${p.amount}MZN`).join(' | ')||'Sem pagamentos registados';
|
||
out.push(['IVA mensal',`Pagamentos e reporte — ${r.period}`,`Calculado24:${r.fields['24']?fractionLabel(r.fields['24']):'Indeterminado'};pagamentos registados:${r.paid?fractionLabel(r.paid):'Indeterminado'};por liquidar:${r.outstanding?fractionLabel(r.outstanding):'Indeterminado'};excedente pago:${r.overpayment?fractionLabel(r.overpayment):'Indeterminado'};transportado26:${r.fields['26']?fractionLabel(r.fields['26']):'Indeterminado'}`,`${context};OrigemL2:revisão${source?.revision??'por identificar'},${source?.period??''},${source?.location??''},${source?.calculatedAt??''};${credits};${payments};entrega${input?.submissionDate??''},${input?.filingRef??''};juros:${input?.interestRef??''}`]);
|
||
}
|
||
return out;
|
||
}catch{return[['IVA mensal','L3-A/B','Fecho guardado não recuperável','Originais preservados;sem crédito ou pagamento presumidos.']];}
|
||
}
|