---
name: shopify-roi-descuentos
description: "Solo lectura: calcula el retorno real (ROI) de cada código de descuento y descuento automático comparando los ingresos que trajo con lo que costó. Úsala cuando quieras saber qué descuentos te dejan plata y cuáles conviene retirar."
---

## Propósito
Evalúa el retorno real de la inversión de cada código de descuento y descuento automático midiendo los ingresos generados, el número de pedidos, el ticket promedio (AOV) con y sin descuento, los clientes nuevos atribuidos a los descuentos y si los pedidos con descuento le quitaron ventas a precio completo (canibalización). Va más allá de `shopify-limpiar-descuentos` (que encuentra códigos rotos o sin uso) para responder «¿valió la pena este descuento?». Solo lectura: no ejecuta mutaciones.

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

## Parámetros

| Parámetro | Tipo | Obligatorio | Por defecto | Descripción |
|-----------|------|-------------|-------------|-------------|
| store | string | sí | — | Dominio de la tienda |
| days_back | integer | no | 90 | Cuántos días hacia atrás analizar |
| min_uses | integer | no | 3 | Usos mínimos para que un descuento se analice |
| format | string | no | human | Formato de salida: `human` o `json` |

## Seguridad

> ℹ️ Skill de solo lectura: no ejecuta mutaciones. Puedes correrla en cualquier momento.

## Pasos

1. **OPERACIÓN:** `discountNodes` — consulta
   **Entradas:** `first: 250`, selecciona los detalles del descuento (título, código, tipo, valor, usageCount, startsAt, endsAt), cursor de paginación
   **Resultado esperado:** Todos los códigos de descuento y descuentos automáticos

2. Filtra los descuentos con `usageCount >= min_uses` que estuvieron activos dentro de la ventana de análisis

3. **OPERACIÓN:** `orders` — consulta
   **Entradas:** `query: "created_at:>='<NOW - days_back days>' discount_code:<code>"`, `first: 250` para cada código de descuento activo; selecciona `totalPriceSet`, `totalDiscountsSet`, `subtotalPriceSet`, `customer { id, numberOfOrders }`, cursor de paginación
   **Resultado esperado:** Todos los pedidos que usaron cada descuento

4. Consulta también los pedidos SIN ningún descuento en el mismo período, como base para comparar el ticket promedio

5. Para cada descuento, calcula:
   - **Costo total del descuento** = Σ(totalDiscountsSet de los pedidos con este código)
   - **Ingresos generados** = Σ(totalPriceSet de los pedidos con este código)
   - **Ticket promedio con descuento** = ingresos / pedidos
   - **Ticket promedio base** = ticket promedio de los pedidos sin descuento en el mismo período
   - **Aumento o caída del ticket promedio** = ticket promedio con descuento − ticket promedio base
   - **% de clientes nuevos** = pedidos donde customer.numberOfOrders == 1 / total
   - **ROI bruto** = (ingresos − costo del descuento) / costo del descuento × 100
   - **Riesgo de canibalización** = alto si el ticket promedio con descuento < ticket promedio base y el % de clientes nuevos < 20%

## Operaciones GraphQL

```graphql
# discountNodes:query — validado con api_version 2025-01
query AllDiscounts($after: String) {
  discountNodes(first: 250, after: $after) {
    edges {
      node {
        id
        discount {
          ... on DiscountCodeBasic {
            title
            codes(first: 1) { edges { node { code } } }
            usageLimit
            asyncUsageCount
            startsAt
            endsAt
            customerGets {
              value {
                ... on DiscountPercentage { percentage }
                ... on DiscountAmount { amount { amount currencyCode } }
              }
            }
          }
          ... on DiscountCodeFreeShipping {
            title
            codes(first: 1) { edges { node { code } } }
            asyncUsageCount
            startsAt
            endsAt
          }
          ... on DiscountAutomaticBasic {
            title
            asyncUsageCount
            startsAt
            endsAt
            customerGets {
              value {
                ... on DiscountPercentage { percentage }
                ... on DiscountAmount { amount { amount currencyCode } }
              }
            }
          }
        }
      }
    }
    pageInfo { hasNextPage endCursor }
  }
}
```

```graphql
# orders:query — validado con api_version 2025-01
query OrdersByDiscount($query: String!, $after: String) {
  orders(first: 250, after: $after, query: $query) {
    edges {
      node {
        id
        name
        createdAt
        totalPriceSet { shopMoney { amount currencyCode } }
        totalDiscountsSet { shopMoney { amount currencyCode } }
        subtotalPriceSet { shopMoney { amount currencyCode } }
        customer {
          id
          numberOfOrders
        }
        discountCodes
      }
    }
    pageInfo { hasNextPage endCursor }
  }
}
```

## Seguimiento de la sesión

**Claude DEBE mostrar esta salida en cada etapa. Es obligatorio.**

**Al iniciar**, muestra:
```
╔══════════════════════════════════════════════╗
║  SKILL: Calculadora de ROI de descuentos     ║
║  Tienda: <dominio de la tienda>              ║
║  Inicio: <YYYY-MM-DD HH:MM UTC>              ║
╚══════════════════════════════════════════════╝
```

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

**Al terminar**, muestra:

Para `format: human` (por defecto):
```
══════════════════════════════════════════════
REPORTE DE ROI DE DESCUENTOS  (<days_back> días)
  Descuentos analizados:     <n>
  Gasto total en descuentos: $<monto>
  Ingresos atribuidos:       $<monto>
  ─────────────────────────────
  MEJOR DESEMPEÑO (por ROI):
    "<código>"  ROI: <n>%  Ingresos: $<n>  Costo: $<n>  Clientes nuevos: <pct>%

  BAJO DESEMPEÑO:
    "<código>"  ROI: <n>%  Ingresos: $<n>  Costo: $<n>  ⚠️ Riesgo de canibalización

  COMPARACIÓN CON LA BASE:
    Ticket promedio sin descuento: $<n>  |  Ticket promedio con descuento: $<n>  |  Δ: $<n>

  Archivo: discount_roi_<fecha>.csv
══════════════════════════════════════════════
```

## Formato de salida
Archivo CSV `discount_roi_<YYYY-MM-DD>.csv` con las columnas:
`discount_id`, `code_or_title`, `type`, `uses`, `revenue`, `discount_cost`, `roi_pct`, `aov`, `baseline_aov`, `aov_delta`, `new_customer_pct`, `cannibalization_risk`

## Manejo de errores
| Error | Causa | Solución |
|-------|-------|----------|
| `THROTTLED` | Se superó el límite de solicitudes de la API | Espera 2 segundos y reintenta hasta 3 veces |
| Descuentos automáticos | No tienen un código para filtrar los pedidos | Relaciónalos con los datos de descuento de cada pedido |
| Descuentos combinados | Varios códigos en un mismo pedido | Atribúyelos de forma proporcional o márcalos como «multidescuento» |

## Buenas prácticas
- Los descuentos con ROI menor al 100% cuestan más de lo que generan: considera retirarlos.
- Un % alto de clientes nuevos con ROI positivo = una gran herramienta de adquisición: mantenlo activo.
- Un % bajo de clientes nuevos con caída del ticket promedio = canibalización: esos clientes igual habrían comprado.
- Cruza los resultados con `shopify-comparar-codigos-descuento` para sacar conclusiones de pruebas A/B.
- Úsala junto con `shopify-limpiar-descuentos` para encontrar y eliminar los códigos de bajo desempeño.
