Update Quote and FormSeries Add
All checks were successful
CI/CD Pipeline / Build and Deploy with Docker Compose (push) Successful in 5m20s
All checks were successful
CI/CD Pipeline / Build and Deploy with Docker Compose (push) Successful in 5m20s
This commit is contained in:
parent
9940189a93
commit
2014218ad0
21
Core/Interfaces/IQuoteDom.cs
Normal file
21
Core/Interfaces/IQuoteDom.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
151
Core/Services/QuoteService.cs
Normal file
151
Core/Services/QuoteService.cs
Normal 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
|
||||
}
|
||||
}
|
||||
66
Domain/Entities/EQuoteDetail.cs
Normal file
66
Domain/Entities/EQuoteDetail.cs
Normal 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!;
|
||||
}
|
||||
}
|
||||
93
Domain/Entities/EQuoteHeader.cs
Normal file
93
Domain/Entities/EQuoteHeader.cs
Normal 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>();
|
||||
}
|
||||
}
|
||||
36
Domain/Entities/EQuoteRole.cs
Normal file
36
Domain/Entities/EQuoteRole.cs
Normal 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!;
|
||||
}
|
||||
}
|
||||
14
Domain/Generics/QuoteSearchParams.cs
Normal file
14
Domain/Generics/QuoteSearchParams.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
7
Models/Interfaces/IPhSFormSeriesRepository.cs
Normal file
7
Models/Interfaces/IPhSFormSeriesRepository.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Models.Interfaces
|
||||
{
|
||||
public interface IPhSFormSeriesRepository
|
||||
{
|
||||
Task<int> GetNextInternalNumberAsync(int formSeriesId);
|
||||
}
|
||||
}
|
||||
14
Models/Interfaces/IPhSQuoteDetailRepository.cs
Normal file
14
Models/Interfaces/IPhSQuoteDetailRepository.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
16
Models/Interfaces/IPhSQuoteHeaderRepository.cs
Normal file
16
Models/Interfaces/IPhSQuoteHeaderRepository.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
14
Models/Interfaces/IPhSQuoteRoleRepository.cs
Normal file
14
Models/Interfaces/IPhSQuoteRoleRepository.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
35
Models/Repositories/PhSFormSeriesRepository.cs
Normal file
35
Models/Repositories/PhSFormSeriesRepository.cs
Normal 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
|
||||
}
|
||||
}
|
||||
63
Models/Repositories/PhSQuoteDetailRepository.cs
Normal file
63
Models/Repositories/PhSQuoteDetailRepository.cs
Normal 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
|
||||
}
|
||||
}
|
||||
137
Models/Repositories/PhSQuoteHeaderRepository.cs
Normal file
137
Models/Repositories/PhSQuoteHeaderRepository.cs
Normal 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
|
||||
}
|
||||
}
|
||||
62
Models/Repositories/PhSQuoteRoleRepository.cs
Normal file
62
Models/Repositories/PhSQuoteRoleRepository.cs
Normal 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
|
||||
}
|
||||
}
|
||||
165
phronCare.API/Controllers/Sales/QuoteController.cs
Normal file
165
phronCare.API/Controllers/Sales/QuoteController.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -15,6 +15,8 @@ using Services.Services;
|
||||
using Services.Interfaces;
|
||||
using System.Text;
|
||||
using Infrastructure.Repositories.Patients;
|
||||
using PhronCare.Core.Services.Sales;
|
||||
using PhronCare.Core.Data.Repositories.Sales;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
@ -70,6 +72,12 @@ builder.Services.AddScoped<IPhSProfessionalRepository, PhSProfessionalRepository
|
||||
|
||||
builder.Services.AddScoped<IInstitutionDom, InstitutionService>();
|
||||
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
|
||||
|
||||
#region Require Confirmed Email
|
||||
|
||||
@ -1206,6 +1206,183 @@
|
||||
],
|
||||
"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",
|
||||
"Method": "GetAll",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user