Packages

Economy - bank, society, billing and shops on one ledger

Economy - bank, society, billing and shops on one ledger

Four resources that share one ledger: a bank, a boss office, invoicing and player-owned shops. Every currency movement in all four goes through one bank export and one stored procedure, where the affordability check and the debit are a single statement inside one transaction; a refusal is written to the journal instead of being swallowed.

What is in the box

jr_banking

  • Personal accounts: one per character. Deposit, withdraw and transfer are ledger calls, not a Lua read of the balance followed by a second write.
  • Organization accounts: created for jobs and gangs listed in config.lua. The current boss is granted owner; ordinary employees are not added automatically.
  • Account permissions: owner, manager and member. Managers have per-call and calendar-day caps; members can view and deposit, not withdraw or transfer.
  • Account freezes: /jrbank_freeze and the freezeAccount export, both gated by ACE. A frozen account blocks deposit, withdraw, transfer and role changes; viewing still works.
  • Append-only journal: every SQL-path success and refusal is a row in jr_transactions. Refusals keep a machine denied_code.
  • Access lifetime: leaving the job, losing boss or changing job revokes the auto-owner row. Manual manager and member grants last only while that citizen still belongs to the job or gang.
  • Branches and ATMs: configured points. Branches include role management; ATMs do deposit, withdraw and transfer only.

jr_society

  • Boss office: one menu per job or gang listed in config, opened only by the framework isboss grade. The resource has no roles of its own.
  • Staff actions: hire, fire and set grade on online players. Offline employees are listed read-only; the resource does not write the framework players table behind its back.
  • Grade wages: salary per grade per pay period, stored in jr_society_grades, bounded by config.lua.
  • Period payroll: the automatic thread and the manual button share one UTC period key, so they cannot name two windows at once.
  • Repeat-safe pay: each employee is paid under payroll:<org>:<period>:<citizenid>. Running the same period again moves no money.
  • All-or-nothing run: a cashbox that cannot cover the whole roster pays nobody, not the first names in list order. Offline employees are never paid.
  • Bonuses and cashbox: one-off bonus, deposit and withdraw go through jr_banking. A bonus is not a period: two presses pay twice.

jr_billing

  • Organization invoices: jobs and gangs listed in config issue bills. There is no player-to-player invoice without an organization.
  • Issue templates: ready-made amounts, or a typed amount still capped by config, with a due time.
  • Automatic collection: a server thread collects overdue invoices. A console pass uses the same keys as the timer, so it cannot double-charge a scheduled tick.
  • Frozen payers: a frozen bank account cannot be charged, by the player or by autopay.
  • Fee and tax split: optional employee share and treasury tax, taken from what the organization just received, not from a second debit of the payer.
  • One payment key: invoice:<id>:pay is unique for the life of the invoice. Paying a paid invoice moves nothing.
  • Cancel, decline, grace: the issuer or a boss can cancel; the payer can decline; after the grace window neither the payer nor autopay may settle.

jr_shops

  • Player-owned shops: a character or an organization owns each shop. The till is a jr_banking account.
  • Own shelf: stock lives in jr_shop_stock, not an inventory stash. Taking stock is UPDATE … WHERE qty >= ? inside jr_shop_buy / jr_shop_sell.
  • Shop clerks: hire staff, optional stock rights, clerk share of each sale (0–50 percent), paid from the till under its own key.
  • Player buy-back: a shop can buy items from players when buy-back is on and the item has a buy price.
  • Held orders: if the inventory cannot take the goods after payment, the order waits at the counter. It is not refunded.
  • Frozen trade: freezing the till in jr_banking, or the shop itself, stops trade without a second money path.
  • Integrity check: stock + handed out + given back == supplied + bought from players, printable from the server console.

Correctness

  • One procedure: every currency movement in jr_banking is a single CALL (jr_ledger_deposit, jr_ledger_withdraw, jr_ledger_transfer). The affordability check and the debit are one UPDATE … WHERE balance >= ? AND is_frozen = 0 joined to access rights, inside one transaction.
  • Zero-row refusal: not an exception in the console. The same transaction writes the journal row with a denied_code.
  • Deterministic keys: payroll, invoices and shop orders reuse a stable op_id. jr_transactions.op_id is UNIQUE, so a replay returns the first result and does not move money again.
  • Payroll idempotency: repeating a period — timer, button, restart, or two bosses at once — pays nobody twice.
  • Frozen accounts: cannot be deposited to, withdrawn from, transferred from or onto, or charged by billing, including autopay.
  • Shelf then till: jr_shop_buy / jr_shop_sell move the shelf and the order row in one transaction. Money is a jr_banking transfer under a unique key; a bank refusal puts the stock back and writes no currency.
  • No unguarded fallback: society, billing and shops refuse with bank_outdated if jr_banking does not export transferAs. They do not fall back to an unguarded payment.
  • Whole-period payroll: a cashbox that cannot cover everyone pays no one a part. Paid op ids are already spent, so a half-run cannot be finished later.

Tests

  • 244 unit tests: Lua 5.4 suites against mocks, shipped with the four resources. They run without FXServer.
  • Live MariaDB: integration suites in tests/integration/ call the real stored procedures, including parallel workers against one account or one last stock unit.
  • Live FXServer: tests/live/ on a headless server. Cases that need a connected player are reported SKIP, not PASS.
  • Outside escrow: tests/*.lua and tests/**/* are in escrow_ignore on every resource. The test files sit in the package, unencrypted, so you can run them.

Requirements

  • Target framework: Qbox (qbx_core) or QBCore (qb-core), auto-detected. Qbox wins if both are started. ESX is not supported.
  • Required resources: oxmysql and ox_lib on all four. ox_inventory for jr_shops only. jr_society, jr_billing and jr_shops require jr_banking 1.0.2 or newer (transferAs).
  • Database engine: MariaDB 10.5.2+ or MySQL 8.0.16+ (non-negative CHECK on balances). Stored procedures must be installed; Lua does not fall back to inline UPDATE.
  • Lua runtime: Lua 5.4 (lua54 'yes'), fx_version 'cerulean', game 'gta5'.
  • FiveM build: Legacy. Enhanced is not claimed.
  • Editable files: config.lua, locales (en, ru), SQL and tests are listed in escrow_ignore.

Support

  • Support email: hello@joyride.games
  • Response target: 72 hours for a complete report.
$69.00