RFC-002: Normalización de match_categories¶
Fecha: 2026-01-15 Estado: Pendiente de implementación Autor: Sistema / HQuintero Relacionado con: RFC-001 (Unificación de Categorías)
Resumen Ejecutivo¶
Este documento describe el plan para normalizar la tabla match_categories, estableciendo una regla clara donde category_a siempre corresponde a un Supplier y category_b siempre corresponde a un Partner.
Problema Actual¶
La tabla match_categories actualmente mezcla diferentes tipos de relaciones sin una estructura clara:
Estado Actual (162 matches)¶
| Tipo de Relación | Cantidad | Problema |
|---|---|---|
| Supplier CL → Supplier Externo | 115 | Relación entre suppliers, no hacia partners |
| Partner TN → Supplier CL | 37 | Invertido (Partner en A, Supplier en B) |
| Huérfanos | 10 | category_a apunta a IDs inexistentes |
Desglose de Supplier → Supplier (115)¶
| Relación | Cantidad |
|---|---|
| Supplier:1 (CL) → Supplier:2 (FX - Fastrax) | 37 |
| Supplier:1 (CL) → Supplier:999 (CPI - Compras Paraguai) | 49 |
| Supplier:1 (CL) → Supplier:888 (NGO) | 25 |
| Supplier:1 (CL) → Supplier:8 (TG - Todegol) | 2 |
| Supplier:1 (CL) → Supplier:3 (GT - Gametec) | 1 |
| Supplier:1 (CL) → Supplier:9 (JWE) | 1 |
Matches Huérfanos (10)¶
| Match ID | category_a_id | category_b (nombre) |
|---|---|---|
| 84 | 870 (NO EXISTE) | Cooler p/ Gabinete |
| 83 | 870 (NO EXISTE) | Memoria RAM para Notebook |
| 86 | 870 (NO EXISTE) | Memoria RAM para PC |
| 82 | 870 (NO EXISTE) | Notebooks |
| 85 | 870 (NO EXISTE) | Procesadores |
| 80 | 871 (NO EXISTE) | Celulares |
| 79 | 877 (NO EXISTE) | Auriculares |
| 78 | 880 (NO EXISTE) | Tablets |
| 87 | 895 (NO EXISTE) | Impresoras |
| 81 | 934 (NO EXISTE) | Aire Acondicionado |
Propuesta de Solución¶
Nueva Regla¶
match_categories:
category_a_id = SIEMPRE Supplier (cualquiera: CL, FX, NGO, CPI, etc.)
category_b_id = SIEMPRE Partner (cualquiera: CL, TN, etc.)
Propósito¶
Sugerir categoría de Partner al confirmar un producto que viene de un Supplier.
Flujo Propuesto¶
┌─────────────────────────────────────────────────────────────────┐
│ ENTRADA SALIDA │
│ │
│ Category (Supplier FX) ──match──► Category (Partner CL) │
│ Category (Supplier NGO) ──match──► Category (Partner CL) │
│ Category (Supplier CPI) ──match──► Category (Partner CL) │
│ Category (Supplier CL) ──match──► Category (Partner TN) │
│ Category (Supplier CL) ──match──► Category (Partner CL) │
│ │
│ + propagación: Si Supplier X → CL y CL → TN, │
│ entonces crear Supplier X → TN │
└─────────────────────────────────────────────────────────────────┘
Transformaciones Requeridas¶
1. Matches Supplier CL → Supplier Externo (115 registros)¶
Lógica de transformación:
ANTES:
Supplier CL (cat_a) → Supplier FX (cat_b)
DESPUÉS:
1. Buscar Partner CL con mismo nombre que Supplier CL
2. Crear: Supplier FX → Partner CL
3. Si Supplier CL tiene match con Partner TN:
Crear: Supplier FX → Partner TN
4. Eliminar match original
Ejemplo concreto (verificado en base de datos 2026-01-15):
ANTES (estado actual):
match_categories:
| category_a_id | category_b_id |
| 2 (Supplier CL: Accesorios p/ Notebook) | 110 (Supplier FX: Accesorios) |
Y existe (invertido):
| 1325 (Partner TN: Accesorios y Complementos) | 2 (Supplier CL: Accesorios p/ Notebook) |
DESPUÉS (normalizado):
match_categories:
| category_a_id | category_b_id |
| 110 (Supplier FX: Accesorios) | 1875 (Partner CL: Accesorios p/ Notebook) |
| 110 (Supplier FX: Accesorios) | 1325 (Partner TN: Accesorios y Complementos) |
| 2 (Supplier CL: Accesorios p/ Notebook) | 1875 (Partner CL: Accesorios p/ Notebook) |
| 2 (Supplier CL: Accesorios p/ Notebook) | 1325 (Partner TN: Accesorios y Complementos) |
IDs reales verificados: - Supplier CL "Accesorios p/ Notebook": ID 2 - Supplier FX "Accesorios": ID 110 - Partner CL "Accesorios p/ Notebook": ID 1875 - Partner TN "Accesorios y Complementos de Computadora": ID 1325
2. Matches Partner TN → Supplier CL (37 registros)¶
Lógica de transformación:
ANTES:
Partner TN (cat_a) → Supplier CL (cat_b)
DESPUÉS:
Supplier CL (cat_a) → Partner TN (cat_b)
(simplemente invertir)
3. Nuevos matches Supplier CL → Partner CL¶
Lógica:
Para cada categoría de Supplier CL:
1. Buscar categoría de Partner CL con mismo nombre
2. Si existe: crear match Supplier CL → Partner CL
3. Si no existe: no crear (no crear categorías faltantes)
Categorías de Supplier CL sin equivalente en Partner CL (9): - Celulares - Pantallas interactivas - Protector - Usados - Antishock - Servicios - NB USADAS - Test 1 - Test 2
Decisión: No crear estas categorías. Los matches no se crearán para ellas.
4. Eliminación de huérfanos (10 registros)¶
Eliminar matches con IDs: 78, 79, 80, 81, 82, 83, 84, 85, 86, 87
Resultado Esperado¶
Antes de la Migración¶
| Tipo | Cantidad |
|---|---|
| Supplier → Supplier | 115 |
| Partner → Supplier | 37 |
| Huérfanos | 10 |
| Total | 162 |
Después de la Migración (estimado)¶
| Tipo de Match | Cantidad Estimada |
|---|---|
| Supplier CL → Partner CL | ~94 |
| Supplier CL → Partner TN | 37 |
| Supplier FX → Partner CL | ~37 |
| Supplier FX → Partner TN | ~10-15 |
| Supplier NGO → Partner CL | ~25 |
| Supplier NGO → Partner TN | ~8-10 |
| Supplier CPI → Partner CL | ~49 |
| Supplier CPI → Partner TN | ~12-15 |
| Otros suppliers → Partners | ~4 |
| Total estimado | ~280-320 |
Validación del Modelo¶
Cambios en MatchCategory::add()¶
// ANTES: Validación parcial
if ($categoryA->categorizable_type == Supplier::class) {
if ($categoryA->categorizable_id != SupplierConstants::CL) {
return "Categoría A debe ser de CL";
}
}
// DESPUÉS: Validación estricta
if ($categoryA->categorizable_type !== Supplier::class) {
return "Categoría A debe ser de un Supplier";
}
if ($categoryB->categorizable_type !== Partner::class) {
return "Categoría B debe ser de un Partner";
}
Dependencias¶
Servicios que usan match_categories¶
| Servicio | Archivo | Uso |
|---|---|---|
| ProductService | app/Services/ProductService.php |
getTNCategoriesIds(), getMatchesWithTN() |
| ProductTransformer | app/Services/TiendaNaranja/Transformers/ProductTransformer.php |
getTNCategoriesFromProductItem() |
| MatchCategory Model | app/Models/MatchCategory.php |
getMatches(), getMatchesWithTN() |
Impacto en servicios¶
Después de la normalización, los métodos existentes seguirán funcionando porque:
- getMatchesWithTN() busca matches donde alguna categoría sea de Partner TN
- La lógica de búsqueda no cambia, solo la estructura de datos
Plan de Implementación¶
Fase 1: Preparación¶
- Backup de tabla
match_categories - Crear comando de diagnóstico (dry-run)
Fase 2: Ejecución¶
- Eliminar huérfanos (10 registros)
- Invertir matches Partner → Supplier (37 registros)
- Transformar matches Supplier CL → Supplier Externo (115 registros)
- Crear matches Supplier CL → Partner CL (~94 registros)
Fase 3: Validación¶
- Verificar que todos los matches cumplan la regla
- Actualizar validación en MatchCategory::add()
- Probar flujo de confirmación de productos
Comandos a Crear¶
# Diagnóstico (solo lectura)
php artisan match-categories:diagnose
# Migración (con backup automático)
php artisan match-categories:normalize
# Verificación post-migración
php artisan match-categories:verify
Riesgos y Mitigación¶
| Riesgo | Mitigación |
|---|---|
| Pérdida de datos | Backup obligatorio antes de migración |
| Matches duplicados | Verificar unicidad antes de insertar |
| Servicios afectados | Los métodos existentes son compatibles con la nueva estructura |
Verificación Técnica (2026-01-15)¶
Análisis del Código Actual¶
El método ProductService::getTNCategoriesIds() busca matches de forma bidireccional:
// Línea 129 de ProductService.php
$relatedCategoryId = $match->category_a_id == $categoryId
? $match->category_b_id
: $match->category_a_id;
Esto significa que actualmente no importa en qué columna esté la categoría, el sistema busca la otra. Después de la normalización, esto seguirá funcionando porque la búsqueda es bidireccional.
Propósito Semántico Confirmado¶
La pregunta que responde el match es:
"Dado un producto con categoría X de Supplier Y, ¿a qué categoría de Partner Z debe asignarse?"
Por lo tanto:
- category_a = Categoría de ENTRADA (Supplier - origen del producto)
- category_b = Categoría de SALIDA (Partner - destino de asignación)
Datos Verificados en Base de Datos¶
| Entidad | Nombre | ID |
|---|---|---|
| Supplier CL | Accesorios p/ Notebook | 2 |
| Supplier FX | Accesorios | 110 |
| Partner CL | Accesorios p/ Notebook | 1875 |
| Partner TN | Accesorios y Complementos de Computadora | 1325 |
Compatibilidad Post-Migración¶
Los servicios existentes (ProductService, ProductTransformer) seguirán funcionando porque:
getMatchesWithTN()busca matches donde alguna categoría sea de Partner TNgetTNCategoriesIds()usa búsqueda bidireccional- La lógica no asume una dirección específica
Notas Adicionales¶
- Esta migración NO elimina la tabla
match_categories, solo normaliza su contenido - La relación
product_partner_categories(RFC-001) es independiente y complementaria - El match sirve para SUGERIR, la relación explícita en
product_partner_categorieses la definitiva
Aprobación¶
- [ ] Revisado por: ___
- [ ] Aprobado por: ___
- [ ] Fecha de implementación: ___