Skip to main content

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

MetricValue
Locations with job_credit_deduction = 1 (outlet-level deduction enabled)681 / 2,315 (29.4%)
Companies with at least one enabled location80 / 290 (27.6%)
All-time completed jobs through outlet-level deduction174,173 / 333,835 (52.2%)
Last 3 months completed jobs through outlet-level deduction13,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

ClientEnabled LocationsNotes
NTUC group (all entities combined)~420+62% of all enabled locations
McDonald's632nd largest user
Unity Pharmacy (NTUC)50NTUC entity
Cheers453rd largest, across 2 company records
NTUC Foodfare20NTUC entity
NTUC Enterprise19NTUC entity
Dim Sum Pte Ltd18Full adoption (all outlets enabled)
Tung Lok Millennium13Partial adoption
The Robertson House10Full 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:

  1. Company-level pool: companies.available_credits — the company's main credit balance
  2. 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 pool
  • job_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, increases location.available_credits
  • Credits are deducted at clock-out time (shift completion), not at job creation

Validation logic (from JodJobValidator):

  1. Primary check: sufficient credits in the target pool (company or location depending on toggle)
  2. Aggregate check: if primary fails, sums company + all location pools to check total sufficiency (returns "please reassign credits" soft error)
  3. 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_balances is keyed by (billing_account_id, billing_entitlement_id)
  • Billing::Account is 1:1 with Org::Company
  • All credits live at the company (division) level
  • The org_outlets table 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::EntitlementBalance or create a new Billing::OutletAllocation table
  • 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::EntitlementBalance stays at company level
  • Billing::OutletAllocation acts 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:

MetricValue
Locations with positive balance191 (28.0%)
Locations with zero balance405 (59.5%)
Locations with negative balance85 (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) and billing_outlet_budget_transfers (allocation/deallocation log)
  • billing_invoices gains optional org_outlet_id for enterprise invoice-to-outlet auto-allocation on posting
  • billing_ledger_entries gains optional org_outlet_id for 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/