ProyectoIntermodular/CLAUDE (1).md

12 KiB
Raw Blame History

TFG — Integración Dolibarr-PrestaShop

Quién soy

Estudiante DAM (Desarrollo de Aplicaciones Multiplataforma, FP Grado Superior). Este es mi TFG, evaluado por tribunal. Criterios: calidad de código, arquitectura limpia, justificación de decisiones técnicas, documentación, patrones correctos, sistema funcional end-to-end. Tengo que defenderlo oralmente — todo debe ser entendido y argumentable por mí.

Objetivo

Integrar Dolibarr (ERP) con PrestaShop (e-commerce), ambos en hosting compartido bajo httpdocs.

Requisitos del enunciado:

  1. Gestionar productos, propiedades y stock del ERP para integrarlos en PrestaShop.
  2. Registrar en el ERP cualquier venta realizada a través de PrestaShop.

Arquitectura (tres capas)

Capa 1 — Apps cliente (se hará después, no tocar ahora):

  • JavaFX (escritorio)
  • Kotlin Android (móvil)
  • Panel web ligero (opcional)

Capa 2 — sync-service (esto es lo que construimos):

  • Spring Boot 3 + Java 21 + Maven
  • API REST propia que consumen las apps cliente
  • Tareas @Scheduled de sincronización
  • Persistencia local: mapeo de IDs y logs de sincronización

Capa 3 — Sistemas externos:

  • Dolibarr: API REST nativa. Base: https://prestashop.loading.net/dolibarr/api/index.php. Auth: header DOLAPIKEY: <clave>. Formato: JSON.
  • PrestaShop: Webservice nativo. Base: https://prestashop.loading.net/tienda/api. Auth: ws_key como query param (NO Basic Auth — nginx del hosting elimina el header Authorization). Formato: JSON con &output_format=JSON.
    • Bug PS 8.2.5: endpoint raíz /api/ devuelve 500. Usar siempre recursos específicos: /api/products, /api/orders, etc.

Identificador común: SKU/referencia del producto — ref en Dolibarr, reference en PrestaShop. Nunca los IDs internos.

Stack tecnológico

Capa Elección
Lenguaje/Framework Java 21 + Spring Boot 3 + Maven
Persistencia Spring Data JPA. H2 en dev, MySQL/MariaDB en prod
Cliente HTTP RestClient de Spring (no RestTemplate, no WebClient)
Serialización Jackson (incluido en Spring Web)
Validación Jakarta Validation
Logging SLF4J + Logback (default Spring Boot)
Testing JUnit 5 + Mockito + Spring Boot Test + WireMock
Docs API SpringDoc OpenAPI (Swagger UI)
Scheduling @Scheduled nativo. ShedLock si hace falta lock distribuido

Estado actual

Fases 0–5 completadas. sync-service funcional end-to-end.

  • Compila y arranca con ./mvnw spring-boot:run -Dspring-boot.run.profiles=dev.
  • Código en Gitea: https://projectes.ieslamar.org/mireya.2526/ProyectoIntermodular.git
  • Espejo en GitHub: https://github.com/luklpz/ProyectoIntermodular.git (para Railway)
  • Despliegue: Railway.app (Loading.es hosting compartido no puede ejecutar procesos Java persistentes)

Pendiente:

  • Fase 6 — Apps cliente (proyecto separado)

Modelo de dominio (entidades JPA locales)

  • ProductMapping: id, sku (único), dolibarrId, prestashopId, lastSyncedAt, syncStatus (enum: PENDING/SYNCED/ERROR), errorMessage.
  • OrderMapping: id, prestashopOrderId, dolibarrOrderId, dolibarrInvoiceId, importedAt, status (enum).
  • SyncLog: id, syncType (enum: PRODUCT_PUSH/STOCK_PUSH/ORDER_PULL), startedAt, finishedAt, itemsProcessed, itemsFailed, errorDetails (TEXT).

Flujos de sincronización

  1. pushProductsToPrestashop — lee productos Dolibarr modificados desde lastSyncedAt, crea/actualiza en PrestaShop por SKU, actualiza ProductMapping.
  2. pushStockToPrestashop — lee stock actual en Dolibarr de productos mapeados, empuja a stock_availables de PrestaShop.
  3. pullOrdersFromPrestashop — consulta pedidos PrestaShop desde lastSyncedAt con filter[date_add]=..., crea/localiza cliente en Dolibarr, crea commande, opcionalmente factura, guarda OrderMapping.

Cada flujo escribe una entrada en SyncLog (éxito o fallo).

