docsresourcesQb Mechanicjob

QB-MechanicJob - Vehicle Repair & Tuning System

The qb-mechanicjob resource provides a comprehensive vehicle repair and tuning system for QBCore servers, featuring mechanic shops, vehicle customization, repair services, and towing operations.

Overview

QB-MechanicJob creates a realistic automotive service experience with vehicle repairs, custom tuning, paint jobs, performance modifications, and complete mechanic shop management. Players can operate as mechanics or customers seeking vehicle services.

Key Features

  • Mechanic Shops: Multiple workshop locations with full service capabilities
  • Vehicle Repairs: Engine, body, and component repair systems
  • Custom Tuning: Performance modifications and upgrades
  • Paint & Liveries: Vehicle customization and visual modifications
  • Towing Service: Flatbed and tow truck operations
  • Parts Inventory: Vehicle parts and supplies management
  • Service Billing: Customer invoicing and payment system
  • Shop Management: Business operations and employee management
  • Vehicle Diagnostics: Damage assessment and repair estimates

Installation

Prerequisites

  • QBCore Framework
  • qb-target (for interaction system)
  • qb-menu (for mechanic menus)
  • qb-input (for service forms)
  • qb-inventory (for vehicle parts)
  • qb-vehiclekeys (for key management)

Installation Steps

  1. Download the Resource
cd resources/[qb]
git clone https://github.com/qbcore-framework/qb-mechanicjob.git
  1. Database Setup
-- Mechanic shop tables
CREATE TABLE IF NOT EXISTS `mechanic_orders` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `citizenid` varchar(50) DEFAULT NULL,
  `mechanic` varchar(50) DEFAULT NULL,
  `vehicle_plate` varchar(50) DEFAULT NULL,
  `service_type` varchar(100) DEFAULT NULL,
  `parts_used` longtext DEFAULT NULL,
  `total_cost` int(11) DEFAULT 0,
  `status` varchar(50) DEFAULT 'pending',
  `created_at` timestamp DEFAULT current_timestamp(),
  PRIMARY KEY (`id`)
);
 
CREATE TABLE IF NOT EXISTS `mechanic_parts` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `shop` varchar(50) DEFAULT NULL,
  `part_name` varchar(100) DEFAULT NULL,
  `quantity` int(11) DEFAULT 0,
  `cost` int(11) DEFAULT 0,
  `updated_at` timestamp DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  PRIMARY KEY (`id`)
);
 
CREATE TABLE IF NOT EXISTS `vehicle_modifications` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `plate` varchar(50) DEFAULT NULL,
  `modifications` longtext DEFAULT NULL,
  `performance_mods` longtext DEFAULT NULL,
  `last_modified` timestamp DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  PRIMARY KEY (`id`),
  UNIQUE KEY `plate` (`plate`)
);
 
CREATE TABLE IF NOT EXISTS `mechanic_invoices` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `citizenid` varchar(50) DEFAULT NULL,
  `shop` varchar(50) DEFAULT NULL,
  `services` longtext DEFAULT NULL,
  `amount` int(11) DEFAULT 0,
  `paid` tinyint(1) DEFAULT 0,
  `date` timestamp DEFAULT current_timestamp(),
  PRIMARY KEY (`id`)
);
  1. Add Items to qb-core/shared/items.lua
-- Vehicle Parts
['engine_part'] = {
    ['name'] = 'engine_part',
    ['label'] = 'Engine Part',
    ['weight'] = 1000,
    ['type'] = 'item',
    ['image'] = 'engine_part.png',
    ['unique'] = false,
    ['useable'] = true,
    ['shouldClose'] = true,
    ['combinable'] = nil,
    ['description'] = 'Engine component for vehicle repairs'
},
['transmission_part'] = {
    ['name'] = 'transmission_part',
    ['label'] = 'Transmission Part',
    ['weight'] = 800,
    ['type'] = 'item',
    ['image'] = 'transmission_part.png',
    ['unique'] = false,
    ['useable'] = true,
    ['shouldClose'] = true,
    ['combinable'] = nil,
    ['description'] = 'Transmission component'
},
['brake_pads'] = {
    ['name'] = 'brake_pads',
    ['label'] = 'Brake Pads',
    ['weight'] = 300,
    ['type'] = 'item',
    ['image'] = 'brake_pads.png',
    ['unique'] = false,
    ['useable'] = true,
    ['shouldClose'] = true,
    ['combinable'] = nil,
    ['description'] = 'Brake pads for vehicle repair'
},
['tire'] = {
    ['name'] = 'tire',
    ['label'] = 'Vehicle Tire',
    ['weight'] = 2000,
    ['type'] = 'item',
    ['image'] = 'tire.png',
    ['unique'] = false,
    ['useable'] = true,
    ['shouldClose'] = true,
    ['combinable'] = nil,
    ['description'] = 'Replacement vehicle tire'
},
['turbo_kit'] = {
    ['name'] = 'turbo_kit',
    ['label'] = 'Turbo Kit',
    ['weight'] = 5000,
    ['type'] = 'item',
    ['image'] = 'turbo_kit.png',
    ['unique'] = false,
    ['useable'] = true,
    ['shouldClose'] = true,
    ['combinable'] = nil,
    ['description'] = 'Performance turbo kit upgrade'
}
  1. Add Job Configuration to qb-core/shared/jobs.lua
