Dev #23
@@ -6,6 +6,10 @@ namespace Distribution.Features.Claims;
|
||||
[ApiController]
|
||||
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}")]
|
||||
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 }); }
|
||||
}
|
||||
|
||||
[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")]
|
||||
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
|
||||
{
|
||||
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(); }
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -8,6 +8,12 @@ public class ClaimRepository(AppDBContext context) : IClaimRepository
|
||||
public async Task<Models.Claim?> GetByIdAsync(Guid 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) =>
|
||||
await context.Claims
|
||||
.Where(c => c.FK_AgencyUserId == agencyUserId && c.IsActive)
|
||||
|
||||
@@ -15,6 +15,9 @@ public class ClaimService(IClaimRepository repo, IUnitOfWork uow)
|
||||
return Map(c);
|
||||
}
|
||||
|
||||
public async Task<List<ClaimResponse>> GetAllAsync() =>
|
||||
(await repo.GetAllAsync()).Select(Map).ToList();
|
||||
|
||||
public async Task<List<ClaimResponse>> 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<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)
|
||||
{
|
||||
var claim = await repo.GetByIdAsync(id);
|
||||
|
||||
@@ -3,6 +3,7 @@ namespace Distribution.Features.Claims;
|
||||
public interface IClaimRepository
|
||||
{
|
||||
Task<Models.Claim?> GetByIdAsync(Guid id);
|
||||
Task<List<Models.Claim>> GetAllAsync();
|
||||
Task<List<Models.Claim>> GetByAgencyUserIdAsync(Guid agencyUserId);
|
||||
Task AddAsync(Models.Claim entity);
|
||||
void Update(Models.Claim entity);
|
||||
|
||||
Reference in New Issue
Block a user