Saltar a contenido

RFC-003: Deprecación de product_categories y migración a product_partner_categories

Fecha: 2026-01-15 Estado: Pendiente de implementación Autor: Sistema / HQuintero Relacionado con: RFC-001 (Unificación de Categorías), RFC-002 (Normalización match_categories)


Resumen Ejecutivo

Este documento analiza el impacto de deprecar la tabla product_categories y el campo products.category_id, migrando a la nueva estructura product_partner_categories que relaciona productos con categorías de Partners.


Cambio Conceptual

Modelo Anterior (Legacy)

Product.category_id ──► ProductCategory (tabla product_categories)
                              │
                              ├── id_woocommerce (sync WooCommerce)
                              ├── id_medusa_category (sync Medusa)
                              ├── seo_formula
                              ├── description_prompt
                              └── categoryAttributes (dimensiones, prompts)

Problema: La categoría es una sola, sin distinción de destino (Partner).

Modelo Nuevo

Product ──► product_partner_categories ──► Category (Partner CL)
        │                              └──► Category (Partner TN)
        │                              └──► Category (Partner X...)
        │
        └── partnerCategories() : BelongsToMany
        └── mainCategory() : Category (Partner CL)
        └── tnCategories() : BelongsToMany (Partner TN)

Ventaja: Un producto puede tener categorías diferentes por cada Partner de salida.


Inventario de Dependencias

1. Modelo Product

Archivo Línea Código Impacto
app/Models/Product.php 20 'category_id' en fillable Eliminar
app/Models/Product.php 37-40 category() → ProductCategory Reemplazar por mainCategory()
app/Models/Product.php 42-52 categoryAttributes() hasManyThrough Migrar a Metadata

2. Modelo ProductCategory

Archivo Uso Impacto
app/Models/ProductCategory.php Modelo completo Deprecar/Eliminar
app/Observers/ProductCategoryObserver.php Sync a Medusa Migrar a Category observer

Campos de ProductCategory a migrar:

Campo Destino Nuevo
name Category.name (Partner CL)
parent_id_category Category.parent_id
id_woocommerce Category.external_code o Metadata
id_medusa_category Category.external_code o Metadata
seo_formula Metadata (categorizable)
description_prompt Metadata (categorizable)
short_description_prompt Metadata (categorizable)
seo_name_prompt Metadata (categorizable)

3. Vista de Base de Datos (product_item_view)

Archivo Línea Código Impacto
Migración view 34 pb.category_id AS id_category Actualizar vista SQL

La vista usa: products.category_id para obtener id_category

Debe cambiar a: Obtener de product_partner_categories donde Partner = CL

4. Servicios de Sincronización

WooCommerce

Archivo Línea Código
WoocommerceApiService.php 620-621 $product->product->category->id_woocommerce
WoocommerceApiService.php 243 syncCategory(ProductCategory $category)

Cambio requerido: - Obtener categoría de Partner CL: $product->product->mainCategory() - El id_woocommerce debe estar en la categoría de Partner CL o en Metadata

TiendaNaranja

Archivo Línea Código
ProductTransformer.php 467-472 $item->product->category_id
ProductTransformer.php 480-496 Busca ProductCategory por ID, luego Category CL por nombre
ProductTransformer.php 510-551 getTNCategoriesFromView() usa id_category

Cambio requerido: - Usar $product->tnCategories() directamente - Eliminar búsqueda transitiva por nombre

Medusa

Archivo Línea Código
MedusaCategoryService.php 115-174 ensureAndAttachCategory() usa $p->category?->name
MedusaProductSyncService.php 2004-2008 $product->category->categoryAttributes()

Cambio requerido: - Usar $product->mainCategory() para nombre - Migrar categoryAttributes a Metadata

5. Jobs y Comandos

Archivo Uso Impacto
GenerateProductContentJob.php:190-199 $productItem->product->category para prompts Migrar prompts a Metadata
MigrateSupplierProductsToProducts.php Crea ProductCategory Actualizar para usar Category Partner CL
SyncComprasParaguaiProducts.php Asigna category_id Cambiar a product_partner_categories
TestGroupProducts.php Busca por category_id Actualizar query

6. Controladores

Archivo Método Uso
ProductCategoryController.php CRUD completo Deprecar o redirigir a Partner CL
ProductItemViewController.php:408-428 updateParent() usa category_id Cambiar a partnerCategories
AIContentController.php:291-302 Valida product->category Usar mainCategory()
SeoNameController.php:15 category->seo_formula Obtener de Metadata

