phronCare/Core/Services/QuoteService.cs

197 lines
8.4 KiB
C#
Raw Normal View History

2025-09-11 22:41:46 -03:00
using Core.Interfaces;
2025-05-13 12:08:38 -03:00
using Domain.Constants;
2025-09-11 22:41:46 -03:00
using Domain.Dtos;
2025-05-13 12:08:38 -03:00
using Domain.Entities;
2025-04-27 02:19:29 -03:00
using Domain.Generics;
using Models.Interfaces;
2025-09-11 22:41:46 -03:00
using System.Drawing.Printing;
using System.Reflection;
using Transversal.Services;
2025-04-27 02:19:29 -03:00
2025-05-08 15:46:04 -03:00
namespace Core.Services
2025-04-27 02:19:29 -03:00
{
2025-05-13 12:08:38 -03:00
public class QuoteService(IQuoteRepository quoteRepository):IQuoteDom
2025-04-27 02:19:29 -03:00
{
#region Declaraciones
2025-05-13 12:08:38 -03:00
private readonly IQuoteRepository _quoteRepository = quoteRepository;
//private readonly IPhSQuoteRepository _quoteRepository = quoteRepository;
2025-04-27 02:19:29 -03:00
#endregion
2025-05-16 17:19:40 -03:00
#region Presupuestos
2025-05-13 12:08:38 -03:00
public async Task<PagedResult<QuoteDto>> SearchAsync(int? customerId, string? customerText, string? quoteNumber, int? professionalId, string? professionalText, int? institutionId, string? institutionText, int? patientId, string? patientText, DateTime? issueDateFrom, DateTime? issueDateTo, string? status, int page, int pageSize)
2025-04-27 02:19:29 -03:00
{
2025-05-13 12:08:38 -03:00
return await _quoteRepository.SearchAsync(
customerId,
2025-05-13 12:08:38 -03:00
customerText,
2025-04-27 02:19:29 -03:00
quoteNumber,
professionalId,
2025-05-13 12:08:38 -03:00
professionalText,
2025-04-27 02:19:29 -03:00
institutionId,
2025-05-13 12:08:38 -03:00
institutionText,
2025-04-27 02:19:29 -03:00
patientId,
2025-05-13 12:08:38 -03:00
patientText,
2025-04-27 02:19:29 -03:00
issueDateFrom,
issueDateTo,
status,
page,
pageSize);
}
2025-05-16 17:19:40 -03:00
public async Task<QuoteDto?> GetDtoByIdAsync(int id)
{
return await _quoteRepository.GetDtoByIdAsync(id);
}
2025-09-11 22:41:46 -03:00
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);
}
}
2025-08-18 00:47:37 -03:00
//public async Task<QuoteDto?> GetDtoByQuoteNumberAsync(string quoteNumber)
//{
// return await _quoteRepository.GetDtoByIdAsync(quoteNumber);
//}
2025-04-27 02:19:29 -03:00
#endregion
2025-05-06 21:00:03 -03:00
#region Guardado completo de presupuesto (encabezado + detalles + roles + ajustes + impuestos)
2025-05-18 03:21:48 -03:00
public async Task<(int Id, string Quotenumber)> CreateFullQuoteAsync(EQuoteHeader quote, int formSeriesId)
2025-05-06 21:00:03 -03:00
{
// 1. Validaciones antes de iniciar transacción
ValidateQuote(quote);
return await _quoteRepository.CreateFullQuoteAsync(quote, formSeriesId);
}
#endregion
2025-05-06 21:00:03 -03:00
2025-05-29 19:21:57 -03:00
public async Task<bool> AuthorizeQuoteAsync(int quoteId, List<QuoteAuthorizationDto> items)
{
2025-05-29 21:16:55 -03:00
if (items == null)
throw new InvalidOperationException("No se recibieron ítems para autorizar.");
2025-05-29 19:21:57 -03:00
2025-05-29 21:16:55 -03:00
// Si no hay ítems aprobados, consideramos que es una anulación
var approvedDetails = items
.Where(i => i.Approved)
.Select(i =>
{
if (!i.ApprovedQuantity.HasValue || !i.ApprovedUnitPrice.HasValue)
throw new InvalidOperationException("Los ítems aprobados deben tener cantidad y precio válidos.");
return new EQuoteDetail
{
Id = i.Id,
Approved = true,
Approvedquantity = i.ApprovedQuantity,
Approvedunitprice = i.ApprovedUnitPrice
};
}).ToList();
// Este llamado puede interpretar lista vacía como anulación completa
2025-05-29 19:21:57 -03:00
return await _quoteRepository.AuthorizeQuoteAsync(quoteId, approvedDetails);
}
#region Validaciones QuoteCreate
private void ValidateQuote(EQuoteHeader quote)
{
if (quote == null)
throw new ArgumentNullException(nameof(quote), "El presupuesto no puede ser nulo.");
2025-05-06 21:00:03 -03:00
if (quote.CustomerId <= 0)
throw new ArgumentException("Debe seleccionar un cliente.");
2025-05-06 21:00:03 -03:00
if (quote.PeopleId <= 0)
throw new ArgumentException("Debe seleccionar un vendedor.");
2025-05-06 21:00:03 -03:00
if (string.IsNullOrWhiteSpace(quote.Currency))
throw new ArgumentException("La moneda es obligatoria.");
2025-05-06 21:00:03 -03:00
if (quote.PhSQuoteDetails == null || !quote.PhSQuoteDetails.Any())
throw new InvalidOperationException("Debe incluir al menos un producto.");
2025-05-06 21:00:03 -03:00
foreach (var detail in quote.PhSQuoteDetails)
{
if (detail.Quantity <= 0)
throw new ArgumentException($"La cantidad para el producto {detail.ProductId} debe ser mayor a cero.");
if (detail.Unitprice < 0)
throw new ArgumentException($"El precio unitario del producto {detail.ProductId} no puede ser negativo.");
2025-05-06 21:00:03 -03:00
}
if (quote.PhSQuoteRoles == null || !quote.PhSQuoteRoles.Any())
throw new InvalidOperationException("Debe asignar al menos un rol (profesional, paciente o institución).");
2025-05-13 12:08:38 -03:00
var hasProfessional = quote.PhSQuoteRoles.Any(r => r.Entitytype == EntityTypes.Professional);
var hasPatient = quote.PhSQuoteRoles.Any(r => r.Entitytype == EntityTypes.Patient);
var hasInstitution = quote.PhSQuoteRoles.Any(r => r.Entitytype == EntityTypes.Institution);
if (!hasProfessional)
throw new InvalidOperationException("Debe asignar un profesional.");
if (!hasInstitution)
throw new InvalidOperationException("Debe asignar un paciente.");
if (!hasPatient)
throw new InvalidOperationException("Debe asignar un paciente.");
if (quote.PhSQuoteTaxes != null)
2025-05-06 21:00:03 -03:00
{
foreach (var tax in quote.PhSQuoteTaxes)
{
if (tax.Taxrate < 0 || tax.Taxrate > 100)
throw new ArgumentException($"La alícuota del impuesto '{tax.Taxname}' no es válida.");
}
2025-05-06 21:00:03 -03:00
}
if (quote.Total < 0)
throw new ArgumentException("El total del presupuesto no puede ser negativo.");
2025-05-06 21:00:03 -03:00
}
2025-08-18 00:47:37 -03:00
public async Task<QuoteDto?> GetDtoByQuoteNumberAsync(string quoteNumber)
{
return await _quoteRepository.GetDtoByQuoteNumberAsync(quoteNumber);
}
2025-05-06 21:00:03 -03:00
#endregion
2025-04-27 02:19:29 -03:00
}
2025-05-06 21:00:03 -03:00
}