Modelo de Asimilación para Configuración de Atributos de Productos¶
Fecha: 2026-01-26
Versión: 1.0
Estado: Propuesta Técnica
Documento relacionado: product-attributes-variants-analysis.md
1. Resumen Ejecutivo¶
Este documento describe una mejora al modelo de gestión de atributos de productos implementado en product_configurations. El nuevo modelo, denominado "Modelo de Asimilación", elimina la duplicación de registros y establece reglas claras para la gestión del ciclo de vida de los atributos.
Problema que resuelve¶
El modelo actual genera duplicación de datos: por cada atributo configurado en un producto existe un registro "template" (product_item_id = NULL) más N registros adicionales (uno por cada variante que usa ese atributo). Esto causa:
- Inconsistencias cuando se eliminan templates
- Datos huérfanos después de migraciones
- Complejidad en la UI al mostrar atributos duplicados
- Falta de integridad referencial entre templates y variantes
Solución propuesta¶
El registro con product_item_id = NULL es temporal. Cuando la primera variante se asigna a ese atributo, el registro se actualiza (asimila) en lugar de crear uno nuevo. Esto elimina la duplicación y simplifica la gestión.
2. Contexto y Problemática¶
2.1 Modelo Actual (con problemas)¶
La tabla product_configurations utiliza dos tipos de registros:
| Tipo | product_item_id | Propósito |
|---|---|---|
| Template | NULL | Define qué atributos están disponibles para variantes |
| Variante | NOT NULL | Indica que una variante específica tiene ese atributo |
Ejemplo del problema:
Producto: iPhone 14 (product_id = 100)
Atributo: Color Negro (term_id = 201)
Variantes que usan Negro: 3
REGISTROS ACTUALES (5 registros para 1 atributo):
┌──────┬────────────┬─────────────────┬─────────┬───────────────────────┐
│ id │ product_id │ product_item_id │ term_id │ Tipo │
├──────┼────────────┼─────────────────┼─────────┼───────────────────────┤
│ 1 │ 100 │ NULL │ 201 │ Template (disponible) │
│ 2 │ 100 │ 1001 │ 201 │ Variante 1 │
│ 3 │ 100 │ 1002 │ 201 │ Variante 2 │
│ 4 │ 100 │ 1003 │ 201 │ Variante 3 │
└──────┴────────────┴─────────────────┴─────────┴───────────────────────┘
Total: 1 template + 3 variantes = 4 registros para el mismo atributo
2.2 Problemas Identificados¶
| # | Problema | Impacto |
|---|---|---|
| 1 | Duplicación de datos | N+1 registros por atributo en lugar de N |
| 2 | Eliminación no cascadea | Si se elimina el template, las variantes mantienen el atributo "fantasma" |
| 3 | Inconsistencia post-migración | Variantes con atributos pero sin template correspondiente |
| 4 | UI muestra duplicados | Al listar atributos del producto aparecen múltiples veces |
| 5 | Lógica compleja | Distinguir entre "disponible" y "asignado" requiere queries complejos |
3. Modelo de Asimilación (Solución Propuesta)¶
3.1 Principio Fundamental¶
El registro con
product_item_id = NULLrepresenta un atributo configurado pero no asignado. Cuando la primera variante usa ese atributo, el registro se actualiza (asimila) con elproduct_item_id. Solo se crean registros nuevos cuando ya existe al menos una variante usando ese atributo.
3.2 Ciclo de Vida de un Registro¶
┌─────────────────────────┐
│ CONFIGURADO │
│ product_item_id=NULL │
│ │
│ "Atributo disponible │
│ para variantes" │
└───────────┬─────────────┘
│
│ Primera variante asignada
│ (UPDATE, no INSERT)
▼
┌─────────────────────────┐
│ ASIGNADO │
│ product_item_id=X │
│ │
│ "Variante X usa este │
│ atributo" │
└───────────┬─────────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Más variantes │ │ Desasignar │ │ Eliminar del │
│ se asignan │ │ última │ │ producto │
│ (INSERT) │ │ variante │ │ │
└───────────────┘ │ (UPDATE→NULL) │ │ (DELETE all) │
└───────────────┘ └───────────────┘
3.3 Comparación: Modelo Actual vs Modelo de Asimilación¶
Escenario: Producto con atributo "Color Negro" usado por 3 variantes
| Aspecto | Modelo Actual | Modelo Asimilación |
|---|---|---|
| Registros totales | 4 (1 template + 3 variantes) | 3 (solo variantes) |
| Eliminar atributo | Template eliminado, variantes quedan huérfanas | Todos eliminados en cascada |
| Agregar atributo nuevo | INSERT con NULL | INSERT con NULL |
| Asignar primera variante | INSERT nuevo registro | UPDATE registro existente |
| Asignar segunda variante | INSERT nuevo registro | INSERT nuevo registro |
| Desasignar única variante | DELETE | UPDATE a NULL (vuelve a disponible) |
4. Reglas de Negocio¶
4.1 Configuración de Atributos en Producto¶
| ID | Regla | Descripción |
|---|---|---|
| R1 | Unicidad de término | Un term_id solo puede configurarse una vez por producto |
| R2 | Atributo compartido | Cuando is_shared = true, el product_item_id debe ser NULL siempre |
| R3 | Configuración previa | Solo se pueden asignar a variantes los términos ya configurados en el producto |
4.2 Asignación de Atributos a Variantes¶
| ID | Regla | Descripción |
|---|---|---|
| R4 | Un valor por atributo | Una variante solo puede tener un valor por cada atributo |
| R5 | Asimilación | Si existe registro con product_item_id = NULL, se actualiza en lugar de crear nuevo |
| R6 | Unicidad de permutación | No pueden existir dos variantes con exactamente la misma combinación de atributos |
4.3 Eliminación y Desasignación¶
| ID | Regla | Descripción |
|---|---|---|
| R7 | Eliminar atributo | Elimina TODOS los registros con ese (product_id, term_id) |
| R8 | Desasignar última variante | UPDATE a product_item_id = NULL (vuelve a disponible) |
| R9 | Desasignar variante (hay otras) | DELETE solo ese registro |
5. Validación de Permutaciones¶
5.1 Concepto¶
Una permutación es la combinación única de atributos variantes que identifica a una variante de producto. Dos variantes del mismo producto NO pueden tener la misma combinación.
5.2 Ejemplo de Permutaciones¶
Producto: MacBook Pro (product_id = 200)
Atributos variantes configurados:
├── Color: Plata, Gris Espacial
└── RAM: 16GB, 32GB, 64GB
Permutaciones posibles: 2 colores × 3 RAM = 6 combinaciones
┌─────────────────┬─────────┬──────────────────┐
│ Permutación │ Color │ RAM │
├─────────────────┼─────────┼──────────────────┤
│ 1 │ Plata │ 16GB │
│ 2 │ Plata │ 32GB │
│ 3 │ Plata │ 64GB │
│ 4 │ Gris │ 16GB │
│ 5 │ Gris │ 32GB │
│ 6 │ Gris │ 64GB │
└─────────────────┴─────────┴──────────────────┘
Máximo de variantes posibles: 6
5.3 Casos de Validación¶
Caso A: Asignación válida
Estado actual del producto:
├── Variante SKU-001: Plata + 16GB
└── Variante SKU-002: Gris + 16GB
Acción: Crear variante SKU-003 con Plata + 32GB
Resultado: ✅ PERMITIDO
Razón: La combinación [Plata, 32GB] no existe en ninguna otra variante
Caso B: Asignación rechazada - Combinación duplicada
Estado actual del producto:
├── Variante SKU-001: Plata + 16GB
└── Variante SKU-002: Gris + 16GB
Acción: Crear variante SKU-003 con Plata + 16GB
Resultado: ❌ RECHAZADO
Razón: La combinación [Plata, 16GB] ya existe en SKU-001
Caso C: Producto con un solo atributo variante
Atributos configurados:
└── Color: Negro, Blanco (solo 2 valores)
Estado actual:
├── Variante SKU-001: Negro
└── Variante SKU-002: Blanco
Acción: Crear variante SKU-003 con Negro
Resultado: ❌ RECHAZADO
Razón: Solo existen 2 permutaciones posibles y ambas están ocupadas.
Para agregar más variantes, primero debe configurarse otro color.
Caso D: Atributo no configurado
Atributos configurados en producto:
└── Color: Negro, Blanco
Acción: Asignar variante con Color = Azul (term_id no configurado)
Resultado: ❌ RECHAZADO
Razón: El término "Azul" no está configurado en el producto.
Debe configurarse primero a nivel de producto.
6. Ejemplos de Operaciones¶
6.1 Configurar Nuevo Atributo en Producto¶
Escenario: Agregar "Almacenamiento: 256GB" al producto iPhone 14
ANTES:
┌──────┬────────────┬─────────────────┬─────────┐
│ id │ product_id │ product_item_id │ term_id │
├──────┼────────────┼─────────────────┼─────────┤
│ 1 │ 100 │ 1001 │ 201 │ (Color Negro, variante 1001)
│ 2 │ 100 │ 1002 │ 202 │ (Color Blanco, variante 1002)
└──────┴────────────┴─────────────────┴─────────┘
ACCIÓN: Configurar Almacenamiento 256GB (term_id = 301)
DESPUÉS:
┌──────┬────────────┬─────────────────┬─────────┐
│ id │ product_id │ product_item_id │ term_id │
├──────┼────────────┼─────────────────┼─────────┤
│ 1 │ 100 │ 1001 │ 201 │
│ 2 │ 100 │ 1002 │ 202 │
│ 3 │ 100 │ NULL │ 301 │ ← NUEVO: Disponible, sin asignar
└──────┴────────────┴─────────────────┴─────────┘
6.2 Asignar Primera Variante (Asimilación)¶
Escenario: Asignar variante 1001 al atributo "256GB"
ANTES:
┌──────┬────────────┬─────────────────┬─────────┐
│ id │ product_id │ product_item_id │ term_id │
├──────┼────────────┼─────────────────┼─────────┤
│ 3 │ 100 │ NULL │ 301 │ (256GB, disponible)
└──────┴────────────┴─────────────────┴─────────┘
ACCIÓN: Asignar variante 1001 a term_id 301
DESPUÉS:
┌──────┬────────────┬─────────────────┬─────────┐
│ id │ product_id │ product_item_id │ term_id │
├──────┼────────────┼─────────────────┼─────────┤
│ 3 │ 100 │ 1001 │ 301 │ ← UPDATE (asimilación)
└──────┴────────────┴─────────────────┴─────────┘
Nota: El registro se ACTUALIZÓ, no se creó uno nuevo.
El ID 3 se mantiene, solo cambió product_item_id.
6.3 Asignar Segunda Variante¶
Escenario: Asignar variante 1002 al mismo atributo "256GB"
ANTES:
┌──────┬────────────┬─────────────────┬─────────┐
│ id │ product_id │ product_item_id │ term_id │
├──────┼────────────┼─────────────────┼─────────┤
│ 3 │ 100 │ 1001 │ 301 │ (256GB, variante 1001)
└──────┴────────────┴─────────────────┴─────────┘
ACCIÓN: Asignar variante 1002 a term_id 301
DESPUÉS:
┌──────┬────────────┬─────────────────┬─────────┐
│ id │ product_id │ product_item_id │ term_id │
├──────┼────────────┼─────────────────┼─────────┤
│ 3 │ 100 │ 1001 │ 301 │
│ 4 │ 100 │ 1002 │ 301 │ ← INSERT (no hay huérfano)
└──────┴────────────┴─────────────────┴─────────┘
Nota: Como ya no existe registro con product_item_id=NULL para term_id=301,
se crea un registro nuevo.
6.4 Desasignar Última Variante¶
Escenario: Desasignar variante 1001 del atributo "256GB" (es la única que lo tiene)
ANTES:
┌──────┬────────────┬─────────────────┬─────────┐
│ id │ product_id │ product_item_id │ term_id │
├──────┼────────────┼─────────────────┼─────────┤
│ 3 │ 100 │ 1001 │ 301 │ (256GB, única variante)
└──────┴────────────┴─────────────────┴─────────┘
ACCIÓN: Desasignar variante 1001 de term_id 301
DESPUÉS:
┌──────┬────────────┬─────────────────┬─────────┐
│ id │ product_id │ product_item_id │ term_id │
├──────┼────────────┼─────────────────┼─────────┤
│ 3 │ 100 │ NULL │ 301 │ ← UPDATE a NULL (vuelve a disponible)
└──────┴────────────┴─────────────────┴─────────┘
Nota: El atributo sigue configurado en el producto, solo está disponible
para asignar a otra variante.
6.5 Desasignar Variante (Hay Otras)¶
Escenario: Desasignar variante 1001 cuando 1002 también tiene el mismo atributo
ANTES:
┌──────┬────────────┬─────────────────┬─────────┐
│ id │ product_id │ product_item_id │ term_id │
├──────┼────────────┼─────────────────┼─────────┤
│ 3 │ 100 │ 1001 │ 301 │
│ 4 │ 100 │ 1002 │ 301 │
└──────┴────────────┴─────────────────┴─────────┘
ACCIÓN: Desasignar variante 1001 de term_id 301
DESPUÉS:
┌──────┬────────────┬─────────────────┬─────────┐
│ id │ product_id │ product_item_id │ term_id │
├──────┼────────────┼─────────────────┼─────────┤
│ 4 │ 100 │ 1002 │ 301 │ ← Solo queda este
└──────┴────────────┴─────────────────┴─────────┘
Nota: El registro ID 3 se ELIMINÓ porque aún existe otra variante (1002)
usando el mismo atributo.
6.6 Eliminar Atributo del Producto¶
Escenario: Eliminar completamente "256GB" del producto (incluyendo todas las variantes)
ANTES:
┌──────┬────────────┬─────────────────┬─────────┐
│ id │ product_id │ product_item_id │ term_id │
├──────┼────────────┼─────────────────┼─────────┤
│ 3 │ 100 │ 1001 │ 301 │
│ 4 │ 100 │ 1002 │ 301 │
│ 5 │ 100 │ 1003 │ 301 │
└──────┴────────────┴─────────────────┴─────────┘
ACCIÓN: Eliminar term_id 301 del producto 100
DESPUÉS:
┌──────┬────────────┬─────────────────┬─────────┐
│ id │ product_id │ product_item_id │ term_id │
├──────┼────────────┼─────────────────┼─────────┤
│ │ (vacío) │ │ │
└──────┴────────────┴─────────────────┴─────────┘
Nota: TODOS los registros con (product_id=100, term_id=301) se eliminan,
independientemente de product_item_id.
7. Migración de Datos Existentes¶
7.1 Problema¶
Los datos actuales tienen registros duplicados:
- 1 registro template (product_item_id = NULL)
- N registros de variantes (product_item_id = X)
7.2 Estrategia de Migración¶
-
Identificar templates redundantes: Registros con
product_item_id = NULLdonde ya existe al menos un registro conproduct_item_id != NULLpara el mismo(product_id, term_id) -
Eliminar templates redundantes: Solo mantener el registro con
product_item_id = NULLsi NO existe ninguna variante asignada -
Validar integridad: Verificar que no queden inconsistencias
7.3 Ejemplo de Migración¶
DATOS ACTUALES (con duplicación):
┌──────┬────────────┬─────────────────┬─────────┐
│ id │ product_id │ product_item_id │ term_id │
├──────┼────────────┼─────────────────┼─────────┤
│ 1 │ 100 │ NULL │ 201 │ ← Template (redundante)
│ 2 │ 100 │ 1001 │ 201 │ ← Variante
│ 3 │ 100 │ 1002 │ 201 │ ← Variante
│ 4 │ 100 │ NULL │ 202 │ ← Template (necesario, sin variantes)
└──────┴────────────┴─────────────────┴─────────┘
DESPUÉS DE MIGRACIÓN:
┌──────┬────────────┬─────────────────┬─────────┐
│ id │ product_id │ product_item_id │ term_id │
├──────┼────────────┼─────────────────┼─────────┤
│ 2 │ 100 │ 1001 │ 201 │ ← Se mantiene
│ 3 │ 100 │ 1002 │ 201 │ ← Se mantiene
│ 4 │ 100 │ NULL │ 202 │ ← Se mantiene (sin variantes)
└──────┴────────────┴─────────────────┴─────────┘
Registro ID 1 eliminado: era template redundante porque ya existen variantes.
Registro ID 4 conservado: es template necesario porque no hay variantes asignadas.
8. Impacto en el Sistema¶
8.1 Componentes Afectados¶
| Componente | Cambios Requeridos |
|---|---|
ProductConfigurationValidationService |
Implementar lógica de asimilación y validación de permutaciones |
ProductAttributeService |
Actualizar métodos de asignación/desasignación |
ProductItemViewController |
Adaptar handleVariantAttributes() al nuevo modelo |
| UI de gestión de atributos | Mostrar estados "disponible" vs "en uso" |
| Servicios de sincronización | Revisar queries de lectura de atributos |
8.2 Beneficios Esperados¶
| Beneficio | Descripción |
|---|---|
| Reducción de datos | Aproximadamente 50% menos registros para productos con variantes |
| Integridad referencial | Eliminación en cascada garantizada |
| Simplicidad de queries | Menos condiciones para distinguir templates de variantes |
| Mejor UX | UI más clara sin duplicados |
9. Criterios de Aceptación¶
- ✅ No existen registros con
product_item_id = NULLsi hay al menos una variante con ese(product_id, term_id) - ✅ Al asignar primera variante a un atributo, el registro se actualiza (no se duplica)
- ✅ Al desasignar última variante, el registro vuelve a
product_item_id = NULL - ✅ Al eliminar atributo del producto, todas las variantes pierden ese atributo
- ✅ No se permite crear variantes con combinación de atributos duplicada
- ✅ No se permite asignar términos no configurados en el producto
- ✅ Datos migrados sin pérdida de información
10. Referencias¶
- Documento original:
docs/technical/product-attributes-variants-analysis.md - Modelo Medusa JS: https://docs.medusajs.com/resources/commerce-modules/product
- Servicio actual:
app/Services/ProductConfigurationValidationService.php
Documento preparado por: Claude AI Revisado por: [Pendiente] Aprobado por: [Pendiente]