Files
pos_heidiver/app/Models/InventarioPrenda.php
2026-02-09 15:02:56 -05:00

165 lines
6.9 KiB
PHP

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class InventarioPrenda extends Model
{
protected $fillable = [
'orden_produccion_id',
'cantidad_terminada',
'cantidad_disponible',
'fecha_ingreso',
'estado',
'producto_id',
];
public function ordenProduccion()
{
return $this->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 desde procesos de acabados: usar recepciones en lugar de cantidad_recibida
$totalRecibido = 0;
$procesosAcabados = \App\Models\ProcesoAcabado::where('orden_produccion_id', $inventario->orden_produccion_id)->get();
foreach ($procesosAcabados as $proceso) {
// Sumar recepciones efectivas (cantidad - defectuosas) menos reprocesos y cobros
$totalRecibido += $proceso->recibido_total ?? 0;
}
$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,
'categoria_id' => \App\Models\Categoria::first()?->id ?? \App\Models\Categoria::create(['nombre' => 'Generica'])->id,
]);
});
}
}