import React,{useEffect,useState,useRef} from 'react'; const kinds={asset:'Cadastro e custódia',plan:'Planos mensais',charge:'Quotas por competência',disposal:'Abate sem contraprestação'}; const fields={asset:{code:'Código QA',description:'Descrição',origin_reference:'Referência da abertura artificial',cost:'Custo MZN (desconhecido se vazio)',residual:'Residual MZN (desconhecido se vazio)',available_on:'Entrada em uso',location:'Localização interna',custodian:'Custódia interna'},plan:{asset_key:'Activo',life_months:'Vida artificial em meses',status:'Estado do plano'},charge:{asset_key:'Activo',plan_key:'Plano escolhido',month:'Competência'},disposal:{asset_key:'Activo',effect_on:'Data do abate'}}; const money=v=>v===null||v===undefined?'Indeterminado':`${BigInt(v)/100n},${String(BigInt(v)%100n).padStart(2,'0')} MZN`; const label=r=>r?.data?.input?.code??r?.data?.input?.month??`${kinds[r?.kind]??'Registo'} · v${r?.version??''}`; export default function FixedAssetsWorkspace({api,context,can,perform,busy}){ const[kind,setKind]=useState('asset'),[page,setPage]=useState(1),[size,setSize]=useState(25),[search,setSearch]=useState(''),[list,setList]=useState(null),[selected,setSelected]=useState(null),[form,setForm]=useState({}),[reason,setReason]=useState(''),[cut,setCut]=useState(null),[key,setKey]=useState(crypto.randomUUID()),[domain,setDomain]=useState(''),[result,setResult]=useState(null),[error,setError]=useState(''),[ledger,setLedger]=useState(null),[ledgerPage,setLedgerPage]=useState(1),[history,setHistory]=useState(false); const reads=useRef({list:0,ledger:0}); const locked=busy||Boolean(cut);const unit=context.unit?.id;const chosenAsset=selected?(selected.kind==='asset'?selected.record_key:selected.data.input.asset_key):null; async function load(){const q=new URLSearchParams({kind,page,per_page:size,...(search?{search}:{}),...(history&&selected?{record_key:selected.record_key}:{})});const n=++reads.current.list;const out=await api('/api/fixed-assets?'+q);if(n===reads.current.list){if(out.kind!==kind||out.records.per_page!==Number(size)||!Array.isArray(out.records.data)||out.records.data.length>size)throw Error('Resposta de consulta incompatível; actualize.');setList(out);}} async function loadLedger(){const n=++reads.current.ledger;if(chosenAsset){const out=await api('/api/fixed-assets/ledger?'+new URLSearchParams({asset_key:chosenAsset,page:ledgerPage,per_page:25}));if(n===reads.current.ledger)setLedger(out);}else setLedger(null);} useEffect(()=>{setList(null);load().catch(e=>setError(e.message));},[context.token,kind,page,size,search,history]); useEffect(()=>{loadLedger().catch(e=>setError(e.message));},[chosenAsset,ledgerPage]); function reset(k=kind){reads.current.list++;reads.current.ledger++;setKind(k);setSelected(null);setForm({operation:'establish',status:'active'});setHistory(false);setPage(1);setCut(null);setResult(null);setError('');setLedger(null);setKey(crypto.randomUUID());} function choose(r){reads.current.ledger++;setLedger(null);setSelected(r);setForm({...((r.effective??r).data.input),operation:'amend'});setCut(null);setResult(null);setLedgerPage(1);setKey(crypto.randomUUID());} async function preview(data,type='admin'){await perform(async()=>{setError('');setResult(null);setDomain(type);const out=await api('/api/fixed-assets'+(type==='ledger'?'/ledger':'')+'/preview','POST',data,context.token,key);setCut(out);},'Confira o impacto individual antes de confirmar.');} async function confirm(){await perform(async()=>{try{const out=await api('/api/fixed-assets'+(domain==='ledger'?'/ledger':'')+'/confirm','POST',{preview:cut.preview});setResult({...out,command_key:key});setCut(null);setKey(crypto.randomUUID());await load();await loadLedger();setSelected(null);setForm({});}catch(e){setError(e.message);throw e;}},'Decisão registada e auditada.');} async function recover(){await perform(async()=>{const out=await api(domain==='ledger'?'/api/general-ledger/receipts/'+key:'/api/fixed-assets/receipts/'+key);setResult(out.result??out);setCut(null);await load();await loadLedger();},'Comprovativo consultado sem repetir a operação.');} const action=data=>preview({action:'effect-prepare',data},'ledger'); const source=selected?.effective??selected;const input=source?.data.input;const proposedAsset=source?.kind==='asset'?source.record_key:input?.asset_key; return

CONTABILIDADE

Activos e depreciações internas QA

Exercício artificial. Cadastro, plano, quota e lançamento exigem decisões próprias. Sem aquisição, pagamento ou tratamento fiscal legal presumidos.

{error&&

{error}

}
{Object.entries(kinds).map(([k,n])=>)}

{kinds[kind]}

{list?.records.data.map(r=>

{r.data.input.description??r.data.input.evidence}

{r.effective&&

Versão efectiva: {r.effective.version}

}
)} {list&&}
{selected&&

Versão seleccionada

{label(selected)} · versão {selected.version} · autor #{selected.actor_id} · {selected.created_at}

{selected.reason}

{Object.entries(selected.data.input).filter(([k])=>fields[kind][k]).map(([k,v])=>
{fields[kind][k]}
{v===null?'Indeterminado':String(v)}
)}
{selected.data.calculation?.complete&&

Quota: {money(selected.data.calculation.quota_cents)} · líquido previsto: {money(selected.data.calculation.carrying_cents)}

}
} {can('fixed_assets.prepare',unit)&&

{selected?'Preparar nova versão':'Preparar registo'}

{e.preventDefault();const d={operation:form.operation??'establish',evidence:form.evidence??''};for(const f of Object.keys(fields[kind]))d[f]=form[f]===''||form[f]===undefined?null:f==='life_months'?Number(form[f]):form[f];const recordKey=selected?.record_key??crypto.randomUUID();preview({action:'prepare',kind,record_key:recordKey,version:selected?.version??0,reason,data:d});}}>
{Object.entries(fields[kind]).map(([f,n])=>)}