---
name: shopify-reembolso-y-reposicion
description: "Hace un reembolso total o parcial de un pedido y, si quieres, crea un borrador de pedido de reposición para el cliente. Úsala cuando un cliente recibió un producto dañado o equivocado y quieres devolverle el dinero o enviarle otro sin pasar por el panel de Shopify."
---

## Propósito
Procesa reembolsos y crea pedidos de reposición sin tener que entrar al panel de Shopify. Esta skill maneja el reembolso y el borrador de pedido de reposición opcional en un solo flujo.

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

## Parámetros

| Parámetro | Tipo | Obligatorio | Predeterminado | Descripción |
|-----------|------|-------------|----------------|-------------|
| store | string | sí | — | Dominio de la tienda (p. ej., tu-tienda.myshopify.com) |
| format | string | no | human | Formato de salida: `human` o `json` |
| dry_run | bool | no | false | Muestra las operaciones sin ejecutar mutaciones |
| order_id | string | sí | — | GID del pedido (p. ej., `gid://shopify/Order/12345`) |
| refund_line_items | array | no | todo lo reembolsable | Lista de `{line_item_id, quantity}` a reembolsar; si se omite, reembolsa todas las cantidades reembolsables |
| reason | string | no | other | Motivo del reembolso: `customer`, `fraud`, `inventory`, `declined`, `other` |
| create_replacement | bool | no | false | Si es true, crea un borrador de pedido con los mismos artículos después del reembolso |
| notify_customer | bool | no | true | Envía al cliente el correo de notificación del reembolso |

## Seguridad

> ⚠️ Los pasos 2 y 3 ejecutan mutaciones financieras irreversibles. `refundCreate` no se puede deshacer: una vez procesado el reembolso, el pago no se puede volver a capturar. `draftOrderCreate` crea un borrador de pedido nuevo que se debe facturar y pagar por separado. Ejecútala con `dry_run: true` para verificar los artículos y los montos del reembolso antes de aplicarlo. Verifica el `refundableQuantity` de cada artículo en el paso 1 antes de continuar.

## Pasos

1. **OPERACIÓN:** `order` — consulta
   **Entradas:** `id: <order_id>`
   **Resultado esperado:** El pedido completo con `displayFinancialStatus`, `lineItems` (con `refundableQuantity`), `transactions`, `customer` y `shippingAddress`; verifica que el pedido se pueda reembolsar antes de continuar

2. **OPERACIÓN:** `refundCreate` — mutación
   **Entradas:** `input.orderId`, `input.refundLineItems` (los del parámetro o todo lo reembolsable), `input.notify`, `input.note: <reason>`
   **Resultado esperado:** `refund.id`, `refund.totalRefundedSet`, `userErrors`

3. **OPERACIÓN:** `draftOrderCreate` — mutación (solo si `create_replacement: true`)
   **Entradas:** `input.lineItems` (los artículos del pedido original), `input.customerId`, `input.shippingAddress`, `input.note: "Reposición del pedido <name>"`
   **Resultado esperado:** `draftOrder.id`, `draftOrder.name`, `draftOrder.invoiceUrl`, `userErrors`

## Operaciones GraphQL

```graphql
# order:query — validado con api_version 2025-01
query OrderForRefund($id: ID!) {
  order(id: $id) {
    id
    name
    displayFinancialStatus
    displayFulfillmentStatus
    totalPriceSet {
      shopMoney { amount currencyCode }
    }
    lineItems(first: 50) {
      edges {
        node {
          id
          title
          quantity
          refundableQuantity
          variant {
            id
            sku
            price
          }
        }
      }
    }
    transactions(first: 10) {
      id
      kind
      status
      amountSet {
        shopMoney { amount currencyCode }
      }
      gateway
    }
    refunds {
      id
      createdAt
      totalRefundedSet {
        shopMoney { amount currencyCode }
      }
    }
    customer {
      id
      defaultEmailAddress {
        emailAddress
      }
      firstName
      lastName
    }
    shippingAddress {
      address1
      city
      province
      country
      zip
    }
  }
}
```

