using Distribution.Infra.UoW; namespace Distribution.Features.Item; public class ItemService(IItemRepository repo, IUnitOfWork uow) { private static UomConversionResponse MapConversion(Models.UomConversion c) => new(c.Id, c.FK_FromUomId, c.FromUom.Name, c.FK_ToUomId, c.ToUom.Name, c.Factor); private static ItemSummaryResponse MapSummary(Models.Item i) => new(i.Id, i.Sku, i.Name, i.FK_CategoryId, i.Category.Name, i.FK_SubCategoryId, i.SubCategory?.Name, i.FK_BrandId, i.Brand?.Name, i.FK_BaseUomId, i.BaseUom.Name, i.SalePrice, i.ReorderLevel, i.SyncSource, i.IsActive, i.CreatedAt); private static ItemDetailResponse MapDetail(Models.Item i) => new(i.Id, i.Sku, i.Name, i.Description, i.FK_CategoryId, i.Category.Name, i.FK_SubCategoryId, i.SubCategory?.Name, i.FK_BrandId, i.Brand?.Name, i.FK_BaseUomId, i.BaseUom.Name, i.SalePrice, i.ReorderLevel, i.SyncSource, i.IsActive, i.CreatedAt, i.Conversions.Select(MapConversion).ToList()); public async Task> GetAllAsync(Guid? categoryId = null, Guid? subCategoryId = null, Guid? brandId = null) => (await repo.GetAllAsync(categoryId, subCategoryId, brandId)) .Select(MapSummary) .ToList(); public async Task GetByIdAsync(Guid id) { var item = await repo.GetByIdAsync(id); if (item is null) return null; return MapDetail(item); } public async Task CreateAsync(CreateItemRequest request, CancellationToken ct = default) { if (await repo.SkuExistsAsync(request.Sku)) throw new InvalidOperationException($"SKU '{request.Sku}' already exists."); var item = new Models.Item { Sku = request.Sku, Name = request.Name, Description = request.Description, FK_CategoryId = request.CategoryId, FK_SubCategoryId = request.SubCategoryId, FK_BrandId = request.BrandId, FK_BaseUomId = request.BaseUomId, SalePrice = request.SalePrice, ReorderLevel = request.ReorderLevel, SyncSource = Models.SyncSource.Manual }; var conversions = (request.Conversions ?? []).Select(c => new Models.UomConversion { FK_ItemId = item.Id, FK_FromUomId = c.FromUomId, FK_ToUomId = c.ToUomId, Factor = c.Factor }).ToList(); item.Conversions = conversions; await uow.BeginAsync(ct); try { await repo.AddAsync(item); await uow.CommitAsync(ct); } catch { await uow.RollbackAsync(ct); throw; } return (await GetByIdAsync(item.Id))!; } public async Task UpdateAsync(Guid id, UpdateItemRequest request, CancellationToken ct = default) { var item = await repo.GetByIdAsync(id); if (item is null) return null; if (item.SyncSource == Models.SyncSource.Erp) return null; item.Name = request.Name; item.Description = request.Description; item.FK_CategoryId = request.CategoryId; item.FK_SubCategoryId = request.SubCategoryId; item.FK_BrandId = request.BrandId; item.FK_BaseUomId = request.BaseUomId; item.SalePrice = request.SalePrice; item.ReorderLevel = request.ReorderLevel; item.IsActive = request.IsActive; await uow.BeginAsync(ct); try { repo.Update(item); await uow.CommitAsync(ct); } catch { await uow.RollbackAsync(ct); throw; } return (await GetByIdAsync(id))!; } public async Task DeleteAsync(Guid id, CancellationToken ct = default) { var item = await repo.GetByIdAsync(id); if (item is null || !item.IsActive) return false; if (item.SyncSource == Models.SyncSource.Erp) return false; await uow.BeginAsync(ct); try { repo.Remove(item); await uow.CommitAsync(ct); } catch { await uow.RollbackAsync(ct); throw; } return true; } public async Task?> ReplaceConversionsAsync(Guid id, ReplaceConversionsRequest request, CancellationToken ct = default) { var item = await repo.GetByIdAsync(id); if (item is null || !item.IsActive) return null; if (item.SyncSource == Models.SyncSource.Erp) return null; var conversions = request.Conversions.Select(c => new Models.UomConversion { FK_ItemId = id, FK_FromUomId = c.FromUomId, FK_ToUomId = c.ToUomId, Factor = c.Factor }).ToList(); await uow.BeginAsync(ct); try { await repo.ReplaceConversionsAsync(id, conversions); await uow.CommitAsync(ct); } catch { await uow.RollbackAsync(ct); throw; } return (await GetByIdAsync(id))!.Conversions; } }