From adb8eb5c5cb215792f8539dee56df3d072696771 Mon Sep 17 00:00:00 2001 From: leandro Date: Wed, 3 Jun 2026 09:00:56 -0300 Subject: [PATCH] feat(api): expose sales document application endpoints close #64 --- Core/Interfaces/ISalesDocumentDom.cs | 12 ++ Core/Services/SalesDocumentService.cs | 24 ++++ .../Interfaces/IPhSSalesDocumentRepository.cs | 12 ++ .../PhSSalesDocumentRepository.cs | 98 +++++++++++++++++ .../Sales/SalesDocumentController.cs | 103 ++++++++++++++++++ phronCare.API/Program.cs | 3 + 6 files changed, 252 insertions(+) create mode 100644 phronCare.API/Controllers/Sales/SalesDocumentController.cs diff --git a/Core/Interfaces/ISalesDocumentDom.cs b/Core/Interfaces/ISalesDocumentDom.cs index 7fd2971..495ad66 100644 --- a/Core/Interfaces/ISalesDocumentDom.cs +++ b/Core/Interfaces/ISalesDocumentDom.cs @@ -1,9 +1,21 @@ using Domain.Dtos.Sales; +using Domain.Generics; namespace Core.Interfaces { public interface ISalesDocumentDom { + Task> SearchAsync( + int? customerId, + string? customerText, + int? quoteId, + int? documentType, + int? status, + DateTime? issueDateFrom, + DateTime? issueDateTo, + int page = 1, + int pageSize = 50); + Task CreateAsync(SalesDocumentCreateRequest request); Task GetDtoByIdAsync(int id); } diff --git a/Core/Services/SalesDocumentService.cs b/Core/Services/SalesDocumentService.cs index 044d708..8ea4839 100644 --- a/Core/Services/SalesDocumentService.cs +++ b/Core/Services/SalesDocumentService.cs @@ -2,6 +2,7 @@ using Core.Interfaces; using Domain.Constants; using Domain.Dtos.Sales; using Domain.Entities; +using Domain.Generics; using Models.Interfaces; namespace Core.Services @@ -10,6 +11,29 @@ namespace Core.Services { private readonly IPhSSalesDocumentRepository _salesDocumentRepository = salesDocumentRepository; + public Task> SearchAsync( + int? customerId, + string? customerText, + int? quoteId, + int? documentType, + int? status, + DateTime? issueDateFrom, + DateTime? issueDateTo, + int page = 1, + int pageSize = 50) + { + return _salesDocumentRepository.SearchAsync( + customerId, + customerText, + quoteId, + documentType, + status, + issueDateFrom, + issueDateTo, + page, + pageSize); + } + public async Task CreateAsync(SalesDocumentCreateRequest request) { ArgumentNullException.ThrowIfNull(request); diff --git a/Models/Interfaces/IPhSSalesDocumentRepository.cs b/Models/Interfaces/IPhSSalesDocumentRepository.cs index 49a0457..7105487 100644 --- a/Models/Interfaces/IPhSSalesDocumentRepository.cs +++ b/Models/Interfaces/IPhSSalesDocumentRepository.cs @@ -1,10 +1,22 @@ using Domain.Dtos.Sales; using Domain.Entities; +using Domain.Generics; namespace Models.Interfaces { public interface IPhSSalesDocumentRepository { + Task> SearchAsync( + int? customerId, + string? customerText, + int? quoteId, + int? documentType, + int? status, + DateTime? issueDateFrom, + DateTime? issueDateTo, + int page = 1, + int pageSize = 50); + Task CreateAsync(ESalesDocument entity); Task GetDtoByIdAsync(int id); } diff --git a/Models/Repositories/PhSSalesDocumentRepository.cs b/Models/Repositories/PhSSalesDocumentRepository.cs index c2e6299..4434b0a 100644 --- a/Models/Repositories/PhSSalesDocumentRepository.cs +++ b/Models/Repositories/PhSSalesDocumentRepository.cs @@ -1,5 +1,6 @@ using Domain.Dtos.Sales; using Domain.Entities; +using Domain.Generics; using Microsoft.EntityFrameworkCore; using Models.Helpers; using Models.Interfaces; @@ -11,6 +12,103 @@ namespace Models.Repositories { private readonly PhronCareOperationsHubContext _context = context; + public async Task> SearchAsync( + int? customerId, + string? customerText, + int? quoteId, + int? documentType, + int? status, + DateTime? issueDateFrom, + DateTime? issueDateTo, + int page = 1, + int pageSize = 50) + { + page = page <= 0 ? 1 : page; + pageSize = pageSize <= 0 ? 50 : pageSize; + + var query = _context.PhSSalesDocuments + .Include(x => x.Customer) + .Include(x => x.BillToCustomer) + .AsNoTracking() + .AsQueryable(); + + if (customerId.HasValue && customerId.Value > 0) + { + query = query.Where(x => x.CustomerId == customerId.Value || x.BillToCustomerId == customerId.Value); + } + + if (!string.IsNullOrWhiteSpace(customerText)) + { + var normalizedCustomerText = customerText.Trim(); + query = query.Where(x => + (x.Customer.Name ?? string.Empty).Contains(normalizedCustomerText) || + (x.BillToCustomer.Name ?? string.Empty).Contains(normalizedCustomerText)); + } + + if (quoteId.HasValue && quoteId.Value > 0) + { + query = query.Where(x => x.QuoteId == quoteId.Value); + } + + if (documentType.HasValue && documentType.Value > 0) + { + query = query.Where(x => x.DocumentType == documentType.Value); + } + + if (status.HasValue && status.Value > 0) + { + query = query.Where(x => x.Status == status.Value); + } + + if (issueDateFrom.HasValue) + { + query = query.Where(x => x.IssueDate >= issueDateFrom.Value); + } + + if (issueDateTo.HasValue) + { + query = query.Where(x => x.IssueDate <= issueDateTo.Value); + } + + var totalItems = await query.CountAsync(); + + var items = await query + .OrderByDescending(x => x.IssueDate) + .ThenByDescending(x => x.Id) + .Skip((page - 1) * pageSize) + .Take(pageSize) + .Select(x => new SalesDocumentSummaryDto + { + Id = x.Id, + InternalDocumentNumber = x.InternalDocumentNumber, + DocumentType = x.DocumentType, + Status = x.Status, + QuoteId = x.QuoteId, + CustomerId = x.CustomerId, + CustomerName = x.Customer.Name ?? string.Empty, + BillToCustomerId = x.BillToCustomerId, + BillToCustomerName = x.BillToCustomer.Name ?? string.Empty, + IssueDate = x.IssueDate, + Currency = x.Currency, + NetAmount = x.NetAmount, + TaxAmount = x.TaxAmount, + TotalAmount = x.TotalAmount, + PeriodFrom = x.PeriodFrom, + PeriodTo = x.PeriodTo, + Createdat = x.Createdat, + Modifiedat = x.Modifiedat + }) + .ToListAsync(); + + return new PagedResult + { + Items = items, + TotalItems = totalItems, + Page = page, + PageSize = pageSize + }; + } + public async Task CreateAsync(ESalesDocument entity) { var mapped = EntityMapper.MapEntity(entity); diff --git a/phronCare.API/Controllers/Sales/SalesDocumentController.cs b/phronCare.API/Controllers/Sales/SalesDocumentController.cs new file mode 100644 index 0000000..27cb741 --- /dev/null +++ b/phronCare.API/Controllers/Sales/SalesDocumentController.cs @@ -0,0 +1,103 @@ +using Core.Interfaces; +using Domain.Dtos.Sales; +using Domain.Generics; +using Microsoft.AspNetCore.Mvc; +using System.Reflection; + +namespace phronCare.API.Controllers.Sales +{ + [Route("api/[controller]")] + [ApiController] + public class SalesDocumentController : ControllerBase + { + private readonly ISalesDocumentDom _salesDocumentService; + + public SalesDocumentController(ISalesDocumentDom salesDocumentService) + { + _salesDocumentService = salesDocumentService ?? throw new ArgumentNullException(nameof(salesDocumentService)); + } + + [HttpGet("search")] + public async Task>> Search( + [FromQuery] int? customerId, + [FromQuery] string? customerText, + [FromQuery] int? quoteId, + [FromQuery] int? documentType, + [FromQuery] int? status, + [FromQuery] DateTime? issueDateFrom, + [FromQuery] DateTime? issueDateTo, + [FromQuery] int page = 1, + [FromQuery] int pageSize = 50) + { + try + { + var result = await _salesDocumentService.SearchAsync( + customerId, + customerText, + quoteId, + documentType, + status, + issueDateFrom, + issueDateTo, + page, + pageSize); + + return Ok(result); + } + catch (Exception ex) + { + var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod"; + return StatusCode(500, $"{methodName} Message: {ex.Message}"); + } + } + + [HttpGet("{id:int}")] + public async Task> GetById(int id) + { + try + { + var salesDocument = await _salesDocumentService.GetDtoByIdAsync(id); + if (salesDocument == null) + return NotFound($"Sales Document con ID {id} no encontrado."); + + return Ok(salesDocument); + } + catch (Exception ex) + { + var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod"; + return StatusCode(500, $"{methodName} Message: {ex.Message}"); + } + } + + [HttpPost] + public async Task> Create([FromBody] SalesDocumentCreateRequest request) + { + try + { + if (request == null) + return BadRequest("El payload no puede ser nulo."); + + var created = await _salesDocumentService.CreateAsync(request); + var salesDocument = await _salesDocumentService.GetDtoByIdAsync(created.Id); + + if (salesDocument == null) + return StatusCode(500, $"No se pudo recuperar el Sales Document creado con ID {created.Id}."); + + return CreatedAtAction(nameof(GetById), new { id = salesDocument.Id }, salesDocument); + } + catch (ArgumentException ex) + { + return BadRequest(ex.Message); + } + catch (InvalidOperationException ex) + { + return BadRequest(ex.Message); + } + catch (Exception ex) + { + var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod"; + return StatusCode(500, $"{methodName} Message: {ex.Message}"); + } + } + } +} diff --git a/phronCare.API/Program.cs b/phronCare.API/Program.cs index 129c6b5..73bbde3 100644 --- a/phronCare.API/Program.cs +++ b/phronCare.API/Program.cs @@ -241,6 +241,9 @@ static void RepositorysAndServices(WebApplicationBuilder builder) builder.Services.AddScoped(); builder.Services.AddScoped(); + + builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped();