Skip to main content

Coppel Create DB

1. Descripción general

coppel-create-db es un microservicio batch (según el pom.xml, "Get orders by coppel") que ingiere en la base de datos logística de Hawkers los pedidos del marketplace mexicano Coppel. No expone ninguna API HTTP: es una aplicación Spring Boot basada en varios CommandLineRunner ordenados que se ejecutan hasta completar su trabajo y terminan, desplegada como un CronJob de Kubernetes cada 15 minutos.

El flujo cubre tres responsabilidades: (1) descargar pedidos nuevos en estado SHIPPING y persistirlos en base de datos, (2) descargar en lote las etiquetas de envío PDF de los pedidos pendientes de etiqueta y promoverlos a "pendiente de envío", y (3) detectar pedidos cancelados en Coppel y reflejar la cancelación en la base de datos (esta última fase está desactivada actualmente). Notifica incidencias operativas a Slack. Dentro del ecosistema Hawkers, cumple el mismo rol que bradery-create-db: puente entre un marketplace externo y el modelo de datos interno de pedidos.

2. Información técnica

PropiedadValor
artifactIdcoppel-create-db
groupIdcom.hawkersco
version1.0.25
Java25
Spring Boot4.0.6
Tipo de artefactoJAR ejecutable (imagen de contenedor vía Jib)
MódulosProyecto único (no multi-módulo)

3. Arquitectura y diseño

Estructura del proyecto:

com.hawkersco.coppelcreatedb
├── CoppelCreateDbApplication.java # @SpringBootApplication + @EnableJpaRepositories
├── CoppelCreateDbRunner.java # CommandLineRunner, orden 1: ingiere pedidos nuevos en estado SHIPPING
├── CoppelProcessLabelRunner.java # CommandLineRunner, orden 2: descarga y asocia etiquetas de envío
├── CoppelCreateDbCheckRunner.java # CommandLineRunner, orden 3: detecta cancelaciones — DESACTIVADO (@Component comentado)
├── config/
│ └── CoppelCreateDbConfig.java # Declara manualmente los @Bean de servicios de logistics-commons
└── util/
└── CoppelCreateDbUtil.java # Transformación de pedido Coppel → entidades logistics-commons, notificaciones Slack

Flujo principal (runners ordenados)

sequenceDiagram
participant CronJob as CronJob K8s (cada 15 min)
participant R1 as CoppelCreateDbRunner (orden 1)
participant Coppel as API Coppel (Mirakl)
participant DB as PostgreSQL (logistics)
participant R2 as CoppelProcessLabelRunner (orden 2)
participant Slack as Slack

CronJob->>R1: run()
loop paginado (100/página, 10s entre páginas)
R1->>Coppel: getOrderListByStateCode(SHIPPING, últimos 5 días)
Coppel-->>R1: pedidos
R1->>R1: omite WAITING_DEBIT
R1->>DB: si no existe, crea OrderMarketplace, Customer, Order, OrderLine, Shipment, OrderShipmentStatus (PENDING_LABEL)
end

CronJob->>R2: run()
R2->>DB: findOrderByListSourceAndListCdProcessingStatusLimit(Coppel, PENDING_LABEL, máx 50)
R2->>Coppel: downloadDocumentsByOrderList(orderIds) → ZIP
R2->>R2: descomprime, busca PDF por tracking o alternativo
alt etiqueta encontrada
R2->>DB: guarda etiqueta en base64, estado → PENDING_SHIPMENT
else sin etiqueta tras 48h
R2->>DB: estado → ERROR_LABEL
R2->>Slack: alerta al canal ATC-MX
end
R2->>R2: System.exit(SpringApplication.exit(context))

CoppelCreateDbCheckRunner (orden 3, detección de cancelaciones) tiene su anotación @Component comentada en el código — está completamente desactivado y no se ejecuta en ningún despliegue actual, pese a estar documentado como parte del flujo en CLAUDE.md y en el propio Javadoc de la clase ("Currently disabled — restore @Component to activate").

CoppelCreateDbConfig declara manualmente cada servicio de logistics-commons como @Bean (mismo patrón que bradery-create-db y chatbot, ya que estos servicios no se auto-detectan por @ComponentScan al vivir en una librería externa).

4. Dependencias principales