Roadmap por fases

  • ✅ Fase 0 — Setup base

    • Estructura de paquetes: integration.dolibarr, integration.prestashop, sync, mapping, api, config
    • Perfiles application-dev.yml (H2 MODE=MySQL) y application-prod.yml (MySQL) — ambos gitignoreados, .example en repo
    • @ConfigurationProperties con @Validated para todas las properties sensibles
    • SpringDoc OpenAPI en pom.xml
  • ✅ Fase 1 — Clientes HTTP tipados

    • DolibarrClient: RestClient + header DOLAPIKEY global. Métodos: getProducts, getProductByRef, createProduct, updateProduct, updateStock, getOrCreateThirdparty, createOrder, createInvoiceFromOrder
    • PrestashopClient: RestClient + ws_key como query param en cada petición. Métodos: getProducts, getProductByReference, createProduct, updateProduct, getStockAvailableForProduct, updateStockAvailable, getOrdersSince, getCustomer
    • DTOs separados por API con @JsonIgnoreProperties(ignoreUnknown=true)
    • DolibarrApiException, PrestashopApiException con body logueado
  • ✅ Fase 2 — Modelo y persistencia

    • Entidades JPA: ProductMapping, OrderMapping, SyncLog
    • Repositorios Spring Data JPA
    • Flyway: V1__init.sql con tablas product_mapping, order_mapping, sync_log
  • ✅ Fase 3 — Servicios de sincronización

    • ProductSyncService, StockSyncService, OrderSyncService
    • Cada uno devuelve SyncResult(processed, failed)
    • try/finally garantiza que SyncLog se escribe siempre
    • NO @Transactional en batch: cada save() auto-commit independiente
  • ✅ Fase 4 — Scheduling

    • SyncScheduler con @Scheduled(fixedDelayString=...) (delay post-finalización, no entre inicios)
    • @ConditionalOnProperty(sync.scheduling.enabled) — desactivado en dev
    • ThreadPoolTaskScheduler(poolSize=1) — ejecución secuencial, sin solapamiento
  • ✅ Fase 5 — API REST propia

    • SyncController: POST /api/sync/{products,stock,orders} — sync manual
    • MappingController: GET /api/mappings/products?status=, /api/mappings/orders
    • SyncLogController: GET /api/logs?type=, /api/logs/{id}
    • Spring Security: Basic Auth, DelegatingPasswordEncoder ({noop} dev / {bcrypt} prod), STATELESS, CSRF off
    • Swagger UI: /swagger-ui.html
  • ✅ Despliegue en Railway ← completado

    • GitHub mirror: https://github.com/luklpz/ProyectoIntermodular.git
    • URL pública: https://proyectointermodular-production-a9c3.up.railway.app
    • Swagger UI: https://proyectointermodular-production-a9c3.up.railway.app/swagger-ui.html
    • Health: https://proyectointermodular-production-a9c3.up.railway.app/actuator/health
    • Root directory del servicio Railway: sync-service
    • MySQL plugin de Railway — red privada (mysql.railway.internal:3306)
    • Todas las credenciales en variables del servicio ProyectoIntermodular (ver sección Despliegue)
  • 🔲 Verificación end-to-end con datos reales ← HACER ANTES DE FASE 6

    • Ver sección "Verificación end-to-end" más abajo
  • Fase 6 — Apps cliente — proyecto separado, avisaré cuando toque

Verificación end-to-end con datos reales

Antes de Fase 6 hay que probar que los tres flujos funcionan con datos reales en Dolibarr y PrestaShop.

Swagger UI: https://proyectointermodular-production-a9c3.up.railway.app/swagger-ui.html Credenciales: usuario admin, contraseña admin123

