logistic-erp
1. Descripción general
Según el pom.xml, el proyecto se describe como "Orders erp send to logistic from Europa". Es el runner batch más extenso de la familia logistic-*: agrupa 7 CommandLineRunner, uno por país/carrier, que envían pedidos ERP pendientes a sus respectivos proveedores logísticos. Es también el único de esta familia que genera facturas y documentos de aduana en PDF (vía Thymeleaf + iText) para determinados destinos.
2. Información técnica
| Campo | Valor |
|---|---|
artifactId | logistic-erp |
groupId | com.hawkersco |
version | 1.0.25 |
| Java | 25 (maven.compiler.release=25) |
| Spring Boot | 4.0.6 |
| Tipo de artefacto | jar (ejecutable, Spring Boot batch/CLI) |
| Módulos | No aplica (proyecto de módulo único) |
3. Arquitectura y diseño
No es una API REST: es una aplicación Spring Boot CLI con 7 CommandLineRunner, todos activos, ejecutados en el orden indicado.
Paquetes principales:
com.hawkersco.logisticerp— clase principal (LogisticErpApplication) y los 7 runners de país..config—LogisticErpConfig(declaración manual de servicios delogistics-commons)..utils—LogisticErpComponent(lógica central: construcción de payloads Auro/LogSolution, generación de facturas PDF),LogisticErpUtils(validaciones, construcción de payload Cubbo, transiciones éxito/error genéricas),LogisticErpConst..models—ProductSend,SkuRow(DTOs internos).- Recursos: plantillas Thymeleaf (
invoice.html,customs.html,mystyle.css, fuenteMontserrat-Regular.ttf), JSON de códigos DANE (dane-code-alt.json,dane-code-mp.json,dane-code-fix.json) ycountry-zipregex.json.
flowchart TD
A["LogisticErpRunner (order=1)<br/>España/UE — Auro"] --> B[Auro API]
C["LogisticErpMxRunner (order=2)<br/>México — Cubbo"] --> D[Cubbo API]
E["LogisticItErpRunner (order=3)<br/>Italia — LogSolution/Asendia"] --> F[LogSolution SOAP]
G["LogisticGrErpRunner (order=4)<br/>Grecia — Sarmed"] --> H[Sarmed API]
I["LogisticCoErpRunner (order=5)<br/>Colombia — Servientrega"] --> J[Servientrega SOAP]
K["LogisticGbErpRunner (order=6)<br/>Reino Unido — Sprint Logistics GB"] --> L[Sprint Logistics API v2 GB]
M["LogisticDeErpRunner (order=7)<br/>Norte de Europa — Sprint Logistics"] --> N[Sprint Logistics API v2]
A -->|si isInvoice| O[Genera factura/aduana PDF<br/>Thymeleaf + iText]
O -->|SFTP| P[sftp.hawkersco.com /src/invoices/]
Cada runner reproduce el mismo patrón: obtener pedidos ERP no procesados (orderService.findByOrdersNoProcessedErp(41L) o variante específica), omitir pedidos de test, construir el payload del carrier correspondiente (LogisticErpComponent/LogisticErpUtils), enviarlo, subir el resultado a GCS y gestionar reintentos/errores.
4. Dependencias principales
| Dependencia | Propósito |
|---|---|
spring-boot-starter | Núcleo de Spring Boot (sin web, es un runner CLI) |
spring-boot-starter-mail | Envío de correo (adjuntos de factura/aduanas para pedidos de Andorra) |
spring-web | RestClient/@HttpExchange |
com.googlecode.json-simple:json-simple | Parseo de los JSON de códigos DANE |
com.hawkersco:auro-client | Cliente REST para el carrier Auro (España/UE) |
com.hawkersco:cubbo-client | Cliente REST para el carrier Cubbo (México) |
com.hawkersco:logsolution-client | Cliente SOAP para LogSolution/Asendia (Italia) |
com.hawkersco:sarmed-client | Cliente REST para Sarmed (Grecia) |
com.hawkersco:sprintlogistics-client | Cliente REST para Sprint Logistics (norte de Europa y Reino Unido, con credenciales distintas por región) |
com.hawkersco:servientrega-client | Cliente SOAP para Servientrega (Colombia) |
com.hawkersco:logistics-commons | Entidades JPA y servicios de logística compartidos |
com.hawkersco:pi-function-commons | DateUtils, SftpUtils, PdfUtils, DirectoryUtils, SeveralUtils |
com.hawkersco:slack-client | Notificaciones de error a Slack |
lombok (versión 1.18.36, distinta de la usada en maven-compiler-plugin, 1.18.46 — ver hallazgo en la sección 13) | Generación de código boilerplate |
Thymeleaf (SpringTemplateEngine, transitiva) + iText (com.itextpdf) | Generación de PDF de factura/aduanas |
spring-boot-starter-test (test) | JUnit 5 + Spring Test |
5. API / Endpoints
No aplica a este proyecto. Es un batch/runner sin capa REST propia.
6. Integraciones externas
| Sistema | Protocolo | Runner | Detalle |
|---|---|---|---|
| Auro | HTTP REST | LogisticErpRunner | Envío de pedido + factura/aduana adjunta en base64 para determinados países/provincias |
| Cubbo | HTTP REST | LogisticErpMxRunner | Creación de pedido para México |
| LogSolution/Asendia | SOAP | LogisticItErpRunner | Envío de pedido, con token/referencia de cliente hardcodeados en el código (ver alerta de seguridad) |
| Sarmed | HTTP REST | LogisticGrErpRunner | Envío de pedido para Grecia |
| Servientrega | SOAP | LogisticCoErpRunner | Envío de pedido para Colombia (misma API que logistic-co) |
| Sprint Logistics API v2 (dos configuraciones: general y GB) | HTTP REST | LogisticGbErpRunner, LogisticDeErpRunner | Envío de pedido para Reino Unido y norte de Europa respectivamente, con credenciales independientes por región |
SFTP (sftp.hawkersco.com) | SFTP | LogisticErpRunner/LogisticErpComponent | Subida de facturas PDF a /src/invoices/ |
Google Cloud Storage (bucket pi-logistics-segment) | API de GCS | Todos los runners | Auditoría de requests/responses |
| Slack | HTTP (SlackClient) | Todos los runners | Alertas de fallo |
PostgreSQL (logistics) | JDBC | Todos los runners | Lectura de pedidos pendientes y actualización de estado |
7. Configuración
En producción (application-pro.properties) las credenciales llegan por variables de entorno inyectadas como Secret de Kubernetes; en local (application.properties) el repositorio contiene actualmente valores reales hardcodeados de un número muy elevado de integraciones (ver alerta de seguridad).
| Clave (prefijo) | Descripción |
|---|---|
spring.datasource.* | Credenciales de la BD logistics |
gcs.bucket.name | Bucket de GCS para auditoría |
logisticerp.ftp.* / logisticerp.auro.ftp.* | Credenciales SFTP para facturas y para Auro |
auro.auth.client.* | Credenciales de la API de Auro |
cubbo.client.* | Credenciales de la API de Cubbo (incluye storeid.prod, ver hallazgo en la sección 13) |
sarmed.api.* | Credenciales de la API de Sarmed |
sprintlogistics-v2.api.* / sprintlogistics-gb-v2.api.* | Credenciales de Sprint Logistics, dos configuraciones distintas |
logisticco.servientrega.credentials.* | Credenciales SOAP de Servientrega |
logsolution.auth-logistic.client.url | URL del servicio SOAP de LogSolution (las credenciales de autenticación están hardcodeadas en el código, no en esta propiedad — ver alerta de seguridad) |
slack.* | Configuración del cliente Slack (varios canales) |
hawkers.order.invoice.countrycode.list / .provincecode.list | Países/provincias que requieren factura adjunta |
⚠️ Alerta de seguridad (severidad crítica)
Este proyecto combina dos problemas de seguridad graves:
application.propertieslocal contiene credenciales reales de prácticamente todos los carriers logísticos de Europa/México/Colombia: contraseña de BD, credenciales SFTP (dos servidores), credenciales de Auro, credenciales de producción de Cubbo (clientid/clientsecret), credenciales de Sarmed, credenciales de Sprint Logistics (dos configuraciones, ES y GB), credenciales SOAP de Servientrega (las mismas ya señaladas como expuestas enlogistic-co), y el token de bot de Slack. Ninguno de estos valores se ha reproducido en este documento.- Credenciales de LogSolution hardcodeadas directamente en el código fuente, no en configuración:
LogisticErpComponentdeclaraLOGSOLUTION_AUTH_TOKENyLOGSOLUTION_CUSTOMER_REFcomo constantesprivate static final Stringcon valores reales tipo GUID. A diferencia de las demás credenciales (que al menos están externalizadas enapplication.properties/application-pro.propertiesy pueden rotarse víaSecretde Kubernetes sin recompilar), estas quedan fijas en el binario compilado: rotarlas exige modificar el código fuente y desplegar una nueva versión, y cualquiera con acceso al.jar/repositorio puede extraerlas sin necesidad de acceder a la configuración de despliegue.
Se recomienda con prioridad máxima:
- Rotar todas las credenciales listadas (Auro, Cubbo, Sarmed, Sprint Logistics ES/GB, Servientrega, Slack, BD, SFTP) y coordinar la rotación de Servientrega/Sprint Logistics con
logistic-co/logistic-de/logistic-gb, que comparten las mismas credenciales. - Mover
LOGSOLUTION_AUTH_TOKENyLOGSOLUTION_CUSTOMER_REFaapplication.properties/application-pro.propertiescomo cualquier otra credencial, y rotarlas también. - Revisar el historial de control de versiones para valorar el alcance real de la exposición.
8. Persistencia
Base de datos PostgreSQL (logistics), acceso vía JPA a través de la librería logistics-commons. spring.jpa.hibernate.ddl-auto=none. No hay Flyway/Liquibase en este repositorio. Además de las entidades habituales (Order, OrderLine, OrderError), este proyecto usa OrderLineProperty (propiedades de línea de pedido, para atributos "creativos" de gafas graduadas/personalizadas de la fuente SOURCE_ID_CREATIVES=17).
9. Procesos programados y mensajería
No hay @Scheduled ni listeners de colas: la periodicidad la impone el CronJob de Kubernetes (k8s/cronjob.yaml), que ejecuta el contenedor cada 10 minutos (schedule: "*/10 * * * *"). Se ejecutan en orden los 7 runners:
| Orden | Runner | País/región | Carrier |
|---|---|---|---|
| 1 | LogisticErpRunner | España/UE | Auro |
| 2 | LogisticErpMxRunner | México | Cubbo |
| 3 | LogisticItErpRunner | Italia | LogSolution/Asendia |
| 4 | LogisticGrErpRunner | Grecia | Sarmed |
| 5 | LogisticCoErpRunner | Colombia | Servientrega |
| 6 | LogisticGbErpRunner | Reino Unido | Sprint Logistics (config. GB) |
| 7 | LogisticDeErpRunner | Norte de Europa | Sprint Logistics (config. general) |
El CLAUDE.md del proyecto lista los mismos 7 runners pero deja el carrier de México y Alemania como "—" (sin especificar); se ha verificado que LogisticErpMxRunner sí usa un carrier concreto (Cubbo), y que LogisticDeErpRunner usa Sprint Logistics. El propio LogisticErpRunner (Auro) solo procesa pedidos cuyo warehouseId sea "02"/"2"; para cualquier otro almacén, no hace nada (default -> {}), sin notificar ni marcar el pedido de ningún modo.
Generación de factura (solo LogisticErpRunner/Auro): si el país de envío está en la lista de países que requieren factura (y, para España, si la provincia está en la lista de provincias forales/especiales), se genera un PDF de factura con Thymeleaf, se combina con un PDF de aduanas si el destino es Reino Unido, se codifica en base64 para incluirlo en el payload de Auro, y además se sube por SFTP a /src/invoices/.
10. Ejecución en local
Requisitos previos: JDK 25, Maven, acceso a la BD logistics y credenciales válidas de los 6 carriers en un application.properties local.
# Compilar
./mvnw clean install
# Compilar sin tests (usado en CI)
./mvnw -B -DskipTests clean install
# Ejecutar tests
./mvnw test
# Ejecutar un test concreto
./mvnw test -Dtest=MyTestClass
# Ejecutar la aplicación localmente
./mvnw spring-boot:run
Al ser un CommandLineRunner, no expone Actuator/health: la forma de verificar la ejecución es revisar el log de consola, los ficheros subidos a GCS, o las facturas subidas al SFTP de facturas.
11. Despliegue
- Imagen: construida con
jib-maven-plugin(baseeclipse-temurin:25-jre,containerizingMode=packaged), publicada eneurope-west3-docker.pkg.dev/pi-saldum/pi-repo/logistic-erp:<tag>. - Orquestación: Kubernetes
CronJob(k8s/cronjob.yaml) en el clúster GKEpi-cluster-hw, namespacepi, ejecutándose cada 10 minutos. - CI/CD (Jenkins): pipeline real de 3 etapas —
Checkout→Build & Push→Deploy to GKE. - Las variables sensibles se inyectan en el pod mediante un
Secretde Kubernetes llamado igual que la app (logistic-erp), con un número muy elevado de claves (una por cada carrier).
Job de Jenkins: https://jenkins-pi.hawkersco.net/job/logistic-erp/
12. Manejo de errores y logging
No hay una estrategia de excepciones centralizada (no hay @ControllerAdvice, es un runner). Cada runner captura RestClientResponseException (o la excepción SOAP equivalente) de forma independiente por pedido, notificando a Slack tras 4 reintentos fallidos (MAX_SEND_ATTEMPTS/MAX_SEND_LOGISTIC_RETRIES según el runner). createInvoiceOrderPdf/createMailAttachment capturan IOException/DocumentException con e.printStackTrace() además del log estándar — un patrón menos consistente que el resto del ecosistema, que normalmente solo usa Logger. Logging mediante java.util.logging.Logger estándar (consola).
13. Notas y consideraciones
- Credenciales de LogSolution hardcodeadas en el código fuente: ver alerta de seguridad en la sección 7. Es el hallazgo más grave de este proyecto: a diferencia del resto de credenciales (externalizadas y rotables sin recompilar), las de LogSolution están fijas en
LogisticErpComponentcomo constantes de clase. - Property
cubbo.client.storeid.prodposiblemente sin efecto:LogisticErpUtils.getOrderCubboRequestfija elstoreIDcon el literal"1720"directamente en el código (con un comentario//1687 test 1720 prod), en lugar de leerlo de la propiedadcubbo.client.storeid.prodya declarada enapplication.properties. Si el valor de producción cambiase, habría que actualizarlo en dos sitios (o el cambio en la propiedad no tendría ningún efecto real). LogisticErpRunner(Auro) ignora silenciosamente pedidos de almacenes no reconocidos: elswitchsobreorder.getWarehouseId()solo gestiona los casos"02"/"2"; cualquier otro valor cae endefault -> {}sin log, sin marca de error y sin notificación — un pedido con unwarehouseIdinesperado queda indefinidamente sin procesar por este runner, sin ninguna señal visible de que existe.- Versión de Lombok inconsistente: la dependencia
lombokse declara con versión1.18.36en el bloque<dependencies>, mientras que elannotationProcessorPathdelmaven-compiler-pluginusa1.18.46. Ambas versiones conviviendo puede producir comportamientos sutilmente distintos entre el procesamiento de anotaciones en compilación y la biblioteca en tiempo de ejecución; se recomienda unificar a una sola versión. CLAUDE.mdincompleto respecto a los carriers de México y Alemania: los deja como "—" en su tabla de runners, cuando el código confirma que usan Cubbo y Sprint Logistics respectivamente. El resto de la descripción arquitectónica coincide con el código real.- Ver alerta de seguridad crítica en la sección 7.