Update Quote and FormSeries Add
All checks were successful
CI/CD Pipeline / Build and Deploy with Docker Compose (push) Successful in 5m20s

This commit is contained in:
Leandro Hernan Rojas 2025-04-27 02:19:29 -03:00
parent 9940189a93
commit 2014218ad0
17 changed files with 1079 additions and 0 deletions

View File

@ -0,0 +1,21 @@
using Domain.Entities;
using Domain.Generics;
namespace Models.Interfaces
{
public interface IQuoteDom
{
Task<PagedResult<EQuoteHeader>> GetAllQuotesAsync(int page = 1, int pageSize = 50);
Task<EQuoteHeader?> GetQuoteByIdAsync(int id);
Task<IEnumerable<EQuoteHeader>> GetQuotesByCustomerAsync(int customerId);
Task<PagedResult<EQuoteHeader>> SearchQuotesAsync(int? customerId,
string? quoteNumber,int? professionalId, int? institutionId,
int? patientId, DateTime? issueDateFrom,DateTime? issueDateTo,
string? status, int page = 1, int pageSize = 50);
Task<EQuoteHeader> CreateQuoteAsync(EQuoteHeader quote, int formSeriesId);
Task UpdateQuoteAsync(EQuoteHeader quote);
Task DeleteQuoteAsync(int id);
Task<byte[]> ExportFilteredQuotesToExcelAsync(QuoteSearchParams searchParams);
}
}

View File

@ -0,0 +1,151 @@
using Domain.Entities;
using Domain.Generics;
using Models.Interfaces;
using System.Reflection;
using Transversal.Services;
namespace PhronCare.Core.Services.Sales
{
public class QuoteService(
IPhSQuoteHeaderRepository quoteHeaderRepository,
IPhSQuoteDetailRepository quoteDetailRepository,
IPhSQuoteRoleRepository quoteRoleRepository,
IPhSFormSeriesRepository formSeriesRepository
) : IQuoteDom
{
#region Declaraciones
private readonly IPhSQuoteHeaderRepository _quoteHeaderRepository = quoteHeaderRepository;
private readonly IPhSQuoteDetailRepository _quoteDetailRepository = quoteDetailRepository;
private readonly IPhSQuoteRoleRepository _quoteRoleRepository = quoteRoleRepository;
private readonly IPhSFormSeriesRepository _formSeriesRepository = formSeriesRepository;
#endregion
#region Métodos
public async Task<PagedResult<EQuoteHeader>> GetAllQuotesAsync(int page = 1, int pageSize = 50)
{
return await _quoteHeaderRepository.GetAllAsync(page, pageSize);
}
public async Task<EQuoteHeader?> GetQuoteByIdAsync(int id)
{
return await _quoteHeaderRepository.GetByIdAsync(id);
}
public async Task<IEnumerable<EQuoteHeader>> GetQuotesByCustomerAsync(int customerId)
{
var quotes = await _quoteHeaderRepository.GetByCustomerIdAsync(customerId);
return quotes;
}
public async Task<PagedResult<EQuoteHeader>> SearchQuotesAsync(int? customerId, string? quoteNumber, int? professionalId, int? institutionId, int? patientId, DateTime? issueDateFrom, DateTime? issueDateTo, string? status, int page = 1, int pageSize = 50)
{
return await _quoteHeaderRepository.SearchAsync(customerId,
quoteNumber,
professionalId,
institutionId,
patientId,
issueDateFrom,
issueDateTo,
status,
page,
pageSize);
}
public async Task<EQuoteHeader> CreateQuoteAsync(EQuoteHeader quote, int formSeriesId)
{
// Obtener el próximo número de documento
var nextNumber = await _formSeriesRepository.GetNextInternalNumberAsync(formSeriesId);
// Asignar el número al presupuesto
quote.Quotenumber = nextNumber.ToString();
// Crear el presupuesto
var newQuote = await _quoteHeaderRepository.AddAsync(quote);
// Crear los detalles asociados
if (quote.Details != null)
{
foreach (var detail in quote.Details)
{
detail.QuoteheaderId = newQuote.Id;
await _quoteDetailRepository.AddAsync(detail);
}
}
// Crear los roles asociados
if (quote.Roles != null)
{
foreach (var role in quote.Roles)
{
role.QuoteheaderId = newQuote.Id;
await _quoteRoleRepository.AddAsync(role);
}
}
return newQuote;
}
public async Task UpdateQuoteAsync(EQuoteHeader quote)
{
await _quoteHeaderRepository.UpdateAsync(quote);
}
public async Task DeleteQuoteAsync(int id)
{
await _quoteHeaderRepository.DeleteAsync(id);
}
public async Task<byte[]> ExportFilteredQuotesToExcelAsync(QuoteSearchParams searchParams)
{
try
{
// Buscar los presupuestos con los filtros indicados
var searchResult = await SearchQuotesAsync(
searchParams.CustomerId,
searchParams.QuoteNumber,
searchParams.ProfessionalId,
searchParams.InstitutionId,
searchParams.PatientId,
searchParams.IssueDateFrom,
searchParams.IssueDateTo,
searchParams.Status,
searchParams.Page,
searchParams.PageSize
);
// Verificar si hay resultados
if (searchResult?.Items == null || !searchResult.Items.Any())
{
throw new Exception("No se encontraron presupuestos para exportar.");
}
// Instanciar exportador
var stream = new XLSXExportBase();
// Armar los datos a exportar
var quotesData = searchResult.Items.Select(q => new
{
NúmeroPresupuesto = q.Quotenumber,
Estado = q.Status,
FechaEmisión = q.Issuedate.ToString("yyyy-MM-dd"),
FechaTentativa = q.Estimateddate?.ToString("yyyy-MM-dd"),
ImporteEstimado = q.Estimatedamount,
ImporteAprobado = q.Approvedamount,
Profesional = q.Roles.FirstOrDefault(r => r.Entitytype == "PhS_Professionals")?.Entitytype,
Institución = q.Roles.FirstOrDefault(r => r.Entitytype == "PhS_Institutions")?.Entitytype,
Paciente = q.Roles.FirstOrDefault(r => r.Entitytype == "PhS_Patients")?.Entitytype
}).ToList();
// Generar archivo Excel
var excelFile = stream.ExportExcel(quotesData);
return excelFile;
}
catch (Exception ex)
{
var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod";
throw new Exception($"{methodName} Message: {ex.Message}", ex);
}
}
#endregion
}
}

