belongsTo(Tela::class); } public function traslados() { return $this->hasMany(\App\Models\TrasladoPrenda::class); } public function confeccions() { return $this->hasMany(\App\Models\Confeccion::class); } public function tintorerias() { return $this->hasMany(\App\Models\Tintoreria::class); } public function procesosAcabado() { return $this->hasMany(\App\Models\ProcesoAcabado::class); } public function inventarioPrenda() { return $this->hasMany(\App\Models\InventarioPrenda::class); } public function producto() { return $this->belongsTo(\App\Models\Producto::class, 'producto_id'); } public function avanzarEstado(): void { $flujo = [ 'en_corte', 'en_confeccion', 'en_tintoreria', 'en_acabados', 'finalizada', ]; $actual = array_search($this->estado, $flujo, true); if ($actual !== false && isset($flujo[$actual + 1])) { $this->update([ 'estado' => $flujo[$actual + 1], ]); // Si la orden llega a finalizada, crear entrada en inventario con lo que haya ingresado if ($this->estado === 'finalizada') { // Sumar traslados cuyo destino sea 'bodega' $cantidad = \App\Models\TrasladoPrenda::where('orden_produccion_id', $this->id) ->where('destino', 'bodega') ->sum('cantidad_recibida'); // Crear inventario solo si hay traslados a bodega con cantidad if ($cantidad > 0 && !\App\Models\InventarioPrenda::where('orden_produccion_id', $this->id)->exists()) { // Determinar producto asociado: preferir producto_id en la OP, sino crear/buscar por prenda_modelo $productoId = $this->producto_id; if (! $productoId) { $nombre = $this->prenda_modelo ?? $this->referencia ?? null; if ($nombre) { $producto = \App\Models\Producto::firstOrCreate( ['nombre' => $nombre], ['descripcion' => 'Creado automáticamente desde OrdenProduccion #' . $this->id, 'stock' => 0, 'estado' => true, 'precio_compra' => 0, 'precio_venta' => 0, 'unidad_medida' => 'unidad'] ); $productoId = $producto->id; // Guardar vínculo en la OP para futuras referencias $this->producto_id = $productoId; $this->save(); } } \App\Models\InventarioPrenda::create([ 'orden_produccion_id' => $this->id, 'cantidad_terminada' => $cantidad, 'cantidad_disponible' => $cantidad, 'fecha_ingreso' => now(), 'estado' => 'en_bodega', 'producto_id' => $productoId ?? null, ]); // Si se creó inventario, también actualizar stock del producto asociado (handler en InventarioPrenda lo hará) } } } } protected static function booted() { static::creating(function ($orden) { if (!$orden->numero_orden) { $ultimoId = self::max('id') + 1; $orden->numero_orden = 'OP-' . str_pad($ultimoId, 6, '0', STR_PAD_LEFT); } // Fecha de inicio por defecto hoy if (! isset($orden->fecha_inicio) || ! $orden->fecha_inicio) { $orden->fecha_inicio = now()->toDateString(); } }); // Validar y reservar tela al cambiar de estado a 'en_corte' static::updating(function ($orden) { // Validación: si se especifica tela y metros_requeridos, verificar disponibilidad if ($orden->tela_id && $orden->metros_requeridos !== null) { $tela = \App\Models\Tela::find($orden->tela_id); $originalMetros = $orden->getOriginal('metros_requeridos') ?? 0; $reservedAlready = $orden->tela_reservada ? $originalMetros : 0; $available = $tela ? ($tela->metros_disponibles + $reservedAlready) : null; if ($available !== null && $orden->metros_requeridos > $available) { throw \Illuminate\Validation\ValidationException::withMessages([ 'metros_requeridos' => 'No hay suficientes metros de tela disponibles. Disponibles: ' . ($available), ]); } } $originalEstado = $orden->getOriginal('estado'); $nuevoEstado = $orden->estado; // Reservar cuando pasamos a en_corte y no estaba reservado if ($originalEstado !== 'en_corte' && $nuevoEstado === 'en_corte' && ! $orden->tela_reservada) { if ($orden->tela_id && $orden->metros_requeridos > 0) { $tela = \App\Models\Tela::find($orden->tela_id); if (! $tela) { throw \Illuminate\Validation\ValidationException::withMessages([ 'tela_id' => 'No se encontró la tela asociada.', ]); } if ($tela->metros_disponibles < $orden->metros_requeridos) { throw \Illuminate\Validation\ValidationException::withMessages([ 'metros_requeridos' => 'No hay suficientes metros disponibles para reservar (' . $tela->metros_disponibles . ').', ]); } // Reservar: restar metros y marcar orden $tela->metros_disponibles = $tela->metros_disponibles - $orden->metros_requeridos; $tela->save(); $orden->tela_reservada = true; } } // Si retrocede desde en_corte y ya estaba reservado, devolver la tela if ($originalEstado === 'en_corte' && $nuevoEstado !== 'en_corte' && $orden->tela_reservada) { if ($orden->tela_id && $orden->metros_requeridos > 0) { $tela = \App\Models\Tela::find($orden->tela_id); if ($tela) { $tela->metros_disponibles = $tela->metros_disponibles + $orden->metros_requeridos; $tela->save(); } } $orden->tela_reservada = false; } }); } }