7. Vistas Blade

Archivo Línea Código
product-info.blade.php 49 $productItem->product->category->name
item-table.blade.php 41 $item->product->category->name
index.blade.php 243 $item->product->category->name
classification-section.blade.php 28 $productItem->product->category_id
show.blade.php 263 $productItem->product->category_id

Cambio: Todas deben usar $product->mainCategory()?->name

8. Resources (API)

Archivo Línea Código
ProductItemResource.php 28-29 $this->product->category->name y categoryAttributes
ProductResourceCollection.php 29 $selectedProduct->product->category

9. Otros Modelos Relacionados

Modelo Relación Impacto
Keyword product_category_id → ProductCategory Migrar a Category (Partner)
CategoryAttribute id_category → ProductCategory Migrar a Metadata
ProductItemView id_category Actualizar vista SQL

Atributos de ProductCategory a Migrar

Tabla: Mapeo de Campos

Campo en ProductCategory Nuevo Destino Tabla/Modelo
id N/A (se elimina) -
name name Category (Partner CL)
parent_id_category parent_id Category (Partner CL)
id_woocommerce key='id_woocommerce' Metadata (categorizable)
id_medusa_category key='id_medusa_category' Metadata (categorizable)
seo_formula key='seo_formula' Metadata (categorizable)
description_prompt key='description_prompt' Metadata (categorizable)
short_description_prompt key='short_description_prompt' Metadata (categorizable)
seo_name_prompt key='seo_name_prompt' Metadata (categorizable)

Tabla: category_attributes → Metadata

Campo Actual Destino
id_category categorizable_id (Category Partner CL)
id_attribute Parte del key en Metadata
value value en Metadata

Estrategia de Migración

Opción A: Migración Gradual con Compatibilidad

  1. Mantener ambas relaciones temporalmente
  2. Agregar accessor en Product que prioriza nueva relación:
    public function getEffectiveCategoryAttribute(): ?Category
    {
        // Primero intentar nueva relación
        $newCategory = $this->mainCategory();
        if ($newCategory) return $newCategory;
    
        // Fallback a legacy
        return $this->category ?
            Category::forPartner(PartnerConstants::CL)
                ->where('name', $this->category->name)
                ->first()
            : null;
    }
    
  3. Actualizar servicios uno por uno
  4. Eliminar legacy cuando todo use la nueva relación

Opción B: Migración Directa

  1. Ejecutar migración de datos completa
  2. Actualizar todos los servicios en un solo deploy
  3. Eliminar legacy inmediatamente

Recomendación: Opción A (gradual) para minimizar riesgo.


Orden de Actualización Sugerido

Fase 1: Preparación (sin romper nada)

  1. ✅ Crear tabla product_partner_categories
  2. ✅ Agregar relaciones nuevas en Product (partnerCategories, mainCategory, tnCategories)
  3. ✅ Crear tabla metadata y modelo
  4. Migrar datos de ProductCategory → Category (Partner CL) + Metadata
  5. Migrar datos de products.category_id → product_partner_categories

Fase 2: Accessor de Compatibilidad

  1. Crear getEffectiveCategoryAttribute() en Product
  2. Crear helpers: getEffectiveCategoryName(), getEffectiveCategoryPrompts()

Fase 3: Actualizar Servicios (por prioridad)

Prioridad Servicio Razón
1 WoocommerceApiService Sync activo diario
2 ProductTransformer (TN) Sync activo diario
3 MedusaCategoryService Sync activo
4 GenerateProductContentJob Generación AI
5 Vistas Blade UI interna
6 API Resources Consumidores externos

Fase 4: Actualizar Vista SQL

  1. Modificar product_item_view para obtener categoría de product_partner_categories
  2. Probar que no rompa queries existentes

Fase 5: Limpieza

  1. Eliminar campo products.category_id
  2. Deprecar modelo ProductCategory
  3. Eliminar tabla product_categories (después de período de gracia)
  4. Eliminar CategoryAttribute (migrado a Metadata)

Impacto en Keywords

La tabla keywords tiene product_category_id que apunta a product_categories.

Opciones: 1. Migrar a category_id apuntando a categories (Partner CL) 2. Eliminar keywords y recrear asociados a Category nueva

Recomendación: Opción 1 con migración de datos.


Vista SQL Actualizada (Propuesta)

-- Cambio en product_item_view
-- ANTES:
pb.category_id AS id_category

