Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
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íaHintPathen los.csprojdeNet.DatayNet.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.dlladotnet build/publish(los.csprojusan esa propiedad si está definida). Forma alternativa: elHintPathes 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í elHintPathresuelve sin tocar los.csproj. (PasarAssemblySearchPathspor 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>"
}
schemaes opcional: si no está, se usaSapServiceLayer: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
Hanala app no arranca (InvalidOperationExceptionclara enLocalData.Configure). Al desplegar sobre un sitio existente hay que agregarla a suappsettings.jsonantes 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.exenativo (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 condotnet --list-runtimesen el servidor: debe listarMicrosoft.AspNetCore.App 3.1.xyMicrosoft.NETCore.App 3.1.x).- El
.xmlde comentarios (ITCA_API_SAP_WMS.xml, que Swagger necesita víaIncludeXmlCommentsenStartup.cs) ya se genera solo en Release desde que se sacó la condiciónDebug|AnyCPUdel.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 publishtransforma automáticamente elprocessPathdelweb.configpara que apunte al.exepublicado — no copiar elweb.configdel código fuente (ese apunta abin\Debug\..., es el de desarrollo) a una carpeta de publish.
Versión y script de publish
- La versión sale de
<Version>/<AssemblyVersion>/<FileVersion>enNet.Business.Services/Net.Business.Services.csproj(actual: 2.1.15.0 en la rama de limpieza;main= 2.1.7.0); súbela en cada publish. Se usa semver (MAJOR.MINOR.PATCH, cuarto número delcsprojsiempre0):MAJORcambia el contrato de la API,MINORagrega funcionalidad compatible,PATCHcorrige o limpia sin cambio de contrato (detalle enCLAUDE.md). publicar.ps1(solo Windows) generadist\ITCA_API_SAP_WMS_v<versión>.zipsinappsettings.json. Desde macOS se hace el mismodotnet publisha mano con-p:HanaDll=..., se borranappsettings.json/appsettings.Development.jsonde la salida y se comprime.- Control de versiones: rama
mainy etiquetasvX.Y.Z.Wpor versión publicada (verCLAUDE.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.0y generar el publish como en "Versión y script de publish" (AssemblyVersionya vale 2.1.7.0 en ese commit). Luegogit switch mainpara regresar. - Si la limpieza se une a
mainy hay que deshacerla, lo más seguro esgit revertdel merge (no reescribe el historial). Evitargit reset --hardygit push --forcesobremain. - La rama
mainno se toca durante la limpieza; el trabajo va enlimpieza-2.1.8.0.
Logs de integración
B_Log(controllers): en cada llamada guarda enITC_WMS_SAP.ITCA_LOG_INTEGRACIONel request del WMS (TRAMA_ORIGEN) y la respuesta de la API (TRAMA_RESPUESTA), conPROCESO(Picking, Recibo...),NRO_ORDEN,DOCENTRY,DOCNUMSAPyDOCENTRYSAP.- 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, conPROCESO = 'ServiceLayer Error',NRO_ORDEN= ruta del recurso yDOCENTRY=BaseEntrydel documento base. Solo en error; no cambia la respuesta de la API. - Ambos usan el esquema fijo
ITC_WMS_SAPy la tabla deHana:logTable(por defectoITCA_LOG_INTEGRACIONpara QA/Desarrollo; en producción ponerITCA_LOG_INTEGRACION_PROD) (columnasPROCESO,NRO_ORDENyDOCENTRYsonVARCHAR(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)
- Detener el Application Pool del sitio (libera los locks de los
.dll/.exe). - Copiar el contenido del nuevo publish sin tocar
appsettings.jsonniweb.configexistentes — esos quedan tal cual están configurados en ese servidor/ambiente (pueden tener valores distintos a los del repo, ej.SapServiceLayer:companyapuntando a una base SAP distinta de QA/prod). - 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:
- Detener el Application Pool primero (si no, falla la compilación por archivos en uso).
- Copiar los
.cs/.csprojque cambiaron a las mismas rutas relativas dentro de la solución en el servidor (no hace falta tocar ningún.csprojpara agregar archivos nuevos — son proyectos SDK-style con glob automático de**/*.cs). - Compilar la solución ahí (Visual Studio → Build Solution en Debug, o
dotnet builddesde la carpeta de la solución). - 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 da500.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 subcarpetalogs\(para questdoutLogEnabledpueda escribir si hace falta diagnosticar).
Diagnóstico de errores de arranque
- Visor de eventos de Windows → Registros de Windows → Aplicación → buscar
el evento de
IIS AspNetCoreModule V2más reciente — casi siempre trae el error real. - Si no alcanza, poner
stdoutLogEnabled="true"en elweb.configde esa carpeta y revisar.\logs\stdout_*.logdespué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.jsontiene credenciales reales en texto plano (SAP Service Layer, HANAB1ADMIN, semilla JWT). Está en.gitignorey no se versiona (soloappsettings.example.json); no subirlo a ningún repositorio.B1ADMINes un usuario administrador de SAP B1; la API solo necesita ejecutar losITCA_*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 porConnectionServiceLayer) acepta cualquier certificado TLS — bypass de validación de certificado hacia el Service Layer, igual criterio queSERVICE_LAYER_VERIFY_SSL=falseen la reescritura Python. (Se quitó el métodoValidarCertificado, que no se usaba.)