Saltar a contenido

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 = NULL representa un atributo configurado pero no asignado. Cuando la primera variante usa ese atributo, el registro se actualiza (asimila) con el product_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

  1. Identificar templates redundantes: Registros con product_item_id = NULL donde ya existe al menos un registro con product_item_id != NULL para el mismo (product_id, term_id)

  2. Eliminar templates redundantes: Solo mantener el registro con product_item_id = NULL si NO existe ninguna variante asignada

  3. 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

  1. ✅ No existen registros con product_item_id = NULL si hay al menos una variante con ese (product_id, term_id)
  2. ✅ Al asignar primera variante a un atributo, el registro se actualiza (no se duplica)
  3. ✅ Al desasignar última variante, el registro vuelve a product_item_id = NULL
  4. ✅ Al eliminar atributo del producto, todas las variantes pierden ese atributo
  5. ✅ No se permite crear variantes con combinación de atributos duplicada
  6. ✅ No se permite asignar términos no configurados en el producto
  7. ✅ 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]