---
name: shopify-despachos-masivos
description: "Despacha en lote las órdenes de preparación abiertas de una ubicación con su número de seguimiento (guía); admite despachos parciales y aviso opcional al cliente. Úsala cuando tengas muchos pedidos listos para despachar."
---

## Propósito
Consulta todas las órdenes de preparación abiertas de una ubicación y crea en lote los despachos (fulfillments) con sus números de seguimiento en un solo flujo. No necesitas apps de terceros: esta skill se encarga de crear los despachos; para generar las etiquetas de envío de la transportadora necesitas otra herramienta o una integración con la transportadora.

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

## Parámetros

| Parámetro | Tipo | Obligatorio | Por defecto | Descripción |
|-----------|------|-------------|-------------|-------------|
| store | string | sí | — | Dominio de la tienda (p. ej., tu-tienda.myshopify.com) |
| location_id | string | sí | — | GID de la ubicación de despacho (p. ej., gid://shopify/Location/123) |
| tracking_numbers | array | no | [] | Lista de objetos `{fulfillment_order_id, tracking_number, tracking_url, carrier}` |
| notify_customer | bool | no | true | Envía al cliente el correo de confirmación de envío |
| dry_run | bool | no | true | Muestra una vista previa de los despachos sin ejecutar mutaciones |
| format | string | no | human | Formato de salida: `human` o `json` |

## Seguridad

> ⚠️ `fulfillmentCreate` es irreversible: los pedidos preparados no se pueden revertir a «no preparado» mediante la API. Ejecuta primero con `dry_run: true` para confirmar la lista de órdenes de preparación antes de aplicar los cambios. Cada mutación crea un registro de despacho por cada orden de preparación.

## Pasos

1. **OPERACIÓN:** `fulfillmentOrders` — consulta
   **Entradas:** `assignedLocationId: <location_id>`, `status: OPEN`, `first: 250`, cursor de paginación
   **Resultado esperado:** Lista de órdenes de preparación abiertas con `id`, `order { name }`, `lineItems`; pagina hasta que `hasNextPage: false`

2. **OPERACIÓN:** `fulfillmentCreate` — mutación
   **Entradas:** Para cada orden de preparación: `fulfillmentOrderId`, `trackingInfo { company, number, url }`, `notifyCustomer`
   **Resultado esperado:** `fulfillment { id, status, trackingInfo }`, `userErrors`

## Operaciones GraphQL

```graphql
# fulfillmentOrders:query — validado con api_version 2025-01
query OpenFulfillmentOrders($locationId: ID!, $after: String) {
  fulfillmentOrders(
    assignedLocationId: $locationId
    first: 250
    after: $after
    query: "status:open"
  ) {
    edges {
      node {
        id
        status
        order {
          id
          name
        }
        lineItems(first: 10) {
          edges {
            node {
              id
              remainingQuantity
              variant {
                sku
                title
              }
            }
          }
        }
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
```

```graphql
# fulfillmentCreate:mutation — validado con api_version 2025-01
mutation FulfillmentCreate($fulfillment: FulfillmentInput!) {
  fulfillmentCreate(fulfillment: $fulfillment) {
    fulfillment {
      id
      status
      trackingInfo {
        company
        number
        url
      }
    }
    userErrors {
      field
      message
    }
  }
}
```

## Seguimiento de la sesión

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

**Al iniciar**, muestra:
```
╔══════════════════════════════════════════════╗
║  SKILL: Despachos masivos                    ║
║  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>
```

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

**Al terminar**, muestra:

Para `format: human` (por defecto):
```
══════════════════════════════════════════════
RESUMEN DEL RESULTADO
  Órdenes de preparación abiertas: <n>
  Despachos creados:               <n>
  Avisos enviados a clientes:      <n>
  Errores:                         <n>
  Archivo:                         fulfillment_batch_<fecha>.csv
══════════════════════════════════════════════
```

Para `format: json`, muestra:
```json
{
  "skill": "shopify-despachos-masivos",
  "store": "<dominio>",
  "started_at": "<ISO8601>",
  "completed_at": "<ISO8601>",
  "dry_run": true,
  "steps": [
    { "step": 1, "operation": "OpenFulfillmentOrders", "type": "query", "params_summary": "ubicación <id>, estado abierto", "result_summary": "<n> órdenes", "skipped": false },
    { "step": 2, "operation": "FulfillmentCreate", "type": "mutation", "params_summary": "<n> despachos", "result_summary": "<n> creados", "skipped": false }
  ],
  "outcome": {
    "orders_found": 0,
    "fulfillments_created": 0,
    "notifications_sent": 0,
    "errors": 0,
    "output_file": "fulfillment_batch_<fecha>.csv"
  }
}
```

## Formato de salida
Archivo CSV `fulfillment_batch_<YYYY-MM-DD>.csv` con las columnas:
`order_name`, `fulfillment_order_id`, `fulfillment_id`, `tracking_number`, `carrier`, `notify_customer`, `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 |
| `userErrors` en fulfillmentCreate | El pedido ya estaba preparado o el seguimiento no es válido | Registra el error, omite el pedido y continúa |
| `FULFILLMENT_ORDER_LINE_ITEM_QUANTITY_MISMATCH` | Las cantidades del despacho parcial no coinciden | Registra una advertencia e intenta el despacho parcial |
| No hay órdenes abiertas en la ubicación | La ubicación no tiene trabajo pendiente | Termina con el resumen: 0 órdenes encontradas |

## Buenas prácticas
- Ejecuta siempre primero con `dry_run: true`: los despachos no se pueden deshacer con la Admin API.
- Usa `notify_customer: false` mientras haces pruebas en lote o para pedidos B2B en los que los clientes no esperan un correo por cada envío.
- Carga los números de seguimiento en el parámetro `tracking_numbers` antes de ejecutar; los pedidos sin seguimiento igual se marcarán como despachados, así que confirma que eso es lo que quieres.
- En lotes grandes (más de 100 pedidos), el ciclo de mutaciones llegará al límite de solicitudes; la skill reintenta automáticamente con una espera de 2 segundos.
