using Distribution.Infra.UoW; namespace Distribution.Features.ExpenseVendor; public record PagedExpenseVendorsResponse(List Items, int Page, int PageSize, int Total); public class ExpenseVendorService(IExpenseVendorRepository repo, IUnitOfWork uow) { private static ExpenseVendorResponse Map(Models.ExpenseVendor v) => new(v.Id, v.Name, v.ContactInfo, v.IsActive); public async Task ListAsync(string? search, int page, int pageSize) { if (page < 1) page = 1; if (pageSize < 1 || pageSize > 200) pageSize = 50; search = string.IsNullOrWhiteSpace(search) ? null : search.Trim(); var items = await repo.ListAsync(search, page, pageSize); var total = await repo.CountAsync(search); return new PagedExpenseVendorsResponse(items.Select(Map).ToList(), page, pageSize, total); } public async Task CreateAsync(CreateExpenseVendorRequest request, CancellationToken ct = default) { var name = (request.Name ?? "").Trim(); if (string.IsNullOrEmpty(name)) throw new InvalidOperationException("Name is required"); if (await repo.NameExistsAsync(name, null)) throw new InvalidOperationException("A vendor with this name already exists"); var entity = new Models.ExpenseVendor { Name = name, ContactInfo = request.ContactInfo }; await uow.BeginAsync(ct); try { await repo.AddAsync(entity); await uow.CommitAsync(ct); } catch { await uow.RollbackAsync(ct); throw; } return Map(entity); } public async Task UpdateAsync(Guid id, UpdateExpenseVendorRequest request, CancellationToken ct = default) { var entity = await repo.GetByIdAsync(id); if (entity is null) return null; var name = (request.Name ?? "").Trim(); if (string.IsNullOrEmpty(name)) throw new InvalidOperationException("Name is required"); if (await repo.NameExistsAsync(name, id)) throw new InvalidOperationException("A vendor with this name already exists"); entity.Name = name; entity.ContactInfo = request.ContactInfo; entity.IsActive = request.IsActive; await uow.BeginAsync(ct); try { repo.Update(entity); await uow.CommitAsync(ct); } catch { await uow.RollbackAsync(ct); throw; } return Map(entity); } }