Add CRUD operations for claims

This commit is contained in:
2026-09-08 16:05:45 +05:30
parent 19ff0b5578
commit 5f303321d0
5 changed files with 56 additions and 3 deletions
@@ -6,6 +6,10 @@ namespace Distribution.Features.Claims;
[ApiController] [ApiController]
public class ClaimController(ClaimService service, ICurrentUserService currentUser) : ControllerBase public class ClaimController(ClaimService service, ICurrentUserService currentUser) : ControllerBase
{ {
[HttpGet("api/claims")]
public async Task<IActionResult> GetAll() =>
Ok(await service.GetAllAsync());
[HttpGet("api/claims/{id:guid}")] [HttpGet("api/claims/{id:guid}")]
public async Task<IActionResult> GetById(Guid id) public async Task<IActionResult> GetById(Guid id)
{ {
@@ -24,13 +28,25 @@ public class ClaimController(ClaimService service, ICurrentUserService currentUs
catch (InvalidOperationException ex) { return Conflict(new { error = ex.Message }); } catch (InvalidOperationException ex) { return Conflict(new { error = ex.Message }); }
} }
[HttpPut("api/claims/{id:guid}")]
public async Task<IActionResult> 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")] [HttpPost("api/claims/{id:guid}/settle")]
public async Task<IActionResult> Settle(Guid id, SettleClaimRequest request, CancellationToken ct) public async Task<IActionResult> 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 try
{ {
var result = await service.SettleAsync(id, request, userId, ct); var result = await service.SettleAsync(id, request, userId.Value, ct);
return Ok(result); return Ok(result);
} }
catch (KeyNotFoundException) { return NotFound(); } catch (KeyNotFoundException) { return NotFound(); }
+8 -1
View File
@@ -4,7 +4,14 @@ namespace Distribution.Features.Claims;
public record CreateClaimRequest(Guid AgencyUserId, string Details); 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( public record ClaimResponse(
Guid Id, Guid Id,
@@ -8,6 +8,12 @@ public class ClaimRepository(AppDBContext context) : IClaimRepository
public async Task<Models.Claim?> GetByIdAsync(Guid id) => public async Task<Models.Claim?> GetByIdAsync(Guid id) =>
await context.Claims.FirstOrDefaultAsync(c => c.Id == id); await context.Claims.FirstOrDefaultAsync(c => c.Id == id);
public async Task<List<Models.Claim>> GetAllAsync() =>
await context.Claims
.Where(c => c.IsActive)
.OrderByDescending(c => c.CreatedAt)
.ToListAsync();
public async Task<List<Models.Claim>> GetByAgencyUserIdAsync(Guid agencyUserId) => public async Task<List<Models.Claim>> GetByAgencyUserIdAsync(Guid agencyUserId) =>
await context.Claims await context.Claims
.Where(c => c.FK_AgencyUserId == agencyUserId && c.IsActive) .Where(c => c.FK_AgencyUserId == agencyUserId && c.IsActive)
@@ -15,6 +15,9 @@ public class ClaimService(IClaimRepository repo, IUnitOfWork uow)
return Map(c); return Map(c);
} }
public async Task<List<ClaimResponse>> GetAllAsync() =>
(await repo.GetAllAsync()).Select(Map).ToList();
public async Task<List<ClaimResponse>> GetByAgencyUserIdAsync(Guid agencyUserId) => public async Task<List<ClaimResponse>> GetByAgencyUserIdAsync(Guid agencyUserId) =>
(await repo.GetByAgencyUserIdAsync(agencyUserId)).Select(Map).ToList(); (await repo.GetByAgencyUserIdAsync(agencyUserId)).Select(Map).ToList();
@@ -40,6 +43,26 @@ public class ClaimService(IClaimRepository repo, IUnitOfWork uow)
return Map(claim); return Map(claim);
} }
public async Task<ClaimResponse> 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<ClaimResponse> SettleAsync(Guid id, SettleClaimRequest request, Guid approverId, CancellationToken ct = default) public async Task<ClaimResponse> SettleAsync(Guid id, SettleClaimRequest request, Guid approverId, CancellationToken ct = default)
{ {
var claim = await repo.GetByIdAsync(id); var claim = await repo.GetByIdAsync(id);
@@ -3,6 +3,7 @@ namespace Distribution.Features.Claims;
public interface IClaimRepository public interface IClaimRepository
{ {
Task<Models.Claim?> GetByIdAsync(Guid id); Task<Models.Claim?> GetByIdAsync(Guid id);
Task<List<Models.Claim>> GetAllAsync();
Task<List<Models.Claim>> GetByAgencyUserIdAsync(Guid agencyUserId); Task<List<Models.Claim>> GetByAgencyUserIdAsync(Guid agencyUserId);
Task AddAsync(Models.Claim entity); Task AddAsync(Models.Claim entity);
void Update(Models.Claim entity); void Update(Models.Claim entity);