From e664799de7bdcecf87701e44f51c0775bfcf4aef Mon Sep 17 00:00:00 2001 From: George Date: Mon, 19 Oct 2026 16:57:46 -0500 Subject: [PATCH] Version 2.1.14.0: se quita PatchAsyncSBA; UpdateCreateCreditNotes usa PatchNoContentAsync (PATCH exitoso = 204 sin cuerpo) Co-Authored-By: Claude Sonnet 5.5 --- CLAUDE.md | 12 +- .../Net.Business.Services.csproj | 2 +- .../ConnectionServiceLayer.cs | 121 +----------------- .../CreditNotes/CreditNotesRepository.cs | 9 +- README.md | 2 +- 5 files changed, 17 insertions(+), 129 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 14dded6..1eeba76 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,7 +10,7 @@ comprimido (versión antigua)**. ## Versión y control de versiones -- **Versión actual: 2.1.13.0** (rama `limpieza-2.1.8.0`; `main` = 2.1.7.0) (`Net.Business.Services.csproj`: `Version`, +- **Versión actual: 2.1.14.0** (rama `limpieza-2.1.8.0`; `main` = 2.1.7.0) (`Net.Business.Services.csproj`: `Version`, `AssemblyVersion`, `FileVersion`). Cada versión tiene su commit; al publicar se sube la versión (ver `publicar.ps1`). - **Git**: rama `main`, remoto `origin` = @@ -59,7 +59,7 @@ transferencias siempre, sin depender de `Notificacion`. Detalle y tabla en - **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 + en los fallos de `PostAsync` y `PatchNoContentAsync` (hasta la 2.1.13.0 también `PatchAsyncSBA`). Guarda en `ITC_WMS_SAP.ITCA_LOG_INTEGRACION`: `ORIGEN = "ServiceLayer"`, `PROCESO = "ServiceLayer Error"`, `NRO_ORDEN` = ruta del recurso (p. ej. `CreditNotes`), `DOCENTRY` = `BaseEntry` de la primera línea del JSON (factura o @@ -145,6 +145,12 @@ transferencias siempre, sin depender de `Notificacion`. Detalle y tabla en la NC estaban copiados en Picking DESPACHO, Picking PICKING (estado 13) y Recibo Devolución; ahora están en `Net.Data/ServicesLayer/CreditNotes/CabeceraNotaCredito.Completar` (con comentarios de la regla de la 2.1.7.0). Cada llamador pasa los datos de su origen (factura o, en Recibo sin factura, la solicitud de devolución). +- **v2.1.14.0 — se quitó `PatchAsyncSBA`**: su único llamador (`CreditNotesRepository.UpdateCreateCreditNotes`, que agrega + a la NC las referencias de las transferencias automáticas) usa ahora `PatchNoContentAsync`. En el Service Layer un PATCH + exitoso es un `204` sin cuerpo; `PatchAsyncSBA` devolvía `null` en ese caso (y en cualquier error de red) y el llamador + lo tomaba como fallo. Ahora el éxito es éxito y un rechazo de SAP lanza su mensaje (se captura y devuelve como error; + `Crear` sigue ignorando el resultado). Sigue sin reintentar el 401 (hace login antes de cada llamada). Los docs de + `docs/PORTE_FUNCIONALIDAD_NUEVA.md` y `docs/SOLICITUD_TRANSFERENCIA.md` mencionan `PatchAsyncSBA` como historia del porte. - 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. @@ -197,8 +203,6 @@ Picking, columna `HORA` en `B_Log`. Lo que **no** se trajo: `Chatbots` y - `BE_ResultadoTransaccion.NombreEstacion`: setter con recursión infinita (bomba de tiempo; hoy nada lo asigna). -- `ConnectionServiceLayer.PatchAsyncSBA`: con un `204` real devuelve `null` - sin marcar éxito. - El cliente HTTP `bypass-ssl-validation` acepta cualquier certificado TLS hacia el Service Layer (se quitó el método `ValidarCertificado`, que no se usaba). - `appsettings.json` (fuera de git, en `.gitignore`) contiene credenciales diff --git a/Net.Business.Services/Net.Business.Services.csproj b/Net.Business.Services/Net.Business.Services.csproj index 3ee87a5..87f106f 100644 --- a/Net.Business.Services/Net.Business.Services.csproj +++ b/Net.Business.Services/Net.Business.Services.csproj @@ -3,7 +3,7 @@ netcoreapp3.1 ITCA_API_SAP_WMS - 2.1.13.0 + 2.1.14.0 2.1.12.0 2.1.12.0 24763424-3300-4c85-87e3-503f8fdebd14 diff --git a/Net.Connection.ServiceLayer/ConnectionServiceLayer.cs b/Net.Connection.ServiceLayer/ConnectionServiceLayer.cs index dc6bd93..cdc7914 100644 --- a/Net.Connection.ServiceLayer/ConnectionServiceLayer.cs +++ b/Net.Connection.ServiceLayer/ConnectionServiceLayer.cs @@ -366,126 +366,13 @@ namespace Net.Connection.ServiceLayer // PASAMOS LA EXCEPCIÓN A LA APP. } } - // PATCH con cuerpo JSON. OJO: con un 204 real devuelve null sin marcar exito (bug conocido); para PATCH sin respuesta usar PatchNoContentAsync - public async Task PatchAsyncSBA(string Query, object obj) - { - try - { - // CONSTRUIMOS LA URL DE LA ACCIÓN - var urlBuilder_ = new StringBuilder(); - urlBuilder_.Append(_url != null ? _url.TrimEnd('/') : "") - .Append("/" + Query); - var url_ = urlBuilder_.ToString(); - - // RECUPERAMOS EL HttpClient - var client_ = _httpClient.CreateClient("bypass-ssl-validation"); - - int NumError = 0; - - band: - - using (var request_ = new HttpRequestMessage()) - { - /////////////////////////////////////// - // CONSTRUIMOS LA PETICIÓN (REQUEST) // - /////////////////////////////////////// - // DEFINIMOS EL MÉTODO HTTP GET - request_.Method = new HttpMethod("PATCH"); - - // DEFINIMOS LA URI - request_.RequestUri = new Uri(url_, System.UriKind.RelativeOrAbsolute); - - // DEFINIMOS EL Accept, EN ESTE CASO ES "application/json" - request_.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); - request_.Content = new StringContent(JsonConvert.SerializeObject(obj), System.Text.Encoding.UTF8, "application/json"); - - // ASIGNAMOS A LA CABECERA DE LA PETICIÓN EL TOKEN JWT. - if (_responseLoginServiceLayer.ServicioActivo) - request_.Headers.Add("Cookie", "B1SESSION=" + _responseLoginServiceLayer.SessionId); - - ///////////////////////////////////////// - // CONSTRUIMOS LA RESPUESTA (RESPONSE) // - ///////////////////////////////////////// - // Utilizamos ConfigureAwait(false) para evitar el DeadLock. - var response_ = await client_.SendAsync(request_, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); - - // OBTENEMOS EL Content DEL RESPONSE como un String - // Utilizamos ConfigureAwait(false) para evitar el DeadLock. - var responseText_ = await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - - // SI ES LA RESPUESTA ESPERADA !! ... - if (response_.StatusCode == System.Net.HttpStatusCode.OK || response_.StatusCode == System.Net.HttpStatusCode.Created) // 200 or Created - { - // DESERIALIZAMOS Content DEL RESPONSE - dynamic responseBody = JsonConvert.DeserializeObject(responseText_); - var estado = (int)response_.StatusCode; - responseBody.Mensaje = "SE ENVIO CORRECTAMENTE A SAP BO "; - return responseBody; - } - else - // SI NO SE ESTÁ AUTORIZADO ... - if (response_.StatusCode == System.Net.HttpStatusCode.Unauthorized) // 401 - { - - NumError += 1; - - _responseLoginServiceLayer = JsonConvert.DeserializeObject(responseText_); - _responseLoginServiceLayer.ServicioActivo = false; - - if (NumError == 2) - { - dynamic responseBody = JsonConvert.DeserializeObject(responseText_); - var estado = (int)response_.StatusCode; - responseBody.Mensaje = "Services Unauthorized : " + _responseLoginServiceLayer.error.message.ToString(); - LogErrorEnvio(Query, obj, responseBody.Mensaje); - return responseBody; - } - - - if (_responseLoginServiceLayer.error.code.ToString() == "301") - { - await Login(); - goto band; - } - } - else - // CUALQUIER OTRA RESPUESTA ... - if (response_.StatusCode != System.Net.HttpStatusCode.OK && response_.StatusCode != System.Net.HttpStatusCode.NoContent) // 204 - { - - //NumError += 1; - - _responseLoginServiceLayer = JsonConvert.DeserializeObject(responseText_); - _responseLoginServiceLayer.ServicioActivo = false; - - dynamic responseBody = JsonConvert.DeserializeObject(responseText_); - var estado = (int)response_.StatusCode; - responseBody.Mensaje = "Error Services : " + _responseLoginServiceLayer.error.message.ToString(); - LogErrorEnvio(Query, obj, responseBody.Mensaje); - return responseBody; - } - - // RETORNAMOS EL OBJETO POR DEFECTO ESPERADO - return default(T); - } - } - catch (Exception ex) - { - return default(T); - } - } // === NUEVO (SolicitudTransferencia / Picking HojaRuta Cerrar) === // - // PatchAsyncSBA/PostAsync estan pensados para respuestas con body - // (200/201) que se deserializan a T y se les "inyecta" Exito/Mensaje/CodigoHttp - // via dynamic - no sirven para una respuesta sin body como el 204 real que - // devuelve SAP en un PATCH exitoso o en una accion /Close, y PatchAsyncSBA - // tiene ademas un bug conocido ahi (ver docs/PORTE_FUNCIONALIDAD_NUEVA.md, - // hallazgo #2): con un 204 real cae a "return default(T)" sin marcar exito. - // Por eso estos dos metodos nuevos son deliberadamente simples: no devuelven - // un T deserializado, solo exito (sin excepcion) o fallo (excepcion con el - // mensaje real de SAP). + // PatchNoContentAsync/PostActionAsync no devuelven un T deserializado, solo exito (sin excepcion) + // o fallo (excepcion con el mensaje real de SAP). En el Service Layer un PATCH exitoso es un 204 sin cuerpo, + // asi que no hay nada que deserializar. (v2.1.14.0: se quito PatchAsyncSBA, que con el 204 devolvia null + // sin marcar exito y con un error de red devolvia lo mismo.) public async Task PatchNoContentAsync(string Query, object obj, Dictionary extraHeaders = null) { diff --git a/Net.Data/ServicesLayer/CreditNotes/CreditNotesRepository.cs b/Net.Data/ServicesLayer/CreditNotes/CreditNotesRepository.cs index 921e844..6078fec 100644 --- a/Net.Data/ServicesLayer/CreditNotes/CreditNotesRepository.cs +++ b/Net.Data/ServicesLayer/CreditNotes/CreditNotesRepository.cs @@ -73,13 +73,10 @@ namespace Net.Data.ServicesLayer.CreditNotes try { var cadena = $"CreditNotes({DocEntry})"; - var data = await _connectServiceLayer.PatchAsyncSBA(cadena, entidad); - if (!data.Exito) - { - return resultadoTransaccion.GetInformacionResultado(-1, -1, data.Mensaje); - } + // PATCH exitoso = 204 sin cuerpo; si SAP rechaza lanza excepcion con su mensaje (y deja el JSON en el log) + await _connectServiceLayer.PatchNoContentAsync(cadena, entidad); - return resultadoTransaccion.GetInformacionResultado(0, "", "Se Listo correctamento...!!!", data); + return resultadoTransaccion.GetInformacionResultado(0, "", "Se Listo correctamento...!!!", new BE_BaseResponseSap { Exito = true }); } catch (Exception ex) { diff --git a/README.md b/README.md index 402356b..fed41b2 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,7 @@ git switch -c recuperar-2.1.7.0 v2.1.7.0 # trabajar desde ahí en una rama nue 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`) + 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