Files
itca_api_sap_wms/README.md
T

182 lines
8.9 KiB
Markdown

# ITCA_API_SAP_WMS (.NET) — build, publish y despliegue en IIS
API de integración WMS ↔ SAP Business One (Service Layer + HANA directo),
**ASP.NET Core 3.1**. Para contexto de por qué este proyecto sigue activo
(hay una reescritura en Python en paralelo) ver [`CLAUDE.md`](./CLAUDE.md).
## Requisitos para compilar
- .NET Core 3.1 SDK.
- Cliente HANA .NET instalado en
`C:\Program Files\sap\hdbclient\dotnetcore\v2.1\Sap.Data.Hana.Core.v2.1.dll`
(ruta hardcodeada vía `HintPath` en los `.csproj` de `Net.Data` y
`Net.Business.Services` — sin esto no compila ningún proyecto que dependa
de HANA).
- **Compilar desde macOS/Linux** (forma simple): pasar la ruta de la DLL con
`-p:HanaDll=<ruta>/Sap.Data.Hana.Core.v2.1.dll` a `dotnet build`/`publish`
(los `.csproj` usan esa propiedad si está definida). Forma alternativa: el
`HintPath` es relativo (6 niveles hacia
arriba + `Program Files\sap\...`). Se puede compilar copiando el repo a
`<raiz>/a/b/c/d/repo/` y poniendo la DLL en
`<raiz>/Program Files/sap/hdbclient/dotnetcore/v2.1/` — así el `HintPath`
resuelve sin tocar los `.csproj`. (Pasar `AssemblySearchPaths` por línea
de comandos NO sirve: rompe la resolución del framework.)
## Configuración de HANA (`appsettings.json`, sección `Hana`)
Las credenciales y el esquema de HANA ya **no** están fijos en el código; hay
un único lugar, `LocalData.Configure(...)` (`Net.Data/LocalData.cs`), que se
llama desde `Startup`:
```json
"Hana": {
"server": "192.168.0.13:30015",
"user": "<usuario>",
"password": "<password>",
"schema": "<opcional>"
}
```
- `schema` es opcional: si no está, se usa `SapServiceLayer:company` (si no hay ninguno de los dos la app no arranca, no hay valor por defecto), de modo
que lecturas por HANA (`CALL "<esquema>"."ITCA_..."`) y escrituras por
Service Layer apunten siempre a la misma compañía.
- **Si falta la sección `Hana` la app no arranca** (`InvalidOperationException`
clara en `LocalData.Configure`). Al desplegar sobre un sitio existente hay
que agregarla a su `appsettings.json` **antes** de iniciar el pool.
## Correr localmente
```
dotnet run --project Net.Business.Services
```
Swagger queda en la raíz (`RoutePrefix = ""` en `Startup.cs`): `http://localhost:<puerto>/`.
## Generar el publish para IIS
```
dotnet publish Net.Business.Services/Net.Business.Services.csproj -c Release -r win-x64 --self-contained false -o <carpeta-salida>
```
- `-r win-x64 --self-contained false`: genera un `.exe` nativo (apphost) de
64 bits que no incluye el runtime — depende de que el servidor ya tenga el
**ASP.NET Core 3.1 Hosting Bundle** instalado (confirmarlo con
`dotnet --list-runtimes` en el servidor: debe listar
`Microsoft.AspNetCore.App 3.1.x` y `Microsoft.NETCore.App 3.1.x`).
- El `.xml` de comentarios (`ITCA_API_SAP_WMS.xml`, que Swagger necesita vía
`IncludeXmlComments` en `Startup.cs`) ya se genera solo en Release desde
que se sacó la condición `Debug|AnyCPU` del `.csproj` — si en algún
momento vuelve a faltar en el output del publish, revisar que esa
propiedad (`DocumentationFile`) siga sin estar condicionada a Debug.
- `dotnet publish` transforma automáticamente el `processPath` del
`web.config` para que apunte al `.exe` publicado — **no** copiar el
`web.config` del código fuente (ese apunta a `bin\Debug\...`, es el de
desarrollo) a una carpeta de publish.
## Versión y script de publish
- La versión sale de `<Version>`/`<AssemblyVersion>`/`<FileVersion>` en
`Net.Business.Services/Net.Business.Services.csproj` (actual: **2.1.7.0**);
súbela en cada publish.
- `publicar.ps1` (solo Windows) genera `dist\ITCA_API_SAP_WMS_v<versión>.zip` sin
`appsettings.json`. Desde macOS se hace el mismo `dotnet publish` a mano con
`-p:HanaDll=...`, se borran `appsettings.json`/`appsettings.Development.json` de
la salida y se comprime.
- Control de versiones: rama `main` y etiquetas `vX.Y.Z.W` por versión
publicada (ver `CLAUDE.md`).
## Logs de integración
- **`B_Log`** (controllers): en cada llamada guarda en
`ITC_WMS_SAP.ITCA_LOG_INTEGRACION` el request del WMS (`TRAMA_ORIGEN`) y la
respuesta de la API (`TRAMA_RESPUESTA`), con `PROCESO` (Picking, Recibo...),
`NRO_ORDEN`, `DOCENTRY`, `DOCNUMSAP` y `DOCENTRYSAP`.
- **Log de errores del Service Layer** (desde 2.1.5.0): cuando SAP rechaza un
envío con cuerpo JSON (`PostAsync`, `PatchAsyncSBA`, `PatchNoContentAsync`)
se guarda en esa misma tabla el JSON enviado y el mensaje de SAP, con
`PROCESO = 'ServiceLayer Error'`, `NRO_ORDEN` = ruta del recurso y `DOCENTRY`
= `BaseEntry` del documento base. Solo en error; no cambia la respuesta de la
API.
- Ambos usan el esquema fijo `ITC_WMS_SAP` (columnas `PROCESO`, `NRO_ORDEN` y
`DOCENTRY` son `VARCHAR(100)`).
- Consulta de ejemplo:
`SELECT * FROM "ITC_WMS_SAP"."ITCA_LOG_INTEGRACION" WHERE "PROCESO" = 'ServiceLayer Error' ORDER BY "FECHA" DESC, "HORA" DESC`
## Desplegar/actualizar un sitio en IIS
### Si el sitio YA corre desde una carpeta de publish (actualización de versión)
1. Detener el Application Pool del sitio (libera los locks de los `.dll`/`.exe`).
2. Copiar el contenido del nuevo publish **sin tocar** `appsettings.json` ni
`web.config` existentes — esos quedan tal cual están configurados en ese
servidor/ambiente (pueden tener valores distintos a los del repo, ej.
`SapServiceLayer:company` apuntando a una base SAP distinta de QA/prod).
3. Iniciar el Application Pool.
### Si el sitio corre directo desde el código fuente (`bin\Debug\...`)
Así corre al menos un ambiente real (`CLONE-SRV-WEB`, ruta física
`...\BackEnd\Desarrollo\BackEnd\Net.Business.Services`, `web.config` con
`processPath="bin\Debug\netcoreapp3.1\ITCA_API_SAP_WMS.exe"`) — ahí **no**
se usa `dotnet publish`, se compila in situ:
1. Detener el Application Pool primero (si no, falla la compilación por
archivos en uso).
2. Copiar los `.cs`/`.csproj` que cambiaron a las mismas rutas relativas
dentro de la solución en el servidor (no hace falta tocar ningún
`.csproj` para agregar archivos nuevos — son proyectos SDK-style con
glob automático de `**/*.cs`).
3. Compilar la solución ahí (Visual Studio → Build Solution en Debug, o
`dotnet build` desde la carpeta de la solución).
4. Iniciar el Application Pool.
### Checklist de configuración del Application Pool (IIS Manager, una sola vez por pool nuevo)
- **.NET CLR Version**: Sin código administrado (No Managed Code).
- **Configuración avanzada → Habilitar aplicaciones de 32 bits**: **False**
(el `.exe`/DLLs son de 64 bits — con esto en True da
`500.32 Failed to load .NET Core host`, bitness mismatch).
- **Configuración avanzada → Cargar perfil de usuario (Load User Profile)**:
**True** — sin esto, arranque falla con `ErrorCode '0x8007023e'`
(excepción no manejada al iniciar el proceso in-process).
- **Permisos de carpeta**: `IIS AppPool\<NombreDelPool>` con **Lectura y
ejecución** sobre toda la carpeta del sitio, y **Modificar** sobre la
subcarpeta `logs\` (para que `stdoutLogEnabled` pueda escribir si hace
falta diagnosticar).
### Diagnóstico de errores de arranque
1. Visor de eventos de Windows → Registros de Windows → Aplicación → buscar
el evento de `IIS AspNetCoreModule V2` más reciente — casi siempre trae
el error real.
2. Si no alcanza, poner `stdoutLogEnabled="true"` en el `web.config` de esa
carpeta y revisar `.\logs\stdout_*.log` después de reciclar el pool.
### Editar `appsettings.json` de un sitio ya desplegado
No hace falta detener el Application Pool para editarlo (no queda
bloqueado como los binarios) — pero conviene **reciclar el pool** después
de guardar, para garantizar que el valor nuevo se relea (el host usa
`Host.CreateDefaultBuilder`, que ya trae `reloadOnChange: true` para
`appsettings.json`, pero no vale la pena depender de eso para un cambio
puntual).
El nombre de la base SAP (`CompanyDB`) se configura en
`SapServiceLayer:company` dentro de `appsettings.json` — lo lee
`ConnectionServiceLayer.cs` (`_company = configuration["SapServiceLayer:company"]`)
para armar el body del `Login` contra el Service Layer.
## Riesgos de seguridad heredados (documentados, no corregidos sin coordinar antes)
- `appsettings.json` tiene credenciales reales en texto plano (SAP Service
Layer, **HANA `B1ADMIN`**, semilla JWT). Está en `.gitignore` y no se
versiona (solo `appsettings.example.json`); no subirlo a ningún repositorio.
- `B1ADMIN` es un usuario administrador de SAP B1; la API solo necesita
ejecutar los `ITCA_*` y leer algunas tablas. Conviene un usuario HANA de
permisos mínimos y rotar la contraseña (estuvo en texto plano en el código
y en el historial git del repo antiguo archivado).
- `ValidarCertificado` en `ConnectionServiceLayer.cs` acepta cualquier
certificado TLS (`return true;` siempre) — bypass de validación de
certificado hacia el Service Layer, igual criterio que
`SERVICE_LAYER_VERIFY_SSL=false` en la reescritura Python.