From e276e9672cdde8b9204da42d04f4cc35fc7e41c7 Mon Sep 17 00:00:00 2001 From: Leandro Hernan Rojas Date: Thu, 29 May 2025 19:21:57 -0300 Subject: [PATCH] Add Authorization Quote --- Core/Interfaces/IQuoteDom.cs | 4 + Core/Services/QuoteService.cs | 13 + Domain/Dtos/QuoteAuthorizationDto.cs | 10 + Domain/Dtos/QuoteAuthorizationRequest.cs | 7 + Domain/Dtos/QuoteItemDto.cs | 7 +- Domain/Entities/EQuoteDetail.cs | 16 +- Domain/Entities/EQuoteHeader.cs | 2 +- Models/Interfaces/IQuoteRepository.cs | 1 + Models/Models/PhSQuoteDetail.cs | 10 + Models/Models/PhSQuoteHeader.cs | 2 +- .../Models/PhronCareOperationsHubContext.cs | 18 +- Models/Repositories/PhSQuoteRepository.cs | 52 ++++ .../Controllers/Sales/QuoteController.cs | 37 +++ .../obj/Debug/net8.0/ApiEndpoints.json | 42 ++++ .../Sales/QuoteAuthorizationViewItem.cs | 13 + .../Pages/Sales/Quotes/QuoteAuthorize.razor | 227 ++++++++++++++++++ .../Pages/Sales/Quotes/QuoteCreate.razor | 2 +- .../Pages/Sales/Quotes/Quotes.razor | 26 +- .../Services/Sales/Quotes/QuoteService.cs | 27 +++ 19 files changed, 494 insertions(+), 22 deletions(-) create mode 100644 Domain/Dtos/QuoteAuthorizationDto.cs create mode 100644 Domain/Dtos/QuoteAuthorizationRequest.cs create mode 100644 phronCare.UIBlazor/Models/Sales/QuoteAuthorizationViewItem.cs create mode 100644 phronCare.UIBlazor/Pages/Sales/Quotes/QuoteAuthorize.razor diff --git a/Core/Interfaces/IQuoteDom.cs b/Core/Interfaces/IQuoteDom.cs index b67234a..cd748f5 100644 --- a/Core/Interfaces/IQuoteDom.cs +++ b/Core/Interfaces/IQuoteDom.cs @@ -21,5 +21,9 @@ namespace Models.Interfaces Task<(int Id, string Quotenumber)> CreateFullQuoteAsync(EQuoteHeader quote, int formSeriesId); Task GetDtoByIdAsync(int id); #endregion + #region Autorización + Task AuthorizeQuoteAsync(int quoteId, List items); + #endregion + } } \ No newline at end of file diff --git a/Core/Services/QuoteService.cs b/Core/Services/QuoteService.cs index 21d334d..48fe83d 100644 --- a/Core/Services/QuoteService.cs +++ b/Core/Services/QuoteService.cs @@ -48,6 +48,19 @@ namespace Core.Services } #endregion + public async Task AuthorizeQuoteAsync(int quoteId, List items) + { + var approvedDetails = items.Select(i => new EQuoteDetail + { + Id = i.Id, + Approved = i.Approved, + Approvedquantity = i.ApprovedQuantity, + Approvedunitprice = i.ApprovedUnitPrice + }).ToList(); + + return await _quoteRepository.AuthorizeQuoteAsync(quoteId, approvedDetails); + } + #region Validaciones QuoteCreate private void ValidateQuote(EQuoteHeader quote) { diff --git a/Domain/Dtos/QuoteAuthorizationDto.cs b/Domain/Dtos/QuoteAuthorizationDto.cs new file mode 100644 index 0000000..19e9517 --- /dev/null +++ b/Domain/Dtos/QuoteAuthorizationDto.cs @@ -0,0 +1,10 @@ +namespace Domain.Dtos +{ + public class QuoteAuthorizationDto + { + public int Id { get; set; } // Id del detalle + public bool Approved { get; set; } // ¿Aprobado? + public int? ApprovedQuantity { get; set; } // Cantidad aprobada + public decimal? ApprovedUnitPrice { get; set; } // Precio aprobado + } +} diff --git a/Domain/Dtos/QuoteAuthorizationRequest.cs b/Domain/Dtos/QuoteAuthorizationRequest.cs new file mode 100644 index 0000000..160bf09 --- /dev/null +++ b/Domain/Dtos/QuoteAuthorizationRequest.cs @@ -0,0 +1,7 @@ +using Domain.Dtos; + +public class QuoteAuthorizationRequest +{ + public int QuoteId { get; set; } + public List? Items { get; set; } = new(); +} \ No newline at end of file diff --git a/Domain/Dtos/QuoteItemDto.cs b/Domain/Dtos/QuoteItemDto.cs index e7904b5..94f7644 100644 --- a/Domain/Dtos/QuoteItemDto.cs +++ b/Domain/Dtos/QuoteItemDto.cs @@ -2,10 +2,15 @@ { public class QuoteItemDto { + /// + /// Identificador único del ítem dentro del presupuesto. + /// + public int Id { get; set; } + /// /// Descripción del producto o servicio cotizado. /// - public string Description { get; set; } = ""; + public string Description { get; set; } = string.Empty; /// /// Cantidad de unidades cotizadas. diff --git a/Domain/Entities/EQuoteDetail.cs b/Domain/Entities/EQuoteDetail.cs index 6276dc3..8989b2e 100644 --- a/Domain/Entities/EQuoteDetail.cs +++ b/Domain/Entities/EQuoteDetail.cs @@ -20,7 +20,7 @@ /// /// Descripción modificable del producto (puede diferir del original) /// - public string? ProductDescription { get; set; }=String.Empty; + public string? ProductDescription { get; set; } = String.Empty; /// /// Cantidad @@ -37,6 +37,16 @@ /// public bool Approved { get; set; } + /// + /// Cantidad aprobada para este ítem del presupuesto. + /// + public int? Approvedquantity { get; set; } + + /// + /// Precio unitario aprobado (puede diferir del original). + /// + public decimal? Approvedunitprice { get; set; } + /// /// Importe aprobado final /// @@ -52,8 +62,8 @@ /// public DateTime? Modifiedat { get; set; } - //public virtual EProduct Product { get; set; } = null!; + //public virtual PhSProduct Product { get; set; } = null!; - //public virtual EQuoteHeader PhSQuoteheader { get; set; } = null!; + //public virtual PhSQuoteHeader Quoteheader { get; set; } = null!; } } diff --git a/Domain/Entities/EQuoteHeader.cs b/Domain/Entities/EQuoteHeader.cs index 251af19..e00fcf7 100644 --- a/Domain/Entities/EQuoteHeader.cs +++ b/Domain/Entities/EQuoteHeader.cs @@ -44,7 +44,7 @@ /// /// Fecha de aprobación /// - public DateOnly? Approvaldate { get; set; } + public DateTime? Approvaldate { get; set; } /// /// Fecha tentativa (de cirugía por ej.) diff --git a/Models/Interfaces/IQuoteRepository.cs b/Models/Interfaces/IQuoteRepository.cs index a906d17..f5bf922 100644 --- a/Models/Interfaces/IQuoteRepository.cs +++ b/Models/Interfaces/IQuoteRepository.cs @@ -12,6 +12,7 @@ namespace Models.Interfaces #region Guardado completo de presupuesto (encabezado + detalles + roles + ajustes + impuestos) Task<(int Id, string Quotenumber)> CreateFullQuoteAsync(EQuoteHeader quote, int formSeriesId); Task GetDtoByIdAsync(int id); + Task AuthorizeQuoteAsync(int quoteId, List approvedItems); #endregion } } diff --git a/Models/Models/PhSQuoteDetail.cs b/Models/Models/PhSQuoteDetail.cs index ffab823..202a86c 100644 --- a/Models/Models/PhSQuoteDetail.cs +++ b/Models/Models/PhSQuoteDetail.cs @@ -43,6 +43,16 @@ public partial class PhSQuoteDetail /// public bool Approved { get; set; } + /// + /// Cantidad aprobada para este ítem del presupuesto. + /// + public int? Approvedquantity { get; set; } + + /// + /// Precio unitario aprobado (puede diferir del original). + /// + public decimal? Approvedunitprice { get; set; } + /// /// Importe aprobado final /// diff --git a/Models/Models/PhSQuoteHeader.cs b/Models/Models/PhSQuoteHeader.cs index c793250..3aac24c 100644 --- a/Models/Models/PhSQuoteHeader.cs +++ b/Models/Models/PhSQuoteHeader.cs @@ -46,7 +46,7 @@ public partial class PhSQuoteHeader /// /// Fecha de aprobación /// - public DateOnly? Approvaldate { get; set; } + public DateTime? Approvaldate { get; set; } /// /// Fecha tentativa (de cirugía por ej.) diff --git a/Models/Models/PhronCareOperationsHubContext.cs b/Models/Models/PhronCareOperationsHubContext.cs index fafebe7..7be3409 100644 --- a/Models/Models/PhronCareOperationsHubContext.cs +++ b/Models/Models/PhronCareOperationsHubContext.cs @@ -74,15 +74,8 @@ public partial class PhronCareOperationsHubContext : DbContext public virtual DbSet PhSQuoteTaxes { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - #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"); +#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"); protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -888,6 +881,13 @@ public partial class PhronCareOperationsHubContext : DbContext .HasComment("Importe aprobado final") .HasColumnType("decimal(18, 2)") .HasColumnName("approvedamount"); + entity.Property(e => e.Approvedquantity) + .HasComment("Cantidad aprobada para este ítem del presupuesto.") + .HasColumnName("approvedquantity"); + entity.Property(e => e.Approvedunitprice) + .HasComment("Precio unitario aprobado (puede diferir del original).") + .HasColumnType("decimal(18, 2)") + .HasColumnName("approvedunitprice"); entity.Property(e => e.Createdat) .HasDefaultValueSql("(getdate())") .HasComment("Fecha de creación del registro") diff --git a/Models/Repositories/PhSQuoteRepository.cs b/Models/Repositories/PhSQuoteRepository.cs index 0e53dcb..757c3e4 100644 --- a/Models/Repositories/PhSQuoteRepository.cs +++ b/Models/Repositories/PhSQuoteRepository.cs @@ -296,6 +296,7 @@ namespace Models.Repositories return new QuoteItemDto { + Id = d.Id, Description = d.ProductDescription, Quantity = d.Quantity, UnitPrice = d.Unitprice, @@ -378,5 +379,56 @@ namespace Models.Repositories } } #endregion + #region Autorización de presupuesto (aprobar/rechazar ítems) + public async Task AuthorizeQuoteAsync(int quoteId, List approvedItems) + { + var header = await _context.PhSQuoteHeaders + .Include(h => h.PhSQuoteDetails) + .FirstOrDefaultAsync(h => h.Id == quoteId); + + if (header == null) + return false; + + bool anyApproved = false; + + foreach (var item in approvedItems) + { + var detail = header.PhSQuoteDetails.FirstOrDefault(d => d.Id == item.Id); + if (detail == null) + continue; + + detail.Approved = item.Approved; + detail.Approvedquantity = item.Approved ? item.Approvedquantity : null; + detail.Approvedunitprice = item.Approved ? item.Approvedunitprice : null; + detail.Approvedamount = item.Approved && item.Approvedquantity.HasValue && item.Approvedunitprice.HasValue + ? item.Approvedquantity.Value * item.Approvedunitprice.Value + : null; + + if (item.Approved) + anyApproved = true; + + detail.Modifiedat = DateTime.Now; + } + + if (anyApproved) + { + header.Status = "Aprobado"; + header.Approvaldate = DateTime.Now; + header.Approvedamount = header.PhSQuoteDetails + .Where(d => d.Approved && d.Approvedamount.HasValue) + .Sum(d => d.Approvedamount.Value); + } + else + { + header.Status = "Anulado"; + header.Approvaldate = DateTime.Now; + header.Approvedamount = 0; + } + header.Modifiedat = DateTime.Now; + + await _context.SaveChangesAsync(); + return true; + } + #endregion } } \ No newline at end of file diff --git a/phronCare.API/Controllers/Sales/QuoteController.cs b/phronCare.API/Controllers/Sales/QuoteController.cs index b31b6ca..684b40a 100644 --- a/phronCare.API/Controllers/Sales/QuoteController.cs +++ b/phronCare.API/Controllers/Sales/QuoteController.cs @@ -72,6 +72,29 @@ namespace phronCare.API.Controllers.Sales } } + /// + /// Obtiene un presupuesto completo por su ID. + /// + [HttpGet("{id}")] + public async Task> GetById(int id) + { + try + { + var quote = await _quoteService.GetDtoByIdAsync(id); + + if (quote == null) + return NotFound($"Presupuesto con ID {id} no encontrado."); + + return Ok(quote); + } + catch (Exception ex) + { + var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod"; + return StatusCode(500, $"{methodName} Message: {ex.Message}"); + } + } + + [HttpGet("{id}/pdf")] public async Task GetQuotePdf(int id) { @@ -130,6 +153,20 @@ namespace phronCare.API.Controllers.Sales #endregion + #region Autorizacion de presupuestos + [HttpPost("authorize")] + public async Task AuthorizeQuote([FromBody] QuoteAuthorizationRequest request) + { + if (request == null || request.Items == null) + return BadRequest("No se recibió información válida para autorizar o anular."); + var result = await _quoteService.AuthorizeQuoteAsync(request.QuoteId, request.Items); + + return result + ? Ok(new { success = true, message = "Presupuesto procesado correctamente." }) + : BadRequest(new { success = false, message = "No se pudo procesar el presupuesto." }); + } + + #endregion } } \ No newline at end of file diff --git a/phronCare.API/obj/Debug/net8.0/ApiEndpoints.json b/phronCare.API/obj/Debug/net8.0/ApiEndpoints.json index f82d4f7..688d77c 100644 --- a/phronCare.API/obj/Debug/net8.0/ApiEndpoints.json +++ b/phronCare.API/obj/Debug/net8.0/ApiEndpoints.json @@ -1606,6 +1606,32 @@ ], "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.Dtos.QuoteDto", + "MediaTypes": [ + "text/plain", + "application/json", + "text/json" + ], + "StatusCode": 200 + } + ] + }, { "ContainingType": "phronCare.API.Controllers.Sales.QuoteController", "Method": "GetQuotePdf", @@ -1622,6 +1648,22 @@ ], "ReturnTypes": [] }, + { + "ContainingType": "phronCare.API.Controllers.Sales.QuoteController", + "Method": "AuthorizeQuote", + "RelativePath": "api/Quote/authorize", + "HttpMethod": "POST", + "IsController": true, + "Order": 0, + "Parameters": [ + { + "Name": "request", + "Type": "QuoteAuthorizationRequest", + "IsRequired": true + } + ], + "ReturnTypes": [] + }, { "ContainingType": "phronCare.API.Controllers.Sales.QuoteController", "Method": "CreateFullQuote", diff --git a/phronCare.UIBlazor/Models/Sales/QuoteAuthorizationViewItem.cs b/phronCare.UIBlazor/Models/Sales/QuoteAuthorizationViewItem.cs new file mode 100644 index 0000000..7d8fcac --- /dev/null +++ b/phronCare.UIBlazor/Models/Sales/QuoteAuthorizationViewItem.cs @@ -0,0 +1,13 @@ +using Domain.Dtos; + +namespace phronCare.UIBlazor.Models.Sales +{ + public class QuoteAuthorizationViewItem : QuoteAuthorizationDto + { + public string ProductDescription { get; set; } = string.Empty; + public int Quantity { get; set; } + public decimal UnitPrice { get; set; } + + public decimal Subtotal => Quantity * UnitPrice; + } +} diff --git a/phronCare.UIBlazor/Pages/Sales/Quotes/QuoteAuthorize.razor b/phronCare.UIBlazor/Pages/Sales/Quotes/QuoteAuthorize.razor new file mode 100644 index 0000000..a2df624 --- /dev/null +++ b/phronCare.UIBlazor/Pages/Sales/Quotes/QuoteAuthorize.razor @@ -0,0 +1,227 @@ +@page "/quotes/authorize/{QuoteId:int}" + +@using Domain.Dtos +@using phronCare.UIBlazor.Models.Sales +@using phronCare.UIBlazor.Services.Sales.Quotes +@using Blazored.Modal +@using Blazored.Modal.Services +@inject QuoteService quoteService +@inject NavigationManager nav +@inject IToastService toast +@inject IModalService Modal + +
+
+

