165 lines
6.9 KiB
PHP
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,
|
|
]);
|
|
});
|
|
}
|
|
}
|