Skip to content

IRIS PoC — Abandoned Cart VTEX ↔ SAP (JDBC) ↔ CRM (Email) Alcance PoC (2 semanas): Detectar abandono en VTEX (webhook o job cada 30 min). Consultar disponibilidad y variantes (talla/color) vía vista JDBC en SAP. Enviar un email transaccional con deeplink a carrito restaurado. Auditoría básica en IRIS (métricas mínimas y log estructurado).

Notifications You must be signed in to change notification settings

christianasmussenb/iris103

Repository files navigation

IRIS E-Commerce — Abandoned Cart Recovery System

Estado:Sprint 1 COMPLETADO (3 Nov 2025) - Sistema 100% funcional

Stack: InterSystems IRIS 2025.1 + MySQL 8.0 + Java Gateway + Interoperability Production


🎯 Sistema Completado

Sistema end-to-end de recuperación de carritos abandonados con:

  1. Webhook REST API - Recibe notificaciones de VTEX
  2. Consulta de inventario REAL - MySQL vía JDBC con stock disponible y variantes alternativas
  3. Generación de emails - Archivos .txt personalizados con alternatives
  4. Auditoría completa - Persistencia en clase ECOM.Data.Audit con 35+ registros
  5. Business Process orchestration - BPL con validación, iteración, error handling

Sistema incluye arquitectura completa: REST API → Business Service → Business Process (BPL) → Business Operations (MySQL + Email + Storage) con traces y logging estructurado.


🚀 Quick Start

Requisitos

  • Docker Desktop
  • InterSystems IRIS 2025.1 en container iris102
  • MySQL 8.0 en container iris102-mysql
  • curl o Postman para testing

Testing el Sistema

# 1. Verificar que la Production está activa
curl http://localhost:52773/csp/ecommerce/health -u "SuperUser:123"

# 2. Enviar carrito abandonado
curl -X POST http://localhost:52773/csp/ecommerce/vtex/abandoned \
  -H "Content-Type: application/json" \
  -u "SuperUser:123" \
  -d '{
    "cartId": "TEST-001",
    "customerEmail": "test@example.com",
    "customerName": "Test User",
    "items": [
      {
        "sku": "SKU-001",
        "size": "M",
        "color": "BLACK",
        "qty": 1
      }
    ]
  }'

# 3. Verificar email generado
docker exec iris102 ls -lh /home/irisowner/data/emails/

# 4. Ver contenido del email
docker exec iris102 cat /home/irisowner/data/emails/email_TEST-001_*.txt

# 5. Verificar audit record
# Acceder a Portal: http://localhost:52773/csp/sys/UtilHome.csp
# SQL: SELECT * FROM ECOM_Data.Audit WHERE CartId='TEST-001'

0) Namespace

  • Namespace: ECOMMERCE (Interoperability habilitado)

1) Mensajes (Request / Response) — Core

Class ECOM.Msg.CartAbandonedRequest Extends Ens.Request
{
  Property CartId As %String(MAXLEN=100);
  Property CustomerEmail As %String(MAXLEN=200);
  Property CustomerName As %String(MAXLEN=120);
  Property Items As %DynamicArray; // [{"sku":"SKU-001","qty":1,"size":"M","color":"Black"}, ...]
  Property LastUpdate As %TimeStamp;
}

Class ECOM.Msg.CartAbandonedResponse Extends Ens.Response
{
  Property Accepted As %Boolean;
  Property Reason As %String(MAXLEN=512);
}

Mensajes para consulta a SAP (JDBC) — Disponibilidad y variantes

Class ECOM.Msg.SAPCheckRequest Extends Ens.Request
{
  Property CartId As %String;
  Property Material As %String;            // material base SAP
  Property Size As %String;                // talla
  Property Color As %String;               // color
  Property Qty As %Integer;
}

Class ECOM.Msg.SAPCheckResponse Extends Ens.Response
{
  Property CartId As %String;
  Property CurrentStock As %Integer;       // stock para talla/color solicitados
  Property Variants As %DynamicArray;      // alternativas: [{"size":"L","color":"Black","stock":7}, ...]
}

Mensajes para CRM Email

Class ECOM.Msg.CRMEmailRequest Extends Ens.Request
{
  Property ToEmail As %String;
  Property Template As %String;           // e.g. "ABANDONED_CART_V1"
  Property Tokens As %DynamicObject;      // { name, productName, original:{size,color}, alternatives:[...], deeplink }
}

