Files
Investidor-V2/resources/js/components/acquisition-closure.tsx
T

49 lines
8.9 KiB
TypeScript

import {useState} from 'react';
import {closureBook,closureSources,newClosure,newClosureCredit,saveClosureBook,recordClosure,adoptPreviousCredit,closureReportRows,type ClosureInput,type ClosureCredit,type ClosureBook} from '@/lib/iva-closure';
export function AcquisitionClosure({draft,onChange}:{draft:Record<string,unknown>;onChange:(field:string,value:string)=>void}){
const[error,setError]=useState('');let b:ClosureBook;try{b=closureBook(draft);}catch(e){return<p role="alert">{String(e)}</p>;}
const save=(next:ClosureBook)=>{try{onChange('modelo_a_snapshot',saveClosureBook(draft,next));setError('');}catch(e){setError(String(e));}};
const change=(id:string,p:Partial<ClosureInput>)=>save({...b,rows:b.rows.map(r=>r.id===id?{...r,...p}:r)});
const sources=closureSources(draft);
const entities=[...new Set(b.rows.map(r=>r.entityRef.trim()).filter(Boolean))];
const creditChange=(r:ClosureInput,id:string,p:Partial<ClosureCredit>)=>change(r.id,{credits:r.credits.map(c=>c.id===id?{...c,...p}:c)});
const choice=(r:ClosureInput,key:'firstDeclaration'|'creditsComplete',label:string)=><label className="block">{label}<select value={r[key]} onChange={e=>change(r.id,{[key]:e.target.value})}><option value="">Por confirmar</option><option value="yes">Sim</option><option value="no">Não</option></select></label>;
return<details className="rounded border p-3"><summary>L3-A/B — crédito,fecho e pagamentos por período</summary>
<p>Reutiliza resultados congelados de L2. Crédito anterior,saldo calculado,pagamento registado e transporte são distintos. Identifique o âmbito fiscal da entidade,sem consolidar oportunidades implicitamente.</p>
<p>Primeira declaração em prazo,2026/MZN e reporte integral ou afectação de parcela ao pedidoD. SubstituiçõesC,acompanhamento de reembolsoD e restituição de bebidasE continuam próprios. A parcela preparada para pedido é separada do reporte,sem concessão automática. Prazo/data são declarados,não calculados pelo portal. Uma simulação não submete declarações nem efectua pagamentos.</p>
{error&&<p role="alert">{error}</p>}
<button type="button" onClick={()=>save({...b,rows:[...b.rows,newClosure(crypto.randomUUID(),sources.length===1?sources[0]:null,entities.length===1?entities[0]:String(draft.fiscal_entity_ref??draft.organization_name??''))]})}>Preparar fecho de outro período</button>
{b.rows.map(r=><fieldset className="rounded border p-3" key={r.id}><legend>Fecho — {r.period||'período por escolher'}</legend>
<label>Resultado L2 congelado<select value={sources.findIndex(s=>JSON.stringify(s)===JSON.stringify(r.source))} onChange={e=>{const s=sources[Number(e.target.value)];if(s)change(r.id,{source:structuredClone(s),period:s.period});}}><option value={-1}>Escolha versão e período</option>{sources.map((s,i)=><option key={i} value={i}>{s.period} — revisão{s.revision} — {s.calculatedAt}</option>)}</select></label>
<label>Âmbito fiscal da entidade<input value={r.entityRef} onChange={e=>change(r.id,{entityRef:e.target.value})}/></label>
<label>Natureza do fecho<select value={r.origin} onChange={e=>change(r.id,{origin:e.target.value as ClosureInput['origin']})}><option value="projected">Pressuposto de simulação</option><option value="effective">Dados efectivos declarados</option></select></label>
{choice(r,'firstDeclaration','É a primeira declaração deste período (ou pressuposto explícito)')}
<label>Data de entrega ou prevista<input type="date" value={r.submissionDate} onChange={e=>change(r.id,{submissionDate:e.target.value})}/></label>
<label>Situação do prazo<select value={r.filing} onChange={e=>change(r.id,{filing:e.target.value as ClosureInput['filing']})}><option value="">Por identificar</option><option value="timely">Dentro do prazo declarado</option><option value="late">Fora do prazo — procedimento próprio</option></select></label>
<label>Fundamento/referência do prazo declarado<input value={r.filingRef} onChange={e=>change(r.id,{filingRef:e.target.value})}/></label>
<h5>Créditos de origem identificada</h5>
{r.credits.map(c=><fieldset className="rounded border p-2" key={c.id}><legend>{c.kind==='opening'?'19 — saldo de abertura':c.kind==='previous'?'19 — transporte anterior':'20 — crédito comunicado'}</legend>
{c.kind==='previous'?<label>Adoptar expressamente o reporte anterior<select value={c.previousId} onChange={e=>{try{const p=b.result?.rows.find(p=>p.id===e.target.value);if(p)creditChange(r,c.id,adoptPreviousCredit(c,p));}catch(err){setError(String(err));}}}><option value="">Escolha o fecho anterior</option>{b.result?.rows.filter(p=>p.id!==r.id&&p.period<r.period).map(p=><option key={p.id} value={p.id}>{p.period} — {p.entityRef} — {p.status==='determined'?'calculado':'indeterminado'}</option>)}</select></label>:<>
<label>Período de origem do crédito/saldo<input type="month" value={c.period} onChange={e=>creditChange(r,c.id,{period:e.target.value})}/></label>
<label>Documento/referência do crédito<input value={c.reference} onChange={e=>creditChange(r,c.id,{reference:e.target.value})}/></label>
<label>Montante disponível documentado (MZN)<input value={c.amount} onChange={e=>creditChange(r,c.id,{amount:e.target.value})}/></label>
{c.kind==='communication'&&<label>Data da comunicação recebida<input type="date" value={c.date} onChange={e=>creditChange(r,c.id,{date:e.target.value})}/></label>}</>}
{c.kind==='previous'&&<><p>Origem:{c.reference};{c.period};apresentação{c.amount}. Cálculo pela fracção exacta;origem alterada exige adopção expressa.</p><button type="button" onClick={()=>{try{const p=b.result?.rows.find(p=>p.id===c.previousId);if(!p)throw Error('Fecho anterior não recuperável.');creditChange(r,c.id,adoptPreviousCredit(c,p));}catch(e){setError(String(e));}}}>Adoptar versão actual deste reporte</button></>}
<button type="button" onClick={()=>change(r.id,{credits:r.credits.filter(v=>v.id!==c.id)})}>Remover vínculo de crédito desta preparação</button></fieldset>)}
{(['opening','communication','previous'] as const).map(kind=><button type="button" key={kind} onClick={()=>change(r.id,{credits:[...r.credits,newClosureCredit(crypto.randomUUID(),kind)]})}>{kind==='opening'?'Identificar saldo inicial':kind==='communication'?'Identificar crédito comunicado':'Reutilizar reporte anterior'}</button>)}
{choice(r,'creditsComplete','Reconciliei todos os créditos;lista vazia confirma expressamente ausência')}
<label>Juros23<select value={r.interest} onChange={e=>change(r.id,{interest:e.target.value as ClosureInput['interest']})}><option value="">Indeterminados</option><option value="none">Sem juros aplicáveis — motivo identificado</option><option value="documented">Montante identificado,sem cálculo de taxa pelo portal</option></select></label>
{r.interest==='documented'&&<label>Montante de juros (MZN)<input value={r.interestAmount} onChange={e=>change(r.id,{interestAmount:e.target.value})}/></label>}
{r.interest&&<label>Fonte/motivo dos juros ou da sua inexistência<input value={r.interestRef} onChange={e=>change(r.id,{interestRef:e.target.value})}/></label>}
<label>Destino do crédito25<select value={r.destination} onChange={e=>change(r.id,{destination:e.target.value as ClosureInput['destination']})}><option value="">Por escolher</option><option value="report">Reportar integralmente26;sem pedido27 neste percurso</option><option value="refund">Destinar parcela ao pedido27 — acompanhar emD</option></select></label>
{r.destination==='refund'&&<label>Parcela do cr?dito destinada ao pedido27 (MZN)<input value={r.refundAmount??''} onChange={e=>change(r.id,{refundAmount:e.target.value})}/></label>}
<h5>Pagamentos registados,distintos do saldo calculado</h5>
{r.destination==='refund'&&<label>Parcela do crédito destinada ao pedido27 (MZN)<input value={r.refundAmount??''} onChange={e=>change(r.id,{refundAmount:e.target.value})}/></label>}
{r.payments.map(p=><fieldset className="rounded border p-2" key={p.id}>{(['reference','date','amount'] as const).map((k,i)=><label key={k}>{['Referência do pagamento','Data do pagamento','Montante do pagamento (MZN)'][i]}<input type={k==='date'?'date':'text'} value={p[k]} onChange={e=>change(r.id,{payments:r.payments.map(v=>v.id===p.id?{...v,[k]:e.target.value}:v)})}/></label>)}<button type="button" onClick={()=>change(r.id,{payments:r.payments.filter(v=>v.id!==p.id)})}>Remover pagamento desta preparação</button></fieldset>)}
<button type="button" onClick={()=>change(r.id,{payments:[...r.payments,{id:crypto.randomUUID(),reference:'',date:'',amount:''}]})}>Registar referência de pagamento</button>
<button type="button" onClick={()=>save({...b,rows:b.rows.filter(v=>v.id!==r.id)})}>Remover fecho em preparação (histórico conservado)</button></fieldset>)}
<button type="button" onClick={()=>{try{save(recordClosure(draft,b));}catch(e){setError(String(e));}}}>Calcular e guardar fecho A/B</button>
{closureReportRows(draft).map(([,label,value,source],i)=><p key={i}><strong>{label}</strong>: {value}<small className="block">{source}</small></p>)}
</details>;
}