---
name: shopify-margen-de-ganancia
description: "Solo lectura: calcula la ganancia neta real por pedido y por producto descontando costo de producto, envío, comisiones de pago, descuentos, reembolsos e impuestos. Úsala cuando quieras saber cuánto ganas de verdad y qué productos te dejan poco margen."
---

## Propósito
Calcula la ganancia neta y el margen reales tanto a nivel de pedido como de producto. A diferencia de los reportes básicos de ingresos, esta skill descuenta todos los componentes de costo: el costo de los productos vendidos o COGS (desde `inventoryItem.unitCost`), los costos de envío, las comisiones por transacción y procesamiento de pagos, los descuentos aplicados, los montos reembolsados y los aranceles e impuestos, para mostrar el porcentaje de margen real. Solo lectura, sin mutaciones.

## Requisitos
- Sesión autenticada de Shopify CLI: `shopify store auth --store <tu-tienda>.myshopify.com --scopes read_orders,read_products,read_inventory`
- Las consultas se ejecutan con `shopify store execute`.
- Permisos (scopes) de API: `read_orders`, `read_products`, `read_inventory`
- Para que los resultados sean exactos, los productos deben tener cargado `inventoryItem.unitCost` (costo por artículo)

## 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 días hacia atrás para los pedidos |
| group_by | string | no | order | Agrupación: `order`, `product` o `variant` |
| min_orders | integer | no | 1 | Pedidos mínimos para que un producto aparezca (modo producto o variante) |
| include_refunded | boolean | no | true | Incluye en el cálculo los pedidos reembolsados por completo |
| format | string | no | human | Formato de salida: `human` o `json` |

## Seguridad

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

## Pasos

1. **OPERACIÓN:** `orders` — consulta
   **Entradas:** `query: "created_at:>='<NOW - days_back days>'"`, `first: 250`, campos `id`, `name`, `createdAt`, `totalPriceSet`, `subtotalPriceSet`, `totalShippingPriceSet`, `totalTaxSet`, `totalDiscountsSet`, `currentTotalPriceSet`, `displayFinancialStatus`, `refunds { totalRefundedSet }`, `lineItems { variant { id, inventoryItem { id, unitCost { amount, currencyCode } } }, quantity, originalTotalSet, discountedTotalSet }`, cursor de paginación
   **Resultado esperado:** Todos los pedidos de la ventana con el desglose completo de costos

2. **OPERACIÓN:** `inventoryItems` — consulta
   **Entradas:** Lote de `inventoryItemIds` de las variantes de los artículos del pedido a las que les falte el dato de unitCost
   **Resultado esperado:** Costo unitario de cada artículo de inventario

3. Para cada pedido, calcula:
   - **Ingreso** = `currentTotalPriceSet.shopMoney.amount`
   - **COGS** = Σ(lineItem.quantity × variant.inventoryItem.unitCost)
   - **Costo de envío** = `totalShippingPriceSet.shopMoney.amount` (estimación de la parte que paga el comercio)
   - **Descuentos** = `totalDiscountsSet.shopMoney.amount`
   - **Comisión por transacción** = estimada en 2.9 % + 0.30 USD del total (configurable)
   - **Reembolsos** = Σ(refunds.totalRefundedSet.shopMoney.amount)
   - **Ganancia neta** = Ingreso - COGS - Envío - Comisión por transacción - Reembolsos
   - **Margen %** = (Ganancia neta / Ingreso) × 100

4. Si `group_by: product` o `variant`, suma las ganancias por producto o variante en todos los pedidos

5. **OPERACIÓN:** `productVariants` — consulta (enriquecimiento)
   **Entradas:** IDs de variantes de los artículos más y menos rentables, para tener el contexto del título del producto
   **Resultado esperado:** Títulos de producto y SKU para mostrar

## Operaciones GraphQL