Class ECOM.Msg.CRMEmailResponse Extends Ens.Response
{
  Property Status As %String;             // OK/ERROR
  Property MessageId As %String;
  Property ErrorMsg As %String(MAXLEN=2000);
}

Mensajes genéricos Call (para callouts sincrónicos simulados)

Class ECOM.Msg.CallRequest Extends Ens.Request
{
  Property Action As %String;             // "SAP.CHECK" | "CRM.SEND" | ...
  Property Payload As %DynamicObject;
  Property Timeout As %Integer [ InitialExpression = 15 ];
}

Class ECOM.Msg.CallResponse Extends Ens.Response
{
  Property Status As %String;             // OK/ERROR/TIMEOUT
  Property Code As %Integer;
  Property Message As %String(MAXLEN=2000);
  Property Raw As %DynamicObject;
}

2) Business Services — VTEX Webhook y/o Poller

A) Webhook HTTP (recomendado)

Class ECOM.BS.VTEXWebhook Extends Ens.BusinessService
{
  Parameter ADAPTER = "EnsLib.HTTP.InboundAdapter";
  Property URLMap As %String [ InitialExpression = "/vtex/abandoned" ];

  Method OnProcessInput(pInput As %Stream.Object, Output pResponse As Ens.Response) As %Status
  {
    #dim sc As %Status = $$$OK
    Try {
      Set tBody = pInput.Read()
      Set tDyn = {}.%FromJSON(tBody)
      Set req = ##class(ECOM.Msg.CartAbandonedRequest).%New()
      Set req.CartId = tDyn.cartId
      Set req.CustomerEmail = tDyn.customer.email
      Set req.CustomerName = $Get(tDyn.customer.name)
      Set req.Items = tDyn.items
      Set req.LastUpdate = $ZDT($H,3)
      Set sc = ..SendRequestAsync("ECOM.BP.CartRecovery", req)
    } Catch ex { Set sc = ex.AsStatus() }
    Quit sc
  }
}

B) Poller (batch cada 30 min) — opcional

Class ECOM.BS.VTEXPoller Extends Ens.BusinessService
{
  Parameter ADAPTER = "EnsLib.ScheduleHandler"; // o EnsLib.File.InboundAdapter si lees JSONs
  Property EveryMinutes As %Integer [ InitialExpression = 30 ];

  Method OnTask() As %Status
  {
    // Simulación: leer archivo o generar payloads de prueba
    Set req = ##class(ECOM.Msg.CartAbandonedRequest).%New()
    Set req.CartId=$SYSTEM.Util.CreateGUID()
    Set req.CustomerEmail="demo@cliente.cl"
    Set req.CustomerName="Demo Cliente"
    Set req.Items=[{"sku":"SKU-001","qty":1,"size":"M","color":"Black"}]
    Set req.LastUpdate=$ZDT($H,3)
    Quit ..SendRequestAsync("ECOM.BP.CartRecovery", req)
  }
}

3) Business Process (BPL) — Orquestación Cart Recovery