DependenciaVersiónPropósito
spring-boot-starter(gestionada SB4)Base de Spring Boot (contexto, CommandLineRunner)
spring-web(gestionada SB4)Soporte HTTP para los clientes @HttpExchange inyectados
com.hawkersco:coppel-client1.0.25-SNAPSHOTCliente @HttpExchange de la API Coppel/Mirakl (CoppelClient), modelo OrdersCoppelResponse
com.hawkersco:logistics-commons1.0.25-SNAPSHOTEntidades JPA (Order, OrderLine, OrderMarketplace, Customer, Shipment, etc.) y servicios
com.hawkersco:pi-function-commons1.0.25-SNAPSHOTUtilidades DateUtils, ZipUtils, DirectoryUtils
com.hawkersco:slack-client1.0.25-SNAPSHOTCliente @HttpExchange de Slack, usado activamente para alertas operativas
spring-boot-starter-test(gestionada SB4)Testing (scope test)

A diferencia de bradery-create-db y chatbot, aquí slack-client está correctamente declarado en el pom.xml y usado en el código (CoppelProcessLabelRunner, CoppelCreateDbUtil, CoppelCreateDbCheckRunner).

5. API / Endpoints

No aplica a este proyecto. coppel-create-db es un batch CommandLineRunner sin servidor HTTP ni controladores REST.

6. Integraciones externas

SistemaProtocoloDirecciónDescripción
API Coppel/Mirakl (vía coppel-client)HTTP RESTSalienteListado de pedidos por estado (SHIPPING, CANCELED) y descarga de documentos ZIP con etiquetas
Slack (vía slack-client)HTTP RESTSalienteAlertas de pedidos sin etiqueta tras 48h, pedidos con Source ID no registrado, errores al guardar Shipment, y (en el runner desactivado) notificación de cancelaciones
PostgreSQL (logistics, vía logistics-commons)JDBCSalientePersistencia de pedidos, clientes, líneas, envíos y estado de etiqueta

Convenciones de negocio relevantes:

  • Todos los pedidos de Coppel se asocian al Source ID 55 (SOURCE_ID_COPPEL, constante duplicada en los tres runners y en CoppelCreateDbUtil).
  • El nombre interno del pedido sigue el formato MKCOPPEL + sufijo numérico secuencial de 7 cifras con ceros a la izquierda, derivado de max(idOrderMarketplace) + 1.
  • Flujo de estados del pedido: PENDING_LABEL (al crearse) → PENDING_SHIPMENT (cuando se encuentra la etiqueta) → ERROR_LABEL (si no aparece etiqueta tras 48 horas) / CANCELLED (si Coppel cancela el pedido, vía el runner desactivado).
  • CoppelCreateDbRunner pausa 10 segundos entre páginas de la API Coppel y 30 segundos adicionales si una página falla con RestClientResponseException, presumiblemente para no saturar la API o respetar límites de tasa.

7. Configuración

El proyecto usa dos perfiles: application.properties (desarrollo, con valores reales) y application-pro.properties (producción, con placeholders ${VAR} inyectados por Kubernetes). El Jenkinsfile sustituye el primero por el segundo antes de empaquetar.

Grupos de propiedades

PropiedadDescripciónValor en producción
spring.datasource.url/username/passwordConexión PostgreSQL de logística${dbLogisitcsUrl} / ${dbLogisitcsUsername} / ${dbLogisitcsPassword} (sic, con el mismo typo "Logisitcs" observado en chatbot)
coppel.credentials.urlURL base de la API Coppel${coppelCredUrl}
coppel.credentials.keyClave de autenticación de la API Coppel${coppelCredKey}
slack.client.url / slack.auth.tokenConfiguración del cliente Slack${slackClientUrl} / ${slackAuthToken}
slack.channel.idCanal Slack para alertas generales (Source ID no registrado, error de Shipment)${slackChannelId}
slack.channel-atc-mx.idCanal Slack específico para alertas de etiquetas faltantes (equipo ATC-MX)${slackChannelAtcMxId}

⚠️ Alerta de seguridad: El fichero src/main/resources/application.properties (perfil de desarrollo, versionado en el repositorio) contiene credenciales reales en texto plano: contraseña de la base de datos PostgreSQL, clave de la API de Coppel y token de bot de Slack. Ninguno de estos valores se reproduce en este documento. Se recomienda rotarlos y migrar el perfil local al mismo patrón de variables de entorno que ya usa application-pro.properties. Mismo hallazgo que en bradery-create-db y chatbot, sugiriendo que es una práctica extendida en los runners *-create-db/batch de este ecosistema.

