# customer-debt-ledger Specification

## Purpose
The ledger that is the source of truth for customer debt: every increase or repayment is an append-only entry, the customer's cached `debt_total` is kept mirroring the sum of those entries, and repayments are prevented from driving a balance below zero.
## Requirements
### Requirement: Every debt or repayment change is recorded as a ledger entry
The system SHALL record every change to a customer's debt balance — whether created from a POS order, entered manually, or from a repayment — as a `customer_debts` entry with a `type` of `pos_debt`, `manual_debt`, or `repayment`, a positive `amount`, and the `customer_id` it applies to. Entries of type `pos_debt` SHALL also record the `order_id` they originated from; entries of type `manual_debt` and `repayment` SHALL have no `order_id`.

#### Scenario: POS-originated debt entry links to its order
- **WHEN** an order is completed with a debt amount greater than zero
- **THEN** a `customer_debts` entry of type `pos_debt` is created with `order_id` set to that order's id and `amount` equal to the entered debt amount

#### Scenario: Manual debt entry has no linked order
- **WHEN** a debt amount is recorded for a customer directly (not through a POS order)
- **THEN** a `customer_debts` entry of type `manual_debt` is created with `order_id` set to null

#### Scenario: Repayment entry has no linked order
- **WHEN** a repayment is recorded for a customer
- **THEN** a `customer_debts` entry of type `repayment` is created with `order_id` set to null

### Requirement: Customer's cached debt total always mirrors the ledger
The system SHALL maintain a `debt_total` value on the customer record that reflects the net effect of all their `customer_debts` entries: `pos_debt` and `manual_debt` entries increase it by their `amount`; `repayment` entries decrease it by their `amount`. This value SHALL be updated in the same database transaction as the ledger entry that causes the change, so the two never observably diverge.

#### Scenario: Debt entry increases the cached total
- **WHEN** a `pos_debt` or `manual_debt` entry of amount X is recorded for a customer
- **THEN** that customer's `debt_total` increases by exactly X

#### Scenario: Repayment entry decreases the cached total
- **WHEN** a `repayment` entry of amount X is recorded for a customer
- **THEN** that customer's `debt_total` decreases by exactly X

### Requirement: Repayment cannot exceed the customer's current debt total
The system SHALL reject a repayment whose amount is greater than the customer's current `debt_total` at the time of the request, and SHALL prevent this check from being bypassed by concurrent requests (e.g. by locking the customer's row for the duration of the balance check and update).

#### Scenario: Repayment amount exceeds current debt
- **WHEN** a repayment is requested for a customer with an amount greater than that customer's current `debt_total`
- **THEN** the repayment is rejected and no `customer_debts` entry is created, and `debt_total` is unchanged

#### Scenario: Repayment amount equals current debt
- **WHEN** a repayment is requested for a customer with an amount exactly equal to that customer's current `debt_total`
- **THEN** the repayment succeeds, a `repayment` entry is recorded, and the customer's `debt_total` becomes zero

#### Scenario: Concurrent repayment requests cannot together overdraw the balance
- **WHEN** two repayment requests for the same customer are submitted at nearly the same time, and either one alone would be valid but their combined amount exceeds the customer's `debt_total`
- **THEN** only the requests whose cumulative amount does not exceed the `debt_total` at the time each is processed succeed; any request that would drive `debt_total` negative is rejected

