Aplicación web de Devolución a Proveedores para SAP Business One
Reemplaza al addon .NET Framework (SolucionLibros). Genera Devoluciones de mercancías enlazadas a sus Entradas por compra a partir de un listado pegado desde Excel. - libs/shared (@dp/shared): modelos y lógica de dominio pura (FIFO, consolidación por proveedor, parseo del portapapeles), portada del addon. - apps/api (NestJS): cliente del Service Layer con cola por sesión, login con la sesión de SAP, metadatos (UDT/UDF/UDO) y botón Reconstruir, catálogos, planificaciones (pegar, procesar, reprocesar, marcar líneas, crear Devoluciones, cerrar), listado paginado con filtros y descarga de PDF de la Guía de despacho desde BES. Sirve también la web compilada, con HTTPS opcional. - apps/web (Angular): login, Configuración → Metadatos y Planificaciones (listado, nueva, detalle). - deploy/windows y scripts/publicar.mjs: despliegue como servicio de Windows con WinSW. Verificado contra el Service Layer de SQL Server. Sin probar aún: SAP HANA y la instalación del servicio en Windows (ver CLAUDE.md, sección "Sin verificar"). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
287b187c14
126 files changed
+20033
No files matched your search
@@ -0,0 +1,138 @@
|
||||
# API — Devolución a Proveedores
|
||||
|
||||
Backend en **NestJS 12** (ESM) de la aplicación Devolución a Proveedores para SAP Business One.
|
||||
No tiene base de datos propia: habla con el **Service Layer** de SAP B1 en nombre del usuario que
|
||||
inició sesión, y guarda cada ejecución en el UDO `LIB_ODEV`.
|
||||
|
||||
La documentación general del proyecto (decisiones, flujo, estado) está en
|
||||
[`../../CLAUDE.md`](../../CLAUDE.md). Este archivo cubre solo cómo ejecutar y mantener la API.
|
||||
|
||||
## Requisitos
|
||||
|
||||
- Node 22 LTS o superior.
|
||||
- Acceso de red al Service Layer de SAP B1 (por defecto `https://servidor:50000/b1s/v1`).
|
||||
- Red con Fortinet (inspección SSL): exportar `NODE_OPTIONS=--use-system-ca` (o `NODE_EXTRA_CA_CERTS`)
|
||||
antes de usar `npm`.
|
||||
|
||||
## Puesta en marcha
|
||||
|
||||
Desde la raíz del monorepo (`devolucion-proveedores-web/`):
|
||||
|
||||
```bash
|
||||
npm install
|
||||
cp apps/api/.env.example apps/api/.env # y completar los valores
|
||||
npm run dev:api # compila @dp/shared y levanta la API en :3000
|
||||
```
|
||||
|
||||
### Variables de `.env`
|
||||
|
||||
| Variable | Descripción |
|
||||
|---|---|
|
||||
| `PORT` | Puerto de la API (3000). |
|
||||
| `WEB_ORIGIN` | Origen del frontend para CORS. En desarrollo `ng serve` usa proxy y no hace falta. |
|
||||
| `SL_URL` | URL del Service Layer, **con `/b1s/v1` al final**. |
|
||||
| `SL_COMPANY_DB` | Base de la sociedad (CompanyDB del Login). |
|
||||
| `SL_REJECT_UNAUTHORIZED` | `false` si el Service Layer usa un certificado autofirmado. |
|
||||
| `DB_TYPE` | `HANA` (producción) o `MSSQL` (pruebas). Elige la variante de cada consulta SQL. |
|
||||
| `COOKIE_SECURE` | `true` solo cuando la API se publica por HTTPS. |
|
||||
| `HTTPS_PFX_PATH` / `HTTPS_PFX_PASSPHRASE` | Solo producción: certificado `.pfx` y su clave. La API escucha por HTTPS. |
|
||||
| `HTTPS_KEY_PATH` / `HTTPS_CERT_PATH` | Alternativa al `.pfx`: clave privada y certificado en PEM (no mezclar con el `.pfx`). |
|
||||
| `HTTP_REDIRECT_PORT` | Con HTTPS: puerto HTTP que redirige (301) al HTTPS, normalmente 80. |
|
||||
| `WEB_DIST_DIR` | Carpeta con la web compilada. Por defecto `apps/web/dist/web/browser` si existe. |
|
||||
|
||||
El **usuario y la contraseña de SAP no se configuran aquí**: se escriben en la pantalla de login y se
|
||||
usan para el `Login` del Service Layer. La API reinicia sin conservar sesiones.
|
||||
|
||||
## Scripts
|
||||
|
||||
Se ejecutan con `npm run <script> -w api` desde la raíz, o directamente desde `apps/api`.
|
||||
|
||||
| Script | Qué hace |
|
||||
|---|---|
|
||||
| `start:dev` | Modo watch (recompila y reinicia; **se pierden las sesiones** en cada reinicio). |
|
||||
| `build` / `start:prod` | Compila a `dist/` y ejecuta `node dist/main`. |
|
||||
| `test` | Tests unitarios (Vitest). |
|
||||
| `test:e2e` | Tests e2e (`test/`). |
|
||||
| `test:cov` | Cobertura. |
|
||||
| `lint` | `oxlint --type-aware src/ test/`. |
|
||||
|
||||
## Endpoints (prefijo `/api`)
|
||||
|
||||
Todos exigen sesión (cookie `dp_sid`) salvo `/health`, `/auth/info`, `/auth/login` y `/auth/logout`.
|
||||
|
||||
| Área | Rutas |
|
||||
|---|---|
|
||||
| Salud y sesión | `GET /health` · `GET /auth/info` · `POST /auth/login` · `POST /auth/logout` · `GET /auth/me` |
|
||||
| Metadatos | `GET /metadata/status` · `POST /metadata/rebuild` |
|
||||
| Catálogos | `GET /catalogos` · `GET /catalogos/almacenes` |
|
||||
| Planificaciones | `GET /planificaciones` (paginado, con filtros) · `GET /planificaciones/abierta` · `GET /planificaciones/:id` |
|
||||
| | `POST /planificaciones/pegar` · `POST /planificaciones` · `PATCH /planificaciones/:id/cabecera` · `PATCH /planificaciones/:id/lineas/:lineId` |
|
||||
| | `POST /planificaciones/:id/devoluciones` · `POST /planificaciones/:id/reprocesar` · `POST /planificaciones/:id/cerrar` |
|
||||
| PDF | `POST /planificaciones/:id/pdf` |
|
||||
|
||||
El detalle de cada ruta (parámetros y reglas) está en la tabla de [`CLAUDE.md`](../../CLAUDE.md#api-prefijo-api).
|
||||
Ejemplo del listado: `GET /api/planificaciones?status=C&whsCode=01&desde=2026-10-01&pagina=2&tamano=20`.
|
||||
|
||||
## Producción
|
||||
|
||||
En producción la API **sirve también la web compilada** (si existe `apps/web/dist/web/browser`): todo queda
|
||||
en el mismo origen y en un solo puerto. Las rutas de Angular (sin extensión y fuera de `/api`) devuelven
|
||||
`index.html`; los archivos con hash se cachean un año; se agregan cabeceras de seguridad (y HSTS con HTTPS).
|
||||
|
||||
```bash
|
||||
npm run publicar # desde la raíz: arma release/devolucion-proveedores/
|
||||
```
|
||||
|
||||
Instalación en Windows (servicio con WinSW, HTTPS, firewall, actualizaciones): [`../../deploy/windows/DEPLOY.md`](../../deploy/windows/DEPLOY.md).
|
||||
|
||||
**Logs:** la API no escribe archivos; usa el `Logger` de Nest por consola. Como servicio, WinSW captura
|
||||
esa salida y la rota. Con `NO_COLOR=1` el log sale sin códigos de color.
|
||||
|
||||
## Estructura
|
||||
|
||||
```
|
||||
src/
|
||||
├── config/ lectura y validación del .env
|
||||
├── service-layer/ ServiceLayerClient (axios) y SessionStore (en memoria)
|
||||
├── auth/ login/logout/me, SessionGuard global y @Public()
|
||||
├── sql-queries/ consultas guardadas (SQLQueries) con variantes HANA y MSSQL
|
||||
├── catalogos/ almacenes, series y valores válidos de los UDF
|
||||
├── planificaciones/ FIFO, UDO LIB_ODEV, Devoluciones, reprocesar y listado
|
||||
├── pdf/ SOAP de BES (Consulta_TXTDTE) y unión de PDF
|
||||
├── metadata/ definiciones JSON de tablas/campos/UDO y su reconstrucción
|
||||
├── health/
|
||||
├── http-exception.logger.ts registra en el log todo error 4xx/5xx
|
||||
├── web-estatico.ts sirve la web compilada, fallback a index.html y cabeceras de seguridad
|
||||
└── https.ts lee el certificado y redirige HTTP → HTTPS
|
||||
```
|
||||
|
||||
La lógica de dominio pura (FIFO, consolidación por proveedor, parseo del texto de Excel) vive en
|
||||
`libs/shared` (`@dp/shared`), que se consume **compilada** (`dist/`): tras cambiarla,
|
||||
`npm run build:shared`.
|
||||
|
||||
## Cosas que conviene saber
|
||||
|
||||
- **Una sesión del Service Layer no admite peticiones simultáneas**: las procesa en serie y cada una
|
||||
espera unos 7 s. `ServiceLayerClient.request` las encola por sesión; no uses otro cliente HTTP para
|
||||
hablar con SAP. Una petición que tarde más de 2 s queda avisada en el log.
|
||||
- **Consultas SQL**: se guardan en SAP como `SQLQueries` (`DP_Stock`, `DP_EntradasAbiertas`) y la API las
|
||||
crea o actualiza sola. SAP reescribe el SQL al guardarlo, por lo que **al modificar una consulta hay
|
||||
que subir su `version`** en `sql-queries/sql-queries.definitions.ts`. El analizador SQL no admite
|
||||
funciones como `FORMAT`; los parámetros van entre comillas simples.
|
||||
- **Credenciales del web service DTE** (`@DTE_CONFC`): se leen en cada descarga de PDF, no se registran
|
||||
en el log y no se devuelven al navegador.
|
||||
- **Metadatos**: la fuente de verdad son los JSON de `src/metadata/definitions/`. *Reconstruir* crea lo
|
||||
que falta; borrar y recrear un campo solo ocurre con autorización explícita.
|
||||
- Los tipos que aparecen en firmas con decoradores se importan con `import type` (por
|
||||
`isolatedModules` + `emitDecoratorMetadata`); los DTO, que son clases, se importan como valor.
|
||||
- **HANA**: las variantes HANA de las consultas están escritas pero no se han ejecutado todavía.
|
||||
|
||||
## Diagnóstico rápido
|
||||
|
||||
| Síntoma | Causa probable |
|
||||
|---|---|
|
||||
| `No se pudo conectar con el Service Layer (EHOSTUNREACH)` | Proceso de la API con un estado de red viejo (cambio de red o VPN). Reiniciarla. |
|
||||
| 401 `Debe iniciar sesión` tras tocar el código | La API se reinició y perdió las sesiones. Volver a iniciar sesión. |
|
||||
| Respuestas de 20-40 s | Peticiones en paralelo sobre una misma sesión (ver arriba). Mirar los avisos `Petición lenta` del log. |
|
||||
| `Invalid SQL syntax ... Cannot support this function` | El analizador SQL del Service Layer rechazó una función en una consulta guardada. |
|
||||
| `SELF_SIGNED_CERT_IN_CHAIN` al instalar | Falta `NODE_OPTIONS=--use-system-ca` o `NODE_EXTRA_CA_CERTS`. |
|
||||
Reference in new issue
Block a user