SudoSOS Back-end API / fines
Fines ​
A fine is a small penalty (capped at 5 EUR per round) applied to a user with a persistently negative Balance. Fines are handed out in batches as a debtor-recovery step, not as a per-transaction fee. The amount is min(5 EUR, floor(-balance / 5 EUR) * 1 EUR), never negative, so a user at -3.40 EUR gets nothing and a user at -42 EUR still gets only 5 EUR.
Three entities, one story ​
- FineHandoutEvent is one batch. A single
POST /fines/handoutcreates one event that groups every fine handed out in that round, records thereferenceDatethe fines were calculated against, and the admin who triggered the run. - Fine is one user's slice of a batch. Each fine owns a debit Transfer that moves the penalty off the user's balance.
- UserFineGroup is the per-user wrapper that ties every fine a user has ever received together and (optionally) holds the
waivedTransferthat cancels them. User.currentFines points to it while the user still owes; a user has at most one open group at a time.
Lifecycle ​
An admin (typically the financial responsible) hands out fines manually. There is no cron. The typical sequence is:
GET /fines/eligiblereturns users who are below -5 EUR both now and on the reference date (calculateFinesOnDate). The double check exists so a user who just topped up does not get fined for a historical low.POST /fines/notifysends a warning email (sendFineWarnings) so users have a grace period to top up.POST /fines/handoutruns the actual handout in one database transaction (handOutFines): one FineHandoutEvent, one Fine per eligible user, one debit Transfer per fine, and one notification per fined user.
Waiving ​
POST /users/{id}/fines/waive creates a credit Transfer on the user's UserFineGroup as waivedTransfer. Waiving is replace-not-append: if the group already has a waived transfer it is removed and a fresh one is created with the new total. Waiving the full outstanding amount nulls User.currentFines, even if the user's balance is still negative.
Undoing ​
DELETE /fines/single/{id} removes one fine and its debit transfer; if that was the only fine in the group, the UserFineGroup goes too. DELETE /fines/handout/{id} rolls back a whole batch the same way. Both delete instead of writing a compensating row; fines are the rare exception to the rule that nothing on the ledger disappears.
Reflection on the balance ​
Balance treats fine and waiver transfers like any other transfer. On top of that, BalanceResponse surfaces the fine state as four "now" fields: fine (outstanding), fineSince (timestamp of the first fine), nrFines (count), and fineWaived (amount already waived). Those fields are not historical, so ignore them when date is in the past.
Reporting ​
GET /fines/report?fromDate=...&tillDate=... returns aggregate handed-out and waived totals for treasurer reconciliation; GET /fines/report/pdf produces the printable version. The service orchestrating everything above lives in debtors.
Classes ​
| Class | Description |
|---|---|
| Fine | - |
| FineHandoutEvent | - |
| UserFineGroup | - |