Files
itca_api_sap_wms/README.md
T

11 KiB

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.

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:

"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.15.0 en main); súbela en cada publish. Se usa semver (MAJOR.MINOR.PATCH, cuarto número del csproj siempre 0): MAJOR cambia el contrato de la API, MINOR agrega funcionalidad compatible, PATCH corrige o limpia sin cambio de contrato (detalle en CLAUDE.md).
  • 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).

Volver a la versión 2.1.7.0 (punto de retorno antes de la limpieza)

La etiqueta v2.1.7.0 (commit d0118d1) es el último estado antes de la limpieza de código (rama limpieza-2.1.8.0, versión 2.1.8.0). Está en el remoto origin y en el repositorio local.

git fetch --tags                       # si la etiqueta no está en tu copia local
git tag -l "v2.1.*"                    # ver las etiquetas disponibles
git diff v2.1.7.0 --stat               # qué cambió respecto a 2.1.7.0
git checkout v2.1.7.0                  # ver/compilar exactamente esa versión (solo lectura)
git switch -c recuperar-2.1.7.0 v2.1.7.0   # trabajar desde ahí en una rama nueva
  • Para volver a publicar 2.1.7.0: git checkout v2.1.7.0 y generar el publish como en "Versión y script de publish" (AssemblyVersion ya vale 2.1.7.0 en ese commit). Luego git switch main para regresar.
  • Para deshacer la limpieza ya unida a main: como fue avance directo (no hay commit de merge), usar git revert --no-commit v2.1.7.0..HEAD y confirmar (no reescribe el historial). Evitar git reset --hard y git push --force sobre main.
  • La limpieza se unió a main el 2026-10-20 (avance directo, sin commit de merge), por lo que ya no hay una rama aparte; la etiqueta v2.1.7.0 sigue siendo el punto de retorno.

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, 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 y la tabla de Hana:logTable (por defecto ITCA_LOG_INTEGRACION para QA/Desarrollo; en producción poner ITCA_LOG_INTEGRACION_PROD) (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).
  • El cliente HTTP bypass-ssl-validation (usado por ConnectionServiceLayer) acepta cualquier certificado TLS — bypass de validación de certificado hacia el Service Layer, igual criterio que SERVICE_LAYER_VERIFY_SSL=false en la reescritura Python. (Se quitó el método ValidarCertificado, que no se usaba.)