264 lines
6.8 KiB
PHP
264 lines
6.8 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Filament\Actions\Concerns\BelongsToGroup;
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
|
|
|
class Producto extends Model
|
|
{
|
|
use HasFactory;
|
|
|
|
/**
|
|
* The attributes that are mass assignable.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $fillable = [
|
|
'nombre',
|
|
'descripcion',
|
|
'codigo_barras',
|
|
'precio_compra',
|
|
'precio_venta',
|
|
'stock',
|
|
'categoria_id',
|
|
'estado',
|
|
'imagen',
|
|
'stock_minimo',
|
|
'stock_maximo',
|
|
'unidad_medida'
|
|
];
|
|
|
|
/**
|
|
* The attributes that should be cast to native types.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $casts = [
|
|
'id' => '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);
|
|
}
|
|
}
|