diff --git a/app/Http/Controllers/BLInventarioController.php b/app/Http/Controllers/BLInventarioController.php index e33f700..6f00934 100644 --- a/app/Http/Controllers/BLInventarioController.php +++ b/app/Http/Controllers/BLInventarioController.php @@ -2,13 +2,14 @@ namespace App\Http\Controllers; -use App\Models\BlEmpaque; + use App\Models\BlEstanteria; use App\Models\BlInventarioDetalle; use App\Models\BlProducto; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Log; use Inertia\Inertia; class BLInventarioController extends Controller @@ -24,41 +25,37 @@ public function index() { $productos = BlProducto::with([ 'color', - 'empaques.inventarioDetalle.zona.nivel.estanteria' + 'inventarioDetalle.zona.nivel.estanteria' // ✅ Relación directa ]) ->get() ->map(function ($producto) { // Obtener todas las ubicaciones donde está este producto $ubicaciones = []; $zonasUnicas = []; - $estanteriasUnicas = []; // NUEVO: para almacenar códigos de estanterías + $estanteriasUnicas = []; - foreach ($producto->empaques as $empaque) { - foreach ($empaque->inventarioDetalle as $inventario) { - if ($inventario->zona && $inventario->zona->nivel && $inventario->zona->nivel->estanteria) { - $estanteria = $inventario->zona->nivel->estanteria; - $nivel = $inventario->zona->nivel; - $zona = $inventario->zona; - - // Crear ubicación legible - $ubicacionLegible = $estanteria->nombre . ' - ' . - $nivel->nivel . ' - ' . - 'Zona ' . $zona->zona; - - $ubicaciones[] = $ubicacionLegible; - - // Guardar información de zona única - $zonasUnicas[] = [ - 'estanteria_nombre' => $estanteria->nombre, - 'estanteria_codigo' => $estanteria->codigo, // EST-04, RACK-04, etc. - 'nivel_nombre' => $nivel->nivel, - 'zona_nombre' => $zona->zona, - 'codigo_completo' => $zona->codigo_completo - ]; - - // NUEVO: Guardar código de estantería para el plano - $estanteriasUnicas[] = $estanteria->codigo; - } + // ✅ Ahora accedemos directamente desde el producto + foreach ($producto->inventarioDetalle as $inventario) { + if ($inventario->zona && $inventario->zona->nivel && $inventario->zona->nivel->estanteria) { + $estanteria = $inventario->zona->nivel->estanteria; + $nivel = $inventario->zona->nivel; + $zona = $inventario->zona; + + $ubicacionLegible = $estanteria->nombre . ' - ' . + $nivel->nivel . ' - ' . + 'Zona ' . $zona->zona; + + $ubicaciones[] = $ubicacionLegible; + + $zonasUnicas[] = [ + 'estanteria_nombre' => $estanteria->nombre, + 'estanteria_codigo' => $estanteria->codigo, + 'nivel_nombre' => $nivel->nivel, + 'zona_nombre' => $zona->zona, + 'codigo_completo' => $zona->codigo_completo + ]; + + $estanteriasUnicas[] = $estanteria->codigo; } } @@ -71,20 +68,101 @@ public function index() 'color_nombre' => $producto->color->nombre, 'descripcion' => $producto->descripcion, 'stock_total' => $producto->empaques - ->where('estado', 'disponible') - ->sum('cantidad_por_empaque'), + ->where('estado', 'disponible') + ->sum('cantidad_por_empaque'), 'estanteria' => $estanteria, 'tiene_ubicacion' => !empty($ubicaciones), 'ubicaciones_detalladas' => $zonasUnicas, 'estanterias' => array_unique(array_column($zonasUnicas, 'estanteria_nombre')), 'zonas_completas' => array_unique(array_column($zonasUnicas, 'codigo_completo')), - // NUEVO: Array de códigos de estantería para el plano 'estanterias_codigos' => array_unique($estanteriasUnicas), ]; }); + $estanterias = BlEstanteria::with('niveles.zonaNivel.inventarioDetalle')->get(); return Inertia::render('BLInventario', [ 'productos' => $productos, + 'estanterias' => $estanterias ]); } -} + public function store(Request $request) + { + try { + // Validar los datos recibidos + $validated = $request->validate([ + 'producto_id' => 'required|exists:bl_productos,id', + 'zona_id' => 'required|exists:bl_zonas_nivel,id', + 'cantidad_actual' => 'required|integer|min:1', + 'fecha_ubicacion' => 'required|date', + 'estado' => 'required|in:disponible,reservado,danado,caducado', + ]); + + Log::info('Datos validados:', $validated); + + // Buscar si ya existe el inventario en esa zona para ese producto + $inventarioExistente = BlInventarioDetalle::where('producto_id', $validated['producto_id']) + ->where('zona_id', $validated['zona_id']) + ->first(); + + Log::info('Inventario existente:', [$inventarioExistente]); + + if ($inventarioExistente) { + $inventarioExistente->update([ + 'cantidad_actual' => $validated['cantidad_actual'], + 'fecha_ubicacion' => $validated['fecha_ubicacion'], + 'estado' => $validated['estado'], + 'fecha_vencimiento' => $request->fecha_vencimiento, + 'notas' => $request->notas, + ]); + + $mensaje = 'Ubicación actualizada correctamente'; + } else { + // Crear nuevo inventario + Log::info('Insertando inventario nuevo:', $validated); + + BlInventarioDetalle::create([ + 'producto_id' => $validated['producto_id'], + 'zona_id' => $validated['zona_id'], + 'cantidad_actual' => $validated['cantidad_actual'], + 'fecha_ubicacion' => $validated['fecha_ubicacion'], + 'fecha_vencimiento' => $request->fecha_vencimiento, + 'estado' => $validated['estado'], + 'notas' => $request->notas, + ]); + + $mensaje = 'Producto ubicado correctamente en el almacén'; + } + + // Actualizar contador de la zona + $this->actualizarContadorZona($validated['zona_id']); + + return redirect()->back()->with([ + 'toast' => [ + 'type' => 'success', + 'message' => $mensaje, + ], + ]); + + } catch (\Exception $e) { + Log::error('Error en store inventario:', [$e->getMessage()]); + + return redirect()->back()->with([ + 'toast' => [ + 'type' => 'error', + 'message' => 'Error al guardar la ubicación: ' . $e->getMessage(), + ], + ]); + } + } + + // Método auxiliar para actualizar el contador de la zona + private function actualizarContadorZona($zonaId) + { + $totalProductos = BlInventarioDetalle::where('zona_id', $zonaId) + ->where('estado', 'disponible') + ->sum('cantidad_actual'); + DB::table('bl_zonas_nivel') + ->where('id', $zonaId) + ->update(['productos_actuales' => $totalProductos]); + } +} diff --git a/app/Http/Controllers/BLMarcacionController.php b/app/Http/Controllers/BLMarcacionController.php index 64f7a58..07ec02a 100644 --- a/app/Http/Controllers/BLMarcacionController.php +++ b/app/Http/Controllers/BLMarcacionController.php @@ -4,6 +4,7 @@ use App\Models\BLCliente; use App\Models\BlMarcacion; +use App\Models\BlMarcaciones; use App\Models\BLPedido; use App\Models\BLPedidoItem; use App\Models\User; @@ -48,7 +49,7 @@ public function store(Request $request) $data['costo_total'] = $data['cantidad'] * $data['precio_unitario']; // 1️⃣ Guardar la marcación - BlMarcacion::create($data); + BlMarcaciones::create($data); // 2️⃣ Actualizar estado del item a "en_proceso" DB::table('bl_pedido_items') @@ -61,7 +62,7 @@ public function store(Request $request) public function update(Request $request, $id) { - $registro = BlMarcacion::findOrFail($id); + $registro = BlMarcaciones::findOrFail($id); $request->validate([ 'cantidad' => 'required|integer|min:1', @@ -79,7 +80,7 @@ public function update(Request $request, $id) public function destroy($id) { - $registro = BlMarcacion::findOrFail($id); + $registro = BlMarcaciones::findOrFail($id); $registro->delete(); return response()->json([ diff --git a/app/Http/Controllers/BlCuentaCobroController.php b/app/Http/Controllers/BlCuentaCobroController.php index 6d1586c..73ec0c2 100644 --- a/app/Http/Controllers/BlCuentaCobroController.php +++ b/app/Http/Controllers/BlCuentaCobroController.php @@ -17,7 +17,7 @@ public function index() { // .producto $user = Auth::user(); - $cuentas = BlCuentaCobro::with(['itemsMarcacion.marcacion.pedido.items.empaque', 'usuario'])->get(); + $cuentas = BlCuentaCobro::with(['itemsMarcacion.marcacion.pedido.items.empaque.producto', 'usuario'])->get(); return Inertia::render('BLCuentaCobro', [ 'user' => $user, 'cuentasCobro' => $cuentas, diff --git a/app/Models/BlEmpaque.php b/app/Models/BlEmpaque.php index 78bdf62..e94b192 100644 --- a/app/Models/BlEmpaque.php +++ b/app/Models/BlEmpaque.php @@ -39,18 +39,8 @@ public function pedidoItems(): HasMany { return $this->hasMany(BLPedidoItem::class, 'empaque_id'); } - public function inventarioDetalle() - { - return $this->hasMany(BlInventarioDetalle::class, 'empaque_id'); - } - // Relación directa con posiciones a través de inventario_detalle - public function posiciones() - { - return $this->belongsToMany(BlPosicion::class, 'bl_inventario_detalle', 'empaque_id', 'posicion_id') - ->withPivot('cantidad_actual', 'estado', 'fecha_ubicacion') - ->withTimestamps(); - } + // Scope para empaques disponibles public function scopeDisponibles($query) @@ -58,33 +48,5 @@ public function scopeDisponibles($query) return $query->where('estado', 'disponible'); } - // Scope para empaques sin ubicación - public function scopeSinUbicacion($query) - { - return $query->whereDoesntHave('inventarioDetalle'); - } - - // Scope para empaques con ubicación - public function scopeConUbicacion($query) - { - return $query->whereHas('inventarioDetalle'); - } - - // Método para verificar si tiene ubicación - public function getTieneUbicacionAttribute() - { - return $this->inventarioDetalle()->exists(); - } - // Método para obtener la ubicación actual (si tiene) - public function getUbicacionActualAttribute() - { - return $this->inventarioDetalle()->first(); - } - - // Método para obtener la cantidad total en inventario - public function getCantidadEnInventarioAttribute() - { - return $this->inventarioDetalle()->sum('cantidad_actual'); - } } \ No newline at end of file diff --git a/app/Models/BlInventarioDetalle.php b/app/Models/BlInventarioDetalle.php index d32e460..10333f4 100644 --- a/app/Models/BlInventarioDetalle.php +++ b/app/Models/BlInventarioDetalle.php @@ -10,42 +10,32 @@ class BlInventarioDetalle extends Model use HasFactory; protected $table = 'bl_inventario_detalle'; + protected $fillable = [ - 'empaque_id', - 'zona_id', // Cambiado: posicion_id → zona_id - 'cantidad_actual', - 'fecha_ubicacion', - 'fecha_vencimiento', - 'estado', + 'producto_id', // 🔄 antes era empaque_id + 'zona_id', + 'cantidad_actual', + 'fecha_ubicacion', + 'fecha_vencimiento', + 'estado', 'notas' ]; protected $dates = ['fecha_ubicacion', 'fecha_vencimiento']; - // Relación con empaque - public function empaque() + // Relación directa con producto (nuevo) + public function producto() { - return $this->belongsTo(BlEmpaque::class, 'empaque_id'); + return $this->belongsTo(BlProducto::class, 'producto_id'); } - // NUEVA RELACIÓN con zona (reemplaza posicion) + // Relación con zona (reemplaza posición) public function zona() { return $this->belongsTo(BlZonaNivel::class, 'zona_id'); } - // Relación con producto (a través de empaque) - public function producto() - { - return $this->hasOneThrough( - BlProducto::class, - BlEmpaque::class, - 'id', // Foreign key on bl_empaques table - 'id', // Foreign key on bl_productos table - 'empaque_id', // Local key on bl_inventario_detalle table - 'producto_id' // Local key on bl_empaques table - ); - } + // 🔥 Elimino la relación con empaque porque ya no aplica // Scopes útiles ACTUALIZADOS public function scopeDisponibles($query) @@ -67,9 +57,7 @@ public function scopeEnZona($query, $zonaId) public function scopeConProducto($query, $productoId) { - return $query->whereHas('empaque', function($q) use ($productoId) { - $q->where('producto_id', $productoId); - }); + return $query->where('producto_id', $productoId); } // Nuevo scope para buscar por código de zona @@ -84,6 +72,6 @@ public function scopeEnZonaCodigo($query, $codigoZona) public function scopeProximosAVencer($query, $dias = 30) { return $query->whereDate('fecha_vencimiento', '<=', now()->addDays($dias)) - ->where('estado', 'disponible'); + ->where('estado', 'disponible'); } -} \ No newline at end of file +} diff --git a/app/Models/BlNivelEstanteria.php b/app/Models/BlNivelEstanteria.php index 6f1a29d..fe2ce6f 100644 --- a/app/Models/BlNivelEstanteria.php +++ b/app/Models/BlNivelEstanteria.php @@ -19,9 +19,9 @@ public function estanteria() } // Relación con posiciones - public function posiciones() + public function zonaNivel() { - return $this->hasMany(BlPosicion::class, 'nivel_id'); + return $this->hasMany(BlZonaNivel::class, 'nivel_id'); } // Relación con inventario detalle (a través de posiciones) diff --git a/app/Models/BlProducto.php b/app/Models/BlProducto.php index a456bde..ce058bc 100644 --- a/app/Models/BlProducto.php +++ b/app/Models/BlProducto.php @@ -23,6 +23,12 @@ public function empaques(): HasMany return $this->hasMany(BlEmpaque::class, 'producto_id'); } + // ✅ Relación DIRECTA con inventarioDetalle (usando producto_id) + public function inventarioDetalle(): HasMany + { + return $this->hasMany(BlInventarioDetalle::class, 'producto_id'); + } + // Accesor: Descripción automática (ej: "BT 20MM Dorado") public function getDescripcionAttribute(): string { diff --git a/database/migrations/2025_09_28_152407_drop_bl_posiciones_table.php b/database/migrations/2025_09_28_152407_drop_bl_posiciones_table.php index 22de0f6..fca74d2 100644 --- a/database/migrations/2025_09_28_152407_drop_bl_posiciones_table.php +++ b/database/migrations/2025_09_28_152407_drop_bl_posiciones_table.php @@ -8,13 +8,20 @@ { public function up(): void { - // Eliminar la tabla de manera segura + // 1. Eliminar la foreign key en bl_inventario_detalle + Schema::table('bl_inventario_detalle', function (Blueprint $table) { + // Si se creó con foreignId('posicion_id')->constrained('bl_posiciones') + // Laravel por defecto crea la llave con el nombre: bl_inventario_detalle_posicion_id_foreign + $table->dropForeign(['posicion_id']); + }); + + // 2. Ahora sí eliminar la tabla Schema::dropIfExists('bl_posiciones'); } public function down(): void { - // En caso de rollback, recrea la tabla si es necesario + // 1. Restaurar la tabla Schema::create('bl_posiciones', function (Blueprint $table) { $table->id(); $table->foreignId('nivel_id')->constrained('bl_niveles_estanteria'); @@ -28,5 +35,13 @@ public function down(): void $table->boolean('activa')->default(true); $table->timestamps(); }); + + // 2. Restaurar la foreign key en bl_inventario_detalle + Schema::table('bl_inventario_detalle', function (Blueprint $table) { + $table->foreign('posicion_id') + ->references('id') + ->on('bl_posiciones') + ->onDelete('cascade'); + }); } }; diff --git a/database/migrations/2025_09_28_153005_change_posicion_to_zona_in_inventario_detalle.php b/database/migrations/2025_09_28_153005_change_posicion_to_zona_in_inventario_detalle.php index 447d164..3c8b8c3 100644 --- a/database/migrations/2025_09_28_153005_change_posicion_to_zona_in_inventario_detalle.php +++ b/database/migrations/2025_09_28_153005_change_posicion_to_zona_in_inventario_detalle.php @@ -12,7 +12,7 @@ public function up(): void // 1️⃣ Crear la nueva tabla temporal con la estructura correcta Schema::create('bl_inventario_detalle_new', function (Blueprint $table) { $table->id(); - $table->foreignId('empaque_id')->constrained('bl_empaques'); + $table->foreignId('producto_id')->constrained('bl_productos'); $table->foreignId('zona_id')->constrained('bl_zonas_nivel'); // NUEVO CAMPO $table->integer('cantidad_actual')->default(0); $table->date('fecha_ubicacion'); @@ -21,7 +21,7 @@ public function up(): void $table->text('notas')->nullable(); $table->timestamps(); - $table->unique(['empaque_id', 'zona_id']); + $table->unique(['producto_id', 'zona_id']); $table->index(['zona_id', 'estado']); $table->index('fecha_vencimiento'); }); @@ -29,11 +29,11 @@ public function up(): void // 2️⃣ Copiar los datos desde la tabla antigua // Aquí asumimos que existe un mapeo directo posicón_id → zona_id // Si necesitas un mapeo distinto, ajusta el SELECT - DB::statement(' - INSERT INTO bl_inventario_detalle_new (id, empaque_id, zona_id, cantidad_actual, fecha_ubicacion, fecha_vencimiento, estado, notas, created_at, updated_at) - SELECT id, empaque_id, posicion_id, cantidad_actual, fecha_ubicacion, fecha_vencimiento, estado, notas, created_at, updated_at - FROM bl_inventario_detalle - '); + // DB::statement(' + // INSERT INTO bl_inventario_detalle_new (id, empaque_id, zona_id, cantidad_actual, fecha_ubicacion, fecha_vencimiento, estado, notas, created_at, updated_at) + // SELECT id, empaque_id, posicion_id, cantidad_actual, fecha_ubicacion, fecha_vencimiento, estado, notas, created_at, updated_at + // FROM bl_inventario_detalle + // '); // 3️⃣ Eliminar la tabla antigua Schema::dropIfExists('bl_inventario_detalle'); @@ -47,7 +47,7 @@ public function down(): void // Revertir: volver a la tabla con posicion_id Schema::create('bl_inventario_detalle_old', function (Blueprint $table) { $table->id(); - $table->foreignId('empaque_id')->constrained('bl_empaques'); + $table->foreignId('producto_id')->constrained('bl_productos'); $table->foreignId('posicion_id')->constrained('bl_posiciones'); $table->integer('cantidad_actual')->default(0); $table->date('fecha_ubicacion'); @@ -56,15 +56,15 @@ public function down(): void $table->text('notas')->nullable(); $table->timestamps(); - $table->unique(['empaque_id', 'posicion_id']); + $table->unique(['producto_id', 'posicion_id']); $table->index(['posicion_id', 'estado']); }); - DB::statement(' - INSERT INTO bl_inventario_detalle_old (id, empaque_id, posicion_id, cantidad_actual, fecha_ubicacion, fecha_vencimiento, estado, notas, created_at, updated_at) - SELECT id, empaque_id, zona_id, cantidad_actual, fecha_ubicacion, fecha_vencimiento, estado, notas, created_at, updated_at - FROM bl_inventario_detalle - '); + // DB::statement(' + // INSERT INTO bl_inventario_detalle_old (id, empaque_id, posicion_id, cantidad_actual, fecha_ubicacion, fecha_vencimiento, estado, notas, created_at, updated_at) + // SELECT id, empaque_id, zona_id, cantidad_actual, fecha_ubicacion, fecha_vencimiento, estado, notas, created_at, updated_at + // FROM bl_inventario_detalle + // '); Schema::dropIfExists('bl_inventario_detalle'); Schema::rename('bl_inventario_detalle_old', 'bl_inventario_detalle'); diff --git a/database/seeders/BlColoresSeeder.php b/database/seeders/BlColoresSeeder.php deleted file mode 100644 index e47e9ee..0000000 --- a/database/seeders/BlColoresSeeder.php +++ /dev/null @@ -1,25 +0,0 @@ - 'Z6', 'nombre' => 'Dorado'], - ['codigo' => 'A3', 'nombre' => 'Plateado'], - ['codigo' => 'R2', 'nombre' => 'Rojo'], - ['codigo' => 'N1', 'nombre' => 'Negro'], - ['codigo' => 'B5', 'nombre' => 'Azul'], - ]; - - foreach ($colores as $color) { - BlColor::create($color); - } - } -} \ No newline at end of file diff --git a/database/seeders/BlEmpaquesSeeder.php b/database/seeders/BlEmpaquesSeeder.php deleted file mode 100644 index 58e50a6..0000000 --- a/database/seeders/BlEmpaquesSeeder.php +++ /dev/null @@ -1,31 +0,0 @@ - 1, // Botón Dorado 20MM - 'codigo_unico' => 'BT-1287', - 'cantidad_por_empaque' => 500, - 'codigo_barras' => '123456789', - ], - [ - 'producto_id' => 2, // Botón Plateado 15MM - 'codigo_unico' => 'BT-1288', - 'cantidad_por_empaque' => 300, - 'codigo_barras' => '987654321', - ], - ]; - - foreach ($empaques as $empaque) { - BlEmpaque::create($empaque); - } - } -} \ No newline at end of file diff --git a/database/seeders/BlMovimientosSeeder.php b/database/seeders/BlMovimientosSeeder.php deleted file mode 100644 index cb1d0df..0000000 --- a/database/seeders/BlMovimientosSeeder.php +++ /dev/null @@ -1,34 +0,0 @@ - 1, // BT-1287 - 'tipo' => 'entrada', - 'cantidad' => 3, // 3 empaques de 500 unidades - 'motivo' => 'compra', - 'usuario_id' => 1, // Asegúrate que exista - 'created_at' => now() - ], - [ - 'empaque_id' => 2, // BT-1288 - 'tipo' => 'entrada', - 'cantidad' => 2, // 2 empaques de 300 unidades - 'motivo' => 'compra', - 'usuario_id' => 1, - 'created_at' => now() - ] - ]; - - foreach ($movimientos as $movimiento) { - BlMovimiento::create($movimiento); - } - } -} \ No newline at end of file diff --git a/database/seeders/BlProductosSeeder.php b/database/seeders/BlProductosSeeder.php deleted file mode 100644 index 76d3d6f..0000000 --- a/database/seeders/BlProductosSeeder.php +++ /dev/null @@ -1,30 +0,0 @@ - 'BT', - 'tamanio' => '20MM', - 'color_id' => 1, // Dorado (Z6) - 'descripcion' => 'Botón metálico dorado 20MM', // Añade esto - ], - [ - 'tipo_producto' => 'BT', - 'tamanio' => '15MM', - 'color_id' => 2, // Plateado (A3) - 'descripcion' => 'Botón metálico plateado 15MM', // Añade esto - ], - ]; - - foreach ($productos as $producto) { - BlProducto::create($producto); - } - } -} \ No newline at end of file diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php deleted file mode 100644 index 2acc065..0000000 --- a/database/seeders/DatabaseSeeder.php +++ /dev/null @@ -1,31 +0,0 @@ -call([ - BlColoresSeeder::class, - BlProductosSeeder::class, - BlEmpaquesSeeder::class, - BlMovimientosSeeder::class, - ]); - } -} diff --git a/resources/js/components/BL/ListaInventario.jsx b/resources/js/components/BL/ListaInventario.jsx index a1b7fad..1e45af8 100644 --- a/resources/js/components/BL/ListaInventario.jsx +++ b/resources/js/components/BL/ListaInventario.jsx @@ -1,5 +1,10 @@ // resources/js/Components/ListaInventario.jsx -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; // Agregar useEffect +import AgentModalWrapper from '../agentsModalWrapper'; +import ModalUbicacion from './modalUBicacion'; +import { router } from '@inertiajs/react'; +import { Toast } from "flowbite-react"; +import { HiCheck, HiX } from "react-icons/hi"; const ListaInventario = ({ productos, @@ -12,16 +17,104 @@ const ListaInventario = ({ onBusquedaChange, onCategoriaChange, onLimpiarFiltros, - onEstanteriaClick + onEstanteriaClick, + estanterias }) => { console.log(productosFiltrados); const [expandida, setExpandida] = useState(true); + const [productoSeleccionado, setProductoSeleccionado] = useState(null); + const [modalUbicacionOpen, setModalUbicacionOpen] = useState(false); + const [toast, setToast] = useState({ + show: false, + success: false, + message: "", + }); + console.log(productosFiltrados); + + + useEffect(() => { + if (toast.show) { + const timer = setTimeout(() => { + setToast({ show: false, success: false, message: '' }); + }, 4000); + return () => clearTimeout(timer); + } + }, [toast]); + const toggleExpandida = () => { setExpandida(!expandida); }; + const openModalUbicacion = (producto) => { + setProductoSeleccionado(producto); + setModalUbicacionOpen(true); + }; + + const closeModalUbicacion = () => { + setModalUbicacionOpen(false); + setProductoSeleccionado(null); + }; + + const handleGuardarUbicacion = async (productoId, ubicacion) => { + try { + console.log('Guardando ubicación:', { productoId, ubicacion }); + + // Preparar datos para enviar + const datos = { + producto_id: productoId, + zona_id: ubicacion.zona_id, + cantidad_actual: productoSeleccionado.stock_total, + fecha_ubicacion: new Date().toISOString().split('T')[0], + estado: 'disponible', + // Incluir información adicional si es necesario + estanteria_nombre: ubicacion.estanteria_nombre, + nivel_nombre: ubicacion.nivel_nombre, + zona_nombre: ubicacion.zona_nombre + }; + + router.post(route('inventarioBL.store'), datos, { // Enviar datos como objeto + preserveState: true, + onSuccess: () => { + setToast({ + show: true, + success: true, + message: "Producto organizado correctamente" + }); + closeModalUbicacion(); + }, + onError: (errors) => { + const primerError = Object.values(errors)[0]; + setToast({ + show: true, + success: false, + message: primerError || "Error al organizar el producto" + }); + }, + onFinish: (visit) => { + // Si hubo error de servidor (status 500 o más) + if (visit?.response?.status >= 500) { + const msg = visit?.response?.data?.message || "Error interno del servidor"; + setToast({ + show: true, + success: false, + message: msg + }); + } + } + }); + + } catch (error) { + console.error('Error al guardar ubicación:', error); + setToast({ + show: true, + success: false, + message: "Error al guardar la ubicación" + }); + } + }; + // Si está contraída, mostrar solo un ícono pequeño if (!expandida) { return ( @@ -42,146 +135,178 @@ const ListaInventario = ({ // Si está expandida, mostrar la lista completa return ( -