Class ECOM.BP.CartRecovery Extends Ens.BusinessProcessBPL
{
XData BPMDefinition [ XMLNamespace = "http://www.intersystems.com/bpl" ]
{
<process language="objectscript" requestClass="ECOM.Msg.CartAbandonedRequest" responseClass="ECOM.Msg.CartAbandonedResponse">
  <sequence>
    <!-- 1) Validación básica -->
    <code>
      Set tSC = $$$OK
      If (request.CartId="")!(request.CustomerEmail="") { Set tSC=$$$ERROR($$$GeneralError,"Missing CartId/Email") }
    </code>
    <if condition="($IsOK(tSC)=0)"><throw statusVar="tSC"/></if>

    <!-- 2) Fan-out: por cada item consultar SAP (simulado o real) -->
    <foreach property="request.Items" keyVar="idx" valueVar="it">
      <assign property="tReqSAP.CartId" value="request.CartId"/>
      <assign property="tReqSAP.Material" value="it.sku"/>
      <assign property="tReqSAP.Size" value="it.size"/>
      <assign property="tReqSAP.Color" value="it.color"/>
      <assign property="tReqSAP.Qty" value="it.qty"/>
      <call target="ECOM.BO.SAPInventory" async="false" requestClass="ECOM.Msg.SAPCheckRequest" responseVar="tRespSAP">
        <request>
          <assign property="CartId" value="tReqSAP.CartId"/>
          <assign property="Material" value="tReqSAP.Material"/>
          <assign property="Size" value="tReqSAP.Size"/>
          <assign property="Color" value="tReqSAP.Color"/>
          <assign property="Qty" value="tReqSAP.Qty"/>
        </request>
      </call>
      <code>
        // Agregar a una lista acumulada de alternativas por ítem
        If '$IsObject(AltList) Set AltList = ##class(%DynamicArray).%New()
        Do AltList.%Push(tRespSAP.Variants)
      </code>
    </foreach>

    <!-- 3) Construir deeplink y payload de email -->
    <code>
      Set dl = "https://shop.example.com/restore?cart="_request.CartId
      Set tokens = {}.%New()
      Do tokens.%Set("name", request.CustomerName)
      Do tokens.%Set("deeplink", dl)
      Do tokens.%Set("alternatives", AltList)
      // tomar el primer producto como referencia
      Set first = request.Items.%Get(0)
      Do tokens.%Set("productName", first.sku)
      Do tokens.%Set("original", {"size":first.size, "color":first.color})
    </code>

    <!-- 4) Enviar email por CRM (simulado) -->
    <call target="ECOM.BO.CRMEmail" async="false" requestClass="ECOM.Msg.CRMEmailRequest" responseVar="tRespCRM">
      <request>
        <assign property="ToEmail" value="request.CustomerEmail"/>
        <assign property="Template" value="'ABANDONED_CART_V1'"/>
        <assign property="Tokens" value="tokens"/>
      </request>
    </call>

    <!-- 5) Auditoría básica -->
    <call target="ECOM.BO.Storage" async="true" requestClass="ECOM.Msg.CallRequest">
      <request>
        <assign property="Action" value="'AUDIT'"/>
        <assign property="Payload" value="{""cartId"":request.CartId, ""emailStatus"":tRespCRM.Status}"/>
      </request>
    </call>

    <!-- 6) Respuesta del BP -->
    <assign property="response.Accepted" value="($Select(tRespCRM.Status=""OK"":1,1:0))"/>
    <assign property="response.Reason" value="($Select(tRespCRM.Status=""OK"":"Sent",""Failed to send email""))"/>
  </sequence>
</process>
}
}

4) Business Operations — Simuladores y Storage

A) SAP Inventory (simulado) — lee una “vista” local o datos embebidos

Class ECOM.BO.SAPInventory Extends Ens.BusinessOperation
{
  Parameter INVOCATION = "Queue";
  Property UseMock As %Boolean [ InitialExpression = 1 ];

  Method OnMessage(pReq As ECOM.Msg.SAPCheckRequest, Output pResp As ECOM.Msg.SAPCheckResponse) As %Status
  {
    Set sc=$$$OK, pResp=##class(ECOM.Msg.SAPCheckResponse).%New()
    Set pResp.CartId=pReq.CartId
    Try {
      If ..UseMock {
        // Mock: stock 0 para talla exacta; ofrecer variantes
        Set pResp.CurrentStock = 0
        Set alts = ##class(%DynamicArray).%New()
        Do alts.%Push({"size":"L","color":pReq.Color,"stock":7})
        Do alts.%Push({"size":pReq.Size,"color":"Navy","stock":3})
        Do alts.%Push({"size":"S","color":pReq.Color,"stock":2})
        Set pResp.Variants = alts
      } Else {
        // Real JDBC: SELECT a la vista ATP (pseudocódigo)
        // Use EnsLib.SQL.OutboundAdapter o %SQL.Statement
        // SELECT stock FROM V_ATP WHERE material=?, size=?, color=?
      }
    } Catch ex { Set sc=ex.AsStatus() }
    Quit sc
  }
}

B) CRM Email (simulado)

Class ECOM.BO.CRMEmail Extends Ens.BusinessOperation
{
  Parameter INVOCATION = "Queue";
  Property UseMock As %Boolean [ InitialExpression = 1 ];

  Method OnMessage(pReq As ECOM.Msg.CRMEmailRequest, Output pResp As ECOM.Msg.CRMEmailResponse) As %Status
  {
    Set sc=$$$OK, pResp=##class(ECOM.Msg.CRMEmailResponse).%New()
    Try {
      If ..UseMock {
        Set pResp.Status="OK", pResp.MessageId=$SYSTEM.Util.CreateGUID()
      } Else {
        // Real: EnsLib.HTTP.OutboundAdapter -> POST /email
      }
    } Catch ex { Set sc=ex.AsStatus(), pResp.Status="ERROR", pResp.ErrorMsg=$ZStatus }
    Quit sc
  }
}

