---
name: shopify-limpiar-precios-comparacion
description: "Borra los precios de comparación (precio tachado) que ya no son un descuento real, porque el precio actual es igual o mayor, o que llevan puestos más tiempo del límite que fijes. Úsala cuando quieras que tus precios tachados muestren solo ahorros verdaderos."
---

## Propósito
Identifica las variantes con un `compareAtPrice` que ya no representa un descuento real y lo borra, para que el precio tachado de la tienda en línea muestre ahorros verdaderos y no restos de precios viejos. Marca dos casos: (a) `compareAtPrice <= price` (no hay descuento; suele quedar después de una subida de precio) y (b) `compareAtPrice` puesto durante más de `max_age_days` (la imagen de «siempre en oferta», que con el tiempo daña la percepción de precio y en algunos países puede violar las normas de publicidad). Por defecto trabaja en modo simulación.

## Requisitos
- Sesión autenticada de Shopify CLI: `shopify store auth --store <tu-tienda>.myshopify.com --scopes read_products,write_products`
- Las consultas se ejecutan con `shopify store execute`.
- Permisos (scopes) de API: `read_products`, `write_products`

## Parámetros

| Parámetro | Tipo | Obligatorio | Predeterminado | Descripción |
|-----------|------|-------------|----------------|-------------|
| store | string | sí | — | Dominio de la tienda (p. ej., mitienda.myshopify.com) |
| dry_run | bool | no | true | Muestra la limpieza sin ejecutar las mutaciones |
| clean_no_discount | bool | no | true | Borra `compareAtPrice` cuando `price >= compareAtPrice` |
| clean_stale | bool | no | true | Borra `compareAtPrice` cuando lleva puesto más de `max_age_days` |
| max_age_days | integer | no | 90 | Límite de antigüedad en días para la regla de precios viejos (usa el `updatedAt` de la variante como referencia aproximada) |
| collection_id | string | no | — | GID de colección opcional para limitar la limpieza |
| tag_filter | string | no | — | Etiqueta de producto opcional para limitar la limpieza |
| format | string | no | human | Formato de salida: `human` o `json` |

## Seguridad

> ⚠️ El paso 2 ejecuta mutaciones `productVariantsBulkUpdate` que dejan `compareAtPrice` en null. Shopify no guarda el valor tachado original: guarda el CSV de la simulación antes de aplicar los cambios si quieres poder restaurarlo. Empieza siempre con `dry_run: true` y revisa el CSV antes de ejecutar con `dry_run: false`.

## Pasos

1. **OPERACIÓN:** `productVariants` — consulta
   **Entradas:** `first: 250`, `query: <armada a partir de collection_id o tag_filter>`, selecciona `price`, `compareAtPrice`, `updatedAt`, `product { id, title, vendor }`, `sku` y el cursor de paginación
   **Resultado esperado:** Todas las variantes del alcance con su precio actual; pagina hasta que `hasNextPage: false`

2. Filtra las variantes que cumplen alguna de las reglas: `clean_no_discount` Y `compareAtPrice != null` Y `parseFloat(compareAtPrice) <= parseFloat(price)`, O `clean_stale` Y `compareAtPrice != null` Y `(now - updatedAt) > max_age_days`. Agrupa por `product.id` para hacer la mutación en lotes.

3. **OPERACIÓN:** `productVariantsBulkUpdate` — mutación (se omite cuando `dry_run: true`)
   **Entradas:** Por producto, `productId` más `[{ id: variantId, compareAtPrice: null }]`
   **Resultado esperado:** Variantes actualizadas con `compareAtPrice` en null; recoge los `userErrors` de cada lote

4. Escribe el CSV (siempre, incluso en simulación) como registro de auditoría y para poder revertir.

## Operaciones GraphQL

```graphql
# productVariants:query — validada con api_version 2025-01
query VariantsForCompareAtCleanup($query: String, $after: String) {
  productVariants(first: 250, after: $after, query: $query) {
    edges {
      node {
        id
        sku
        price
        compareAtPrice
        updatedAt
        product {
          id
          title
          vendor
          tags
        }
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
```

