---
name: shopify-reponer-stock-devoluciones
description: "Para devoluciones aprobadas o cerradas, devuelve al inventario las unidades en la ubicación que recibió la devolución, ajustando las cantidades de cada artículo devuelto. Úsala cuando la bodega procese las devoluciones por fuera de Shopify o tengas reposiciones atrasadas."
---

## Propósito
Recorre las devoluciones aprobadas o cerradas recientemente y repone el inventario de cada `returnLineItem` cuyo artículo físico ya se recibió e inspeccionó. Ajusta la cantidad `available` en la ubicación de destino de la devolución usando `inventoryAdjustQuantities` con el motivo `restock` y un `referenceDocumentUri` que enlaza con el registro de la devolución. Úsala cuando la bodega registra su trabajo en un sistema distinto de Shopify, o cuando la reposición manual quedó pendiente y necesitas ponerte al día de forma ordenada.

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

## Parámetros

| Parámetro | Tipo | Obligatorio | Predeterminado | Descripción |
|-----------|------|-------------|----------------|-------------|
| store | string | sí | — | Dominio de la tienda (p. ej., mitienda.myshopify.com) |
| format | string | no | human | Formato de salida: `human` o `json` |
| dry_run | bool | no | true | Muestra el plan de reposición sin ejecutar las mutaciones de inventario |
| days_back | integer | no | 14 | Ventana de búsqueda (en días) de devoluciones completadas recientemente |
| return_status | string | no | CLOSED | Solo repone devoluciones en este estado: `CLOSED`, `OPEN` o `ANY` |
| location_id | string | no | — | Si se indica, solo repone las devoluciones cuyo destino de inventario coincide con este GID de ubicación |
| restock_only_return_reasons | array | no | — | Si se indica (p. ej., `["UNWANTED", "SIZE_TOO_SMALL"]`), solo repone los artículos devueltos por estos motivos; `DEFECTIVE` queda excluido por defecto |
| skip_defective | bool | no | true | Excluye de la reposición los artículos con `returnLineItem.returnReason: DEFECTIVE` |

## Seguridad

> ⚠️ El paso 2 ejecuta mutaciones `inventoryAdjustQuantities` que suman unidades de inmediato a la cantidad `available` en la ubicación de destino. Reponer inventario dañado o que no se puede vender infla el stock disponible y provoca ventas sin existencias. El valor predeterminado es `dry_run: true`: revisa el CSV de vista previa para confirmar que cada artículo devuelto de verdad se puede vender antes de aplicar los cambios. Por defecto, `skip_defective: true` excluye las devoluciones `DEFECTIVE`. Cada reposición deja una entrada permanente en el historial de inventario de Shopify con el motivo `restock`.

## Pasos

1. **OPERACIÓN:** `returns` — consulta
   **Entradas:** `query: "status:<return_status> updated_at:>='<NOW - days_back days>'"` (usa solo `updated_at:>='...'` cuando `return_status:ANY`), `first: 250`, selecciona `id`, `name`, `status`, `closedAt`, `order { id name }`, `returnLineItems(first: 50) { quantity, returnReason, fulfillmentLineItem { lineItem { variant { id sku inventoryItem { id tracked } } } } }`, `reverseFulfillmentOrders(first: 5) { reverseDeliveries(first: 5) { deliverable { ... on ReverseDeliveryShippingDeliverable { label { ... } } } }, location { id name } }` y el cursor de paginación
   **Resultado esperado:** Devoluciones con sus artículos, motivos de devolución, IDs de los artículos de inventario y ubicación de destino

2. Arma el plan de reposición: para cada `returnLineItem` que no se haya repuesto antes, cuyo `returnReason` esté permitido por los parámetros y con `inventoryItem.tracked: true`, agrupa por `(inventoryItemId, locationId)` sumando los `quantity` como deltas. Omite los artículos cuya variante no existe, los que tienen `tracked: false` y las devoluciones sin ubicación de destino.

3. **OPERACIÓN:** `inventoryAdjustQuantities` — mutación
   **Entradas:** `input.reason: "restock"`, `input.name: "available"`, `input.referenceDocumentUri: "shopify://returns/<return_id>"`, `input.changes: [{ inventoryItemId, locationId, delta: +<quantity> }, ...]`
   **Resultado esperado:** `inventoryAdjustmentGroup.changes` con `quantityAfterChange` por artículo; `userErrors`

## Operaciones GraphQL

