From 6476cfd701854e68e2b09edbcf17ea24b6d2309f Mon Sep 17 00:00:00 2001 From: Leandro Hernan Rojas Date: Thu, 1 May 2025 17:59:59 -0300 Subject: [PATCH] Update Model Quote and Adjustment in API --- Core/Services/QuoteService.cs | 73 ++++++---- Domain/Entities/EQuoteAdjustment.cs | 42 ++++++ Domain/Entities/EQuoteHeader.cs | 135 ++++++++++++++++-- Domain/obj/Domain.csproj.nuget.g.props | 2 +- Models/Helpers/PhSEntityTypes.cs | 9 ++ .../Interfaces/IPhSQuoteHeaderRepository.cs | 13 +- Models/Models/PhSAdjustmentReason.cs | 17 +++ Models/Models/PhSQuoteAdjustment.cs | 44 ++++++ Models/Models/PhSQuoteHeader.cs | 45 ++++-- .../Models/PhronCareOperationsHubContext.cs | 108 +++++++++++++- .../Repositories/PhSQuoteHeaderRepository.cs | 75 ++++++---- Models/obj/Models.csproj.nuget.g.props | 2 +- phronCare.UIBlazor/Shared/MainLayout.razor | 5 +- 13 files changed, 484 insertions(+), 86 deletions(-) create mode 100644 Domain/Entities/EQuoteAdjustment.cs create mode 100644 Models/Helpers/PhSEntityTypes.cs create mode 100644 Models/Models/PhSAdjustmentReason.cs create mode 100644 Models/Models/PhSQuoteAdjustment.cs diff --git a/Core/Services/QuoteService.cs b/Core/Services/QuoteService.cs index 94fdc68..8ae0a1c 100644 --- a/Core/Services/QuoteService.cs +++ b/Core/Services/QuoteService.cs @@ -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> 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> GetQuotesByCustomerAsync(int customerId) { - var quotes = await _quoteHeaderRepository.GetByCustomerIdAsync(customerId); - return quotes; + return await _quoteHeaderRepository.GetByCustomerIdAsync(customerId); } - public async Task> 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> 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> GetAdjustmentsByQuoteIdAsync(int quoteId) + { + return await _quoteHeaderRepository.GetAdjustmentsByQuoteIdAsync(quoteId); + } + + public async Task 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 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) { diff --git a/Domain/Entities/EQuoteAdjustment.cs b/Domain/Entities/EQuoteAdjustment.cs new file mode 100644 index 0000000..42ba630 --- /dev/null +++ b/Domain/Entities/EQuoteAdjustment.cs @@ -0,0 +1,42 @@ +namespace Domain.Entities +{ + /// + /// Tabla de ajustes aplicados a presupuestos comerciales + /// + public class EQuoteAdjustment + { + /// + /// ID interno del ajuste aplicado al presupuesto + /// + public int Id { get; set; } + + /// + /// FK al presupuesto (PhS_QuoteHeaders) + /// + public int QuoteheaderId { get; set; } + + /// + /// Código del motivo de ajuste (FK a PhS_AdjustmentReasons) + /// + public string ReasonCode { get; set; } = null!; + + /// + /// Importe del ajuste realizado (positivo o nulo) + /// + public decimal? Amount { get; set; } + + /// + /// Descripción adicional del ajuste + /// + public string? Description { get; set; } + + /// + /// Fecha de registro del ajuste + /// + public DateTime Createdat { get; set; } + + //public virtual PhSQuoteHeader Quoteheader { get; set; } = null!; + + //public virtual PhSAdjustmentReason ReasonCodeNavigation { get; set; } = null!; + } +} diff --git a/Domain/Entities/EQuoteHeader.cs b/Domain/Entities/EQuoteHeader.cs index 9442860..bbc8ab5 100644 --- a/Domain/Entities/EQuoteHeader.cs +++ b/Domain/Entities/EQuoteHeader.cs @@ -3,6 +3,7 @@ /// /// Tabla de cabeceras de presupuestos /// + /// public class EQuoteHeader { /// @@ -15,6 +16,11 @@ /// public Guid TicketId { get; set; } + /// + /// Número visible del presupuesto + /// + public string Quotenumber { get; set; } = null!; + /// /// Cliente asociado /// @@ -30,11 +36,6 @@ /// public int PeopleId { get; set; } - /// - /// Estado: E (Emitido), A (Aprobado), AC (Aprobado para cirugia), etc. - /// - public string Status { get; set; } = null!; - /// /// Fecha de emisión /// @@ -51,9 +52,24 @@ public DateTime? Estimateddate { get; set; } /// - /// Importe estimado total + /// Código de moneda pactada (ISO 4217). Ej: ARS, USD /// - public decimal? Estimatedamount { get; set; } + public string Currency { get; set; } = null!; + + /// + /// Tipo de cambio pactado para conversión a pesos argentinos + /// + public decimal? Exchangerate { get; set; } + + /// + /// Total del presupuesto expresado en moneda extranjera + /// + public decimal? TotalForeign { get; set; } + + /// + /// Total final del presupuesto expresado en moneda local, considerando ajustes o acuerdos comerciales + /// + public decimal? Total { get; set; } /// /// Importe aprobado @@ -61,9 +77,19 @@ public decimal? Approvedamount { get; set; } /// - /// Número visible del presupuesto + /// Estado: E (Emitido), A (Aprobado), AC (Aprobado para cirugia), etc. /// - public string Quotenumber { get; set; } = null!; + public string Status { get; set; } = null!; + + /// + /// Indica si la cirugía se realizará fuera de la ciudad/localidad habitual (“out of town”) + /// + public bool OutOfTown { get; set; } + + /// + /// Instrucción dirigida al área de logística para detallar qué debe prepararse o despacharse (ej: “CMF 1.5 + INSTRUMENTAL”) + /// + public string? DispatchInstruction { get; set; } /// /// Cantidad de impresiones @@ -85,9 +111,98 @@ /// public DateTime? Modifiedat { get; set; } + public virtual ICollection PhSQuoteAdjustments { get; set; } = new List(); + public virtual ICollection PhSQuoteDetails { get; set; } = new List(); public virtual ICollection PhSQuoteRoles { get; set; } = new List(); - } + + //public class EQuoteHeader + //{ + // /// + // /// ID interno + // /// + // public int Id { get; set; } + + // /// + // /// Relación con Tickets + // /// + // public Guid TicketId { get; set; } + + // /// + // /// Cliente asociado + // /// + // public int CustomerId { get; set; } + + // /// + // /// Unidad de negocio + // /// + // public int BusinessunitId { get; set; } + + // /// + // /// Identificador único del vendedor + // /// + // public int PeopleId { get; set; } + + // /// + // /// Estado: E (Emitido), A (Aprobado), AC (Aprobado para cirugia), etc. + // /// + // public string Status { get; set; } = null!; + + // /// + // /// Fecha de emisión + // /// + // public DateTime Issuedate { get; set; } + + // /// + // /// Fecha de aprobación + // /// + // public DateOnly? Approvaldate { get; set; } + + // /// + // /// Fecha tentativa (de cirugía por ej.) + // /// + // public DateTime? Estimateddate { get; set; } + + // /// + // /// Importe estimado total + // /// + // public decimal? Estimatedamount { get; set; } + + // /// + // /// Importe aprobado + // /// + // public decimal? Approvedamount { get; set; } + + // /// + // /// Número visible del presupuesto + // /// + // public string Quotenumber { get; set; } = null!; + + // /// + // /// Cantidad de impresiones + // /// + // public int Printcount { get; set; } + + // /// + // /// Observaciones internas + // /// + // public string? Observations { get; set; } + + // /// + // /// Fecha de creación + // /// + // public DateTime Createdat { get; set; } + + // /// + // /// Fecha de modificación + // /// + // public DateTime? Modifiedat { get; set; } + + // public virtual ICollection PhSQuoteDetails { get; set; } = new List(); + + // public virtual ICollection PhSQuoteRoles { get; set; } = new List(); + + //} } diff --git a/Domain/obj/Domain.csproj.nuget.g.props b/Domain/obj/Domain.csproj.nuget.g.props index 8b4eb7e..3efed38 100644 --- a/Domain/obj/Domain.csproj.nuget.g.props +++ b/Domain/obj/Domain.csproj.nuget.g.props @@ -7,7 +7,7 @@ $(UserProfile)\.nuget\packages\ C:\Users\maski\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages PackageReference - 6.13.2 + 6.13.1 diff --git a/Models/Helpers/PhSEntityTypes.cs b/Models/Helpers/PhSEntityTypes.cs new file mode 100644 index 0000000..0f0488f --- /dev/null +++ b/Models/Helpers/PhSEntityTypes.cs @@ -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"; + } +} diff --git a/Models/Interfaces/IPhSQuoteHeaderRepository.cs b/Models/Interfaces/IPhSQuoteHeaderRepository.cs index 4fd1cb2..ebebdbc 100644 --- a/Models/Interfaces/IPhSQuoteHeaderRepository.cs +++ b/Models/Interfaces/IPhSQuoteHeaderRepository.cs @@ -6,11 +6,20 @@ namespace Models.Interfaces public interface IPhSQuoteHeaderRepository { Task> GetAllAsync(int page = 1, int pageSize = 50); - Task> GetByCustomerIdAsync(int customerId); Task GetByIdAsync(int id); - Task> SearchAsync(int? customerId, string? quoteNumber, int? professionalId, int? institutionId, int? patientId, DateTime? issueDateFrom, DateTime? issueDateTo, string? status, int page = 1, int pageSize = 50); + Task> GetByCustomerIdAsync(int customerId); + Task> SearchAsync(int? customerId, string? quoteNumber, int? professionalId, + int? institutionId, int? patientId, DateTime? issueDateFrom, DateTime? issueDateTo, string? status, + int page = 1, int pageSize = 50); + Task AddAsync(EQuoteHeader quoteHeader); Task UpdateAsync(EQuoteHeader quoteHeader); Task DeleteAsync(int id); + + // Ajustes + Task> GetAdjustmentsByQuoteIdAsync(int quoteId); + Task AddAdjustmentAsync(EQuoteAdjustment adjustment); + Task UpdateAdjustmentAsync(EQuoteAdjustment adjustment); + Task DeleteAdjustmentAsync(int adjustmentId); } } diff --git a/Models/Models/PhSAdjustmentReason.cs b/Models/Models/PhSAdjustmentReason.cs new file mode 100644 index 0000000..e704717 --- /dev/null +++ b/Models/Models/PhSAdjustmentReason.cs @@ -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 PhSQuoteAdjustments { get; set; } = new List(); +} diff --git a/Models/Models/PhSQuoteAdjustment.cs b/Models/Models/PhSQuoteAdjustment.cs new file mode 100644 index 0000000..53012d0 --- /dev/null +++ b/Models/Models/PhSQuoteAdjustment.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; + +namespace Models.Models; + +/// +/// Tabla de ajustes aplicados a presupuestos comerciales +/// +public partial class PhSQuoteAdjustment +{ + /// + /// ID interno del ajuste aplicado al presupuesto + /// + public int Id { get; set; } + + /// + /// FK al presupuesto (PhS_QuoteHeaders) + /// + public int QuoteheaderId { get; set; } + + /// + /// Código del motivo de ajuste (FK a PhS_AdjustmentReasons) + /// + public string ReasonCode { get; set; } = null!; + + /// + /// Importe del ajuste realizado (positivo o nulo) + /// + public decimal? Amount { get; set; } + + /// + /// Descripción adicional del ajuste + /// + public string? Description { get; set; } + + /// + /// Fecha de registro del ajuste + /// + public DateTime Createdat { get; set; } + + public virtual PhSQuoteHeader Quoteheader { get; set; } = null!; + + public virtual PhSAdjustmentReason ReasonCodeNavigation { get; set; } = null!; +} diff --git a/Models/Models/PhSQuoteHeader.cs b/Models/Models/PhSQuoteHeader.cs index 3967735..566eb3f 100644 --- a/Models/Models/PhSQuoteHeader.cs +++ b/Models/Models/PhSQuoteHeader.cs @@ -18,6 +18,11 @@ public partial class PhSQuoteHeader /// public Guid TicketId { get; set; } + /// + /// Número visible del presupuesto + /// + public string Quotenumber { get; set; } = null!; + /// /// Cliente asociado /// @@ -33,11 +38,6 @@ public partial class PhSQuoteHeader /// public int PeopleId { get; set; } - /// - /// Estado: E (Emitido), A (Aprobado), AC (Aprobado para cirugia), etc. - /// - public string Status { get; set; } = null!; - /// /// Fecha de emisión /// @@ -54,9 +54,24 @@ public partial class PhSQuoteHeader public DateTime? Estimateddate { get; set; } /// - /// Importe estimado total + /// Código de moneda pactada (ISO 4217). Ej: ARS, USD /// - public decimal? Estimatedamount { get; set; } + public string Currency { get; set; } = null!; + + /// + /// Tipo de cambio pactado para conversión a pesos argentinos + /// + public decimal? Exchangerate { get; set; } + + /// + /// Total del presupuesto expresado en moneda extranjera + /// + public decimal? TotalForeign { get; set; } + + /// + /// Total final del presupuesto expresado en moneda local, considerando ajustes o acuerdos comerciales + /// + public decimal? Total { get; set; } /// /// Importe aprobado @@ -64,9 +79,19 @@ public partial class PhSQuoteHeader public decimal? Approvedamount { get; set; } /// - /// Número visible del presupuesto + /// Estado: E (Emitido), A (Aprobado), AC (Aprobado para cirugia), etc. /// - public string Quotenumber { get; set; } = null!; + public string Status { get; set; } = null!; + + /// + /// Indica si la cirugía se realizará fuera de la ciudad/localidad habitual (“out of town”) + /// + public bool OutOfTown { get; set; } + + /// + /// Instrucción dirigida al área de logística para detallar qué debe prepararse o despacharse (ej: “CMF 1.5 + INSTRUMENTAL”) + /// + public string? DispatchInstruction { get; set; } /// /// Cantidad de impresiones @@ -88,6 +113,8 @@ public partial class PhSQuoteHeader /// public DateTime? Modifiedat { get; set; } + public virtual ICollection PhSQuoteAdjustments { get; set; } = new List(); + public virtual ICollection PhSQuoteDetails { get; set; } = new List(); public virtual ICollection PhSQuoteRoles { get; set; } = new List(); diff --git a/Models/Models/PhronCareOperationsHubContext.cs b/Models/Models/PhronCareOperationsHubContext.cs index 8ba2c76..5a97cfc 100644 --- a/Models/Models/PhronCareOperationsHubContext.cs +++ b/Models/Models/PhronCareOperationsHubContext.cs @@ -23,6 +23,8 @@ public partial class PhronCareOperationsHubContext : DbContext public virtual DbSet PhSAccountTypes { get; set; } + public virtual DbSet PhSAdjustmentReasons { get; set; } + public virtual DbSet PhSBusinessUnits { get; set; } public virtual DbSet PhSCustomers { get; set; } @@ -55,6 +57,8 @@ public partial class PhronCareOperationsHubContext : DbContext public virtual DbSet PhSProfessionalSpecialties { get; set; } + public virtual DbSet PhSQuoteAdjustments { get; set; } + public virtual DbSet PhSQuoteDetails { get; set; } public virtual DbSet PhSQuoteHeaders { get; set; } @@ -62,8 +66,15 @@ public partial class PhronCareOperationsHubContext : DbContext public virtual DbSet 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(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(entity => { entity.ToTable("PhS_BusinessUnits"); @@ -695,6 +728,48 @@ public partial class PhronCareOperationsHubContext : DbContext .HasColumnName("name"); }); + modelBuilder.Entity(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(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(entity => diff --git a/Models/Repositories/PhSQuoteHeaderRepository.cs b/Models/Repositories/PhSQuoteHeaderRepository.cs index deccea2..f90f6d1 100644 --- a/Models/Repositories/PhSQuoteHeaderRepository.cs +++ b/Models/Repositories/PhSQuoteHeaderRepository.cs @@ -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> 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(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); } public async Task> 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 @@ -132,6 +116,39 @@ namespace PhronCare.Core.Data.Repositories.Sales await _context.SaveChangesAsync(); } } - #endregion + + // ---------------------------- + // Métodos para Ajustes + // ---------------------------- + public async Task> GetAdjustmentsByQuoteIdAsync(int quoteId) + { + var adjustments = await _context.PhSQuoteAdjustments + .Where(a => a.QuoteheaderId == quoteId) + .ToListAsync(); + + return adjustments.Select(EntityMapper.MapEntity); + } + public async Task AddAdjustmentAsync(EQuoteAdjustment adjustment) + { + var dbEntity = EntityMapper.MapEntity(adjustment); + _context.PhSQuoteAdjustments.Add(dbEntity); + await _context.SaveChangesAsync(); + return EntityMapper.MapEntity(dbEntity); + } + public async Task UpdateAdjustmentAsync(EQuoteAdjustment adjustment) + { + var dbEntity = EntityMapper.MapEntity(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(); + } + } } -} +} \ No newline at end of file diff --git a/Models/obj/Models.csproj.nuget.g.props b/Models/obj/Models.csproj.nuget.g.props index 57bd42c..d21a6b0 100644 --- a/Models/obj/Models.csproj.nuget.g.props +++ b/Models/obj/Models.csproj.nuget.g.props @@ -7,7 +7,7 @@ $(UserProfile)\.nuget\packages\ C:\Users\maski\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages PackageReference - 6.13.2 + 6.13.1 diff --git a/phronCare.UIBlazor/Shared/MainLayout.razor b/phronCare.UIBlazor/Shared/MainLayout.razor index 23e93dd..9a75bc1 100644 --- a/phronCare.UIBlazor/Shared/MainLayout.razor +++ b/phronCare.UIBlazor/Shared/MainLayout.razor @@ -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 )