import React,{useEffect,useRef,useState} from 'react'; const labels={material:'Material',labour:'Trabalho',allocated:'Indirectos imputados'}; const value=v=>v===null?'Indisponível':v; export default function IndustrialBudgetsWorkspace({context,can,api,busy}){ const [data,setData]=useState(null),[lotId,setLotId]=useState(''),[error,setError]=useState(''),[working,setWorking]=useState(false);const retry=useRef(null); const allowed=r=>can(r,context.unit?.id??null);const disabled=busy||working; const load=async()=>setData(await api('/api/industrial-budgets')); useEffect(()=>{setData(null);setLotId('');retry.current=null;load().catch(e=>setError(e.message));},[context.token]); const lot=data?.lots.find(l=>String(l.id)===lotId)??data?.lots[0]; async function send(action,input){setWorking(true);setError('');const path=`/api/industrial-budgets/lots/${lot.id}/${action}`,signature=JSON.stringify([path,input]);if(retry.current?.signature!==signature)retry.current={signature,key:crypto.randomUUID()};try{await api(path,'POST',input,context.token,retry.current.key);retry.current=null;await load();}catch(e){setError(e.message);}finally{setWorking(false);}} return

INDUSTRIAL-BUDGET-ACTUAL-SIM-v1

Orçamento versus realizado

ENSAIO — SEM UTILIZAÇÃO OPERACIONAL. Hipótese retrospectiva sintética; diferenças monetárias não demonstram causas ou eficiência.
{error&&

{error}

}{!data?

A carregar…

:data.blocked?

Operações reais bloqueadas.

:<>{lot?<>

{lot.product.name} · {lot.period_start} a {lot.period_end} · 10 UN · MZN · Material, trabalho e indirectos imputados

{allowed('industrial_budgets.prepare')&&
{e.preventDefault();const f=new FormData(e.currentTarget),last=lot.budgets.at(-1);send('proposals',{version:(last?.version??0)+1,previous_id:last?.id??null,dimensions:lot.dimensions,amounts:Object.fromEntries(Object.keys(labels).map(k=>[k,f.get(k)===''?null:f.get(k)])),evidence:f.get('evidence')});}}>

Preparar orçamento versionado

{lot.budgets.length?`Sucessora explícita de #${lot.budgets.at(-1).id} v${lot.budgets.at(-1).version}. Referência histórica preservada.`:'Primeira proposta. Não é planeamento anterior comprovado.'}

{Object.entries(labels).map(([k,l])=>)}
}

Orçamentos e decisões

{lot.budgets.map(b=>

Orçamento #{b.id} · v{b.version}

{b.current?'Última versão':'HISTÓRICO — substituído explicitamente'} · {b.decision?`${b.decision.decision==='approved'?'Validado':'Recusado'} — ${b.decision.reason}`:'Aguarda validação distinta'}

{b.evidence}

{allowed('industrial_budgets.validate')&&b.current&&!b.decision&&
{e.preventDefault();const f=new FormData(e.currentTarget);send('decisions',{id:b.id,version:b.version,decision:f.get('decision'),reason:f.get('reason')});}}>
}
)} {allowed('industrial_budget_comparisons.prepare')&&
{e.preventDefault();const f=new FormData(e.currentTarget),b=lot.budgets.find(x=>String(x.id)===f.get('budget')),r=lot.results.find(x=>String(x.id)===f.get('result')),last=lot.comparisons.at(-1);send('comparisons',{version:(last?.version??0)+1,previous_id:last?.id??null,budget_id:b.id,budget_version:b.version,result_id:r.id,result_version:r.version});}}>

Comparar versões explícitas

}

Comparações preservadas

{lot.comparisons.map(c=>

Comparação #{c.id} · v{c.version}

{c.current?'Versões actuais':'DESACTUALIZADA / HISTÓRICA — não substituída silenciosamente'}

Orçamento #{c.snapshot.budget.id} v{c.snapshot.budget.version} · Realizado #{c.snapshot.result.id} v{c.snapshot.result.version}

{Object.entries(labels).map(([k,l])=>{const r=c.snapshot.rows[k];return ;})}
ComponentePrevisto MZNRealizado MZNDesvio R − PPercentagem
{l}{value(r.planned)}{value(r.actual)}{value(r.delta)}{r.percent===null?`Indisponível — ${r.percent_state==='zero_base'?'base zero':'valor desconhecido'}`:`${r.percent} %`}

{c.snapshot.incomplete?'COMPARAÇÃO INCOMPLETA — sem desvio total conclusivo':`Desvio total: ${c.snapshot.total_delta} MZN · ${c.snapshot.total_percent===null?'Percentagem indisponível — base zero':c.snapshot.total_percent+' %'}`}

Realizado conhecido: {c.snapshot.actual_known} MZN

Reconciliação {c.snapshot.incomplete?'dos pares conhecidos':'total'}: {c.snapshot.paired_planned} + ({c.snapshot.paired_delta}) = {c.snapshot.paired_actual} MZN

Residual separado: {c.snapshot.residual_separate} MZN por imputar — fora do desvio do lote

Origens realizadas: {c.snapshot.actual_reconciliation.known_origins_exact} = {c.snapshot.actual_reconciliation.assigned_exact} atribuídos + {c.snapshot.actual_reconciliation.unassigned_exact} por imputar{c.snapshot.incomplete?' (cobertura parcial)':''}.

)}:

Sem lotes autorizados.

}}
; }