diff --git a/Core/Interfaces/Stock/ILSUnitOfMeasureDom.cs b/Core/Interfaces/Stock/ILSUnitOfMeasureDom.cs new file mode 100644 index 0000000..c41fba3 --- /dev/null +++ b/Core/Interfaces/Stock/ILSUnitOfMeasureDom.cs @@ -0,0 +1,16 @@ +using Domain.Entities; +using Domain.Generics; + +namespace Core.Interfaces.Stock +{ + public interface ILSUnitOfMeasureDom + { + Task> SearchAsync(string? text, int page = 1, int pageSize = 100); + Task GetByIdAsync(int id); + Task AddAsync(ELSUnitOfMeasure model); + Task UpdateAsync(ELSUnitOfMeasure model); + Task ExistsByCodeAsync(string code); + Task> GetAllCodesAsync(); + + } +} diff --git a/Core/Services/Stock/LSUnitOfMeasureService.cs b/Core/Services/Stock/LSUnitOfMeasureService.cs new file mode 100644 index 0000000..a78963a --- /dev/null +++ b/Core/Services/Stock/LSUnitOfMeasureService.cs @@ -0,0 +1,35 @@ +using Core.Interfaces.Stock; +using Domain.Entities; +using Domain.Generics; +using Models.Interfaces; + +namespace Core.Services.Stock +{ + public class LSUnitOfMeasureService : ILSUnitOfMeasureDom + { + private readonly IPhLSMUnitOfMeasureRepository _repo; + + public LSUnitOfMeasureService(IPhLSMUnitOfMeasureRepository repo) + { + _repo = repo; + } + + public Task> SearchAsync(string? text, int page = 1, int pageSize = 100) + => _repo.SearchAsync(text, page, pageSize); + + public Task GetByIdAsync(int id) + => _repo.GetByIdAsync(id); + + public Task AddAsync(ELSUnitOfMeasure model) + => _repo.AddAsync(model); + + public Task UpdateAsync(ELSUnitOfMeasure model) + => _repo.UpdateAsync(model); + + public Task ExistsByCodeAsync(string code) + => _repo.ExistsByCodeAsync(code); + + public Task> GetAllCodesAsync() + => _repo.GetAllCodesAsync(); + } +} diff --git a/Domain/Generics/ProductDivisionSearchParams.cs b/Domain/Generics/DivisionUnitSearchParams.cs similarity index 58% rename from Domain/Generics/ProductDivisionSearchParams.cs rename to Domain/Generics/DivisionUnitSearchParams.cs index 545ea47..ad799c5 100644 --- a/Domain/Generics/ProductDivisionSearchParams.cs +++ b/Domain/Generics/DivisionUnitSearchParams.cs @@ -1,6 +1,6 @@ namespace Domain.Generics { - public class ProductDivisionSearchParams : PagedRequest + public class DivisionUnitSearchParams : PagedRequest { public string? Term { get; set; } } diff --git a/Models/Interfaces/IPhLSMUnitOfMeasureRepository.cs b/Models/Interfaces/IPhLSMUnitOfMeasureRepository.cs index 4b904a3..bb16f82 100644 --- a/Models/Interfaces/IPhLSMUnitOfMeasureRepository.cs +++ b/Models/Interfaces/IPhLSMUnitOfMeasureRepository.cs @@ -12,5 +12,9 @@ namespace Models.Interfaces /// Task ExistsByCodeAsync(string code); Task> GetAllCodesAsync(); + Task GetByIdAsync(int id); + Task AddAsync(ELSUnitOfMeasure model); + Task UpdateAsync(ELSUnitOfMeasure model); + Task> SearchAsync(string? text, int page = 1, int pageSize = 100); } } diff --git a/Models/Repositories/Stock/PhLSMUnitOfMeasureRepository.cs b/Models/Repositories/Stock/PhLSMUnitOfMeasureRepository.cs index b59e474..3c2370b 100644 --- a/Models/Repositories/Stock/PhLSMUnitOfMeasureRepository.cs +++ b/Models/Repositories/Stock/PhLSMUnitOfMeasureRepository.cs @@ -23,6 +23,13 @@ namespace Models.Repositories.Stock PageSize = paged.PageSize }; } + public async Task GetByIdAsync(int id) + { + var entity = await _context.PhLsmUnitOfMeasures.FindAsync(id); + return entity != null + ? EntityMapper.MapEntity(entity) + : null; + } public async Task ExistsByCodeAsync(string code) { if (string.IsNullOrWhiteSpace(code)) @@ -37,5 +44,52 @@ namespace Models.Repositories.Stock .Select(x => x.Code) .ToListAsync(); } + public async Task AddAsync(ELSUnitOfMeasure model) + { + var entity = new PhLsmUnitOfMeasure + { + Name = model.Name, + Description = model.Description, + Code = model.Code // si lo tenés + }; + + _context.PhLsmUnitOfMeasures.Add(entity); + await _context.SaveChangesAsync(); + + return entity.Id; + } + public async Task UpdateAsync(ELSUnitOfMeasure model) + { + var entity = await _context.PhLsmUnitOfMeasures.FindAsync(model.Id); + if (entity == null) throw new Exception("Unidad no encontrada"); + + entity.Name = model.Name; + entity.Description = model.Description; + entity.Code = model.Code; + + await _context.SaveChangesAsync(); + } + public async Task> SearchAsync(string? text, int page = 1, int pageSize = 100) + { + var query = _context.PhLsmUnitOfMeasures.AsQueryable(); + + if (!string.IsNullOrWhiteSpace(text)) + { + var lowered = text.ToLower(); + query = query.Where(x => + x.Name.ToLower().Contains(lowered) || + x.Description.ToLower().Contains(lowered)); + } + + var paged = await query.ToPagedResultAsync(page, pageSize); + + return new PagedResult + { + Items = paged.Items.Select(EntityMapper.MapEntity), + TotalItems = paged.TotalItems, + Page = paged.Page, + PageSize = paged.PageSize + }; + } } } diff --git a/phronCare.API/Controllers/Stock/LSUnitOfMeasureController.cs b/phronCare.API/Controllers/Stock/LSUnitOfMeasureController.cs new file mode 100644 index 0000000..c25017c --- /dev/null +++ b/phronCare.API/Controllers/Stock/LSUnitOfMeasureController.cs @@ -0,0 +1,60 @@ +using Core.Interfaces.Stock; +using Domain.Entities; +using Microsoft.AspNetCore.Mvc; + +namespace phronCare.API.Controllers.Stock +{ + [Route("api/[controller]")] + [ApiController] + public class LSUnitOfMeasureController : ControllerBase + { + private readonly ILSUnitOfMeasureDom _service; + + public LSUnitOfMeasureController(ILSUnitOfMeasureDom service) + { + _service = service ?? throw new ArgumentNullException(nameof(service)); + } + + [HttpGet("Search")] + public async Task Search([FromQuery] string? term, [FromQuery] int page = 1, [FromQuery] int pageSize = 50) + { + var result = await _service.SearchAsync(term, page, pageSize); + return Ok(result); + } + + [HttpGet("GetById/{id}")] + public async Task GetById(int id) + { + var result = await _service.GetByIdAsync(id); + return result is null ? NotFound() : Ok(result); + } + + [HttpPost("Create")] + public async Task Create([FromBody] ELSUnitOfMeasure model) + { + try + { + var newId = await _service.AddAsync(model); + return CreatedAtAction(nameof(GetById), new { id = newId }, model); + } + catch (Exception ex) + { + return BadRequest(ex.Message); + } + } + + [HttpPut("Update")] + public async Task Update([FromBody] ELSUnitOfMeasure model) + { + try + { + await _service.UpdateAsync(model); + return Ok(); + } + catch (Exception ex) + { + return BadRequest(ex.Message); + } + } + } +} diff --git a/phronCare.API/Program.cs b/phronCare.API/Program.cs index 53c31c5..0fd06db 100644 --- a/phronCare.API/Program.cs +++ b/phronCare.API/Program.cs @@ -265,6 +265,9 @@ static void RepositorysAndServices(WebApplicationBuilder builder) builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); diff --git a/phronCare.API/obj/Debug/net8.0/ApiEndpoints.json b/phronCare.API/obj/Debug/net8.0/ApiEndpoints.json index 547faba..e9d2daa 100644 --- a/phronCare.API/obj/Debug/net8.0/ApiEndpoints.json +++ b/phronCare.API/obj/Debug/net8.0/ApiEndpoints.json @@ -1200,6 +1200,80 @@ ], "ReturnTypes": [] }, + { + "ContainingType": "phronCare.API.Controllers.Stock.LSUnitOfMeasureController", + "Method": "Create", + "RelativePath": "api/LSUnitOfMeasure/Create", + "HttpMethod": "POST", + "IsController": true, + "Order": 0, + "Parameters": [ + { + "Name": "model", + "Type": "Domain.Entities.ELSUnitOfMeasure", + "IsRequired": true + } + ], + "ReturnTypes": [] + }, + { + "ContainingType": "phronCare.API.Controllers.Stock.LSUnitOfMeasureController", + "Method": "GetById", + "RelativePath": "api/LSUnitOfMeasure/GetById/{id}", + "HttpMethod": "GET", + "IsController": true, + "Order": 0, + "Parameters": [ + { + "Name": "id", + "Type": "System.Int32", + "IsRequired": true + } + ], + "ReturnTypes": [] + }, + { + "ContainingType": "phronCare.API.Controllers.Stock.LSUnitOfMeasureController", + "Method": "Search", + "RelativePath": "api/LSUnitOfMeasure/Search", + "HttpMethod": "GET", + "IsController": true, + "Order": 0, + "Parameters": [ + { + "Name": "term", + "Type": "System.String", + "IsRequired": false + }, + { + "Name": "page", + "Type": "System.Int32", + "IsRequired": false + }, + { + "Name": "pageSize", + "Type": "System.Int32", + "IsRequired": false + } + ], + "ReturnTypes": [] + }, + { + "ContainingType": "phronCare.API.Controllers.Stock.LSUnitOfMeasureController", + "Method": "Update", + "RelativePath": "api/LSUnitOfMeasure/Update", + "HttpMethod": "PUT", + "IsController": true, + "Order": 0, + "Parameters": [ + { + "Name": "model", + "Type": "Domain.Entities.ELSUnitOfMeasure", + "IsRequired": true + } + ], + "ReturnTypes": [] + }, { "ContainingType": "phronCare.API.Controllers.Sales.PatientController", "Method": "GetById", diff --git a/phronCare.UIBlazor/Pages/Stock/LSProduct.razor b/phronCare.UIBlazor/Pages/Stock/LSProduct.razor index 3dddb0d..f6a5bd7 100644 --- a/phronCare.UIBlazor/Pages/Stock/LSProduct.razor +++ b/phronCare.UIBlazor/Pages/Stock/LSProduct.razor @@ -147,8 +147,8 @@ { new PhTable.ButtonOptions { - Caption = "Editar", - ElementClass = "btn btn-primary btn-sm", + Caption = "", + ElementClass = "btn btn-success btn-sm", UrlAction = "/stock/productform/", OnClickAction = async (id) => { @@ -157,8 +157,6 @@ } } }; - - // await Buscar(); } private async Task Buscar() @@ -177,8 +175,8 @@ { { "Id", p.Id }, { "Código Fábrica", p.FactoryCode }, - { "Código Externo", p.ExternalCode }, - { "Nombre", p.Name }, + { "Código Externo", p.ExternalCode?? string.Empty }, + { "Nombre", p.Name?? string.Empty }, { "Descripción", p.Descripcion }, { "División", p.Division?.Name ?? "" }, { "Unidad", p.Unit?.Name ?? "" }, diff --git a/phronCare.UIBlazor/Pages/Stock/LSUnitOfMeasure.razor b/phronCare.UIBlazor/Pages/Stock/LSUnitOfMeasure.razor new file mode 100644 index 0000000..8e2e56a --- /dev/null +++ b/phronCare.UIBlazor/Pages/Stock/LSUnitOfMeasure.razor @@ -0,0 +1,156 @@ +@page "/stock/units" + +@using Domain.Generics +@using phronCare.UIBlazor.Services.Stock + +@inject LSUnitOfMeasureService unitService +@inject IToastService toastService +@inject NavigationManager Navigation + +
+
+

Unidades de medida

+
+
+
+ + + + +
+
+
+ @if (Tabla != null && Tabla.Any()) + { + + } + else + { +

No hay resultados.

+ } +
+
+ +
+ +@code { + private PagedResult? Resultado; + private List> Tabla = new(); + private List Columnas = new() { "Id", "Nombre", "Descripción" }; + private DivisionUnitSearchParams SearchParams = new() { PageSize = 10 }; + private List Botones = new(); + private int PaginaDeseada = 1; + + protected override void OnInitialized() + { + Botones = new List + { + new PhTable.ButtonOptions + { + Caption = "", + ElementClass = "btn btn-success", + UrlAction = "/stock/unitform/", + OnClickAction = async (id) => + { + Navigation.NavigateTo($"/stock/unitform/{id}"); + } + } + }; + } + + private async Task Buscar() + { + SearchParams.Page = 1; + await CargarPaginaActual(); + } + + private async Task CargarPaginaActual() + { + Resultado = await unitService.SearchAsync(SearchParams.Term, SearchParams.Page, SearchParams.PageSize); + if (Resultado?.Items != null) + { + Tabla = Resultado.Items.Select(u => new Dictionary + { + { "Id", u.Id }, + { "Nombre", u.Name ?? string.Empty }, + { "Descripción", u.Description ?? string.Empty } + }).ToList(); + } + } + + private async Task PrimeraPagina() { SearchParams.Page = 1; await CargarPaginaActual(); } + private async Task UltimaPagina() { SearchParams.Page = TotalPaginas; await CargarPaginaActual(); } + private async Task SiguientePagina() => await CambiarPagina(1); + private async Task AnteriorPagina() => await CambiarPagina(-1); + + private async Task CambiarPagina(int delta) + { + var nuevaPagina = SearchParams.Page + delta; + if (nuevaPagina >= 1 && nuevaPagina <= TotalPaginas) + { + SearchParams.Page = nuevaPagina; + await CargarPaginaActual(); + } + } + + private async Task IrAPagina() + { + if (PaginaDeseada >= 1 && PaginaDeseada <= TotalPaginas) + { + SearchParams.Page = PaginaDeseada; + await Buscar(); + } + else + { + toastService.ShowWarning("Número de página fuera de rango."); + } + } + + private void Nuevo() => Navigation.NavigateTo("/stock/unitform/"); + private void Volver() => Navigation.NavigateTo("/DashboardPanel"); + + private int TotalPaginas => Resultado == null || Resultado.TotalItems == 0 + ? 1 + : (int)Math.Ceiling((double)(Resultado.TotalItems) / SearchParams.PageSize); + + private bool PuedeRetroceder => Resultado != null && SearchParams.Page > 1; + private bool PuedeAvanzar => Resultado != null && SearchParams.Page < TotalPaginas; +} diff --git a/phronCare.UIBlazor/Pages/Stock/LSUnitOfMeasureForm.razor b/phronCare.UIBlazor/Pages/Stock/LSUnitOfMeasureForm.razor new file mode 100644 index 0000000..68b2d51 --- /dev/null +++ b/phronCare.UIBlazor/Pages/Stock/LSUnitOfMeasureForm.razor @@ -0,0 +1,84 @@ +@page "/stock/unitform/" +@page "/stock/unitform/{Id:int?}" + +@using phronCare.UIBlazor.Services.Stock +@inject LSUnitOfMeasureService unitService +@inject NavigationManager Navigation +@inject IToastService toastService + + + + + +
+
+

@((model.Id == 0) ? "Nueva Unidad de Medida" : "Editar Unidad de Medida")

+
+ +
+
+
+ + + +
+ +
+ + + +
+
+
+ + +
+
+ +@code { + [Parameter] + public int? Id { get; set; } + + private ELSUnitOfMeasure model = new(); + + protected override async Task OnInitializedAsync() + { + if (Id.HasValue && Id > 0) + { + var result = await unitService.GetByIdAsync(Id.Value); + if (result != null) + model = result; + else + toastService.ShowError("No se pudo cargar la unidad de medida."); + } + } + + private async Task HandleValidSubmit() + { + try + { + if (model.Id == 0) + { + await unitService.CreateAsync(model); + } + else + { + await unitService.UpdateAsync(model); + } + + toastService.ShowSuccess("Unidad guardada correctamente."); + Navigation.NavigateTo("/stock/units"); + } + catch (Exception ex) + { + toastService.ShowError($"Error al guardar: {ex.Message}"); + } + } + + private void Cancel() => Navigation.NavigateTo("/stock/units"); +} diff --git a/phronCare.UIBlazor/Pages/Stock/ProductDivision.razor b/phronCare.UIBlazor/Pages/Stock/ProductDivision.razor index e8c6517..ac4b78b 100644 --- a/phronCare.UIBlazor/Pages/Stock/ProductDivision.razor +++ b/phronCare.UIBlazor/Pages/Stock/ProductDivision.razor @@ -74,7 +74,7 @@ private PagedResult? Resultado; private List> Tabla = new(); private List Columnas = new() { "Id", "Codigo", "Nombre", "Descripción" }; - private ProductDivisionSearchParams SearchParams = new() { PageSize = 10 }; + private DivisionUnitSearchParams SearchParams = new() { PageSize = 10 }; private List Botones; private int PaginaDeseada = 1; @@ -84,8 +84,8 @@ { new PhTable.ButtonOptions { - Caption = "Editar", - ElementClass = "btn btn-primary btn-sm", + Caption = "", + ElementClass = "btn btn-success", UrlAction = "/stock/productdivisionform/", OnClickAction = async (id) => { diff --git a/phronCare.UIBlazor/Program.cs b/phronCare.UIBlazor/Program.cs index 32fff4b..7b82878 100644 --- a/phronCare.UIBlazor/Program.cs +++ b/phronCare.UIBlazor/Program.cs @@ -66,5 +66,7 @@ static void InjectDependencies(WebAssemblyHostBuilder builder) builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); + } \ No newline at end of file diff --git a/phronCare.UIBlazor/Services/Stock/LSUnitOfMeasureService.cs b/phronCare.UIBlazor/Services/Stock/LSUnitOfMeasureService.cs new file mode 100644 index 0000000..d7b5a87 --- /dev/null +++ b/phronCare.UIBlazor/Services/Stock/LSUnitOfMeasureService.cs @@ -0,0 +1,41 @@ +using Domain.Entities; +using Domain.Generics; +using System.Net.Http.Json; + +namespace phronCare.UIBlazor.Services.Stock +{ + public class LSUnitOfMeasureService + { + private readonly HttpClient _http; + + public LSUnitOfMeasureService(HttpClient http) + { + _http = http; + } + + public async Task> SearchAsync(string? term, int page = 1, int pageSize = 50) + { + var response = await _http.GetFromJsonAsync>( + $"api/LSUnitOfMeasure/Search?term={term}&page={page}&pageSize={pageSize}"); + return response!; + } + + public async Task GetByIdAsync(int id) + { + return await _http.GetFromJsonAsync($"api/LSUnitOfMeasure/GetById/{id}"); + } + + public async Task CreateAsync(ELSUnitOfMeasure model) + { + var response = await _http.PostAsJsonAsync("api/LSUnitOfMeasure/Create", model); + response.EnsureSuccessStatusCode(); + return await response.Content.ReadFromJsonAsync(); + } + + public async Task UpdateAsync(ELSUnitOfMeasure model) + { + var response = await _http.PutAsJsonAsync("api/LSUnitOfMeasure/Update", model); + response.EnsureSuccessStatusCode(); + } + } +} diff --git a/phronCare.UIBlazor/Services/Stock/ProductDivisionService.cs b/phronCare.UIBlazor/Services/Stock/ProductDivisionService.cs index db67d19..716d293 100644 --- a/phronCare.UIBlazor/Services/Stock/ProductDivisionService.cs +++ b/phronCare.UIBlazor/Services/Stock/ProductDivisionService.cs @@ -43,7 +43,7 @@ namespace phronCare.UIBlazor.Services.Stock return await _http.DeleteAsync($"/api/ProductDivision/Delete/{id}"); } - public async Task?> SearchAsync(ProductDivisionSearchParams searchParams) + public async Task?> SearchAsync(DivisionUnitSearchParams searchParams) { var url = $"/api/ProductDivision/Search?" + $"term={searchParams.Term}&" + diff --git a/phronCare.UIBlazor/Shared/Components/PhTable.razor b/phronCare.UIBlazor/Shared/Components/PhTable.razor index 4f85818..045591f 100644 --- a/phronCare.UIBlazor/Shared/Components/PhTable.razor +++ b/phronCare.UIBlazor/Shared/Components/PhTable.razor @@ -178,7 +178,7 @@ - @button.Caption + @((MarkupString)button.Caption) } }