Variables de entorno (perfil de producción)

VariablePropiedad mapeada
dbLogisitcsUrl / Username / Passwordspring.datasource.*
coppelCredUrl / coppelCredKeycoppel.credentials.url / .key
slackClientUrl / slackAuthTokenslack.client.url / slack.auth.token
slackChannelIdslack.channel.id
slackChannelAtcMxIdslack.channel-atc-mx.id

8. Persistencia

Base de datos: PostgreSQL (logistics), accedida vía JPA/Hibernate a través de las entidades de logistics-commons.

Entidades principales manipuladas por este runner:

EntidadRol
OrderMarketplaceVínculo entre el pedido interno (MKCOPPEL...) y el pedido de Coppel; también almacena la etiqueta en base64 (updateLabel)
Customer / CustomerSourceCliente asociado al pedido, con origen registrado para Source ID 55
OrderPedido logístico completo; rawData guarda el JSON crudo del pedido Coppel (con formato de fecha específico vía Gson)
OrderLineLíneas de producto, con resolución de SKU interno vía ProductMarketplaceService cuando existe mapeo
Shipment / ShipmentLineEnvío generado a partir del pedido y sus líneas
OrderShipmentStatusEstado inicial de envío (PENDING_SHIPMENT)

No se han encontrado migraciones Flyway/Liquibase en este proyecto (spring.jpa.hibernate.ddl-auto=none); el esquema se gestiona externamente, compartido con otros proyectos del ecosistema.

9. Procesos programados y mensajería

No hay @Scheduled en el código: la periodicidad se gestiona vía Kubernetes. k8s/cronjob.yaml define un CronJob con expresión 0/15 * * * * (cada 15 minutos) y concurrencyPolicy: Forbid (no permite solapar ejecuciones).

Dentro de cada ejecución, los CommandLineRunner activos (CoppelCreateDbRunner, orden 1; CoppelProcessLabelRunner, orden 2) se ejecutan secuencialmente al arrancar el contexto de Spring. CoppelCreateDbCheckRunner (orden 3) no se ejecuta al estar desactivado.

10. Ejecución en local

Requisitos previos

  • Java 25
  • Maven 3.x
  • Acceso a la base de datos PostgreSQL de logística
  • Credenciales válidas de la API de Coppel
  • Configuración de Slack (URL, token, IDs de canal) si se quiere probar el envío de alertas

Comandos

# Build sin tests (como en CI)
./mvnw -B -DskipTests clean install

# Ejecutar tests
./mvnw test

# Ejecutar un test concreto
./mvnw test -Dtest=CoppelCreateDbApplicationTests

# Build de imagen Docker
docker build -t coppel-create-db .

Al no tener servidor HTTP, no hay endpoint de health; la verificación de correcta ejecución se hace revisando los logs (CoppelCreateDbRunner - END, CoppelProcessLabelRunner - END) o el estado del Job generado por el CronJob en Kubernetes.

Nota sobre el directorio zip/ en el repositorio local: el proyecto usa el directorio zip/ en el directorio de trabajo como área temporal para descargar y descomprimir el ZIP de etiquetas de Coppel (CoppelProcessLabelRunner.run() lo borra y recrea al inicio de cada ejecución). En el checkout local de este repositorio se han encontrado ficheros PDF y un labels.zip residuales dentro de zip/, aparentemente de ejecuciones previas — ver advertencia de privacidad en la sección 13.

11. Despliegue

Se despliega como imagen de contenedor en GKE, orquestada por un CronJob, con el mismo patrón que bradery-create-db y chatbot:

  1. Checkout — descarga el código.
  2. Build & Push — sustituye application.properties por application-pro.properties, compila con mvn clean package jib:build -DskipTests -U -Dimage.tag=${BUILD_NUMBER} y publica en europe-west3-docker.pkg.dev/pi-saldum/pi-repo/coppel-create-db:${BUILD_NUMBER}.
  3. Deploy to GKE — obtiene credenciales del clúster pi-cluster-hw (zona europe-west3-a), elimina el CronJob existente y aplica k8s/cronjob.yaml en el namespace pi.

