Add CRUD operations for claims
This commit is contained in:
@@ -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(); }
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user