# Migración de colecciones legacy (Fase 5 / Etapa 2 / sub-fase 1, fix #2) Script: `scripts/migrate_legacy_collections.py`. ## Contexto Al cerrar el fix #2 de la sub-fase 1 (categorías reales) se confirmó vía Store API (2026-08-24) que los 17 productos legacy ya tenían la `category` real correcta asignada desde una migración anterior, pero **ningún producto tenía `collection` asignada** — el campo estaba en `null` en los 27 productos existentes en ese momento (17 legacy + 1 Cybertron Sentinel + 9 demo). Sólo 2 colecciones existían en Medusa: "Universo Marvel" y "Reino de Frozen" (creadas en la auditoría de Etapa 1). Este script cierra esa brecha. De paso asigna la categoría "Figuras de accion" a `cybertron-sentinel-figura-de-coleccion-test-revalidate`, el único de los 18 productos pre-existentes (no-demo) que quedó sin categoría en la migración anterior — no es uno de los "17 legacy", es el producto usado para validar el flujo de auto-refresh en el incidente Fase 4. **Fuera de alcance, por decisión explícita del usuario:** `metadata` (brand/ageRange), precios, descripciones, imágenes. Solo `categories`/`collection_id`. ## Mapeo aplicado | Colección | Nueva/existente | Productos (handle) | |---|---|---| | Toy Story | nueva | toy-story-5-rex, toy-story-5-buzz-lightyear, toy-story-5-jessie, toy-story-5-woody | | Intensamente | nueva | intensamente-furia, intensamente-tristeza | | Lilo & Stitch | nueva | angel-lilo-y-stitch | | Universo Marvel | existente (`pcol_01M0PDFBDA0TXQNCX4E2AP8DJ5`) | mochilas-disney-coleccion-marvel, mochila-escolar-de-spider-man-..., disney-mochila-iron-man-..., mochila-de-disney-marvel-capitan-america-... | | Reino de Frozen | existente (`pcol_01M0PDFBX1PT3ZA4KHJE6C9VKT`) | mochila-ligera-...-frozen-... | | — (sin colección, por mapeo explícito del usuario) | — | minnie-y-mickey-mouse, mochila-pequena-bob-esponja, mochila-casual-...-bob-esponja-..., disney-mochila-de-poliester-..., mochila-escolar-estilo-britanico-... | Categoría faltante: `cybertron-sentinel-figura-de-coleccion-test-revalidate` → "Figuras de accion" (`pcat_01M0PDF24BRVRKKMPMXCTY7QXJ`). Las 17 categorías legacy en sí **no se tocan** — se verificaron 1:1 contra el mapeo del usuario antes de correr este script y ya coincidían exactamente (ver memoria del proyecto `project_fase5_etapa2_subfase1_categorias_2026_08_23.md`). ## Endpoints Admin API (Medusa v2) usados - `GET /admin/collections?title=X` — chequear si una colección ya existe. - `POST /admin/collections` — crear colección (`title`, `handle`). - `POST /admin/collections/:id/products` con body `{"add": [...], "remove": []}` — asignar productos a una colección. - `POST /admin/product-categories/:id/products` con body `{"add": [...], "remove": []}` — asignar productos a una categoría (mismo patrón confirmado en el fix #2 de categorías). Confirmado que **no existen** en esta versión de Medusa: `POST /admin/products/:id/categories` (404), ni `collection_id`/`category_ids` en el body de `PATCH /admin/products/:id` (400). ## Cómo correrlo Requiere un JWT de admin (`ADMIN_JWT`) obtenido vía `POST /auth/user/emailpass` — no se persiste en este repo. El bloqueo del clasificador de auto-mode sobre escrituras en lote contra producción aplica aquí igual que en el fix #2 de categorías (ver memoria `feedback_auto_mode_classifier_hard_blocks`), así que el `--apply` lo corre el usuario directamente, no Claude: ```bash export MEDUSA_BACKEND_URL=https://commerce.cruzcloud.net export ADMIN_JWT= # 1. Verificar el plan sin escribir nada python3 scripts/migrate_legacy_collections.py --dry-run # 2. Ejecutar python3 scripts/migrate_legacy_collections.py --apply ``` ## Rollback Cada asignación es aditiva (`add`) sobre relaciones many-to-many, no destructiva de datos previos. Para revertir, llamar los mismos endpoints con `{"remove": [...ids], "add": []}` con los mismos product_id/collection_id/category_id documentados arriba. Las colecciones creadas ("Toy Story", "Intensamente", "Lilo & Stitch") se pueden borrar con `DELETE /admin/collections/:id` si hace falta deshacer la migración por completo.