```graphql
# productVariantsBulkUpdate:mutation — validada con api_version 2025-01
mutation ClearCompareAtPrice($productId: ID!, $variants: [ProductVariantsBulkInput!]!) {
  productVariantsBulkUpdate(productId: $productId, variants: $variants) {
    productVariants {
      id
      price
      compareAtPrice
    }
    userErrors {
      field
      message
    }
  }
}
```

## Seguimiento de la sesión

**Claude DEBE mostrar lo siguiente en cada etapa. Es obligatorio.**

**Al iniciar**, muestra:
```
╔══════════════════════════════════════════════╗
║  SKILL: shopify-limpiar-precios-comparacion  ║
║  Tienda: <dominio de la tienda>              ║
║  Inicio: <AAAA-MM-DD HH:MM UTC>              ║
╚══════════════════════════════════════════════╝
```

**Después de cada paso**, muestra:
```
[N/TOTAL] <CONSULTA|MUTACIÓN>  <OperationName>
          → Parámetros: <resumen breve de las entradas clave>
          → Resultado: <cantidad o desenlace>
```

Si `dry_run: true`, antepón `[SIMULACIÓN]` a cada paso de mutación y no lo ejecutes.

**Al terminar**, muestra:

Para `format: human` (predeterminado):
```
══════════════════════════════════════════════
LIMPIEZA DE PRECIOS DE COMPARACIÓN  (<simulación|real>)
  Variantes revisadas:           <n>
  Borrados (sin descuento):      <n>
  Viejos (>= <días> días):       <n>
  Total borrados:                <n>
  Errores:                       <n>
  Archivo: compare_at_cleanup_<fecha>.csv
══════════════════════════════════════════════
```

Para `format: json`, muestra:
```json
{
  "skill": "shopify-limpiar-precios-comparacion",
  "store": "<dominio>",
  "dry_run": true,
  "variants_inspected": 0,
  "cleared_no_discount": 0,
  "cleared_stale": 0,
  "total_cleared": 0,
  "errors": 0,
  "output_file": "compare_at_cleanup_<fecha>.csv"
}
```

## Formato de salida
Archivo CSV `compare_at_cleanup_<AAAA-MM-DD>.csv` con las columnas:
`variant_id`, `product_id`, `product_title`, `vendor`, `sku`, `price`, `original_compare_at_price`, `rule_matched`, `variant_updated_at`, `mutation_status`

## Manejo de errores
| Error | Causa | Solución |
|-------|-------|----------|
| `userErrors` en la respuesta de la mutación | Variante bloqueada o incluida en un contrato de suscripción activo | Registra el error de esa variante y sigue con las demás |
| `THROTTLED` | Se superó el límite de solicitudes de la API | Espera 2 segundos y reintenta hasta 3 veces |
| `compareAtPrice` ya está en null a mitad de la ejecución | Otro proceso lo borró al mismo tiempo | Omítela y cuéntala como un éxito sin cambios |
| `updatedAt` más reciente de lo esperado | Alguien editó la variante durante la revisión | Vuelve a consultar esa variante antes de la mutación; omítela si ya no cumple la regla |

## Buenas prácticas
1. Ejecuta siempre primero con `dry_run: true` y revisa el CSV. No hay forma de deshacer en lote el borrado de compareAtPrice.
2. Desactiva `clean_stale` (pon `clean_stale: false`) durante promociones largas planeadas: esa oferta «vieja» en realidad es intencional.
3. Usa `collection_id` para limitar la limpieza a productos de línea permanente y dejar por fuera una colección en oferta activa.
4. Después de ejecutarla de verdad, revisa entre 5 y 10 variantes en la tienda en línea para confirmar que el precio tachado desapareció.
5. Prográmala cada trimestre como parte del orden del catálogo; combínala con `shopify-auditoria-seo` y `shopify-calidad-fichas-producto` para una revisión completa del catálogo al cierre del trimestre.
