Migration
This guide covers replacing an existing wasabi_billing installation and optionally importing records from supported billing systems. Keep the public folder name and exports as wasabi_billing; there is no separate resource name to deploy for the replacement.
Back up the full database, existing billing folder, configuration, and inventory definitions. Test a restored copy in staging first. Schema setup and imports write to the database automatically when enabled; there is no interactive migration confirmation, automatic rollback, or write-fenced maintenance mode.
Replace an existing wasabi_billing installationβ
- Schedule maintenance and stop invoice callers and the old billing resource. Keep players out while validating migration so no live payments race the replacement.
- Back up the
wasabi_billingtable and all related player/society balances. - Replace the resource files, retaining the folder name
wasabi_billingand the existing table. - Reapply settings into the new shared/client/server configuration files. Do not copy the old monolithic config over them. Review the current defaults, especially automatic collection, reference prefix, providers, and language selection.
- Before starting, restore every billed society job name to
Config.SocietiesSettings.allowedSocietiesToReceiveInvoices, including societies represented in historical invoices. This list is used to classify rows missing a receiver type. - Leave
Config.AutomaticAddDatabaseTables = trueandConfig.CompatImport = false. The import switch is for other products, not your existingwasabi_billingtable. - Start dependencies and billing, inspect SQL/module errors, and verify records and money paths before reopening the server.
What automatic setup changesβ
Billing retains the table name wasabi_billing and existing invoice data. Standard setup:
- Creates the table if absent.
- Adds missing
receiver_type,payment_method, andinvoicer_identifiercolumns. - Adds missing
idx_status_limitandidx_invoicerindexes. - Attempts to make
idx_referenceunique. - Backfills receiver classification using the configured society receive list.
Fresh-table fields include invoice/party identifiers and names, amount/VAT/total, status, payment method, and date strings. Dates remain string columns; the current deadline parser also accepts an ISO-style T separator. The setup does not generally resize every existing column or repair arbitrary customized schemas.
If any row has receiver_type IS NULL, the backfill first sets null rows to personal, then sets all rows whose receiver identifier is in the society list to society. That second update is not restricted to only the previously null rows. Validate both historical society invoices and any existing explicit classifications before production use.
Duplicate references and verificationβ
When a non-unique idx_reference exists, duplicate reference values prevent conversion to a unique index. The resource logs a warning but does not repair or delete the duplicates for you.
Read-only checks after backup:
SELECT reference_id, COUNT(*) AS records
FROM wasabi_billing
GROUP BY reference_id
HAVING COUNT(*) > 1;
SHOW COLUMNS FROM wasabi_billing;
SHOW INDEX FROM wasabi_billing;
SELECT receiver_identifier, receiver_type, COUNT(*) AS records
FROM wasabi_billing
GROUP BY receiver_identifier, receiver_type;
Reconcile duplicate references deliberately with your integrations and item copies, then restart and confirm idx_reference is actually unique. Do not blindly delete duplicate rows. Startup continues after some setup failures, so verify the table itself rather than trusting a general startup message.
Check representative unpaid, paid, cancelled, overdue, personal, and society records. Existing decimal totals are floored during settlement, while payouts are derived from that same floored total. Historical rows without an issuing employee cannot deliver an employee commission to a missing identifier; do not assume the new column reconstructs missing history.
Replace another billing resourceβ
Three independent concerns must be handled:
- Stop the old system. Remove its startup entry and stop callers during migration.
- Compatibility APIs. Enable the particular adapters needed by your job scripts in
Config.CompatExports. - Historical records. Opt into a supported import with
Config.CompatImport, if required.
The manifest provides okokBilling, esx_billing, and codem-billing independently of Lua switches. Turning a shim off does not remove those aliases or make running the original product alongside billing a supported configuration. Do not remove your separate banking resource.
API compatibilityβ
| Replaced resource | Server setting | Supported surfaces |
|---|---|---|
okokBilling | Config.CompatExports.okokBilling = true | okokBilling:CreateCustomInvoice; client UI events okokBilling:ToggleMyInvoices and okokBilling:ToggleCreateInvoice |
esx_billing | Config.CompatExports.esx_billing = true | esx_billing:sendBill |
codem-billing | Config.CompatExports.codemBilling = true | Server exports createBilling and CheckBilling |
All three server switches default to true. Only these specific surfaces are implemented; test each existing caller rather than assuming complete emulation of the replaced product. New integrations should use native Exports or the wasabi_billing: events.
The shipped Config.CompatExports table is defined only in config/server/config.lua. The two okokBilling client open-event handlers read that table on the client, so they are not enabled by the server default alone. If existing job menus still call those events, add Config.CompatExports = { okokBilling = true } in config/client/config.lua before its final module registration.
Import historical invoicesβ
Set one backend in config/server/config.lua:
Config.CompatImport = 'okokBilling'
-- Alternatively: Config.CompatImport = 'esx_billing'
The import is scheduled about five seconds after initialization. It reads the selected source table in the same database, inserts mapped rows into wasabi_billing, and adds/updates a marker column called wasabi_imported in the source table. This is not a read-only operation on the source table.
| Setting | Source table | Reference mapping |
|---|---|---|
'okokBilling' | okokbilling | okok- plus ref_id (or id when no ref_id) |
'esx_billing' | billing | esx- plus the source id |
false | None | Import disabled, the default |
There is no CodeM database importer. The CodeM export shim does not migrate its stored records.
okokBilling mappingβ
The importer expects the documented source columns, including receiver/author identifiers and names, society, society_name, item, invoice_value, fees_amount, status, notes, and dates.
invoice_valuebecomes the pre-VAT amount andfees_amountthe VAT portion; current configured VAT is not applied again.- A nonempty society value becomes the issuer identifier; otherwise the author identifier is used. The stored author identifier is also copied as
invoicer_identifier. - Unlike the compatibility event adapter, this importer does not strip
society_from the stored society value. Check source society names against your banking account IDs before importing. - Receiver type is personal.
itembecomes the reason, with nonblanknotesappended; the resulting reason is truncated to 200 characters.- Only
paidandunpaidstatuses are retained verbatim. Other source statuses becomeunpaid; review these carefully before enabling collection. - Sent, deadline, and paid dates are copied. The reference prefix changes to the import mapping above, so old paper references or external links may need updating.
esx_billing mappingβ
The importer expects id, identifier, sender, target_type, target, label, and amount.
- The receiver is the character in
identifierand receiver type is personal. - A society target uses
targetwithsociety_removed as the issuing society; otherwisesenderis the author.senderis also copied intoinvoicer_identifier. labelbecomes the reason, truncated to 200 characters.- Source amount becomes the total with zero VAT.
- All imported rows are unpaid because standard ESX billing removes paid records.
- Sent date is the import time; deadline is that time plus
Config.MaxDaysToPayInvoice. Historical original dates cannot be reconstructed.
Import safeguards and limitationsβ
- Rows already present under the mapped reference are skipped; positive amounts are floored and nonpositive amounts are skipped. Imported VAT is floored and clamped at zero; total is recomputed as amount plus VAT.
- Source rows with
wasabi_imported = 0are considered. After a completed pass, remaining unmarked source rows are marked processed, including skipped rows. Do not assume a skip means the original row was imported correctly. - The source table is not dropped or emptied, but it is altered and marked. Keep the backup and source table until reconciliation is complete.
- Import inserts do not use the normal creation side effects, so they do not issue item copies or send creation notifications.
- This is not an atomic all-or-nothing migration or a schema-fingerprint validator. SQL errors can leave a partial import. Existing mapped references help avoid repeating completed inserts, but that is not an automatic rollback guarantee.
- Keep automatic collection disabled during reconciliation, especially when source statuses or deadlines change meaning.
After importβ
- Review the console's imported/skipped counts and any SQL errors.
- Compare source records with mapped references in
wasabi_billing, including statuses, totals, society identifiers, dates, and skipped records. - Test lookups, item behavior for newly created invoices, permissions, and payments against real provider accounts on staging.
- Set
Config.CompatImport = falseand restart after the import is verified. Leave the API compatibility switches enabled only where needed by existing callers. - Retain backups. Delete an old source table only as a separate, deliberate database-maintenance decision after verification; billing never drops it for you.
Rollback planningβ
There is no built-in rollback command. If verification fails, stop billing and invoice callers. Restore the matching resource/configuration and database backup under maintenance. If live payments have occurred since the backup, reconcile player and society balances as well as invoice rows before restoring service - rolling back invoice status alone can cause invoices to be collected twice.