Skip to main content

Editing rates from the panel

Two operations on a product’s page, and the difference between them matters: Both save only the rules for the product you are looking at. The rest of the scope is untouched, so setting * for promo cannot disturb * for anything else. The whole-scope editor on the Pricing page still replaces a scope wholesale — which is correct there, because it shows you all of it.
An account’s own rules are tried first and the first match wins, so anything the account carries beats the default — including a bare catch-all.That is what makes a negotiated tariff an override rather than a suggestion. It also means an account-level catch-all silently disables every default you set afterwards.

Two identities, not one

Whether an account’s credit adds up is checked against two equations. Getting this wrong reports a false discrepancy on every account, so it is worth stating precisely.
The obvious equation is wrong. credited − consumed = balance + reserved was written that way in the design and the data refuted it immediately — it was out by exactly the consumption on every account.Consumption never touches the balance columns. Credit is reserved in blocks (balance -= n, reserved += n), spent from an in-memory counter, and the spent part is never written back. So reserved is a lifetime-consumed counter, not a hold, and every movement the database sees leaves balance + reserved unchanged.
The money identity holds to the unit on every account, which is what makes a genuine discrepancy meaningful.

What is excluded, and why

Lifetime, never a period

Credit is a running total. Any shorter window needs an opening balance that nothing records — and a wrong opening figure invents a discrepancy every time it is run.
So “reconcile this account for March” is not a question the data can answer. “Reconcile this account” is, and always has been.

Where the two sides come from

Never both for one day. That is the join that makes reconciliation survive the retention window: once a day is rolled up and purged, its consumption still counts.

When the numbers do not agree

Work through in this order:
1

Are there pending recharges?

A RECHARGE for an account with no account_balance row credits nothing and stays pending. It is in the ledger and not in the balance — which is exactly a money-side gap.
2

Was there an unclean shutdown?

A kill -9 strands credit in reserved. balance + reserved is still conserved, so the money identity holds — but the account has less spendable than it should until POST /ops/credit/release runs.
3

Is the rollup behind?

A day neither rolled up nor still in cdr_submit is consumption nobody counts. The purge is supposed to make this impossible; check it has not been bypassed.
4

Is the panel reading the same database?

METRICS_DATABASE_URL against the gateway’s FIREFLO_DB_URL. A split here makes every figure disagree in a way that looks like an accounting bug.

How pricing works

Scopes, the * default, and LCR.

Prepaid credit

Blocks, reservations and the ledger.