Version 2.1.14.0: se quita PatchAsyncSBA; UpdateCreateCreditNotes usa PatchNoContentAsync (PATCH exitoso = 204 sin cuerpo)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
GeorgeandClaude Sonnet 5.5 committed 2026-10-19 16:57:46 -05:00
1 parent a65cd4d90a
commit e664799de7
5 files changed
+17 -129

No files matched your search

+8 -4
View File
@@ -10,7 +10,7 @@ comprimido (versión antigua)**.
## Versión y control de versiones ## 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 `AssemblyVersion`, `FileVersion`). Cada versión tiene su commit; al publicar
se sube la versión (ver `publicar.ps1`). se sube la versión (ver `publicar.ps1`).
- **Git**: rama `main`, remoto `origin` = - **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** - **v2.1.5.0 — log del JSON enviado al Service Layer cuando SAP lo rechaza**
(cualquier documento, no solo la NC): `ConnectionServiceLayer` llama a (cualquier documento, no solo la NC): `ConnectionServiceLayer` llama a
`ErrorLogger` (asignado en `Startup` a `Net.Data/HelperData/Log/HelperLogServiceLayer.AddError`) `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"`, `ITC_WMS_SAP.ITCA_LOG_INTEGRACION`: `ORIGEN = "ServiceLayer"`,
`PROCESO = "ServiceLayer Error"`, `NRO_ORDEN` = ruta del recurso (p. ej. `PROCESO = "ServiceLayer Error"`, `NRO_ORDEN` = ruta del recurso (p. ej.
`CreditNotes`), `DOCENTRY` = `BaseEntry` de la primera línea del JSON (factura o `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 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). `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). 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 - 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. `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<T>.NombreEstacion`: setter con recursión infinita - `BE_ResultadoTransaccion<T>.NombreEstacion`: setter con recursión infinita
(bomba de tiempo; hoy nada lo asigna). (bomba de tiempo; hoy nada lo asigna).
- `ConnectionServiceLayer.PatchAsyncSBA<T>`: con un `204` real devuelve `null`
sin marcar éxito.
- El cliente HTTP `bypass-ssl-validation` acepta cualquier certificado TLS hacia - El cliente HTTP `bypass-ssl-validation` acepta cualquier certificado TLS hacia
el Service Layer (se quitó el método `ValidarCertificado`, que no se usaba). el Service Layer (se quitó el método `ValidarCertificado`, que no se usaba).
- `appsettings.json` (fuera de git, en `.gitignore`) contiene credenciales - `appsettings.json` (fuera de git, en `.gitignore`) contiene credenciales
@@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyName>ITCA_API_SAP_WMS</AssemblyName> <AssemblyName>ITCA_API_SAP_WMS</AssemblyName>
<Version>2.1.13.0</Version> <Version>2.1.14.0</Version>
<AssemblyVersion>2.1.12.0</AssemblyVersion> <AssemblyVersion>2.1.12.0</AssemblyVersion>
<FileVersion>2.1.12.0</FileVersion> <FileVersion>2.1.12.0</FileVersion>
<UserSecretsId>24763424-3300-4c85-87e3-503f8fdebd14</UserSecretsId> <UserSecretsId>24763424-3300-4c85-87e3-503f8fdebd14</UserSecretsId>
@@ -366,126 +366,13 @@ namespace Net.Connection.ServiceLayer
// PASAMOS LA EXCEPCIÓN A LA APP. // 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<T> PatchAsyncSBA<T>(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<T>(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<ResponseLoginServiceLayer>(responseText_);
_responseLoginServiceLayer.ServicioActivo = false;
if (NumError == 2)
{
dynamic responseBody = JsonConvert.DeserializeObject<T>(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<ResponseLoginServiceLayer>(responseText_);
_responseLoginServiceLayer.ServicioActivo = false;
dynamic responseBody = JsonConvert.DeserializeObject<T>(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) === // === NUEVO (SolicitudTransferencia / Picking HojaRuta Cerrar) ===
// //
// PatchAsyncSBA<T>/PostAsync<T> estan pensados para respuestas con body // PatchNoContentAsync/PostActionAsync no devuelven un T deserializado, solo exito (sin excepcion)
// (200/201) que se deserializan a T y se les "inyecta" Exito/Mensaje/CodigoHttp // o fallo (excepcion con el mensaje real de SAP). En el Service Layer un PATCH exitoso es un 204 sin cuerpo,
// via dynamic - no sirven para una respuesta sin body como el 204 real que // asi que no hay nada que deserializar. (v2.1.14.0: se quito PatchAsyncSBA, que con el 204 devolvia null
// devuelve SAP en un PATCH exitoso o en una accion /Close, y PatchAsyncSBA // sin marcar exito y con un error de red devolvia lo mismo.)
// 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).
public async Task PatchNoContentAsync(string Query, object obj, Dictionary<string, string> extraHeaders = null) public async Task PatchNoContentAsync(string Query, object obj, Dictionary<string, string> extraHeaders = null)
{ {
@@ -73,13 +73,10 @@ namespace Net.Data.ServicesLayer.CreditNotes
try try
{ {
var cadena = $"CreditNotes({DocEntry})"; var cadena = $"CreditNotes({DocEntry})";
var data = await _connectServiceLayer.PatchAsyncSBA<BE_BaseResponseSap>(cadena, entidad); // PATCH exitoso = 204 sin cuerpo; si SAP rechaza lanza excepcion con su mensaje (y deja el JSON en el log)
if (!data.Exito) await _connectServiceLayer.PatchNoContentAsync(cadena, entidad);
{
return resultadoTransaccion.GetInformacionResultado(-1, -1, data.Mensaje);
}
return resultadoTransaccion.GetInformacionResultado(0, "", "Se Listo correctamento...!!!", data); return resultadoTransaccion.GetInformacionResultado(0, "", "Se Listo correctamento...!!!", new BE_BaseResponseSap { Exito = true });
} }
catch (Exception ex) catch (Exception ex)
{ {
+1 -1
View File
@@ -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...), respuesta de la API (`TRAMA_RESPUESTA`), con `PROCESO` (Picking, Recibo...),
`NRO_ORDEN`, `DOCENTRY`, `DOCNUMSAP` y `DOCENTRYSAP`. `NRO_ORDEN`, `DOCENTRY`, `DOCNUMSAP` y `DOCENTRYSAP`.
- **Log de errores del Service Layer** (desde 2.1.5.0): cuando SAP rechaza un - **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 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` `PROCESO = 'ServiceLayer Error'`, `NRO_ORDEN` = ruta del recurso y `DOCENTRY`
= `BaseEntry` del documento base. Solo en error; no cambia la respuesta de la = `BaseEntry` del documento base. Solo en error; no cambia la respuesta de la