-- DESPUÉS:
(
    SELECT ppc.category_id
    FROM product_partner_categories ppc
    INNER JOIN categories c ON ppc.category_id = c.id
    WHERE ppc.product_id = pb.id
    AND c.categorizable_type = 'App\\Models\\Partner'
    AND c.categorizable_id = 2  -- Partner CL
    LIMIT 1
) AS id_category

Riesgos y Mitigación

Riesgo Probabilidad Impacto Mitigación
Sync WooCommerce falla Alta Alto Accessor de compatibilidad
Sync TN falla Alta Alto Probar en staging primero
Vista SQL lenta Media Medio Índices en product_partner_categories
Pérdida de prompts AI Baja Alto Migrar a Metadata antes de eliminar
Keywords huérfanos Media Bajo Migrar antes de eliminar tabla

Comandos de Migración Implementados

Migración de Dimensiones

# Ver estado de dimensiones Supplier CL vs Partner CL
php artisan categories:migrate-dimensions --dry-run

# Migrar dimensiones de Supplier CL a Partner CL
php artisan categories:migrate-dimensions

# Incluir limpieza de dimensiones en Supplier CL
php artisan categories:migrate-dimensions --clean-supplier

Migración de id_woocommerce

# Ver estado de id_woocommerce en Partner CL
php artisan categories:migrate-woocommerce-ids --dry-run

# Migrar id_woocommerce de product_categories a metadata de Partner CL
php artisan categories:migrate-woocommerce-ids

Sincronización de Categorías con WooCommerce (RFC-002)

# Ver qué categorías se sincronizarían (dry-run)
php artisan sync:woocommerce-categories --dry-run

# Sincronizar solo categorías sin id_woocommerce
php artisan sync:woocommerce-categories --missing-only

# Sincronizar y crear en WooCommerce las que no existen
php artisan sync:woocommerce-categories --create

# Combinación: solo las que faltan, crear si no existen
php artisan sync:woocommerce-categories --missing-only --create

Nota: Este comando reemplaza al deprecado app:sync-woocommerce-categories que usaba el modelo legacy ProductCategory.

Migración de master_category_id (TN → Partner CL)

# Ver relaciones TN → Supplier CL → Partner CL
php artisan categories:migrate-tn-master --dry-run

# Configurar master_category_id de categorías TN apuntando a Partner CL
php artisan categories:migrate-tn-master

Cambios en Observers

CategoryObserver (RFC-002)

El CategoryObserver fue actualizado para sincronizar categorías de Partner CL con WooCommerce y Medusa:

// Al crear categoría de Partner CL:
// - Sincroniza con WooCommerce (crea categoría y guarda id_woocommerce en metadata)
// - Sincroniza con Medusa (crea categoría y guarda id_medusa_category en metadata)

// Al actualizar nombre de categoría de Partner CL:
// - Actualiza en WooCommerce
// - Actualiza en Medusa

Deprecación de ProductCategoryObserver

El ProductCategoryObserver (para product_categories legacy) queda deprecado. La sincronización ahora se maneja desde CategoryObserver para categorías de Partner CL.


Checklist Pre-Implementación

  • [ ] Backup completo de product_categories
  • [ ] Backup de category_attributes
  • [ ] Verificar que todas las categorías existen en Partner CL
  • [ ] Migrar prompts a Metadata
  • [x] Migrar id_woocommerce a Metadata (categories:migrate-woocommerce-ids)
  • [x] Migrar id_medusa_category a Metadata (ya migrado)
  • [ ] Poblar product_partner_categories desde products.category_id
  • [ ] Crear accessor de compatibilidad
  • [ ] Probar en staging cada servicio
  • [x] Actualizar CategoryObserver para sincronizar con WooCommerce y Medusa
  • [x] Eliminar fallback legacy en WoocommerceApiService::getWooCommerceCategoryIds()
  • [x] Repurpose comando sync:woocommerce-categories para Partner CL (RFC-002)
  • [x] Eliminar comando deprecado app:sync-woocommerce-categories

Métricas de Éxito

  1. Sync WooCommerce: 0 errores por categoría no encontrada
  2. Sync TiendaNaranja: Productos con categorías correctas
  3. Generación AI: Prompts funcionando desde Metadata
  4. Vista SQL: Tiempo de query < 2x actual

Aprobación

  • [ ] Revisado por: ___
  • [ ] Aprobado por: ___
  • [ ] Fecha de implementación: ___