View File

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Domain.Entities
{
/// <summary>
/// Tabla de detalles de presupuestos
/// </summary>
public class EQuoteDetail
{
/// <summary>
/// ID interno
/// </summary>
public int Id { get; set; }
/// <summary>
/// FK a encabezado de presupuesto
/// </summary>
public int QuoteheaderId { get; set; }
/// <summary>
/// ID del producto original
/// </summary>
public int ProductId { get; set; }
/// <summary>
/// Descripción modificable del producto (puede diferir del original)
/// </summary>
public string? ProductDescription { get; set; }
/// <summary>
/// Cantidad
/// </summary>
public int Quantity { get; set; }
/// <summary>
/// Precio unitario original
/// </summary>
public decimal Unitprice { get; set; }
/// <summary>
/// ¿Aprobado? (0=No, 1=Si)
/// </summary>
public bool Approved { get; set; }
/// <summary>
/// Importe aprobado final
/// </summary>
public decimal? Approvedamount { get; set; }
/// <summary>
/// Fecha de creación del registro
/// </summary>
public DateTime Createdat { get; set; }
/// <summary>
/// Fecha de última modificación
/// </summary>
public DateTime? Modifiedat { get; set; }
public virtual EQuoteHeader Quoteheader { get; set; } = null!;
}
}

View File

