From e5ea57b6732aa52c0074174c39067161c1e990f9 Mon Sep 17 00:00:00 2001 From: George Date: Wed, 14 Oct 2026 17:06:07 -0500 Subject: [PATCH] =?UTF-8?q?Log=20del=20JSON=20enviado=20al=20Service=20Lay?= =?UTF-8?q?er=20cuando=20SAP=20rechaza,=20para=20todos=20los=20documentos.?= =?UTF-8?q?=20Versi=C3=B3n=202.1.5.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Se mueve de CreditNotesRepository a ConnectionServiceLayer (PostAsync, PatchAsyncSBA, PatchNoContentAsync) con un hook asignado en Startup. Co-Authored-By: Claude Sonnet 5.5 --- CLAUDE.md | 19 +++++++++++-------- Net.Business.Services/Startup.cs | 2 ++ .../ConnectionServiceLayer.cs | 18 +++++++++++++++++- .../HelperData/Log/HelperLogServiceLayer.cs | 14 +++++++++----- .../CreditNotes/CreditNotesRepository.cs | 10 ---------- 5 files changed, 39 insertions(+), 24 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index f081b74..8dece2e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -42,14 +42,17 @@ transferencias siempre, sin depender de `Notificacion`. Detalle y tabla en línea de la factura (`LineNum`). Si los lotes no cubren la cantidad, o no se lee la Entrega, responde un error con línea y artículo. PICKING (estado 13) no cambió (lote nuevo por fecha y hora). Detalle en `docs/PICKING.md`. -- **v2.1.5.0 — log del JSON de la NC cuando SAP la rechaza**: - `CreditNotesRepository.SetCreateCreditNotes` guarda en - `ITC_WMS_SAP.ITCA_LOG_INTEGRACION` (vía `Net.Data/HelperData/Log/HelperLogServiceLayer`) - el JSON enviado al Service Layer y la respuesta de SAP, con `ORIGEN = - "ServiceLayer"` y `PROCESO = "NotaCredito Error"`; `NRO_ORDEN`/`DOCENTRY` = - `BaseEntry` de la primera línea. Solo en error (no en éxito); si el log - falla, no rompe el flujo. No cubre transferencias, solicitud de devolución - ni reapertura. +- **v2.1.5.0 — log del JSON enviado al Service Layer cuando SAP lo rechaza** + (cualquier documento, no solo la NC): `ConnectionServiceLayer` llama a + `ErrorLogger` (asignado en `Startup` a `Net.Data/HelperData/Log/HelperLogServiceLayer.AddError`) + en los fallos de `PostAsync`, `PatchAsyncSBA` y `PatchNoContentAsync`. Guarda en + `ITC_WMS_SAP.ITCA_LOG_INTEGRACION`: `ORIGEN = "ServiceLayer"`, + `PROCESO = "ServiceLayer Error"`, `NRO_ORDEN`/`DOCENTRY` = ruta del recurso + (p. ej. `CreditNotes`, truncada a 30), `TRAMA_ORIGEN` = JSON enviado, + `TRAMA_RESPUESTA` = mensaje de SAP. Solo en error. La respuesta de la API al + consumidor no cambia y si el log falla no rompe el flujo. No cubre + `PostActionAsync` (cuerpo `{}`) ni `PostReOpen`/`PostClose`. Los tamaños de las + columnas del log no se verificaron. - Sin probar contra SAP: confirmar con la factura 292410 que SAP devuelve `BaseType`/`BaseEntry` en las líneas y `BatchNumbers` en las de la Entrega. diff --git a/Net.Business.Services/Startup.cs b/Net.Business.Services/Startup.cs index 0a412ec..4e0468d 100644 --- a/Net.Business.Services/Startup.cs +++ b/Net.Business.Services/Startup.cs @@ -19,6 +19,8 @@ namespace Net.Business.Services { Configuration = configuration; Net.Data.LocalData.Configure(configuration); + // Guarda en ITCA_LOG_INTEGRACION el JSON enviado al Service Layer cuando SAP lo rechaza + Net.Connection.ServiceLayer.ConnectionServiceLayer.ErrorLogger = Net.Data.HelperData.Log.HelperLogServiceLayer.AddError; } public IConfiguration Configuration { get; } diff --git a/Net.Connection.ServiceLayer/ConnectionServiceLayer.cs b/Net.Connection.ServiceLayer/ConnectionServiceLayer.cs index 0ca46ac..84e2937 100644 --- a/Net.Connection.ServiceLayer/ConnectionServiceLayer.cs +++ b/Net.Connection.ServiceLayer/ConnectionServiceLayer.cs @@ -19,6 +19,16 @@ namespace Net.Connection.ServiceLayer public static bool ServicioActivo = false; public static string SLSession; + // Se invoca solo cuando SAP rechaza un envio: (ruta del recurso, JSON enviado, respuesta de SAP). + // Lo asigna Startup (esta capa no conoce HANA). + public static Action ErrorLogger; + + private static void LogErrorEnvio(string query, object jsonEnviado, string respuesta) + { + try { ErrorLogger?.Invoke(query, jsonEnviado, respuesta); } + catch { /* el log nunca debe romper el flujo principal */ } + } + private readonly string _url; private readonly string _company; private readonly string _user; @@ -464,6 +474,7 @@ namespace Net.Connection.ServiceLayer var estado = (int)response_.StatusCode; responseBody.CodigoHttp = estado.ToString(); responseBody.Mensaje = "Services Unauthorized : " + _responseLoginServiceLayer.error.message.ToString(); + LogErrorEnvio(Query, obj, responseBody.Mensaje); return responseBody; } @@ -493,6 +504,7 @@ namespace Net.Connection.ServiceLayer var estado = (int)response_.StatusCode; responseBody.CodigoHttp = estado.ToString(); responseBody.Mensaje = "Error Services : " + _responseLoginServiceLayer.error.message.ToString(); + LogErrorEnvio(Query, obj, responseBody.Mensaje); return responseBody; } @@ -1072,6 +1084,7 @@ namespace Net.Connection.ServiceLayer var estado = (int)response_.StatusCode; //responseBody.CodigoHttp = estado.ToString(); responseBody.Mensaje = "Services Unauthorized : " + _responseLoginServiceLayer.error.message.ToString(); + LogErrorEnvio(Query, obj, responseBody.Mensaje); return responseBody; } @@ -1096,6 +1109,7 @@ namespace Net.Connection.ServiceLayer var estado = (int)response_.StatusCode; //responseBody.CodigoHttp = estado.ToString(); responseBody.Mensaje = "Error Services : " + _responseLoginServiceLayer.error.message.ToString(); + LogErrorEnvio(Query, obj, responseBody.Mensaje); return responseBody; } @@ -1163,7 +1177,9 @@ namespace Net.Connection.ServiceLayer return; // exito real - sin body que parsear } - throw new Exception(ExtraerMensajeErrorSap(responseText_)); + var mensajeError = ExtraerMensajeErrorSap(responseText_); + LogErrorEnvio(Query, obj, mensajeError); + throw new Exception(mensajeError); } } diff --git a/Net.Data/HelperData/Log/HelperLogServiceLayer.cs b/Net.Data/HelperData/Log/HelperLogServiceLayer.cs index 563d45c..771d47c 100644 --- a/Net.Data/HelperData/Log/HelperLogServiceLayer.cs +++ b/Net.Data/HelperData/Log/HelperLogServiceLayer.cs @@ -5,14 +5,18 @@ using System.Text; namespace Net.Data.HelperData.Log { - // Guarda en ITCA_LOG_INTEGRACION el JSON enviado al Service Layer cuando SAP rechaza el documento. + // Guarda en ITCA_LOG_INTEGRACION el JSON enviado al Service Layer cuando SAP rechaza un envio (cualquier documento). // Misma tabla que Net.Business.Services.Log.B_Log (esa clase no se puede usar desde Net.Data). public class HelperLogServiceLayer { - public static void AddError(string proceso, string docEntryOrigen, object jsonEnviado, string respuesta) + // query: ruta del recurso del Service Layer (ej. "CreditNotes", "StockTransfers") + public static void AddError(string query, object jsonEnviado, string respuesta) { try { + var recurso = query ?? ""; + if (recurso.Length > 30) recurso = recurso.Substring(0, 30); + var trama = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(jsonEnviado)); var tramaRespuesta = Encoding.UTF8.GetBytes(respuesta ?? ""); @@ -25,10 +29,10 @@ namespace Net.Data.HelperData.Log hanaCommand.Parameters.Add("ORIGEN", HanaDbType.VarChar).Value = "ServiceLayer"; hanaCommand.Parameters.Add("TRAMA_ORIGEN", HanaDbType.Blob).Value = trama; hanaCommand.Parameters.Add("TRAMA_RESPUESTA", HanaDbType.Blob).Value = tramaRespuesta; - hanaCommand.Parameters.Add("PROCESO", HanaDbType.VarChar).Value = proceso; + hanaCommand.Parameters.Add("PROCESO", HanaDbType.VarChar).Value = "ServiceLayer Error"; hanaCommand.Parameters.Add("FECHA", HanaDbType.Date).Value = DateTime.Now.ToString("yyyy-MM-dd"); - hanaCommand.Parameters.Add("NRO_ORDEN", HanaDbType.VarChar).Value = docEntryOrigen ?? ""; - hanaCommand.Parameters.Add("DOCENTRY", HanaDbType.VarChar).Value = docEntryOrigen ?? ""; + hanaCommand.Parameters.Add("NRO_ORDEN", HanaDbType.VarChar).Value = recurso; + hanaCommand.Parameters.Add("DOCENTRY", HanaDbType.VarChar).Value = recurso; hanaCommand.Parameters.Add("DOCNUMSAP", HanaDbType.VarChar).Value = "0"; hanaCommand.Parameters.Add("DOCENTRYSAP", HanaDbType.VarChar).Value = "0"; hanaCommand.Parameters.Add("HORA", HanaDbType.VarChar).Value = DateTime.Now.ToString("HH:mm:ss"); diff --git a/Net.Data/ServicesLayer/CreditNotes/CreditNotesRepository.cs b/Net.Data/ServicesLayer/CreditNotes/CreditNotesRepository.cs index 19a5c1b..118c63b 100644 --- a/Net.Data/ServicesLayer/CreditNotes/CreditNotesRepository.cs +++ b/Net.Data/ServicesLayer/CreditNotes/CreditNotesRepository.cs @@ -6,7 +6,6 @@ using Net.Business.Entities.ServicesLayer.CreditNotes; using Net.Connection; using Net.Connection.ServiceLayer; using Net.CrossCotting; -using Net.Data.HelperData.Log; using System; using System.Collections.Generic; using System.Net.Http; @@ -51,8 +50,6 @@ namespace Net.Data.ServicesLayer.CreditNotes if (!data.Exito) { - // Solo si SAP rechaza: guarda el JSON enviado al Service Layer para poder revisarlo - HelperLogServiceLayer.AddError("NotaCredito Error", DocEntryOrigen(entidad), entidad, data.Mensaje); return resultadoTransaccion.GetInformacionResultado(-1, -1, data.Mensaje); } @@ -60,17 +57,10 @@ namespace Net.Data.ServicesLayer.CreditNotes } catch (Exception ex) { - HelperLogServiceLayer.AddError("NotaCredito Error", DocEntryOrigen(entidad), entidad, ex.Message); return resultadoTransaccion.GetInformacionResultado(-1, -1, ex.Message.ToString()); } } - // DocEntry del documento base (factura o solicitud de devolucion) de la primera linea - private static string DocEntryOrigen(BE_CreditNotesNotes entidad) - { - return entidad?.DocumentLines?.Count > 0 ? entidad.DocumentLines[0].BaseEntry?.ToString() : null; - } - public async Task> UpdateCreateCreditNotes(dynamic entidad, int DocEntry) {