---
name: shopify-tiempos-de-entrega
description: "Solo lectura: calcula el tiempo promedio entre el despacho y la entrega de cada transportadora con los datos de pedidos y despachos. Úsala cuando quieras saber qué transportadoras son lentas o no confirman las entregas."
---

## Propósito
Analiza los pedidos despachados para calcular el tiempo promedio en tránsito (despacho creado → entregado) por transportadora. Muestra qué transportadoras son lentas de forma constante o no envían confirmación de entrega. Solo lectura: no hace mutaciones.

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

## Parámetros

| Parámetro | Tipo | Obligatorio | Predeterminado | Descripción |
|-----------|------|-------------|----------------|-------------|
| store | string | sí | — | Dominio de la tienda (p. ej., mitienda.myshopify.com) |
| days_back | integer | no | 30 | Período hacia atrás en el que se buscan pedidos despachados |
| min_orders | integer | no | 5 | Mínimo de pedidos por transportadora para incluirla en los promedios |
| location_id | string | no | — | Filtra por ubicación de despacho (opcional) |
| 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:** `orders` — consulta
   **Entradas:** `query: "fulfillment_status:shipped created_at:>='<NOW - days_back days>'"`, `first: 250` y el cursor de paginación
   **Resultado esperado:** Pedidos con `fulfillments { createdAt, updatedAt, deliveredAt, trackingInfo { company } }`; pagina hasta `hasNextPage: false`

2. Calcula los tiempos en tránsito por transportadora: `deliveredAt - createdAt` (omite los pedidos donde `deliveredAt` es null)

3. **OPERACIÓN:** `fulfillmentOrders` — consulta (opcional, para el desglose por ubicación)
   **Entradas:** `assignedLocationId: <location_id>`, `status: CLOSED`, `first: 250`
   **Resultado esperado:** Pedidos despachados por ubicación, para segmentar por ubicación

## Operaciones GraphQL

```graphql
# orders:query — validada con api_version 2025-01
query FulfilledOrders($query: String!, $after: String) {
  orders(first: 250, after: $after, query: $query) {
    edges {
      node {
        id
        name
        createdAt
        fulfillments {
          id
          createdAt
          updatedAt
          deliveredAt
          status
          trackingInfo {
            company
            number
          }
        }
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
```

```graphql
# fulfillmentOrders:query — validada con api_version 2025-01
query FulfillmentOrdersByLocation($locationId: ID!, $after: String) {
  fulfillmentOrders(
    assignedLocationId: $locationId
    first: 250
    after: $after
    query: "status:closed"
  ) {
    edges {
      node {
        id
        assignedLocation {
          location {
            id
            name
          }
        }
        order {
          id
          name
        }
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
```

## Seguimiento de la sesión

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

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

**Al terminar**, muestra:

Para `format: human` (predeterminado):
```
══════════════════════════════════════════════
TIEMPOS DE ENTREGA  (<days_back> días)
  Pedidos analizados:       <n>
  Con datos de entrega:     <n>

  Transportadora       Pedidos  Días prom.  Mín  Máx
  ─────────────────────────────────────────────
  UPS                  <n>      <d>         <d>  <d>
  USPS                 <n>      <d>         <d>  <d>
  FedEx                <n>      <d>         <d>  <d>
  (se excluyen las transportadoras por debajo de min_orders)
  Archivo: delivery_analysis_<date>.csv
══════════════════════════════════════════════
```

Para `format: json`, muestra:
```json
{
  "skill": "shopify-tiempos-de-entrega",
  "store": "<dominio>",
  "period_days": 30,
  "carriers": [
    { "name": "UPS", "orders": 0, "avg_days": 0, "min_days": 0, "max_days": 0 }
  ],
  "output_file": "delivery_analysis_<date>.csv"
}
```

## Formato de salida
Archivo CSV `delivery_analysis_<YYYY-MM-DD>.csv` con las columnas:
`order_name`, `fulfillment_id`, `carrier`, `fulfilled_at`, `delivered_at`, `transit_days`

## 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 |
| `deliveredAt` es null | La transportadora no ha confirmado la entrega | Exclúyelo de los promedios y cuéntalo como «en tránsito» |
| No hay pedidos despachados en el período | Período muy corto o sin pedidos | Termina con el resumen: 0 pedidos |

## Buenas prácticas
- Usa `min_orders: 10` para que los promedios tengan peso estadístico: las transportadoras con menos pedidos distorsionan los resultados.
- `deliveredAt` solo se llena cuando la transportadora confirma la entrega con sus eventos de seguimiento; algunas transportadoras no lo reportan, así que es normal encontrar valores null.
- Ejecútala cada mes para seguir el desempeño de las transportadoras en el tiempo y tener argumentos al negociar contratos con ellas.
- Crúzala con la skill `shopify-donde-esta-mi-pedido` para ver si las transportadoras lentas coinciden con más solicitudes de «¿dónde está mi pedido?».
