Add Export QUOTES & refactoring
All checks were successful
CI/CD Pipeline / Build and Deploy with Docker Compose (push) Successful in 6m2s
All checks were successful
CI/CD Pipeline / Build and Deploy with Docker Compose (push) Successful in 6m2s
This commit is contained in:
parent
4c99757fb4
commit
0361d4c978
@ -11,7 +11,7 @@ namespace Core.Interfaces
|
|||||||
Task<QuoteDto?> GetDtoByQuoteNumberAsync(string quoteNumber);
|
Task<QuoteDto?> GetDtoByQuoteNumberAsync(string quoteNumber);
|
||||||
#endregion
|
#endregion
|
||||||
#region Exportación
|
#region Exportación
|
||||||
//Task<byte[]> ExportFilteredQuotesToExcelAsync(QuoteSearchParams searchParams);
|
Task<byte[]> ExportFilteredToExcelAsync(QuoteSearchParams searchParams);
|
||||||
#endregion
|
#endregion
|
||||||
#region Guardado completo de presupuesto (encabezado + detalles + roles + ajustes + impuestos)
|
#region Guardado completo de presupuesto (encabezado + detalles + roles + ajustes + impuestos)
|
||||||
Task<(int Id, string Quotenumber)> CreateFullQuoteAsync(EQuoteHeader quote, int formSeriesId);
|
Task<(int Id, string Quotenumber)> CreateFullQuoteAsync(EQuoteHeader quote, int formSeriesId);
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
using Domain.Dtos;
|
using Core.Interfaces;
|
||||||
using Domain.Constants;
|
using Domain.Constants;
|
||||||
|
using Domain.Dtos;
|
||||||
using Domain.Entities;
|
using Domain.Entities;
|
||||||
using Domain.Generics;
|
using Domain.Generics;
|
||||||
using Models.Interfaces;
|
using Models.Interfaces;
|
||||||
using Core.Interfaces;
|
using System.Drawing.Printing;
|
||||||
|
using System.Reflection;
|
||||||
|
using Transversal.Services;
|
||||||
|
|
||||||
namespace Core.Services
|
namespace Core.Services
|
||||||
{
|
{
|
||||||
@ -38,6 +41,61 @@ namespace Core.Services
|
|||||||
{
|
{
|
||||||
return await _quoteRepository.GetDtoByIdAsync(id);
|
return await _quoteRepository.GetDtoByIdAsync(id);
|
||||||
}
|
}
|
||||||
|
public async Task<byte[]> ExportFilteredToExcelAsync(QuoteSearchParams searchParams)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Realiza la búsqueda de clientes con los parámetros proporcionados
|
||||||
|
var searchResult = await _quoteRepository.SearchAsync(
|
||||||
|
searchParams.CustomerId,
|
||||||
|
searchParams.CustomerText,
|
||||||
|
searchParams.QuoteNumber,
|
||||||
|
searchParams.ProfessionalId,
|
||||||
|
searchParams.ProfessionalText,
|
||||||
|
searchParams.InstitutionId,
|
||||||
|
searchParams.InstitutionText,
|
||||||
|
searchParams.PatientId,
|
||||||
|
searchParams.PatientText,
|
||||||
|
searchParams.IssueDateFrom,
|
||||||
|
searchParams.IssueDateTo,
|
||||||
|
searchParams.Status,
|
||||||
|
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.Quotenumber,
|
||||||
|
Issuedate = c.IssueDate.ToString("dd/MM/yyyy"), // ← string
|
||||||
|
EstimatedDate = c.EstimatedDate?.ToString("dd/MM/yyyy HH:mm"), // ← string
|
||||||
|
c.Status,
|
||||||
|
c.CustomerName,
|
||||||
|
c.ProfessionalName,
|
||||||
|
c.InstitutionName,
|
||||||
|
c.PatientName,
|
||||||
|
c.BusinessUnitName,
|
||||||
|
c.SalespersonName,
|
||||||
|
c.Observations,
|
||||||
|
c.Total
|
||||||
|
}).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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//public async Task<QuoteDto?> GetDtoByQuoteNumberAsync(string quoteNumber)
|
//public async Task<QuoteDto?> GetDtoByQuoteNumberAsync(string quoteNumber)
|
||||||
//{
|
//{
|
||||||
|
|||||||
@ -55,7 +55,7 @@ namespace Core.Services.Stock
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Realiza la búsqueda de clientes con los parámetros proporcionados
|
// Realiza la búsqueda de clientes con los parámetros proporcionados
|
||||||
var searchResult = await SearchAsync(
|
var searchResult = await _repo.SearchAsync(
|
||||||
searchParams.Number,
|
searchParams.Number,
|
||||||
searchParams.Status,
|
searchParams.Status,
|
||||||
searchParams.From,
|
searchParams.From,
|
||||||
|
|||||||
@ -135,6 +135,21 @@ namespace phronCare.API.Controllers.Sales
|
|||||||
|
|
||||||
return File(pdfBytes, "application/pdf", $"Presupuesto_{quote.Quotenumber}.pdf");
|
return File(pdfBytes, "application/pdf", $"Presupuesto_{quote.Quotenumber}.pdf");
|
||||||
}
|
}
|
||||||
|
[HttpPost("exportfiltered")]
|
||||||
|
public async Task<IActionResult> ExportFiltered([FromBody] QuoteSearchParams searchParams)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var file = await _quoteService.ExportFilteredToExcelAsync(searchParams);
|
||||||
|
return File(file,
|
||||||
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||||
|
"Expediciones.xlsx");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return BadRequest(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#region Endpoint de emision de presupuesto (encabezado + detalles + roles + ajustes + impuestos)
|
#region Endpoint de emision de presupuesto (encabezado + detalles + roles + ajustes + impuestos)
|
||||||
[HttpPost("createfull")]
|
[HttpPost("createfull")]
|
||||||
|
|||||||
@ -128,8 +128,8 @@ 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")]
|
|
||||||
|
|
||||||
|
[HttpPost("exportfiltered")]
|
||||||
public async Task<IActionResult> ExportFiltered([FromBody] ExpeditionSearchParams searchParams)
|
public async Task<IActionResult> ExportFiltered([FromBody] ExpeditionSearchParams searchParams)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@ -2383,6 +2383,22 @@
|
|||||||
],
|
],
|
||||||
"ReturnTypes": []
|
"ReturnTypes": []
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ContainingType": "phronCare.API.Controllers.Sales.QuoteController",
|
||||||
|
"Method": "ExportFiltered",
|
||||||
|
"RelativePath": "api/Quote/exportfiltered",
|
||||||
|
"HttpMethod": "POST",
|
||||||
|
"IsController": true,
|
||||||
|
"Order": 0,
|
||||||
|
"Parameters": [
|
||||||
|
{
|
||||||
|
"Name": "searchParams",
|
||||||
|
"Type": "Domain.Generics.QuoteSearchParams",
|
||||||
|
"IsRequired": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ReturnTypes": []
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ContainingType": "phronCare.API.Controllers.Sales.QuoteController",
|
"ContainingType": "phronCare.API.Controllers.Sales.QuoteController",
|
||||||
"Method": "Search",
|
"Method": "Search",
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
@page "/quotes"
|
@page "/quotes"
|
||||||
|
|
||||||
@using Domain.Dtos
|
@using Domain.Dtos
|
||||||
@using Domain.Generics
|
@using Domain.Generics
|
||||||
@using phronCare.UIBlazor.Services.Sales.Quotes
|
@using phronCare.UIBlazor.Services.Sales.Quotes
|
||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
@inject QuoteService quoteService
|
@inject IQuoteService quoteService
|
||||||
@inject IToastService toastService
|
@inject IToastService toastService
|
||||||
|
|
||||||
<div class="card shadow-sm mb-3" style="zoom: 0.8;">
|
<div class="card shadow-sm mb-3" style="zoom: 0.8;">
|
||||||
@ -14,7 +13,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- BODY -->
|
<!-- BODY -->
|
||||||
<div class="card-body">
|
<div class="card-body pt-2 pb-0">
|
||||||
<!-- FILTROS -->
|
<!-- FILTROS -->
|
||||||
<div class="mb-3 row g-2 align-items-end">
|
<div class="mb-3 row g-2 align-items-end">
|
||||||
<div class="col-sm">
|
<div class="col-sm">
|
||||||
@ -58,21 +57,24 @@
|
|||||||
<InputDate id="dateto" @bind-Value="Filters.IssueDateTo" class="form-control form-control-sm" />
|
<InputDate id="dateto" @bind-Value="Filters.IssueDateTo" class="form-control form-control-sm" />
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-end gap-2 mt-3">
|
<div class="d-flex justify-content-end gap-2 mt-3">
|
||||||
<button class="btn btn-primary btn-sm rounded-pill" @onclick="Search">
|
<button class="btn btn-primary rounded-pill" @onclick="Search">
|
||||||
<i class="fas fa-binoculars me-1"></i> Buscar
|
<i class="fas fa-binoculars me-1"></i> Buscar
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-secondary btn-sm rounded-pill ms-1" @onclick="OnClear">
|
<button class="btn btn-secondary rounded-pill ms-1" @onclick="OnClear">
|
||||||
<i class="fas fa-eraser me-1"></i> Limpiar
|
<i class="fas fa-eraser me-1"></i> Limpiar
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-success btn-sm 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>
|
||||||
|
<button class="btn btn-success rounded-pill" @onclick="ExportarExcel">
|
||||||
|
<i class="fas fa-file-excel me-1"></i> Excel
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card shadow-sm" style="zoom:0.8;">
|
<div class="card shadow-sm" style="zoom:0.8;">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive" style="zoom:0.8;">
|
||||||
<!-- TABLA DE RESULTADOS -->
|
<!-- TABLA DE RESULTADOS -->
|
||||||
<table class="table table-sm align-middle mb-0">
|
<table class="table table-sm align-middle mb-0">
|
||||||
<thead class="table-light">
|
<thead class="table-light">
|
||||||
@ -139,8 +141,8 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<!-- Paginación debajo -->
|
<!-- Paginación debajo -->
|
||||||
<div class="d-flex justify-content-between align-items-center px-3 py-2 border-top">
|
<div class="d-flex justify-content-center align-items-center px-3 py-2 border-top">
|
||||||
<div class="btn-group">
|
<div class="btn-group justify-content-center">
|
||||||
<button class="btn btn-outline-secondary btn-sm rounded-pill" @onclick="PrimeraPagina" disabled="@(Filters.Page == 1)">Primera</button>
|
<button class="btn btn-outline-secondary btn-sm rounded-pill" @onclick="PrimeraPagina" disabled="@(Filters.Page == 1)">Primera</button>
|
||||||
<button class="btn btn-outline-secondary btn-sm rounded-pill" @onclick="AnteriorPagina" disabled="@(!PuedeRetroceder)">Anterior</button>
|
<button class="btn btn-outline-secondary btn-sm rounded-pill" @onclick="AnteriorPagina" disabled="@(!PuedeRetroceder)">Anterior</button>
|
||||||
<span class="mx-2">
|
<span class="mx-2">
|
||||||
@ -285,9 +287,8 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private QuoteSearchParams Filters = new() { PageSize = 9 };
|
private QuoteSearchParams Filters = new() { PageSize = 10 };
|
||||||
private PagedResult<QuoteDto>? PagedQuotes;
|
private PagedResult<QuoteDto>? PagedQuotes;
|
||||||
private QuoteDto? SelectedQuote { get; set; }
|
private QuoteDto? SelectedQuote { get; set; }
|
||||||
private bool IsLoading;
|
private bool IsLoading;
|
||||||
@ -399,5 +400,18 @@
|
|||||||
{
|
{
|
||||||
Navigation.NavigateTo($"/quotes/authorize/{id}");
|
Navigation.NavigateTo($"/quotes/authorize/{id}");
|
||||||
}
|
}
|
||||||
|
private async Task ExportarExcel()
|
||||||
|
{
|
||||||
|
Filters.Page = 1;
|
||||||
|
Filters.PageSize = int.MaxValue; // Exportar todos los resultados
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await quoteService.ExportFilteredAsync(Filters);
|
||||||
|
toastService.ShowSuccess("Exportación completada.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
toastService.ShowError($"Error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,13 +22,13 @@
|
|||||||
<div class="row g-2 align-items-end">
|
<div class="row g-2 align-items-end">
|
||||||
<!-- En monitores grandes queda todo en una fila (col-xxl-2 = 6 columnas por fila) -->
|
<!-- En monitores grandes queda todo en una fila (col-xxl-2 = 6 columnas por fila) -->
|
||||||
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xxl-2">
|
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xxl-2">
|
||||||
<label class="form-label mb-1">Número</label>
|
<label for="number" class="form-label mb-1">Número</label>
|
||||||
<InputText class="form-control form-control-sm" @bind-Value="filters.Number" />
|
<InputText id="number" class="form-control form-control-sm" @bind-Value="filters.Number" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xxl-2">
|
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xxl-2">
|
||||||
<label class="form-label mb-1">Estado</label>
|
<label for="status" class="form-label mb-1">Estado</label>
|
||||||
<InputSelect class="form-select form-select-sm" @bind-Value="filters.Status">
|
<InputSelect id="status" class="form-select form-select-sm" @bind-Value="filters.Status">
|
||||||
<option value="">(Todos)</option>
|
<option value="">(Todos)</option>
|
||||||
<option value="Emitida">Emitida</option>
|
<option value="Emitida">Emitida</option>
|
||||||
<option value="EnTransito">En tránsito</option>
|
<option value="EnTransito">En tránsito</option>
|
||||||
@ -40,24 +40,24 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xxl-2">
|
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xxl-2">
|
||||||
<label class="form-label mb-1">Fecha desde</label>
|
<label for="from" class="form-label mb-1">Fecha desde</label>
|
||||||
<InputDate class="form-control form-control-sm" @bind-Value="filters.From" />
|
<InputDate id="from" class="form-control form-control-sm" @bind-Value="filters.From" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xxl-2">
|
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xxl-2">
|
||||||
<label class="form-label mb-1">Fecha hasta</label>
|
<label for="to" class="form-label mb-1">Fecha hasta</label>
|
||||||
<InputDate class="form-control form-control-sm" @bind-Value="filters.To" />
|
<InputDate id="to" class="form-control form-control-sm" @bind-Value="filters.To" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xxl-2">
|
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xxl-2">
|
||||||
<label class="form-label mb-1">Ubicación</label>
|
<label for="location" class="form-label mb-1">Ubicación</label>
|
||||||
<InputNumber class="form-control form-control-sm" @bind-Value="filters.LocationId" />
|
<InputNumber id="location" class="form-control form-control-sm" @bind-Value="filters.LocationId" />
|
||||||
@* TODO: reemplazar por BlazoredTypeahead cuando conectes lookup de ubicaciones *@
|
@* TODO: reemplazar por BlazoredTypeahead cuando conectes lookup de ubicaciones *@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xxl-2">
|
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xxl-2">
|
||||||
<label class="form-label mb-1">Tam. página</label>
|
<label for="pageg" class="form-label mb-1">Tam. página</label>
|
||||||
<InputSelect class="form-select form-select-sm" @bind-Value="pageSize" @onchange="@(e => ChangePageSize(e.Value?.ToString()))">
|
<InputSelect id="pageg"class="form-select form-select-sm" @bind-Value="pageSize" @onchange="@(e => ChangePageSize(e.Value?.ToString()))">
|
||||||
<option value="10">10</option>
|
<option value="10">10</option>
|
||||||
<option value="20">20</option>
|
<option value="20">20</option>
|
||||||
<option value="50">50</option>
|
<option value="50">50</option>
|
||||||
@ -72,12 +72,12 @@
|
|||||||
<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>
|
||||||
|
<button class="btn btn-secondary rounded-pill" @onclick="Clear">
|
||||||
|
<i class="fas fa-eraser me-1"></i> Limpiar
|
||||||
|
</button>
|
||||||
<button class="btn btn-success rounded-pill" @onclick="ExportarExcel">
|
<button class="btn btn-success rounded-pill" @onclick="ExportarExcel">
|
||||||
<i class="fas fa-file-excel me-1"></i> Excel
|
<i class="fas fa-file-excel me-1"></i> Excel
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-secondary rounded-pill" @onclick="Clear">
|
|
||||||
<i class="fas fa-arrow-left me-1"></i> Volver
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</EditForm>
|
</EditForm>
|
||||||
</div>
|
</div>
|
||||||
@ -127,6 +127,10 @@
|
|||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (IsLoading)
|
||||||
|
{
|
||||||
|
<tr><td colspan="9" class="text-center text-muted py-4">Cargando...</td></tr>
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<tr><td colspan="9" class="text-center text-muted py-4">Sin resultados</td></tr>
|
<tr><td colspan="9" class="text-center text-muted py-4">Sin resultados</td></tr>
|
||||||
@ -170,6 +174,8 @@
|
|||||||
|
|
||||||
private Filters filters = new();
|
private Filters filters = new();
|
||||||
private PagedResult<ExpeditionDto>? result;
|
private PagedResult<ExpeditionDto>? result;
|
||||||
|
private bool IsLoading;
|
||||||
|
|
||||||
private int page = 1;
|
private int page = 1;
|
||||||
private int pageSize = 10;
|
private int pageSize = 10;
|
||||||
private int TotalPages => result is null ? 1 : (int)Math.Ceiling((double)result.TotalItems / result.PageSize);
|
private int TotalPages => result is null ? 1 : (int)Math.Ceiling((double)result.TotalItems / result.PageSize);
|
||||||
@ -203,6 +209,9 @@
|
|||||||
|
|
||||||
private async Task Search()
|
private async Task Search()
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IsLoading = true;
|
||||||
result = await expeditionService.SearchAsync(
|
result = await expeditionService.SearchAsync(
|
||||||
expeditionNumber: filters.Number,
|
expeditionNumber: filters.Number,
|
||||||
status: filters.Status,
|
status: filters.Status,
|
||||||
@ -213,6 +222,15 @@
|
|||||||
pageSize: pageSize);
|
pageSize: pageSize);
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Toast.ShowError(ex.Message);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void Clear()
|
private void Clear()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -54,9 +54,9 @@ static void InjectDependencies(WebAssemblyHostBuilder builder)
|
|||||||
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<IExpeditionService, ExpeditionService>();
|
||||||
|
builder.Services.AddScoped<IQuoteService,QuoteService>();
|
||||||
|
|
||||||
builder.Services.AddScoped<ExchangeRateService>();
|
builder.Services.AddScoped<ExchangeRateService>();
|
||||||
builder.Services.AddScoped<QuoteService>();
|
|
||||||
builder.Services.AddScoped<TicketsService>();
|
builder.Services.AddScoped<TicketsService>();
|
||||||
builder.Services.AddScoped<CustomerService>();
|
builder.Services.AddScoped<CustomerService>();
|
||||||
builder.Services.AddScoped<TaxConditionService>();
|
builder.Services.AddScoped<TaxConditionService>();
|
||||||
|
|||||||
@ -1,11 +1,16 @@
|
|||||||
using Domain.Entities;
|
using Domain.Dtos;
|
||||||
using phronCare.UIBlazor.Services.Sales.Quotes;
|
using Domain.Entities;
|
||||||
|
using Domain.Generics;
|
||||||
|
|
||||||
namespace Services.Sales.Quotes
|
namespace phronCare.UIBlazor.Services.Sales.Quotes
|
||||||
{
|
{
|
||||||
public interface IQuoteService
|
public interface IQuoteService
|
||||||
{
|
{
|
||||||
//Task<CreateQuoteResult> CreateFullQuoteAsync(EQuoteHeader quote, int formSeriesId);
|
Task<bool> AuthorizeQuoteAsync(QuoteAuthorizationRequest request);
|
||||||
// Aquí podrías agregar otros métodos: GetById, Search, etc.
|
Task<CreateQuoteResult> CreateFullQuoteAsync(EQuoteHeader quote, int formSeriesId);
|
||||||
|
Task ExportFilteredAsync(QuoteSearchParams searchParams);
|
||||||
|
Task ExportPdfAsync(int quoteId, string quoteNumber);
|
||||||
|
Task<QuoteDto?> GetDtoByIdAsync(int id);
|
||||||
|
Task<PagedResult<QuoteDto>> SearchAsync(int? customerId = null, string? customerText = null, string? quoteNumber = null, int? professionalId = null, string? professionalText = null, int? institutionId = null, string? institutionText = null, int? patientId = null, string? patientText = null, string? status = null, DateTime? issueDateFrom = null, DateTime? issueDateTo = null, int page = 1, int pageSize = 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,10 +3,13 @@ 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.Sales.Quotes
|
namespace phronCare.UIBlazor.Services.Sales.Quotes
|
||||||
{
|
{
|
||||||
public class QuoteService
|
public class QuoteService:IQuoteService
|
||||||
{
|
{
|
||||||
private readonly IJSRuntime _js;
|
private readonly IJSRuntime _js;
|
||||||
private readonly HttpClient _http;
|
private readonly HttpClient _http;
|
||||||
@ -156,6 +159,32 @@ namespace phronCare.UIBlazor.Services.Sales.Quotes
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public async Task ExportFilteredAsync(QuoteSearchParams searchParams)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var content = new StringContent(JsonSerializer.Serialize(searchParams), Encoding.UTF8, "application/json");
|
||||||
|
var response = await _http.PostAsync("api/quote/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}_quotes.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user