Add Products
All checks were successful
CI/CD Pipeline / Build and Deploy with Docker Compose (push) Successful in 16m35s

This commit is contained in:
Leandro Hernan Rojas 2025-06-30 16:15:08 -03:00
parent ebbfe90d1e
commit 1be33c37b5
30 changed files with 1124 additions and 58 deletions

View File

@ -0,0 +1,10 @@
using Domain.Entities;
namespace Core.Interfaces
{
public interface ILSMLookUpDom
{
Task<IEnumerable<ELookUpItem>> ProductDivisionsListAsync(string filter, int limit = 10);
Task<IEnumerable<ELookUpItem>> UnitsOfMeasureListAsync(string filter, int limit = 10);
}
}

View File

@ -0,0 +1,15 @@
using Domain.Entities;
using Domain.Generics;
namespace Core.Interfaces.Stock
{
public interface ILSProductDivisionDom
{
Task<PagedResult<ELSProductDivision>> GetAllAsync(int page = 1, int pageSize = 50);
Task<ELSProductDivision?> GetByIdAsync(int id);
Task<PagedResult<ELSProductDivision>> SearchAsync(string? term, int page = 1, int pageSize = 50);
Task<ELSProductDivision> CreateAsync(ELSProductDivision entity);
Task<bool> UpdateAsync(ELSProductDivision entity);
Task<bool> DeleteAsync(int id);
}
}

View File

@ -0,0 +1,15 @@
using Domain.Entities;
using Domain.Generics;
namespace Core.Interfaces
{
public interface ILSProductDom
{
Task<PagedResult<ELSProduct>> SearchAsync(LSProductSearchParams searchParams);
Task<ELSProduct?> GetByIdAsync(int id);
Task<ELSProduct> CreateAsync(ELSProduct entity);
Task<bool> UpdateAsync(ELSProduct entity);
Task<bool> DeleteAsync(int id);
Task<byte[]> ExportToExcelAsync(LSProductSearchParams searchParams);
}
}

View File

@ -1,15 +0,0 @@
using Domain.Entities;
using Domain.Generics;
namespace Core.Interfaces.Stock
{
public interface IProductDivisionDom
{
Task<PagedResult<EProductDivision>> GetAllAsync(int page = 1, int pageSize = 50);
Task<EProductDivision?> GetByIdAsync(int id);
Task<PagedResult<EProductDivision>> SearchAsync(string? term, int page = 1, int pageSize = 50);
Task<EProductDivision> CreateAsync(EProductDivision entity);
Task<bool> UpdateAsync(EProductDivision entity);
Task<bool> DeleteAsync(int id);
}
}

View File

@ -0,0 +1,26 @@
using Core.Interfaces;
using Domain.Entities;
using Models.Interfaces;
namespace Core.Services
{
public class LSMLookUpService : ILSMLookUpDom
{
#region Declaraciones y Constructor
private readonly IPhLSMLookUpRepository _repository;
public LSMLookUpService(IPhLSMLookUpRepository repository)
{
_repository = repository ?? throw new ArgumentNullException(nameof(repository));
}
#endregion
#region Métodos de clase
public Task<IEnumerable<ELookUpItem>> ProductDivisionsListAsync(string filter, int limit = 10)
=> _repository.ProductDivisionsAsync(filter, limit);
public Task<IEnumerable<ELookUpItem>> UnitsOfMeasureListAsync(string filter, int limit = 10)
=> _repository.UnitsOfMeasureAsync(filter, limit);
#endregion
}
}

View File