Imagen base de runtime (Jib): eclipse-temurin:25-jre.

Job de Jenkins:

https://jenkins-pi.hawkersco.net/job/coppel-create-db/

12. Manejo de errores y logging

Los tres runners usan java.util.logging.Logger.

  • CoppelCreateDbRunner: captura RestClientResponseException por pedido individual dentro del bucle (no por página completa), registra el error y espera 30 segundos antes de continuar con el siguiente pedido — un fallo aislado al persistir un pedido no detiene el procesamiento de los demás.
  • CoppelProcessLabelRunner: no envuelve su lógica principal en un try/catch general (la firma run() declara throws Exception); un fallo no controlado propagaría la excepción y probablemente detendría el proceso antes de llegar al System.exit() final.
  • CoppelCreateDbUtil.saveShipment: captura ParseException y notifica a Slack en lugar de propagar, dejando el pedido persistido sin Shipment asociado.
  • Los pedidos sin etiqueta tras 48 horas se marcan ERROR_LABEL y generan una alerta Slack específica al canal ATC-MX — es el único de los tres runners *-create-db documentados hasta ahora que integra notificaciones operativas activas.

No hay configuración de logback específica; los logs usan el formato por defecto de java.util.logging.

13. Notas y consideraciones

  • ⚠️ Posibles etiquetas de envío reales en el repositorio local: El checkout local de este proyecto contiene, dentro del directorio zip/, ficheros PDF de etiquetas de envío (p. ej. zip/309639623-B/TMKP2646569281724807.pdf) y un labels.zip, con cambios de trabajo sin commitear detectados en el control de versiones (ficheros añadidos/modificados/eliminados). Estos ficheros pueden contener datos personales de clientes (nombre, dirección de envío) si corresponden a pedidos reales descargados por una ejecución previa de CoppelProcessLabelRunner. No se ha inspeccionado el contenido de estos ficheros al redactar este documento. Se recomienda: (1) confirmar si zip/ está en .gitignore (actualmente no lo está) y añadirlo si contiene datos generados en tiempo de ejecución, y (2) verificar si estos ficheros concretos llegaron a incluirse en algún commit del historial, en cuyo caso deberían purgarse del historial de Git por motivos de protección de datos.

  • CoppelCreateDbCheckRunner desactivado pero mantenido en el código: La detección de cancelaciones existe y está completamente implementada, pero su @Component está comentado. Cualquier pedido cancelado en Coppel actualmente no se refleja en el estado interno del pedido ni genera notificación — pendiente de verificar si esta funcionalidad se gestiona por otra vía (manual, otro proceso) o si simplemente está pendiente de reactivarse.

  • Constante SOURCE_ID_COPPEL duplicada en cuatro clases: Igual que en bradery-create-db, el Source ID (55L) se declara como constante privada independiente en CoppelCreateDbRunner, CoppelProcessLabelRunner, CoppelCreateDbCheckRunner y CoppelCreateDbUtil, sin una única fuente de verdad compartida.

  • Typo "Logisitcs" consistente con chatbot: Las variables de entorno dbLogisitcsUrl/Username/Password reproducen el mismo error ortográfico visto en chatbot, sugiriendo que ambos proyectos comparten una plantilla de configuración con el typo ya incorporado.

  • System.exit() solo en el runner de etiquetas: Igual que en chatbot, solo el último runner en ejecutarse (CoppelProcessLabelRunner, orden 2, dado que el orden 3 está desactivado) fuerza System.exit(SpringApplication.exit(context)); CoppelCreateDbRunner no lo hace.

  • Resolución de SKU interno best-effort: transformToOrderLinesLogistic usa el SKU de Coppel (offerSku) como valor por defecto y solo lo sustituye por el SKU interno si existe un mapeo en ProductMarketplaceService — si no hay mapeo, la línea de pedido queda con el SKU externo de Coppel en el campo cdSku, lo que podría causar inconsistencias si otros procesos esperan siempre un SKU interno en ese campo.

  • Sin tests funcionales: Solo existe CoppelCreateDbApplicationTests (test de contexto por defecto); no hay pruebas sobre la lógica de negocio (transformación de pedidos, resolución de etiquetas, cálculo de umbral de 48 horas).