// ─── Reestructuración (2026-09-08) ───────────────────────────────────────────── // Corrige errores de captura de Compras: reasigna a qué proyecto pertenece una // OC, o a qué clave de insumo pertenece una de sus partidas, dejando un motivo // obligatorio con historial. Acceso restringido por defecto a Director de // Proyectos + Administrador (ver api/config.php ROLE_ACCESS / api/permisos.php). // No crea OCs ni toca montos/proveedor/fecha/pagos — solo estas 2 reasignaciones. // ── Sub-modal: cambiar el proyecto de la OC completa ────────────────────────── function CambiarProyectoModal({ open, onClose, oc, proyectos, onSaved }) { const [proyectoidNuevo, setProyectoidNuevo] = useState(''); const [motivo, setMotivo] = useState(''); const [saving, setSaving] = useState(false); useEffect(() => { if (open) { setProyectoidNuevo(''); setMotivo(''); } }, [open, oc]); async function guardar() { if (!proyectoidNuevo) { toast.error('Selecciona el proyecto nuevo'); return; } if (motivo.trim().length < 10) { toast.error('Escribe un motivo de al menos 10 caracteres'); return; } const nombreNuevo = (proyectos || []).find(p => p.id === Number(proyectoidNuevo))?.nombre || ''; if (!window.confirm(`¿Mover la OC #${fmtFolio(oc.folio)} de "${oc.proyecto_nombre}" a "${nombreNuevo}"?\n\nEsto cambia a qué presupuesto se le suma este gasto.`)) return; setSaving(true); try { await apiFetch(`reclasificacion.php?action=proyecto&ocid=${oc.id}`, 'PUT', { proyectoid_nuevo: Number(proyectoidNuevo), motivo: motivo.trim(), }); toast.success('Proyecto reasignado'); onSaved(); } catch (e) { toast.error(e.message); } finally { setSaving(false); } } if (!oc) return null; return ( }>