belongsTo(OrdenProduccion::class); } public function producto() { return $this->belongsTo(\App\Models\Producto::class, 'producto_id'); } public function distribuciones() { return $this->hasMany(\App\Models\InventarioPrendaDistribucion::class, 'inventario_prenda_id'); } protected static function booted() { static::creating(function ($inventario) { // Sincronizar producto desde la Orden de Producción si no se especificó if ($inventario->orden_produccion_id && ! $inventario->producto_id) { $op = \App\Models\OrdenProduccion::find($inventario->orden_produccion_id); if ($op && $op->producto_id) { $inventario->producto_id = $op->producto_id; } } // Validar conflicto: si el OP tiene producto y difiere del seleccionado, bloquear if ($inventario->producto_id && $inventario->orden_produccion_id) { $op = \App\Models\OrdenProduccion::find($inventario->orden_produccion_id); if ($op && $op->producto_id && $op->producto_id != $inventario->producto_id) { throw \Illuminate\Validation\ValidationException::withMessages([ 'producto_id' => 'El producto seleccionado difiere del producto asociado a la Orden de Producción (fuente de verdad).', ]); } } // Validación y auto-filling: la cantidad terminada se calcula automáticamente desde lo recibido para esta OP (traslados a bodega) if ($inventario->orden_produccion_id) { // Total recibido considerando procesos de acabados que se han recibido $totalRecibido = \App\Models\ProcesoAcabado::where('orden_produccion_id', $inventario->orden_produccion_id) ->whereNotNull('fecha_recepcion') ->sum('cantidad_recibida'); $assigned = \App\Models\InventarioPrenda::where('orden_produccion_id', $inventario->orden_produccion_id)->sum('cantidad_terminada'); $available = max(0, (int) $totalRecibido - (int) $assigned); // Si no se proporcionó cantidad_terminada, asignar automáticamente lo disponible if (! isset($inventario->cantidad_terminada) || (int) $inventario->cantidad_terminada <= 0) { $inventario->cantidad_terminada = $available; } if ((int) $inventario->cantidad_terminada > $available) { throw \Illuminate\Validation\ValidationException::withMessages([ 'cantidad_terminada' => 'No se puede registrar más cantidad terminada de la que está disponible desde acabados/traslados (' . $available . ').', ]); } if ((int) $inventario->cantidad_terminada <= 0) { throw \Illuminate\Validation\ValidationException::withMessages([ 'cantidad_terminada' => 'No hay cantidad terminada disponible para esta Orden de Producción.', ]); } } // Fecha de ingreso por defecto a hoy si no se proporcionó if (! isset($inventario->fecha_ingreso) || ! $inventario->fecha_ingreso) { $inventario->fecha_ingreso = now()->toDateString(); } // Al ingresar al inventario, todo está disponible $inventario->cantidad_disponible = $inventario->cantidad_terminada; // Estado automático inicial $inventario->estado = 'en_bodega'; }); // Al crear inventario de prenda, alimentar (incrementar o crear) el stock de Producto static::created(function ($inventario) { if (! $inventario->cantidad_terminada || $inventario->cantidad_terminada <= 0) { return; } $op = $inventario->ordenProduccion; // Priorizar producto explícito si fue seleccionado if ($inventario->producto_id) { $producto = \App\Models\Producto::find($inventario->producto_id); if ($producto && ! $producto->variants()->exists()) { $producto->increment('stock', $inventario->cantidad_terminada); } return; } if (! $op) { return; } // Intentar emparejar producto por nombre de prenda_modelo o referencia $nombre = $op->prenda_modelo ?? $op->referencia ?? null; if (! $nombre) { return; } $producto = \App\Models\Producto::where('nombre', $nombre)->first(); // Si existe y no tiene variantes, incrementar stock if ($producto) { if (! $producto->variants()->exists()) { // Si el producto usa bodegas, sumar al stock directo (simplificado) $producto->increment('stock', $inventario->cantidad_terminada); } else { // Tiene variantes: no hacemos cambios automáticos aquí (se requiere asignación manual) // Podríamos implementar reglas adicionales si se definieran las variantes en la OP } return; } // Si no existe el producto, crearlo automáticamente con stock inicial $codigo = 'AUTOP-' . $op->id . '-' . time(); $codigo = substr($codigo, 0, 50); // Asegurar unicidad del codigo_barras while (\App\Models\Producto::where('codigo_barras', $codigo)->exists()) { $codigo .= '-' . rand(0, 9); $codigo = substr($codigo, 0, 50); } \App\Models\Producto::create([ 'nombre' => $nombre, 'descripcion' => 'Creado automáticamente desde InventarioPrenda (OP #' . $op->id . ')', 'stock' => $inventario->cantidad_terminada, 'estado' => true, 'precio_compra' => 0, 'precio_venta' => 0, 'unidad_medida' => 'unidad', 'codigo_barras' => $codigo, ]); }); } }