-
Notifications
You must be signed in to change notification settings - Fork 0
Settlements Example
Masked-Kunsiquat edited this page Dec 24, 2025
·
1 revision
SYSTEM ARCHITECT: Step-by-step example showing settlements in action
Participants:
- Alice
- Bob
- Charlie
Trip Currency: USD
Expense 1: Hotel - $300
Paid by: Alice
Split: Equal (Alice $100, Bob $100, Charlie $100)
Expense 2: Lift Tickets - $150
Paid by: Bob
Split: Equal (Alice $50, Bob $50, Charlie $50)
Expense 3: Groceries - $90
Paid by: Alice
Split: Equal (Alice $30, Bob $30, Charlie $30)
expenses:
| id | paidBy | amount | date |
|---|---|---|---|
| e1 | alice | 30000 | Jan 15 |
| e2 | bob | 15000 | Jan 15 |
| e3 | alice | 9000 | Jan 16 |
expense_splits:
| id | expenseId | participantId | amount |
|---|---|---|---|
| s1 | e1 | alice | 10000 |
| s2 | e1 | bob | 10000 |
| s3 | e1 | charlie | 10000 |
| s4 | e2 | alice | 5000 |
| s5 | e2 | bob | 5000 |
| s6 | e2 | charlie | 5000 |
| s7 | e3 | alice | 3000 |
| s8 | e3 | bob | 3000 |
| s9 | e3 | charlie | 3000 |
settlements: (empty)
Alice:
totalPaid = $300 (e1) + $90 (e3) = $390
totalOwed = $100 (s1) + $50 (s4) + $30 (s7) = $180
netPosition = $390 - $180 = +$210 (owed $210)
Bob:
totalPaid = $150 (e2) = $150
totalOwed = $100 (s2) + $50 (s5) + $30 (s8) = $180
netPosition = $150 - $180 = -$30 (owes $30)
Charlie:
totalPaid = $0
totalOwed = $100 (s3) + $50 (s6) + $30 (s9) = $180
netPosition = $0 - $180 = -$180 (owes $180)
Verification: +$210 + (-$30) + (-$180) = $0 ✓
Charlie → Alice: $180
Bob → Alice: $30
┌───────────────────────────────────────┐
│ Balances │
├───────────────────────────────────────┤
│ Alice: +$210.00 │
│ (is owed money) │
│ │
│ Bob: -$30.00 │
│ (owes money) │
│ │
│ Charlie: -$180.00 │
│ (owes money) │
└───────────────────────────────────────┘
┌───────────────────────────────────────┐
│ Settlement Suggestions │
├───────────────────────────────────────┤
│ Charlie pays Alice: $180.00 │
│ Bob pays Alice: $30.00 │
└───────────────────────────────────────┘
Bob pays Alice $20 via Venmo (not the full $30 owed).
Settlement 1:
from: Bob
to: Alice
originalCurrency: USD
originalAmountMinor: 2000 ($20.00)
fxRateToTrip: null (same currency)
convertedAmountMinor: 2000 ($20.00)
date: Jan 20
description: "Partial payment"
paymentMethod: "venmo"
expenseSplitId: null (general payment, not linked to specific expense)
settlements:
| id | fromParticipantId | toParticipantId | convertedAmountMinor | date |
|---|---|---|---|---|
| st1 | bob | alice | 2000 | Jan 20 |
Alice:
Base balance:
totalPaid = $390
totalOwed = $180
Settlement adjustment:
Received $20 from Bob → totalOwed += $20 = $200
netPosition = $390 - $200 = +$190
Bob:
Base balance:
totalPaid = $150
totalOwed = $180
Settlement adjustment:
Paid $20 to Alice → totalPaid += $20 = $170
netPosition = $170 - $180 = -$10
Charlie:
Base balance:
totalPaid = $0
totalOwed = $180
Settlement adjustment: (none)
netPosition = $0 - $180 = -$180
Verification: +$190 + (-$10) + (-$180) = $0 ✓
Charlie → Alice: $180
Bob → Alice: $10 (reduced from $30)
┌───────────────────────────────────────┐
│ Balances │
├───────────────────────────────────────┤
│ Alice: +$190.00 │
│ (was +$210.00) │
│ Settlements received: $20.00 │
│ │
│ Bob: -$10.00 │
│ (was -$30.00) │
│ Settlements paid: $20.00 │
│ │
│ Charlie: -$180.00 │
│ (unchanged) │
└───────────────────────────────────────┘
┌───────────────────────────────────────┐
│ Settlement Suggestions │
├───────────────────────────────────────┤
│ Charlie pays Alice: $180.00 │
│ Bob pays Alice: $10.00 ← reduced │
└───────────────────────────────────────┘
┌───────────────────────────────────────┐
│ Settlement History │
├───────────────────────────────────────┤
│ Jan 20, 2025 │
│ Bob → Alice: $20.00 │
│ Method: Venmo │
│ Note: Partial payment │
└───────────────────────────────────────┘
Charlie wants to specifically pay off his share of the hotel bill ($100).
Settlement 2:
from: Charlie
to: Alice
originalCurrency: USD
originalAmountMinor: 10000 ($100.00)
fxRateToTrip: null
convertedAmountMinor: 10000 ($100.00)
date: Jan 21
description: "Hotel split payment"
paymentMethod: "cash"
expenseSplitId: s3 ← LINKED to Charlie's hotel split
settlements:
| id | fromParticipantId | toParticipantId | convertedAmountMinor | expenseSplitId | date |
|---|---|---|---|---|---|
| st1 | bob | alice | 2000 | null | Jan 20 |
| st2 | charlie | alice | 10000 | s3 | Jan 21 |
Alice:
Base balance:
totalPaid = $390
totalOwed = $180
Settlement adjustments:
Received $20 from Bob → +$20
Received $100 from Charlie → +$100
totalOwed = $180 + $20 + $100 = $300
netPosition = $390 - $300 = +$90
Bob:
Base balance:
totalPaid = $150
totalOwed = $180
Settlement adjustment:
Paid $20 to Alice → +$20
totalPaid = $150 + $20 = $170
netPosition = $170 - $180 = -$10
Charlie:
Base balance:
totalPaid = $0
totalOwed = $180
Settlement adjustment:
Paid $100 to Alice → +$100
totalPaid = $0 + $100 = $100
netPosition = $100 - $180 = -$80
Verification: +$90 + (-$10) + (-$80) = $0 ✓
Charlie → Alice: $80 (reduced from $180)
Bob → Alice: $10
┌───────────────────────────────────────┐
│ Expense: Hotel - $300.00 │
├───────────────────────────────────────┤
│ Paid by: Alice │
│ Date: Jan 15, 2025 │
│ │
│ Split Breakdown: │
│ │
│ Alice: $100.00 │
│ Status: Paid (she paid the expense) │
│ │
│ Bob: $100.00 │
│ Status: Unpaid │
│ [Mark as Paid] │
│ │
│ Charlie: $100.00 │
│ Status: Fully Paid ✓ │
│ Settlements: │
│ • $100.00 on Jan 21 (Cash) │
│ Note: Hotel split payment │
└───────────────────────────────────────┘
Bob pays his remaining $10 owed to Alice.
Settlement 3:
from: Bob
to: Alice
originalCurrency: USD
originalAmountMinor: 1000 ($10.00)
fxRateToTrip: null
convertedAmountMinor: 1000 ($10.00)
date: Jan 22
description: "Final payment"
paymentMethod: "venmo"
expenseSplitId: null
settlements:
| id | fromParticipantId | toParticipantId | convertedAmountMinor | date |
|---|---|---|---|---|
| st1 | bob | alice | 2000 | Jan 20 |
| st2 | charlie | alice | 10000 | Jan 21 |
| st3 | bob | alice | 1000 | Jan 22 |
Alice:
Base balance:
totalPaid = $390
totalOwed = $180
Settlement adjustments:
Received $20 + $100 + $10 = $130
totalOwed = $180 + $130 = $310
netPosition = $390 - $310 = +$80
Bob:
Base balance:
totalPaid = $150
totalOwed = $180
Settlement adjustments:
Paid $20 + $10 = $30
totalPaid = $150 + $30 = $180
netPosition = $180 - $180 = $0 ← SETTLED!
Charlie:
Base balance:
totalPaid = $0
totalOwed = $180
Settlement adjustment:
Paid $100
totalPaid = $0 + $100 = $100
netPosition = $100 - $180 = -$80
Verification: +$80 + $0 + (-$80) = $0 ✓
Charlie → Alice: $80
┌───────────────────────────────────────┐
│ Balances │
├───────────────────────────────────────┤
│ Alice: +$80.00 │
│ Settlements received: $130.00 │
│ │
│ Bob: $0.00 ✓ SETTLED │
│ Settlements paid: $30.00 │
│ │
│ Charlie: -$80.00 │
│ Settlements paid: $100.00 │
└───────────────────────────────────────┘
┌───────────────────────────────────────┐
│ Settlement Suggestions │
├───────────────────────────────────────┤
│ Charlie pays Alice: $80.00 │
│ │
│ ✓ Bob is settled up! │
└───────────────────────────────────────┘
Charlie is in Europe and pays Alice €75 via PayPal.
Date: Jan 23, 2025
From: EUR
To: USD
Rate: 1.08 (1 EUR = 1.08 USD)
Source: CachedFxRateProvider (fetched from Frankfurter API)
Settlement 4:
from: Charlie
to: Alice
originalCurrency: EUR
originalAmountMinor: 7500 (€75.00)
fxRateToTrip: 1.08
convertedAmountMinor: 8100 (round(7500 * 1.08) = $81.00)
date: Jan 23
description: "PayPal payment from Europe"
paymentMethod: "paypal"
expenseSplitId: null
settlements:
| id | fromParticipantId | toParticipantId | originalCurrency | originalAmountMinor | fxRateToTrip | convertedAmountMinor | date |
|---|---|---|---|---|---|---|---|
| st1 | bob | alice | USD | 2000 | null | 2000 | Jan 20 |
| st2 | charlie | alice | USD | 10000 | null | 10000 | Jan 21 |
| st3 | bob | alice | USD | 1000 | null | 1000 | Jan 22 |
| st4 | charlie | alice | EUR | 7500 | 1.08 | 8100 | Jan 23 |
Alice:
Base balance:
totalPaid = $390
totalOwed = $180
Settlement adjustments:
Received $20 + $100 + $10 + $81 = $211
totalOwed = $180 + $211 = $391
netPosition = $390 - $391 = -$1 ← Charlie overpaid by $1!
Bob:
Base balance:
totalPaid = $150
totalOwed = $180
Settlement adjustments:
Paid $20 + $10 = $30
totalPaid = $150 + $30 = $180
netPosition = $180 - $180 = $0
Charlie:
Base balance:
totalPaid = $0
totalOwed = $180
Settlement adjustments:
Paid $100 + $81 = $181
totalPaid = $0 + $181 = $181
netPosition = $181 - $180 = +$1 ← Charlie is now owed $1!
Verification: -$1 + $0 + $1 = $0 ✓
Alice → Charlie: $1 (Charlie overpaid by $1)
┌───────────────────────────────────────┐
│ Balances │
├───────────────────────────────────────┤
│ Alice: -$1.00 │
│ Settlements received: $211.00 │
│ Note: Owes Charlie $1 (overpayment) │
│ │
│ Bob: $0.00 ✓ SETTLED │
│ │
│ Charlie: +$1.00 │
│ Settlements paid: $181.00 │
│ Note: Overpaid by $1 │
└───────────────────────────────────────┘
┌───────────────────────────────────────┐
│ Settlement History │
├───────────────────────────────────────┤
│ Jan 23, 2025 │
│ Charlie → Alice: €75.00 EUR │
│ (=$81.00 USD @ 1.08) │
│ Method: PayPal │
│ Note: PayPal payment from Europe │
│ │
│ Jan 22, 2025 │
│ Bob → Alice: $10.00 USD │
│ Method: Venmo │
│ │
│ Jan 21, 2025 │
│ Charlie → Alice: $100.00 USD │
│ Method: Cash │
│ Linked to: Hotel expense │
│ │
│ Jan 20, 2025 │
│ Bob → Alice: $20.00 USD │
│ Method: Venmo │
└───────────────────────────────────────┘
$300 (Hotel) + $150 (Lift Tickets) + $90 (Groceries) = $540
$20 + $100 + $10 + $81 = $211
$1 (Alice owes Charlie due to overpayment)
Alice:
- Paid for expenses: $390
- Owed for splits: $180
- Received in settlements: $211
- Net position: -$1 (owes Charlie)
Bob:
- Paid for expenses: $150
- Owed for splits: $180
- Paid in settlements: $30
- Net position: $0 (settled)
Charlie:
- Paid for expenses: $0
- Owed for splits: $180
- Paid in settlements: $181
- Net position: +$1 (owed by Alice)
- Flexible payments: Bob paid in two installments ($20 + $10)
- Expense-specific tracking: Charlie's hotel payment linked to specific split
- Multi-currency support: Charlie's EUR payment converted to USD automatically
- Automatic recalculation: Balance suggestions updated after each settlement
- Overpayment handling: System correctly shows when someone overpays
- Audit trail: Complete history of who paid what, when, and how
- Deterministic: Same data always produces same balances (testable)
This example demonstrates all core features of the settlements system working together in a realistic scenario.