🔄 Sincronización Automática de Productos a Shopify

📋 Descripción

Sistema de sincronización automática que replica productos y variantes creados desde ThreeTrackr hacia Shopify cuando la integración está activa y configurada para sincronizar inventario.

🏗️ Arquitectura Implementada

📦 Archivos Creados/Modificados

✅ Eventos

  • events/product-events.ts - Eventos para creación de productos y variantes

✅ Listeners

  • listeners/shopify-product.listener.ts - Maneja eventos de productos y sincroniza a Shopify

✅ Adapters (Extendido)

  • adapters/shopify-adapter.ts - Métodos para crear productos/variantes en Shopify

✅ Servicios (Modificado)

  • tenant/services/tenant-product-variant.service.ts - Emite eventos al crear variantes

✅ Resolvers (Modificado)

  • tenant/resolvers/tenant-product-variant.resolver.ts - Pasa organizationId al servicio

✅ Módulos (Actualizado)

  • integrations/integrations.module.ts - Incluye el nuevo listener

🔄 Flujo de Sincronización

1. **Creación de Variante en ThreeTrackr**

Usuario crea variante → TenantProductVariantResolver → TenantProductVariantService

2. **Emisión de Evento**

TenantProductVariantService.create() → EventEmitter.emit('tenant.product-variant.created')

3. **Procesamiento del Evento**

ShopifyProductListener.handleProductVariantCreated()

4. **Validaciones**

  • ✅ Integración de Shopify activa (status: "ACTIVE")
  • ✅ Integración configurada (isConfigured: true)
  • ✅ Sincronización de inventario habilitada (settings.syncInventory: true)

5. **Sincronización a Shopify**

CREATE:
Si producto existe:
ShopifyAdapter.addVariantToExistingProduct() → REST API de Shopify
Si producto NO existe:
ShopifyAdapter.createProductInShopify() → GraphQL + REST API de Shopify

UPDATE:
ShopifyAdapter.updateVariantInShopify() → REST API de Shopify
Si variante no existe: ShopifyAdapter.createVariantInShopifyProduct() → REST API de Shopify

DELETE:
ShopifyAdapter.deleteVariantFromShopify() → REST API de Shopify

⚙️ Configuración Requerida

🔧 Integración de Shopify

En el panel de integraciones, la integración de Shopify debe tener:

{
"status": "ACTIVE",
"isConfigured": true,
"storeUrl": "store.myshopify.com",
"apiKey": "shpat_xxxxx",
"apiVersion": "2025-07",
"settings": "{\"syncInventory\": true}"
}

📋 Campo settings (JSON)

{
"syncInventory": true, // ← Checkbox de sincronización de inventario
"syncOrders": true, // Para órdenes (ya implementado)
"syncCustomers": false // Para clientes (futuro)
}

🚀 Funcionalidades Implementadas

✅ Creación de Productos Completos

  • Producto principal con metadatos (nombre, descripción, vendor, etc.)
  • Variantes con opciones (talla, color, material)
  • Inventario inicial por sucursal
  • Imágenes y SEO

✅ Gestión de Opciones

  • Option1 Talla (automático)
  • Option2 Color (si existe)
  • Option3 Material (si existe)

✅ Mapeo de Inventario

  • Suma de inventario disponible de todas las sucursales
  • Configuración automática de tracking de inventario
  • Política de inventario (deny por defecto)

✅ Manejo de Errores

  • Logging detallado de errores
  • Continuación del flujo aunque falle la sincronización
  • Actualización de lastUsedAt en integraciones exitosas

📊 Datos Sincronizados

🛍️ Producto

{
title: string, // Nombre del producto
descriptionHtml: string, // Descripción
vendor: string, // Proveedor
productType: string, // Tipo de producto
tags: string[], // Etiquetas
status: "ACTIVE"|"DRAFT",// Estado
handle: string, // URL slug
seo: { title, description },
images: [{ src: url }], // Imágenes
options: [{ name, values }], // Opciones (talla, color, etc.)
variants: [...] // Variantes
}

🏷️ Variante

{
sku: string,
barcode: string,
price: string,
compareAtPrice?: string,
weight?: number,
weightUnit: "KG"|"G"|"LB"|"OZ",
inventoryQuantities: [{
availableQuantity: number,
locationId: string
}],
inventoryItem: {
tracked: true,
requiresShipping: true
},
inventoryPolicy: "DENY",
options: [option1, option2, option3]
}

🔍 Logging y Monitoreo

📝 Logs Implementados

  • ✅ Eventos de productos creados
  • ✅ Validación de integraciones activas
  • ✅ Estado de sincronización de inventario
  • ✅ Éxitos y errores de API Shopify
  • ✅ Mapeo de datos de productos

📊 Métricas Actualizadas

  • lastUsedAt Última sincronización exitosa
  • usageThisMonth Contador de uso (futuro)
  • lastErrorAt Último error (si ocurre)

🛠️ Extensibilidad

🔌 Patrón Adapter

El sistema está diseñado para agregar fácilmente más plataformas:

  • MercadoLibre
  • Falabella
  • WordPress/WooCommerce
  • Amazon

🎯 Próximos Pasos

  1. Actualización de Productos - Sincronizar cambios de productos existentes
  2. Actualización de Inventario - Sincronizar cambios de stock
  3. Eliminación de Productos - Manejar productos eliminados
  4. Mapeo Bidireccional - Tabla de mapeo ThreeTrackr ↔ Shopify
  5. Sincronización Manual - Botón para sincronizar productos existentes

🧪 Testing

✅ Flujo de Prueba

  1. Configurar integración de Shopify con syncInventory: true
  2. Crear producto en ThreeTrackr con variantes
  3. Verificar logs de sincronización
  4. Comprobar producto creado en Shopify
  5. Validar inventario sincronizado