@ -7,18 +7,18 @@ using System.Reflection;
namespace Core.Services.Stock namespace Core.Services.Stock
{ {
public class ProductDivisionService : IProductDivisionDom public class LSProductDivisionService : ILSProductDivisionDom
{ {
#region Declaraciones y Constructor #region Declaraciones y Constructor
private readonly IPhLSMProductDivisionRepository _repository; private readonly IPhLSMProductDivisionRepository _repository;
public ProductDivisionService(IPhLSMProductDivisionRepository repository) public LSProductDivisionService(IPhLSMProductDivisionRepository repository)
{ {
_repository = repository ?? throw new ArgumentNullException(nameof(repository)); _repository = repository ?? throw new ArgumentNullException(nameof(repository));
} }
#endregion #endregion
#region Métodos de clase #region Métodos de clase
public async Task<PagedResult<EProductDivision>> GetAllAsync(int page = 1, int pageSize = 50) public async Task<PagedResult<ELSProductDivision>> GetAllAsync(int page = 1, int pageSize = 50)
{ {
try try
{ {
@ -31,7 +31,7 @@ namespace Core.Services.Stock
} }
} }
public async Task<EProductDivision?> GetByIdAsync(int id) public async Task<ELSProductDivision?> GetByIdAsync(int id)
{ {
try try
{ {
@ -44,7 +44,7 @@ namespace Core.Services.Stock
} }
} }
public async Task<PagedResult<EProductDivision>> SearchAsync(string? term, int page = 1, int pageSize = 50) public async Task<PagedResult<ELSProductDivision>> SearchAsync(string? term, int page = 1, int pageSize = 50)
{ {
try try
{ {
@ -57,7 +57,7 @@ namespace Core.Services.Stock
} }
} }
public async Task<EProductDivision> CreateAsync(EProductDivision entity) public async Task<ELSProductDivision> CreateAsync(ELSProductDivision entity)
{ {
try try
{ {
@ -70,7 +70,7 @@ namespace Core.Services.Stock
} }
} }
public async Task<bool> UpdateAsync(EProductDivision entity) public async Task<bool> UpdateAsync(ELSProductDivision entity)
{ {
try try
{ {

View File

@ -0,0 +1,81 @@
using Core.Interfaces;
using Domain.Entities;
using Domain.Generics;
using Models.Interfaces;
using System.Reflection;
using Transversal.Services;
namespace Core.Services
{
public class LSProductService : ILSProductDom
{
private readonly IPhLSMProductRepository _repository;
public LSProductService(IPhLSMProductRepository repository)
{
_repository = repository ?? throw new ArgumentNullException(nameof(repository));
}
public async Task<PagedResult<ELSProduct>> SearchAsync(LSProductSearchParams searchParams)
{
return await _repository.SearchAsync(searchParams);
}
public async Task<ELSProduct?> GetByIdAsync(int id)
{
return await _repository.GetByIdAsync(id);
}
public async Task<ELSProduct> CreateAsync(ELSProduct entity)
{
return await _repository.CreateAsync(entity);
}
public async Task<bool> UpdateAsync(ELSProduct entity)
{
return await _repository.UpdateAsync(entity);
}
public async Task<bool> DeleteAsync(int id)
{
return await _repository.DeleteAsync(id);
}
public async Task<byte[]> ExportToExcelAsync(LSProductSearchParams searchParams)
{
try
{
var result = await _repository.SearchAsync(searchParams);
if (result?.Items == null || !result.Items.Any())
throw new Exception("No se encontraron productos para exportar.");
var exportador = new XLSXExportBase();
var exportData = result.Items.Select(p => new
{
p.Id,
p.FactoryCode,
p.ExternalCode,
p.Name,
p.Descripcion,
Tipo = p.ProductType == 1 ? "Implantable" :
p.ProductType == 2 ? "Instrumental" :
p.ProductType == 3 ? "Inyectable" : "Otro",
Trazabilidad = p.TraceabilityType == 1 ? "No aplica" :
p.TraceabilityType == 2 ? "Por cantidad" :
p.TraceabilityType == 3 ? "Por lote y vencimiento" : "Otro",
PlusProcess = p.PlusProcess ? "Sí" : "No",
División = p.Division?.Name ?? "-",
Unidad = p.Unit?.Name ?? "-"
}).ToList();
return exportador.ExportExcel(exportData);
}
catch (Exception ex)
{
var method = MethodBase.GetCurrentMethod()?.Name ?? "ExportToExcel";
throw new Exception($"Error en {method}: {ex.Message}", ex);
}
}
}
}

View File

@ -0,0 +1,59 @@
namespace Domain.Entities
{
public partial class ELSProduct
{
/// <summary>
/// Identificador único del producto médico o industrial
/// </summary>
public int Id { get; set; }
/// <summary>
/// Código de producto definido por la fábrica o fabricante. Puede variar según proveedor, presentación o país de origen.
/// </summary>
public string FactoryCode { get; set; } = string.Empty;
/// <summary>
/// Nombre técnico o estandarizado del producto (ej: ficha técnica, fabricante)
/// </summary>
public string? Name { get; set; } = string.Empty;
/// <summary>
/// Descripción comercial o práctica del producto (impresión logística, uso cotidiano)
/// </summary>
public string Descripcion { get; set; } = string.Empty;
/// <summary>
/// Tipo de producto: 1=Implantable, 2=Instrumental, 3=Inyectable, etc.
/// </summary>
public int ProductType { get; set; }
/// <summary>
/// Tipo de trazabilidad: 1=No aplica, 2=Por cantidad, 3=Por lote y vencimiento
/// </summary>
public int TraceabilityType { get; set; }
/// <summary>
/// Indica si el producto requiere un proceso adicional previo a su uso (ej: esterilización, calibración, limpieza, inspección, etc.)
/// </summary>
public bool PlusProcess { get; set; }
/// <summary>
/// Código externo estándar del producto (ej: GTIN, código de proveedor, catálogo EAN, etc.)
/// </summary>
public string? ExternalCode { get; set; } = string.Empty;
/// <summary>
/// División o familia técnica del producto (ej: columna, trauma, descartables, etc.)
/// </summary>
public int? DivisionId { get; set; }
/// <summary>
/// Unidad de medida base del producto (ej: unidad, mililitro, metro)
/// </summary>
public int UnitId { get; set; }
public virtual ELSProductDivision? Division { get; set; }
public virtual ELSUnitOfMeasure Unit { get; set; } = null!;
}
}

View File

@ -1,6 +1,6 @@
namespace Domain.Entities namespace Domain.Entities
{ {
public class EProductDivision public class ELSProductDivision
{ {
/// <summary> /// <summary>
/// Identificador único de la división de productos /// Identificador único de la división de productos

View File

@ -0,0 +1,23 @@
namespace Domain.Entities
{
public class ELSUnitOfMeasure
{
public int Id { get; set; }
/// <summary>
/// Código abreviado de unidad de medida (ej: UN, ML, MT)
/// </summary>
public string Code { get; set; } = null!;
/// <summary>
/// Nombre descriptivo de la unidad de medida
/// </summary>
public string Name { get; set; } = null!;
/// <summary>
/// Descripción extendida o notas adicionales sobre la unidad
/// </summary>
public string? Description { get; set; }
}
}

View File

@ -0,0 +1,41 @@
namespace Domain.Generics
{
public class LSProductSearchParams : PagedRequest
{
/// <summary>
/// Código interno o externo del producto (factory_code o external_code)
/// </summary>
public string? Code { get; set; }
/// <summary>
/// Nombre técnico o descripción comercial (name o descripcion)
/// </summary>
public string? Description { get; set; }
/// <summary>
/// Tipo de producto: 1=Implantable, 2=Instrumental, 3=Inyectable
/// </summary>
public int? ProductType { get; set; }
/// <summary>
/// Tipo de trazabilidad: 1=No aplica, 2=Por cantidad, 3=Por lote y vencimiento
/// </summary>
public int? TraceabilityType { get; set; }
/// <summary>
/// División técnica del producto (FK a ProductDivision)
/// </summary>
public int? DivisionId { get; set; }
/// <summary>
/// Unidad de medida base del producto (FK a UnitOfMeasure)
/// </summary>
public int? UnitId { get; set; }
/// <summary>
/// Indica si el producto requiere un proceso adicional como esterilización
/// </summary>
public bool? PlusProcess { get; set; }
}
}

View File

@ -0,0 +1,10 @@
using Domain.Entities;
namespace Models.Interfaces
{
public interface IPhLSMLookUpRepository
{
Task<IEnumerable<ELookUpItem>> ProductDivisionsAsync(string filter, int limit = 10);
Task<IEnumerable<ELookUpItem>> UnitsOfMeasureAsync(string filter, int limit = 10);
}
}

View File

@ -5,11 +5,11 @@ namespace Models.Interfaces
{ {
public interface IPhLSMProductDivisionRepository public interface IPhLSMProductDivisionRepository
{ {
Task<EProductDivision> CreateAsync(EProductDivision entity); Task<ELSProductDivision> CreateAsync(ELSProductDivision entity);
Task<bool> DeleteAsync(int id); Task<bool> DeleteAsync(int id);
Task<PagedResult<EProductDivision>> GetAllAsync(int page = 1, int pageSize = 50); Task<PagedResult<ELSProductDivision>> GetAllAsync(int page = 1, int pageSize = 50);
Task<EProductDivision?> GetByIdAsync(int id); Task<ELSProductDivision?> GetByIdAsync(int id);
Task<PagedResult<EProductDivision>> SearchAsync(string? term, int page = 1, int pageSize = 50); Task<PagedResult<ELSProductDivision>> SearchAsync(string? term, int page = 1, int pageSize = 50);
Task<bool> UpdateAsync(EProductDivision entity); Task<bool> UpdateAsync(ELSProductDivision entity);
} }
} }

View File

@ -0,0 +1,15 @@
using Domain.Entities;
using Domain.Generics;
using Models.Models;
namespace Models.Interfaces
{
public interface IPhLSMProductRepository
{
Task<PagedResult<ELSProduct>> SearchAsync(LSProductSearchParams searchParams);
Task<ELSProduct?> GetByIdAsync(int id);
Task<ELSProduct> CreateAsync(ELSProduct entity);
Task<bool> UpdateAsync(ELSProduct entity);
Task<bool> DeleteAsync(int id);
}
}

View File

@ -0,0 +1,39 @@
using Microsoft.EntityFrameworkCore;
using Domain.Entities;
using Models.Interfaces;
using Models.Models;
namespace Models.Repositories
{
public class PhLSMLookUpRepository(PhronCareOperationsHubContext context) : IPhLSMLookUpRepository
{
private readonly PhronCareOperationsHubContext _context = context;
public async Task<IEnumerable<ELookUpItem>> ProductDivisionsAsync(string filter, int limit = 10)
=> await _context.PhLsmProductDivisions
.Where(d =>
d.Code.Contains(filter) ||
d.Name.Contains(filter) ||
d.Description.Contains(filter))
.OrderBy(d => d.Code)
.Select(d => new ELookUpItem
{
Id = d.Id,
Nombre = d.Code + " | " + d.Name
})
.Take(limit)
.ToListAsync();
public async Task<IEnumerable<ELookUpItem>> UnitsOfMeasureAsync(string filter, int limit = 10)
=> await _context.PhLsmUnitOfMeasures
.Where(u => u.Code.Contains(filter) || u.Name.Contains(filter))
.OrderBy(u => u.Code)
.Select(u => new ELookUpItem
{
Id = u.Id,
Nombre = u.Code + " | " + u.Name
})
.Take(limit)
.ToListAsync();
}
}

View File

@ -11,28 +11,28 @@ namespace Models.Repositories.Stock
{ {
private readonly PhronCareOperationsHubContext _context = context; private readonly PhronCareOperationsHubContext _context = context;
public async Task<PagedResult<EProductDivision>> GetAllAsync(int page = 1, int pageSize = 50) public async Task<PagedResult<ELSProductDivision>> GetAllAsync(int page = 1, int pageSize = 50)
{ {
var query = _context.PhLsmProductDivisions.AsQueryable(); var query = _context.PhLsmProductDivisions.AsQueryable();
var pagedEntities = await query.ToPagedResultAsync(page, pageSize); var pagedEntities = await query.ToPagedResultAsync(page, pageSize);
return new PagedResult<EProductDivision> return new PagedResult<ELSProductDivision>
{ {
Items = pagedEntities.Items.Select(EntityMapper.MapEntity<PhLsmProductDivision, EProductDivision>), Items = pagedEntities.Items.Select(EntityMapper.MapEntity<PhLsmProductDivision, ELSProductDivision>),
TotalItems = pagedEntities.TotalItems, TotalItems = pagedEntities.TotalItems,
Page = pagedEntities.Page, Page = pagedEntities.Page,
PageSize = pagedEntities.PageSize PageSize = pagedEntities.PageSize
}; };
} }
public async Task<EProductDivision?> GetByIdAsync(int id) public async Task<ELSProductDivision?> GetByIdAsync(int id)
{ {
var entity = await _context.PhLsmProductDivisions.FirstOrDefaultAsync(x => x.Id == id); var entity = await _context.PhLsmProductDivisions.FirstOrDefaultAsync(x => x.Id == id);
return entity is null ? null : EntityMapper.MapEntity<PhLsmProductDivision, EProductDivision>(entity); return entity is null ? null : EntityMapper.MapEntity<PhLsmProductDivision, ELSProductDivision>(entity);
} }
public async Task<PagedResult<EProductDivision>> SearchAsync(string? term, int page = 1, int pageSize = 50) public async Task<PagedResult<ELSProductDivision>> SearchAsync(string? term, int page = 1, int pageSize = 50)
{ {
var query = _context.PhLsmProductDivisions.AsQueryable(); var query = _context.PhLsmProductDivisions.AsQueryable();
@ -47,26 +47,26 @@ namespace Models.Repositories.Stock
} }
var pagedEntities = await query.ToPagedResultAsync(page, pageSize); var pagedEntities = await query.ToPagedResultAsync(page, pageSize);
return new PagedResult<EProductDivision> return new PagedResult<ELSProductDivision>
{ {
Items = pagedEntities.Items.Select(EntityMapper.MapEntity<PhLsmProductDivision, EProductDivision>), Items = pagedEntities.Items.Select(EntityMapper.MapEntity<PhLsmProductDivision, ELSProductDivision>),
TotalItems = pagedEntities.TotalItems, TotalItems = pagedEntities.TotalItems,
Page = pagedEntities.Page, Page = pagedEntities.Page,
PageSize = pagedEntities.PageSize PageSize = pagedEntities.PageSize
}; };
} }
public async Task<EProductDivision> CreateAsync(EProductDivision entity) public async Task<ELSProductDivision> CreateAsync(ELSProductDivision entity)
{ {
if (entity == null) if (entity == null)
throw new ArgumentNullException(nameof(entity)); throw new ArgumentNullException(nameof(entity));
try try
{ {
var mapped = EntityMapper.MapEntity<EProductDivision, PhLsmProductDivision>(entity); var mapped = EntityMapper.MapEntity<ELSProductDivision, PhLsmProductDivision>(entity);
_context.PhLsmProductDivisions.Add(mapped); _context.PhLsmProductDivisions.Add(mapped);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
return EntityMapper.MapEntity<PhLsmProductDivision, EProductDivision>(mapped); return EntityMapper.MapEntity<PhLsmProductDivision, ELSProductDivision>(mapped);
} }
catch (DbUpdateException dbEx) catch (DbUpdateException dbEx)
{ {
@ -78,7 +78,7 @@ namespace Models.Repositories.Stock
} }
} }
public async Task<bool> UpdateAsync(EProductDivision entity) public async Task<bool> UpdateAsync(ELSProductDivision entity)
{ {
if (entity == null) if (entity == null)
throw new ArgumentNullException(nameof(entity)); throw new ArgumentNullException(nameof(entity));

View File

@ -0,0 +1,101 @@
using Domain.Entities;
using Domain.Generics;
using Microsoft.EntityFrameworkCore;
using Models.Helpers;
using Models.Interfaces;
using Models.Models;
namespace Models.Repositories
{
public class PhLSMProductRepository(PhronCareOperationsHubContext context) : IPhLSMProductRepository
{
private readonly PhronCareOperationsHubContext _context = context;
public async Task<PagedResult<ELSProduct>> SearchAsync(LSProductSearchParams searchParams)
{
var query = _context.PhLsmProducts
.Include(p => p.Division)
.Include(p => p.Unit)
.AsQueryable();
if (!string.IsNullOrWhiteSpace(searchParams.Code))
{
var lowered = searchParams.Code.ToLower();
query = query.Where(p =>
(!string.IsNullOrEmpty(p.FactoryCode) && p.FactoryCode.ToLower().Contains(lowered)) ||
(!string.IsNullOrEmpty(p.ExternalCode) && p.ExternalCode.ToLower().Contains(lowered)));
}
if (!string.IsNullOrWhiteSpace(searchParams.Description))
{
var lowered = searchParams.Description.ToLower();
query = query.Where(p =>
(!string.IsNullOrEmpty(p.Name) && p.Name.ToLower().Contains(lowered)) ||
(!string.IsNullOrEmpty(p.Descripcion) && p.Descripcion.ToLower().Contains(lowered)));
}
if (searchParams.ProductType.HasValue)
query = query.Where(p => p.ProductType == searchParams.ProductType);
if (searchParams.TraceabilityType.HasValue)
query = query.Where(p => p.TraceabilityType == searchParams.TraceabilityType);
if (searchParams.DivisionId.HasValue)
query = query.Where(p => p.DivisionId == searchParams.DivisionId);
if (searchParams.UnitId.HasValue)
query = query.Where(p => p.UnitId == searchParams.UnitId);
if (searchParams.PlusProcess.HasValue)
query = query.Where(p => p.PlusProcess == searchParams.PlusProcess);
var paged = await query.ToPagedResultAsync(searchParams.Page, searchParams.PageSize);
return new PagedResult<ELSProduct>
{
Items = paged.Items.Select(EntityMapper.MapEntity<PhLsmProduct, ELSProduct>),
TotalItems = paged.TotalItems,
Page = paged.Page,
PageSize = paged.PageSize
};
}
public async Task<ELSProduct?> GetByIdAsync(int id)
{
var entity = await _context.PhLsmProducts
.Include(p => p.Division)
.Include(p => p.Unit)
.FirstOrDefaultAsync(p => p.Id == id);
return entity != null ? EntityMapper.MapEntity<PhLsmProduct, ELSProduct>(entity) : null;
}
public async Task<ELSProduct> CreateAsync(ELSProduct entity)
{
var mapped = EntityMapper.MapEntity<ELSProduct, PhLsmProduct>(entity);
_context.PhLsmProducts.Add(mapped);
await _context.SaveChangesAsync();
return EntityMapper.MapEntity<PhLsmProduct, ELSProduct>(mapped);
}
public async Task<bool> UpdateAsync(ELSProduct entity)
{
var existing = await _context.PhLsmProducts.FindAsync(entity.Id);
if (existing == null) return false;
EntityMapper.MapEntityToExisting(entity, existing);
await _context.SaveChangesAsync();
return true;
}
public async Task<bool> DeleteAsync(int id)
{
var entity = await _context.PhLsmProducts.FindAsync(id);
if (entity == null) return false;
_context.PhLsmProducts.Remove(entity);
await _context.SaveChangesAsync();
return true;
}
}
}

View File

@ -0,0 +1,22 @@
using Core.Interfaces;
using Domain.Entities;
using Microsoft.AspNetCore.Mvc;
namespace phronCare.API.Controllers.Stock
{
[Route("api/[controller]")]
[ApiController]
public class LSMLookUpController : ControllerBase
{
private readonly ILSMLookUpDom _lookup;
public LSMLookUpController(ILSMLookUpDom lookup) => _lookup = lookup;
[HttpGet("productdivisions")]
public Task<IEnumerable<ELookUpItem>> ProductDivisions([FromQuery] string q = "")
=> _lookup.ProductDivisionsListAsync(q);
[HttpGet("units")]
public Task<IEnumerable<ELookUpItem>> Units([FromQuery] string q = "")
=> _lookup.UnitsOfMeasureListAsync(q);
}
}

View File

@ -0,0 +1,68 @@
using Core.Interfaces;
using Domain.Entities;
using Domain.Generics;
using Microsoft.AspNetCore.Mvc;
namespace API.Controllers.Stock
{
[Route("api/[controller]")]
[ApiController]
public class LSProductController : ControllerBase
{
private readonly ILSProductDom _service;
public LSProductController(ILSProductDom service)
{
_service = service;
}
[HttpPost("search")]
public async Task<ActionResult<PagedResult<ELSProduct>>> Search([FromBody] LSProductSearchParams searchParams)
{
var result = await _service.SearchAsync(searchParams);
return Ok(result);
}
[HttpGet("{id}")]
public async Task<ActionResult<ELSProduct>> GetById(int id)
{
var product = await _service.GetByIdAsync(id);
if (product == null)
return NotFound($"No se encontró el producto con ID {id}.");
return Ok(product);
}
[HttpPost("create")]
public async Task<ActionResult<ELSProduct>> Create([FromBody] ELSProduct model)
{
var created = await _service.CreateAsync(model);
return CreatedAtAction(nameof(GetById), new { id = created.Id }, created);
}
[HttpPut("update")]
public async Task<IActionResult> Update([FromBody] ELSProduct model)
{
var success = await _service.UpdateAsync(model);
if (!success)
return NotFound("No se pudo actualizar el producto.");
return Ok();
}
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id)
{
var success = await _service.DeleteAsync(id);
if (!success)
return NotFound("No se pudo eliminar el producto.");
return Ok();
}
[HttpPost("exportfiltered")]
public async Task<IActionResult> ExportFiltered([FromBody] LSProductSearchParams searchParams)
{
var content = await _service.ExportToExcelAsync(searchParams);
var fileName = $"productos_{DateTime.Now:yyyyMMddHHmm}.xlsx";
return File(content, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName);
}
}
}

View File

@ -8,9 +8,9 @@ namespace phronCare.API.Controllers.Stock
[ApiController] [ApiController]
public class ProductDivisionController : ControllerBase public class ProductDivisionController : ControllerBase
{ {
private readonly IProductDivisionDom _service; private readonly ILSProductDivisionDom _service;
public ProductDivisionController(IProductDivisionDom service) public ProductDivisionController(ILSProductDivisionDom service)
{ {
_service = service ?? throw new ArgumentNullException(nameof(service)); _service = service ?? throw new ArgumentNullException(nameof(service));
} }
@ -54,7 +54,7 @@ namespace phronCare.API.Controllers.Stock
} }
[HttpPost("Create")] [HttpPost("Create")]
public async Task<IActionResult> Create([FromBody] EProductDivision division) public async Task<IActionResult> Create([FromBody] ELSProductDivision division)
{ {
try try
{ {
@ -68,7 +68,7 @@ namespace phronCare.API.Controllers.Stock
} }
[HttpPut("Update")] [HttpPut("Update")]
public async Task<IActionResult> Update([FromBody] EProductDivision division) public async Task<IActionResult> Update([FromBody] ELSProductDivision division)
{ {
try try
{ {

View File

@ -257,7 +257,12 @@ static void RepositorysAndServices(WebApplicationBuilder builder)
client.BaseAddress = new Uri("https://api.bcra.gob.ar/"); client.BaseAddress = new Uri("https://api.bcra.gob.ar/");
}); });
//Core de Divisiones de Productos //Core de Divisiones de Productos
builder.Services.AddScoped<IProductDivisionDom, ProductDivisionService>(); builder.Services.AddScoped<ILSProductDivisionDom, LSProductDivisionService>();
builder.Services.AddScoped<IPhLSMProductDivisionRepository, PhLSMProductDivisionRepository>(); builder.Services.AddScoped<IPhLSMProductDivisionRepository, PhLSMProductDivisionRepository>();
builder.Services.AddScoped<ILSProductDom, LSProductService>();
builder.Services.AddScoped<IPhLSMProductRepository, PhLSMProductRepository>();
builder.Services.AddScoped<IPhLSMLookUpRepository, PhLSMLookUpRepository>();
builder.Services.AddScoped<ILSMLookUpDom, LSMLookUpService>();
} }

View File

@ -960,6 +960,184 @@
} }
] ]
}, },
{
"ContainingType": "phronCare.API.Controllers.Stock.LSMLookUpController",
"Method": "ProductDivisions",
"RelativePath": "api/LSMLookUp/productdivisions",
"HttpMethod": "GET",
"IsController": true,
"Order": 0,
"Parameters": [
{
"Name": "q",
"Type": "System.String",
"IsRequired": false
}
],
"ReturnTypes": [
{
"Type": "System.Collections.Generic.IEnumerable\u00601[[Domain.Entities.ELookUpItem, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]",
"MediaTypes": [
"text/plain",
"application/json",
"text/json"
],
"StatusCode": 200
}
]
},
{
"ContainingType": "phronCare.API.Controllers.Stock.LSMLookUpController",
"Method": "Units",
"RelativePath": "api/LSMLookUp/units",
"HttpMethod": "GET",
"IsController": true,
"Order": 0,
"Parameters": [
{
"Name": "q",
"Type": "System.String",
"IsRequired": false
}
],
"ReturnTypes": [
{
"Type": "System.Collections.Generic.IEnumerable\u00601[[Domain.Entities.ELookUpItem, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]",
"MediaTypes": [
"text/plain",
"application/json",
"text/json"
],
"StatusCode": 200
}
]
},
{
"ContainingType": "API.Controllers.Stock.LSProductController",
"Method": "GetById",
"RelativePath": "api/LSProduct/{id}",
"HttpMethod": "GET",
"IsController": true,
"Order": 0,
"Parameters": [
{
"Name": "id",
"Type": "System.Int32",
"IsRequired": true
}
],
"ReturnTypes": [
{
"Type": "Domain.Entities.ELSProduct",
"MediaTypes": [
"text/plain",
"application/json",
"text/json"
],
"StatusCode": 200
}
]
},
{
"ContainingType": "API.Controllers.Stock.LSProductController",
"Method": "Delete",
"RelativePath": "api/LSProduct/{id}",
"HttpMethod": "DELETE",
"IsController": true,
"Order": 0,
"Parameters": [
{
"Name": "id",
"Type": "System.Int32",
"IsRequired": true
}
],
"ReturnTypes": []
},
{
"ContainingType": "API.Controllers.Stock.LSProductController",
"Method": "Create",
"RelativePath": "api/LSProduct/create",
"HttpMethod": "POST",
"IsController": true,
"Order": 0,
"Parameters": [
{
"Name": "model",
"Type": "Domain.Entities.ELSProduct",
"IsRequired": true
}
],
"ReturnTypes": [
{
"Type": "Domain.Entities.ELSProduct",
"MediaTypes": [
"text/plain",
"application/json",
"text/json"
],
"StatusCode": 200
}
]
},
{
"ContainingType": "API.Controllers.Stock.LSProductController",
"Method": "ExportFiltered",
"RelativePath": "api/LSProduct/exportfiltered",
"HttpMethod": "POST",
"IsController": true,
"Order": 0,
"Parameters": [
{
"Name": "searchParams",
"Type": "Domain.Generics.LSProductSearchParams",
"IsRequired": true
}
],
"ReturnTypes": []
},
{
"ContainingType": "API.Controllers.Stock.LSProductController",
"Method": "Search",
"RelativePath": "api/LSProduct/search",
"HttpMethod": "POST",
"IsController": true,
"Order": 0,
"Parameters": [
{
"Name": "searchParams",
"Type": "Domain.Generics.LSProductSearchParams",
"IsRequired": true
}
],
"ReturnTypes": [
{
"Type": "Domain.Generics.PagedResult\u00601[[Domain.Entities.ELSProduct, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]",
"MediaTypes": [
"text/plain",
"application/json",
"text/json"
],
"StatusCode": 200
}
]
},
{
"ContainingType": "API.Controllers.Stock.LSProductController",
"Method": "Update",
"RelativePath": "api/LSProduct/update",
"HttpMethod": "PUT",
"IsController": true,
"Order": 0,
"Parameters": [
{
"Name": "model",
"Type": "Domain.Entities.ELSProduct",
"IsRequired": true
}
],
"ReturnTypes": []
},
{ {
"ContainingType": "phronCare.API.Controllers.Sales.PatientController", "ContainingType": "phronCare.API.Controllers.Sales.PatientController",
"Method": "GetById", "Method": "GetById",
@ -1459,7 +1637,7 @@
"Parameters": [ "Parameters": [
{ {
"Name": "division", "Name": "division",
"Type": "Domain.Entities.EProductDivision", "Type": "Domain.Entities.ELSProductDivision",
"IsRequired": true "IsRequired": true
} }
], ],
@ -1554,7 +1732,7 @@
"Parameters": [ "Parameters": [
{ {
"Name": "division", "Name": "division",
"Type": "Domain.Entities.EProductDivision", "Type": "Domain.Entities.ELSProductDivision",
"IsRequired": true "IsRequired": true
} }
], ],

View File

@ -0,0 +1,255 @@
@page "/stock/products"
@using Blazored.Typeahead
@using phronCare.UIBlazor.Services.Stock
@using phronCare.UIBlazor.Services.Lookups
@using Domain.Entities
@using Domain.Generics
@inject IToastService toastService
@inject NavigationManager Navigation
@inject LSProductService productService
@inject IStockLookUpService lookUpService
<div class="card" style="zoom:80%">
<div class="card-header d-flex justify-content-center align-items-center" style="zoom:80%;">
<h3 class="card-title m-0">Catálogo de Productos Médicos</h3>
</div>
<div class="card-body px-4">
<!-- FILTROS -->
<div class="mb-3 row g-2 align-items-end">
<div class="col-md-1">
<label for="code">Código</label>
<input id="code" class="form-control form-control-sm" style="height: 38px;" placeholder="Código interno o externo" @bind="SearchParams.Code" />
</div>
<div class="col-sm">
<label for="description">Nombre o descripción</label>
<input id="description" class="form-control form-control-sm" style="height: 38px;" placeholder="Nombre o descripción" @bind="SearchParams.Description" />
</div>
<div class="col-sm">
<label for="division">División</label>
<BlazoredTypeahead id="division" TItem="ELookUpItem" TValue="ELookUpItem"
SearchMethod="@(filter => lookUpService.GetProductDivisionsAsync(filter).ContinueWith(t => t.Result.AsEnumerable()))"
Value="_selectedDivision" ValueChanged="OnDivisionSelected"
ValueExpression="@(() => _selectedDivision)"
Placeholder="Buscar división..." TextProperty="Nombre" MaximumSuggestions="5"
class="form-control form-control-sm" style="height: 38px;">
<ResultTemplate Context="item">@item.Nombre</ResultTemplate>
<SelectedTemplate Context="item">@item.Nombre</SelectedTemplate>
</BlazoredTypeahead>
</div>
<div class="col-sm-2">
<label for="unitmeasure">Unidad</label>
<BlazoredTypeahead id="unitmeasure" TItem="ELookUpItem" TValue="ELookUpItem"
SearchMethod="@(filter => lookUpService.GetUnitsOfMeasureAsync(filter).ContinueWith(t => t.Result.AsEnumerable()))"
Value="_selectedUnit" ValueChanged="OnUnitSelected"
ValueExpression="@(() => _selectedUnit)"
Placeholder="Buscar unidad..." TextProperty="Nombre" MaximumSuggestions="5"
class="form-control form-control-sm" style="height: 38px;">
<ResultTemplate Context="item">@item.Nombre</ResultTemplate>
<SelectedTemplate Context="item">@item.Nombre</SelectedTemplate>
</BlazoredTypeahead>
</div>
<div class="col-sm-2">
<label for="type">Tipo</label>
<InputSelect id="type" class="form-select form-control-sm" style="height: 38px;" @bind-Value="SearchParams.ProductType">
<option value="">Tipo</option>
<option value="1">Implantable</option>
<option value="2">Instrumental</option>
<option value="3">Inyectable</option>
</InputSelect>
</div>
<div class="col-sm">
<label for="traceability">División</label>
<InputSelect id="traceability" class="form-select form-control-sm" style="height: 38px;" @bind-Value="SearchParams.TraceabilityType">
<option value="">Trazabilidad</option>
<option value="1">No aplica</option>
<option value="2">Por cantidad</option>
<option value="3">Por lote y vencimiento</option>
</InputSelect>
</div>
<div class="col-sm">
<div class="form-check form-switch">
<input type="checkbox" class="form-check-input me-2" id="plusProcess" @bind="SearchParams.PlusProcess" />
<label class="form-check-label" for="plusProcess">Esterilizable</label>
</div>
</div>
</div>
<!-- BOONES DE BUSQUEDA -->
<div class="mb-3 d-flex justify-content-end gap-2">
<button class="btn btn-primary rounded-pill" @onclick="Buscar">
<i class="fas fa-binoculars me-1"></i> Buscar
</button>
<button class="btn btn-success rounded-pill" disabled="1" @onclick="Nuevo">
<i class="fas fa-plus me-1"></i> Nuevo
</button>
<button class="btn btn-success rounded-pill" @onclick="ExportarExcel">
<i class="fas fa-file-excel me-1"></i> Excel
</button>
<button class="btn btn-secondary rounded-pill" @onclick="Cancelar">
<i class="fas fa-arrow-left me-1"></i> Volver
</button>
</div>
<hr />
<div style="zoom:90%;">
@if (TablaProductos != null && TablaProductos.Any())
{
<PhTable Columns="TableColumns"
Data="TablaProductos"
SelectionField="Id"
RowsPerPage="SearchParams.PageSize"
RenderButtons="true"
Buttons="botones"
ShowPageButtons="false"
ShowQuickSearch="false"
RenderSelect="false" />
}
else
{
<p>No hay resultados.</p>
}
</div>
</div>
<div class="card-footer d-flex justify-content-center align-items-end" style="zoom:80%;">
<div class="d-flex align-items-center gap-3">
<button class="btn btn-secondary rounded-pill" @onclick="PrimeraPagina" disabled="@(SearchParams.Page == 1)">« Primera</button>
<button class="btn btn-secondary rounded-pill" @onclick="AnteriorPagina" disabled="@(!PuedeRetroceder)"> Anterior</button>
<span class="mx-2">Página <strong>@SearchParams.Page</strong> de <strong>@TotalPaginas</strong></span>
<button class="btn btn-secondary rounded-pill" @onclick="SiguientePagina" disabled="@(!PuedeAvanzar)">Siguiente </button>
<button class="btn btn-secondary rounded-pill" @onclick="UltimaPagina" disabled="@(SearchParams.Page == TotalPaginas)">Última »</button>
<input type="number" class="form-control form-control-sm rounded ms-3" style="width: 80px;" min="1" max="@TotalPaginas" @bind="PaginaDeseada" />
<button class="btn btn-outline-primary btn-sm rounded-pill ms-2" @onclick="IrAPagina">Ir</button>
</div>
</div>
</div>
@code {
private LSProductSearchParams SearchParams = new() { Page = 1, PageSize = 10 };
private List<Dictionary<string, object>> TablaProductos = new();
private PagedResult<ELSProduct>? PagedResult;
private int PaginaDeseada = 1;
private ELookUpItem? _selectedDivision;
private ELookUpItem? _selectedUnit;
List<PhTable.ButtonOptions> botones = new();
private List<string> TableColumns = new()
{
"Id", "Código Fábrica", "Código Externo", "Nombre", "Descripción", "División", "Unidad", "Tipo", "Trazabilidad", "Esteriliza"
};
protected override async Task OnInitializedAsync()
{
botones = new List<PhTable.ButtonOptions>
{
new PhTable.ButtonOptions
{
Caption = "Editar",
ElementClass = "btn btn-primary btn-sm",
UrlAction = "/stock/productform/",
OnClickAction = async (id) =>
{
if (int.TryParse(id, out var pid))
Navigation.NavigateTo($"/stock/productform/{pid}");
}
}
};
// await Buscar();
}
private async Task Buscar()
{
SearchParams.PageSize = 13;
SearchParams.Page = 1;
await CargarPaginaActual();
}
private async Task CargarPaginaActual()
{
SearchParams.DivisionId = _selectedDivision?.Id;
SearchParams.UnitId = _selectedUnit?.Id;
PagedResult = await productService.SearchAsync(SearchParams);
TablaProductos = PagedResult?.Items.Select(p => new Dictionary<string, object>
{
{ "Id", p.Id },
{ "Código Fábrica", p.FactoryCode },
{ "Código Externo", p.ExternalCode },
{ "Nombre", p.Name },
{ "Descripción", p.Descripcion },
{ "División", p.Division?.Name ?? "" },
{ "Unidad", p.Unit?.Name ?? "" },
{ "Tipo", ObtenerTipoProducto(p.ProductType) },
{ "Trazabilidad", ObtenerTipoTrazabilidad(p.TraceabilityType) },
{ "Esteriliza", p.PlusProcess ? "Sí" : "No" }
}).ToList() ?? [];
}
private void OnDivisionSelected(ELookUpItem item) => _selectedDivision = item;
private void OnUnitSelected(ELookUpItem item) => _selectedUnit = item;
private string ObtenerTipoProducto(int? tipo) => tipo switch
{
1 => "Implantable",
2 => "Instrumental",
3 => "Inyectable",
_ => ""
};
private string ObtenerTipoTrazabilidad(int? tipo) => tipo switch
{
1 => "No aplica",
2 => "Por cantidad",
3 => "Por lote/vencimiento",
_ => ""
};
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 nueva = SearchParams.Page + delta;
if (nueva >= 1 && nueva <= TotalPaginas)
{
SearchParams.Page = nueva;
await CargarPaginaActual();
}
}
private async Task IrAPagina()
{
if (PaginaDeseada >= 1 && PaginaDeseada <= TotalPaginas)
{
SearchParams.Page = PaginaDeseada;
await CargarPaginaActual();
}
else
{
toastService.ShowWarning("Página fuera de rango.");
}
}
private async Task ExportarExcel()
{
SearchParams.Page = 1;
SearchParams.PageSize = int.MaxValue; // Exportar todos los resultados
try
{
await productService.ExportFilteredAsync(SearchParams);
toastService.ShowSuccess("Exportación completada.");
}
catch (Exception ex)
{
toastService.ShowError($"Error: {ex.Message}");
}
}
private void Nuevo() => Navigation.NavigateTo("/stock/productform/");
private void Cancelar() => Navigation.NavigateTo("/DashboardPanel");
private int TotalPaginas => PagedResult is null ? 1 : (int)Math.Ceiling(PagedResult.TotalItems / (double)SearchParams.PageSize);
private bool PuedeRetroceder => SearchParams.Page > 1;
private bool PuedeAvanzar => PagedResult != null && SearchParams.Page < TotalPaginas;
}

View File

@ -71,7 +71,7 @@
</div> </div>
@code { @code {
private PagedResult<EProductDivision>? Resultado; private PagedResult<ELSProductDivision>? Resultado;
private List<Dictionary<string, object>> Tabla = new(); private List<Dictionary<string, object>> Tabla = new();
private List<string> Columnas = new() { "Id", "Codigo", "Nombre", "Descripción" }; private List<string> Columnas = new() { "Id", "Codigo", "Nombre", "Descripción" };
private ProductDivisionSearchParams SearchParams = new() { PageSize = 10 }; private ProductDivisionSearchParams SearchParams = new() { PageSize = 10 };

View File

@ -50,7 +50,7 @@
[Parameter] [Parameter]
public int? Id { get; set; } public int? Id { get; set; }
private EProductDivision model = new(); private ELSProductDivision model = new();
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {

View File

@ -46,6 +46,7 @@ await builder.Build().RunAsync();
static void InjectDependencies(WebAssemblyHostBuilder builder) static void InjectDependencies(WebAssemblyHostBuilder builder)
{ {
builder.Services.AddScoped<ISalesLookupService, SalesLookupService>(); builder.Services.AddScoped<ISalesLookupService, SalesLookupService>();
builder.Services.AddScoped<IStockLookUpService, StockLookUpService>();
builder.Services.AddScoped<IExchangeRateService, ExchangeRateService>(); builder.Services.AddScoped<IExchangeRateService, ExchangeRateService>();
builder.Services.AddScoped<ExchangeRateService>(); builder.Services.AddScoped<ExchangeRateService>();
@ -64,4 +65,6 @@ static void InjectDependencies(WebAssemblyHostBuilder builder)
builder.Services.AddScoped<ProductCategoryService>(); builder.Services.AddScoped<ProductCategoryService>();
builder.Services.AddScoped<PatientService>(); builder.Services.AddScoped<PatientService>();
builder.Services.AddScoped<ProductDivisionService>(); builder.Services.AddScoped<ProductDivisionService>();
builder.Services.AddScoped<LSProductService>();
} }

View File

@ -0,0 +1,10 @@
using Domain.Entities;
namespace phronCare.UIBlazor.Services.Lookups
{
public interface IStockLookUpService
{
Task<List<ELookUpItem>> GetProductDivisionsAsync(string filter = "");
Task<List<ELookUpItem>> GetUnitsOfMeasureAsync(string filter = "");
}
}

View File

@ -0,0 +1,28 @@
using Domain.Entities;
using phronCare.UIBlazor.Services.Lookups;
using System.Net.Http.Json;
public class StockLookUpService: IStockLookUpService
{
private readonly HttpClient _http;
public StockLookUpService(HttpClient http)
{
_http = http;
}
public async Task<List<ELookUpItem>> GetProductDivisionsAsync(string filter = "")
{
string url = string.IsNullOrWhiteSpace(filter)
? "/api/LSMLookUp/productdivisions"
: $"/api/LSMLookUp/productdivisions?q={Uri.EscapeDataString(filter)}";
var result = await _http.GetFromJsonAsync<List<ELookUpItem>>(url);
return result ?? new List<ELookUpItem>();
}
public async Task<List<ELookUpItem>> GetUnitsOfMeasureAsync(string filter = "")
{
var result = await _http.GetFromJsonAsync<List<ELookUpItem>>($"/api/LSMLookUp/units?q={filter}");
return result ?? [];
}
}

View File

@ -0,0 +1,77 @@
using Domain.Entities;
using Domain.Generics;
using Microsoft.JSInterop;
using System.Net.Http.Json;
using System.Reflection;
using System.Text;
using System.Text.Json;
namespace phronCare.UIBlazor.Services.Stock
{
public class LSProductService
{
private readonly HttpClient _http;
private readonly IJSRuntime _js;
public LSProductService(HttpClient http, IJSRuntime js)
{
_http = http;
_js = js;
}
public async Task<PagedResult<ELSProduct>?> SearchAsync(LSProductSearchParams searchParams)
{
return await _http.PostAsJsonAsync("/api/LSProduct/Search", searchParams)
.ContinueWith(async t =>
await t.Result.Content.ReadFromJsonAsync<PagedResult<ELSProduct>>())
.Unwrap();
}
public async Task<ELSProduct?> GetByIdAsync(int id)
{
return await _http.GetFromJsonAsync<ELSProduct>($"/api/LSProduct/{id}");
}
public async Task<HttpResponseMessage> CreateAsync(ELSProduct product)
{
return await _http.PostAsJsonAsync("/api/LSProduct", product);
}
public async Task<HttpResponseMessage> UpdateAsync(ELSProduct product)
{
return await _http.PutAsJsonAsync("/api/LSProduct", product);
}
public async Task<HttpResponseMessage> DeleteAsync(int id)
{
return await _http.DeleteAsync($"/api/LSProduct/{id}");
}
public async Task ExportFilteredAsync(LSProductSearchParams searchParams)
{
try
{
var content = new StringContent(JsonSerializer.Serialize(searchParams), Encoding.UTF8, "application/json");
var response = await _http.PostAsync("/api/LSProduct/exportfiltered", content);
if (!response.IsSuccessStatusCode)
{
var errorContent = await response.Content.ReadAsStringAsync();
throw new Exception(errorContent);
}
var bytes = await response.Content.ReadAsByteArrayAsync();
var base64 = Convert.ToBase64String(bytes);
var timestamp = DateTime.Now.ToString("yyyyMMddHHmm");
var fileName = $"productos_{DateTime.Now:yyyyMMddHHmmss}.xlsx";
await _js.InvokeVoidAsync("saveAsFile", fileName, base64);
}
catch(Exception ex)
{
var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod";
var message = ex.Message ?? "No message provided";
throw new Exception($"{message}", ex);
}
}
}
}

View File

@ -16,24 +16,24 @@ namespace phronCare.UIBlazor.Services.Stock
_js = js; _js = js;
} }
public async Task<List<EProductDivision>> GetAllAsync() public async Task<List<ELSProductDivision>> GetAllAsync()
{ {
var result = await _http.GetFromJsonAsync<List<EProductDivision>>("/api/ProductDivision/GetAll"); var result = await _http.GetFromJsonAsync<List<ELSProductDivision>>("/api/ProductDivision/GetAll");
return result ?? new List<EProductDivision>(); return result ?? new List<ELSProductDivision>();
} }
public async Task<EProductDivision?> GetByIdAsync(int id) public async Task<ELSProductDivision?> GetByIdAsync(int id)
{ {
var result = await _http.GetFromJsonAsync<EProductDivision>($"/api/ProductDivision/GetById/{id}"); var result = await _http.GetFromJsonAsync<ELSProductDivision>($"/api/ProductDivision/GetById/{id}");
return result; return result;
} }
public async Task<HttpResponseMessage> CreateAsync(EProductDivision division) public async Task<HttpResponseMessage> CreateAsync(ELSProductDivision division)
{ {
return await _http.PostAsJsonAsync("/api/ProductDivision/Create", division); return await _http.PostAsJsonAsync("/api/ProductDivision/Create", division);
} }
public async Task<HttpResponseMessage> UpdateAsync(EProductDivision division) public async Task<HttpResponseMessage> UpdateAsync(ELSProductDivision division)
{ {
return await _http.PutAsJsonAsync("/api/ProductDivision/Update", division); return await _http.PutAsJsonAsync("/api/ProductDivision/Update", division);
} }
@ -43,13 +43,13 @@ namespace phronCare.UIBlazor.Services.Stock
return await _http.DeleteAsync($"/api/ProductDivision/Delete/{id}"); return await _http.DeleteAsync($"/api/ProductDivision/Delete/{id}");
} }
public async Task<PagedResult<EProductDivision>?> SearchAsync(ProductDivisionSearchParams searchParams) public async Task<PagedResult<ELSProductDivision>?> SearchAsync(ProductDivisionSearchParams searchParams)
{ {
var url = $"/api/ProductDivision/Search?" + var url = $"/api/ProductDivision/Search?" +
$"term={searchParams.Term}&" + $"term={searchParams.Term}&" +
$"page={searchParams.Page}&" + $"page={searchParams.Page}&" +
$"pageSize={searchParams.PageSize}"; $"pageSize={searchParams.PageSize}";
return await _http.GetFromJsonAsync<PagedResult<EProductDivision>>(url); return await _http.GetFromJsonAsync<PagedResult<ELSProductDivision>>(url);
} }
} }
} }