['mechanic'] = {
    label = 'Mechanic',
    defaultDuty = true,
    offDutyPay = false,
    grades = {
        ['0'] = {
            name = 'Apprentice',
            payment = 50
        },
        ['1'] = {
            name = 'Mechanic',
            payment = 75
        },
        ['2'] = {
            name = 'Advanced Mechanic',
            payment = 100
        },
        ['3'] = {
            name = 'Shop Supervisor',
            payment = 125,
            isboss = true
        },
        ['4'] = {
            name = 'Shop Manager',
            payment = 150,
            isboss = true
        }
    }
}
  1. Add to server.cfg
ensure qb-mechanicjob

Ensure qb-mechanicjob loads after qb-core, qb-target, and qb-inventory in your server.cfg

Configuration

Basic Configuration

The main configuration file is located at config.lua:

Config = {}
 
-- General Settings
Config.UseTarget = true
Config.RepairCost = {
    body = 1000,
    engine = 1500,
    transmission = 1200,
    brakes = 800,
    suspension = 900
}
 
-- Mechanic Shop Locations
Config.Locations = {
    ["bennys"] = {
        label = "Benny's Original Motor Works",
        coords = vector3(-212.55, -1324.2, 30.89),
        blip = {
            sprite = 72,
            color = 17,
            scale = 0.8
        },
        zones = {
            duty = vector3(-212.55, -1324.2, 30.89),
            stash = vector3(-222.47, -1329.73, 30.89),
            vehicle = vector4(-229.47, -1315.39, 30.89, 270.0),
            lift1 = vector4(-212.0, -1308.0, 31.3, 270.0),
            lift2 = vector4(-212.0, -1318.0, 31.3, 270.0)
        }
    },
    ["hayes"] = {
        label = "Hayes Autos",
        coords = vector3(-1419.24, -446.58, 35.91),
        blip = {
            sprite = 72,
            color = 17,
            scale = 0.8
        },
        zones = {
            duty = vector3(-1419.24, -446.58, 35.91),
            stash = vector3(-1413.35, -448.26, 35.91),
            vehicle = vector4(-1428.46, -455.12, 35.91, 120.0),
            lift1 = vector4(-1419.0, -450.0, 36.0, 120.0)
        }
    }
}
 
-- Tow Truck Spawn Points
Config.TowSpawns = {
    [1] = vector4(-229.47, -1315.39, 30.89, 270.0),
    [2] = vector4(-228.47, -1320.39, 30.89, 270.0)
}
 
-- Vehicle Modification Costs
Config.ModificationCosts = {
    engine = {
        [0] = 0,
        [1] = 5000,
        [2] = 10000,
        [3] = 15000,
        [4] = 25000
    },
    brakes = {
        [0] = 0,
        [1] = 2500,
        [2] = 5000,
        [3] = 7500
    },
    transmission = {
        [0] = 0,
        [1] = 4000,
        [2] = 8000,
        [3] = 12000
    },
    suspension = {
        [0] = 0,
        [1] = 3000,
        [2] = 6000,
        [3] = 9000,
        [4] = 12000
    },
    turbo = 15000,
    armor = {
        [0] = 0,
        [1] = 5000,
        [2] = 10000,
        [3] = 15000,
        [4] = 20000,
        [5] = 25000
    }
}

Vehicle Damage System

