Runs on: shared. This file stays editable when the resource is escrow-protected. Restart the resource after editing.
Full View
Preview File Updated: 1.1.0 - 05.11.2026config/forms.lua
Config = Config or {}
-- ═════════════════════════════════════════════
-- Paper forms — tickets, invoices, receipts and vehicle sale agreements.
-- Players write them in game with inventory items. Switch them with
-- Config.Features.billingForms and Config.Features.vehicleSale.
--
-- The tables use the same keys as vms_cityhall (Config.BillingItems,
-- Config.AllowedJobBillings, Config.BillingPayments, Config.VehicleSaleAgreement),
-- so an existing vms config can be pasted in.
-- All items must exist in your inventory: see docs → Paper Forms.
-- ═════════════════════════════════════════════
-- ─────────────────────────────────────────────
-- General
-- distance the receiver / buyer must stand this close
-- showRealNames false: the receiver list shows "Citizen 1", "Citizen 2" …
-- so players don't learn a stranger's name from the form
-- allowSelf also list yourself (for testing alone; keep false on live servers)
-- issueCooldown seconds between two forms from the same player
-- maxRows most lines on an invoice / receipt
-- maxQuantity highest quantity on one line
-- maxPenaltyPoints most points one traffic ticket can add
-- ─────────────────────────────────────────────
Config.Forms = {
distance = 5.0,
showRealNames = false,
allowSelf = false,
issueCooldown = 3,
maxRows = 15,
maxQuantity = 999,
maxPenaltyPoints = 12,
}
-- ─────────────────────────────────────────────
-- Items per form
-- requiredItem must be carried, not used up (e.g. the payment terminal)
-- emptyItemName the blank form (nil = none needed)
-- removeOnIssue use up the blank form when the bill is issued
-- itemName the filled form the receiver gets
-- addForReceiver give the receiver that item
-- payWithItem the receiver can pay by using the item, anywhere
-- (false = only on a City Hall PC)
-- 'ticket' covers both ticket and traffic-ticket.
-- With neither requiredItem nor emptyItemName the form can only be opened
-- with exports['aps_cityhall']:openBillingForm(source, 'ticket').
-- ─────────────────────────────────────────────
Config.BillingItems = {
['ticket'] = {
emptyItemName = 'empty_ticket',
removeOnIssue = true,
itemName = 'ticket',
addForReceiver = true,
payWithItem = false,
},
['invoice'] = {
emptyItemName = 'empty_invoice',
removeOnIssue = true,
itemName = 'invoice',
addForReceiver = true,
payWithItem = false,
},
['receipt'] = {
requiredItem = 'payment_terminal',
emptyItemName = 'thermal_paper',
removeOnIssue = true,
itemName = 'receipt',
addForReceiver = true,
payWithItem = true,
},
}
-- ─────────────────────────────────────────────
-- Payment by using the item (payWithItem above)
-- ─────────────────────────────────────────────
Config.BillingPayments = {
withItem = { ['cash'] = false, ['bank'] = true },
}
-- ─────────────────────────────────────────────
-- Jobs that can write forms
-- label shown on the form
-- society account that receives the society share
-- percentageForSociety share of a paid bill for the society
-- percentageForIssuer share for the player who wrote it (online or offline)
-- types forms this job can write:
-- 'traffic-ticket', 'ticket', 'invoice', 'receipt'
-- amountLimits lowest / highest amount per form, before tax
-- dueDates choices in days (-1 = no due date)
-- taxFromInvoice / taxFromReceipt percent added on top. With
-- Config.Features.taxes it goes to the treasury
-- allowLicenseRevocation / licenseRevocationList licenses a traffic ticket
-- can take: City Hall id (or Config.Compat.licenseAliases
-- name) = label
-- allowLicenseSuspension / licenseSuspensionDays suspension choices in days
-- allowPenaltyPoints traffic tickets can add penalty points
-- ─────────────────────────────────────────────
Config.AllowedJobBillings = {
['police'] = {
label = 'LSPD',
society = 'society_police',
percentageForSociety = 85,
percentageForIssuer = 15,
types = { ['traffic-ticket'] = true, ['ticket'] = true },
amountLimits = {
['traffic-ticket'] = { min = 1, max = 12000 },
['ticket'] = { min = 1, max = 50000 },
},
dueDates = { -1, 1, 2, 3, 4, 5, 6, 7, 14, 30 },
allowLicenseRevocation = true,
licenseRevocationList = {
['motorcycle'] = 'Motorcycle',
['car'] = 'Car',
['truck'] = 'Truck',
},
allowLicenseSuspension = true,
licenseSuspensionDays = { 3, 4, 5, 6, 7, 14 },
allowPenaltyPoints = true,
},
['ambulance'] = {
label = 'EMS',
society = 'society_ambulance',
percentageForSociety = 90,
percentageForIssuer = 10,
types = { ['invoice'] = true },
amountLimits = { ['invoice'] = { min = 1, max = 100000 } },
dueDates = { -1, 1, 2, 3, 4, 5, 6, 7, 14, 30 },
taxFromInvoice = 4.5,
},
['mechanic'] = {
label = 'MECHANIC',
society = 'society_mechanic',
percentageForSociety = 90,
percentageForIssuer = 10,
types = { ['receipt'] = true },
amountLimits = { ['receipt'] = { min = 1, max = 10000 } },
taxFromReceipt = 2.3,
},
}
-- ─────────────────────────────────────────────
-- Supplies: sold in the Document Office app (tab "Forms & Supplies")
-- name / label / description item, and the text in the shop
-- price taken from Config.MoneyAccount (0 = free)
-- jobs only these jobs can buy it (nil = everyone)
-- ─────────────────────────────────────────────
Config.EmptyPapers = {
{ name = 'vehicle_sale_agreement', label = 'Sale Agreement', description = 'Vehicle purchase and sale agreement', price = 0 },
{ name = 'empty_ticket', label = 'Ticket Pad Sheet', description = 'Blank ticket', price = 0, jobs = { police = true } },
{ name = 'empty_invoice', label = 'Blank Invoice', description = 'Blank invoice', price = 0, jobs = { ambulance = true } },
{ name = 'thermal_paper', label = 'Thermal Paper', description = 'Paper for the payment terminal', price = 5, jobs = { mechanic = true } },
{ name = 'payment_terminal', label = 'Payment Terminal', description = 'Card terminal for receipts', price = 500, jobs = { mechanic = true } },
}
-- ─────────────────────────────────────────────
-- Vehicle sale agreement (Config.Features.vehicleSale)
-- UseOnItem open the agreement by using ItemName
-- ItemName blank agreement, used up when both parties signed
-- SignedItemName copy of the signed agreement
-- GiveSignedItemToSeller / GiveSignedItemToBuyer
-- UseOnCommand also open it with /CommandName (no item needed)
-- PaymentMethods 'cash', 'bank'
-- MinPrice / MaxPrice allowed sale price
-- OfferSeconds how long the buyer has to sign
-- BlacklistVehicles model names that can't be sold
-- AllowSellForCompany the buyer can also sign for their company: the vehicle
-- then gets the company in CompanyColumn (needs that
-- column, vms_garagesv2 adds it). The buyer pays
-- CompaniesList job → grades that may buy, and allowed models
-- (model names, or vms style [GetHashKey('taxi')] = true)
-- Business, job, company and gang vehicles can't be sold.
-- With vms_garagesv2: set its Config.UseSaleAgreement = false.
-- ─────────────────────────────────────────────
Config.VehicleSaleAgreement = {
UseOnItem = true,
ItemName = 'vehicle_sale_agreement',
SignedItemName = 'signed_vehicle_sale_agreement',
GiveSignedItemToSeller = true,
GiveSignedItemToBuyer = true,
UseOnCommand = false,
CommandName = 'sellvehicle',
PaymentMethods = { 'cash', 'bank' },
MinPrice = 1,
MaxPrice = 100000000,
OfferSeconds = 120,
BlacklistVehicles = { 'rhino', 'police', 'police2', 'police3' },
AllowSellForCompany = GetResourceState('vms_garagesv2') ~= 'missing',
CompanyColumn = 'company',
CompaniesList = {
['ambulance'] = { grades = { 5, 6 }, allowedModels = { 'ambulance' } },
['taxi'] = { grades = { 3, 4 }, allowedModels = { 'taxi', 'baller' } },
},
}
-- ─────────────────────────────────────────────
-- Company billings overview: every bill of the player's job, for jobs in
-- Config.AllowedJobBillings (nil command = off)
-- command / description / key chat command, its help text, optional key
-- fullAccessGrade this grade and up see the whole company, like bosses
-- (nil = bosses only). Everybody else sees their own bills
-- historyDays how far back paid and cancelled bills are listed
-- limit most bills listed
-- showPaid / showCancelled
-- ─────────────────────────────────────────────
Config.CompanyBillings = {
command = 'billings',
description = 'Company billings',
key = nil,
fullAccessGrade = nil,
historyDays = 60,
limit = 200,
showPaid = true,
showCancelled = false,
}
-- ─────────────────────────────────────────────
-- Prop and animation while a form is open (false = none)
-- paper tickets, invoices, agreements
-- terminal receipts. The model is the free payment terminal by BzZz
-- (https://forum.cfx.re/u/bzzzi), included in stream/ (THIRD_PARTY.md).
-- `fallback` is used if the model isn't available
-- ─────────────────────────────────────────────
Config.FormProps = {
paper = {
dict = 'missheistdockssetup1clipboard@base', anim = 'base',
prop = 'p_amb_clipboard_01', bone = 36029,
offset = vector3(0.16, 0.08, 0.1), rotation = vector3(-130.0, -50.0, 0.0),
},
terminal = {
dict = 'cellphone@', anim = 'cellphone_text_read_base',
prop = 'bzzz_prop_payment_terminal', bone = 57005,
offset = vector3(0.18, 0.01, 0.0), rotation = vector3(-54.0, 220.0, 43.0),
fallback = {
prop = 'prop_cs_hand_radio', bone = 28422,
offset = vector3(0.0, 0.0, 0.0), rotation = vector3(0.0, 0.0, 0.0),
},
},
}
