profileShare

rasmusjy / splitapp-backend-clean-onion

Read-only snapshot

No repository description.

main default branch 429 files Expires Sep 13, 2026, 9:06 AM
SettlementsController.cs 5,273 bytes
1 using App.BLL.Services;
2 using App.DTO.Mappers;
3 using App.DTO.v1;
4 using Asp.Versioning;
5 using Microsoft.AspNetCore.Authentication.JwtBearer;
6 using Microsoft.AspNetCore.Authorization;
7 using Microsoft.AspNetCore.Mvc;
8 using System.Net;
9 using System.Security.Claims;
10
11 namespace WebApp.ApiControllers;
12
13 [ApiVersion("1.0")]
14 [Route("api/v{version:apiVersion}/[controller]")]
15 [ApiController]
16 [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
17 public class SettlementsController : ControllerBase
18 {
19 private readonly ISettlementService _settlementService;
20 private readonly ITripService _tripService;
21
22 public SettlementsController(ISettlementService settlementService, ITripService tripService)
23 {
24 _settlementService = settlementService;
25 _tripService = tripService;
26 }
27
28 private Guid GetUserId() => Guid.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!);
29
30 // GET: api/v1/settlements/trip/{tripId}
31 [HttpGet("trip/{tripId:guid}")]
32 [Produces("application/json")]
33 [ProducesResponseType<SettlementPlanDto>((int)HttpStatusCode.OK)]
34 [ProducesResponseType((int)HttpStatusCode.NotFound)]
35 [ProducesResponseType((int)HttpStatusCode.Forbidden)]
36 public async Task<ActionResult<SettlementPlanDto>> GetLatestPlan(Guid tripId)
37 {
38 var userId = GetUserId();
39
40 if (!await _tripService.IsParticipantAsync(tripId, userId)) return Forbid();
41
42 var plan = await _settlementService.GetLatestPlanRawAsync(tripId);
43 if (plan == null) return NotFound();
44
45 return Ok(SettlementMapper.MapToDto(plan));
46 }
47
48 // GET: api/v1/settlements/trip/{tripId}/summary
49 [HttpGet("trip/{tripId:guid}/summary")]
50 [Produces("application/json")]
51 [ProducesResponseType<SettlementSummaryDto>((int)HttpStatusCode.OK)]
52 [ProducesResponseType((int)HttpStatusCode.Forbidden)]
53 public async Task<ActionResult<SettlementSummaryDto>> GetSettlementSummary(Guid tripId)
54 {
55 var userId = GetUserId();
56
57 if (!await _tripService.IsParticipantAsync(tripId, userId)) return Forbid();
58
59 // Calculate balances through service
60 var balanceEntries = await _settlementService.CalculateBalancesAsync(tripId);
61 var balances = balanceEntries.Select(b => new BalanceDto
62 {
63 UserId = b.UserId,
64 UserName = b.UserName,
65 Balance = b.NetBalance
66 }).ToList();
67
68 // Get latest plan
69 var plan = await _settlementService.GetLatestPlanRawAsync(tripId);
70
71 return Ok(new SettlementSummaryDto
72 {
73 Balances = balances,
74 LatestPlan = plan != null ? SettlementMapper.MapToDto(plan) : null
75 });
76 }
77
78 // GET: api/v1/settlements/trip/{tripId}/balances
79 [HttpGet("trip/{tripId:guid}/balances")]
80 [Produces("application/json")]
81 [ProducesResponseType<List<BalanceDto>>((int)HttpStatusCode.OK)]
82 [ProducesResponseType((int)HttpStatusCode.Forbidden)]
83 public async Task<ActionResult<List<BalanceDto>>> GetBalances(Guid tripId)
84 {
85 var userId = GetUserId();
86
87 if (!await _tripService.IsParticipantAsync(tripId, userId)) return Forbid();
88
89 var balanceEntries = await _settlementService.CalculateBalancesAsync(tripId);
90 var result = balanceEntries.Select(b => new BalanceDto
91 {
92 UserId = b.UserId,
93 UserName = b.UserName,
94 Balance = b.NetBalance
95 }).ToList();
96
97 return Ok(result);
98 }
99
100 // POST: api/v1/settlements/payments/{paymentId}/mark-paid
101 [HttpPost("payments/{paymentId:guid}/mark-paid")]
102 [Produces("application/json")]
103 [ProducesResponseType((int)HttpStatusCode.OK)]
104 [ProducesResponseType((int)HttpStatusCode.NotFound)]
105 [ProducesResponseType((int)HttpStatusCode.Forbidden)]
106 public async Task<IActionResult> MarkPaid(Guid paymentId)
107 {
108 var userId = GetUserId();
109
110 var payment = await _settlementService.GetPaymentByIdAsync(paymentId);
111 if (payment == null) return NotFound();
112
113 var (ok, errorCode) = await _settlementService.MarkPaidGuardedAsync(paymentId, userId);
114 if (!ok)
115 {
116 return errorCode switch
117 {
118 "forbidden" => Forbid(),
119 "notfound" => NotFound(),
120 _ => NotFound()
121 };
122 }
123
124 return Ok();
125 }
126
127 // POST: api/v1/settlements/payments/{paymentId}/confirm
128 [HttpPost("payments/{paymentId:guid}/confirm")]
129 [Produces("application/json")]
130 [ProducesResponseType((int)HttpStatusCode.OK)]
131 [ProducesResponseType((int)HttpStatusCode.NotFound)]
132 [ProducesResponseType((int)HttpStatusCode.Forbidden)]
133 public async Task<IActionResult> ConfirmPayment(Guid paymentId)
134 {
135 var userId = GetUserId();
136
137 var payment = await _settlementService.GetPaymentByIdAsync(paymentId);
138 if (payment == null) return NotFound();
139
140 var (ok, errorCode) = await _settlementService.ConfirmPaymentGuardedAsync(paymentId, userId);
141 if (!ok)
142 {
143 return errorCode switch
144 {
145 "forbidden" => Forbid(),
146 "notfound" => NotFound(),
147 _ => NotFound()
148 };
149 }
150
151 return Ok();
152 }
153 }
154