131 lines
3.1 KiB
PHP
131 lines
3.1 KiB
PHP
<?php namespace App\Models;
|
|
/**
|
|
* @copyright (c) 2025 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
|
|
*/
|
|
|
|
|
|
use App\Services\InventoryMovementService;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
|
|
/**
|
|
* Modelo de Inventario (Catálogo de productos)
|
|
*
|
|
* El stock NO vive aquí, vive en inventory_warehouse
|
|
*
|
|
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
|
|
*
|
|
* @version 1.0.0
|
|
*/
|
|
class Inventory extends Model
|
|
{
|
|
protected $fillable = [
|
|
'category_id',
|
|
'unit_of_measure_id',
|
|
'name',
|
|
'key_sat',
|
|
'sku',
|
|
'barcode',
|
|
'track_serials',
|
|
'is_active',
|
|
];
|
|
|
|
protected $casts = [
|
|
'is_active' => 'boolean',
|
|
'track_serials' => 'boolean',
|
|
];
|
|
|
|
protected $appends = ['has_serials', 'inventory_value', 'stock'];
|
|
|
|
public function warehouses()
|
|
{
|
|
return $this->belongsToMany(Warehouse::class, 'inventory_warehouse')
|
|
->withPivot('stock', 'min_stock', 'max_stock')
|
|
->withTimestamps();
|
|
}
|
|
|
|
/**
|
|
* Stock total en todos los almacenes (ahora soporta decimales)
|
|
*/
|
|
public function getStockAttribute(): float
|
|
{
|
|
return (float) $this->warehouses()->sum('inventory_warehouse.stock');
|
|
}
|
|
|
|
/**
|
|
* Alias para compatibilidad
|
|
*/
|
|
public function getTotalStockAttribute(): float
|
|
{
|
|
return $this->stock;
|
|
}
|
|
|
|
/**
|
|
* Stock en un almacén específico (ahora soporta decimales)
|
|
*/
|
|
public function stockInWarehouse(int $warehouseId): float
|
|
{
|
|
return (float) ($this->warehouses()
|
|
->where('warehouse_id', $warehouseId)
|
|
->value('inventory_warehouse.stock') ?? 0);
|
|
}
|
|
|
|
public function category()
|
|
{
|
|
return $this->belongsTo(Category::class);
|
|
}
|
|
|
|
public function unitOfMeasure()
|
|
{
|
|
return $this->belongsTo(UnitOfMeasurement::class);
|
|
}
|
|
|
|
public function price()
|
|
{
|
|
return $this->hasOne(Price::class);
|
|
}
|
|
|
|
public function serials()
|
|
{
|
|
return $this->hasMany(InventorySerial::class);
|
|
}
|
|
|
|
/**
|
|
* Obtener seriales disponibles
|
|
*/
|
|
public function availableSerials()
|
|
{
|
|
return $this->hasMany(InventorySerial::class)
|
|
->where('status', 'disponible');
|
|
}
|
|
|
|
/**
|
|
* Stock basado en seriales disponibles (para productos con track_serials)
|
|
*/
|
|
public function getAvailableStockAttribute(): int
|
|
{
|
|
return $this->availableSerials()->count();
|
|
}
|
|
|
|
public function getHasSerialsAttribute(): bool
|
|
{
|
|
return isset($this->attributes['serials_count']) && $this->attributes['serials_count'] > 0;
|
|
}
|
|
|
|
/**
|
|
* Valor total del inventario (stock * costo)
|
|
*/
|
|
public function getInventoryValueAttribute(): float
|
|
{
|
|
return $this->stock * ($this->price?->cost ?? 0);
|
|
}
|
|
|
|
/**
|
|
* Sincronizar stock basado en seriales disponibles
|
|
* Delega al servicio para mantener la lógica centralizada
|
|
*/
|
|
public function syncStock(): void
|
|
{
|
|
app(InventoryMovementService::class)->syncStockFromSerials($this);
|
|
}
|
|
}
|