2025-05-13 12:08:38 -03:00
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
|
|
using Core.Dtos;
|
|
|
|
|
|
using Domain.Entities;
|
|
|
|
|
|
using Domain.Generics;
|
2025-05-07 18:35:49 -03:00
|
|
|
|
using Models.Interfaces;
|
2025-05-13 12:08:38 -03:00
|
|
|
|
using Models.Helpers;
|
2025-05-07 18:35:49 -03:00
|
|
|
|
using Models.Models;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Models.Repositories
|
|
|
|
|
|
{
|
|
|
|
|
|
public class PhSQuoteRepository(PhronCareOperationsHubContext context,
|
2025-05-13 12:08:38 -03:00
|
|
|
|
IPhSFormSeriesRepository formSeriesRepository) : IQuoteRepository
|
2025-05-07 18:35:49 -03:00
|
|
|
|
{
|
|
|
|
|
|
private readonly PhronCareOperationsHubContext _context = context;
|
|
|
|
|
|
private readonly IPhSFormSeriesRepository _formSeriesRepository = formSeriesRepository;
|
2025-05-13 12:08:38 -03:00
|
|
|
|
#region Busqueda usando el QuoteDto
|
|
|
|
|
|
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 = 1,
|
|
|
|
|
|
int pageSize = 50)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 1) Base query with includes
|
|
|
|
|
|
var query = _context.PhSQuoteHeaders
|
|
|
|
|
|
.Include(q => q.PhSQuoteDetails)
|
|
|
|
|
|
.Include(q => q.PhSQuoteRoles)
|
|
|
|
|
|
.Include(q => q.PhSQuoteAdjustments)
|
|
|
|
|
|
.Include(q => q.PhSQuoteTaxes)
|
|
|
|
|
|
.AsQueryable();
|
|
|
|
|
|
|
|
|
|
|
|
// 2) Apply filters
|
|
|
|
|
|
if (customerId.HasValue)
|
|
|
|
|
|
query = query.Where(q => q.CustomerId == customerId.Value);
|
|
|
|
|
|
else if (!string.IsNullOrWhiteSpace(customerText))
|
|
|
|
|
|
query = query.Where(q =>
|
|
|
|
|
|
_context.PhSCustomers.Any(c =>
|
|
|
|
|
|
c.Id == q.CustomerId &&
|
|
|
|
|
|
c.Name.Contains(customerText)));
|
|
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(quoteNumber))
|
|
|
|
|
|
query = query.Where(q => q.Quotenumber.Contains(quoteNumber));
|
|
|
|
|
|
|
|
|
|
|
|
if (professionalId.HasValue)
|
|
|
|
|
|
{
|
|
|
|
|
|
query = query.Where(q =>
|
|
|
|
|
|
q.PhSQuoteRoles.Any(r =>
|
|
|
|
|
|
r.Entitytype == PhSEntityTypes.Professional &&
|
|
|
|
|
|
r.EntityId == professionalId.Value));
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (!string.IsNullOrWhiteSpace(professionalText))
|
|
|
|
|
|
{
|
|
|
|
|
|
query = query.Where(q =>
|
|
|
|
|
|
q.PhSQuoteRoles.Any(r =>
|
|
|
|
|
|
r.Entitytype == PhSEntityTypes.Professional &&
|
|
|
|
|
|
_context.PhSProfessionals.Any(p =>
|
|
|
|
|
|
p.Id == r.EntityId &&
|
|
|
|
|
|
p.Fullname.Contains(professionalText))));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (institutionId.HasValue)
|
|
|
|
|
|
{
|
|
|
|
|
|
query = query.Where(q =>
|
|
|
|
|
|
q.PhSQuoteRoles.Any(r =>
|
|
|
|
|
|
r.Entitytype == PhSEntityTypes.Institution &&
|
|
|
|
|
|
r.EntityId == institutionId.Value));
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (!string.IsNullOrWhiteSpace(institutionText))
|
|
|
|
|
|
{
|
|
|
|
|
|
query = query.Where(q =>
|
|
|
|
|
|
q.PhSQuoteRoles.Any(r =>
|
|
|
|
|
|
r.Entitytype == PhSEntityTypes.Institution &&
|
|
|
|
|
|
_context.PhSInstitutions.Any(i =>
|
|
|
|
|
|
i.Id == r.EntityId &&
|
|
|
|
|
|
i.Name.Contains(institutionText))));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (patientId.HasValue)
|
|
|
|
|
|
{
|
|
|
|
|
|
query = query.Where(q =>
|
|
|
|
|
|
q.PhSQuoteRoles.Any(r =>
|
|
|
|
|
|
r.Entitytype == PhSEntityTypes.Patient &&
|
|
|
|
|
|
r.EntityId == patientId.Value));
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (!string.IsNullOrWhiteSpace(patientText))
|
|
|
|
|
|
{
|
|
|
|
|
|
query = query.Where(q =>
|
|
|
|
|
|
q.PhSQuoteRoles.Any(r =>
|
|
|
|
|
|
r.Entitytype == PhSEntityTypes.Patient &&
|
|
|
|
|
|
(_context.PhSPatients.Any(pt =>
|
|
|
|
|
|
pt.Id == r.EntityId &&
|
|
|
|
|
|
pt.Firstname.Contains(patientText)) ||
|
|
|
|
|
|
_context.PhSPatients.Any(pt =>
|
|
|
|
|
|
pt.Id == r.EntityId &&
|
|
|
|
|
|
pt.Lastname.Contains(patientText)))));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (issueDateFrom.HasValue)
|
|
|
|
|
|
query = query.Where(q => q.Issuedate >= issueDateFrom.Value);
|
|
|
|
|
|
if (issueDateTo.HasValue)
|
|
|
|
|
|
query = query.Where(q => q.Issuedate <= issueDateTo.Value);
|
|
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(status))
|
|
|
|
|
|
query = query.Where(q => q.Status == status);
|
|
|
|
|
|
|
|
|
|
|
|
// 3) Execute paged query
|
|
|
|
|
|
var pagedEntities = await query.ToPagedResultAsync(page, pageSize);
|
|
|
|
|
|
|
|
|
|
|
|
// 4) Project to DTOs
|
|
|
|
|
|
var dtos = pagedEntities.Items.Select(header =>
|
|
|
|
|
|
{
|
|
|
|
|
|
// Precompute tax sums
|
|
|
|
|
|
var totalTaxAmount = header.PhSQuoteTaxes.Sum(t => t.Taxamount);
|
|
|
|
|
|
var netBase = header.Netamount.GetValueOrDefault() != 0m
|
|
|
|
|
|
? header.Netamount.Value
|
|
|
|
|
|
: 1m;
|
|
|
|
|
|
|
|
|
|
|
|
return new QuoteDto
|
|
|
|
|
|
{
|
|
|
|
|
|
Id = header.Id,
|
|
|
|
|
|
Quotenumber = header.Quotenumber,
|
|
|
|
|
|
IssueDate = header.Issuedate,
|
|
|
|
|
|
EstimatedDate = header.Estimateddate,
|
|
|
|
|
|
CustomerName = _context.PhSCustomers
|
|
|
|
|
|
.Where(c => c.Id == header.CustomerId)
|
|
|
|
|
|
.Select(c => c.Name)
|
|
|
|
|
|
.FirstOrDefault() ?? "",
|
|
|
|
|
|
ProfessionalName = header.PhSQuoteRoles
|
|
|
|
|
|
.Where(r => r.Entitytype == PhSEntityTypes.Professional)
|
|
|
|
|
|
.Select(r => _context.PhSProfessionals
|
|
|
|
|
|
.Where(p => p.Id == r.EntityId)
|
|
|
|
|
|
.Select(p => p.Fullname)
|
|
|
|
|
|
.FirstOrDefault())
|
|
|
|
|
|
.FirstOrDefault() ?? "",
|
|
|
|
|
|
InstitutionName = header.PhSQuoteRoles
|
|
|
|
|
|
.Where(r => r.Entitytype == PhSEntityTypes.Institution)
|
|
|
|
|
|
.Select(r => _context.PhSInstitutions
|
|
|
|
|
|
.Where(i => i.Id == r.EntityId)
|
|
|
|
|
|
.Select(i => i.Name)
|
|
|
|
|
|
.FirstOrDefault())
|
|
|
|
|
|
.FirstOrDefault() ?? "",
|
|
|
|
|
|
PatientName = header.PhSQuoteRoles
|
|
|
|
|
|
.Where(r => r.Entitytype == PhSEntityTypes.Patient)
|
|
|
|
|
|
.Select(r => _context.PhSPatients
|
|
|
|
|
|
.Where(pt => pt.Id == r.EntityId)
|
|
|
|
|
|
.Select(pt => (pt.Firstname + " " + pt.Lastname).Trim())
|
|
|
|
|
|
.FirstOrDefault())
|
|
|
|
|
|
.FirstOrDefault() ?? "",
|
|
|
|
|
|
BusinessUnitName = _context.PhSBusinessUnits
|
|
|
|
|
|
.Where(b => b.Id == header.BusinessunitId)
|
|
|
|
|
|
.Select(b => b.Code)
|
|
|
|
|
|
.FirstOrDefault() ?? "",
|
|
|
|
|
|
Currency = header.Currency,
|
|
|
|
|
|
Total = header.Total.GetValueOrDefault(0m),
|
|
|
|
|
|
Status = header.Status.Trim(),
|
|
|
|
|
|
SalespersonName = _context.PhSPeople
|
|
|
|
|
|
.Where(u => u.Id == header.PeopleId)
|
|
|
|
|
|
.Select(u => u.Name)
|
|
|
|
|
|
.FirstOrDefault() ?? "",
|
|
|
|
|
|
|
|
|
|
|
|
Items = header.PhSQuoteDetails.Select(d =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var itemBase = d.Quantity * d.Unitprice;
|
|
|
|
|
|
var itemTax = totalTaxAmount * itemBase / netBase;
|
|
|
|
|
|
return new QuoteItemDto
|
|
|
|
|
|
{
|
|
|
|
|
|
Description = d.ProductDescription,
|
|
|
|
|
|
Quantity = d.Quantity,
|
|
|
|
|
|
UnitPrice = d.Unitprice,
|
|
|
|
|
|
Subtotal = itemBase,
|
|
|
|
|
|
TaxAmount = itemTax,
|
|
|
|
|
|
Total = itemBase + itemTax
|
|
|
|
|
|
};
|
|
|
|
|
|
}).ToList(),
|
|
|
|
|
|
|
|
|
|
|
|
Taxes = header.PhSQuoteTaxes.Select(t => new QuoteTaxDto
|
|
|
|
|
|
{
|
|
|
|
|
|
TaxName = t.Taxname,
|
|
|
|
|
|
TaxCode = t.Taxcode,
|
|
|
|
|
|
TaxableAmount = t.TaxableAmount,
|
|
|
|
|
|
TaxRate = t.Taxrate,
|
|
|
|
|
|
TaxAmount = t.Taxamount,
|
|
|
|
|
|
IsIncludedInPrice = t.IsIncludedInPrice
|
|
|
|
|
|
}).ToList(),
|
|
|
|
|
|
|
|
|
|
|
|
Adjustments = header.PhSQuoteAdjustments.Select(a => new QuoteAdjustmentDto
|
|
|
|
|
|
{
|
|
|
|
|
|
Reason = _context.PhSAdjustmentReasons
|
|
|
|
|
|
.Where(r => r.Code == a.ReasonCode)
|
|
|
|
|
|
.Select(r => r.Description)
|
|
|
|
|
|
.FirstOrDefault() ?? "",
|
|
|
|
|
|
Amount = a.Amount.GetValueOrDefault(0m)
|
|
|
|
|
|
}).ToList()
|
|
|
|
|
|
};
|
|
|
|
|
|
}).ToList();
|
|
|
|
|
|
|
|
|
|
|
|
// 5) Return paged DTO result
|
|
|
|
|
|
return new PagedResult<QuoteDto>
|
|
|
|
|
|
{
|
|
|
|
|
|
Items = dtos,
|
|
|
|
|
|
TotalItems = pagedEntities.TotalItems,
|
|
|
|
|
|
Page = pagedEntities.Page,
|
|
|
|
|
|
PageSize = pagedEntities.PageSize
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
2025-05-07 18:35:49 -03:00
|
|
|
|
|
2025-05-13 12:08:38 -03:00
|
|
|
|
#endregion
|
2025-05-07 18:35:49 -03:00
|
|
|
|
#region Guardado completo de presupuesto (encabezado + detalles + roles + ajustes + impuestos)
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Crea un nuevo presupuesto, incluyendo encabezado, detalles, roles, ajustes e impuestos asociados.
|
|
|
|
|
|
/// Genera automáticamente el número de presupuesto en base a la serie indicada.
|
|
|
|
|
|
/// <param name="quote">Presupuesto a registrar, incluyendo entidades relacionadas.</param>
|
|
|
|
|
|
/// <param name="formSeriesId">Identificador de la serie de numeración a utilizar.</param>
|
|
|
|
|
|
/// <returns>Cadena con el número generado del presupuesto.</returns>
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public async Task<string> CreateFullQuoteAsync(EQuoteHeader quote, int formSeriesId)
|
|
|
|
|
|
{
|
|
|
|
|
|
using var transaction = await _context.Database.BeginTransactionAsync();
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var nextNumber = await _formSeriesRepository.GetNextInternalNumberAsync(formSeriesId);
|
|
|
|
|
|
var series = await _formSeriesRepository.GetByIdAsync(formSeriesId)
|
|
|
|
|
|
?? throw new InvalidOperationException("Serie no encontrada");
|
|
|
|
|
|
int padding = 8; /* format "00000000" */
|
2025-05-13 16:28:22 -03:00
|
|
|
|
quote.Status = "Emitido";
|
2025-05-07 18:35:49 -03:00
|
|
|
|
quote.Quotenumber = $"{series.Letter}-{nextNumber.ToString($"D{padding}")}";
|
|
|
|
|
|
|
|
|
|
|
|
var headerEntity = EntityMapper.MapEntity<EQuoteHeader, PhSQuoteHeader>(quote);
|
|
|
|
|
|
_context.PhSQuoteHeaders.Add(headerEntity);
|
|
|
|
|
|
|
|
|
|
|
|
#region Nota: Esta seccion queda para futura modificacion en caso de cambiar CASCADE INSERT de las entidades relacionadas
|
|
|
|
|
|
//// Guardado de Detalles
|
|
|
|
|
|
//if (quote.PhSQuoteDetails?.Any() == true)
|
|
|
|
|
|
//{
|
|
|
|
|
|
// foreach (var detail in quote.PhSQuoteDetails)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// detail.QuoteheaderId = headerEntity.Id;
|
|
|
|
|
|
// var dbDetail = EntityMapper.MapEntity<EQuoteDetail, PhSQuoteDetail>(detail);
|
|
|
|
|
|
// _context.PhSQuoteDetails.Add(dbDetail);
|
|
|
|
|
|
// }
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
|
|
//// Guardado de Roles
|
|
|
|
|
|
//if (quote.PhSQuoteRoles?.Any() == true)
|
|
|
|
|
|
//{
|
|
|
|
|
|
// foreach (var role in quote.PhSQuoteRoles)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// role.QuoteheaderId = headerEntity.Id;
|
|
|
|
|
|
// var dbRole = EntityMapper.MapEntity<EQuoteRole, PhSQuoteRole>(role);
|
|
|
|
|
|
// _context.PhSQuoteRoles.Add(dbRole);
|
|
|
|
|
|
// }
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
|
|
//// Guardado de Ajustes
|
|
|
|
|
|
//if (quote.PhSQuoteAdjustments?.Any() == true)
|
|
|
|
|
|
//{
|
|
|
|
|
|
// foreach (var adj in quote.PhSQuoteAdjustments)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// adj.QuoteheaderId = headerEntity.Id;
|
|
|
|
|
|
// var dbAdj = EntityMapper.MapEntity<EQuoteAdjustment, PhSQuoteAdjustment>(adj);
|
|
|
|
|
|
// _context.PhSQuoteAdjustments.Add(dbAdj);
|
|
|
|
|
|
// }
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
|
|
//// Guardado de Impuestos
|
|
|
|
|
|
//if (quote.PhSQuoteTaxes?.Any() == true)
|
|
|
|
|
|
//{
|
|
|
|
|
|
// foreach (var tax in quote.PhSQuoteTaxes)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// tax.QuoteheaderId = headerEntity.Id;
|
|
|
|
|
|
// var dbTax = EntityMapper.MapEntity<EQuoteTax, PhSQuoteTaxis>(tax);
|
|
|
|
|
|
// _context.PhSQuoteTaxes.Add(dbTax);
|
|
|
|
|
|
// }
|
|
|
|
|
|
//}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
await _context.SaveChangesAsync();
|
|
|
|
|
|
await transaction.CommitAsync();
|
|
|
|
|
|
return headerEntity.Quotenumber;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
await transaction.RollbackAsync();
|
|
|
|
|
|
throw;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|