Add Export Exoeditions
All checks were successful
CI/CD Pipeline / Build and Deploy with Docker Compose (push) Successful in 21m48s
All checks were successful
CI/CD Pipeline / Build and Deploy with Docker Compose (push) Successful in 21m48s
This commit is contained in:
parent
813bcc24b1
commit
c2bd8247a1
@ -23,5 +23,6 @@ namespace Core.Interfaces.Stock
|
|||||||
ELSExpeditionHeader header,
|
ELSExpeditionHeader header,
|
||||||
IEnumerable<ELSExpeditionDetail> details,
|
IEnumerable<ELSExpeditionDetail> details,
|
||||||
int formSeriesId);
|
int formSeriesId);
|
||||||
|
Task<byte[]> ExportFilteredToExcelAsync(ExpeditionSearchParams searchParams);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,8 @@ using Domain.Dtos.Stock;
|
|||||||
using Domain.Entities;
|
using Domain.Entities;
|
||||||
using Domain.Generics;
|
using Domain.Generics;
|
||||||
using Models.Interfaces;
|
using Models.Interfaces;
|
||||||
|
using System.Reflection;
|
||||||
|
using Transversal.Services;
|
||||||
|
|
||||||
namespace Core.Services.Stock
|
namespace Core.Services.Stock
|
||||||
{
|
{
|
||||||
@ -47,5 +49,53 @@ namespace Core.Services.Stock
|
|||||||
=> _repo.SearchAsync(expeditionNumber, status, issueDateFrom, issueDateTo, locationId, page, pageSize);
|
=> _repo.SearchAsync(expeditionNumber, status, issueDateFrom, issueDateTo, locationId, page, pageSize);
|
||||||
public Task<ExpeditionDto?> GetDtoByIdAsync(int id)
|
public Task<ExpeditionDto?> GetDtoByIdAsync(int id)
|
||||||
=> _repo.GetDtoByIdAsync(id);
|
=> _repo.GetDtoByIdAsync(id);
|
||||||
|
|
||||||
|
public async Task<byte[]> ExportFilteredToExcelAsync(ExpeditionSearchParams searchParams)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Realiza la búsqueda de clientes con los parámetros proporcionados
|
||||||
|
var searchResult = await SearchAsync(
|
||||||
|
searchParams.Number,
|
||||||
|
searchParams.Status,
|
||||||
|
searchParams.From,
|
||||||
|
searchParams.To,
|
||||||
|
searchParams.LocationId,
|
||||||
|
searchParams.Page,
|
||||||
|
searchParams.PageSize
|
||||||
|
);
|
||||||
|
// Verifica que se hayan encontrado resultados
|
||||||
|
if (searchResult?.Items is null || !searchResult.Items.Any())
|
||||||
|
{
|
||||||
|
throw new Exception("No se encontraron clientes para exportar.");
|
||||||
|
}
|
||||||
|
// Llamamos a un método que exporta los datos a Excel
|
||||||
|
var stream = new XLSXExportBase();
|
||||||
|
// Convertimos los resultados de la búsqueda a un formato adecuado para el exportador
|
||||||
|
var items = searchResult.Items.Select(c => new
|
||||||
|
{
|
||||||
|
c.Expeditionnumber,
|
||||||
|
Issuedate = c.Issuedate.ToString("yyyy-MM-dd"), // ← string
|
||||||
|
Createdat = c.Createdat.ToString("yyyy-MM-dd HH:mm"), // ← string
|
||||||
|
|
||||||
|
c.Status,
|
||||||
|
c.LocationId,
|
||||||
|
c.ExternalReference,
|
||||||
|
c.TicketId,
|
||||||
|
c.ExtrainfoJson,
|
||||||
|
c.Observations,
|
||||||
|
c.TotalItems
|
||||||
|
}).ToList();
|
||||||
|
// Genera el archivo Excel
|
||||||
|
var excelFile = stream.ExportExcel(items);
|
||||||
|
// Devuelve el archivo Excel como un array de bytes
|
||||||
|
return excelFile;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod";
|
||||||
|
throw new Exception($"{ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
Domain/Generics/ExpeditionSearchParams.cs
Normal file
12
Domain/Generics/ExpeditionSearchParams.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace Domain.Generics
|
||||||
|
{
|
||||||
|
public class ExpeditionSearchParams:PagedRequest
|
||||||
|
{
|
||||||
|
public string? Number { get; set; }
|
||||||
|
public string? Status { get; set; }
|
||||||
|
public DateTime? From { get; set; }
|
||||||
|
public DateTime? To { get; set; }
|
||||||
|
public int? LocationId { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,7 +10,7 @@ namespace Transversal.Services
|
|||||||
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
|
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
|
||||||
using (var package = new ExcelPackage())
|
using (var package = new ExcelPackage())
|
||||||
{
|
{
|
||||||
var worksheet = package.Workbook.Worksheets.Add("Datos");
|
var worksheet = package.Workbook.Worksheets.Add("datos");
|
||||||
|
|
||||||
// Obtener las propiedades de T
|
// Obtener las propiedades de T
|
||||||
var propiedades = typeof(T).GetProperties();
|
var propiedades = typeof(T).GetProperties();
|
||||||
|
|||||||
@ -81,8 +81,6 @@ namespace phronCare.API.Controllers.Stock
|
|||||||
if (dto is null) return NotFound($"No se encontró expedición {expeditionNumber}.");
|
if (dto is null) return NotFound($"No se encontró expedición {expeditionNumber}.");
|
||||||
return Ok(dto);
|
return Ok(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#region Endpoint de emision de expedicion (encabezado + detalles)
|
#region Endpoint de emision de expedicion (encabezado + detalles)
|
||||||
[HttpPost("createfull")]
|
[HttpPost("createfull")]
|
||||||
public async Task<IActionResult> CreateFullExpedition([FromBody] CreateFullExpeditionRequest request)
|
public async Task<IActionResult> CreateFullExpedition([FromBody] CreateFullExpeditionRequest request)
|
||||||
@ -111,7 +109,6 @@ namespace phronCare.API.Controllers.Stock
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Genera y devuelve un archivo PDF correspondiente al presupuesto especificado por su ID.
|
/// Genera y devuelve un archivo PDF correspondiente al presupuesto especificado por su ID.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -131,6 +128,22 @@ namespace phronCare.API.Controllers.Stock
|
|||||||
|
|
||||||
return File(pdfBytes, "application/pdf", $"Expedicion_{expedition.Expeditionnumber}.pdf");
|
return File(pdfBytes, "application/pdf", $"Expedicion_{expedition.Expeditionnumber}.pdf");
|
||||||
}
|
}
|
||||||
|
[HttpPost("exportfiltered")]
|
||||||
|
|
||||||
|
public async Task<IActionResult> ExportFiltered([FromBody] ExpeditionSearchParams searchParams)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var file = await _expeditionService.ExportFilteredToExcelAsync(searchParams);
|
||||||
|
return File(file,
|
||||||
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||||
|
"Expediciones.xlsx");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return BadRequest(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public class CreateFullExpeditionRequest
|
public class CreateFullExpeditionRequest
|
||||||
{
|
{
|
||||||
|
|||||||
@ -694,6 +694,22 @@
|
|||||||
],
|
],
|
||||||
"ReturnTypes": []
|
"ReturnTypes": []
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ContainingType": "phronCare.API.Controllers.Stock.ExpeditionController",
|
||||||
|
"Method": "ExportFiltered",
|
||||||
|
"RelativePath": "api/Expedition/exportfiltered",
|
||||||
|
"HttpMethod": "POST",
|
||||||
|
"IsController": true,
|
||||||
|
"Order": 0,
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "searchParams",
|
||||||
|
"Type": "Domain.Generics.ExpeditionSearchParams",
|
||||||
|
"IsRequired": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnTypes": []
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ContainingType": "phronCare.API.Controllers.Stock.ExpeditionController",
|
"ContainingType": "phronCare.API.Controllers.Stock.ExpeditionController",
|
||||||
"Method": "Search",
|
"Method": "Search",
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
@using System.Text.Json
|
@using System.Text.Json
|
||||||
|
|
||||||
@using phronCare.UIBlazor.Services.Stock.Expeditions
|
@using phronCare.UIBlazor.Services.Stock.Expeditions
|
||||||
@inject ExpeditionService expeditionService
|
@inject IExpeditionService expeditionService
|
||||||
|
|
||||||
@inject NavigationManager Nav
|
@inject NavigationManager Nav
|
||||||
@inject IToastService Toast
|
@inject IToastService Toast
|
||||||
@ -71,14 +71,13 @@
|
|||||||
</button>
|
</button>
|
||||||
<button class="btn btn-success rounded-pill" @onclick="Create">
|
<button class="btn btn-success rounded-pill" @onclick="Create">
|
||||||
<i class="fas fa-plus me-1"></i> Nuevo
|
<i class="fas fa-plus me-1"></i> Nuevo
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-success rounded-pill" @onclick="ExportarExcel">
|
||||||
|
<i class="fas fa-file-excel me-1"></i> Excel
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-secondary rounded-pill" @onclick="Clear">
|
<button class="btn btn-secondary rounded-pill" @onclick="Clear">
|
||||||
<i class="fas fa-arrow-left me-1"></i> Volver
|
<i class="fas fa-arrow-left me-1"></i> Volver
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-success rounded-pill" @onclick="ExportCurrent">
|
|
||||||
<i class="fas fa-file-excel me-1"></i> Excel
|
|
||||||
</button>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</EditForm>
|
</EditForm>
|
||||||
</div>
|
</div>
|
||||||
@ -167,6 +166,8 @@
|
|||||||
Loading="@loadingDetail" />
|
Loading="@loadingDetail" />
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
private LSProductSearchParams SearchParams = new() { Page = 1, PageSize = 10 };
|
||||||
|
|
||||||
private Filters filters = new();
|
private Filters filters = new();
|
||||||
private PagedResult<ExpeditionDto>? result;
|
private PagedResult<ExpeditionDto>? result;
|
||||||
private int page = 1;
|
private int page = 1;
|
||||||
@ -225,11 +226,26 @@
|
|||||||
Nav.NavigateTo("/expeditions/create");
|
Nav.NavigateTo("/expeditions/create");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ExportCurrent()
|
private async Task ExportarExcel()
|
||||||
{
|
{
|
||||||
// Opcional: /api/lsm/expeditions/export?{filtros}
|
SearchParams.Page = 1;
|
||||||
Toast.ShowInfo("Export en preparación.");
|
SearchParams.PageSize = int.MaxValue; // Exportar todos los resultados
|
||||||
}
|
try
|
||||||
|
{
|
||||||
|
await expeditionService.ExportFilteredAsync(SearchParams);
|
||||||
|
Toast.ShowSuccess("Exportación completada.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Toast.ShowError($"Error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// private async Task ExportCurrent()
|
||||||
|
// {
|
||||||
|
// // Opcional: /api/lsm/expeditions/export?{filtros}
|
||||||
|
// Toast.ShowInfo("Export en preparación.");
|
||||||
|
// }
|
||||||
|
|
||||||
private async Task ViewPdf(int id, string number)
|
private async Task ViewPdf(int id, string number)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -53,6 +53,7 @@ static void InjectDependencies(WebAssemblyHostBuilder builder)
|
|||||||
builder.Services.AddScoped<IStockLookUpService, StockLookUpService>();
|
builder.Services.AddScoped<IStockLookUpService, StockLookUpService>();
|
||||||
builder.Services.AddScoped<IExchangeRateService, ExchangeRateService>();
|
builder.Services.AddScoped<IExchangeRateService, ExchangeRateService>();
|
||||||
builder.Services.AddScoped<IStockScanService, StockScanService>();
|
builder.Services.AddScoped<IStockScanService, StockScanService>();
|
||||||
|
builder.Services.AddScoped<IExpeditionService, ExpeditionService>();
|
||||||
|
|
||||||
builder.Services.AddScoped<ExchangeRateService>();
|
builder.Services.AddScoped<ExchangeRateService>();
|
||||||
builder.Services.AddScoped<QuoteService>();
|
builder.Services.AddScoped<QuoteService>();
|
||||||
|
|||||||
@ -4,6 +4,9 @@ using Domain.Entities;
|
|||||||
using Domain.Generics;
|
using Domain.Generics;
|
||||||
using Microsoft.JSInterop;
|
using Microsoft.JSInterop;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace phronCare.UIBlazor.Services.Stock.Expeditions
|
namespace phronCare.UIBlazor.Services.Stock.Expeditions
|
||||||
{
|
{
|
||||||
@ -53,6 +56,7 @@ namespace phronCare.UIBlazor.Services.Stock.Expeditions
|
|||||||
var result = await _http.GetFromJsonAsync<PagedResult<ExpeditionDto>>(url);
|
var result = await _http.GetFromJsonAsync<PagedResult<ExpeditionDto>>(url);
|
||||||
return result!;
|
return result!;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Obtiene un presupuesto por QuoteNumber.
|
/// Obtiene un presupuesto por QuoteNumber.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -148,10 +152,37 @@ namespace phronCare.UIBlazor.Services.Stock.Expeditions
|
|||||||
throw new Exception($"ExportPdfAsync: {message}", ex);
|
throw new Exception($"ExportPdfAsync: {message}", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task ExportFilteredAsync(LSProductSearchParams searchParams)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var content = new StringContent(JsonSerializer.Serialize(searchParams), Encoding.UTF8, "application/json");
|
||||||
|
var response = await _http.PostAsync("api/Expedition/exportfiltered", content);
|
||||||
|
|
||||||
|
//response.EnsureSuccessStatusCode();
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
var errorContent = await response.Content.ReadAsStringAsync();
|
||||||
|
throw new Exception(errorContent);
|
||||||
|
}
|
||||||
|
var bytes = await response.Content.ReadAsByteArrayAsync();
|
||||||
|
var base64 = Convert.ToBase64String(bytes);
|
||||||
|
var timestamp = DateTime.Now.ToString("yyyyMMddHHmm");
|
||||||
|
var fileName = $"{timestamp}_expeditions.xlsx";
|
||||||
|
await _js.InvokeVoidAsync("saveAsFile", fileName, base64);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod";
|
||||||
|
var message = ex.Message ?? "No message provided";
|
||||||
|
throw new Exception($"{message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contrato de request simétrico a CreateFullQuoteRequest.
|
/// Contrato de request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class CreateFullExpeditionRequest
|
public class CreateFullExpeditionRequest
|
||||||
{
|
{
|
||||||
@ -160,7 +191,7 @@ namespace phronCare.UIBlazor.Services.Stock.Expeditions
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resultado del create/issue simétrico a CreateQuoteResult.
|
/// Resultado del create/issue.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class CreateExpeditionResult
|
public class CreateExpeditionResult
|
||||||
{
|
{
|
||||||
@ -170,6 +201,4 @@ namespace phronCare.UIBlazor.Services.Stock.Expeditions
|
|||||||
public string ErrorMessage { get; set; } = string.Empty;
|
public string ErrorMessage { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Ajustar namespace real si es distinto
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -10,5 +10,6 @@ namespace phronCare.UIBlazor.Services.Stock.Expeditions
|
|||||||
Task<ExpeditionDto?> GetDtoByIdAsync(int id);
|
Task<ExpeditionDto?> GetDtoByIdAsync(int id);
|
||||||
Task<QuoteDto?> GetQuoteByNumberAsync(string quoteNumber);
|
Task<QuoteDto?> GetQuoteByNumberAsync(string quoteNumber);
|
||||||
Task<PagedResult<ExpeditionDto>> SearchAsync(string? expeditionNumber = null, string? status = null, DateTime? issueDateFrom = null, DateTime? issueDateTo = null, int? locationId = null, int page = 1, int pageSize = 10);
|
Task<PagedResult<ExpeditionDto>> SearchAsync(string? expeditionNumber = null, string? status = null, DateTime? issueDateFrom = null, DateTime? issueDateTo = null, int? locationId = null, int page = 1, int pageSize = 10);
|
||||||
|
Task ExportFilteredAsync(LSProductSearchParams searchParams);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user