From 86c12556e5d2c5148a164dacb48a3b6c71a0708b Mon Sep 17 00:00:00 2001 From: mireya-sp Date: Sun, 24 May 2026 19:23:34 +0200 Subject: [PATCH] d --- CLAUDE (1).md | 226 -------------------------------------------------- 1 file changed, 226 deletions(-) delete mode 100644 CLAUDE (1).md diff --git a/CLAUDE (1).md b/CLAUDE (1).md deleted file mode 100644 index dec17cf..0000000 --- a/CLAUDE (1).md +++ /dev/null @@ -1,226 +0,0 @@ - # 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: `. 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= -SPRING_JPA_DATABASE_PLATFORM=org.hibernate.dialect.MySQLDialect -SPRING_JPA_HIBERNATE_DDL_AUTO=validate -SYNC_SCHEDULING_ENABLED=true -DOLIBARR_API_KEY= -PRESTASHOP_API_KEY= -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.