using Microsoft.AspNetCore.Mvc; using Documents.Models; using Domain.Dtos; using Domain.Entities; using Domain.Generics; using System.Reflection; using Documents.Interfaces; using Core.Interfaces; namespace phronCare.API.Controllers.Sales { [Route("api/[controller]")] [ApiController] public class QuoteController : ControllerBase { private readonly IDocumentTemplateService _documentTemplateService; private readonly IQuoteDom _quoteService; public QuoteController(IDocumentTemplateService documentTemplateService, IQuoteDom quoteService) { _documentTemplateService = documentTemplateService ?? throw new ArgumentNullException(nameof(documentTemplateService)); ; _quoteService = quoteService ?? throw new ArgumentNullException(nameof(quoteService)); } #region Obtener Presupuestos /// /// Busca presupuestos con filtros de texto libre o por ID, paginados. /// [HttpGet("search")] public async Task>> Search( [FromQuery] int? customerId, [FromQuery] string? customerText, [FromQuery] string? quoteNumber, [FromQuery] int? professionalId, [FromQuery] string? professionalText, [FromQuery] int? institutionId, [FromQuery] string? institutionText, [FromQuery] int? patientId, [FromQuery] string? patientText, [FromQuery] DateTime? issueDateFrom, [FromQuery] DateTime? issueDateTo, [FromQuery] string? status, [FromQuery] int page = 1, [FromQuery] int pageSize = 50) { try { var result = await _quoteService.SearchAsync( customerId, customerText, quoteNumber, professionalId, professionalText, institutionId, institutionText, patientId, patientText, issueDateFrom, issueDateTo, status, page, pageSize); return Ok(result); } catch (Exception ex) { var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod"; // Log exception as needed... return StatusCode(500, $"{methodName} Message: {ex.Message}"); } } /// /// 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}"); } } /// /// Devuelve un presupuesto DTO con información básica a partir del número de presupuesto. /// [HttpGet("summary/{quoteNumber}")] public async Task> GetHeaderDtoByQuoteNumber(string quoteNumber) { try { var dto = await _quoteService.GetDtoByQuoteNumberAsync(quoteNumber); if (dto == null) return NotFound($"No se encontró un presupuesto con el número: {quoteNumber}"); return Ok(dto); } catch (Exception ex) { var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod"; return StatusCode(500, $"{methodName} Message: {ex.Message}"); } } #endregion /// /// Genera y devuelve un archivo PDF correspondiente al presupuesto especificado por su ID. /// [HttpGet("{id}/pdf")] public async Task GetQuotePdf(int id) { var quote = await _quoteService.GetDtoByIdAsync(id); if (quote == null) return NotFound($"Presupuesto con ID {id} no encontrado."); var pdfBytes = await _documentTemplateService.GenerateDocumentAsync(new DocumentGenerationRequest { Model = quote, DocumentType = DocumentType.Quote }); return File(pdfBytes, "application/pdf", $"Presupuesto_{quote.Quotenumber}.pdf"); } [HttpPost("exportfiltered")] public async Task ExportFiltered([FromBody] QuoteSearchParams searchParams) { try { var file = await _quoteService.ExportFilteredToExcelAsync(searchParams); return File(file, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "Expediciones.xlsx"); } catch (Exception ex) { return BadRequest(ex.Message); } } #region Endpoint de emision de presupuesto (encabezado + detalles + roles + ajustes + impuestos) [HttpPost("createfull")] public async Task CreateFullQuote([FromBody] CreateFullQuoteRequest request) { try { // Validamos que el request y el objeto Quote no sean nulos if (request == null || request.Quote == null) return BadRequest("El payload no puede contener elementos nulos."); // Desempaquetamos los datos var quote = request.Quote; var formSeriesId = request.FormSeriesId; // Llamada al servicio de negocio (int quoteId, string quoteNumber) = await _quoteService.CreateFullQuoteAsync(request.Quote, request.FormSeriesId); // Devolvemos el número generado return Ok(new { Success = true, Id = quoteId, QuoteNumber = quoteNumber }); } catch (ArgumentNullException ex) { return BadRequest($"Validación fallida: {ex.Message}"); } catch (InvalidOperationException ex) { return BadRequest($"Error de negocio: {ex.Message}"); } catch (Exception ex) { return StatusCode(500, $"Ocurrió un error interno: {ex.Message}"); } } public class CreateFullQuoteRequest { public EQuoteHeader Quote { get; set; } = default!; public int FormSeriesId { get; set; } } #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."); 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 autorizar el presupuesto." }); } #endregion } }