-- Damage Thresholds
Config.DamageSystem = {
    engine = {
        threshold = 650.0,
        repairCost = 1500,
        parts = {"engine_part"}
    },
    body = {
        threshold = 800.0,
        repairCost = 1000,
        parts = {"body_panel"}
    },
    transmission = {
        threshold = 700.0,
        repairCost = 1200,
        parts = {"transmission_part"}
    },
    brakes = {
        threshold = 750.0,
        repairCost = 800,
        parts = {"brake_pads"}
    },
    tires = {
        threshold = 900.0,
        repairCost = 400,
        parts = {"tire"}
    }
}
 
-- Repair Requirements
Config.RepairRequirements = {
    mechanic_level = {
        basic = 0,      -- Apprentice can do basic repairs
        advanced = 2,   -- Advanced Mechanic for complex repairs
        custom = 3      -- Supervisor+ for custom modifications
    },
    tools_required = {
        basic = {"wrench", "screwdriver"},
        advanced = {"diagnostic_tool", "hydraulic_jack"},
        custom = {"tuning_laptop", "dyno_access"}
    }
}

Parts and Pricing

-- Vehicle Parts Configuration
Config.Parts = {
    ["engine_part"] = {
        label = "Engine Component",
        cost = 500,
        category = "engine",
        required_skill = 1
    },
    ["transmission_part"] = {
        label = "Transmission Component",
        cost = 400,
        category = "transmission",
        required_skill = 1
    },
    ["brake_pads"] = {
        label = "Brake Pads",
        cost = 200,
        category = "brakes",
        required_skill = 0
    },
    ["tire"] = {
        label = "Performance Tire",
        cost = 300,
        category = "tires",
        required_skill = 0
    },
    ["turbo_kit"] = {
        label = "Turbo Kit",
        cost = 2500,
        category = "performance",
        required_skill = 3
    }
}
 
-- Service Pricing
Config.ServicePrices = {
    oil_change = 150,
    tire_repair = 100,
    brake_service = 300,
    engine_tune = 500,
    full_service = 800,
    custom_paint = 1200,
    performance_tune = 2000
}

API Reference

Client Exports

RepairVehicle

Repair a vehicle with specified components.

-- Repair specific component
exports['qb-mechanicjob']:RepairVehicle(vehicle, "engine")
 
-- Repair multiple components
exports['qb-mechanicjob']:RepairVehicle(vehicle, {"engine", "body", "brakes"})
 
-- Full repair
exports['qb-mechanicjob']:RepairVehicle(vehicle, "all")
 
-- Parameters:
-- vehicle: Vehicle entity
-- components: String or table of components to repair

GetVehicleDamage

Get detailed damage information for a vehicle.

local damageInfo = exports['qb-mechanicjob']:GetVehicleDamage(vehicle)
 
-- Returns:
-- {
--   engine = number (0-1000),
--   body = number (0-1000),
--   transmission = number (0-1000),
--   brakes = number (0-1000),
--   tires = table of tire conditions
-- }

ModifyVehicle

Apply performance modifications to a vehicle.

-- Apply single modification
exports['qb-mechanicjob']:ModifyVehicle(vehicle, "engine", 2)
 
-- Apply multiple modifications
exports['qb-mechanicjob']:ModifyVehicle(vehicle, {
    engine = 2,
    brakes = 1,
    suspension = 1,
    turbo = true
})
 
-- Parameters:
-- vehicle: Vehicle entity
-- modifications: Modification type and level

Server Exports

CreateServiceOrder

Create a new service order for a customer.

local orderId = exports['qb-mechanicjob']:CreateServiceOrder(data)
 
-- Parameters:
-- data: {
--   citizenid = string,
--   mechanic = string,
--   vehicle_plate = string,
--   services = table,
--   total_cost = number
-- }

ProcessPayment

Process payment for mechanic services.

local success = exports['qb-mechanicjob']:ProcessPayment(source, amount, description)
 
-- Parameters:
-- source: Player server ID
-- amount: Payment amount
-- description: Service description

UpdatePartInventory

Update shop part inventory.

exports['qb-mechanicjob']:UpdatePartInventory(shop, partName, quantity)
 
-- Parameters:
-- shop: Shop identifier
-- partName: Part name
-- quantity: Quantity change (+/-)

GetShopInventory

Get current shop inventory.

local inventory = exports['qb-mechanicjob']:GetShopInventory(shop)
 
-- Returns table of parts and quantities

Events

Client Events

