'datetime', 'cantidad' => 'integer', 'prendas_defectuosas' => 'integer', ]; public function referencia() { return $this->morphTo(); } public function usuario() { return $this->belongsTo(User::class, 'user_id'); } protected static function booted() { static::creating(function ($rec) { // Cantidad debe ser entero >= 1 $rec->cantidad = (int) $rec->cantidad; $rec->prendas_defectuosas = (int) ($rec->prendas_defectuosas ?? 0); if ($rec->cantidad <= 0) { throw ValidationException::withMessages(['cantidad' => 'La cantidad debe ser un entero mayor que 0.']); } if ($rec->prendas_defectuosas < 0) { throw ValidationException::withMessages(['prendas_defectuosas' => 'La cantidad de prendas defectuosas no puede ser negativa.']); } if ($rec->prendas_defectuosas > $rec->cantidad) { throw ValidationException::withMessages(['prendas_defectuosas' => 'Las prendas defectuosas no pueden superar la cantidad recibida.']); } // Fecha por defecto if (! $rec->fecha_recepcion) { $rec->fecha_recepcion = now(); } // Validación: no permitir sobrepasar la cantidad enviada en la referencia (considerando cobros) if ($rec->referencia) { $referencia = $rec->referencia; $enviada = (int) ($referencia->cantidad_enviada ?? 0); // Ya recibido efectivo (cantidad - defectuosos) - arreglos permiten re-recepción $yaRecibido = (int) \App\Models\Recepcion::where('referencia_type', get_class($referencia)) ->where('referencia_id', $referencia->id) ->sum(\DB::raw('cantidad - COALESCE(prendas_defectuosas, 0)')); // Los arreglos se restan porque permiten re-recepción cuando regresen $arreglos = 0; if (method_exists($referencia, 'ajustes')) { $arreglos = (int) $referencia->ajustes()->where('tipo', 'arreglo')->sum('cantidad'); } // Los cobros NO permiten más recepciones (son prendas que no llegaron) $cobros = 0; if (method_exists($referencia, 'cobros')) { $cobros = (int) $referencia->cobros()->sum('cantidad'); } $efectivo = $rec->cantidad - $rec->prendas_defectuosas; $yaRecibidoNeto = $yaRecibido - $arreglos; $maxPermitido = $enviada - $cobros; if (($yaRecibidoNeto + $efectivo) > $maxPermitido) { $disponible = max(0, $maxPermitido - $yaRecibidoNeto); throw ValidationException::withMessages([ 'cantidad' => "La recepción excede lo permitido. 📊 Resumen: • Enviadas: {$enviada} • Ya recibidas (neto): {$yaRecibidoNeto} • Cobros registrados: {$cobros} • Arreglos (permiten re-recepción): {$arreglos} • Disponible para recibir: {$disponible} ➡️ Solo puedes recibir {$disponible} prendas más." ]); } } // Set user if available if (! $rec->user_id && auth()->check()) { $rec->user_id = auth()->id(); } }); static::created(function ($rec) { $rec->syncParentAggregates(); // Crear un traslado parcial desde la referencia usando esta recepción if ($rec->referencia) { $referencia = $rec->referencia; $efectivo = (int) ($rec->cantidad - ($rec->prendas_defectuosas ?? 0)); $payload = [ 'cantidad_enviada' => $rec->cantidad, 'cantidad_recibida' => $efectivo, 'fecha_recepcion' => $rec->fecha_recepcion, 'notas' => 'Creado desde recepción #' . $rec->id . ($rec->notas ? (" - " . $rec->notas) : ''), 'prendas_defectuosas' => $rec->prendas_defectuosas ?? 0, ]; // Map destino según tipo de referencia if ($referencia instanceof Confeccion) { $origen = 'confeccion'; $destino = 'tintoreria'; } elseif ($referencia instanceof Tintoreria) { $origen = 'tintoreria'; $destino = 'acabados'; } else { $origen = 'referencia'; $destino = 'bodega'; } \App\Models\TrasladoPrenda::crearDesdeReferencia($referencia, $origen, $destino, $payload); // Avanzar OP por seguridad $referencia->ordenProduccion?->avanzarEstado(); } }); static::updated(function ($rec) { $rec->syncParentAggregates(); }); static::deleted(function ($rec) { $rec->syncParentAggregates(); }); } public function syncParentAggregates() { if (! $this->referencia) return; $referencia = $this->referencia; $sum = (int) static::where('referencia_type', get_class($referencia)) ->where('referencia_id', $referencia->id) ->sum(\DB::raw('cantidad - COALESCE(prendas_defectuosas, 0)')); $referencia->cantidad_recibida = $sum; $referencia->save(); } }