+ Autorización de Presupuesto +

+
+
+ @if (IsLoading) + { +

Cargando información del presupuesto...

+ } + else if (LoadError) + { +
+ Ocurrió un error al intentar cargar el presupuesto. +
+ } + else + { +
+
+
N°: @QuoteHeader.Quotenumber
+
Fecha: @QuoteHeader.IssueDate.ToShortDateString()
+
Cliente: @QuoteHeader.CustomerName
+
+
+
Médico: @QuoteHeader.ProfessionalName
+
Hospital: @QuoteHeader.InstitutionName
+
Paciente: @QuoteHeader.PatientName
+
+
+
Cirugía estimada: @QuoteHeader.EstimatedDate?.ToShortDateString()
+
Importe total: @QuoteHeader.Total.ToString("C")
+
+ Estado: + + @QuoteHeader.Status + +
+
+
+ + +
+ @if (FormModel.Items.Any(i => i.Approved)) + { + Al menos un ítem será autorizado + } + else + { + Todos los ítems serán anulados + } +
+ + + + + + + + + + + + + + @foreach (var item in FormModel.Items) + { + + + + + + + + + + } + +
¿Aprobar?DescripciónCant.Precio U.SubtotalCant. Aprob.Precio Aprob.
+ + @item.ProductDescription@item.Quantity@item.UnitPrice.ToString("N2")@item.Subtotal.ToString("N2") + + + +
+
+ + +
+
+ } +
+
+ +@code { + [Parameter] public int QuoteId { get; set; } + + private QuoteDto? QuoteHeader; + private QuoteAuthorizationFormModel FormModel = new(); + private bool IsLoading = true; + private bool LoadError = false; + + protected override async Task OnInitializedAsync() + { + try + { + var quote = await quoteService.GetDtoByIdAsync(QuoteId); + if (quote?.Items == null || !quote.Items.Any()) + { + LoadError = true; + return; + } + + QuoteHeader = quote; + + FormModel.Items = quote.Items.Select(x => new QuoteAuthorizationViewItem + { + Id = x.Id, + ProductDescription = x.Description, + Quantity = x.Quantity, + UnitPrice = x.UnitPrice, + Approved = false, + ApprovedQuantity = null, + ApprovedUnitPrice = null + }).ToList(); + } + catch + { + LoadError = true; + } + finally + { + IsLoading = false; + } + } + + private async Task OpenConfirmation() + { + var options = new ModalOptions() + { + HideHeader = true + }; + var parameters = new ModalParameters(); + parameters.Add("Message", "¿Desea continuar con esta operación?"); + var modal = Modal.Show("Confirmación", parameters,options); + var result = await modal.Result; + + if (!result.Cancelled) + { + await AuthorizeQuote(); + } + } + + private async Task AuthorizeQuote() + { + var approvedItems = FormModel.Items + .Where(i => i.Approved) + .Select(i => new QuoteAuthorizationDto + { + Id = i.Id, + Approved = true, + ApprovedQuantity = i.ApprovedQuantity, + ApprovedUnitPrice = i.ApprovedUnitPrice + }) + .ToList(); + + var payload = new QuoteAuthorizationRequest + { + QuoteId = QuoteId, + Items = approvedItems + }; + + var success = await quoteService.AuthorizeQuoteAsync(payload); + if (success) + { + if (!approvedItems.Any()) + toast.ShowInfo("Presupuesto anulado correctamente."); + else + toast.ShowSuccess("Presupuesto autorizado con éxito."); + nav.NavigateTo("/quotes"); + } + else + { + toast.ShowError("No se pudo procesar el presupuesto."); + } + } + + private void Cancel() => nav.NavigateTo("/quotes"); + + private string GetStatusColor(string status) => status.ToLower() switch + { + "Anulado" => "bg-danger text-white", + "Emitido" => "bg-primary text-white", + "Aprobado" => "bg-success", + "Despacho" => "bg-info text-white", + "SinConsumo" => "bg-warning text-dark", + "Transito" => "bg-secondary text-white", + "Cerrado" => "bg-dark text-white", + _ => "bg-light text-dark" + }; + + public class QuoteAuthorizationFormModel + { + public List Items { get; set; } = new(); + } +} diff --git a/phronCare.UIBlazor/Pages/Sales/Quotes/QuoteCreate.razor b/phronCare.UIBlazor/Pages/Sales/Quotes/QuoteCreate.razor index e7241d1..d9f7841 100644 --- a/phronCare.UIBlazor/Pages/Sales/Quotes/QuoteCreate.razor +++ b/phronCare.UIBlazor/Pages/Sales/Quotes/QuoteCreate.razor @@ -1,4 +1,4 @@ -@page "/quote/create" +@page "/quotes/create" @using System.Globalization; @using System.Net.Http.Json @using Blazored.Typeahead diff --git a/phronCare.UIBlazor/Pages/Sales/Quotes/Quotes.razor b/phronCare.UIBlazor/Pages/Sales/Quotes/Quotes.razor index a364627..14b13a4 100644 --- a/phronCare.UIBlazor/Pages/Sales/Quotes/Quotes.razor +++ b/phronCare.UIBlazor/Pages/Sales/Quotes/Quotes.razor @@ -87,13 +87,13 @@ Total Estado Vendedor - Acciones + Acciones @foreach (var quote in PagedQuotes.Items) { - + @quote.Quotenumber @quote.IssueDate.ToString("dd/MM/yyyy") @(quote.EstimatedDate.HasValue ? quote.EstimatedDate.Value.ToString("dd/MM/yyyy") : "—") @@ -108,9 +108,17 @@ @quote.Status @quote.SalespersonName - - - + + + + @if (quote.Status?.ToLower() == "emitido") + { + + } } @@ -359,7 +367,7 @@ private void Create() { - Navigation.NavigateTo("/quote/create/"); + Navigation.NavigateTo("/quotes/create/"); } private void OnClear() { @@ -369,6 +377,7 @@ } private string GetStatusBadge(string status) => status switch { + "Anulado" => "bg-danger text-white", "Emitido" => "bg-primary text-white", "Aprobado" => "bg-success", "Despacho" => "bg-info text-white", @@ -389,4 +398,9 @@ toastService.ShowError(ex.Message); } } + private void Autorizar(int id) + { + Navigation.NavigateTo($"/quotes/authorize/{id}"); + } + } diff --git a/phronCare.UIBlazor/Services/Sales/Quotes/QuoteService.cs b/phronCare.UIBlazor/Services/Sales/Quotes/QuoteService.cs index d300aff..edd5378 100644 --- a/phronCare.UIBlazor/Services/Sales/Quotes/QuoteService.cs +++ b/phronCare.UIBlazor/Services/Sales/Quotes/QuoteService.cs @@ -124,7 +124,34 @@ namespace phronCare.UIBlazor.Services.Sales.Quotes throw new Exception($"ExportPdfAsync: {message}", ex); } } + public async Task AuthorizeQuoteAsync(QuoteAuthorizationRequest request) + { + var response = await _http.PostAsJsonAsync("/api/quote/authorize", request); + if (!response.IsSuccessStatusCode) + { + var serverMessage = await response.Content.ReadAsStringAsync(); + throw new Exception($"Error al autorizar el presupuesto: {serverMessage}"); + } + + return true; + } + /// + /// Obtiene un presupuesto completo por ID para su visualización y autorización. + /// + public async Task GetDtoByIdAsync(int id) + { + try + { + var result = await _http.GetFromJsonAsync($"/api/quote/{id}"); + return result; + } + catch (Exception ex) + { + Console.WriteLine($"Error al obtener QuoteDto por ID: {ex.Message}"); + return null; + } + } } public class CreateQuoteResult