-- Vehicle repair started
RegisterNetEvent('qb-mechanicjob:client:repairStarted', function(repairType)
    -- Handle repair start
end)
 
-- Vehicle modification applied
RegisterNetEvent('qb-mechanicjob:client:modificationApplied', function(modType, level)
    -- Handle modification
end)
 
-- Service order created
RegisterNetEvent('qb-mechanicjob:client:orderCreated', function(orderId)
    -- Handle new order
end)
 
-- Payment processed
RegisterNetEvent('qb-mechanicjob:client:paymentProcessed', function(amount)
    -- Handle payment completion
end)

Server Events

-- Mechanic on/off duty
RegisterNetEvent('qb-mechanicjob:server:dutyToggle', function(onDuty)
    -- Handle duty status change
end)
 
-- Vehicle towed
RegisterNetEvent('qb-mechanicjob:server:vehicleTowed', function(plate, location)
    -- Handle vehicle towing
end)
 
-- Parts ordered
RegisterNetEvent('qb-mechanicjob:server:orderParts', function(parts, shop)
    -- Handle parts ordering
end)
 
-- Service completed
RegisterNetEvent('qb-mechanicjob:server:serviceCompleted', function(orderId, services)
    -- Handle service completion
end)

Usage Examples

Vehicle Repair System

-- Client-side repair process
RegisterNetEvent('qb-mechanicjob:client:startRepair', function(repairType)
    local player = QBCore.Functions.GetPlayerData()
    if player.job.name == "mechanic" and player.job.onduty then
        local vehicle = QBCore.Functions.GetClosestVehicle()
        local requiredParts = Config.DamageSystem[repairType].parts
        
        -- Check if mechanic has required parts
        for i = 1, #requiredParts do
            local hasPart = QBCore.Functions.HasItem(requiredParts[i])
            if not hasPart then
                QBCore.Functions.Notify("Missing required part: " .. requiredParts[i], "error")
                return
            end
        end
        
        -- Start repair animation and progress
        QBCore.Functions.Progressbar("vehicle_repair", "Repairing " .. repairType .. "...", 10000, false, true, {
            disableMovement = true,
            disableCarMovement = true,
            disableMouse = false,
            disableCombat = true,
        }, {
            animDict = "mini@repair",
            anim = "fixing_a_ped",
        }, {
            model = "prop_tool_box_04",
            bone = 57005,
            coords = vector3(0.4, 0.0, 0.0),
            rotation = vector3(0.0, 0.0, 0.0),
        }, {}, function()
            -- Repair completed
            TriggerServerEvent('qb-mechanicjob:server:repairVehicle', VehToNet(vehicle), repairType)
        end)
    end
end)
 
-- Server-side repair handler
RegisterNetEvent('qb-mechanicjob:server:repairVehicle', function(netId, repairType)
    local src = source
    local player = QBCore.Functions.GetPlayer(src)
    local vehicle = NetworkGetEntityFromNetworkId(netId)
    
    if player.PlayerData.job.name == "mechanic" then
        local repairCost = Config.RepairCost[repairType] or 500
        local requiredParts = Config.DamageSystem[repairType].parts
        
        -- Remove required parts from inventory
        for i = 1, #requiredParts do
            player.Functions.RemoveItem(requiredParts[i], 1)
        end
        
        -- Apply repair to vehicle
        if repairType == "engine" then
            SetVehicleEngineHealth(vehicle, 1000.0)
        elseif repairType == "body" then
            SetVehicleBodyHealth(vehicle, 1000.0)
        elseif repairType == "brakes" then
            SetVehicleBrake(vehicle, false)
        end
        
        TriggerClientEvent('QBCore:Notify', src, repairType .. ' repaired successfully!', 'success')
    end
end)

Vehicle Modification System

-- Performance tuning interface
RegisterNetEvent('qb-mechanicjob:client:openTuningMenu', function()
    local vehicle = QBCore.Functions.GetClosestVehicle()
    if not vehicle then return end
    
    local tuningMenu = {
        {
            header = "Vehicle Tuning",
            isMenuHeader = true
        },
        {
            header = "Engine Upgrade",
            txt = "Improve engine performance",
            params = {
                event = "qb-mechanicjob:client:upgradeEngine",
                args = {vehicle = vehicle}
            }
        },
        {
            header = "Brake System",
            txt = "Upgrade brake system",
            params = {
                event = "qb-mechanicjob:client:upgradeBrakes",
                args = {vehicle = vehicle}
            }
        },
        {
            header = "Suspension",
            txt = "Modify suspension",
            params = {
                event = "qb-mechanicjob:client:upgradeSuspension",
                args = {vehicle = vehicle}
            }
        },
        {
            header = "Turbo Kit",
            txt = "Install turbo kit - $" .. Config.ModificationCosts.turbo,
            params = {
                event = "qb-mechanicjob:client:installTurbo",
                args = {vehicle = vehicle}
            }
        }
    }
    
    exports['qb-menu']:openMenu(tuningMenu)
end)
 
