Update Model Quote and Adjustment in API
All checks were successful
CI/CD Pipeline / Build and Deploy with Docker Compose (push) Successful in 5m24s

This commit is contained in:
Leandro Hernan Rojas 2025-05-01 17:59:59 -03:00
parent 5c2489b168
commit 6476cfd701
13 changed files with 484 additions and 86 deletions

View File

@ -1,5 +1,6 @@
using Domain.Entities;
using Domain.Generics;
using Models.Helpers;
using Models.Interfaces;
using System.Reflection;
using Transversal.Services;
@ -20,7 +21,7 @@ namespace PhronCare.Core.Services.Sales
private readonly IPhSFormSeriesRepository _formSeriesRepository = formSeriesRepository;
#endregion
#region Métodos
#region Presupuestos
public async Task<PagedResult<EQuoteHeader>> GetAllQuotesAsync(int page = 1, int pageSize = 50)
{
return await _quoteHeaderRepository.GetAllAsync(page, pageSize);
@ -33,12 +34,23 @@ namespace PhronCare.Core.Services.Sales
public async Task<IEnumerable<EQuoteHeader>> GetQuotesByCustomerAsync(int customerId)
{
var quotes = await _quoteHeaderRepository.GetByCustomerIdAsync(customerId);
return quotes;
return await _quoteHeaderRepository.GetByCustomerIdAsync(customerId);
}
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)
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,
return await _quoteHeaderRepository.SearchAsync(
customerId,
quoteNumber,
professionalId,
institutionId,
@ -54,14 +66,12 @@ namespace PhronCare.Core.Services.Sales
{
// 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
// Crear encabezado
var newQuote = await _quoteHeaderRepository.AddAsync(quote);
// Crear los detalles asociados
// Crear detalles asociados
if (quote.PhSQuoteDetails != null)
{
foreach (var detail in quote.PhSQuoteDetails)
@ -71,7 +81,7 @@ namespace PhronCare.Core.Services.Sales
}
}
// Crear los roles asociados
// Crear roles asociados
if (quote.PhSQuoteRoles != null)
{
foreach (var role in quote.PhSQuoteRoles)
@ -93,12 +103,35 @@ namespace PhronCare.Core.Services.Sales
{
await _quoteHeaderRepository.DeleteAsync(id);
}
#endregion
#region Ajustes
public async Task<IEnumerable<EQuoteAdjustment>> GetAdjustmentsByQuoteIdAsync(int quoteId)
{
return await _quoteHeaderRepository.GetAdjustmentsByQuoteIdAsync(quoteId);
}
public async Task<EQuoteAdjustment> AddAdjustmentAsync(EQuoteAdjustment adjustment)
{
return await _quoteHeaderRepository.AddAdjustmentAsync(adjustment);
}
public async Task UpdateAdjustmentAsync(EQuoteAdjustment adjustment)
{
await _quoteHeaderRepository.UpdateAdjustmentAsync(adjustment);
}
public async Task DeleteAdjustmentAsync(int adjustmentId)
{
await _quoteHeaderRepository.DeleteAdjustmentAsync(adjustmentId);
}
#endregion
#region Exportación
public async Task<byte[]> ExportFilteredQuotesToExcelAsync(QuoteSearchParams searchParams)
{
try
{
// Buscar los presupuestos con los filtros indicados
var searchResult = await SearchQuotesAsync(
searchParams.CustomerId,
searchParams.QuoteNumber,
@ -109,36 +142,28 @@ namespace PhronCare.Core.Services.Sales
searchParams.IssueDateTo,
searchParams.Status,
searchParams.Page,
searchParams.PageSize
);
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.PhSQuoteRoles.FirstOrDefault(r => r.Entitytype == "PhS_Professionals")?.Entitytype,
Institución = q.PhSQuoteRoles.FirstOrDefault(r => r.Entitytype == "PhS_Institutions")?.Entitytype,
Paciente = q.PhSQuoteRoles.FirstOrDefault(r => r.Entitytype == "PhS_Patients")?.Entitytype
Profesional = q.PhSQuoteRoles.FirstOrDefault(r => r.Entitytype == PhSEntityTypes.Professional)?.Entitytype,
Institución = q.PhSQuoteRoles.FirstOrDefault(r => r.Entitytype == PhSEntityTypes.Institution)?.Entitytype,
Paciente = q.PhSQuoteRoles.FirstOrDefault(r => r.Entitytype == PhSEntityTypes.Patient)?.Entitytype
}).ToList();
// Generar archivo Excel
var excelFile = stream.ExportExcel(quotesData);
return excelFile;
return stream.ExportExcel(quotesData);
}
catch (Exception ex)
{

View File

@ -0,0 +1,42 @@
namespace Domain.Entities
{
/// <summary>
/// Tabla de ajustes aplicados a presupuestos comerciales
/// </summary>
public class EQuoteAdjustment
{
/// <summary>
/// ID interno del ajuste aplicado al presupuesto
/// </summary>
public int Id { get; set; }
/// <summary>
/// FK al presupuesto (PhS_QuoteHeaders)
/// </summary>
public int QuoteheaderId { get; set; }
/// <summary>
/// Código del motivo de ajuste (FK a PhS_AdjustmentReasons)
/// </summary>
public string ReasonCode { get; set; } = null!;
/// <summary>
/// Importe del ajuste realizado (positivo o nulo)
/// </summary>
public decimal? Amount { get; set; }
/// <summary>
/// Descripción adicional del ajuste
/// </summary>
public string? Description { get; set; }
/// <summary>
/// Fecha de registro del ajuste
/// </summary>
public DateTime Createdat { get; set; }
//public virtual PhSQuoteHeader Quoteheader { get; set; } = null!;
//public virtual PhSAdjustmentReason ReasonCodeNavigation { get; set; } = null!;
}
}

View File

@ -3,6 +3,7 @@
/// <summary>
/// Tabla de cabeceras de presupuestos
/// </summary>
///
public class EQuoteHeader
{
/// <summary>
@ -15,6 +16,11 @@
/// </summary>
public Guid TicketId { get; set; }
/// <summary>
/// Número visible del presupuesto
/// </summary>
public string Quotenumber { get; set; } = null!;
/// <summary>
/// Cliente asociado
/// </summary>
@ -30,11 +36,6 @@
/// </summary>
public int PeopleId { 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>
@ -51,9 +52,24 @@
public DateTime? Estimateddate { get; set; }
/// <summary>
/// Importe estimado total
/// Código de moneda pactada (ISO 4217). Ej: ARS, USD
/// </summary>
public decimal? Estimatedamount { get; set; }
public string Currency { get; set; } = null!;
/// <summary>
/// Tipo de cambio pactado para conversión a pesos argentinos
/// </summary>
public decimal? Exchangerate { get; set; }
/// <summary>
/// Total del presupuesto expresado en moneda extranjera
/// </summary>
public decimal? TotalForeign { get; set; }
/// <summary>
/// Total final del presupuesto expresado en moneda local, considerando ajustes o acuerdos comerciales
/// </summary>
public decimal? Total { get; set; }
/// <summary>
/// Importe aprobado
@ -61,9 +77,19 @@
public decimal? Approvedamount { get; set; }
/// <summary>
/// Número visible del presupuesto
/// Estado: E (Emitido), A (Aprobado), AC (Aprobado para cirugia), etc.
/// </summary>
public string Quotenumber { get; set; } = null!;
public string Status { get; set; } = null!;
/// <summary>
/// Indica si la cirugía se realizará fuera de la ciudad/localidad habitual (“out of town”)
/// </summary>
public bool OutOfTown { get; set; }
/// <summary>
/// Instrucción dirigida al área de logística para detallar qué debe prepararse o despacharse (ej: “CMF 1.5 + INSTRUMENTAL”)
/// </summary>
public string? DispatchInstruction { get; set; }
/// <summary>
/// Cantidad de impresiones
@ -85,9 +111,98 @@
/// </summary>
public DateTime? Modifiedat { get; set; }
public virtual ICollection<EQuoteAdjustment> PhSQuoteAdjustments { get; set; } = new List<EQuoteAdjustment>();
public virtual ICollection<EQuoteDetail> PhSQuoteDetails { get; set; } = new List<EQuoteDetail>();
public virtual ICollection<EQuoteRole> PhSQuoteRoles { get; set; } = new List<EQuoteRole>();
}
//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>
// /// Identificador único del vendedor
// /// </summary>
// public int PeopleId { 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> PhSQuoteDetails { get; set; } = new List<EQuoteDetail>();
// public virtual ICollection<EQuoteRole> PhSQuoteRoles { get; set; } = new List<EQuoteRole>();
//}
}

View File

@ -7,7 +7,7 @@
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\maski\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.13.2</NuGetToolVersion>
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.13.1</NuGetToolVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<SourceRoot Include="C:\Users\maski\.nuget\packages\" />

View File

@ -0,0 +1,9 @@
namespace Models.Helpers
{
public static class PhSEntityTypes
{
public const string Professional = "PhS_Professionals";
public const string Institution = "PhS_Institutions";
public const string Patient = "PhS_Patients";
}
}

View File

@ -6,11 +6,20 @@ 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<IEnumerable<EQuoteHeader>> GetByCustomerIdAsync(int customerId);
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);
// Ajustes
Task<IEnumerable<EQuoteAdjustment>> GetAdjustmentsByQuoteIdAsync(int quoteId);
Task<EQuoteAdjustment> AddAdjustmentAsync(EQuoteAdjustment adjustment);
Task UpdateAdjustmentAsync(EQuoteAdjustment adjustment);
Task DeleteAdjustmentAsync(int adjustmentId);
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
namespace Models.Models;
public partial class PhSAdjustmentReason
{
public string Code { get; set; } = null!;
public string Description { get; set; } = null!;
public bool Active { get; set; }
public DateTime Createdat { get; set; }
public virtual ICollection<PhSQuoteAdjustment> PhSQuoteAdjustments { get; set; } = new List<PhSQuoteAdjustment>();
}

View File

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
namespace Models.Models;
/// <summary>
/// Tabla de ajustes aplicados a presupuestos comerciales
/// </summary>
public partial class PhSQuoteAdjustment
{
/// <summary>
/// ID interno del ajuste aplicado al presupuesto
/// </summary>
public int Id { get; set; }
/// <summary>
/// FK al presupuesto (PhS_QuoteHeaders)
/// </summary>
public int QuoteheaderId { get; set; }
/// <summary>
/// Código del motivo de ajuste (FK a PhS_AdjustmentReasons)
/// </summary>
public string ReasonCode { get; set; } = null!;
/// <summary>
/// Importe del ajuste realizado (positivo o nulo)
/// </summary>
public decimal? Amount { get; set; }
/// <summary>
/// Descripción adicional del ajuste
/// </summary>
public string? Description { get; set; }
/// <summary>
/// Fecha de registro del ajuste
/// </summary>
public DateTime Createdat { get; set; }
public virtual PhSQuoteHeader Quoteheader { get; set; } = null!;
public virtual PhSAdjustmentReason ReasonCodeNavigation { get; set; } = null!;
}

View File

@ -18,6 +18,11 @@ public partial class PhSQuoteHeader
/// </summary>
public Guid TicketId { get; set; }
/// <summary>
/// Número visible del presupuesto
/// </summary>
public string Quotenumber { get; set; } = null!;
/// <summary>
/// Cliente asociado
/// </summary>
@ -33,11 +38,6 @@ public partial class PhSQuoteHeader
/// </summary>
public int PeopleId { 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>
@ -54,9 +54,24 @@ public partial class PhSQuoteHeader
public DateTime? Estimateddate { get; set; }
/// <summary>
/// Importe estimado total
/// Código de moneda pactada (ISO 4217). Ej: ARS, USD
/// </summary>
public decimal? Estimatedamount { get; set; }
public string Currency { get; set; } = null!;
/// <summary>
/// Tipo de cambio pactado para conversión a pesos argentinos
/// </summary>
public decimal? Exchangerate { get; set; }
/// <summary>
/// Total del presupuesto expresado en moneda extranjera
/// </summary>
public decimal? TotalForeign { get; set; }
/// <summary>
/// Total final del presupuesto expresado en moneda local, considerando ajustes o acuerdos comerciales
/// </summary>
public decimal? Total { get; set; }
/// <summary>
/// Importe aprobado
@ -64,9 +79,19 @@ public partial class PhSQuoteHeader
public decimal? Approvedamount { get; set; }
/// <summary>
/// Número visible del presupuesto
/// Estado: E (Emitido), A (Aprobado), AC (Aprobado para cirugia), etc.
/// </summary>
public string Quotenumber { get; set; } = null!;
public string Status { get; set; } = null!;
/// <summary>
/// Indica si la cirugía se realizará fuera de la ciudad/localidad habitual (“out of town”)
/// </summary>
public bool OutOfTown { get; set; }
/// <summary>
/// Instrucción dirigida al área de logística para detallar qué debe prepararse o despacharse (ej: “CMF 1.5 + INSTRUMENTAL”)
/// </summary>
public string? DispatchInstruction { get; set; }
/// <summary>
/// Cantidad de impresiones
@ -88,6 +113,8 @@ public partial class PhSQuoteHeader
/// </summary>
public DateTime? Modifiedat { get; set; }
public virtual ICollection<PhSQuoteAdjustment> PhSQuoteAdjustments { get; set; } = new List<PhSQuoteAdjustment>();
public virtual ICollection<PhSQuoteDetail> PhSQuoteDetails { get; set; } = new List<PhSQuoteDetail>();
public virtual ICollection<PhSQuoteRole> PhSQuoteRoles { get; set; } = new List<PhSQuoteRole>();

View File

@ -23,6 +23,8 @@ public partial class PhronCareOperationsHubContext : DbContext
public virtual DbSet<PhSAccountType> PhSAccountTypes { get; set; }
public virtual DbSet<PhSAdjustmentReason> PhSAdjustmentReasons { get; set; }
public virtual DbSet<PhSBusinessUnit> PhSBusinessUnits { get; set; }
public virtual DbSet<PhSCustomer> PhSCustomers { get; set; }
@ -55,6 +57,8 @@ public partial class PhronCareOperationsHubContext : DbContext
public virtual DbSet<PhSProfessionalSpecialty> PhSProfessionalSpecialties { get; set; }
public virtual DbSet<PhSQuoteAdjustment> PhSQuoteAdjustments { get; set; }
public virtual DbSet<PhSQuoteDetail> PhSQuoteDetails { get; set; }
public virtual DbSet<PhSQuoteHeader> PhSQuoteHeaders { get; set; }
@ -62,8 +66,15 @@ public partial class PhronCareOperationsHubContext : DbContext
public virtual DbSet<PhSQuoteRole> PhSQuoteRoles { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263.
=> optionsBuilder.UseSqlServer("data source=srv01.saludlab.com.ar,39458;initial catalog=phronCare_OperationsHub;User ID=sa;Password=HS|s[~xxQzTo/n>9jO;encrypt=False;trustServerCertificate=True;MultipleActiveResultSets=True");
#region VERSION DOCKER
{
if (!optionsBuilder.IsConfigured)
{
// Dejarlo vacío para usar la configuración externa desde Program.cs o Startup.cs
}
}
#endregion
// => optionsBuilder.UseSqlServer("data source=srv01.saludlab.com.ar,39458;initial catalog=phronCare_OperationsHub;User ID=sa;Password=HS|s[~xxQzTo/n>9jO;encrypt=False;trustServerCertificate=True;MultipleActiveResultSets=True");
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
@ -152,6 +163,28 @@ public partial class PhronCareOperationsHubContext : DbContext
.HasColumnName("name");
});
modelBuilder.Entity<PhSAdjustmentReason>(entity =>
{
entity.HasKey(e => e.Code).HasName("PK__PhS_Adju__357D4CF809676D8C");
entity.ToTable("PhS_AdjustmentReasons");
entity.Property(e => e.Code)
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnName("code");
entity.Property(e => e.Active)
.HasDefaultValue(true)
.HasColumnName("active");
entity.Property(e => e.Createdat)
.HasDefaultValueSql("(getdate())")
.HasColumnType("datetime")
.HasColumnName("createdat");
entity.Property(e => e.Description)
.HasMaxLength(100)
.HasColumnName("description");
});
modelBuilder.Entity<PhSBusinessUnit>(entity =>
{
entity.ToTable("PhS_BusinessUnits");
@ -695,6 +728,48 @@ public partial class PhronCareOperationsHubContext : DbContext
.HasColumnName("name");
});
modelBuilder.Entity<PhSQuoteAdjustment>(entity =>
{
entity.HasKey(e => e.Id).HasName("PK__PhS_Quot__3213E83FFBF7D244");
entity.ToTable("PhS_QuoteAdjustments", tb => tb.HasComment("Tabla de ajustes aplicados a presupuestos comerciales"));
entity.Property(e => e.Id)
.HasComment("ID interno del ajuste aplicado al presupuesto")
.HasColumnName("id");
entity.Property(e => e.Amount)
.HasComment("Importe del ajuste realizado (positivo o nulo)")
.HasColumnType("decimal(18, 2)")
.HasColumnName("amount");
entity.Property(e => e.Createdat)
.HasDefaultValueSql("(getdate())")
.HasComment("Fecha de registro del ajuste")
.HasColumnType("datetime")
.HasColumnName("createdat");
entity.Property(e => e.Description)
.HasMaxLength(255)
.HasComment("Descripción adicional del ajuste")
.HasColumnName("description");
entity.Property(e => e.QuoteheaderId)
.HasComment("FK al presupuesto (PhS_QuoteHeaders)")
.HasColumnName("quoteheader_id");
entity.Property(e => e.ReasonCode)
.HasMaxLength(50)
.IsUnicode(false)
.HasComment("Código del motivo de ajuste (FK a PhS_AdjustmentReasons)")
.HasColumnName("reason_code");
entity.HasOne(d => d.Quoteheader).WithMany(p => p.PhSQuoteAdjustments)
.HasForeignKey(d => d.QuoteheaderId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_QuoteAdjustments_Header");
entity.HasOne(d => d.ReasonCodeNavigation).WithMany(p => p.PhSQuoteAdjustments)
.HasForeignKey(d => d.ReasonCode)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_QuoteAdjustments_Reason");
});
modelBuilder.Entity<PhSQuoteDetail>(entity =>
{
entity.HasKey(e => e.Id).HasName("PK__PhS_Quot__3213E83F94207114");
@ -773,17 +848,27 @@ public partial class PhronCareOperationsHubContext : DbContext
.HasComment("Fecha de creación")
.HasColumnType("datetime")
.HasColumnName("createdat");
entity.Property(e => e.Currency)
.HasMaxLength(3)
.IsUnicode(false)
.HasDefaultValue("ARS")
.HasComment("Código de moneda pactada (ISO 4217). Ej: ARS, USD")
.HasColumnName("currency");
entity.Property(e => e.CustomerId)
.HasComment("Cliente asociado")
.HasColumnName("customer_id");
entity.Property(e => e.Estimatedamount)
.HasComment("Importe estimado total")
.HasColumnType("decimal(18, 2)")
.HasColumnName("estimatedamount");
entity.Property(e => e.DispatchInstruction)
.HasMaxLength(255)
.HasComment("Instrucción dirigida al área de logística para detallar qué debe prepararse o despacharse (ej: “CMF 1.5 + INSTRUMENTAL”)")
.HasColumnName("dispatch_instruction");
entity.Property(e => e.Estimateddate)
.HasComment("Fecha tentativa (de cirugía por ej.)")
.HasColumnType("datetime")
.HasColumnName("estimateddate");
entity.Property(e => e.Exchangerate)
.HasComment("Tipo de cambio pactado para conversión a pesos argentinos")
.HasColumnType("decimal(18, 6)")
.HasColumnName("exchangerate");
entity.Property(e => e.Issuedate)
.HasDefaultValueSql("(getdate())")
.HasComment("Fecha de emisión")
@ -796,6 +881,9 @@ public partial class PhronCareOperationsHubContext : DbContext
entity.Property(e => e.Observations)
.HasComment("Observaciones internas")
.HasColumnName("observations");
entity.Property(e => e.OutOfTown)
.HasComment("Indica si la cirugía se realizará fuera de la ciudad/localidad habitual (“out of town”)")
.HasColumnName("out_of_town");
entity.Property(e => e.PeopleId)
.HasDefaultValue(1)
.HasComment("Identificador único del vendedor")
@ -818,6 +906,14 @@ public partial class PhronCareOperationsHubContext : DbContext
entity.Property(e => e.TicketId)
.HasComment("Relación con Tickets")
.HasColumnName("ticket_id");
entity.Property(e => e.Total)
.HasComment("Total final del presupuesto expresado en moneda local, considerando ajustes o acuerdos comerciales")
.HasColumnType("decimal(18, 2)")
.HasColumnName("total");
entity.Property(e => e.TotalForeign)
.HasComment("Total del presupuesto expresado en moneda extranjera")
.HasColumnType("decimal(18, 2)")
.HasColumnName("total_foreign");
});
modelBuilder.Entity<PhSQuoteRole>(entity =>

View File

@ -7,18 +7,16 @@ using Models.Models;
namespace PhronCare.Core.Data.Repositories.Sales
{
public class PhSQuoteHeaderRepository(PhronCareOperationsHubContext context): IPhSQuoteHeaderRepository
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();
.Include(q => q.PhSQuoteAdjustments)
.AsNoTracking();
var pagedEntities = await query.ToPagedResultAsync(page, pageSize);
@ -35,6 +33,7 @@ namespace PhronCare.Core.Data.Repositories.Sales
var entity = await _context.PhSQuoteHeaders
.Include(q => q.PhSQuoteDetails)
.Include(q => q.PhSQuoteRoles)
.Include(q => q.PhSQuoteAdjustments)
.FirstOrDefaultAsync(q => q.Id == id);
return entity != null ? EntityMapper.MapEntity<PhSQuoteHeader, EQuoteHeader>(entity) : null;
@ -45,61 +44,46 @@ namespace PhronCare.Core.Data.Repositories.Sales
.Where(q => q.CustomerId == customerId)
.Include(q => q.PhSQuoteDetails)
.Include(q => q.PhSQuoteRoles)
.Include(q => q.PhSQuoteAdjustments)
.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)
DateTime? issueDateFrom, DateTime? issueDateTo, string? status,
int page = 1, int pageSize = 50)
{
var query = _context.PhSQuoteHeaders
.Include(q => q.PhSQuoteDetails)
.Include(q => q.PhSQuoteRoles)
.Include(q => q.PhSQuoteAdjustments)
.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));
}
query = query.Where(q => q.PhSQuoteRoles.Any(r => r.Entitytype == PhSEntityTypes.Professional && r.EntityId == professionalId));
if (institutionId.HasValue)
{
query = query.Where(q => q.PhSQuoteRoles.Any(r => r.Entitytype == "PhS_Institutions" && r.EntityId == institutionId));
}
query = query.Where(q => q.PhSQuoteRoles.Any(r => r.Entitytype == PhSEntityTypes.Institution && r.EntityId == institutionId));
if (patientId.HasValue)
{
query = query.Where(q => q.PhSQuoteRoles.Any(r => r.Entitytype == "PhS_Patients" && r.EntityId == patientId));
}
query = query.Where(q => q.PhSQuoteRoles.Any(r => r.Entitytype == PhSEntityTypes.Patient && 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>
@ -132,6 +116,39 @@ namespace PhronCare.Core.Data.Repositories.Sales
await _context.SaveChangesAsync();
}
}
#endregion
// ----------------------------
// Métodos para Ajustes
// ----------------------------
public async Task<IEnumerable<EQuoteAdjustment>> GetAdjustmentsByQuoteIdAsync(int quoteId)
{
var adjustments = await _context.PhSQuoteAdjustments
.Where(a => a.QuoteheaderId == quoteId)
.ToListAsync();
return adjustments.Select(EntityMapper.MapEntity<PhSQuoteAdjustment, EQuoteAdjustment>);
}
public async Task<EQuoteAdjustment> AddAdjustmentAsync(EQuoteAdjustment adjustment)
{
var dbEntity = EntityMapper.MapEntity<EQuoteAdjustment, PhSQuoteAdjustment>(adjustment);
_context.PhSQuoteAdjustments.Add(dbEntity);
await _context.SaveChangesAsync();
return EntityMapper.MapEntity<PhSQuoteAdjustment, EQuoteAdjustment>(dbEntity);
}
public async Task UpdateAdjustmentAsync(EQuoteAdjustment adjustment)
{
var dbEntity = EntityMapper.MapEntity<EQuoteAdjustment, PhSQuoteAdjustment>(adjustment);
_context.PhSQuoteAdjustments.Update(dbEntity);
await _context.SaveChangesAsync();
}
public async Task DeleteAdjustmentAsync(int adjustmentId)
{
var entity = await _context.PhSQuoteAdjustments.FindAsync(adjustmentId);
if (entity != null)
{
_context.PhSQuoteAdjustments.Remove(entity);
await _context.SaveChangesAsync();
}
}
}
}

View File

@ -7,7 +7,7 @@
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\maski\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.13.2</NuGetToolVersion>
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.13.1</NuGetToolVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<SourceRoot Include="C:\Users\maski\.nuget\packages\" />

View File

@ -42,19 +42,16 @@
SuccessIcon="fa fa-thumbs-up"
ErrorIcon="fa fa-ban" />
@code {
private bool state=false;
private string MinimizeMenuCss => navMenuService.Minimized ? "width:82px" : string.Empty;
protected void ToggleMinNavMenu(bool status)
{
navMenuService.Minimized = status;
}
private bool state=false;
private void OnChangeToggleSwitchState(bool value)
{
state = value;
}
private string GetMode()
{
if (state )