🔧 Debug

Para debuggear, revisar logs del ShopifyProductListener y ShopifyAdapter:

# Filtrar logs de sincronización de productos
grep -i "shopify.*product" logs/api.log

⚠️ Limitaciones Actuales

  1. Solo Creación Por ahora solo sincroniza productos nuevos, no actualizaciones
  2. Sin Mapeo Persistente No guarda relación ThreeTrackr ID ↔ Shopify ID
  3. Location Fija Usa location por defecto de Shopify
  4. Sin Rollback Si falla, no revierte la creación en ThreeTrackr

🎯 Beneficios

  • Automático No requiere intervención manual
  • Condicional Solo sincroniza si está configurado
  • Robusto Maneja errores sin afectar el flujo principal
  • Extensible Fácil agregar más plataformas
  • Completo Sincroniza productos, variantes e inventario
  • Siguiendo Patrones Usa la arquitectura existente de eventos

📋 Eventos del Sistema

tenant.product.created

new ProductCreatedFromTenantEvent(
organizationId: string,
productData: {
id: string;
name: string;
description?: string;
vendor?: string;
productType?: string;
tags?: string;
status: string;
handle?: string;
imageUrl?: string;
images?: string[];
seoTitle?: string;
seoDescription?: string;
}
)

tenant.product-variant.created

new ProductVariantCreatedFromTenantEvent(
organizationId: string,
variantData: {
id: string;
productId: string;
sku: string;
barcode: string;
title: string;
option1: string;
option2?: string;
option3?: string;
price: number;
compareAtPrice?: number;
costPerItem?: number;
weight?: number;
weightUnit: string;
imageUrl?: string;
images?: string[];
status: string;
inventoryItems?: Array<{
branchId: string;
available: number;
committed: number;
onHand: number;
}>;
},
productData: {
name: string;
description?: string;
vendor?: string;
productType?: string;
tags?: string;
handle?: string;
imageUrl?: string;
images?: string[];
}
)

📝 Notas Técnicas

API Utilizada

  • Productos GraphQL API (productCreate) - Para metadatos básicos
  • Variantes REST API (/products/{id}/variants.json) - Más estable y confiable
  • Inventario REST API (/inventory_levels/set.json) - Configuración por ubicación
  • Búsqueda GraphQL API (products query) - Para encontrar productos existentes
  • Opciones REST API (/products/{id}.json PUT) - Para actualizar opciones del producto

Razón del Enfoque Mixto

La API GraphQL de Shopify ha tenido cambios recientes:

  • v2024-04+ Campo variants removido de ProductInput
  • v2024-07+ Mutación productVariantCreate no disponible en todas las versiones

Por esto, se usa:

  1. GraphQL para crear productos (más eficiente para metadatos)
  2. REST para crear variantes (más estable y compatible)

🔧 Correcciones Implementadas

Problema: Creación de Productos Duplicados

  • Antes Cada variante creaba un producto nuevo
  • Solución Búsqueda de productos existentes por nombre antes de crear

Problema: Error de Opciones "Unknown option(s)"

  • Antes Enviaba opciones sin definir en el producto
  • Solución Actualización automática de opciones del producto antes de crear variantes

Problema: Campos Incompletos

  • Antes No pasaba todos los campos necesarios
  • Solución Mapeo completo de datos de ThreeTrackr a Shopify

Problema: Handles Duplicados

  • Antes Error "Handle already in use" al crear productos
  • Solución Generación automática de handles únicos con verificación

Problema: Búsqueda de Productos Ineficiente

  • Antes Búsqueda simple que no encontraba productos existentes
  • Solución Múltiples estrategias de búsqueda (título exacto, parcial, handle)

Problema: Sincronización Incompleta

  • Antes Solo sincronizaba creación de variantes
  • Solución Sincronización completa CREATE, UPDATE, DELETE

Problema: Eliminación No Funcionaba

  • Antes Las variantes no se eliminaban de la DB del tenant
  • Solución Emisión de eventos en métodos update/delete + listeners correspondientes

Problema: Update Fallaba si Variante No Existía

  • Antes Error "Variant not found" al actualizar variantes inexistentes
  • Solución Lógica inteligente: intenta UPDATE, si falla hace CREATE

Problema: Eventos No Se Emitían (DELETE)

  • Antes El módulo tenant no tenía EventEmitterModule importado
  • Solución Agregar EventEmitterModule.forRoot() al TenantModule

Problema: Foreign Key Constraint en Eliminación

  • Antes Error "Foreign key constraint violated on InventoryItem_variantId_fkey"
  • Solución Eliminar primero los registros de inventario antes de eliminar la variante

✅ Funcionalidades Implementadas

  • [x] Sincronización CREATE Crear productos y variantes en Shopify
  • [x] Sincronización UPDATE Actualizar variantes existentes en Shopify (con fallback a CREATE)
  • [x] Sincronización DELETE Eliminar variantes de Shopify
  • [x] Búsqueda inteligente Encontrar productos existentes por múltiples criterios
  • [x] Handles únicos Generación automática de handles sin duplicados
  • [x] Inventario Configuración automática de inventario por ubicación
  • [x] Opciones de producto Actualización automática de opciones (talla, color, material)

🚀 Próximos Pasos

  • [ ] Implementar sincronización de actualizaciones de productos (no solo variantes)
  • [ ] Agregar soporte para imágenes de productos
  • [ ] Implementar sincronización bidireccional (Shopify → ThreeTrackr)
  • [ ] Agregar mapeo de categorías de productos
  • [ ] Implementar sincronización de precios dinámicos

🔗 Referencias