-- Engine upgrade implementation
RegisterNetEvent('qb-mechanicjob:client:upgradeEngine', function(data)
    local vehicle = data.vehicle
    local currentLevel = GetVehicleMod(vehicle, 11) -- Engine mod
    local nextLevel = currentLevel + 1
    
    if nextLevel <= 4 then
        local cost = Config.ModificationCosts.engine[nextLevel]
        
        QBCore.Functions.TriggerCallback('qb-mechanicjob:server:canAfford', function(canAfford)
            if canAfford then
                SetVehicleMod(vehicle, 11, nextLevel, false)
                TriggerServerEvent('qb-mechanicjob:server:payForMod', cost, 'Engine Upgrade Level ' .. nextLevel)
            else
                QBCore.Functions.Notify("Insufficient funds for upgrade", "error")
            end
        end, cost)
    else
        QBCore.Functions.Notify("Engine already at maximum level", "error")
    end
end)

Towing Service

-- Tow truck operations
RegisterNetEvent('qb-mechanicjob:client:towVehicle', function()
    local player = QBCore.Functions.GetPlayerData()
    if player.job.name == "mechanic" and player.job.onduty then
        local towTruck = GetVehiclePedIsIn(PlayerPedId(), false)
        local towModel = GetEntityModel(towTruck)
        
        -- Check if player is in a tow truck
        if towModel == GetHashKey("flatbed") or towModel == GetHashKey("towtruck") then
            local targetVehicle = QBCore.Functions.GetClosestVehicle()
            if targetVehicle then
                QBCore.Functions.Progressbar("towing_vehicle", "Attaching vehicle to tow truck...", 8000, false, true, {
                    disableMovement = true,
                    disableCarMovement = true,
                    disableMouse = false,
                    disableCombat = true,
                }, {
                    animDict = "mini@repair",
                    anim = "fixing_a_ped",
                }, {}, {}, function()
                    AttachEntityToEntity(targetVehicle, towTruck, 0, 0.0, -2.5, 1.0, 0.0, 0.0, 0.0, false, false, false, false, 2, true)
                    QBCore.Functions.Notify("Vehicle attached to tow truck", "success")
                end)
            else
                QBCore.Functions.Notify("No vehicle nearby to tow", "error")
            end
        else
            QBCore.Functions.Notify("You need to be in a tow truck", "error")
        end
    end
end)

Troubleshooting

Common Issues

Vehicle Repairs Not Working

-- Check vehicle entity validity
if not DoesEntityExist(vehicle) then
    print("Vehicle entity does not exist")
    return
end
 
-- Verify repair permissions
local player = QBCore.Functions.GetPlayerData()
if player.job.name ~= "mechanic" or not player.job.onduty then
    print("Player not authorized for repairs")
    return
end

Modification Costs Not Updating

  • Verify modification cost configuration in config.lua
  • Check if vehicle model supports specific modifications
  • Ensure proper database table structure for tracking modifications

Parts Inventory Issues

-- Debug parts inventory
RegisterCommand('checkparts', function()
    local inventory = exports['qb-mechanicjob']:GetShopInventory('bennys')
    for part, quantity in pairs(inventory) do
        print(part .. ": " .. quantity)
    end
end)

Debug Commands

-- Repair vehicle instantly (admin only)
/fixveh
 
-- Add mechanic parts to inventory
/givepart [part_name] [quantity]
 
-- Check vehicle modification levels
/checkmods
 
-- Toggle mechanic duty
/mechduty

Performance Optimization

  1. Damage Calculations: Optimize vehicle damage checking frequency
  2. Part Inventory: Cache inventory data to reduce database queries
  3. Vehicle Spawning: Implement proper vehicle cleanup and pooling
⚠️

Always test vehicle modifications and repairs thoroughly to ensure they work correctly with your server’s vehicle system.