Enhance category and sub-category update requests to include IsActive flag and deletes and inactive categories
This commit is contained in:
@@ -1,9 +1,9 @@
|
|||||||
namespace Distribution.Features.Category;
|
namespace Distribution.Features.Category;
|
||||||
|
|
||||||
public record CreateCategoryRequest(string Name);
|
public record CreateCategoryRequest(string Name);
|
||||||
public record UpdateCategoryRequest(string Name);
|
public record UpdateCategoryRequest(string Name, bool IsActive = true);
|
||||||
public record CreateSubCategoryRequest(string Name);
|
public record CreateSubCategoryRequest(string Name);
|
||||||
public record UpdateSubCategoryRequest(string Name);
|
public record UpdateSubCategoryRequest(string Name, bool IsActive = true);
|
||||||
|
|
||||||
public record SubCategoryResponse(Guid Id, string Name, bool IsActive, DateTime CreatedAt);
|
public record SubCategoryResponse(Guid Id, string Name, bool IsActive, DateTime CreatedAt);
|
||||||
public record CategorySummaryResponse(Guid Id, string Name, bool IsActive, DateTime CreatedAt);
|
public record CategorySummaryResponse(Guid Id, string Name, bool IsActive, DateTime CreatedAt);
|
||||||
|
|||||||
@@ -4,18 +4,27 @@ namespace Distribution.Features.Category;
|
|||||||
|
|
||||||
public class CategoryService(ICategoryRepository categoryRepo, ISubCategoryRepository subCategoryRepo, IUnitOfWork uow)
|
public class CategoryService(ICategoryRepository categoryRepo, ISubCategoryRepository subCategoryRepo, IUnitOfWork uow)
|
||||||
{
|
{
|
||||||
public async Task<List<CategorySummaryResponse>> GetAllAsync() =>
|
public async Task<List<CategoryResponse>> GetAllAsync()
|
||||||
(await categoryRepo.GetAllAsync())
|
{
|
||||||
.Where(c => c.IsActive)
|
// Deleted categories/sub-categories stay IsActive=false but remain visible showing
|
||||||
.Select(c => new CategorySummaryResponse(c.Id, c.Name, c.IsActive, c.CreatedAt))
|
// Inactive — delete never removes the row, only marks it (same rule as Orders/Stores/Items).
|
||||||
|
var categoryList = await categoryRepo.GetAllAsync();
|
||||||
|
var result = new List<CategoryResponse>();
|
||||||
|
foreach (var category in categoryList)
|
||||||
|
{
|
||||||
|
var subs = (await subCategoryRepo.GetByCategoryIdAsync(category.Id))
|
||||||
|
.Select(s => new SubCategoryResponse(s.Id, s.Name, s.IsActive, s.CreatedAt))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
result.Add(new CategoryResponse(category.Id, category.Name, category.IsActive, category.CreatedAt, subs));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<CategoryResponse?> GetByIdAsync(Guid id)
|
public async Task<CategoryResponse?> GetByIdAsync(Guid id)
|
||||||
{
|
{
|
||||||
var category = await categoryRepo.GetByIdAsync(id);
|
var category = await categoryRepo.GetByIdAsync(id);
|
||||||
if (category is null || !category.IsActive) return null;
|
if (category is null) return null;
|
||||||
var subs = (await subCategoryRepo.GetByCategoryIdAsync(id))
|
var subs = (await subCategoryRepo.GetByCategoryIdAsync(id))
|
||||||
.Where(s => s.IsActive)
|
|
||||||
.Select(s => new SubCategoryResponse(s.Id, s.Name, s.IsActive, s.CreatedAt))
|
.Select(s => new SubCategoryResponse(s.Id, s.Name, s.IsActive, s.CreatedAt))
|
||||||
.ToList();
|
.ToList();
|
||||||
return new CategoryResponse(category.Id, category.Name, category.IsActive, category.CreatedAt, subs);
|
return new CategoryResponse(category.Id, category.Name, category.IsActive, category.CreatedAt, subs);
|
||||||
@@ -33,9 +42,10 @@ public class CategoryService(ICategoryRepository categoryRepo, ISubCategoryRepos
|
|||||||
public async Task<CategorySummaryResponse?> UpdateAsync(Guid id, UpdateCategoryRequest request, CancellationToken ct = default)
|
public async Task<CategorySummaryResponse?> UpdateAsync(Guid id, UpdateCategoryRequest request, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
var category = await categoryRepo.GetByIdAsync(id);
|
var category = await categoryRepo.GetByIdAsync(id);
|
||||||
if (category is null || !category.IsActive) return null;
|
if (category is null) return null;
|
||||||
if (category.ExternalErpId.HasValue) return null;
|
if (category.ExternalErpId.HasValue) return null;
|
||||||
category.Name = request.Name;
|
category.Name = request.Name;
|
||||||
|
category.IsActive = request.IsActive;
|
||||||
await uow.BeginAsync(ct);
|
await uow.BeginAsync(ct);
|
||||||
try { categoryRepo.Update(category); await uow.CommitAsync(ct); }
|
try { categoryRepo.Update(category); await uow.CommitAsync(ct); }
|
||||||
catch { await uow.RollbackAsync(ct); throw; }
|
catch { await uow.RollbackAsync(ct); throw; }
|
||||||
@@ -44,11 +54,11 @@ public class CategoryService(ICategoryRepository categoryRepo, ISubCategoryRepos
|
|||||||
|
|
||||||
public async Task<bool> DeleteAsync(Guid id, CancellationToken ct = default)
|
public async Task<bool> DeleteAsync(Guid id, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
|
// Soft-delete only — never blocked by child sub-categories/items, same rule as
|
||||||
|
// Items/Stores/Orders. The row stays visible showing Inactive.
|
||||||
var category = await categoryRepo.GetByIdAsync(id);
|
var category = await categoryRepo.GetByIdAsync(id);
|
||||||
if (category is null || !category.IsActive) return false;
|
if (category is null || !category.IsActive) return false;
|
||||||
if (category.ExternalErpId.HasValue) 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);
|
await uow.BeginAsync(ct);
|
||||||
try { categoryRepo.Remove(category); await uow.CommitAsync(ct); }
|
try { categoryRepo.Remove(category); await uow.CommitAsync(ct); }
|
||||||
catch { await uow.RollbackAsync(ct); throw; }
|
catch { await uow.RollbackAsync(ct); throw; }
|
||||||
@@ -69,9 +79,10 @@ public class CategoryService(ICategoryRepository categoryRepo, ISubCategoryRepos
|
|||||||
public async Task<SubCategoryResponse?> UpdateSubCategoryAsync(Guid id, UpdateSubCategoryRequest request, CancellationToken ct = default)
|
public async Task<SubCategoryResponse?> UpdateSubCategoryAsync(Guid id, UpdateSubCategoryRequest request, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
var sub = await subCategoryRepo.GetByIdAsync(id);
|
var sub = await subCategoryRepo.GetByIdAsync(id);
|
||||||
if (sub is null || !sub.IsActive) return null;
|
if (sub is null) return null;
|
||||||
if (sub.ExternalErpId.HasValue) return null;
|
if (sub.ExternalErpId.HasValue) return null;
|
||||||
sub.Name = request.Name;
|
sub.Name = request.Name;
|
||||||
|
sub.IsActive = request.IsActive;
|
||||||
await uow.BeginAsync(ct);
|
await uow.BeginAsync(ct);
|
||||||
try { subCategoryRepo.Update(sub); await uow.CommitAsync(ct); }
|
try { subCategoryRepo.Update(sub); await uow.CommitAsync(ct); }
|
||||||
catch { await uow.RollbackAsync(ct); throw; }
|
catch { await uow.RollbackAsync(ct); throw; }
|
||||||
@@ -80,10 +91,10 @@ public class CategoryService(ICategoryRepository categoryRepo, ISubCategoryRepos
|
|||||||
|
|
||||||
public async Task<bool> DeleteSubCategoryAsync(Guid id, CancellationToken ct = default)
|
public async Task<bool> DeleteSubCategoryAsync(Guid id, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
|
// Soft-delete only — never blocked by child items, same rule as the parent category.
|
||||||
var sub = await subCategoryRepo.GetByIdAsync(id);
|
var sub = await subCategoryRepo.GetByIdAsync(id);
|
||||||
if (sub is null || !sub.IsActive) return false;
|
if (sub is null || !sub.IsActive) return false;
|
||||||
if (sub.ExternalErpId.HasValue) return false;
|
if (sub.ExternalErpId.HasValue) return false;
|
||||||
if (await subCategoryRepo.HasActiveItemsAsync(id)) return false;
|
|
||||||
await uow.BeginAsync(ct);
|
await uow.BeginAsync(ct);
|
||||||
try { subCategoryRepo.Remove(sub); await uow.CommitAsync(ct); }
|
try { subCategoryRepo.Remove(sub); await uow.CommitAsync(ct); }
|
||||||
catch { await uow.RollbackAsync(ct); throw; }
|
catch { await uow.RollbackAsync(ct); throw; }
|
||||||
|
|||||||
Reference in New Issue
Block a user