---
name: shopify-disponibilidad-kits
description: "Solo lectura: revisa tus kits y combos (nativos de Shopify o definidos con metacampos) y verifica que cada componente tenga stock suficiente para armarlos. Úsala cuando quieras evitar vender kits que no puedes despachar."
---

## Propósito
Recorre todos los productos marcados como kit o combo (ya sea con el mecanismo nativo de Shopify `requiresComponents` o con la convención del metacampo `bundle.components`) y verifica que cada variante componente tenga inventario suficiente para cubrir la proporción de unidades que pide el kit. Detecta los kits que aparecen con stock en la tienda online pero que en realidad no se pueden despachar porque se agotó uno de sus componentes. Solo lectura: no ejecuta mutaciones.

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

## Parámetros

| Parámetro | Tipo | Obligatorio | Por defecto | Descripción |
|-----------|------|-------------|-------------|-------------|
| store | string | sí | — | Dominio de la tienda (p. ej., tu-tienda.myshopify.com) |
| metafield_namespace | string | no | bundle | Namespace del metacampo donde viven las definiciones de componentes del kit |
| metafield_key | string | no | components | Clave del metacampo que contiene la lista JSON de `{variantId, quantity}` |
| safety_stock | integer | no | 0 | Considera agotado un componente si su existencia menos este margen queda por debajo de lo requerido |
| only_listed | bool | no | true | Revisa solo los kits con estado `ACTIVE` |
| format | string | no | human | Formato de salida: `human` o `json` |

## Seguridad

> ℹ️ Skill de solo lectura: no ejecuta mutaciones. Puedes correrla en cualquier momento. Solo lee inventario y metacampos; nunca ajusta las cantidades de los componentes ni la disponibilidad de los kits.

## Pasos

1. **OPERACIÓN:** `products` — consulta
   **Entradas:** `first: 250`, `query: "metafield:<namespace>.<key>:* OR product_type:bundle"`, selecciona `requiresSellingPlan`, `status`, `metafield(namespace, key)`, `variants`, cursor de paginación
   **Resultado esperado:** Productos tipo kit y sus variantes principales; pagina hasta que `hasNextPage: false`

2. Para cada kit, interpreta la lista de componentes. Los kits nativos usan `productVariant.requiresComponents` y `productVariant.productVariantComponents`. Los kits por metacampo convierten el valor JSON en `[{variantId, quantity}]`.

3. **OPERACIÓN:** `productVariants` — consulta
   **Entradas:** IDs agrupados de todas las variantes componentes únicas; selecciona `inventoryQuantity`, `inventoryItem { id }`, `product { title }`
   **Resultado esperado:** Cantidad en existencia de cada componente

4. **OPERACIÓN:** `inventoryItems` — consulta
   **Entradas:** IDs agrupados de los artículos de inventario de los componentes; selecciona `tracked`, `inventoryLevels(first: 25) { quantities }`
   **Resultado esperado:** Cantidad por ubicación de cada componente

5. Para cada kit, calcula `max_buildable_units = floor(mínimo entre los componentes de (component_on_hand - safety_stock) / required_qty)`. Marca los kits con `max_buildable_units == 0` (kit roto) o `< min_listed_inventory_threshold`.

## Operaciones GraphQL

```graphql
# products:query — validado con api_version 2025-01
query BundleProducts($query: String!, $after: String, $namespace: String!, $key: String!) {
  products(first: 250, after: $after, query: $query) {
    edges {
      node {
        id
        title
        status
        productType
        metafield(namespace: $namespace, key: $key) {
          id
          value
          type
        }
        variants(first: 100) {
          edges {
            node {
              id
              title
              sku
              inventoryQuantity
              requiresComponents
              productVariantComponents(first: 50) {
                edges {
                  node {
                    quantity
                    productVariant {
                      id
                      sku
                      inventoryQuantity
                      product {
                        id
                        title
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
```

```graphql
# productVariants:query — validado con api_version 2025-01
query ComponentVariantStock($ids: [ID!]!) {
  nodes(ids: $ids) {
    ... on ProductVariant {
      id
      sku
      inventoryQuantity
      product {
        id
        title
      }
      inventoryItem {
        id
        tracked
      }
    }
  }
}
```

```graphql
# inventoryItems:query — validado con api_version 2025-01
query ComponentInventoryLevels($ids: [ID!]!) {
  nodes(ids: $ids) {
    ... on InventoryItem {
      id
      tracked
      inventoryLevels(first: 25) {
        edges {
          node {
            location {
              id
              name
            }
            quantities(names: ["available", "on_hand", "committed"]) {
              name
              quantity
            }
          }
        }
      }
    }
  }
}
```

## Seguimiento de la sesión

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

**Al iniciar**, muestra:
```
╔══════════════════════════════════════════════╗
║  SKILL: Disponibilidad de kits y combos      ║
║  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>
```

**Al terminar**, muestra:

Para `format: human` (por defecto):
```
══════════════════════════════════════════════
DISPONIBILIDAD DE KITS Y COMBOS
  Kits revisados:              <n>
  Se pueden armar completos:   <n>
  Limitados (stock bajo):      <n>
  Rotos (no se pueden armar):  <n>

  Principales kits rotos:
    "<kit>"  Cuello de botella: "<componente>"  Necesario: <n>  Disponible: <n>
  Archivo: bundle_availability_<fecha>.csv
══════════════════════════════════════════════
```

Para `format: json`, muestra:
```json
{
  "skill": "shopify-disponibilidad-kits",
  "store": "<dominio>",
  "bundles_inspected": 0,
  "fully_buildable": 0,
  "constrained": 0,
  "broken": 0,
  "issues": [],
  "output_file": "bundle_availability_<fecha>.csv"
}
```

## Formato de salida
Archivo CSV `bundle_availability_<YYYY-MM-DD>.csv` con las columnas:
`bundle_product_id`, `bundle_title`, `bundle_variant_sku`, `max_buildable_units`, `bottleneck_component_sku`, `bottleneck_component_title`, `bottleneck_required_qty`, `bottleneck_on_hand`, `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 |
| El valor del metacampo no es un JSON válido | Configuración mal formada | Omite el kit, registra una advertencia y súmalo al conteo de errores |
| El ID de la variante componente no existe | Se eliminó el producto componente | Marca el kit como `BROKEN_REFERENCE` e inclúyelo en el resultado |
| El componente tiene `tracked: false` | Inventario sin seguimiento | Trata el componente como disponible sin límite y anótalo en el resultado |

## Buenas prácticas
- Córrela a diario si tienes muchos kits; así detectas los kits rotos antes de que un cliente compre algo que no puedes despachar.
- Usa `safety_stock` para reservar un margen para las ventas sueltas del mismo componente: los kits comparten inventario con las variantes que se venden por separado.
- Combínala con `shopify-ajuste-inventario` o `shopify-reabastecer-stock-bajo` para convertir un kit roto en un pedido de reabastecimiento.
- En los kits nativos, `requiresComponents: true` es la fuente confiable: dale prioridad sobre las convenciones de metacampos cuando existan ambas.
- Un kit con `max_buildable_units = 0` también debería despublicarse temporalmente hasta que se reabastezca el componente que lo frena; considera encadenar esta skill con `shopify-ciclo-vida-productos`.
