Railbase
← Marketplace

Budgeting

A live spend gate over your budget lines — reserve, release and cancel money against a line before it leaves the company, for finance controllers and cost-centre owners.

v0.1.3 · needs core ≥ 0.9.0 · release history
$99/ tenant
per month
Internal testingAvailable only through an active Cockpit testing assignment.

Problems it solves

  • The budget sits in a spreadsheet and the spend sits in the accounting system, so a line is only shown as overspent at month-end, after the money has gone.
  • Nobody can answer 'how much is left on this line?' — the annual figure is written down, but orders that are approved and not yet invoiced are invisible.
  • A purchase gets approved without anyone checking the line first; the overspend turns up in a variance report when the company is already committed.
  • Money set aside for an order stays set aside forever, because nothing frees it when the order is cancelled or the invoice arrives lower than the order.
  • A cost-centre owner learns they are at 95 percent of their line only when somebody finally opens the report — there is no signal on the way up.

Budgeting is the spend-authority layer for the rest of your Railbase: it puts a hold on a budget line before money is committed, and refuses when the line cannot carry it. It is for finance controllers and cost-centre owners who approve purchases and must show the arithmetic.

It is a data-resident module — nine tenant-scoped collections in your own Vault, and a write path of three verbs under /api/budgeting/commitments/*, each running against the trusted session identity, refusing anonymous callers, and gated on budgeting.use, the permission carried by the bundle's budgeting_operator role.

What you get

  • A gate that refuses. A reservation fails with "insufficient budget available" when the committed amount plus the new amount would pass the line's total. The commitment row and the line's figures are written in one transaction, or neither is.
  • A live projection on the line itself. Committed, actual and available (total minus actual minus committed, committed floored at zero) stay current on the line, so "can we spend this?" is a lookup rather than an aggregation. Amounts are held as integer cents, so the number that decides cannot drift.
  • Holds that survive a replay. A hold is keyed on the source module and source document, so a re-delivered approval finds it rather than committing twice. Release moves the amount from committed into actual, taking an invoice figure that differs from the reserved one; cancel frees the hold without recording spend. Both are no-ops once the hold is closed.
  • Threshold crossings as events. A threshold row carries a percentage and a type. After each change the module compares consumption before and after: a threshold sitting between the two fires once, stamps its trigger time in the same transaction, and re-arms only when consumption drops back below.
  • Three reports off the same numbers. Budget versus actual per line with consumed percent and a portfolio total; available funds with the thinnest lines first; open commitments, optionally narrowed to one source module.

In the rest of the estate

Budgeting reaches procurement only over the core's event bus. ptp.requisition.approved opens a hold against the requisition's line, ptp.purchase_order.cancelled frees it, and ptp.invoice.posted rolls committed into actual — all three keyed on the requisition id, so a cancellation matches what the approval opened. It publishes budgeting.commitment.reserved, .released, .cancelled and budgeting.threshold.crossed; with no procurement module present the listeners idle.

Where it stands today

This iteration is the commitment engine, not the planning tool.

  • It does not install yet. The core checks a bundle against its own surface before accepting it, and this one is still refused — the screens above call endpoints outside the module's namespace, and some declared events cannot be proven from the code. That gate is the reason the module is listed as coming soon rather than sold.
  • Nothing in the module creates a budget, a version, a line, an allocation, a transfer, a driver or a template: those six collections have a shape and no write path. Lines and thresholds must be created by an administrator in the admin data browser before a reservation is possible, and transfers move no money.
  • A threshold's type — warning, soft stop, hard stop — travels in the published event but changes nothing: only the total-amount gate refuses a reservation. notify_users is not read, so a crossing is an event and not a message.
  • The four pages at /budgeting list rows through the core's generic record endpoint. The bundle declares no access rule on its collections, so the core treats them as locked and those tables come back empty; they will fill only once the module serves those lists from its own verbs. The Reserve form is the one control bound to a working endpoint, and it wants a line id typed by hand; no page shows the reports.
  • Reports are JSON only. The default_currency setting is declared but never read: a reservation without an explicit currency is recorded in USD. Release takes the actual amount it is given without comparing it to the reserved figure.

Reviews

No reviews yet — be the first to review Budgeting.

Sign in to leave a review. Reviews are moderated before they appear.