C) Storage/Auditoría mínima

Class ECOM.BO.Storage Extends Ens.BusinessOperation
{
  Parameter INVOCATION = "Queue";

  ClassMethod EnsureTables()
  {
    // Tablas simples para auditoría
    &sql(CREATE TABLE IF NOT EXISTS CartAudit(
      CartId VARCHAR(100),
      Email VARCHAR(200),
      Status VARCHAR(20),
      TS TIMESTAMP
    ))
  }

  Method OnMessage(pReq As ECOM.Msg.CallRequest, Output pResp As ECOM.Msg.CallResponse) As %Status
  {
    Set sc=$$$OK, pResp=##class(ECOM.Msg.CallResponse).%New()
    Try {
      If pReq.Action="AUDIT" {
        Set cartId = pReq.Payload.cartId
        Set status = pReq.Payload.emailStatus
        &sql(INSERT INTO CartAudit(CartId,Email,Status,TS) VALUES(?,?,?,?,?)) // simplificado: ajusta columnas
      }
      Set pResp.Status="OK", pResp.Code=200
    } Catch ex { Set sc=ex.AsStatus(), pResp.Status="ERROR", pResp.Message=$ZStatus }
    Quit sc
  }
}

Nota: La sentencia SQL de INSERT arriba es un placeholder: adapta columnas y parámetros según Payload. Alternativamente, persiste en una clase persistente ECOM.Audit.CartAudit.


5) Production — Wiring

Class ECOM.Production Extends Ens.Production
{
XData ProductionDefinition
{
<Production Name="ECOM.Production">
  <Description>Abandoned Cart PoC</Description>

  <!-- Services -->
  <Item Name="BS.VTEXWebhook" ClassName="ECOM.BS.VTEXWebhook" PoolSize="1" Enabled="true">
    <Setting Target="Adapter" Name="URLMap" Value="/vtex/abandoned"/>
  </Item>
  <Item Name="BS.VTEXPoller" ClassName="ECOM.BS.VTEXPoller" PoolSize="1" Enabled="false"/>

  <!-- Process -->
  <Item Name="BP.CartRecovery" ClassName="ECOM.BP.CartRecovery" PoolSize="1" Enabled="true"/>

  <!-- Operations -->
  <Item Name="BO.SAPInventory" ClassName="ECOM.BO.SAPInventory" PoolSize="2" Enabled="true"/>
  <Item Name="BO.CRMEmail" ClassName="ECOM.BO.CRMEmail" PoolSize="2" Enabled="true"/>
  <Item Name="BO.Storage" ClassName="ECOM.BO.Storage" PoolSize="1" Enabled="true"/>
</Production>
}
}

6) Payload de prueba (Webhook VTEX simulado)

{
  "cartId": "c_123",
  "customer": { "email": "ana@cliente.cl", "name": "Ana" },
  "items": [
    { "sku": "SKU-001", "qty": 1, "size": "M", "color": "Black" }
  ],
  "lastUpdate": "2025-10-28T12:00:00Z"
}

cURL (si usas HTTP Inbound):

curl -X POST "http://localhost:52773/csp/ecommerce/vtex/abandoned" \
  -H "Content-Type: application/json" \
  -d @payload.json

Ajusta path según tu aplicación web/dispatch class.


7) Puesta en marcha rápida

ZN "ECOMMERCE"
Do ##class(Ens.Director).StopProduction()
Set sc=##class(Ens.Config.Production).CreateProductionFromClass("ECOM.Production")
Write $SYSTEM.Status.DisplayError(sc)
Do ##class(Ens.Director).StartProduction()
  • (Opcional) Ejecuta Do ##class(ECOM.BO.Storage).EnsureTables() en la Terminal para crear tablas.
  • Envía el cURL del payload para recorrer el flujo.

