26 lines
9.0 KiB
TypeScript
26 lines
9.0 KiB
TypeScript
import {useState} from 'react';
|
|
import {annualBook,newAnnualBook,saveAnnualBook,reuseAnnualAssessment,annualAssessment,annualWithholdingOptions,reuseAnnualWithholding,newAnnualPpc,recordAnnual,annualReportRows,annualHtml,annualSettlementSource,type AnnualBook,type AnnualPpc} from '@/lib/irpc-annual-settlement';
|
|
|
|
type Props={draft:Record<string,unknown>;onChange:(field:string,value:string)=>void};
|
|
export function SimulationIrpcAnnual({draft,onChange}:Props){
|
|
const[error,setError]=useState('');if(draft.fiscal_component!=='irpc')return null;
|
|
try{
|
|
const b=annualBook(draft),save=(next:AnnualBook)=>{try{onChange('modelo_a_snapshot',saveAnnualBook(draft,next));setError('');}catch(e){setError(String(e));}};
|
|
if(!b)return<section aria-label="IRPC — L8 fecho anual" className="rounded border p-4"><h4>L8 — da colecta ao saldo anual simulado</h4><p>Reutiliza a colecta válida, retenções efectivamente sofridas por conta e PPC comprovados do próprio sujeito/exercício. PEC e créditos com regras próprias permanecem separados.</p><button type="button" onClick={()=>{try{save(reuseAnnualAssessment(draft,newAnnualBook()));}catch(e){setError(String(e));}}}>Preparar saldo anual L8</button>{error&&<p role="alert">{error}</p>}</section>;
|
|
const patch=(p:Partial<AnnualBook>)=>save({...b,...p}),a=annualAssessment(draft),options=annualWithholdingOptions(draft);
|
|
const field=(key:'subjectNuit'|'year'|'reference'|'assessmentReference',label:string)=><label className="block">{label}<input aria-label={label} value={b[key]} onChange={e=>patch({[key]:e.target.value})}/></label>;
|
|
const group=(key:'withholdings'|'ppc',label:string)=><><label className="block">Estado — {label}<select aria-label={'Estado L8 — '+label} value={b[key].state} onChange={e=>patch({[key]:{...b[key],state:e.target.value as ''|'known'|'absent'}})}><option value="">Informação em falta</option><option value="known">Valores conhecidos — seleccionar comprovativos</option><option value="absent">Ausência declarada com suporte</option></select></label><label className="block">Suporte — {label}<input aria-label={'Suporte L8 — '+label} value={b[key].reference} onChange={e=>patch({[key]:{...b[key],reference:e.target.value}})}/></label></>;
|
|
return<section aria-label="IRPC — L8 fecho anual" className="space-y-3 rounded border p-4"><h4>L8 — colecta, abatimentos e saldo anual simulado</h4><p>A colecta acima é anterior aos abatimentos. Este saldo tem cálculo e histórico próprios; retenção entregue pela empresa por pagamento a terceiros não é retenção sofrida. O resultado continua simulado, sem submissão, pagamento ou reembolso automático.</p><p>{annualSettlementSource}</p>{error&&<p role="alert">{error}</p>}
|
|
{field('subjectNuit','NUIT do sujeito do fecho L8')}{field('year','Exercício do fecho L8')}{field('reference','Suporte do fecho da entidade e exercício L8')}
|
|
<p>Colecta disponível:{a.colecta?a.colecta.numerator+'/'+a.colecta.denominator+' MZN':'Indeterminada'};origem:{a.kind==='special'?'L7 qualificado':'L6 normal'};NUIT:{a.subjectNuit||'Por identificar'};exercício:{a.year||'Por identificar'}.</p><button type="button" onClick={()=>{try{save(reuseAnnualAssessment(draft,b));}catch(e){setError(String(e));}}}>Reutilizar colecta corrente L6/L7</button>{field('assessmentReference','Suporte da colecta reutilizada L8')}
|
|
<fieldset className="space-y-2 rounded border p-3"><legend>Retenções efectivamente sofridas por conta</legend>{group('withholdings','Retenções sofridas')}
|
|
{options.length===0?<p>Não há afectações de comprovativosL5 neste contexto. Reutilize e reconcilie a retenção recebida emL5 ou declare ausência com suporte.</p>:options.map(o=><div key={o.rowId+'|'+o.paymentId} className="rounded border p-2"><p>{o.document||'Prova por identificar'};emitente:{o.issuer};data:{o.date};parcelaL5:{o.amount}MZN;{o.reason||'Retenção recebida/efectiva por conta reconciliada emL5.'}</p><button type="button" disabled={!o.ready||b.withholdings.rows.some(r=>r.rowId===o.rowId&&r.paymentId===o.paymentId)} onClick={()=>{try{save(reuseAnnualWithholding(draft,b,o.rowId,o.paymentId,crypto.randomUUID()));}catch(e){setError(String(e));}}}>Seleccionar prova L5 {o.document||o.paymentId}</button></div>)}
|
|
{b.withholdings.rows.map((r,index)=>{const change=(p:Partial<typeof r>)=>patch({withholdings:{...b.withholdings,rows:b.withholdings.rows.map(x=>x.id===r.id?{...x,...p}:x)}});return<fieldset key={r.id} className="rounded border p-2"><legend>Retenção seleccionada {index+1}</legend><p>Rendimento:{r.rowId};prova:{r.paymentId};NUIT:{r.subjectNuit};exercício:{r.year}. Fonte conservada. Se mudou, retire esta selecção e seleccione a prova actual. O histórico mantém-se.</p><label className="block">Parcela a abater<input aria-label={'Parcela da retenção L8 '+(index+1)} value={r.amount} onChange={e=>change({amount:e.target.value})}/></label><label className="block">O próprio sujeito é beneficiário e o rendimento está incluído na base deste exercício<select aria-label={'Rendimento incluído L8 '+(index+1)} value={r.incomeIncluded} onChange={e=>change({incomeIncluded:e.target.value as ''|'yes'|'no'})}><option value="">Informação em falta</option><option value="yes">Sim — documentado</option><option value="no">Não — crédito não aplicável neste fecho</option></select></label><label className="block">Suporte da titularidade e inclusão<input aria-label={'Suporte da retenção L8 '+(index+1)} value={r.reference} onChange={e=>change({reference:e.target.value})}/></label><button type="button" onClick={()=>patch({withholdings:{...b.withholdings,rows:b.withholdings.rows.filter(x=>x.id!==r.id)}})}>Retirar selecção L8 da retenção {index+1}</button></fieldset>;})}</fieldset>
|
|
<fieldset className="space-y-2 rounded border p-3"><legend>PPC efectivamente pagos</legend>{group('ppc','PPC efectuados')}<p>Regista comprovativos de pagamentos por conta; não calcula automaticamente a obrigação PPC nem usa pagamentos previstos no plano.</p><button type="button" onClick={()=>patch({ppc:{...b.ppc,rows:[...b.ppc.rows,{...newAnnualPpc(crypto.randomUUID()),subjectNuit:b.subjectNuit,year:b.year}]}})}>Registar comprovativo PPC</button>
|
|
{b.ppc.rows.map((r,index)=>{const change=(p:Partial<AnnualPpc>)=>patch({ppc:{...b.ppc,rows:b.ppc.rows.map(x=>x.id===r.id?{...x,...p}:x)}});return<fieldset key={r.id} className="rounded border p-2"><legend>PPC {index+1}</legend>{([['document','Comprovativo'],['issuer','Emitente'],['date','Data (AAAA-MM-DD)'],['subjectNuit','NUIT beneficiário'],['year','Exercício do comprovativo'],['currency','Moeda do comprovativo'],['proofAmount','Montante comprovado'],['amount','Parcela a abater'],['reference','Suporte do pagamento e afectação']]as const).map(([key,label])=><label key={key} className="block">{label}<input aria-label={'PPC L8 '+(index+1)+' — '+label} value={r[key]} onChange={e=>change({[key]:e.target.value})}/></label>)}<label className="block">Pagamento efectivamente comprovado<select aria-label={'PPC L8 '+(index+1)+' — Efectivo'} value={r.effective} onChange={e=>change({effective:e.target.value as ''|'yes'|'no'})}><option value="">Informação em falta</option><option value="yes">Sim — comprovativo reconciliado</option><option value="no">Não — apenas previsão/outro estado</option></select></label><button type="button" onClick={()=>patch({ppc:{...b.ppc,rows:b.ppc.rows.filter(x=>x.id!==r.id)}})}>Retirar PPC {index+1}</button></fieldset>;})}</fieldset>
|
|
{([['pec','PEC'],['credits','Créditos com regras próprias']]as const).map(([key,label])=><fieldset key={key} className="rounded border p-3"><legend>{label} — tratamento separado</legend><label className="block">Estado — {label}<select aria-label={'Estado L8 — '+label} value={b[key].state} onChange={e=>patch({[key]:{...b[key],state:e.target.value as ''|'known'|'absent'}})}><option value="">Informação em falta</option><option value="absent">Ausência declarada com suporte</option><option value="known">Valor conhecido — positivo exige regra própria</option></select></label>{(b[key].state==='known'||b[key].amount)&&<label className="block">Montante declarado<input aria-label={'Montante L8 — '+label} value={b[key].amount} onChange={e=>patch({[key]:{...b[key],amount:e.target.value}})}/></label>}<label className="block">Suporte<input aria-label={'Suporte L8 — '+label} value={b[key].reference} onChange={e=>patch({[key]:{...b[key],reference:e.target.value}})}/></label></fieldset>)}
|
|
<button type="button" onClick={()=>{try{save(recordAnnual(draft,b));}catch(e){setError(String(e));}}}>Calcular e guardar saldo anual L8</button><button type="button" onClick={()=>{try{const html=annualHtml(draft),url=URL.createObjectURL(new Blob([html],{type:'text/html;charset=utf-8'})),link=document.createElement('a');link.href=url;link.download='IRPC-L8-saldo-simulado.html';link.click();URL.revokeObjectURL(url);setError('');}catch(e){setError(String(e));}}}>Exportar saldo anual L8</button>
|
|
{annualReportRows(draft).map((r,n)=><p key={n}>{r[1]} — {r[2]} — {r[3]}</p>)}</section>;
|
|
}catch(e){return<p role="alert">PreparaçãoL8 não recuperável:{String(e)}. Originais e históricos conservados.</p>;}
|
|
}
|