using Core.Dtos; using Domain.Constants; using Domain.Entities; using Domain.Generics; using Models.Interfaces; namespace Core.Services { public class QuoteService(IQuoteRepository quoteRepository):IQuoteDom { #region Declaraciones private readonly IQuoteRepository _quoteRepository = quoteRepository; //private readonly IPhSQuoteRepository _quoteRepository = quoteRepository; #endregion public async Task> 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) { return await _quoteRepository.SearchAsync( customerId, customerText, quoteNumber, professionalId, professionalText, institutionId, institutionText, patientId, patientText, issueDateFrom, issueDateTo, status, page, pageSize); } #region Presupuestos #endregion #region Guardado completo de presupuesto (encabezado + detalles + roles + ajustes + impuestos) public async Task CreateFullQuoteAsync(EQuoteHeader quote, int formSeriesId) { // 1. Validaciones antes de iniciar transacción ValidateQuote(quote); return await _quoteRepository.CreateFullQuoteAsync(quote, formSeriesId); } #endregion #region Validaciones QuoteCreate private void ValidateQuote(EQuoteHeader quote) { if (quote == null) throw new ArgumentNullException(nameof(quote), "El presupuesto no puede ser nulo."); if (quote.CustomerId <= 0) throw new ArgumentException("Debe seleccionar un cliente."); if (quote.PeopleId <= 0) throw new ArgumentException("Debe seleccionar un vendedor."); if (string.IsNullOrWhiteSpace(quote.Currency)) throw new ArgumentException("La moneda es obligatoria."); if (quote.PhSQuoteDetails == null || !quote.PhSQuoteDetails.Any()) throw new InvalidOperationException("Debe incluir al menos un producto."); 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."); } if (quote.PhSQuoteRoles == null || !quote.PhSQuoteRoles.Any()) throw new InvalidOperationException("Debe asignar al menos un rol (profesional, paciente o institución)."); 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) { 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."); } } if (quote.Total < 0) throw new ArgumentException("El total del presupuesto no puede ser negativo."); } #endregion } }