import React,{useEffect,useRef,useState} from 'react'; import {milliQuantity} from './StockReadWorkspace.jsx'; import {WorkshopStockOperation} from './workshop-stock-operation.js'; export function WorkshopStockImpact({impact:p}) { return
Físico: {milliQuantity(p.physical_before)} → {milliQuantity(p.physical_after)}. Nova saída: {milliQuantity(p.quantity_milli)}. {['link_existing_delivery','allocate_existing_delivery'].includes(p.mode)&&<>Ligar entrega #{p.existing_movement_id}: {milliQuantity(p.linked_quantity_milli)}, sem novo abatimento. >}{p.mode==='return_existing_delivery'&&<>Entrada por devolução: {milliQuantity(p.return_quantity_milli)}, da entrega #{p.existing_movement_id}. >}{['allocate_existing_delivery','return_existing_delivery'].includes(p.mode)&&<>Quantidade não associada: {milliQuantity(p.unallocated_before)} → {milliQuantity(p.unallocated_after)}. >}Artigo #{p.stock_item_id}, armazém #{p.warehouse_id}, origem versão {p.source_version}.
; } export function WorkshopStockClosureImpact({action}) { returnEncerrar esta proposta como {action==='withdraw'?'retirada':'recusada'}. Efeito físico: 0; nenhuma origem será associada ou libertada. O original e esta decisão ficam no histórico; voltar a tratar a origem exige nova conferência.
; } export default function WorkshopStockWorkspace({api,context,can}) { const [data,setData]=useState(null),[error,setError]=useState(''),[reload,setReload]=useState(0),[page,setPage]=useState(1),[busy,setBusy]=useState(false),[preview,setPreview]=useState(null),[proposalPage,setProposalPage]=useState(1),[status,setStatus]=useState("all"); const [uncertain,setUncertain]=useState(false);const operation=useRef(null);if(!operation.current)operation.current=new WorkshopStockOperation(api); const request=useRef(null),generation=useRef(0),unit=context.unit.id; useEffect(()=>{setPreview(null);setBusy(false);request.current=null;},[context.token]); useEffect(()=>{const n=++generation.current;setData(null);setError('');api(`/api/workshop-stock?page=${page}&proposal_page=${proposalPage}&status=${status}`).then(d=>{if(n===generation.current)setData(d);}).catch(e=>{if(n===generation.current)setError(e.message);});return()=>{generation.current++;};},[context.token,reload,page,proposalPage,status]); async function execute(path,body,key,recover=false){ if(busy||(!recover&&uncertain))return;const n=generation.current;setBusy(true);setError(''); try{const result=recover?await operation.current.retry():await operation.current.send(path,body,key);if(n===generation.current){setPreview(result.impact?result:null);setReload(v=>v+1);}} catch(e){if(n===generation.current)setError(operation.current.uncertain?'Resultado incerto. Recupere a mesma operação antes de continuar.':e.message);} finally{if(n===generation.current){setUncertain(operation.current.uncertain);setBusy(false);}} } async function prepare(e,source){e.preventDefault();if(busy||uncertain)return;const body={...Object.fromEntries(new FormData(e.currentTarget)),entry_id:source.id,version:source.version};body.warehouse_id=Number(body.warehouse_id); const allocation=body.mode==='allocate_existing_delivery',returning=body.mode==='return_existing_delivery',delivery=returning||allocation||body.mode==='link_existing_delivery';delete body.mode;if(delivery)body.movement_id=Number(body.movement_id);else delete body.movement_id; const path=returning?'/api/workshop-stock/prepare-return':allocation?'/api/workshop-stock/prepare-allocation':delivery?'/api/workshop-stock/prepare-delivery':'/api/workshop-stock/prepare'; const text=JSON.stringify([path,body]);if(request.current?.text!==text)request.current={text,key:crypto.randomUUID()};return execute(path,body,request.current.key); } async function confirm(e,p){e.preventDefault();if(busy||uncertain)return;const body=Object.fromEntries(new FormData(e.currentTarget));if('received_in_warehouse' in body)body.received_in_warehouse=body.received_in_warehouse==='on';return execute(`/api/workshop-stock/${p.id}/confirm`,body,crypto.randomUUID());} async function close(e,p,action){e.preventDefault();if(busy||uncertain)return;return execute(`/api/workshop-stock/${p.id}/${action}`,{...Object.fromEntries(new FormData(e.currentTarget)),state_version:p.state_version,source_hash:p.source_hash},crypto.randomUUID());} const closureForm=(p,action)=>Uma origem verificada não provoca saída automática. Entregas já abatidas e correcções exigem reconciliação própria.
{error&&{error}
}{!data&&!error&&A consultar origens autorizadas…
} {uncertain&&}