10 Commits
Author SHA1 Message Date
GeorgeandClaude Sonnet 5.5 44efabb44c AssemblyVersion y FileVersion a 2.1.15.0 (estaban en 2.1.12.0); publicar.ps1 multiplataforma, unica forma de generar el zip
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 14:51:41 -05:00
GeorgeandClaude Sonnet 5.5 3c104cca45 README: como deshacer la limpieza (avance directo, sin commit de merge)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 14:46:49 -05:00
GeorgeandClaude Sonnet 5.5 215aff2c64 Docs: la limpieza se unio a main (rama unica)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 14:46:41 -05:00
GeorgeandClaude Sonnet 5.5 18e01c2774 Documentar semver (regla en CLAUDE.md y README), publicar.ps1 con 3 numeros y nuevo remoto
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 14:45:14 -05:00
GeorgeandClaude Sonnet 5.5 d7676dee41 Version 2.1.15.0: avisar en el Message si falla la transferencia automatica de la NC
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 16:59:31 -05:00
GeorgeandClaude Sonnet 5.5 e664799de7 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>
2026-10-19 16:57:46 -05:00
GeorgeandClaude Sonnet 5.5 a65cd4d90a Version 2.1.13.0: cabecera de la NC en un solo metodo (CabeceraNotaCredito.Completar)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 16:52:23 -05:00
GeorgeandClaude Sonnet 5.5 2fb7509fc2 Version 2.1.12.0: limpieza de codigo comentado y SetCreatePurchaseInvoices duplicado
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 16:49:04 -05:00
GeorgeandClaude Sonnet 5.5 20f3a59d35 Version 2.1.11.0: tabla de log configurable (Hana:logTable) y lotes completos en la NC de Recibo
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 16:42:12 -05:00
GeorgeandClaude Sonnet 5.5 48167d9138 Version 2.1.10.0: Version, AssemblyVersion y FileVersion alineadas (Swagger muestra AssemblyVersion)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-16 12:36:09 -05:00
22 changed files with 205 additions and 401 deletions

No files matched your search

