---
name: shopify-ventas-por-bodega
description: "Solo lectura: desglosa los ingresos según la ubicación que despachó cada pedido, para ver el estado de resultados y el rendimiento de cada bodega. Úsala cuando despaches desde varias bodegas o puntos de venta y quieras comparar cuánto mueve cada uno."
---

## Propósito
Asigna los ingresos de cada pedido a la ubicación de despacho que lo envió. Produce un desglose de ingresos por bodega o centro de despacho, útil para el estado de resultados por ubicación, para decidir el personal de cada sitio y para entender desde dónde se está atendiendo la demanda. Solo lectura: no hace cambios.

## 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 | Ventana de análisis en días |
| format | string | no | human | Formato de salida: `human` o `json` |

## Seguridad

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

## Pasos

1. **OPERACIÓN:** `locations` — consulta
   **Entradas:** `first: 50`, solo activas
   **Resultado esperado:** IDs y nombres de las ubicaciones para completar el reporte

2. **OPERACIÓN:** `orders` — consulta
   **Entradas:** `query: "fulfillment_status:shipped created_at:>='<NOW - days_back days>'"`, `first: 250`, selecciona `fulfillments { assignedLocation }`, `totalPriceSet` y el cursor de paginación
   **Resultado esperado:** Pedidos despachados con la ubicación asignada

3. **OPERACIÓN:** `fulfillmentOrders` — consulta (para asignar los pedidos abiertos)
   **Entradas:** Por ubicación, `status: CLOSED`, `first: 250`
   **Resultado esperado:** Órdenes de preparación cerradas para asignar los ingresos

4. Suma los ingresos por ubicación; los pedidos sin datos de ubicación se asignan a «Desconocida»

## Operaciones GraphQL

```graphql
# locations:query — validada con api_version 2025-01
query LocationsList {
  locations(first: 50, includeInactive: false) {
    edges {
      node {
        id
        name
      }
    }
  }
}
```

```graphql
# orders:query — validada con api_version 2025-01
query RevenueByLocation($query: String!, $after: String) {
  orders(first: 250, after: $after, query: $query) {
    edges {
      node {
        id
        name
        createdAt
        totalPriceSet {
          shopMoney {
            amount
            currencyCode
          }
        }
        fulfillments {
          assignedLocation {
            location {
              id
              name
            }
          }
          status
        }
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
```

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

## Seguimiento de la sesión

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

**Al iniciar**, muestra:
```
╔══════════════════════════════════════════════╗
║  SKILL: shopify-ventas-por-bodega            ║
║  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):
```
══════════════════════════════════════════════
INGRESOS POR UBICACIÓN  (<days_back> días)
  Montos en la moneda de la tienda.
  Ingresos totales:    <monto>
  Pedidos incluidos:   <n>

  Ubicación               Pedidos  Ingresos   Participación
  ─────────────────────────────────────────────────────────
  Bodega A                <n>      <n>        <pct>%
  Bodega B                <n>      <n>        <pct>%
  Archivo: revenue_by_location_<fecha>.csv
══════════════════════════════════════════════
```

Para `format: json`, muestra:
```json
{
  "skill": "shopify-ventas-por-bodega",
  "store": "<dominio>",
  "period_days": 30,
  "total_revenue": 0,
  "currency": "<moneda de la tienda>",
  "by_location": [],
  "output_file": "revenue_by_location_<fecha>.csv"
}
```

## Formato de salida
Archivo CSV `revenue_by_location_<AAAA-MM-DD>.csv` con las columnas:
`location_id`, `location_name`, `order_count`, `total_revenue`, `currency`, `share_pct`

## 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 |
| Pedido sin ubicación de despacho | Pedido sin despachar o del punto de venta (POS) | Asígnalo a «Sin asignar» |
| Tienda con una sola ubicación | Todos los ingresos vienen de una ubicación | El reporte sigue siendo válido y muestra el total completo |

## Buenas prácticas
- Si trabajas con varios operadores logísticos (3PL), este reporte te ayuda a ver si uno está manejando una parte desproporcionada y quizá necesita alivio de capacidad.
- Aquí los ingresos se asignan según la ubicación que despacha, no según la dirección de envío del cliente: reflejan el volumen operativo de cada ubicación, no la demanda geográfica.
- Combínala con el reporte de valor del inventario (`shopify-valor-inventario`) para calcular la rotación de inventario por ubicación (ingresos / valor del inventario).
