From 5f303321d0734c1e3284402c595f43f41d49395b Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Tue, 8 Sep 2026 16:05:45 +0530 Subject: [PATCH] Add CRUD operations for claims --- .../Features/Claims/ClaimController.cs | 20 ++++++++++++++-- Distribution/Features/Claims/ClaimDTOs.cs | 9 +++++++- .../Features/Claims/ClaimRepository.cs | 6 +++++ Distribution/Features/Claims/ClaimService.cs | 23 +++++++++++++++++++ .../Features/Claims/IClaimRepository.cs | 1 + 5 files changed, 56 insertions(+), 3 deletions(-) diff --git a/Distribution/Features/Claims/ClaimController.cs b/Distribution/Features/Claims/ClaimController.cs index b4a6de2..922835f 100644 --- a/Distribution/Features/Claims/ClaimController.cs +++ b/Distribution/Features/Claims/ClaimController.cs @@ -6,6 +6,10 @@ namespace Distribution.Features.Claims; [ApiController] public class ClaimController(ClaimService service, ICurrentUserService currentUser) : ControllerBase { + [HttpGet("api/claims")] + public async Task GetAll() => + Ok(await service.GetAllAsync()); + [HttpGet("api/claims/{id:guid}")] public async Task GetById(Guid id) { @@ -24,13 +28,25 @@ public class ClaimController(ClaimService service, ICurrentUserService currentUs catch (InvalidOperationException ex) { return Conflict(new { error = ex.Message }); } } + [HttpPut("api/claims/{id:guid}")] + public async Task Update(Guid id, UpdateClaimRequest request, CancellationToken ct) + { + try + { + var result = await service.UpdateAsync(id, request, ct); + return Ok(result); + } + catch (InvalidOperationException ex) { return Conflict(new { error = ex.Message }); } + } + [HttpPost("api/claims/{id:guid}/settle")] public async Task Settle(Guid id, SettleClaimRequest request, CancellationToken ct) { - var userId = currentUser.UserId ?? throw new UnauthorizedAccessException(); + var userId = currentUser.UserId ?? request.SettledByUserId; + if (userId is null) return BadRequest(new { error = "SettledByUserId is required while auth is unavailable" }); try { - var result = await service.SettleAsync(id, request, userId, ct); + var result = await service.SettleAsync(id, request, userId.Value, ct); return Ok(result); } catch (KeyNotFoundException) { return NotFound(); } diff --git a/Distribution/Features/Claims/ClaimDTOs.cs b/Distribution/Features/Claims/ClaimDTOs.cs index a459855..e99a634 100644 --- a/Distribution/Features/Claims/ClaimDTOs.cs +++ b/Distribution/Features/Claims/ClaimDTOs.cs @@ -4,7 +4,14 @@ namespace Distribution.Features.Claims; public record CreateClaimRequest(Guid AgencyUserId, string Details); -public record SettleClaimRequest(decimal SettlementAmount); +// Only editable while the claim is still Open — once settled, the settlement amount is a +// financial record and the claim details it was settled against should not silently change. +public record UpdateClaimRequest(Guid AgencyUserId, string Details); + +public record SettleClaimRequest( + decimal SettlementAmount, + // TEMPORARY: required while auth doesn't resolve a logged-in user server-side — see testbug.md #28. + Guid? SettledByUserId = null); public record ClaimResponse( Guid Id, diff --git a/Distribution/Features/Claims/ClaimRepository.cs b/Distribution/Features/Claims/ClaimRepository.cs index 68f6d07..4203386 100644 --- a/Distribution/Features/Claims/ClaimRepository.cs +++ b/Distribution/Features/Claims/ClaimRepository.cs @@ -8,6 +8,12 @@ public class ClaimRepository(AppDBContext context) : IClaimRepository public async Task GetByIdAsync(Guid id) => await context.Claims.FirstOrDefaultAsync(c => c.Id == id); + public async Task> GetAllAsync() => + await context.Claims + .Where(c => c.IsActive) + .OrderByDescending(c => c.CreatedAt) + .ToListAsync(); + public async Task> GetByAgencyUserIdAsync(Guid agencyUserId) => await context.Claims .Where(c => c.FK_AgencyUserId == agencyUserId && c.IsActive) diff --git a/Distribution/Features/Claims/ClaimService.cs b/Distribution/Features/Claims/ClaimService.cs index d882bfb..25cc17c 100644 --- a/Distribution/Features/Claims/ClaimService.cs +++ b/Distribution/Features/Claims/ClaimService.cs @@ -15,6 +15,9 @@ public class ClaimService(IClaimRepository repo, IUnitOfWork uow) return Map(c); } + public async Task> GetAllAsync() => + (await repo.GetAllAsync()).Select(Map).ToList(); + public async Task> GetByAgencyUserIdAsync(Guid agencyUserId) => (await repo.GetByAgencyUserIdAsync(agencyUserId)).Select(Map).ToList(); @@ -40,6 +43,26 @@ public class ClaimService(IClaimRepository repo, IUnitOfWork uow) return Map(claim); } + public async Task UpdateAsync(Guid id, UpdateClaimRequest request, CancellationToken ct = default) + { + var claim = await repo.GetByIdAsync(id); + if (claim is null || !claim.IsActive) + throw new InvalidOperationException("Claim not found."); + if (claim.Status == ClaimStatus.Settled) + throw new InvalidOperationException("Cannot edit a claim that has already been settled."); + if (string.IsNullOrWhiteSpace(request.Details)) + throw new InvalidOperationException("Claim details are required."); + + claim.FK_AgencyUserId = request.AgencyUserId; + claim.Details = request.Details; + + await uow.BeginAsync(ct); + try { repo.Update(claim); await uow.CommitAsync(ct); } + catch { await uow.RollbackAsync(ct); throw; } + + return Map(claim); + } + public async Task SettleAsync(Guid id, SettleClaimRequest request, Guid approverId, CancellationToken ct = default) { var claim = await repo.GetByIdAsync(id); diff --git a/Distribution/Features/Claims/IClaimRepository.cs b/Distribution/Features/Claims/IClaimRepository.cs index 839ee78..0aca4c5 100644 --- a/Distribution/Features/Claims/IClaimRepository.cs +++ b/Distribution/Features/Claims/IClaimRepository.cs @@ -3,6 +3,7 @@ namespace Distribution.Features.Claims; public interface IClaimRepository { Task GetByIdAsync(Guid id); + Task> GetAllAsync(); Task> GetByAgencyUserIdAsync(Guid agencyUserId); Task AddAsync(Models.Claim entity); void Update(Models.Claim entity);