item mng with sync
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Distribution.Features.Category;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
public class CategoryController(CategoryService service) : ControllerBase
|
||||
{
|
||||
[HttpGet("api/categories")]
|
||||
[Authorize(Policy = "categories.view")]
|
||||
public async Task<IActionResult> GetAll() =>
|
||||
Ok(await service.GetAllAsync());
|
||||
|
||||
[HttpGet("api/categories/{id:guid}")]
|
||||
[Authorize(Policy = "categories.view")]
|
||||
public async Task<IActionResult> GetById(Guid id)
|
||||
{
|
||||
var result = await service.GetByIdAsync(id);
|
||||
return result is null ? NotFound() : Ok(result);
|
||||
}
|
||||
|
||||
[HttpPost("api/categories")]
|
||||
[Authorize(Policy = "categories.create")]
|
||||
public async Task<IActionResult> Create(CreateCategoryRequest request, CancellationToken ct)
|
||||
{
|
||||
var result = await service.CreateAsync(request, ct);
|
||||
return CreatedAtAction(nameof(GetById), new { id = result.Id }, result);
|
||||
}
|
||||
|
||||
[HttpPut("api/categories/{id:guid}")]
|
||||
[Authorize(Policy = "categories.update")]
|
||||
public async Task<IActionResult> Update(Guid id, UpdateCategoryRequest request, CancellationToken ct)
|
||||
{
|
||||
var result = await service.UpdateAsync(id, request, ct);
|
||||
return result is null ? NotFound() : Ok(result);
|
||||
}
|
||||
|
||||
[HttpDelete("api/categories/{id:guid}")]
|
||||
[Authorize(Policy = "categories.delete")]
|
||||
public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
|
||||
{
|
||||
var deleted = await service.DeleteAsync(id, ct);
|
||||
return deleted ? NoContent() : NotFound();
|
||||
}
|
||||
|
||||
[HttpPost("api/categories/{categoryId:guid}/subcategories")]
|
||||
[Authorize(Policy = "categories.create")]
|
||||
public async Task<IActionResult> CreateSubCategory(Guid categoryId, CreateSubCategoryRequest request, CancellationToken ct)
|
||||
{
|
||||
var result = await service.CreateSubCategoryAsync(categoryId, request, ct);
|
||||
return result is null ? NotFound() : Ok(result);
|
||||
}
|
||||
|
||||
[HttpPut("api/subcategories/{id:guid}")]
|
||||
[Authorize(Policy = "categories.update")]
|
||||
public async Task<IActionResult> UpdateSubCategory(Guid id, UpdateSubCategoryRequest request, CancellationToken ct)
|
||||
{
|
||||
var result = await service.UpdateSubCategoryAsync(id, request, ct);
|
||||
return result is null ? NotFound() : Ok(result);
|
||||
}
|
||||
|
||||
[HttpDelete("api/subcategories/{id:guid}")]
|
||||
[Authorize(Policy = "categories.delete")]
|
||||
public async Task<IActionResult> DeleteSubCategory(Guid id, CancellationToken ct)
|
||||
{
|
||||
var deleted = await service.DeleteSubCategoryAsync(id, ct);
|
||||
return deleted ? NoContent() : NotFound();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Distribution.Features.Category;
|
||||
|
||||
public record CreateCategoryRequest(string Name);
|
||||
public record UpdateCategoryRequest(string Name);
|
||||
public record CreateSubCategoryRequest(string Name);
|
||||
public record UpdateSubCategoryRequest(string Name);
|
||||
|
||||
public record SubCategoryResponse(Guid Id, string Name, bool IsActive, DateTime CreatedAt);
|
||||
public record CategorySummaryResponse(Guid Id, string Name, bool IsActive, DateTime CreatedAt);
|
||||
public record CategoryResponse(Guid Id, string Name, bool IsActive, DateTime CreatedAt, List<SubCategoryResponse> SubCategories);
|
||||
@@ -0,0 +1,34 @@
|
||||
using Distribution.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Distribution.Features.Category;
|
||||
|
||||
public class CategoryRepository(AppDBContext context) : ICategoryRepository
|
||||
{
|
||||
public async Task<Models.Category?> GetByIdAsync(Guid id) =>
|
||||
await context.Categories.FindAsync(id);
|
||||
|
||||
public async Task<List<Models.Category>> GetAllAsync() =>
|
||||
await context.Categories.ToListAsync();
|
||||
|
||||
public async Task<Models.Category?> GetByExternalErpIdAsync(int erpId) =>
|
||||
await context.Categories.FirstOrDefaultAsync(c => c.ExternalErpId == erpId);
|
||||
|
||||
public async Task<bool> HasActiveSubCategoriesAsync(Guid id) =>
|
||||
await context.SubCategories.AnyAsync(s => s.FK_CategoryId == id && s.IsActive);
|
||||
|
||||
public async Task<bool> HasActiveItemsAsync(Guid id) =>
|
||||
await context.Items.AnyAsync(i => i.FK_CategoryId == id && i.IsActive);
|
||||
|
||||
public async Task AddAsync(Models.Category entity) =>
|
||||
await context.Categories.AddAsync(entity);
|
||||
|
||||
public void Update(Models.Category entity) =>
|
||||
context.Categories.Update(entity);
|
||||
|
||||
public void Remove(Models.Category entity)
|
||||
{
|
||||
entity.IsActive = false;
|
||||
context.Categories.Update(entity);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
using Distribution.Infra.UoW;
|
||||
|
||||
namespace Distribution.Features.Category;
|
||||
|
||||
public class CategoryService(ICategoryRepository categoryRepo, ISubCategoryRepository subCategoryRepo, IUnitOfWork uow)
|
||||
{
|
||||
public async Task<List<CategorySummaryResponse>> GetAllAsync() =>
|
||||
(await categoryRepo.GetAllAsync())
|
||||
.Where(c => c.IsActive)
|
||||
.Select(c => new CategorySummaryResponse(c.Id, c.Name, c.IsActive, c.CreatedAt))
|
||||
.ToList();
|
||||
|
||||
public async Task<CategoryResponse?> GetByIdAsync(Guid id)
|
||||
{
|
||||
var category = await categoryRepo.GetByIdAsync(id);
|
||||
if (category is null || !category.IsActive) return null;
|
||||
var subs = (await subCategoryRepo.GetByCategoryIdAsync(id))
|
||||
.Where(s => s.IsActive)
|
||||
.Select(s => new SubCategoryResponse(s.Id, s.Name, s.IsActive, s.CreatedAt))
|
||||
.ToList();
|
||||
return new CategoryResponse(category.Id, category.Name, category.IsActive, category.CreatedAt, subs);
|
||||
}
|
||||
|
||||
public async Task<CategorySummaryResponse> CreateAsync(CreateCategoryRequest request, CancellationToken ct = default)
|
||||
{
|
||||
var category = new Models.Category { Name = request.Name };
|
||||
await uow.BeginAsync(ct);
|
||||
try { await categoryRepo.AddAsync(category); await uow.CommitAsync(ct); }
|
||||
catch { await uow.RollbackAsync(ct); throw; }
|
||||
return new CategorySummaryResponse(category.Id, category.Name, category.IsActive, category.CreatedAt);
|
||||
}
|
||||
|
||||
public async Task<CategorySummaryResponse?> UpdateAsync(Guid id, UpdateCategoryRequest request, CancellationToken ct = default)
|
||||
{
|
||||
var category = await categoryRepo.GetByIdAsync(id);
|
||||
if (category is null || !category.IsActive) return null;
|
||||
if (category.ExternalErpId.HasValue) return null;
|
||||
category.Name = request.Name;
|
||||
await uow.BeginAsync(ct);
|
||||
try { categoryRepo.Update(category); await uow.CommitAsync(ct); }
|
||||
catch { await uow.RollbackAsync(ct); throw; }
|
||||
return new CategorySummaryResponse(category.Id, category.Name, category.IsActive, category.CreatedAt);
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var category = await categoryRepo.GetByIdAsync(id);
|
||||
if (category is null || !category.IsActive) return false;
|
||||
if (category.ExternalErpId.HasValue) return false;
|
||||
if (await categoryRepo.HasActiveSubCategoriesAsync(id)) return false;
|
||||
if (await categoryRepo.HasActiveItemsAsync(id)) return false;
|
||||
await uow.BeginAsync(ct);
|
||||
try { categoryRepo.Remove(category); await uow.CommitAsync(ct); }
|
||||
catch { await uow.RollbackAsync(ct); throw; }
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<SubCategoryResponse?> CreateSubCategoryAsync(Guid categoryId, CreateSubCategoryRequest request, CancellationToken ct = default)
|
||||
{
|
||||
var category = await categoryRepo.GetByIdAsync(categoryId);
|
||||
if (category is null || !category.IsActive) return null;
|
||||
var sub = new Models.SubCategory { Name = request.Name, FK_CategoryId = categoryId };
|
||||
await uow.BeginAsync(ct);
|
||||
try { await subCategoryRepo.AddAsync(sub); await uow.CommitAsync(ct); }
|
||||
catch { await uow.RollbackAsync(ct); throw; }
|
||||
return new SubCategoryResponse(sub.Id, sub.Name, sub.IsActive, sub.CreatedAt);
|
||||
}
|
||||
|
||||
public async Task<SubCategoryResponse?> UpdateSubCategoryAsync(Guid id, UpdateSubCategoryRequest request, CancellationToken ct = default)
|
||||
{
|
||||
var sub = await subCategoryRepo.GetByIdAsync(id);
|
||||
if (sub is null || !sub.IsActive) return null;
|
||||
if (sub.ExternalErpId.HasValue) return null;
|
||||
sub.Name = request.Name;
|
||||
await uow.BeginAsync(ct);
|
||||
try { subCategoryRepo.Update(sub); await uow.CommitAsync(ct); }
|
||||
catch { await uow.RollbackAsync(ct); throw; }
|
||||
return new SubCategoryResponse(sub.Id, sub.Name, sub.IsActive, sub.CreatedAt);
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteSubCategoryAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var sub = await subCategoryRepo.GetByIdAsync(id);
|
||||
if (sub is null || !sub.IsActive) return false;
|
||||
if (sub.ExternalErpId.HasValue) return false;
|
||||
if (await subCategoryRepo.HasActiveItemsAsync(id)) return false;
|
||||
await uow.BeginAsync(ct);
|
||||
try { subCategoryRepo.Remove(sub); await uow.CommitAsync(ct); }
|
||||
catch { await uow.RollbackAsync(ct); throw; }
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Distribution.Features.Category;
|
||||
|
||||
public interface ICategoryRepository
|
||||
{
|
||||
Task<Models.Category?> GetByIdAsync(Guid id);
|
||||
Task<List<Models.Category>> GetAllAsync();
|
||||
Task<Models.Category?> GetByExternalErpIdAsync(int erpId);
|
||||
Task<bool> HasActiveSubCategoriesAsync(Guid id);
|
||||
Task<bool> HasActiveItemsAsync(Guid id);
|
||||
Task AddAsync(Models.Category entity);
|
||||
void Update(Models.Category entity);
|
||||
void Remove(Models.Category entity);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Distribution.Features.Category;
|
||||
|
||||
public interface ISubCategoryRepository
|
||||
{
|
||||
Task<Models.SubCategory?> GetByIdAsync(Guid id);
|
||||
Task<List<Models.SubCategory>> GetByCategoryIdAsync(Guid categoryId);
|
||||
Task<Models.SubCategory?> GetByExternalErpIdAsync(int erpId);
|
||||
Task<bool> HasActiveItemsAsync(Guid id);
|
||||
Task AddAsync(Models.SubCategory entity);
|
||||
void Update(Models.SubCategory entity);
|
||||
void Remove(Models.SubCategory entity);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using Distribution.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Distribution.Features.Category;
|
||||
|
||||
public class SubCategoryRepository(AppDBContext context) : ISubCategoryRepository
|
||||
{
|
||||
public async Task<Models.SubCategory?> GetByIdAsync(Guid id) =>
|
||||
await context.SubCategories.FindAsync(id);
|
||||
|
||||
public async Task<List<Models.SubCategory>> GetByCategoryIdAsync(Guid categoryId) =>
|
||||
await context.SubCategories.Where(s => s.FK_CategoryId == categoryId).ToListAsync();
|
||||
|
||||
public async Task<Models.SubCategory?> GetByExternalErpIdAsync(int erpId) =>
|
||||
await context.SubCategories.FirstOrDefaultAsync(s => s.ExternalErpId == erpId);
|
||||
|
||||
public async Task<bool> HasActiveItemsAsync(Guid id) =>
|
||||
await context.Items.AnyAsync(i => i.FK_SubCategoryId == id && i.IsActive);
|
||||
|
||||
public async Task AddAsync(Models.SubCategory entity) =>
|
||||
await context.SubCategories.AddAsync(entity);
|
||||
|
||||
public void Update(Models.SubCategory entity) =>
|
||||
context.SubCategories.Update(entity);
|
||||
|
||||
public void Remove(Models.SubCategory entity)
|
||||
{
|
||||
entity.IsActive = false;
|
||||
context.SubCategories.Update(entity);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user