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.
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.