```graphql
# refundCreate:mutation — validado con api_version 2025-01
mutation RefundCreate($input: RefundInput!) {
  refundCreate(input: $input) {
    refund {
      id
      createdAt
      totalRefundedSet {
        shopMoney { amount currencyCode }
      }
    }
    userErrors {
      field
      message
    }
  }
}
```

```graphql
# draftOrderCreate:mutation — validado con api_version 2025-01
mutation DraftOrderCreate($input: DraftOrderInput!) {
  draftOrderCreate(input: $input) {
    draftOrder {
      id
      name
      invoiceUrl
    }
    userErrors {
      field
      message
    }
  }
}
```

## Seguimiento de la sesión

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

**Al iniciar**, muestra:
```
╔══════════════════════════════════════════════╗
║  SKILL: shopify-reembolso-y-reposicion       ║
║  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):
```
══════════════════════════════════════════════
RESUMEN DEL RESULTADO
  Pedido:                  <name>
  ID del reembolso:        <id>
  Monto reembolsado:       <monto> <moneda>
  Borrador de reposición:  <nombre del borrador o "ninguno">
  Errores:                 0
  Archivo:                 ninguno
══════════════════════════════════════════════
```

Para `format: json`, muestra:
```json
{
  "skill": "shopify-reembolso-y-reposicion",
  "store": "<dominio>",
  "started_at": "<ISO8601>",
  "completed_at": "<ISO8601>",
  "dry_run": false,
  "steps": [
    { "step": 1, "operation": "OrderForRefund", "type": "query", "params_summary": "pedido <id>", "result_summary": "<estado>", "skipped": false },
    { "step": 2, "operation": "RefundCreate", "type": "mutation", "params_summary": "<n> artículos, motivo: <reason>", "result_summary": "reembolso <id>", "skipped": false },
    { "step": 3, "operation": "DraftOrderCreate", "type": "mutation", "params_summary": "<n> artículos, cliente <id>", "result_summary": "borrador <name>", "skipped": false }
  ],
  "outcome": {
    "order_name": "<name>",
    "refund_id": "<id>",
    "amount_refunded": "<monto>",
    "currency": "<moneda>",
    "draft_order_name": "<nombre o null>",
    "draft_order_invoice_url": "<url o null>",
    "errors": 0,
    "output_file": null
  }
}
```

## Formato de salida
No genera archivo CSV. El resumen final de la sesión muestra el ID y el monto del reembolso. Si `create_replacement: true`, también incluye el nombre del borrador de pedido y la URL de su factura.

## Manejo de errores
| Error | Causa | Solución |
|-------|-------|----------|
| `refundableQuantity` es 0 | El artículo ya se reembolsó por completo | Revisa el historial de reembolsos del pedido |
| `userErrors` de refundCreate | Montos de reembolso no válidos o pedido que no se puede reembolsar | Revisa `displayFinancialStatus`: no debe ser `REFUNDED` |
| `userErrors` de draftOrderCreate | Artículos o cliente no válidos | Verifica que las variantes de producto todavía existan |
| Pedido no encontrado | GID de pedido no válido | Usa la skill `shopify-buscar-pedido` para encontrar el ID correcto del pedido |

## Buenas prácticas
1. Ejecútala siempre primero con `dry_run: true`: el paso 2 es irreversible. Verifica el `refundableQuantity` de cada artículo en el resultado del paso 1 antes de aplicarlo.
2. En los reembolsos parciales, especifica `refund_line_items` de forma explícita: si lo omites, se reembolsan todos los artículos reembolsables, y puede que eso no sea lo que quieres.
3. El borrador de pedido de `create_replacement` no se factura ni se despacha automáticamente: comparte la `invoiceUrl` con el cliente para que pague.
4. Usa `notify_customer: false` en las correcciones internas en las que no se debe avisar al cliente.
5. Revisa `displayFinancialStatus` en el paso 1: si es `REFUNDED`, no queda nada por reembolsar.