8) Métricas mínimas y logs

  • Logs estructurados: usa $$$LOGINFO/$$$LOGERROR en BOs para registrar CartId, tiempos y resultados.
  • Métricas: crea un BO ligero ECOM.BO.Metrics para contar procesados y errores (almacenado en clase persistente o global). Llama de forma asíncrona desde el BPL.

9) Cambio de Mock → Real

  • ECOM.BO.SAPInventory.UseMock=0 y configurar EnsLib.SQL.OutboundAdapter o %SQL.Statement hacia la vista JDBC en SAP (solo lectura).
  • ECOM.BO.CRMEmail.UseMock=0 e implementar EnsLib.HTTP.OutboundAdapter al endpoint transaccional del CRM.

10) Notas de diseño (resumen)

  • Idempotencia por CartId si el webhook se repite.
  • Deeplink incluye cart y (opcional) sku de la alternativa sugerida.
  • Privacidad: enviar solo email y datos mínimos necesarios al CRM.
  • Alertas mínimas: si CRMEmail devuelve ERROR, generar Ens.Alert.

11) Buenas Prácticas de Testing y Debugging

11.1) Revisión de LOGs desde línea de comando

Acceder al terminal de IRIS:

# Desde el host
docker exec -it iris102 iris session IRIS -U ECOMMERCE

Consultar logs de eventos:

-- Ver últimos 20 errores de todos los componentes
SELECT TOP 20 TimeLogged, ConfigName, Type, Text 
FROM Ens_Util.Log 
WHERE Type = 2  -- Type 2 = Error
ORDER BY ID DESC

-- Ver logs de un componente específico
SELECT TOP 10 TimeLogged, Type, Text 
FROM Ens_Util.Log 
WHERE ConfigName = 'BP.CartRecovery' 
ORDER BY ID DESC

-- Buscar logs por CartId o SessionId
SELECT TimeLogged, ConfigName, Text 
FROM Ens_Util.Log 
WHERE Text LIKE '%test_017_COMPLETE%' 
ORDER BY ID DESC

-- Ver logs de todos los componentes del flujo
SELECT TimeLogged, ConfigName, Type, SUBSTRING(Text,1,100) AS Message 
FROM Ens_Util.Log 
WHERE ConfigName IN ('BP.CartRecovery','BO.SAPInventory','BO.CRMEmail','BO.Storage') 
ORDER BY ID DESC

Tipos de Log:

  • Type = 1: Info
  • Type = 2: Error
  • Type = 3: Warning
  • Type = 4: Alert

11.2) Revisión de Mensajes desde línea de comando

Consultar mensajes en la cola:

// Ver todos los headers de mensajes recientes
Set rs = ##class(%ResultSet).%New("%DynamicQuery:SQL")
Set query = "SELECT TOP 20 ID, SessionId, TimeCreated, SourceConfigName, TargetConfigName, MessageBodyClassName FROM Ens.MessageHeader ORDER BY ID DESC"
Do rs.Prepare(query)
Do rs.Execute()
While rs.Next() {
    Write "ID: ", rs.Data("ID"), " | Session: ", rs.Data("SessionId"), !
    Write "From: ", rs.Data("SourceConfigName"), " -> To: ", rs.Data("TargetConfigName"), !
    Write "Body: ", rs.Data("MessageBodyClassName"), !
    Write "---", !
}

Ver contenido de un mensaje específico:

// Por ID de mensaje
Set header = ##class(Ens.MessageHeader).%OpenId(123)
If $IsObject(header) {
    Set body = ##class(Ens.MessageBody).%OpenId(header.MessageBodyId)
    zwrite body
}

// Por SessionId (ver todo el flujo)
Set query = "SELECT ID FROM Ens.MessageHeader WHERE SessionId = 528740"
// Luego abrir cada mensaje

Consultar cola de un componente:

// Ver mensajes pendientes en una cola
Do ##class(Ens.Queue).Enumerate("BP.CartRecovery", .count, .list)
Write "Mensajes en cola: ", count, !

11.3) Uso de Terminal IRIS con rutinas de diagnóstico

Ejecutar DiagnosticReport (pre-compilado):

ZN "ECOMMERCE"
Do ##class(ECOM.Setup.DiagnosticReport).Run()

Salida esperada:

=== ECOM Diagnostic Report ===
Generated: 2025-10-29 22:48:00

1. PRODUCTION STATUS
   Running: 1

2. LOG SUMMARY
   Total logs: 45

3. AUDIT SUMMARY
   Total processed: 12