```graphql
# orders:query — validada con api_version 2025-01
query OrdersWithCosts($query: String!, $after: String) {
  orders(first: 250, after: $after, query: $query) {
    edges {
      node {
        id
        name
        createdAt
        displayFinancialStatus
        totalPriceSet { shopMoney { amount currencyCode } }
        subtotalPriceSet { shopMoney { amount currencyCode } }
        totalShippingPriceSet { shopMoney { amount currencyCode } }
        totalTaxSet { shopMoney { amount currencyCode } }
        totalDiscountsSet { shopMoney { amount currencyCode } }
        currentTotalPriceSet { shopMoney { amount currencyCode } }
        refunds {
          totalRefundedSet { shopMoney { amount currencyCode } }
        }
        lineItems(first: 50) {
          edges {
            node {
              quantity
              originalTotalSet { shopMoney { amount currencyCode } }
              discountedTotalSet { shopMoney { amount currencyCode } }
              variant {
                id
                sku
                inventoryItem {
                  id
                  unitCost { amount currencyCode }
                }
                product {
                  id
                  title
                }
              }
            }
          }
        }
      }
    }
    pageInfo { hasNextPage endCursor }
  }
}
```

```graphql
# inventoryItems:query — validada con api_version 2025-01
query InventoryItemCosts($ids: [ID!]!) {
  nodes(ids: $ids) {
    ... on InventoryItem {
      id
      unitCost { amount currencyCode }
    }
  }
}
```

```graphql
# productVariants:query — validada con api_version 2025-01
query VariantDetails($ids: [ID!]!) {
  nodes(ids: $ids) {
    ... on ProductVariant {
      id
      sku
      title
      product { id title vendor }
    }
  }
}
```

## Seguimiento de la sesión

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

**Al iniciar**, muestra:
```
╔══════════════════════════════════════════════╗
║  SKILL: Margen de ganancia real              ║
║  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):
```
══════════════════════════════════════════════
REPORTE DE GANANCIA Y MARGEN  (<days_back> días)
  Pedidos analizados:     <n>
  Ingreso total:          <monto>
  COGS total:             <monto>
  Envío total:            <monto>
  Comisiones totales:     <monto>
  Reembolsos totales:     <monto>
  ─────────────────────────────
  Ganancia neta:          <monto>
  Margen general:         <pct>%

  Productos más rentables:
    "<producto>"  Margen: <pct>%  Ganancia: <monto>
  Productos con menor margen:
    "<producto>"  Margen: <pct>%  Ganancia: <monto>
  Archivo: profit_report_<fecha>.csv
══════════════════════════════════════════════
```

Los montos van en la moneda de la tienda.

Para `format: json`, muestra:
```json
{
  "skill": "shopify-margen-de-ganancia",
  "store": "<dominio>",
  "period_days": 30,
  "orders_analyzed": 0,
  "total_revenue": 0,
  "total_cogs": 0,
  "total_shipping": 0,
  "total_tx_fees": 0,
  "total_refunds": 0,
  "net_profit": 0,
  "overall_margin_pct": 0,
  "currency": "<moneda de la tienda>",
  "output_file": "profit_report_<fecha>.csv"
}
```

## Formato de salida
Archivo CSV `profit_report_<AAAA-MM-DD>.csv` con las columnas:
`order_id`, `order_name`, `date`, `revenue`, `cogs`, `shipping`, `discounts`, `tx_fees`, `refunds`, `net_profit`, `margin_pct`

Para la agrupación por producto: `product_id`, `product_title`, `vendor`, `units_sold`, `revenue`, `cogs`, `net_profit`, `margin_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 |
| Falta unitCost | El producto no tiene costo cargado | Márcalo como «COGS desconocido»: inclúyelo en el reporte, pero exclúyelo del cálculo de margen |
| Pedidos reembolsados | Reembolso total | Inclúyelos con ingreso neto 0 si `include_refunded: true` |

## Buenas prácticas
- Carga `inventoryItem.unitCost` (costo por artículo) en todos los productos para tener un COGS exacto. Sin ese dato no se puede calcular el margen.
- Ajusta la estimación de la comisión por transacción según tu procesador de pagos (el valor predeterminado, 2.9 % + 0.30 USD, corresponde a Shopify Payments en Estados Unidos).
- Usa `group_by: product` para identificar qué productos tienen margen positivo y cuáles negativo.
- Cruza el resultado con `shopify-velocidad-de-venta` para encontrar artículos que se venden rápido pero con poco margen y que quizás necesiten un nuevo precio.
- Usa los resultados con `shopify-ajuste-masivo-precios` para subir el precio de los productos con margen negativo.