+45 -9
View File
@@ -10,12 +10,18 @@ comprimido (versión antigua)**.
## Versión y control de versiones ## Versión y control de versiones
- **Versión actual: 2.1.9.0** (rama `limpieza-2.1.8.0`; `main` = 2.1.7.0) (`Net.Business.Services.csproj`: `Version`, - **Versión actual: 2.1.15.0** (rama única `main`; la rama `limpieza-2.1.8.0` se unió a `main` el 2026-10-20 por avance directo) (`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`, única forma de generar el zip; subir las TRES propiedades, el script falla si no coinciden).
- **Semver** (desde la 2.1.15): `MAJOR.MINOR.PATCH`; el cuarto número del `csproj` (.NET lo pide) va siempre en `0`.
`MAJOR` = cambia el contrato de la API y el WMS debe adaptarse (campo obligatorio nuevo, ruta distinta);
`MINOR` = funcionalidad nueva compatible (endpoint o campo opcional nuevo, aviso en `Message`);
`PATCH` = corrección o limpieza sin cambio de contrato. Etiquetas de git nuevas sin el cuarto número (`v2.1.15`;
las anteriores, como `v2.1.7.0`, no se tocan). Una versión de prueba para QA puede ir en `Version` como
`2.2.0-rc.1`, pero `AssemblyVersion`/`FileVersion` solo aceptan dígitos (`2.2.0.0`).
- **Git**: rama `main`, remoto `origin` = - **Git**: rama `main`, remoto `origin` =
`http://100.68.242.27:30000/george/itca_api_sap_wms.git` (HTTP sin cifrar, `https://unasgeorge.vaquita-hadar.ts.net/george/itca_api_sap_wms.git` (cambiado desde
red interna; confirmar que el repositorio sea **privado**). Etiqueta `http://100.68.242.27:30000/...`; confirmar que el repositorio sea **privado**). Etiqueta
`v2.1.7.0` = último estado antes de la limpieza de código. `v2.1.7.0` = último estado antes de la limpieza de código.
- `appsettings.json` y `appsettings.*.json` están en `.gitignore` (solo se - `appsettings.json` y `appsettings.*.json` están en `.gitignore` (solo se
versiona `appsettings.example.json`); las credenciales no están en el versiona `appsettings.example.json`); las credenciales no están en el
@@ -59,7 +65,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
@@ -124,6 +130,40 @@ transferencias siempre, sin depender de `Notificacion`. Detalle y tabla en
la línea de `StockTransfers`. Detalle, errores y diagnóstico en la línea de `StockTransfers`. Detalle, errores y diagnóstico en
`docs/TRANSFERENCIA_IMPORTACION.md`. Probado con datos reales (SQL Server) solo la consulta: entradas `docs/TRANSFERENCIA_IMPORTACION.md`. Probado con datos reales (SQL Server) solo la consulta: entradas
de 1 y 2 líneas, y una transferencia cancelada libera la entrada. de 1 y 2 líneas, y una transferencia cancelada libera la entrada.
- **v2.1.10.0 — solo versión** (sin cambios de comportamiento): `Version`, `AssemblyVersion` y
`FileVersion` del `csproj` quedan iguales (2.1.10.0). Swagger muestra `AssemblyVersion`
(`Startup.cs`: `OpenApiInfo.Version`); si Swagger muestra otro número el servidor corre
binarios viejos (pool sin reiniciar, carpeta equivocada o sitio desde `bin\Debug`).
- **v2.1.11.0 — tabla de log configurable y lotes de la NC de Recibo**: (1) la tabla de log sale de
`Hana:logTable` en `appsettings.json` (`LocalData.LogTable`; por defecto `ITCA_LOG_INTEGRACION` para
QA/Desarrollo, producción usa `ITCA_LOG_INTEGRACION_PROD`); el esquema sigue fijo en `ITC_WMS_SAP`.
Aplica a `B_Log` y `HelperLogServiceLayer`. (2) Recibo/Devolución: si el reparto de lotes (stock HANA o
factura) no cubre la cantidad de la línea, el resto va a un lote nuevo por fecha/hora (antes SAP
respondía "No es posible añadir una línea sin la selección completa de los números de serie/lote");
una línea sin equivalente en la factura ya no revienta con referencia nula. Sin probar contra SAP.
- **v2.1.12.0 — más limpieza** (sin cambios de comportamiento): se quitó `PurchaseOrdersRepository.SetCreatePurchaseInvoices`
(duplicado sin llamadores; queda el de `PurchaseInvoicesRepository`), el código comentado de `HRRepository`
(rama "Privado"), `Program.cs` (`CreateHostBuilder` viejo), `ConnectionServiceLayer` (líneas de header/éxito y un
`if` de `NoContent` vacío), `CreditNotesRepository`, `ReciboRepository`, `InventarioRepository`, `ConnectionSQL`, `DBParameter` y
`BE_BusinessPartners` (ya no queda código comentado). Sigue pendiente `PatchAsyncSBA` y la redundancia de serie/cabecera de la NC.
- **v2.1.13.0 — cabecera única de la NC** (sin cambios de comportamiento): la serie (`BC10` si el tipo del
documento origen es `03`, si no `FC10`), el correlativo (`HelperCorrelativo`) y los ~15 campos de la cabecera de
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.
- **v2.1.15.0 — se avisa al consumidor si falla la transferencia automática**: `TransferenciaAutomaticaNC.Crear` devuelve un
texto de advertencia (vacío si todo salió bien) cuando SAP rechaza una transferencia o no se puede agregar su referencia a
la NC. Los 3 llamadores (Picking DESPACHO y PICKING, Recibo Devolución) lo agregan al `Message` de la respuesta
(`"Se registro correctamente...!!! ADVERTENCIA: la nota de credito se creo, pero no se creo la transferencia A -> B: <SAP>."`).
La respuesta sigue siendo 200 con el DocEntry/DocNum de la NC (la NC existe en SAP; responder 500 haría que el WMS
reintente y duplique la NC). El consumidor debe leer el `Message`. El detalle del rechazo también queda en `ITCA_LOG_INTEGRACION`
(`ServiceLayer Error`). Sin probar contra SAP.
- 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.
@@ -176,8 +216,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
@@ -188,8 +226,6 @@ Picking, columna `HORA` en `B_Log`. Lo que **no** se trajo: `Chatbots` y
`ITC_WMS_SAP` (`ITCA_LOG_INTEGRACION`), no en `LocalData.Schema`; si la tabla `ITC_WMS_SAP` (`ITCA_LOG_INTEGRACION`), no en `LocalData.Schema`; si la tabla
está en otro esquema el `INSERT` falla. `B_Log.Add` no tiene `try/catch`, así está en otro esquema el `INSERT` falla. `B_Log.Add` no tiene `try/catch`, así
que un fallo del log puede devolver 500 tras haber creado el documento en SAP. que un fallo del log puede devolver 500 tras haber creado el documento en SAP.
- Si una transferencia automática de stock falla (`TransferenciaAutomaticaNC.Crear`),
el fallo se ignora (la NC ya está creada) y no se informa al consumidor.
## Despliegue ## Despliegue
@@ -108,7 +108,6 @@ namespace Net.Business.Entities.ServicesLayer
[DBParameter(SqlDbType.Int, 0, ActionType.Everything)] [DBParameter(SqlDbType.Int, 0, ActionType.Everything)]
public int U_BPP_BPTD { get; set; } public int U_BPP_BPTD { get; set; }
public int PriceListNum { get; set; } public int PriceListNum { get; set; }
//U_BPP_BPTD = 6,
} }
+1 -1
View File
@@ -7,7 +7,7 @@ namespace Net.Business.Services.Log
{ {
public static string Add(string Origen, byte[] Trama_Origen, byte[] Trama_Respuesta, string Proceso, string Nro_Orden, string DocEntry, string DocNumSAP, string DocEntrySAP) public static string Add(string Origen, byte[] Trama_Origen, byte[] Trama_Respuesta, string Proceso, string Nro_Orden, string DocEntry, string DocNumSAP, string DocEntrySAP)
{ {
string DB = "ITCA_LOG_INTEGRACION"; string DB = Net.Data.LocalData.LogTable;
string Code = System.Guid.NewGuid().ToString(); string Code = System.Guid.NewGuid().ToString();
string Fecha = DateTime.Now.ToString("yyyy-MM-dd"); string Fecha = DateTime.Now.ToString("yyyy-MM-dd");
HanaConnection hana = new HanaConnection(Net.Data.LocalData.ConnectionString); HanaConnection hana = new HanaConnection(Net.Data.LocalData.ConnectionString);
@@ -3,9 +3,9 @@
<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.9</Version> <Version>2.1.15.0</Version>
<AssemblyVersion>2.1.9.0</AssemblyVersion> <AssemblyVersion>2.1.15.0</AssemblyVersion>
<FileVersion>2.1.9.0</FileVersion> <FileVersion>2.1.15.0</FileVersion>
<UserSecretsId>24763424-3300-4c85-87e3-503f8fdebd14</UserSecretsId> <UserSecretsId>24763424-3300-4c85-87e3-503f8fdebd14</UserSecretsId>
<!-- Antes solo estaba condicionado a Debug|AnyCPU - un "dotnet publish -c Release" <!-- Antes solo estaba condicionado a Debug|AnyCPU - un "dotnet publish -c Release"
(lo normal para IIS) se quedaba sin este archivo, y Startup.cs revienta con (lo normal para IIS) se quedaba sin este archivo, y Startup.cs revienta con
-20
View File
@@ -23,25 +23,5 @@ namespace Net.Business.Services
{ {
webBuilder.UseStartup<Startup>(); webBuilder.UseStartup<Startup>();
}); });
//public static IHostBuilder CreateHostBuilder(string[] args) =>
// Host.CreateDefaultBuilder(args)
// .ConfigureWebHostDefaults(webBuilder =>
// {
// webBuilder.
// UseStartup<Startup>()
// .UseContentRoot(Directory.GetCurrentDirectory())
// .UseKestrel(options =>
// {
// options.Limits.MaxRequestBodySize = long.MaxValue;
// })
// .UseIISIntegration();
// //.UseKestrel(options =>
// //{
// // options.Limits.MaxRequestBodySize = long.MaxValue;
// //})
// });
} }
} }
@@ -40,7 +40,8 @@
"Hana": { "Hana": {
"server": "", "server": "",
"user": "", "user": "",
"password": "" "password": "",
"logTable": "ITCA_LOG_INTEGRACION"
}, },
"EmailSenderOptions": { "EmailSenderOptions": {
"Port": "", "Port": "",
-16
View File
@@ -23,14 +23,6 @@ namespace Net.Connection.Attributes
{ {
DBParameterMethod(dbType, 0, ParameterDirection.Input, "", actionType, Key); DBParameterMethod(dbType, 0, ParameterDirection.Input, "", actionType, Key);
} }
//public DBParameter(SqlDbType dbType, ActionType actionType)
//{
// DBParameterMethod(dbType, 0, ParameterDirection.Input, "", actionType, false);
//}
//public DBParameter(SqlDbType dbType, ActionType actionType, ParameterDirection direction)
//{
// DBParameterMethod(dbType, 0, direction, "", actionType, false);
//}
/// <summary> /// <summary>
/// Asignacion del Tipo de Dato /// Asignacion del Tipo de Dato
@@ -42,14 +34,6 @@ namespace Net.Connection.Attributes
{ {
DBParameterMethod(dbType, size, ParameterDirection.Input, "", actionType, false); DBParameterMethod(dbType, size, ParameterDirection.Input, "", actionType, false);
} }
//public DBParameter(SqlDbType dbType, int size, ParameterDirection direction, ActionType actionType)
//{
// DBParameterMethod(dbType, size, direction, "", actionType, false);
//}
//public DBParameter(SqlDbType dbType, int size, ParameterDirection direction, string name, ActionType actionType)
//{
// DBParameterMethod(dbType, size, direction, name, actionType, false);
//}
private void DBParameterMethod(SqlDbType dbType, int size, ParameterDirection direction, string name, ActionType actionType, Boolean Key) private void DBParameterMethod(SqlDbType dbType, int size, ParameterDirection direction, string name, ActionType actionType, Boolean Key)
{ {
@@ -172,7 +172,6 @@ namespace Net.Connection.ServiceLayer
// ASIGNAMOS A LA CABECERA DE LA PETICIÓN EL TOKEN JWT. // ASIGNAMOS A LA CABECERA DE LA PETICIÓN EL TOKEN JWT.
if (_responseLoginServiceLayer.ServicioActivo) if (_responseLoginServiceLayer.ServicioActivo)
//request_.Headers = new AuthenticationHeaderValue("B1SESSION", _responseLoginServiceLayer.SessionId);
request_.Headers.Add("Cookie", "B1SESSION=" + _responseLoginServiceLayer.SessionId); request_.Headers.Add("Cookie", "B1SESSION=" + _responseLoginServiceLayer.SessionId);
///////////////////////////////////////// /////////////////////////////////////////
// CONSTRUIMOS LA RESPUESTA (RESPONSE) // // CONSTRUIMOS LA RESPUESTA (RESPONSE) //
@@ -198,10 +197,6 @@ namespace Net.Connection.ServiceLayer
{ {
List<T> lista = new List<T>(); List<T> lista = new List<T>();
dynamic responseBody = JsonConvert.DeserializeObject<T>(responseText_); dynamic responseBody = JsonConvert.DeserializeObject<T>(responseText_);
//responseBody.Exito = true;
//var estado = (int)response_.StatusCode;
//responseBody.CodigoHttp = estado.ToString();
//responseBody.Mensaje = "Datos Correctos del SAP";
lista.Add(responseBody); lista.Add(responseBody);
return lista; return lista;
} }
@@ -271,8 +266,6 @@ namespace Net.Connection.ServiceLayer
// DEFINIMOS EL Accept, EN ESTE CASO ES "application/json" // DEFINIMOS EL Accept, EN ESTE CASO ES "application/json"
request_.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); request_.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json"));
//request_.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("odata=minimalmetadata"));
//request_.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("charset=utf-8"));
if (conCuerpo) if (conCuerpo)
request_.Content = new StringContent(JsonConvert.SerializeObject(obj), System.Text.Encoding.UTF8, "application/json"); request_.Content = new StringContent(JsonConvert.SerializeObject(obj), System.Text.Encoding.UTF8, "application/json");
@@ -373,138 +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_.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("odata=minimalmetadata"));
//request_.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("charset=utf-8"));
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.CodigoHttp = estado.ToString();
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.CodigoHttp = estado.ToString();
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.CodigoHttp = estado.ToString();
responseBody.Mensaje = "Error Services : " + _responseLoginServiceLayer.error.message.ToString();
LogErrorEnvio(Query, obj, responseBody.Mensaje);
return responseBody;
}
if (response_.StatusCode == System.Net.HttpStatusCode.NoContent)
{
//dynamic responseBody = JsonConvert.DeserializeObject<T>(responseText_);
////responseBody.Mensaje = "OK";
//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)
{ {
@@ -188,63 +188,6 @@ namespace Net.Connection
return queryResult; return queryResult;
} }
//private static SqlParameterCollection CreateParametros(string procedureName, string conexion)
//{
// SqlCommand cmd = new SqlCommand(procedureName.Trim());
// try
// {
// cmd.CommandType = CommandType.StoredProcedure;
// cmd.Connection = new SqlConnection(conexion);
// if (cmd.Connection.State == ConnectionState.Closed)
// {
// cmd.Connection.Open();
// }
// SqlCommandBuilder.DeriveParameters(cmd);
// for (int i = 0; i < cmd.Parameters.Count; i++)
// {
// string Nombre = cmd.Parameters[i].ParameterName;
// if (Nombre.Substring(0, 1) == "@")
// {
// Nombre = Nombre.Substring(1);
// }
// cmd.Parameters[i].ParameterName = Nombre.ToUpper();
// }
// return cmd.Parameters;
// }
// catch (Exception ex)
// {
// throw ex;
// }
// finally
// {
// cmd.Connection.Dispose();
// cmd.Dispose();
// }
//}
//private static IList<DbParametro> GetParametersSqlQuery(Object parameters)
//{
// IList<DbParametro> listaParametro = new List<DbParametro>();
// foreach (PropertyInfo prop in parameters.GetType().GetProperties())
// {
// dynamic valor = prop.GetValue(parameters, null);
// Type type = Nullable.GetUnderlyingType(prop.PropertyType);
// if (valor != null)
// {
// type = (type == null) ? prop.PropertyType : type;
// if (type != typeof(System.Collections.Generic.ICollection<>))
// {
// listaParametro.Add(new DbParametro() { nombre = prop.Name, valor = valor });
// }
// }
// }
// return listaParametro;
//}
private static IList<DbParametro> GetParametersSqlQueryAnotation<T>(T parameters, ActionType actionType) private static IList<DbParametro> GetParametersSqlQueryAnotation<T>(T parameters, ActionType actionType)
{ {
IList<DbParametro> listaParametro = new List<DbParametro>(); IList<DbParametro> listaParametro = new List<DbParametro>();
@@ -27,7 +27,7 @@ namespace Net.Data.HelperData.Log
{ {
hana.Open(); hana.Open();
HanaCommand hanaCommand = hana.CreateCommand(); HanaCommand hanaCommand = hana.CreateCommand();
hanaCommand.CommandText = "INSERT INTO \"ITC_WMS_SAP\".\"ITCA_LOG_INTEGRACION\" (\"CODIGO\", \"ORIGEN\", \"TRAMA_ORIGEN\", \"TRAMA_RESPUESTA\", \"PROCESO\", \"FECHA\", \"NRO_ORDEN\", \"DOCENTRY\", \"DOCNUMSAP\", \"DOCENTRYSAP\", \"HORA\") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; hanaCommand.CommandText = $"INSERT INTO \"ITC_WMS_SAP\".\"{LocalData.LogTable}\" (\"CODIGO\", \"ORIGEN\", \"TRAMA_ORIGEN\", \"TRAMA_RESPUESTA\", \"PROCESO\", \"FECHA\", \"NRO_ORDEN\", \"DOCENTRY\", \"DOCNUMSAP\", \"DOCENTRYSAP\", \"HORA\") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
hanaCommand.Parameters.Add("CODIGO", HanaDbType.VarChar).Value = Guid.NewGuid().ToString(); hanaCommand.Parameters.Add("CODIGO", HanaDbType.VarChar).Value = Guid.NewGuid().ToString();
hanaCommand.Parameters.Add("ORIGEN", HanaDbType.VarChar).Value = "ServiceLayer"; hanaCommand.Parameters.Add("ORIGEN", HanaDbType.VarChar).Value = "ServiceLayer";
hanaCommand.Parameters.Add("TRAMA_ORIGEN", HanaDbType.Blob).Value = trama; hanaCommand.Parameters.Add("TRAMA_ORIGEN", HanaDbType.Blob).Value = trama;
+7
View File
@@ -10,6 +10,10 @@ namespace Net.Data
// Esquema HANA: por defecto la compañia del Service Layer (SapServiceLayer:company) // Esquema HANA: por defecto la compañia del Service Layer (SapServiceLayer:company)
public static string Schema { get; private set; } public static string Schema { get; private set; }
// Tabla de log de integracion (esquema fijo ITC_WMS_SAP). QA/Desarrollo: ITCA_LOG_INTEGRACION;
// produccion: ITCA_LOG_INTEGRACION_PROD (Hana:logTable en appsettings.json)
public static string LogTable { get; private set; } = "ITCA_LOG_INTEGRACION";
// Unico punto de lectura de las credenciales HANA (seccion "Hana" de appsettings.json) // Unico punto de lectura de las credenciales HANA (seccion "Hana" de appsettings.json)
public static void Configure(IConfiguration configuration) public static void Configure(IConfiguration configuration)
{ {
@@ -32,6 +36,9 @@ namespace Net.Data
} }
Schema = schema; Schema = schema;
var logTable = configuration["Hana:logTable"];
if (!string.IsNullOrWhiteSpace(logTable)) LogTable = logTable.Trim();
} }
} }
} }
@@ -0,0 +1,53 @@
using Net.Business.Entities.ServicesLayer.CreditNotes;
using Net.Data.HelperData.Correlativo;
using System;
namespace Net.Data.ServicesLayer.CreditNotes
{
// Cabecera de la nota de credito (Picking DESPACHO, Picking PICKING estado 13 y Recibo Devolucion).
// Antes la serie, el correlativo y estos campos estaban copiados 3 veces.
public static class CabeceraNotaCredito
{
// Completa la cabecera de la NC con los datos del documento origen (la factura o, en Recibo sin factura,
// la solicitud de devolucion). Las lineas las arma cada llamador.
// tipoOrigen/serieOrigen/correlativoOrigen: U_SYP_MDTD / U_SYP_MDSD / U_SYP_MDCD del documento origen.
public static void Completar(BE_CreditNotesNotes nc, string docCurrency, string cardCode, string cardName, string paymentGroupCode,
string tipoOrigen, string serieOrigen, string correlativoOrigen, DateTime fechaOrigen, string motivo, string tipoMotivo)
{
// v2.1.7.0: la serie sale del TIPO del documento origen (03 = boleta -> BC10, otro -> FC10), no de su serie.
// En Recibo se comparaba la serie origen (p. ej. B005) con "03" y siempre salia FC10.
// Pendiente: validar con contabilidad la serie de boletas.
string serie = SerieNotaCredito(tipoOrigen);
// El correlativo sale de @SYP_NUMDOC (HelperCorrelativo)
string correlativo = HelperCorrelativo.Get(serie);
nc.DocCurrency = docCurrency;
nc.CardCode = cardCode;
nc.CardName = cardName;
nc.PaymentGroupCode = paymentGroupCode;
nc.DocDate = DateTime.Now;
nc.DocDueDate = DateTime.Now;
// Documento origen (el que se devuelve)
nc.U_SYP_MDTO = tipoOrigen;
nc.U_SYP_MDCO = correlativoOrigen;
nc.U_SYP_MDSO = serieOrigen;
nc.U_SYP_FECHAREF = fechaOrigen.ToString("yyyy-MM-dd");
nc.U_SYP_MOTNC = motivo;
nc.U_SYP_TPONC = tipoMotivo;
// La NC misma: tipo 07, con la serie y el correlativo elegidos arriba
nc.U_SYP_MDTD = "07";
nc.U_SYP_MDSD = serie;
nc.U_SYP_MDCD = correlativo;
nc.NumAtCard = "07-" + serie + "-" + correlativo;
}
public static string SerieNotaCredito(string tipoOrigen)
{
return tipoOrigen == "03" ? "BC10" : "FC10";
}
}
}
@@ -46,7 +46,6 @@ namespace Net.Data.ServicesLayer.CreditNotes
try try
{ {
//var cadena = "Drafts";
var cadena = "CreditNotes"; var cadena = "CreditNotes";
var data = await _connectServiceLayer.PostAsync<BE_BaseResponseSap>(cadena, entidad); var data = await _connectServiceLayer.PostAsync<BE_BaseResponseSap>(cadena, entidad);
@@ -73,15 +72,11 @@ namespace Net.Data.ServicesLayer.CreditNotes
try try
{ {
//var cadena = "Drafts";
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)
{ {
-22
View File
@@ -75,28 +75,6 @@ namespace Net.Data.ServicesLayer.HR
U_ARD_PHR_CNTDOC = entidad.Detalle.Count() U_ARD_PHR_CNTDOC = entidad.Detalle.Count()
}; };
//if(entidad.ModalidadTransporte == "Privado")
//{
// HojaRuta = new HojaRutaSAP()
// {
// ARD_PCK_HJRT_DETCollection = new List<HojaRutaDetailsEntities>(),
// U_ARD_PHR_PLACA = entidad.PlacaVehiculo,
// U_ARD_PHR_TERRI = entidad.Territorio,
// U_ARD_PHR_ZONDIS = entidad.Zona,
// U_ARD_PHR_FECCRE = entidad.FechaDespacho,
// U_ARD_PHR_MODTRANSP = entidad.ModalidadTransporte,
// U_ARD_PHR_NOMDESP = entidad.Despachador,
// U_ARD_PHR_NOMAGENTRANS = "ARDICORP S.A.C.",
// U_ARD_PHR_CODAGENTRANS = "P20136836545",
// U_ARD_PHR_RUCAGENTRANS = "20136836545",
// U_ARD_PHR_ESTDESP = "Embarcado"
// };
//}
//else
//{
//}
HojaRuta.ARD_PCK_HJRT_DETCollection = detalle; HojaRuta.ARD_PCK_HJRT_DETCollection = detalle;
var cadena = "ARD_PHR"; var cadena = "ARD_PHR";
@@ -1,6 +1,5 @@
using Net.Business.Entities; using Net.Business.Entities;
using Net.Business.Entities.ServicesLayer; using Net.Business.Entities.ServicesLayer;
using Net.Business.Entities.ServicesLayer.PurchaseInvoices;
using Net.Business.Entities.ServicesLayer.PurchaseOrders; using Net.Business.Entities.ServicesLayer.PurchaseOrders;
using Net.Connection; using Net.Connection;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -9,7 +8,6 @@ namespace Net.Data.ServicesLayer
{ {
public interface IPurchaseOrdersRepository public interface IPurchaseOrdersRepository
{ {
Task<BE_ResultadoTransaccion<BE_BaseResponseSap>> SetCreatePurchaseInvoices(BE_PurchaseInvoices entidad);
Task<BE_ResultadoTransaccion<BE_PurchaseOrders>> GetPurchaseInvoices(int DocEntry); Task<BE_ResultadoTransaccion<BE_PurchaseOrders>> GetPurchaseInvoices(int DocEntry);
} }
} }
@@ -1,7 +1,6 @@
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Net.Business.Entities; using Net.Business.Entities;
using Net.Business.Entities.ServicesLayer; using Net.Business.Entities.ServicesLayer;
using Net.Business.Entities.ServicesLayer.PurchaseInvoices;
using Net.Business.Entities.ServicesLayer.PurchaseOrders; using Net.Business.Entities.ServicesLayer.PurchaseOrders;
using Net.Connection.ServiceLayer; using Net.Connection.ServiceLayer;
using Net.CrossCotting; using Net.CrossCotting;
@@ -32,31 +31,6 @@ namespace Net.Data.ServicesLayer
_connectServiceLayer = new ConnectionServiceLayer(configuration, clientFactory); _connectServiceLayer = new ConnectionServiceLayer(configuration, clientFactory);
} }
public async Task<BE_ResultadoTransaccion<BE_BaseResponseSap>> SetCreatePurchaseInvoices(BE_PurchaseInvoices entidad)
{
_metodoName = regex.Match(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name).Groups[1].Value.ToString();
ResultadoTransaccion<BE_BaseResponseSap> resultadoTransaccion = new ResultadoTransaccion<BE_BaseResponseSap>(_metodoName, _aplicacionName);
try
{
var cadena = "PurchaseInvoices";
var data = await _connectServiceLayer.PostAsync<BE_BaseResponseSap>(cadena, entidad);
if (!data.Exito)
{
return resultadoTransaccion.GetInformacionResultado(-1, -1, data.Mensaje);
}
return resultadoTransaccion.GetInformacionResultado(0, "", "Se Listo correctamento...!!!", data);
}
catch (Exception ex)
{
return resultadoTransaccion.GetInformacionResultado(-1, -1, ex.Message.ToString());
}
}
public async Task<BE_ResultadoTransaccion<BE_PurchaseOrders>> GetPurchaseInvoices(int DocEntry) public async Task<BE_ResultadoTransaccion<BE_PurchaseOrders>> GetPurchaseInvoices(int DocEntry)
{ {
_metodoName = regex.Match(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name).Groups[1].Value.ToString(); _metodoName = regex.Match(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name).Groups[1].Value.ToString();
@@ -16,11 +16,14 @@ namespace Net.Data.ServicesLayer.StockTransfer
// Por cada almacen de origen (WarehouseCode) y almacen destino (U_ITC_ALMDES) distintos de las lineas de la NC // Por cada almacen de origen (WarehouseCode) y almacen destino (U_ITC_ALMDES) distintos de las lineas de la NC
// crea una transferencia de stock en SAP, con las cantidades y lotes de esas lineas y la NC como referencia. // crea una transferencia de stock en SAP, con las cantidades y lotes de esas lineas y la NC como referencia.
// Luego agrega a la NC una referencia por cada transferencia creada. // Luego agrega a la NC una referencia por cada transferencia creada.
// OJO: si SAP rechaza una transferencia se ignora (la NC ya esta creada) y no se informa al consumidor. // La NC ya esta creada, asi que un fallo aqui NO la deshace ni responde error (el consumidor reintentaria y
public static void Crear(BE_CreditNotesNotes creditNotesNotes, int docEntryNC, IHttpClientFactory clientFactory, IConfiguration configuration, CreditNotesRepository creditNotesRepository) // duplicaria la NC). Devuelve el texto de advertencia para el mensaje de la respuesta ("" si todo salio bien):
// transferencias que SAP rechazo y/o la referencia que no se pudo agregar a la NC.
public static string Crear(BE_CreditNotesNotes creditNotesNotes, int docEntryNC, IHttpClientFactory clientFactory, IConfiguration configuration, CreditNotesRepository creditNotesRepository)
{ {
List<string> ListaUnicaAlmacen = creditNotesNotes.DocumentLines.Select(x => x.WarehouseCode).Distinct().ToList(); List<string> ListaUnicaAlmacen = creditNotesNotes.DocumentLines.Select(x => x.WarehouseCode).Distinct().ToList();
List<int> ListDocEntry = new List<int>(); List<int> ListDocEntry = new List<int>();
List<string> Advertencias = new List<string>();
ListaUnicaAlmacen.ForEach(Almacen => ListaUnicaAlmacen.ForEach(Almacen =>
{ {
List<string> ListaUnicaAlmDestino = creditNotesNotes.DocumentLines.Where(Linea => Linea.WarehouseCode == Almacen).ToList().Select(x => x.U_ITC_ALMDES).Distinct().ToList().Where(x => !string.IsNullOrEmpty(x)).ToList(); List<string> ListaUnicaAlmDestino = creditNotesNotes.DocumentLines.Where(Linea => Linea.WarehouseCode == Almacen).ToList().Select(x => x.U_ITC_ALMDES).Distinct().ToList().Where(x => !string.IsNullOrEmpty(x)).ToList();
@@ -83,6 +86,10 @@ namespace Net.Data.ServicesLayer.StockTransfer
{ {
ListDocEntry.Add((int)ResultTransfer.Data.DocEntry); ListDocEntry.Add((int)ResultTransfer.Data.DocEntry);
} }
else
{
Advertencias.Add($"no se creo la transferencia {Almacen} -> {Destino}: {ResultTransfer.Message}");
}
} }
}); });
}); });
@@ -108,7 +115,15 @@ namespace Net.Data.ServicesLayer.StockTransfer
}; };
var Actualizacion = creditNotesRepository.UpdateCreateCreditNotes(credit, docEntryNC).Result; var Actualizacion = creditNotesRepository.UpdateCreateCreditNotes(credit, docEntryNC).Result;
if (Actualizacion.StatusCode == -1)
{
Advertencias.Add($"se crearon las transferencias {string.Join(", ", ListDocEntry)} pero no se pudo agregar su referencia a la NC: {Actualizacion.Message}");
} }
} }
return Advertencias.Count == 0
? ""
: " ADVERTENCIA: la nota de credito se creo, pero " + string.Join("; ", Advertencias) + ".";
}
} }
} }
@@ -497,7 +497,6 @@ namespace Net.Data.WMS.Inventario
}; };
return resultadoTransaccion.GetInformacionResultado(DocEntry, DocNum.ToString(), "Registrado Correctamente", entidad); return resultadoTransaccion.GetInformacionResultado(DocEntry, DocNum.ToString(), "Registrado Correctamente", entidad);
// return resultadoTransaccion.GetInformacionResultado((int)inventoryGenEntries_result.Data.DocEntry, inventoryGenEntries_result.Data.DocNum.ToString(), "Registrado Correctamente", entidad);
} }
//=== SALIDA DE MERCANCIA //=== SALIDA DE MERCANCIA
@@ -586,7 +585,6 @@ namespace Net.Data.WMS.Inventario
}; };
return resultadoTransaccion.GetInformacionResultado(DocEntry, DocNum.ToString(), "Registrado Correctamente", entidad); return resultadoTransaccion.GetInformacionResultado(DocEntry, DocNum.ToString(), "Registrado Correctamente", entidad);
//return resultadoTransaccion.GetInformacionResultado((int)inventoryGenExits_result.Data.DocEntry, inventoryGenExits_result.Data.DocNum.ToString(), "Registrado Correctamente", entidad);
} }
} }
return resultadoTransaccion.GetInformacionResultado(0, "", "Registrado Correctamente", entidad); return resultadoTransaccion.GetInformacionResultado(0, "", "Registrado Correctamente", entidad);
+10 -45
View File
@@ -15,7 +15,6 @@ using Net.Business.Entities.WMS.ProcesoPicking;
using Net.Connection; using Net.Connection;
using Net.Connection.ServiceLayer; using Net.Connection.ServiceLayer;
using Net.CrossCotting; using Net.CrossCotting;
using Net.Data.HelperData.Correlativo;
using Net.Data.HelperData.HelperProcesoPermitido; using Net.Data.HelperData.HelperProcesoPermitido;
using Net.Data.HelperData.Stock; using Net.Data.HelperData.Stock;
using Net.Data.ServicesLayer; using Net.Data.ServicesLayer;
@@ -171,26 +170,9 @@ namespace Net.Data.WMS.Picking
} }
} }
// Serie de la NC segun el TIPO de documento de la factura (U_SYP_MDTD): 03 = boleta -> BC10, otro -> FC10. // Cabecera de la NC (serie BC10/FC10 por tipo de la factura, correlativo, documento origen)
// El correlativo sale de @SYP_NUMDOC (HelperCorrelativo) CabeceraNotaCredito.Completar(creditNotesNotes, _invoices.DocCurrency, _invoices.CardCode, _invoices.CardName, _invoices.PaymentGroupCode,
string Serie = _invoices.U_SYP_MDTD == "03" ? "BC10" : "FC10"; _invoices.U_SYP_MDTD, _invoices.U_SYP_MDSD, _invoices.U_SYP_MDCD, _invoices.DocDate, DescripcionMotivo, MotivoNotaCredito);
string Correlativo = HelperCorrelativo.Get(Serie);
creditNotesNotes.DocCurrency = _invoices.DocCurrency;
creditNotesNotes.CardCode = _invoices.CardCode;
creditNotesNotes.CardName = _invoices.CardName;
creditNotesNotes.PaymentGroupCode = _invoices.PaymentGroupCode;
creditNotesNotes.DocDate = DateTime.Now;
creditNotesNotes.DocDueDate = DateTime.Now;
creditNotesNotes.U_SYP_MDTO = _invoices.U_SYP_MDTD;
creditNotesNotes.U_SYP_MDCO = _invoices.U_SYP_MDCD;
creditNotesNotes.U_SYP_MDSO = _invoices.U_SYP_MDSD;
creditNotesNotes.U_SYP_MOTNC = DescripcionMotivo;
creditNotesNotes.U_SYP_TPONC = MotivoNotaCredito;
creditNotesNotes.U_SYP_MDCD = Correlativo;
creditNotesNotes.U_SYP_MDSD = Serie;
creditNotesNotes.U_SYP_MDTD = "07";
creditNotesNotes.U_SYP_FECHAREF = _invoices.DocDate.ToString("yyyy-MM-dd");
creditNotesNotes.NumAtCard = ("07-" + Serie + "-" + Correlativo);
entidad.Detalle.ForEach(x => entidad.Detalle.ForEach(x =>
{ {
@@ -300,8 +282,8 @@ namespace Net.Data.WMS.Picking
else else
{ {
// Transferencias de stock automaticas por almacen destino (U_ITC_ALMDES) y su referencia en la NC // Transferencias de stock automaticas por almacen destino (U_ITC_ALMDES) y su referencia en la NC
TransferenciaAutomaticaNC.Crear(creditNotesNotes, (int)result_creditNotesRepository.Data.DocEntry, _clientFactory, _configuration, _creditNotesRepository); var advertenciaTransferencias = TransferenciaAutomaticaNC.Crear(creditNotesNotes, (int)result_creditNotesRepository.Data.DocEntry, _clientFactory, _configuration, _creditNotesRepository);
return resultadoTransaccion.GetInformacionResultado((int)result_creditNotesRepository.Data.DocEntry, result_creditNotesRepository.Data.DocNum.ToString(), "Se registro correctamente...!!!", entidad); return resultadoTransaccion.GetInformacionResultado((int)result_creditNotesRepository.Data.DocEntry, result_creditNotesRepository.Data.DocNum.ToString(), "Se registro correctamente...!!!" + advertenciaTransferencias, entidad);
} }
} }
else else
@@ -437,26 +419,9 @@ namespace Net.Data.WMS.Picking
} }
} }
// Serie segun el tipo de documento de la factura (03 = boleta -> BC10, otro -> FC10) // Cabecera de la NC (serie BC10/FC10 por tipo de la factura, correlativo, documento origen)
string Serie = _invoices.U_SYP_MDTD == "03" ? "BC10" : "FC10"; CabeceraNotaCredito.Completar(creditNotesNotes, _invoices.DocCurrency, _invoices.CardCode, _invoices.CardName, _invoices.PaymentGroupCode,
string Correlativo = HelperCorrelativo.Get(Serie); _invoices.U_SYP_MDTD, _invoices.U_SYP_MDSD, _invoices.U_SYP_MDCD, _invoices.DocDate, DescripcionMotivo, MotivoNotaCredito);
creditNotesNotes.DocCurrency = _invoices.DocCurrency;
creditNotesNotes.CardCode = _invoices.CardCode;
creditNotesNotes.CardName = _invoices.CardName;
creditNotesNotes.PaymentGroupCode = _invoices.PaymentGroupCode;
creditNotesNotes.DocDate = DateTime.Now;
creditNotesNotes.DocDueDate = DateTime.Now;
creditNotesNotes.U_SYP_MDTO = _invoices.U_SYP_MDTD;
creditNotesNotes.U_SYP_MDCO = _invoices.U_SYP_MDCD;
creditNotesNotes.U_SYP_MDSO = _invoices.U_SYP_MDSD;
creditNotesNotes.U_SYP_MOTNC = DescripcionMotivo;
creditNotesNotes.U_SYP_TPONC = MotivoNotaCredito;
creditNotesNotes.U_SYP_MDCD = Correlativo;
creditNotesNotes.U_SYP_MDSD = Serie;
creditNotesNotes.U_SYP_MDTD = "07";
creditNotesNotes.U_SYP_FECHAREF = _invoices.DocDate.ToString("yyyy-MM-dd");
creditNotesNotes.NumAtCard = ("07-" + Serie+ "-" + Correlativo);
entidad.Detalle.ForEach(x => entidad.Detalle.ForEach(x =>
{ {
@@ -499,8 +464,8 @@ namespace Net.Data.WMS.Picking
{ {
// Transferencias de stock automaticas por almacen destino (U_ITC_ALMDES) y su referencia en la NC // Transferencias de stock automaticas por almacen destino (U_ITC_ALMDES) y su referencia en la NC
TransferenciaAutomaticaNC.Crear(creditNotesNotes, (int)result_creditNotesRepository.Data.DocEntry, _clientFactory, _configuration, _creditNotesRepository); var advertenciaTransferencias = TransferenciaAutomaticaNC.Crear(creditNotesNotes, (int)result_creditNotesRepository.Data.DocEntry, _clientFactory, _configuration, _creditNotesRepository);
return resultadoTransaccion.GetInformacionResultado((int)result_creditNotesRepository.Data.DocEntry, result_creditNotesRepository.Data.DocNum.ToString(), "Se registro correctamente...!!!", entidad); return resultadoTransaccion.GetInformacionResultado((int)result_creditNotesRepository.Data.DocEntry, result_creditNotesRepository.Data.DocNum.ToString(), "Se registro correctamente...!!!" + advertenciaTransferencias, entidad);
} }
} }
else else
+17 -26
View File
@@ -13,7 +13,6 @@ using Net.Business.Entities.ServicesLayer.StockTransfer;
using Net.Business.Entities.WMS.Recibo; using Net.Business.Entities.WMS.Recibo;
using Net.Connection; using Net.Connection;
using Net.CrossCotting; using Net.CrossCotting;
using Net.Data.HelperData.Correlativo;
using Net.Data.HelperData.Stock; using Net.Data.HelperData.Stock;
using Net.Data.ServicesLayer; using Net.Data.ServicesLayer;
using Net.Data.ServicesLayer.CreditNotes; using Net.Data.ServicesLayer.CreditNotes;
@@ -402,27 +401,10 @@ namespace Net.Data.WMS.Recibo
} }
#endregion #endregion
// Serie segun el TIPO de documento origen (03 = boleta), no segun su serie // Cabecera de la NC: serie BC10/FC10 por el TIPO del documento origen (v2.1.7.0), correlativo y documento origen.
// El correlativo sale de @SYP_NUMDOC (HelperCorrelativo) // El origen es la factura de la solicitud de devolucion o, si no hay, la propia solicitud
string Serie = U_SYP_MDTO == "03" ? "BC10" : "FC10"; CabeceraNotaCredito.Completar(creditNotesNotes, returnRequest.DocCurrency, returnRequest.CardCode, returnRequest.CardName, returnRequest.PaymentGroupCode,
string Correlativo = HelperCorrelativo.Get(Serie); U_SYP_MDTO, U_SYP_MDSO, U_SYP_MDCO, DocDate, returnRequest.U_SYP_MOTNC, returnRequest.U_SYP_TPONC);
creditNotesNotes.DocCurrency = returnRequest.DocCurrency;
creditNotesNotes.CardCode = returnRequest.CardCode;
creditNotesNotes.CardName = returnRequest.CardName;
creditNotesNotes.PaymentGroupCode = returnRequest.PaymentGroupCode;
creditNotesNotes.DocDate = DateTime.Now;
creditNotesNotes.DocDueDate = DateTime.Now;
//creditNotesNotes.NumAtCard = entidad.DocImportacion;
creditNotesNotes.NumAtCard = ("07-" + Serie +"-" + Correlativo);
creditNotesNotes.U_SYP_MDTO = U_SYP_MDTO;
creditNotesNotes.U_SYP_MDCO = U_SYP_MDCO;
creditNotesNotes.U_SYP_MDSO = U_SYP_MDSO;
creditNotesNotes.U_SYP_MOTNC = returnRequest.U_SYP_MOTNC;
creditNotesNotes.U_SYP_TPONC = returnRequest.U_SYP_TPONC;
creditNotesNotes.U_SYP_MDCD = Correlativo;
creditNotesNotes.U_SYP_MDSD = Serie;
creditNotesNotes.U_SYP_MDTD = "07";
creditNotesNotes.U_SYP_FECHAREF = DocDate.ToString("yyyy-MM-dd");
entidad.Detalle.ForEach(x => entidad.Detalle.ForEach(x =>
{ {
var Item = returnRequest.DocumentLines.ToList().FirstOrDefault(t => t.LineNum == x.Item); var Item = returnRequest.DocumentLines.ToList().FirstOrDefault(t => t.LineNum == x.Item);
@@ -486,6 +468,11 @@ namespace Net.Data.WMS.Recibo
} }
} }
}); });
// Stock de HANA menor a la cantidad: el resto va a un lote nuevo para que los lotes sumen la cantidad de la linea
if (CantidadPendiente > 0)
{
line.BatchNumbers.Add(new BE_CreditNotesBatchNumbers() { BatchNumber = DateTime.Now.ToString("yyyyMMddhhmmss"), Quantity = (double)CantidadPendiente });
}
} }
else else
{ {
@@ -501,7 +488,7 @@ namespace Net.Data.WMS.Recibo
{ {
line.BatchNumbers.Clear(); line.BatchNumbers.Clear();
var ReferenciaFactura = returnRequest.DocumentLines.FirstOrDefault(t => t.LineNum == line.LineNum); var ReferenciaFactura = returnRequest.DocumentLines.FirstOrDefault(t => t.LineNum == line.LineNum);
var ListaStock = _Invoice.DocumentLines.FirstOrDefault(InvoiceLine => InvoiceLine.LineNum == ReferenciaFactura.BaseLine).BatchNumbers; var ListaStock = _Invoice.DocumentLines.FirstOrDefault(InvoiceLine => InvoiceLine.LineNum == ReferenciaFactura.BaseLine)?.BatchNumbers ?? new List<BE_InvoicesBatchNumbers>();
if (ListaStock.Count > 0) if (ListaStock.Count > 0)
{ {
var CantidadPendiente = line.Quantity; var CantidadPendiente = line.Quantity;
@@ -522,6 +509,11 @@ namespace Net.Data.WMS.Recibo
} }
} }
}); });
// Lotes de la factura menores a la cantidad: el resto va a un lote nuevo para que los lotes sumen la cantidad de la linea
if (CantidadPendiente > 0)
{
line.BatchNumbers.Add(new BE_CreditNotesBatchNumbers() { BatchNumber = DateTime.Now.ToString("yyyyMMddhhmmss"), Quantity = (double)CantidadPendiente });
}
} }
else else
{ {
@@ -539,11 +531,10 @@ namespace Net.Data.WMS.Recibo
{ {
// Transferencias de stock automaticas por almacen destino (U_ITC_ALMDES). OJO: en Recibo las lineas de la NC no llenan // Transferencias de stock automaticas por almacen destino (U_ITC_ALMDES). OJO: en Recibo las lineas de la NC no llenan
// U_ITC_ALMDES, por lo que hoy no se genera ninguna // U_ITC_ALMDES, por lo que hoy no se genera ninguna
TransferenciaAutomaticaNC.Crear(creditNotesNotes, (int)result_creditNotesRepository.Data.DocEntry, _clientFactory, _configuration, _creditNotesRepository); var advertenciaTransferencias = TransferenciaAutomaticaNC.Crear(creditNotesNotes, (int)result_creditNotesRepository.Data.DocEntry, _clientFactory, _configuration, _creditNotesRepository);
return resultadoTransaccion.GetInformacionResultado((int)result_creditNotesRepository.Data.DocEntry, result_creditNotesRepository.Data.DocNum.ToString(), "Se registro correctamente...!!!", entidad); return resultadoTransaccion.GetInformacionResultado((int)result_creditNotesRepository.Data.DocEntry, result_creditNotesRepository.Data.DocNum.ToString(), "Se registro correctamente...!!!" + advertenciaTransferencias, entidad);
} }
//return resultadoTransaccion.GetInformacionResultado((int)result_creditNotesRepository.Data.DocEntry, result_creditNotesRepository.Data.DocNum.ToString(), "Se registro correctamente...!!!", entidad);
} }
#endregion #endregion
+17 -12
View File
@@ -76,12 +76,15 @@ dotnet publish Net.Business.Services/Net.Business.Services.csproj -c Release -r
## Versión y script de publish ## Versión y script de publish
- La versión sale de `<Version>`/`<AssemblyVersion>`/`<FileVersion>` en - La versión sale de `<Version>`/`<AssemblyVersion>`/`<FileVersion>` en
`Net.Business.Services/Net.Business.Services.csproj` (actual: **2.1.9.0** en la rama de limpieza; `main` = 2.1.7.0); `Net.Business.Services/Net.Business.Services.csproj` (actual: **2.1.15.0** en `main`);
súbela en cada publish. súbela en cada publish. Se usa **semver** (`MAJOR.MINOR.PATCH`, cuarto número del
- `publicar.ps1` (solo Windows) genera `dist\ITCA_API_SAP_WMS_v<versión>.zip` sin `csproj` siempre `0`): `MAJOR` cambia el contrato de la API, `MINOR` agrega funcionalidad
`appsettings.json`. Desde macOS se hace el mismo `dotnet publish` a mano con compatible, `PATCH` corrige o limpia sin cambio de contrato (detalle en `CLAUDE.md`).
`-p:HanaDll=...`, se borran `appsettings.json`/`appsettings.Development.json` de - **Única forma de generar el zip**: `publicar.ps1` (Windows: `.\publicar.ps1`; macOS/Linux con PowerShell 7:
la salida y se comprime. `pwsh ./publicar.ps1`). Genera `dist/ITCA_API_SAP_WMS_v<MAJOR.MINOR.PATCH>.zip` sin `appsettings.json` ni
`appsettings.Development.json`; `-Destino <carpeta>` además copia el zip ahí (p. ej. `-Destino ~/Desktop`).
Verifica que `Version`, `AssemblyVersion` y `FileVersion` del `csproj` coincidan. En macOS/Linux define antes
`export HanaDll=<ruta>/Sap.Data.Hana.Core.v2.1.dll`. No hacer el `dotnet publish` a mano.
- Control de versiones: rama `main` y etiquetas `vX.Y.Z.W` por versión - Control de versiones: rama `main` y etiquetas `vX.Y.Z.W` por versión
publicada (ver `CLAUDE.md`). publicada (ver `CLAUDE.md`).
@@ -102,11 +105,11 @@ git switch -c recuperar-2.1.7.0 v2.1.7.0 # trabajar desde ahí en una rama nue
- Para **volver a publicar 2.1.7.0**: `git checkout v2.1.7.0` y generar el - 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 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. 2.1.7.0 en ese commit). Luego `git switch main` para regresar.
- Si la limpieza se une a `main` y hay que **deshacerla**, lo más seguro es - Para **deshacer** la limpieza ya unida a `main`: como fue avance directo (no hay commit de merge), usar
`git revert` del merge (no reescribe el historial). Evitar `git reset --hard` `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`. y `git push --force` sobre `main`.
- La rama `main` no se toca durante la limpieza; el trabajo va en - La limpieza se unió a `main` el 2026-10-20 (avance directo, sin commit de merge), por
`limpieza-2.1.8.0`. lo que ya no hay una rama aparte; la etiqueta `v2.1.7.0` sigue siendo el punto de retorno.
## Logs de integración ## Logs de integración
@@ -115,12 +118,14 @@ 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
API. API.
- Ambos usan el esquema fijo `ITC_WMS_SAP` (columnas `PROCESO`, `NRO_ORDEN` y - 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)`). `DOCENTRY` son `VARCHAR(100)`).
- Consulta de ejemplo: - Consulta de ejemplo:
`SELECT * FROM "ITC_WMS_SAP"."ITCA_LOG_INTEGRACION" WHERE "PROCESO" = 'ServiceLayer Error' ORDER BY "FECHA" DESC, "HORA" DESC` `SELECT * FROM "ITC_WMS_SAP"."ITCA_LOG_INTEGRACION" WHERE "PROCESO" = 'ServiceLayer Error' ORDER BY "FECHA" DESC, "HORA" DESC`
+25 -11
View File
@@ -1,21 +1,35 @@
# Genera el publish para IIS y lo comprime como dist\ITCA_API_SAP_WMS_v<version>.zip # UNICA forma de generar el zip del publish para IIS (Windows y macOS/Linux con PowerShell 7: pwsh ./publicar.ps1).
# La versión sale de <AssemblyVersion> en Net.Business.Services.csproj. # Genera dist/ITCA_API_SAP_WMS_v<MAJOR.MINOR.PATCH>.zip a partir de <AssemblyVersion> de Net.Business.Services.csproj.
# El zip NO incluye appsettings.json ni appsettings.Development.json (no pisar los del servidor). # El zip NO incluye appsettings.json ni appsettings.Development.json (no pisar los del servidor).
# Requiere Windows (el driver Sap.Data.Hana se referencia desde C:\Program Files\sap\hdbclient). # -Destino <carpeta>: ademas copia el zip a esa carpeta (p. ej. el escritorio).
# Driver Sap.Data.Hana: en Windows sale de C:\Program Files\sap\hdbclient; en macOS/Linux definir antes la variable de entorno
# HanaDll con la ruta de Sap.Data.Hana.Core.v2.1.dll (MSBuild la toma como propiedad, ver README).
param([string]$Destino)
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
Set-Location $PSScriptRoot Set-Location $PSScriptRoot
$csproj = 'Net.Business.Services\Net.Business.Services.csproj' $csproj = 'Net.Business.Services/Net.Business.Services.csproj'
$version = ([xml](Get-Content $csproj)).Project.PropertyGroup.AssemblyVersion | Where-Object { $_ } | Select-Object -First 1 $props = ([xml](Get-Content $csproj)).Project.PropertyGroup
if (-not $version) { throw "No se encontró AssemblyVersion en $csproj" } $version = $props.AssemblyVersion | Where-Object { $_ } | Select-Object -First 1
if (-not $version) { throw "No se encontro AssemblyVersion en $csproj" }
# Version, AssemblyVersion y FileVersion deben ser iguales (Swagger muestra AssemblyVersion; se desalinearon en la 2.1.12-2.1.15)
$otras = @($props.Version, $props.FileVersion) | Where-Object { $_ }
if ($otras | Where-Object { $_ -ne $version }) { throw "Version/AssemblyVersion/FileVersion del csproj no coinciden (AssemblyVersion=$version)" }
# Semver: el nombre lleva 3 numeros (2.1.15.0 -> 2.1.15); el cuarto del csproj siempre es 0
$version = ($version -replace '\.0$', '')
$out = "dist\ITCA_API_SAP_WMS_v$version" $out = "dist/ITCA_API_SAP_WMS_v$version"
$zip = "dist\ITCA_API_SAP_WMS_v$version.zip" $zip = "dist/ITCA_API_SAP_WMS_v$version.zip"
Remove-Item $out, $zip -Recurse -Force -ErrorAction SilentlyContinue Remove-Item $out, $zip -Recurse -Force -ErrorAction SilentlyContinue
dotnet publish $csproj -c Release -r win-x64 --self-contained false -o $out dotnet publish $csproj -c Release -r win-x64 --self-contained false -o $out
if ($LASTEXITCODE -ne 0) { throw 'dotnet publish falló' } if ($LASTEXITCODE -ne 0) { throw 'dotnet publish fallo' }
Remove-Item "$out\appsettings.json", "$out\appsettings.Development.json" -ErrorAction SilentlyContinue Remove-Item "$out/appsettings.json", "$out/appsettings.Development.json" -ErrorAction SilentlyContinue
Compress-Archive -Path "$out\*" -DestinationPath $zip Compress-Archive -Path "$out/*" -DestinationPath $zip
Write-Host "Generado: $zip" Write-Host "Generado: $zip"
if ($Destino) {
Copy-Item $zip $Destino -Force
Write-Host "Copiado a: $Destino"
}