Flujo 1 — Productos Dolibarr → PrestaShop

  1. Crear un producto en Dolibarr (https://prestashop.loading.net/dolibarr) con referencia/SKU única (ej: PROD-001), nombre, precio y stock.
  2. En Swagger: click "Authorize" → admin / admin123.
  3. POST /api/sync/products → "Try it out" → "Execute".
  4. Verificar respuesta: {"processed": 1, "failed": 0}.
  5. Comprobar que el producto aparece en PrestaShop (https://prestashop.loading.net/tienda).
  6. GET /api/mappings/products → debe listar el producto con syncStatus: SYNCED.

Flujo 2 — Stock Dolibarr → PrestaShop

  1. Modificar el stock del producto en Dolibarr.
  2. POST /api/sync/stock → "Try it out" → "Execute".
  3. Verificar que el stock actualizado aparece en PrestaShop.

Flujo 3 — Pedidos PrestaShop → Dolibarr

  1. Crear un pedido en PrestaShop comprando el producto.
  2. POST /api/sync/orders → "Try it out" → "Execute".
  3. Verificar respuesta: {"processed": 1, "failed": 0}.
  4. Comprobar que el pedido aparece en Dolibarr como commande.
  5. GET /api/mappings/orders → debe listar el pedido mapeado.

Ver logs de cualquier sync

GET /api/logs → lista todas las ejecuciones con duración, items procesados y errores. GET /api/logs/{id} → detalle de una ejecución concreta.

Si algo falla

  • GET /api/logs → campo errorDetails del log tendrá el mensaje de error.
  • Los logs del servidor están en Railway → ProyectoIntermodular → Deploy Logs.

Despliegue (Railway)

Variables en el servicio ProyectoIntermodular (literales, NO Shared Variables):

SPRING_PROFILES_ACTIVE=prod
SPRING_DATASOURCE_URL=jdbc:mysql://mysql.railway.internal:3306/railway?useSSL=false&serverTimezone=UTC&characterEncoding=UTF-8&allowPublicKeyRetrieval=true
SPRING_DATASOURCE_USERNAME=root
SPRING_DATASOURCE_PASSWORD=<ver MySQL service → MYSQLPASSWORD>
SPRING_JPA_DATABASE_PLATFORM=org.hibernate.dialect.MySQLDialect
SPRING_JPA_HIBERNATE_DDL_AUTO=validate
SYNC_SCHEDULING_ENABLED=true
DOLIBARR_API_KEY=<ver application-dev.yml local>
PRESTASHOP_API_KEY=<ver application-dev.yml local>
API_SECURITY_PASSWORD={noop}admin123

Lecciones aprendidas Railway:

  • Las Shared Variables NO inyectan automáticamente en servicios — poner todo en variables del servicio directamente
  • ${{MySQL.MYSQLPASSWORD}} solo resuelve en variables propias del servicio, no en Shared Variables
  • MySQL 8 requiere allowPublicKeyRetrieval=true en la URL JDBC (usa caching_sha2_password)
  • mysql.railway.internal:3306 es la red privada interna — funciona sin configuración adicional

Flyway ejecuta V1__init.sql en primer arranque y crea las tablas automáticamente.

Convenciones de código

  • Constructor injection con @RequiredArgsConstructor (Lombok). Sin @Autowired en campos.
  • Lombok: @Getter, @Setter, @RequiredArgsConstructor, @Builder cuando aporten. Nunca @Data en entidades JPA (rompe equals/hashCode con relaciones bidireccionales).
  • Nombres: PascalCase clases, minúsculas paquetes.
  • JavaDoc en clases públicas y métodos no triviales (para la memoria del TFG).
  • Commits pequeños y atómicos, mensajes en imperativo en inglés.
  • Tests: unitarios con Mockito para servicios; integración mínima con @SpringBootTest + WireMock.

Lo que NO quiero

  • Microservicios, Kafka, Redis, Docker Compose con cinco contenedores.
  • Dependencias sin valor real.
  • CQRS, event sourcing, hexagonal estricto, DDD táctico complejo. Arquitectura en capas limpia es suficiente.
  • Mezclar lógica Dolibarr y PrestaShop en la misma clase.
  • Strings mágicos: todo a constantes o properties.
  • catch (Exception e) genérico.
  • Generar 15 archivos de golpe sin explicar qué hace cada uno.

Cómo trabajamos

  • Discutir diseño antes de generar código grande.
  • Justificar decisiones técnicas (las tengo que defender ante tribunal).
  • Avisar de trade-offs explícitamente.
  • Una fase del roadmap a la vez. No saltar fases.
  • README.md actualizado: cómo arrancar, perfiles, variables de entorno, endpoints.

Decisiones técnicas clave (para defensa oral)

  • fixedDelay no fixedRate: espera a que termine antes de contar el siguiente intervalo. Evita solapamiento si una sync tarda más de lo esperado.
  • ThreadPoolTaskScheduler(poolSize=1): jobs ejecutan secuencial, imposible ejecutar dos a la vez aunque lleguen a la vez.
  • No @Transactional en batch: fallo en item 3 no hace rollback de items 1-2 ya guardados. Procesamiento parcial es mejor que todo-o-nada para sync.
  • try/finally en services: SyncLog siempre se escribe aunque explote algo inesperado.
  • DelegatingPasswordEncoder: soporta {noop} en dev y {bcrypt} en prod con el mismo campo de configuración.
  • Flyway sobre Liquibase: menos verboso, SQL puro, suficiente para este proyecto.
  • H2 con MODE=MySQL: mismas migraciones Flyway funcionan en dev y prod sin cambios.