@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Domain.Entities
{
/// <summary>
/// Tabla de cabeceras de presupuestos
/// </summary>
public class EQuoteHeader
{
/// <summary>
/// ID interno
/// </summary>
public int Id { get; set; }
/// <summary>
/// Relación con Tickets
/// </summary>
public Guid TicketId { get; set; }
/// <summary>
/// Cliente asociado
/// </summary>
public int CustomerId { get; set; }
/// <summary>
/// Unidad de negocio
/// </summary>
public int BusinessunitId { get; set; }
/// <summary>
/// Estado: E (Emitido), A (Aprobado), AC (Aprobado para cirugia), etc.
/// </summary>
public string Status { get; set; } = null!;
/// <summary>
/// Fecha de emisión
/// </summary>
public DateTime Issuedate { get; set; }
/// <summary>
/// Fecha de aprobación
/// </summary>
public DateOnly? Approvaldate { get; set; }
/// <summary>
/// Fecha tentativa (de cirugía por ej.)
/// </summary>
public DateTime? Estimateddate { get; set; }
/// <summary>
/// Importe estimado total
/// </summary>
public decimal? Estimatedamount { get; set; }
/// <summary>
/// Importe aprobado
/// </summary>
public decimal? Approvedamount { get; set; }
/// <summary>
/// Número visible del presupuesto
/// </summary>
public string Quotenumber { get; set; } = null!;
/// <summary>
/// Cantidad de impresiones
/// </summary>
public int Printcount { get; set; }
/// <summary>
/// Observaciones internas
/// </summary>
public string? Observations { get; set; }
/// <summary>
/// Fecha de creación
/// </summary>
public DateTime Createdat { get; set; }
/// <summary>
/// Fecha de modificación
/// </summary>
public DateTime? Modifiedat { get; set; }
public virtual ICollection<EQuoteDetail> Details { get; set; } = new List<EQuoteDetail>();
public virtual ICollection<EQuoteRole> Roles { get; set; } = new List<EQuoteRole>();
}
}

View File

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Domain.Entities
{
public class EQuoteRole
{
/// <summary>
/// ID interno
/// </summary>
public int Id { get; set; }
/// <summary>
/// FK a encabezado de presupuesto (PhS_QuoteHeaders)
/// </summary>
public int QuoteheaderId { get; set; }
/// <summary>
/// Tipo de entidad asociada (Ej: PhS_Professionals, PhS_Institutions, PhS_Patients)
/// </summary>
public string Entitytype { get; set; } = null!;
/// <summary>
/// ID de la entidad asociada
/// </summary>
public int EntityId { get; set; }
/// <summary>
/// Rol que cumple la entidad en el presupuesto (Ej: Medico, Hospital, Paciente)
/// </summary>
public string Role { get; set; } = null!;
}
}

View File

@ -0,0 +1,14 @@
namespace Domain.Generics
{
public class QuoteSearchParams : PagedRequest
{
public int? CustomerId { get; set; }
public string? QuoteNumber { get; set; }
public int? ProfessionalId { get; set; }
public int? InstitutionId { get; set; }
public int? PatientId { get; set; }
public DateTime? IssueDateFrom { get; set; }
public DateTime? IssueDateTo { get; set; }
public string? Status { get; set; }
}
}

View File

@ -0,0 +1,7 @@
namespace Models.Interfaces
{
public interface IPhSFormSeriesRepository
{
Task<int> GetNextInternalNumberAsync(int formSeriesId);
}
}

View File

@ -0,0 +1,14 @@
using Domain.Entities;
using Domain.Generics;
namespace Models.Interfaces
{
public interface IPhSQuoteDetailRepository
{
Task<EQuoteDetail> AddAsync(EQuoteDetail quoteDetail);
Task DeleteAsync(int id);
Task<PagedResult<EQuoteDetail>> GetAllAsync(int page = 1, int pageSize = 50);
Task<EQuoteDetail?> GetByIdAsync(int id);
Task UpdateAsync(EQuoteDetail quoteDetail);
}
}

View File

@ -0,0 +1,16 @@
using Domain.Entities;
using Domain.Generics;
namespace Models.Interfaces
{
public interface IPhSQuoteHeaderRepository
{
Task<PagedResult<EQuoteHeader>> GetAllAsync(int page = 1, int pageSize = 50);
Task<IEnumerable<EQuoteHeader>> GetByCustomerIdAsync(int customerId);
Task<EQuoteHeader?> GetByIdAsync(int id);
Task<PagedResult<EQuoteHeader>> SearchAsync(int? customerId, string? quoteNumber, int? professionalId, int? institutionId, int? patientId, DateTime? issueDateFrom, DateTime? issueDateTo, string? status, int page = 1, int pageSize = 50);
Task<EQuoteHeader> AddAsync(EQuoteHeader quoteHeader);
Task UpdateAsync(EQuoteHeader quoteHeader);
Task DeleteAsync(int id);
}
}

