
# Cancelación de flujos de trabajo

Los flujos de trabajo se pueden cancelar mediante programación o a través de CLI. Cuando se cancela un flujo de trabajo, Queuety establece su estado en `cancelled`, entierra las tareas pendientes o en proceso y ejecuta un controlador de limpieza opcional.

## Registrar un controlador de limpieza

Use `on_cancel()` en el generador de flujos de trabajo para registrar una clase que se ejecute cuando se cancele el flujo. La clase controladora debe implementar un método `handle( array $state ): void`.

```php
use Queuety\Queuety;

Queuety::workflow( 'import_data' )
    ->then( FetchDataHandler::class )
    ->then( TransformDataHandler::class )
    ->then( LoadDataHandler::class )
    ->on_cancel( ImportCleanupHandler::class )
    ->dispatch( [ 'source_url' => $url ] );
```

El controlador de limpieza recibe el estado público del flujo de trabajo (todas las claves que no empiezan con `_`):

```php
class ImportCleanupHandler {

    public function handle( array $state ): void {
        // Clean up any partial imports.
        if ( isset( $state['temp_table'] ) ) {
            global $wpdb;
            $wpdb->query( "DROP TABLE IF EXISTS {$state['temp_table']}" );
        }

        // Notify the user.
        wp_mail(
            $state['notify_email'] ?? '',
            'Import cancelled',
            'Your data import was cancelled.'
        );
    }
}
```

## Cancelar mediante PHP

Llame a `Queuety::cancel_workflow()` con el ID del flujo de trabajo:

```php
Queuety::cancel_workflow( $workflow_id );
```

Este método:

1. Bloquea la fila del flujo de trabajo
2. Establece el estado del flujo de trabajo en `cancelled`
3. Entierra todas las tareas pendientes y en proceso del flujo de trabajo con el mensaje "Workflow cancelled"
4. Registra un evento `workflow_cancelled` y confirma la transacción
5. Ejecuta las compensaciones de los pasos completados en orden inverso (si se registraron mediante `compensate_with()`)
6. Ejecuta el controlador de limpieza `on_cancel` (si se registró)

Se lanza un `RuntimeException` si el flujo de trabajo ya está `completed` o `cancelled`.

## Cancelar mediante CLI

```bash
wp queuety workflow cancel <id>
```

| Opción | Descripción |
|---|---|
| `<id>` | ID del flujo de trabajo que se cancelará (obligatorio) |

```bash
wp queuety workflow cancel 42
```

## Qué ocurre al cancelar

Cuando se cancela un flujo de trabajo:

- **El estado** cambia a `cancelled`
- **Las compensaciones de pasos** se ejecutan en orden inverso al de finalización si se añadieron con `compensate_with()`
- **El controlador de limpieza** se ejecuta después de que el flujo de trabajo se haya marcado como `cancelled`
- **Las tareas pendientes** del flujo de trabajo se entierran de inmediato
- **Las tareas en proceso** del flujo de trabajo se entierran; cuando termina el controlador que está en curso, se descarta su salida y el flujo de trabajo no avanza
- **Las tareas completadas** de pasos anteriores se mantienen sin cambios
- **Se registra una entrada de registro** con el evento `workflow_cancelled`

## Cancelado frente a fallido

| Aspecto | Fallido | Cancelado |
|---|---|---|
| Activador | Un paso agota sus intentos de reintento | Llamada explícita a `cancel_workflow()` |
| Estado | `failed` | `cancelled` |
| Se puede reintentar | Sí, mediante `retry_workflow()`, salvo que el flujo de trabajo ya se haya compensado | No |
| Controlador de limpieza | No se llama | Se llama si se registró mediante `on_cancel()` |
| Evento de registro | `workflow_failed` | `workflow_cancelled` |

## Ejemplo: cancelar con confirmación

Un patrón habitual consiste en comprobar el estado del flujo de trabajo antes de cancelarlo:

```php
$status = Queuety::workflow_status( $workflow_id );

if ( null === $status ) {
    throw new \RuntimeException( 'Workflow not found.' );
}

if ( 'running' === $status->status->value || 'paused' === $status->status->value ) {
    Queuety::cancel_workflow( $workflow_id );
}
```
