'integer', 'estado' => 'boolean', ]; /** * Valores por defecto para atributos del modelo */ protected $attributes = [ 'imagen' => '', ]; public function variants(): HasMany { return $this->hasMany(ProductVariant::class); } public function categoria(): BelongsTo { return $this->belongsTo(Categoria::class); } /** * Relación many-to-many con bodegas */ public function bodegas(): BelongsToMany { return $this->belongsToMany(Bodega::class, 'producto_bodega') ->withPivot('stock') ->withTimestamps(); } /** * Obtiene las unidades de medida disponibles */ public static function getUnidadesMedida(): array { return [ 'unidad' => 'Unidad (1)', 'docena' => 'Docena (12)', ]; } /** * Obtiene el factor de conversión para una unidad de medida */ public static function getFactorConversion(string $unidad): int { $factores = [ 'unidad' => 1, 'docena' => 12, ]; return $factores[$unidad] ?? 1; } /** * Convierte una cantidad en la unidad especificada a unidades individuales */ public static function convertirAUnidades(float $cantidad, string $unidad): int { return (int) ($cantidad * self::getFactorConversion($unidad)); } /** * Convierte unidades individuales a la unidad de medida especificada */ public static function convertirDesdeUnidades(int $unidades, string $unidad): float { $factor = self::getFactorConversion($unidad); return $factor > 1 ? round($unidades / $factor, 2) : $unidades; } /** * Obtiene el stock en la unidad de medida configurada */ public function getStockEnUnidadMedida(): float { $stockTotal = $this->variants()->exists() ? $this->variants()->sum('stock') : $this->stock; return self::convertirDesdeUnidades($stockTotal, $this->unidad_medida ?? 'unidad'); } /** * Obtiene el nombre de la unidad de medida */ public function getNombreUnidadMedida(): string { $unidades = self::getUnidadesMedida(); return $unidades[$this->unidad_medida ?? 'unidad'] ?? 'Unidad (1)'; } /** * Obtener el stock total en todas las bodegas */ public function getStockTotalBodegas(): int { return $this->bodegas()->sum('producto_bodega.stock'); } /** * Obtener el stock en una bodega específica */ public function getStockEnBodega(int $bodegaId): int { $bodega = $this->bodegas()->where('bodega_id', $bodegaId)->first(); return $bodega ? $bodega->pivot->stock : 0; } /** * Obtener el stock total en una bodega específica (considerando variantes) */ public function getStockTotalEnBodega(int $bodegaId): int { if ($this->variants()->exists()) { $stockVariantes = 0; foreach ($this->variants as $variante) { $bodegaVariante = $variante->bodegas()->where('bodega_id', $bodegaId)->first(); if ($bodegaVariante) { $stockVariantes += $bodegaVariante->pivot->stock; } } return $stockVariantes; } else { return $this->getStockEnBodega($bodegaId); } } /** * Distribuir stock total entre bodegas */ public function distribuirStock(array $distribucion): void { foreach ($distribucion as $bodegaId => $stock) { $this->bodegas()->syncWithoutDetaching([ $bodegaId => ['stock' => $stock] ]); } } /** * Obtener el stock efectivo (considerando bodegas Y variantes correctamente) */ public function getStockEfectivo(): int { // Si tiene variantes, el stock se calcula desde las variantes if ($this->variants()->exists()) { $stockVariantes = 0; foreach ($this->variants as $variante) { $stockVariantes += $variante->getStockEfectivo(); } return $stockVariantes; } else { // Para productos sin variantes if ($this->bodegas()->exists()) { return $this->getStockTotalBodegas(); } else { return $this->stock; } } } /** * Accessor para código de barras dinámico */ public function getCodigoBarrasAttribute($value) { // Si el producto tiene variantes, el código de barras es NULL if ($this->variants()->exists()) { return null; } // Si NO tiene variantes, usa su código de barras propio return $value; } /** * Accessor para stock dinámico * Si el producto tiene variantes, el stock directo debe ser 0 */ public function getStockAttribute($value) { // Si el producto tiene variantes, el stock directo debe ser 0 if ($this->variants()->exists()) { return 0; } // Si NO tiene variantes, devuelve el stock real return $value; } /** * Mutator para stock - evita que se asigne stock directo si hay variantes */ public function setStockAttribute($value) { // Si el producto ya tiene variantes, no permitir stock directo if ($this->exists && $this->variants()->exists()) { $this->attributes['stock'] = 0; } else { $this->attributes['stock'] = $value; } } /** * Relación con detalles de compra */ public function detalleCompras(): HasMany { return $this->hasMany(DetalleCompra::class); } /** * Relación con detalles de venta */ public function detalleVentas(): HasMany { return $this->hasMany(DetalleVenta::class); } }