View File

@ -0,0 +1,14 @@
using Domain.Entities;
using Domain.Generics;
namespace Models.Interfaces
{
public interface IPhSQuoteRoleRepository
{
Task<EQuoteRole> AddAsync(EQuoteRole quoteRole);
Task DeleteAsync(int id);
Task<PagedResult<EQuoteRole>> GetAllAsync(int page = 1, int pageSize = 50);
Task<EQuoteRole?> GetByIdAsync(int id);
Task UpdateAsync(EQuoteRole quoteRole);
}
}

View File

@ -0,0 +1,35 @@
using Microsoft.EntityFrameworkCore;
using Models.Interfaces;
using Models.Models;
namespace PhronCare.Core.Data.Repositories.Sales
{
public class PhSFormSeriesRepository(PhronCareOperationsHubContext context) : IPhSFormSeriesRepository
{
#region Declaraciones
private readonly PhronCareOperationsHubContext _context = context;
#endregion
#region Métodos
public async Task<int> GetNextInternalNumberAsync(int formSeriesId)
{
var nextNumberParam = new Microsoft.Data.SqlClient.SqlParameter
{
ParameterName = "@NextNumber",
SqlDbType = System.Data.SqlDbType.Int,
Direction = System.Data.ParameterDirection.Output
};
await _context.Database.ExecuteSqlRawAsync(
"EXEC dbo.PhS_FormSeries_GetNextNumber @FormSeriesId = {0}, @NextNumber = @NextNumber OUTPUT",
formSeriesId,
nextNumberParam
);
return (int)nextNumberParam.Value;
}
#endregion
}
}

View File

@ -0,0 +1,63 @@
using Domain.Entities;
using Domain.Generics;
using Microsoft.EntityFrameworkCore;
using Models.Helpers;
using Models.Interfaces;
using Models.Models;
namespace PhronCare.Core.Data.Repositories.Sales
{
public class PhSQuoteDetailRepository(PhronCareOperationsHubContext context) : IPhSQuoteDetailRepository
{
#region Declaraciones
private readonly PhronCareOperationsHubContext _context = context;
#endregion
#region Métodos
public async Task<PagedResult<EQuoteDetail>> GetAllAsync(int page = 1, int pageSize = 50)
{
var query = _context.PhSQuoteDetails
.AsQueryable();
var pagedEntities = await query.ToPagedResultAsync(page, pageSize);
return new PagedResult<EQuoteDetail>
{
Items = pagedEntities.Items.Select(EntityMapper.MapEntity<PhSQuoteDetail, EQuoteDetail>),
TotalItems = pagedEntities.TotalItems,
Page = pagedEntities.Page,
PageSize = pagedEntities.PageSize
};
}
public async Task<EQuoteDetail?> GetByIdAsync(int id)
{
var entity = await _context.PhSQuoteDetails
.FirstOrDefaultAsync(q => q.Id == id);
return entity != null ? EntityMapper.MapEntity<PhSQuoteDetail, EQuoteDetail>(entity) : null;
}
public async Task<EQuoteDetail> AddAsync(EQuoteDetail quoteDetail)
{
var dbEntity = EntityMapper.MapEntity<EQuoteDetail, PhSQuoteDetail>(quoteDetail);
_context.PhSQuoteDetails.Add(dbEntity);
await _context.SaveChangesAsync();
return EntityMapper.MapEntity<PhSQuoteDetail, EQuoteDetail>(dbEntity);
}
public async Task UpdateAsync(EQuoteDetail quoteDetail)
{
var dbEntity = EntityMapper.MapEntity<EQuoteDetail, PhSQuoteDetail>(quoteDetail);
_context.PhSQuoteDetails.Update(dbEntity);
await _context.SaveChangesAsync();
}
public async Task DeleteAsync(int id)
{
var entity = await _context.PhSQuoteDetails.FindAsync(id);
if (entity != null)
{
_context.PhSQuoteDetails.Remove(entity);
await _context.SaveChangesAsync();
}
}
#endregion
}
}