4. MESSAGE VIEWER
   Go to: http://localhost:52773/csp/ecommerce/EnsPortal.MessageViewer.zen
   Search by: CartId or SessionId

Crear rutinas de diagnóstico custom:

Archivo: src/ECOM/Setup/DebugTools.cls

Class ECOM.Setup.DebugTools
{

/// Buscar mensajes por CartId
ClassMethod FindByCartId(pCartId As %String)
{
    Write "=== Messages for CartId: ", pCartId, " ===", !
    
    Set sql = "SELECT h.ID, h.SessionId, h.TimeCreated, h.SourceConfigName, h.TargetConfigName, h.Status "_
              "FROM Ens.MessageHeader h "_
              "JOIN Ens_Util.Log l ON h.SessionId = l.SessionId "_
              "WHERE l.Text LIKE ? "_
              "ORDER BY h.TimeCreated DESC"
    
    Set stmt = ##class(%SQL.Statement).%New()
    Set sc = stmt.%Prepare(sql)
    Set rs = stmt.%Execute("%"_pCartId_"%")
    
    While rs.%Next() {
        Write "Message ID: ", rs.ID, !
        Write "  Session: ", rs.SessionId, !
        Write "  Time: ", rs.TimeCreated, !
        Write "  Route: ", rs.SourceConfigName, " -> ", rs.TargetConfigName, !
        Write "  Status: ", rs.Status, !
        Write "---", !
    }
    Quit $$$OK
}

/// Ver estado de la producción
ClassMethod ProductionStatus()
{
    Write "=== Production Status ===", !
    Set running = ##class(Ens.Director).IsProductionRunning()
    Write "Running: ", running, !
    
    If running {
        Set prod = ##class(Ens.Director).GetActiveProductionName()
        Write "Production: ", prod, !
        
        // Listar componentes
        Set sql = "SELECT Name, ClassName, Enabled FROM Ens_Config.Item WHERE Production = ?"
        Set stmt = ##class(%SQL.Statement).%New()
        Do stmt.%Prepare(sql)
        Set rs = stmt.%Execute(prod)
        
        Write !, "Components:", !
        While rs.%Next() {
            Write "  ", rs.Name, " (", rs.ClassName, ") - Enabled: ", rs.Enabled, !
        }
    }
    Quit $$$OK
}

/// Limpiar logs antiguos (útil para testing)
ClassMethod CleanOldLogs(pDaysOld As %Integer = 7)
{
    Set cutoff = $HOROLOG - pDaysOld
    Set sql = "DELETE FROM Ens_Util.Log WHERE TimeLogged < ?"
    Set stmt = ##class(%SQL.Statement).%New()
    Do stmt.%Prepare(sql)
    Set result = stmt.%Execute(cutoff)
    Write "Deleted ", result.%ROWCOUNT, " old log entries", !
    Quit $$$OK
}

}

Comandos útiles para debugging:

// Ver estado de producción
Do ##class(ECOM.Setup.DebugTools).ProductionStatus()

// Buscar todos los mensajes de un cart
Do ##class(ECOM.Setup.DebugTools).FindByCartId("test_017_COMPLETE")

// Ver globals de auditoría directamente
zwrite ^ECOM("Audit","Rows")
zwrite ^ECOM("Log","Rows")

// Reiniciar producción
Do ##class(Ens.Director).StopProduction()
Do ##class(Ens.Director).StartProduction("ECOM.Production")

// Ver configuración de un componente
Set item = ##class(Ens.Config.Item).%OpenId("ECOM.Production||BP.CartRecovery")
zwrite item

11.4) Testing desde cURL

Health Check:

curl http://localhost:52773/csp/ecommerce/health

Enviar carrito abandonado:

curl -X POST "http://localhost:52773/csp/ecommerce/vtex/abandoned" \
  -H "Content-Type: application/json" \
  -d '{
    "cartId": "test_debug_001",
    "customer": {"email": "debug@test.com", "name": "Debug User"},
    "items": [
      {"sku":"SKU-TEST","qty":1,"size":"M","color":"Red"}
    ]
  }'

Script de prueba múltiple:

#!/bin/bash
# test-multiple-carts.sh

