/* ============================================ Admin Modules — Producción, Stock, Ventas, Terceros, Gastos, Rentabilidad, Maquinaria ============================================ */ /* ============================================ Producción ============================================ */ function TabBtn({ active, onClick, children }) { return ( ); } function ModalDetalleLote({ lote, onClose, initialTab }) { const [tab, setTab] = useState(initialTab || "labores"); const [labores, setLabores] = useState([]); const [cortes, setCortes] = useState([]); const [fardos, setFardos] = useState([]); const [showNew, setShowNew] = useState(false); const [loading, setLoading] = useState(true); // Rendimiento tab state const [corteSelId, setCorteSelId] = useState(""); const [rndCants, setRndCants] = useState({}); const [rndKgTotal, setRndKgTotal] = useState(""); const [rndMsg, setRndMsg] = useState(""); const [rndSaving, setRndSaving] = useState(false); const tipoLabel = { rastrillado:"Rastrillado", fumigacion:"Fumigación", fertilizacion:"Fertilización", herbicida:"Herbicida", riego:"Riego", siembra:"Siembra", otro:"Otro" }; const tipoIcon = { rastrillado:"Tractor", fumigacion:"Wind", fertilizacion:"Leaf", herbicida:"Sprout", riego:"Droplets", siembra:"Wheat", otro:"MoreHorizontal" }; const tipoCls = { rastrillado:"b-info", fumigacion:"b-gold", fertilizacion:"b-green", herbicida:"b-mute", riego:"b-info", siembra:"b-green", otro:"b-mute" }; const recargar = () => { setLoading(true); window.API.get(`/lotes/${lote.id}/detalle`).then(r => { setLabores(r.labores || []); setCortes(r.cortes || []); if (r.cortes?.length && !corteSelId) setCorteSelId(String(r.cortes[0].id)); setLoading(false); }); window.API.get("/fardos").then(r => { if (r.data) { const destino = lote.destino_produccion || "ambos"; setFardos(r.data.filter(f => { if (destino === "rollos") return f.tipo === "redondo"; if (destino === "megas") return f.tipo === "mega"; return f.tipo === "redondo" || f.tipo === "mega"; // ambos })); } }); }; useEffect(() => { recargar(); }, [lote.id]); const guardarRendimiento = async () => { const ultimoId = cortes[0]?.id; const cant = Object.values(rndCants).reduce((s, v) => s + (parseInt(v) || 0), 0); const kgTot = parseFloat(rndKgTotal) || null; if (!cant) { setRndMsg("Ingresá al menos una cantidad."); return; } const kgUnd = (cant && kgTot) ? parseFloat((kgTot / cant).toFixed(2)) : null; const kgHa = (kgTot && lote.hectareas) ? parseFloat((kgTot / lote.hectareas).toFixed(2)) : null; setRndSaving(true); setRndMsg(""); const cantidadesInt = {}; Object.entries(rndCants).forEach(([id, v]) => { const n = parseInt(v); if (n > 0) cantidadesInt[id] = n; }); const res = await window.API.post(`/cortes/${ultimoId}/rendimiento`, { cantidad_total: cant, kg_total: kgTot, kg_por_unidad: kgUnd, kg_por_ha: kgHa, cantidades: cantidadesInt, }); setRndSaving(false); if (res.mensaje) { setRndMsg("✓ " + res.mensaje); setRndCants({}); setRndKgTotal(""); recargar(); } else { setRndMsg(res.message || "Error al guardar."); } }; return (
e.stopPropagation()} style={{ maxWidth:760, width:"92vw", maxHeight:"88vh", display:"flex", flexDirection:"column" }}>
{lote.hectareas} ha · {lote.tenencia} · {lote.destino_produccion === "rollos" ? "Solo rollos" : lote.destino_produccion === "megas" ? "Solo megas" : "Rollos y megas"}
{lote.nombre}
{tab === "labores" && ( )}
setTab("labores")}>Labores setTab("cortes")}>Cortes setTab("rendimiento")}>Rendimiento
{loading ? (
Cargando...
) : tab === "labores" ? ( labores.length === 0 ?
Sin labores. Usá "Registrar labor".
:
{labores.map(lb => (
{tipoLabel[lb.tipo] || lb.tipo} {lb.fecha}
{lb.producto &&
{lb.producto}
} {lb.observaciones &&
{lb.observaciones}
}
))}
) : tab === "cortes" ? ( cortes.length === 0 ?
Sin cortes registrados.
: (() => { const cortesConRend = cortes.filter(c => c.cantidad_total > 0); const totalFardos = cortesConRend.reduce((s, c) => s + (c.cantidad_total || 0), 0); const totalKg = cortesConRend.reduce((s, c) => s + (c.kg_total || 0), 0); return ( {cortes.map(c => ( ))} {cortesConRend.length > 1 && ( )}
CorteFechaProductoFardosKg totalKg/unidadKg/haObservaciones
{c.numero_corte}° {c.fecha} {c.producto ? {c.producto} : } {c.cantidad_total ?? } {c.kg_total ? c.kg_total.toLocaleString("es-AR") + " kg" : } {c.kg_por_unidad ? c.kg_por_unidad + " kg" : } {c.kg_por_ha ? c.kg_por_ha + " kg/ha" : } {c.observaciones || "—"}
Total campaña {totalFardos} {totalKg ? totalKg.toLocaleString("es-AR") + " kg" : "—"}
); })() ) : ( /* ── TAB RENDIMIENTO ── */ cortes.length === 0 ?
Registrá un corte primero.
: (() => { const ultimoCorte = cortes[0]; const sinRendimiento = !ultimoCorte?.cantidad_total; const rndCantN = Object.values(rndCants).reduce((s, v) => s + (parseInt(v) || 0), 0); const rndKgN = parseFloat(rndKgTotal) || null; const kgUnd = (rndCantN && rndKgN) ? parseFloat((rndKgN / rndCantN).toFixed(2)) : null; const kgHa = (rndKgN && lote.hectareas) ? parseFloat((rndKgN / lote.hectareas).toFixed(2)) : null; return (
{/* ── Cargar rendimiento del último corte ── */} {sinRendimiento && (
Cargar rendimiento — {ultimoCorte.numero_corte}° corte · {ultimoCorte.fecha}
{fardos.length === 0 ? (
Sin productos en Stock para este lote.
) : ( <>
¿Qué se produjo en este corte?
{fardos.map(f => (
{f.tipo === "redondo" ? "Rollo" : "Mega"} {f.nombre}
stock actual: {f.stock} unidades
setRndCants(prev => ({ ...prev, [f.id]: e.target.value }))} />
))}
{rndCantN > 0 && (
setRndKgTotal(e.target.value)} placeholder="Ej: 60000" />
)} {(kgUnd || kgHa) && (
{kgUnd && Promedio por unidad: {kgUnd} kg} {kgHa && Kg por hectárea: {kgHa} kg/ha}
)}
{rndMsg && {rndMsg}}
)}
)} {!sinRendimiento && rndMsg && ( {rndMsg} )}
); })() )}
{showNew && ( setShowNew(false)} onSuccess={() => { setShowNew(false); recargar(); }} /> )}
); } function ModalNuevaLabor({ loteId, loteName, onClose, onSuccess }) { const tiposLabor = [ { value:"rastrillado", label:"Rastrillado" }, { value:"fumigacion", label:"Fumigación" }, { value:"fertilizacion", label:"Fertilización" }, { value:"herbicida", label:"Herbicida" }, { value:"riego", label:"Riego" }, { value:"siembra", label:"Siembra" }, { value:"otro", label:"Otro" }, ]; const [tipo, setTipo] = useState("rastrillado"); const [fecha, setFecha] = useState(new Date().toISOString().slice(0,10)); const [producto, setProducto]= useState(""); const [obs, setObs] = useState(""); const [err, setErr] = useState(""); const [loading, setLoading] = useState(false); const submit = async (e) => { e.preventDefault(); setErr(""); setLoading(true); const res = await window.API.post("/labores", { lote_id: loteId, tipo, fecha, producto: producto || null, observaciones: obs || null, }); setLoading(false); if (res.data?.id || res.id) { onSuccess(); } else { setErr(res.message || "Error al guardar."); } }; return (
e.stopPropagation()} style={{ maxWidth:420 }}>
Registrar labor · {loteName}
setFecha(e.target.value)} required />
setProducto(e.target.value)} placeholder="Ej: Roundup 2L/ha, Urea 150kg/ha..." />