View File

@ -0,0 +1,137 @@
using Domain.Entities;
using Domain.Generics;
using Microsoft.EntityFrameworkCore;
using Models.Helpers;
using Models.Interfaces;
using Models.Models;
namespace PhronCare.Core.Data.Repositories.Sales
{
public class PhSQuoteHeaderRepository(PhronCareOperationsHubContext context): IPhSQuoteHeaderRepository
{
#region Declaraciones
private readonly PhronCareOperationsHubContext _context = context;
#endregion
#region Métodos
public async Task<PagedResult<EQuoteHeader>> GetAllAsync(int page = 1, int pageSize = 50)
{
var query = _context.PhSQuoteHeaders
.Include(q => q.PhSQuoteDetails)
.Include(q => q.PhSQuoteRoles)
.AsQueryable();
var pagedEntities = await query.ToPagedResultAsync(page, pageSize);
return new PagedResult<EQuoteHeader>
{
Items = pagedEntities.Items.Select(EntityMapper.MapEntity<PhSQuoteHeader, EQuoteHeader>),
TotalItems = pagedEntities.TotalItems,
Page = pagedEntities.Page,
PageSize = pagedEntities.PageSize
};
}
public async Task<EQuoteHeader?> GetByIdAsync(int id)
{
var entity = await _context.PhSQuoteHeaders
.Include(q => q.PhSQuoteDetails)
.Include(q => q.PhSQuoteRoles)
.FirstOrDefaultAsync(q => q.Id == id);
return entity != null ? EntityMapper.MapEntity<PhSQuoteHeader, EQuoteHeader>(entity) : null;
}
public async Task<IEnumerable<EQuoteHeader>> GetByCustomerIdAsync(int customerId)
{
var entities = await _context.PhSQuoteHeaders
.Where(q => q.CustomerId == customerId)
.Include(q => q.PhSQuoteDetails)
.Include(q => q.PhSQuoteRoles)
.ToListAsync();
return entities.Select(EntityMapper.MapEntity<PhSQuoteHeader, EQuoteHeader>);
}
public async Task<PagedResult<EQuoteHeader>> SearchAsync(int? customerId,
string? quoteNumber, int? professionalId, int? institutionId, int? patientId,
DateTime? issueDateFrom, DateTime? issueDateTo,
string? status, int page = 1, int pageSize = 50)
{
var query = _context.PhSQuoteHeaders
.Include(q => q.PhSQuoteDetails)
.Include(q => q.PhSQuoteRoles)
.AsQueryable();
if (customerId.HasValue)
{
query = query.Where(q => q.CustomerId == customerId);
}
if (!string.IsNullOrEmpty(quoteNumber))
{
query = query.Where(q => q.Quotenumber.Contains(quoteNumber));
}
if (professionalId.HasValue)
{
query = query.Where(q => q.PhSQuoteRoles.Any(r => r.Entitytype == "PhS_Professionals" && r.EntityId == professionalId));
}
if (institutionId.HasValue)
{
query = query.Where(q => q.PhSQuoteRoles.Any(r => r.Entitytype == "PhS_Institutions" && r.EntityId == institutionId));
}
if (patientId.HasValue)
{
query = query.Where(q => q.PhSQuoteRoles.Any(r => r.Entitytype == "PhS_Patients" && r.EntityId == patientId));
}
if (issueDateFrom.HasValue)
{
query = query.Where(q => q.Issuedate >= issueDateFrom.Value);
}
if (issueDateTo.HasValue)
{
query = query.Where(q => q.Issuedate <= issueDateTo.Value);
}
if (!string.IsNullOrEmpty(status))
{
query = query.Where(q => q.Status == status);
}
// Paginación final
var pagedEntities = await query.ToPagedResultAsync(page, pageSize);
return new PagedResult<EQuoteHeader>
{
Items = pagedEntities.Items.Select(EntityMapper.MapEntity<PhSQuoteHeader, EQuoteHeader>),
TotalItems = pagedEntities.TotalItems,
Page = pagedEntities.Page,
PageSize = pagedEntities.PageSize
};
}
public async Task<EQuoteHeader> AddAsync(EQuoteHeader quoteHeader)
{
var dbEntity = EntityMapper.MapEntity<EQuoteHeader, PhSQuoteHeader>(quoteHeader);
_context.PhSQuoteHeaders.Add(dbEntity);
await _context.SaveChangesAsync();
return EntityMapper.MapEntity<PhSQuoteHeader, EQuoteHeader>(dbEntity);
}
public async Task UpdateAsync(EQuoteHeader quoteHeader)
{
var dbEntity = EntityMapper.MapEntity<EQuoteHeader, PhSQuoteHeader>(quoteHeader);
_context.PhSQuoteHeaders.Update(dbEntity);
await _context.SaveChangesAsync();
}
public async Task DeleteAsync(int id)
{
var entity = await _context.PhSQuoteHeaders.FindAsync(id);
if (entity != null)
{
_context.PhSQuoteHeaders.Remove(entity);
await _context.SaveChangesAsync();
}
}
#endregion
}
}

