Billing: Outlet-Level Entitlement Balance — Design Checkpoint
This document captures findings from the Org domain design session (April 2026) that have direct implications for the Billing domain. The Billing domain is still in the design phase — this serves as a checkpoint for when we revisit the entitlement balance design.
The Finding
Analysis of jodgig_2026_clean (dumped 10 March 2026) revealed that outlet-level credit allocation is not a niche feature — it carries the majority of recent job volume.
Usage Statistics
| Metric | Value |
|---|---|
Locations with job_credit_deduction = 1 (outlet-level deduction enabled) | 681 / 2,315 (29.4%) |
| Companies with at least one enabled location | 80 / 290 (27.6%) |
| All-time completed jobs through outlet-level deduction | 174,173 / 333,835 (52.2%) |
| Last 3 months completed jobs through outlet-level deduction | 13,130 / 17,443 (75.3%) |
| Last 3 months salary volume through outlet-level deduction | $1,292,527 / $1,773,533 (72.9%) |
The trend is growing — from 52% of all-time volume to 75% of recent volume.
Who Uses It
| Client | Enabled Locations | Notes |
|---|---|---|
| NTUC group (all entities combined) | ~420+ | 62% of all enabled locations |
| McDonald's | 63 | 2nd largest user |
| Unity Pharmacy (NTUC) | 50 | NTUC entity |
| Cheers | 45 | 3rd largest, across 2 company records |
| NTUC Foodfare | 20 | NTUC entity |
| NTUC Enterprise | 19 | NTUC entity |
| Dim Sum Pte Ltd | 18 | Full adoption (all outlets enabled) |
| Tung Lok Millennium | 13 | Partial adoption |
| The Robertson House | 10 | Full adoption |
These are the largest and most active clients on the platform.
How the Legacy System Works
In JodGig, credits operate as a two-level pool:
- Company-level pool:
companies.available_credits— the company's main credit balance - Location-level pool:
locations.available_credits— an outlet-specific allocation
A per-location toggle (locations.job_credit_deduction) determines which pool is deducted when a shift is completed at that location:
job_credit_deduction = 0→ deduct from company pooljob_credit_deduction = 1→ deduct from location pool
Credit assignment flow:
- Jod admin assigns credits to a company → increases
company.available_credits - HQ manager allocates credits from company to location → decreases
company.available_credits, increaseslocation.available_credits - Credits are deducted at clock-out time (shift completion), not at job creation
Validation logic (from JodJobValidator):
- Primary check: sufficient credits in the target pool (company or location depending on toggle)
- Aggregate check: if primary fails, sums company + all location pools to check total sufficiency (returns "please reassign credits" soft error)
- Floor check: allows negative balance down to
credit_negative_validation_floor
Current New System Design
The current Billing::EntitlementBalance design has no outlet-level concept:
billing_entitlement_balancesis keyed by(billing_account_id, billing_entitlement_id)Billing::Accountis 1:1 withOrg::Company- All credits live at the company (division) level
- The
org_outletstable currently has transitional credit fields synced from JodGig (noted in DBML as "should be migrated into Billing")
Why This Matters
The enterprise division pattern (child Org::Company records for NTUC divisions) solves division-level budget separation. But within a division, outlets have their own allocations:
NTUC Fairprice Supermarkets (South) → Org::Company (child) → Billing::Account
├── FairPrice Bukit Ho Swee → $X allocated
├── FairPrice Redhill → $Y allocated
├── FairPrice Buona Vista CC → $15,090 available, $85,056 consumed
└── ... 15 more outlets
This is how enterprise clients delegate spending authority: HQ allocates credits to outlets so that outlet/area managers can hire independently without coordinating with HQ for every job.
Design Options to Evaluate
Option A: Outlet allocation as a Billing concern
- Add outlet reference to
Billing::EntitlementBalanceor create a newBilling::OutletAllocationtable - Credits are granted to
Billing::Account(company level) as currently designed - HQ can allocate a portion of company credits to specific outlets
- Reservations at an outlet with allocation deduct from the outlet's sub-pool
- The ledger stays at the account level but records which outlet allocation was used
- Pro: billing is the source of truth for all credit movements
- Con: adds complexity to the ledger/balance system; FIFO lot allocation would need outlet awareness
Option B: Outlet allocation as an Org concern
- New entity
Org::OutletBudget(or similar) — a policy/limit on the Org side - Credits remain entirely in
Billing::EntitlementBalance(company level) - Before allowing a reservation, system checks the outlet's budget/allocation
- Pro: billing stays simple (single account balance, single ledger stream)
- Con: "available credits per outlet" concept lives outside billing; two sources of truth for "can I spend?"
Option C: Hybrid — billing handles the pool, org handles the policy
Billing::EntitlementBalancestays at company levelBilling::OutletAllocationacts as a sub-ledger or allocation record that tracks how much of the company pool is earmarked for each outlet- Reservation checks both: company has enough AND outlet allocation has enough
- Pro: clean separation — billing tracks money, org tracks operational limits
- Con: still adds a new billing entity
Credit Balance Health (as of 10 March 2026)
For locations with job_credit_deduction = 1:
| Metric | Value |
|---|---|
| Locations with positive balance | 191 (28.0%) |
| Locations with zero balance | 405 (59.5%) |
| Locations with negative balance | 85 (12.5%) |
| Net aggregate balance | -$117,949.39 |
| Max balance | $21,313.83 |
| Min balance (most overdrawn) | -$79,927.50 |
59.5% of enabled locations have zero credits, and the aggregate is negative. This suggests the system tolerates significant overdraft, and that credit allocation is loosely managed in practice.
Decision Status
Decided (April 2026). A refined version of Option A was selected: outlet budgets live in the Billing domain as a partitioning layer on top of the existing entitlement balance. The key design properties:
- The main financial ledger (
billing_ledger_entries), company-level balance (billing_entitlement_balances), and FIFO lot system (billing_entitlement_lots) are unchanged - Two new tables:
billing_outlet_budgets(projection) andbilling_outlet_budget_transfers(allocation/deallocation log) billing_invoicesgains optionalorg_outlet_idfor enterprise invoice-to-outlet auto-allocation on postingbilling_ledger_entriesgains optionalorg_outlet_idfor outlet-level SOA reporting- FIFO lot consumption remains company-wide and outlet-unaware — lots serve finance, outlet budgets serve operations
Full specification: models/billing-outlet-budget.md
Data Source
- Database:
jodgig_2026_clean(MySQL) - Dump date: 10 March 2026
- Legacy code:
/jodgig-api/app/Services/CreditService.php,JodJobService.php,JodJobValidator.php - Billing design docs:
/docs/30-49-domains/40-billing/ - Org design docs:
/docs/30-49-domains/33-org/