for i in {1..10}; do
  echo "Sending cart test_$i..."
  curl -X POST "http://localhost:52773/csp/ecommerce/vtex/abandoned" \
    -H "Content-Type: application/json" \
    -d "{
      \"cartId\": \"test_load_$i\",
      \"customer\": {\"email\": \"load$i@test.com\", \"name\": \"Load Test $i\"},
      \"items\": [{\"sku\":\"SKU-$i\",\"qty\":1,\"size\":\"M\",\"color\":\"Blue\"}]
    }"
  sleep 0.5
done

echo "Done. Check logs with: Do ##class(ECOM.Setup.DiagnosticReport).Run()"

11.5) Uso del Management Portal

Acceso:

http://localhost:52773/csp/sys/UtilHome.csp

Rutas importantes:

  • Message Viewer: Interoperability > Monitor > Messages
  • Event Log: Interoperability > Monitor > Event Log
  • Production Config: Interoperability > Configure > Production
  • Queues: Interoperability > Monitor > Queues
  • Business Rules: Interoperability > Configure > Business Rules

Filtros útiles en Message Viewer:

  • Por SessionId: Ver todo el flujo de un mensaje
  • Por Time Range: Últimos 15 minutos
  • Por Status: Error, Completed, Queued
  • Por Source/Target: Filtrar por componente

11.6) Checklist de Debugging

Cuando un mensaje falla, seguir este orden:

  1. Verificar que la producción está corriendo

    Write ##class(Ens.Director).IsProductionRunning()
  2. Revisar Event Log para errores

    SELECT TOP 5 TimeLogged, ConfigName, Text 
    FROM Ens_Util.Log 
    WHERE Type = 2 
    ORDER BY ID DESC
  3. Buscar el mensaje en Message Viewer

    • Por SessionId o CartId en el texto
  4. Ver el contenido del mensaje

    Set header = ##class(Ens.MessageHeader).%OpenId(12345)
    zwrite header
  5. Verificar configuración del componente

    • Management Portal > Production Config
    • Revisar Settings (Enabled, PoolSize, etc.)
  6. Revisar globals de datos custom

    zwrite ^ECOM
  7. Re-compilar si hay cambios

    Do $SYSTEM.OBJ.Load("/external/src/ECOM/BP/CartRecovery.cls","ck")
  8. Restart Production si es necesario

    Do ##class(Ens.Director).StopProduction()
    Do ##class(Ens.Director).StartProduction("ECOM.Production")

12) Estructura del Proyecto

iris103/
├── README.MD                          # Este archivo
├── SPRINT_BACKLOG.md                  # Errores pendientes y mejoras
├── src/
│   └── ECOM/
│       ├── Production.cls             # Definición de la producción
│       ├── BO/                        # Business Operations
│       │   ├── CRMEmail.cls          # Envío de emails
│       │   ├── SAPInventory.cls      # Consulta SAP
│       │   └── Storage.cls           # Auditoría y logs
│       ├── BP/                        # Business Processes
│       │   └── CartRecovery.cls      # BPL principal
│       ├── BS/                        # Business Services
│       │   ├── VTEXPoller.cls        # Polling (deshabilitado)
│       │   └── VTEXWebhook.cls       # Webhook receiver
│       ├── Msg/                       # Message classes
│       │   ├── CartAbandonedRequest.cls
│       │   ├── CartAbandonedResponse.cls
│       │   ├── SAPCheckRequest.cls
│       │   ├── SAPCheckResponse.cls
│       │   ├── CRMEmailRequest.cls
│       │   ├── CRMEmailResponse.cls
│       │   ├── CallRequest.cls
│       │   └── CallResponse.cls
│       ├── REST/                      # REST API
│       │   └── VTEXWebhookAPI.cls    # REST endpoints
│       └── Setup/                     # Utilidades
│           ├── ConfigureWebApp.cls   # Configuración de webapp
│           ├── ProductionManager.cls # Gestión de producción
│           ├── DiagnosticReport.cls  # Reporte de diagnóstico
│           └── DebugTools.cls        # Herramientas de debug (nuevo)

13) Referencias

About

IRIS PoC — Abandoned Cart VTEX ↔ SAP (JDBC) ↔ CRM (Email) Alcance PoC (2 semanas): Detectar abandono en VTEX (webhook o job cada 30 min). Consultar disponibilidad y variantes (talla/color) vía vista JDBC en SAP. Enviar un email transaccional con deeplink a carrito restaurado. Auditoría básica en IRIS (métricas mínimas y log estructurado).

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published