View File

@ -0,0 +1,62 @@
using Domain.Entities;
using Domain.Generics;
using Microsoft.EntityFrameworkCore;
using Models.Helpers;
using Models.Interfaces;
using Models.Models;
namespace PhronCare.Core.Data.Repositories.Sales
{
public class PhSQuoteRoleRepository(PhronCareOperationsHubContext context) : IPhSQuoteRoleRepository
{
#region Declaraciones
private readonly PhronCareOperationsHubContext _context = context;
#endregion
#region Métodos
public async Task<PagedResult<EQuoteRole>> GetAllAsync(int page = 1, int pageSize = 50)
{
var query = _context.PhSQuoteRoles
.AsQueryable();
var pagedEntities = await query.ToPagedResultAsync(page, pageSize);
return new PagedResult<EQuoteRole>
{
Items = pagedEntities.Items.Select(EntityMapper.MapEntity<PhSQuoteRole, EQuoteRole>),
TotalItems = pagedEntities.TotalItems,
Page = pagedEntities.Page,
PageSize = pagedEntities.PageSize
};
}
public async Task<EQuoteRole?> GetByIdAsync(int id)
{
var entity = await _context.PhSQuoteRoles
.FirstOrDefaultAsync(q => q.Id == id);
return entity != null ? EntityMapper.MapEntity<PhSQuoteRole, EQuoteRole>(entity) : null;
}
public async Task<EQuoteRole> AddAsync(EQuoteRole quoteRole)
{
var dbEntity = EntityMapper.MapEntity<EQuoteRole, PhSQuoteRole>(quoteRole);
_context.PhSQuoteRoles.Add(dbEntity);
await _context.SaveChangesAsync();
return EntityMapper.MapEntity<PhSQuoteRole, EQuoteRole>(dbEntity);
}
public async Task UpdateAsync(EQuoteRole quoteRole)
{
var dbEntity = EntityMapper.MapEntity<EQuoteRole, PhSQuoteRole>(quoteRole);
_context.PhSQuoteRoles.Update(dbEntity);
await _context.SaveChangesAsync();
}
public async Task DeleteAsync(int id)
{
var entity = await _context.PhSQuoteRoles.FindAsync(id);
if (entity != null)
{
_context.PhSQuoteRoles.Remove(entity);
await _context.SaveChangesAsync();
}
}
#endregion
}
}

View File

