A closed period is immutable
Closing writes oneBILL row recording what was invoiced. It is never rewritten.
A refund that lands afterwards, a late receipt, a rate corrected in arrears — none of them restate a
closed period. The panel re-runs the summary, compares it against the stored figure, and shows the
difference as a credit note against the next period.
The BILL row moves no money
delta records the amount invoiced — negative, because it is what the account consumed. But the row
is inert.
The gateway’s recharge poll selects only RECHARGE and TRANSFER, so nothing ever applies a
BILL. Arrears never enter account_balance.balance, which keeps its non-negative constraint and
its single meaning: money available to spend.
What the row is load-bearing for is the window. The most recent BILL row —
max(created_at) WHERE entry_type = 'BILL' — is the start of the unbilled period — so closing is what
returns a postpaid account’s headroom.
Closing twice is a conflict, not a second invoice
The reference is deterministic:uq_balance_ledger_ref is unique where not null, so the same period cannot be closed twice.
Deterministic on purpose — two operators clicking at once must collide, and a retried request must
not produce a duplicate. The label is YYYY-MM or a date span, and may not contain a colon, so the
last colon splits the reference back apart and an account id containing colons still round-trips.
What a statement is computed from
The invoice is a pure function ofcdr_submit over a half-open window. It never reads the
postpaid counter, never reads account_balance, and never reads the ledger.
So the only thing that can make a bill wrong is a missing or extra cdr_submit row.
Half-open windows
A period covers[start, end) — start inclusive, end exclusive. A message at exactly midnight on the
boundary belongs to the later period, and belongs to exactly one.
That is the property worth checking in any query you write yourself: BETWEEN in SQL is inclusive at
both ends and will double-count a boundary message across two statements.
The order that works
1
Let the period end
Billable figures are final the moment the day is over — billing is on
submitted.2
Review before closing
The summary is a live query until you close it. This is the last point at which a correction is
an edit rather than a credit note.
3
Close
One
BILL row, idempotent on the reference.4
Let later corrections be credit notes
They are visible, attributable, and land on the next period rather than silently altering a
number the customer has already paid.
Related
Postpaid
Credit limits, and why closing returns headroom.
Usage and retention
What survives after the call records are purged.