diff --git a/app/Http/Livewire/ShowPromociones.php b/app/Http/Livewire/ShowPromociones.php index 096146c..eaaa775 100755 --- a/app/Http/Livewire/ShowPromociones.php +++ b/app/Http/Livewire/ShowPromociones.php @@ -329,11 +329,28 @@ class ShowPromociones extends Component } if ($cuenta) { - $perfil = Historial_cuenta::where('cuenta_id', $cuenta->id)->max('perfil') ?? 0; + // Bloquear la fila y re-verificar conteo real antes de insertar (protección race condition) + $cuentaLocked = Cuentas::where('id', $cuenta->id)->lockForUpdate()->first(); + $perfil = Historial_cuenta::where('cuenta_id', $cuentaLocked->id)->max('perfil') ?? 0; + + $perfilesOcupados = Historial_cuenta::where('cuenta_id', $cuentaLocked->id) + ->whereHas('historial', fn($q) => $q->where('estado', 'activo')) + ->count(); + + $maxPerfiles = ($cuentaLocked->estado === 'pendiente') + ? $pantalla_servicio_completa + : $pantalla_servicio; + + if (($perfilesOcupados + (int) $pantallas_tarifa) > $maxPerfiles) { + $this->f[] = $servicio_id; + $this->alert('warning', 'No hay perfiles disponibles de ' . $tarifa->servicio->nombre . ', intenta nuevamente.', ['position' => 'top']); + continue; + } + for ($y = 0; $y < (int) $pantallas_tarifa; $y++) { $historial_cuenta = new Historial_cuenta(); $historial_cuenta->historial_id = $historial->id; - $historial_cuenta->cuenta_id = $cuenta->id; + $historial_cuenta->cuenta_id = $cuentaLocked->id; $historial_cuenta->perfil = (int) $perfil + (int) $y + 1; $historial_cuenta->save(); diff --git a/app/Http/Livewire/ShowServicios.php b/app/Http/Livewire/ShowServicios.php index aac0a19..bfebf61 100755 --- a/app/Http/Livewire/ShowServicios.php +++ b/app/Http/Livewire/ShowServicios.php @@ -394,19 +394,46 @@ class ShowServicios extends Component $esta_error = false; $cuentasMostar = ''; - //$perfil = $cuenta[$h]->historiales_count; - $perfil = Historial_cuenta::where('cuenta_id', $cuenta[$h]->id)->max('perfil') ?? 0; + // Bloquear la fila de la cuenta para leer el conteo real dentro de la transacción + $cuentaLocked = Cuentas::where('id', $cuenta[$h]->id)->lockForUpdate()->first(); + $perfil = Historial_cuenta::where('cuenta_id', $cuentaLocked->id)->max('perfil') ?? 0; + + // Verificar que aún hay espacio real ANTES de insertar (protección contra race condition) + $perfilesOcupados = Historial_cuenta::where('cuenta_id', $cuentaLocked->id) + ->whereHas('historial', fn($q) => $q->where('estado', 'activo')) + ->count(); + + $maxPerfiles = ($cuentaLocked->estado === 'pendiente') + ? ($cuentaLocked->servicio->completa ?? $pantallas) + : ($cuentaLocked->servicio->pantallas ?? $pantallas); + + if (($perfilesOcupados + (int) $pantallas) > $maxPerfiles) { + $esta_error = true; + $historial->update(['estado' => 'cancelado']); + Historial_cuenta::where('historial_id', $historial->id)->delete(); + $logsGeneral = new Log_general(); + $logsGeneral->user_id = Auth::user()->id; + $logsGeneral->detalle = 'El usuario ' . Auth::user()->name . ' - ' . Auth::user()->email . ' Tuvo error de concurrencia: la cuenta ' . $cuentaLocked->correo . ' ya estaba llena al momento de insertar el perfil. Compra cancelada automáticamente. Servicio: ' . $servicioNombre; + $logsGeneral->tipo = 'compra_error'; + $logsGeneral->historial_id = $historial->id; + $logsGeneral->save(); + DB::rollBack(); + $this->alert('warning', 'No hay perfiles disponibles, intenta nuevamente!', [ + 'position' => 'top' + ]); + return; + } for ($y = 0; $y < (int) $pantallas; $y++) { $historial_cuenta = new Historial_cuenta; - $historial_cuenta->cuenta_id = $cuenta[$h]->id; + $historial_cuenta->cuenta_id = $cuentaLocked->id; $historial_cuenta->historial_id = $historial->id; $historial_cuenta->perfil = (int) $perfil + (int) $y + 1; $historial_cuenta->save(); - if ($cuenta[$h]->correo != $cuentasMostar) { - $cuentasMostar .= $cuenta[$h]->correo . ' '; + if ($cuentaLocked->correo != $cuentasMostar) { + $cuentasMostar .= $cuentaLocked->correo . ' '; } } } else {