```graphql
# returns:query — validada con api_version 2025-01
query ReturnsForRestock($query: String!, $after: String) {
  returns(first: 250, after: $after, query: $query) {
    edges {
      node {
        id
        name
        status
        closedAt
        order { id name }
        returnLineItems(first: 50) {
          edges {
            node {
              id
              quantity
              returnReason
              returnReasonNote
              fulfillmentLineItem {
                lineItem {
                  id
                  title
                  variant {
                    id
                    sku
                    inventoryItem { id tracked }
                  }
                }
              }
            }
          }
        }
        reverseFulfillmentOrders(first: 5) {
          edges {
            node {
              id
              reverseDeliveries(first: 5) { edges { node { id } } }
            }
          }
        }
      }
    }
    pageInfo { hasNextPage endCursor }
  }
}
```

```graphql
# inventoryAdjustQuantities:mutation — validada con api_version 2025-01
mutation RestockOnReturn($input: InventoryAdjustQuantitiesInput!) {
  inventoryAdjustQuantities(input: $input) {
    inventoryAdjustmentGroup {
      id
      reason
      referenceDocumentUri
      changes {
        name
        delta
        quantityAfterChange
        item { id sku }
        location { id name }
      }
    }
    userErrors { field message }
  }
}
```

## Seguimiento de la sesión

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

**Al iniciar**, muestra:
```
╔══════════════════════════════════════════════╗
║  SKILL: shopify-reponer-stock-devoluciones   ║
║  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):
```
══════════════════════════════════════════════
REPOSICIÓN DE STOCK POR DEVOLUCIONES
  Devoluciones revisadas:     <n>
  Artículos que califican:    <n>
  Omitidos (defectuosos):     <n>
  Omitidos (sin seguimiento): <n>
  Unidades repuestas:         <n>  (u "omitido — dry_run")
  Grupos de ajuste:           <n>
  Errores:                    <n>
  Archivo:                    restock_on_return_<fecha>.csv
══════════════════════════════════════════════
```

Para `format: json`, muestra:
```json
{
  "skill": "shopify-reponer-stock-devoluciones",
  "store": "<dominio>",
  "started_at": "<ISO8601>",
  "completed_at": "<ISO8601>",
  "dry_run": true,
  "outcome": {
    "returns_scanned": 0,
    "line_items_eligible": 0,
    "skipped_defective": 0,
    "skipped_untracked": 0,
    "units_restocked": 0,
    "adjustment_groups": 0,
    "errors": 0,
    "output_file": "restock_on_return_<fecha>.csv"
  }
}
```

## Formato de salida
Archivo CSV `restock_on_return_<AAAA-MM-DD>.csv` con las columnas:
`return_id`, `return_name`, `order_name`, `sku`, `product_title`, `quantity_restocked`, `return_reason`, `location_name`, `quantity_after`, `inventory_item_id`, `status`

## 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 |
| `inventoryItem.tracked: false` | La variante no tiene activado el seguimiento de inventario | Regístralo y omítelo; hay que activarlo a mano |
| `userErrors` de inventoryAdjustQuantities | Ubicación o artículo no válidos | Verifica que los GID coincidan con el destino de la devolución |
| La devolución no tiene ubicación de destino | Todavía no se ha confirmado la entrega de vuelta | Omite la devolución; vuelve a ejecutar cuando se confirme la entrega |
| Variante eliminada | El producto se borró después de la venta | Omite el artículo; regístralo para revisarlo |

## Buenas prácticas
- Ejecuta siempre primero con `dry_run: true` y revisa el CSV de vista previa: reponer unidades dañadas infla el inventario y provoca ventas sin existencias más adelante.
- Deja `skip_defective: true` en la operación normal. Cámbialo a `false` solo después de inspeccionar las unidades defectuosas y confirmar que se pueden reacondicionar.
- Usa `restock_only_return_reasons: ["UNWANTED", "SIZE_TOO_SMALL", "SIZE_TOO_LARGE", "STYLE", "COLOR"]` para enfocarte en motivos que casi siempre dejan inventario revendible.
- Usa `location_id` cuando trabajes por bodega: así la ejecución se limita al proceso de devoluciones de un solo sitio.
- Cruza el resultado con `shopify-auditoria-inventario-bodegas` para confirmar que los SKU repuestos cuadran.
- En devoluciones activas cuya inspección física no se ha hecho, espera a que estén en `status: CLOSED` (el valor predeterminado) antes de reponer: nunca repongas stock de devoluciones `OPEN` solo por lo que dice el cliente.
