using Core.Interfaces.Stock; using Documents.Interfaces; using Documents.Models; using Domain.Dtos.Stock; using Domain.Entities; using Domain.Generics; using Microsoft.AspNetCore.Mvc; using System.Reflection; namespace phronCare.API.Controllers.Stock { [Route("api/[controller]")] [ApiController] public class ExpeditionController : ControllerBase { private readonly IExpeditionDom _expeditionService; // ← este es _expeditionService private readonly IDocumentTemplateService _documentTemplateService; public ExpeditionController( IDocumentTemplateService documentTemplateService, IExpeditionDom expeditionService) { _documentTemplateService = documentTemplateService ?? throw new ArgumentNullException(nameof(documentTemplateService)); _expeditionService = expeditionService ?? throw new ArgumentNullException(nameof(expeditionService)); } /// /// Busca expediciones con filtros y paginación. /// Filtros: número, estado, fechas de emisión, ubicación (opcional). /// [HttpGet("search")] public async Task>> Search( [FromQuery] string? expeditionNumber, [FromQuery] string? status, [FromQuery] DateTime? issueDateFrom, [FromQuery] DateTime? issueDateTo, [FromQuery] int? locationId, [FromQuery] int page = 1, [FromQuery] int pageSize = 50) { try { var result = await _expeditionService.SearchAsync( expeditionNumber, status, issueDateFrom, issueDateTo, locationId, page, pageSize ); return Ok(result); } catch (Exception ex) { var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod"; return StatusCode(500, $"{methodName} Message: {ex.Message}"); } } // GET /api/expedition/{id} [HttpGet("{id:int}")] [ProducesResponseType(typeof(ExpeditionDto), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> GetById(int id) { var dto = await _expeditionService.GetDtoByIdAsync(id); if (dto is null) return NotFound($"Expedición con ID {id} no encontrada."); return Ok(dto); } // (opcional) GET /api/expedition/summary/{expeditionNumber} [HttpGet("summary/{expeditionNumber}")] [ProducesResponseType(typeof(ExpeditionDto), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> GetByNumber(string expeditionNumber) { var dto = await _expeditionService.GetDtoByExpeditionNumberAsync(expeditionNumber); if (dto is null) return NotFound($"No se encontró expedición {expeditionNumber}."); return Ok(dto); } #region Endpoint de emision de expedicion (encabezado + detalles) [HttpPost("createfull")] public async Task CreateFullExpedition([FromBody] CreateFullExpeditionRequest request) { try { if (request == null || request.Expedition == null) return BadRequest("El payload no puede contener elementos nulos."); // Delegamos al service, que ahora arma el grafo y llama al repo atómico var (id, number) = await _expeditionService.CreateAndIssueAsync( request.Expedition, request.Expedition.PhLsmExpeditionDetails, // detalles vienen dentro, igual que en Quotes request.FormSeriesId); // <<< Simetría absoluta con QuoteController: objeto anónimo >>> return Ok(new { Success = true, Id = id, ExpeditionNumber = number }); } catch (InvalidOperationException ex) { return BadRequest($"Error de negocio: {ex.Message}"); } catch (Exception ex) { return StatusCode(500, $"Ocurrió un error interno: {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 expedition = await _expeditionService.GetDtoByIdAsync(id); if (expedition == null) return NotFound($"Expedicion con ID {id} no encontrado."); var pdfBytes = await _documentTemplateService.GenerateDocumentAsync(new DocumentGenerationRequest { Model = expedition, DocumentType = DocumentType.Expedition }); return File(pdfBytes, "application/pdf", $"Expedicion_{expedition.Expeditionnumber}.pdf"); } [HttpPost("exportfiltered")] public async Task ExportFiltered([FromBody] ExpeditionSearchParams searchParams) { try { var file = await _expeditionService.ExportFilteredToExcelAsync(searchParams); return File(file, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "Expediciones.xlsx"); } catch (Exception ex) { return BadRequest(ex.Message); } } } public class CreateFullExpeditionRequest { public ELSExpeditionHeader Expedition { get; set; } = default!; public int FormSeriesId { get; set; } } }