@ -0,0 +1,165 @@
using Core.Interfaces;
using Domain.Entities;
using Domain.Generics;
using Microsoft.AspNetCore.Mvc;
using Models.Interfaces;
using System.Reflection;
namespace phronCare.API.Controllers.Sales
{
[Route("api/[controller]")]
[ApiController]
public class QuoteController : ControllerBase
{
private readonly IQuoteDom _quoteService;
public QuoteController(IQuoteDom quoteService)
{
_quoteService = quoteService ?? throw new ArgumentNullException(nameof(quoteService));
}
[HttpGet("all")]
public async Task<IActionResult> GetAll([FromQuery] int page = 1, [FromQuery] int pageSize = 50)
{
try
{
var result = await _quoteService.GetAllQuotesAsync(page, pageSize);
return Ok(result);
}
catch (Exception ex)
{
var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod";
return StatusCode(500, $"{methodName} Message: {ex.Message}");
}
}
[HttpGet("search")]
public async Task<IActionResult> Search(
[FromQuery] int? customerId,
[FromQuery] string? quoteNumber,
[FromQuery] int? professionalId,
[FromQuery] int? institutionId,
[FromQuery] int? patientId,
[FromQuery] DateTime? issueDateFrom,
[FromQuery] DateTime? issueDateTo,
[FromQuery] string? status,
[FromQuery] int page = 1,
[FromQuery] int pageSize = 50)
{
try
{
var result = await _quoteService.SearchQuotesAsync(
customerId,
quoteNumber,
professionalId,
institutionId,
patientId,
issueDateFrom,
issueDateTo,
status,
page,
pageSize);
return Ok(result);
}
catch (Exception ex)
{
var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod";
return StatusCode(500, $"{methodName} Message: {ex.Message}");
}
}
[HttpGet("{id:int}")]
public async Task<ActionResult<EQuoteHeader>> GetById(int id)
{
try
{
var quote = await _quoteService.GetQuoteByIdAsync(id);
if (quote == null)
return NotFound();
return Ok(quote);
}
catch (Exception ex)
{
var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod";
return StatusCode(500, $"{methodName} Message: {ex.Message}");
}
}
[HttpPost("create")]
public async Task<IActionResult> Create([FromBody] EQuoteHeader quote, [FromQuery] int formSeriesId)
{
try
{
if (quote == null)
return BadRequest("El presupuesto no puede ser nulo.");
var result = await _quoteService.CreateQuoteAsync(quote, formSeriesId);
return Ok(result);
}
catch (ArgumentNullException ex)
{
return BadRequest($"Validación fallida: {ex.Message}");
}
catch (InvalidOperationException ex)
{
return BadRequest($"Error de negocio: {ex.Message}");
}
catch (Exception ex)
{
var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod";
return StatusCode(500, $"{methodName} Message: {ex.Message}");
}
}
[HttpPut("update")]
public async Task<IActionResult> Update([FromBody] EQuoteHeader quote)
{
try
{
if (quote == null || quote.Id <= 0)
return BadRequest("El presupuesto es inválido o no tiene un ID válido.");
await _quoteService.UpdateQuoteAsync(quote);
return Ok("Presupuesto actualizado correctamente.");
}
catch (Exception ex)
{
var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod";
return StatusCode(500, $"{methodName} Message: {ex.Message}");
}
}
[HttpDelete("delete/{id:int}")]
public async Task<IActionResult> Delete(int id)
{
try
{
await _quoteService.DeleteQuoteAsync(id);
return Ok("Presupuesto eliminado correctamente.");
}
catch (Exception ex)
{
var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod";
return StatusCode(500, $"{methodName} Message: {ex.Message}");
}
}
[HttpPost("exportfiltered")]
public async Task<IActionResult> ExportFiltered([FromBody] QuoteSearchParams searchParams)
{
try
{
var file = await _quoteService.ExportFilteredQuotesToExcelAsync(searchParams);
return File(file,
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"Presupuestos.xlsx");
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
}
}

View File

@ -15,6 +15,8 @@ using Services.Services;
using Services.Interfaces; using Services.Interfaces;
using System.Text; using System.Text;
using Infrastructure.Repositories.Patients; using Infrastructure.Repositories.Patients;
using PhronCare.Core.Services.Sales;
using PhronCare.Core.Data.Repositories.Sales;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@ -70,6 +72,12 @@ builder.Services.AddScoped<IPhSProfessionalRepository, PhSProfessionalRepository
builder.Services.AddScoped<IInstitutionDom, InstitutionService>(); builder.Services.AddScoped<IInstitutionDom, InstitutionService>();
builder.Services.AddScoped<IPhSInstitutionRepository, PhSInstitutionRepository>(); builder.Services.AddScoped<IPhSInstitutionRepository, PhSInstitutionRepository>();
builder.Services.AddScoped<IQuoteDom, QuoteService>();
builder.Services.AddScoped<IPhSQuoteHeaderRepository, PhSQuoteHeaderRepository>();
builder.Services.AddScoped<IPhSQuoteDetailRepository, PhSQuoteDetailRepository>();
builder.Services.AddScoped<IPhSQuoteRoleRepository, PhSQuoteRoleRepository>();
builder.Services.AddScoped<IPhSFormSeriesRepository, PhSFormSeriesRepository>();
#endregion #endregion
#region Require Confirmed Email #region Require Confirmed Email

View File

@ -1206,6 +1206,183 @@
], ],
"ReturnTypes": [] "ReturnTypes": []
}, },
{
"ContainingType": "phronCare.API.Controllers.Sales.QuoteController",
"Method": "GetById",
"RelativePath": "api/Quote/{id}",
"HttpMethod": "GET",
"IsController": true,
"Order": 0,
"Parameters": [
{
"Name": "id",
"Type": "System.Int32",
"IsRequired": true
}
],
"ReturnTypes": [
{
"Type": "Domain.Entities.EQuoteHeader",
"MediaTypes": [
"text/plain",
"application/json",
"text/json"
],
"StatusCode": 200
}
]
},
{
"ContainingType": "phronCare.API.Controllers.Sales.QuoteController",
"Method": "GetAll",
"RelativePath": "api/Quote/all",
"HttpMethod": "GET",
"IsController": true,
"Order": 0,
"Parameters": [
{
"Name": "page",
"Type": "System.Int32",
"IsRequired": false
},
{
"Name": "pageSize",
"Type": "System.Int32",
"IsRequired": false
}
],
"ReturnTypes": []
},
{
"ContainingType": "phronCare.API.Controllers.Sales.QuoteController",
"Method": "Create",
"RelativePath": "api/Quote/create",
"HttpMethod": "POST",
"IsController": true,
"Order": 0,
"Parameters": [
{
"Name": "quote",
"Type": "Domain.Entities.EQuoteHeader",
"IsRequired": true
},
{
"Name": "formSeriesId",
"Type": "System.Int32",
"IsRequired": false
}
],
"ReturnTypes": []
},
{
"ContainingType": "phronCare.API.Controllers.Sales.QuoteController",
"Method": "Delete",
"RelativePath": "api/Quote/delete/{id}",
"HttpMethod": "DELETE",
"IsController": true,
"Order": 0,
"Parameters": [
{
"Name": "id",
"Type": "System.Int32",
"IsRequired": true
}
],
"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",
"Method": "Search",
"RelativePath": "api/Quote/search",
"HttpMethod": "GET",
"IsController": true,
"Order": 0,
"Parameters": [
{
"Name": "customerId",
"Type": "System.Nullable\u00601[[System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]",
"IsRequired": false
},
{
"Name": "quoteNumber",
"Type": "System.String",
"IsRequired": false
},
{
"Name": "professionalId",
"Type": "System.Nullable\u00601[[System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]",
"IsRequired": false
},
{
"Name": "institutionId",
"Type": "System.Nullable\u00601[[System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]",
"IsRequired": false
},
{
"Name": "patientId",
"Type": "System.Nullable\u00601[[System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]",
"IsRequired": false
},
{
"Name": "issueDateFrom",
"Type": "System.Nullable\u00601[[System.DateTime, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]",
"IsRequired": false
},
{
"Name": "issueDateTo",
"Type": "System.Nullable\u00601[[System.DateTime, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]",
"IsRequired": false
},
{
"Name": "status",
"Type": "System.String",
"IsRequired": false
},
{
"Name": "page",
"Type": "System.Int32",
"IsRequired": false
},
{
"Name": "pageSize",
"Type": "System.Int32",
"IsRequired": false
}
],
"ReturnTypes": []
},
{
"ContainingType": "phronCare.API.Controllers.Sales.QuoteController",
"Method": "Update",
"RelativePath": "api/Quote/update",
"HttpMethod": "PUT",
"IsController": true,
"Order": 0,
"Parameters": [
{
"Name": "quote",
"Type": "Domain.Entities.EQuoteHeader",
"IsRequired": true
}
],
"ReturnTypes": []
},
{ {
"ContainingType": "phronCare.API.Controllers.Sales.TaxConditionController", "ContainingType": "phronCare.API.Controllers.Sales.TaxConditionController",
"Method": "GetAll", "Method": "GetAll",