diff --git a/client/main.lua b/client/main.lua index 54a1ba3..2a7916f 100644 --- a/client/main.lua +++ b/client/main.lua @@ -87,7 +87,7 @@ local function showFinancedVehiclesMenu() if vehicles == nil or #vehicles == 0 then return exports.qbx_core:Notify(locale('error.nofinanced'), 'error') end for _, v in pairs(vehicles) do - if v.balance ~= 0 then + if v.balance and v.balance > 0 then local name = VEHICLES[v.vehicle].name local plate = v.plate:upper() ownedVehicles[#ownedVehicles + 1] = { @@ -152,14 +152,18 @@ end ---@param targetShowroomVehicle integer vehicleName ---@param buyVehicle string model local function openFinance(targetShowroomVehicle, buyVehicle) - local dialog = lib.inputDialog(VEHICLES[buyVehicle].name:upper()..' '..buyVehicle:upper()..' - $'..getVehPrice(targetShowroomVehicle), { + local dialog = lib.inputDialog(VEHICLES[buyVehicle].brand:upper()..' '..VEHICLES[buyVehicle].name:upper()..' - $'..getVehPrice(targetShowroomVehicle), { { type = 'number', label = locale('menus.financesubmit_downpayment')..sharedConfig.finance.minimumDown..'%', + min = VEHICLES[buyVehicle].price * sharedConfig.finance.minimumDown / 100, + max = VEHICLES[buyVehicle].price }, { type = 'number', label = locale('menus.financesubmit_totalpayment')..sharedConfig.finance.maximumPayments, + min = 2, + max = sharedConfig.finance.maximumPayments } }) diff --git a/fxmanifest.lua b/fxmanifest.lua index 4073aed..976da56 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -29,6 +29,8 @@ files { 'locales/*.json' } +dependency 'qbx_vehicles' + provide 'qb-vehicleshop' lua54 'yes' -use_experimental_fxv2_oal 'yes' +use_experimental_fxv2_oal 'yes' \ No newline at end of file diff --git a/migrate.sql b/migrate.sql new file mode 100644 index 0000000..8018e1b --- /dev/null +++ b/migrate.sql @@ -0,0 +1,20 @@ +CREATE TABLE IF NOT EXISTS `vehicle_financing` ( + `vehicleId` int(11) NOT NULL, + `balance` int(11) DEFAULT NULL, + `paymentamount` int(11) DEFAULT NULL, + `paymentsleft` tinyint(4) DEFAULT NULL, + `financetime` int(11) DEFAULT NULL, + PRIMARY KEY (`vehicleId`), + FOREIGN KEY `vehicleId` (`vehicleId`) REFERENCES `player_vehicles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +INSERT INTO vehicle_financing (vehicleId, balance, paymentamount, paymentsleft, financetime) +SELECT id, balance, paymentamount, paymentsleft, financetime +FROM player_vehicles +WHERE balance > 0 OR paymentamount > 0 OR paymentsleft > 0 OR financetime > 0; + +ALTER TABLE player_vehicles +DROP COLUMN balance, +DROP COLUMN paymentamount, +DROP COLUMN paymentsleft, +DROP COLUMN financetime; diff --git a/server/main.lua b/server/main.lua index 287c887..79aec26 100644 --- a/server/main.lua +++ b/server/main.lua @@ -16,12 +16,12 @@ RegisterNetEvent('qbx_vehicleshop:server:removePlayer', function(citizenid) if not financeTimer[citizenid] then return end local playTime = financeTimer[citizenid] - local financetime = FetchVehicleEntitiesByCitizenId(citizenid) - for _, v in pairs(financetime) do + local vehicles = FetchFinancedVehicleEntitiesByCitizenId(citizenid) + for _, v in pairs(vehicles) do if v.balance >= 1 then local newTime = math.floor(v.financetime - (((os.time() - playTime) / 1000) / 60)) if newTime < 0 then newTime = 0 end - UpdateVehicleEntityFinanceTime(newTime, v.plate) + UpdateVehicleEntityFinanceTime(newTime, v.vehicleId) end end financeTimer[citizenid] = nil @@ -32,14 +32,14 @@ AddEventHandler('playerDropped', function() local src = source local license = GetPlayerIdentifierByType(src, 'license2') or GetPlayerIdentifierByType(src, 'license') if not license then return end - local vehicles = FetchVehicleEntitiesByLicense(license) + local vehicles = FetchFinancedVehicleEntitiesByLicense(license) if not vehicles then return end for _, v in pairs(vehicles) do local playTime = financeTimer[v.citizenid] if v.balance >= 1 and playTime then local newTime = math.floor(v.financetime - (((os.time() - playTime) / 1000) / 60)) if newTime < 0 then newTime = 0 end - UpdateVehicleEntityFinanceTime(newTime, v.plate) + UpdateVehicleEntityFinanceTime(newTime, v.vehicleId) end end if vehicles[1] and financeTimer[vehicles[1].citizenid] then @@ -94,6 +94,13 @@ lib.callback.register('qbx_vehicleshop:server:GetVehiclesByName', function(sourc local player = exports.qbx_core:GetPlayer(src) if not player then return end local vehicles = FetchVehicleEntitiesByCitizenId(player.PlayerData.citizenid) + local financeVehicles = FetchFinancedVehicleEntitiesByCitizenId(player.PlayerData.citizenid) + for _, v in pairs(financeVehicles) do + vehicles[v.vehicleId].balance = v.balance + vehicles[v.vehicleId].paymentamount = v.paymentamount + vehicles[v.vehicleId].paymentsleft = v.paymentsleft + vehicles[v.vehicleId].financetime = v.financetime + end if vehicles[1] then return vehicles end @@ -237,11 +244,9 @@ RegisterNetEvent('qbx_vehicleshop:server:buyShowroomVehicle', function(vehicle) return end - local cid = player.PlayerData.citizenid local plate = generatePlate() - InsertVehicleEntity({ - license = player.PlayerData.license, - citizenId = cid, + exports.qbx_vehicles:CreateVehicleEntity({ + citizenId = player.PlayerData.citizenid, model = vehicle, plate = plate, }) @@ -282,7 +287,6 @@ RegisterNetEvent('qbx_vehicleshop:server:financeVehicle', function(downPayment, InsertVehicleEntityWithFinance({ insertVehicleEntityRequest = { - license = player.PlayerData.license, citizenId = cid, model = vehicle, plate = plate, @@ -343,8 +347,7 @@ RegisterNetEvent('qbx_vehicleshop:server:sellShowroomVehicle', function(data, pl if not sellShowroomVehicleTransact(src, target, vehiclePrice, vehiclePrice) then return end - InsertVehicleEntity({ - license = target.PlayerData.license, + exports.qbx_vehicles:CreateVehicleEntity({ citizenId = cid, model = vehicle, plate = plate @@ -390,7 +393,6 @@ RegisterNetEvent('qbx_vehicleshop:server:sellfinanceVehicle', function(downPayme InsertVehicleEntityWithFinance({ insertVehicleEntityRequest = { - license = target.PlayerData.license, citizenId = cid, model = vehicle, plate = plate, @@ -418,8 +420,8 @@ RegisterNetEvent('qbx_vehicleshop:server:checkFinance', function() local vehicles = FetchFinancedVehicleEntitiesByCitizenId(player.PlayerData.citizenid) for _, v in pairs(vehicles) do local plate = v.plate - DeleteVehicleEntity(plate) - --MySQL.update('UPDATE player_vehicles SET citizenid = ? WHERE plate = ?', {'REPO-'..v.citizenid, plate}) -- Use this if you don't want them to be deleted + DeleteVehicleEntity(v.id) + --MySQL.update('UPDATE player_vehicles SET citizenid = ? WHERE id = ?', {'REPO-'..v.citizenid, v.id}) -- Use this if you don't want them to be deleted exports.qbx_core:Notify(src, locale('error.repossessed', plate), 'error') end end) @@ -457,8 +459,11 @@ lib.addCommand('transfervehicle', {help = locale('general.command_transfervehicl local player = exports.qbx_core:GetPlayer(src) local target = exports.qbx_core:GetPlayer(buyerId) local row = FetchVehicleEntityByPlate(plate) - if config.finance.preventSelling and row.balance > 0 then - return exports.qbx_core:Notify(src, locale('error.financed'), 'error') + if config.finance.preventSelling then + local financeRow = FetchFinancedVehicleEntityById(row.id) + if financeRow.balance > 0 then + return exports.qbx_core:Notify(src, locale('error.financed'), 'error') + end end if row.citizenid ~= player.PlayerData.citizenid then return exports.qbx_core:Notify(src, locale('error.notown'), 'error') @@ -491,11 +496,11 @@ lib.addCommand('transfervehicle', {help = locale('general.command_transfervehicl player.Functions.AddMoney(currencyType, sellAmount) target.Functions.RemoveMoney(currencyType, sellAmount) end - UpdateVehicleEntityOwner(targetcid, targetlicense, plate) + UpdateVehicleEntityOwner(targetcid, targetlicense, row.id) TriggerClientEvent('vehiclekeys:client:SetOwner', buyerId, plate) local sellerMessage = sellAmount > 0 and locale('success.soldfor') .. lib.math.groupdigits(sellAmount) or locale('success.gifted') local buyerMessage = sellAmount > 0 and locale('success.boughtfor') .. lib.math.groupdigits(sellAmount) or locale('success.received_gift') exports.qbx_core:Notify(src, sellerMessage, 'success') exports.qbx_core:Notify(buyerId, buyerMessage, 'success') end, GetEntityModel(vehicle), sellAmount) -end) +end) \ No newline at end of file diff --git a/server/storage.lua b/server/storage.lua index 38b2785..7dceafc 100644 --- a/server/storage.lua +++ b/server/storage.lua @@ -1,23 +1,8 @@ ---@class InsertVehicleEntityRequest ----@field license string ---@field citizenId string ---@field model string ---@field plate string ----@param request InsertVehicleEntityRequest -function InsertVehicleEntity(request) - MySQL.insert('INSERT INTO player_vehicles (license, citizenid, vehicle, hash, mods, plate, garage, state) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', { - request.license, - request.citizenId, - request.model, - joaat(request.model), - '{}', - request.plate, - 'pillboxgarage', - 0 - }) -end - ---@class VehicleFinanceServer ---@field balance number ---@field payment number @@ -30,15 +15,13 @@ end ---@param request InsertVehicleEntityWithFinanceRequest function InsertVehicleEntityWithFinance(request) - MySQL.insert('INSERT INTO player_vehicles (license, citizenid, vehicle, hash, mods, plate, garage, state, balance, paymentamount, paymentsleft, financetime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', { - request.insertVehicleEntityRequest.license, - request.insertVehicleEntityRequest.citizenId, - request.insertVehicleEntityRequest.model, - joaat(request.insertVehicleEntityRequest.model), - '{}', - request.insertVehicleEntityRequest.plate, - 'pillboxgarage', - 0, + local vehicleId = exports.qbx_vehicles:CreateVehicleEntity({ + citizenId = request.insertVehicleEntityRequest.citizenId, + model = request.insertVehicleEntityRequest.model, + plate = request.insertVehicleEntityRequest.plate + }) + MySQL.insert('INSERT INTO vehicle_financing (vehicleId, balance, paymentamount, paymentsleft, financetime) VALUES (?, ?, ?, ?, ?)', { + vehicleId, request.vehicleFinance.balance, request.vehicleFinance.payment, request.vehicleFinance.paymentsLeft, @@ -48,18 +31,19 @@ end ---@alias VehicleEntity table +---@class VehicleFinancingEntity +---@field vehicleId integer +---@field balance number +---@field paymentamount number +---@field paymentsleft integer +---@field financetime number + ---@param citizenId string ---@return VehicleEntity[] function FetchVehicleEntitiesByCitizenId(citizenId) return MySQL.query.await('SELECT * FROM player_vehicles WHERE citizenid = ?', {citizenId}) end ----@param license string ----@return VehicleEntity[] -function FetchVehicleEntitiesByLicense(license) - return MySQL.query.await('SELECT * FROM player_vehicles WHERE license = ?', {license}) -end - ---@param plate string ---@return VehicleEntity function FetchVehicleEntityByPlate(plate) @@ -74,15 +58,15 @@ function DoesVehicleEntityExist(plate) end ---@param time number ----@param plate string -function UpdateVehicleEntityFinanceTime(time, plate) - MySQL.update('UPDATE player_vehicles SET financetime = ? WHERE plate = ?', {time, plate}) +---@param vehicleId integer +function UpdateVehicleEntityFinanceTime(time, vehicleId) + MySQL.update('UPDATE vehicle_financing SET financetime = ? WHERE vehicleId = ?', {time, vehicleId}) end ---@param vehicleFinance VehicleFinanceServer ---@param plate string function UpdateVehicleFinance(vehicleFinance, plate) - MySQL.update('UPDATE player_vehicles SET balance = ?, paymentamount = ?, paymentsleft = ?, financetime = ? WHERE plate = ?', { + MySQL.update('UPDATE vehicle_financing AS vf INNER JOIN player_vehicles AS pv ON vf.vehicleId = pv.id SET vf.balance = ?, vf.paymentamount = ?, vf.paymentsleft = ?, vf.financetime = ? WHERE pv.plate = ?', { vehicleFinance.balance, vehicleFinance.payment, vehicleFinance.paymentsLeft, @@ -93,18 +77,30 @@ end ---@param citizenId string ---@param license string ----@param plate string -function UpdateVehicleEntityOwner(citizenId, license, plate) - MySQL.update('UPDATE player_vehicles SET citizenid = ?, license = ? WHERE plate = ?', {citizenId, license, plate}) +---@param vehicleId integer +function UpdateVehicleEntityOwner(citizenId, license, vehicleId) + MySQL.update('UPDATE player_vehicles SET citizenid = ?, license = ? WHERE id = ?', {citizenId, license, vehicleId}) +end + +---@param id integer +---@return VehicleFinancingEntity +function FetchFinancedVehicleEntityById(id) + return MySQL.single.await('SELECT * FROM vehicle_financing WHERE vehicleId = ? AND balance > 0 AND financetime < 1', {id}) end ---@param citizenId string ----@return VehicleEntity[] +---@return VehicleFinancingEntity function FetchFinancedVehicleEntitiesByCitizenId(citizenId) - return MySQL.query.await('SELECT * FROM player_vehicles WHERE citizenid = ? AND balance > 0 AND financetime < 1', {citizenId}) + return MySQL.query.await('SELECT vehicle_financing.* FROM vehicle_financing INNER JOIN player_vehicles ON player_vehicles.citizenid = ? WHERE vehicle_financing.vehicleId = player_vehicles.id AND vehicle_financing.balance > 0 AND vehicle_financing.financetime > 1', {citizenId}) end ----@param plate string -function DeleteVehicleEntity(plate) - MySQL.query('DELETE FROM player_vehicles WHERE plate = ?', {plate}) +---@param license string +---@return VehicleFinancingEntity +function FetchFinancedVehicleEntitiesByLicense(license) + return MySQL.query.await('SELECT vf.*, p.citizenid FROM vehicle_financing AS vf INNER JOIN players AS p ON p.citizenid = ? INNER JOIN player_vehicles AS pv ON pv.citizenid = p.citizenid AND vf.balance > 0 AND vf.financetime < 1', {license}) +end + +---@param vehicleId integer +function DeleteVehicleEntity(vehicleId) + exports.qbx_vehicles:DeleteEntityById(vehicleId) end diff --git a/vehshop.sql b/vehshop.sql index 7e8f6eb..9aba9b7 100644 --- a/vehshop.sql +++ b/vehshop.sql @@ -1,38 +1,9 @@ -CREATE TABLE IF NOT EXISTS `player_vehicles` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `license` varchar(50) DEFAULT NULL, - `citizenid` varchar(50) DEFAULT NULL, - `vehicle` varchar(50) DEFAULT NULL, - `hash` varchar(50) DEFAULT NULL, - `mods` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, - `plate` varchar(15) NOT NULL, - `fakeplate` varchar(50) DEFAULT NULL, - `garage` varchar(50) DEFAULT 'pillboxgarage', - `fuel` int(11) DEFAULT 100, - `engine` float DEFAULT 1000, - `body` float DEFAULT 1000, - `state` int(11) DEFAULT 1, - `depotprice` int(11) NOT NULL DEFAULT 0, - `drivingdistance` int(50) DEFAULT NULL, - `status` text DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `plate` (`plate`), - KEY `citizenid` (`citizenid`), - KEY `license` (`license`) -) ENGINE=InnoDB AUTO_INCREMENT=1; - -ALTER TABLE `player_vehicles` -ADD UNIQUE INDEX UK_playervehicles_plate (plate); - -ALTER TABLE `player_vehicles` -ADD CONSTRAINT FK_playervehicles_players FOREIGN KEY (citizenid) -REFERENCES `players` (citizenid) ON DELETE CASCADE ON UPDATE CASCADE; - -ALTER TABLE `player_vehicles` -ADD COLUMN `balance` int(11) NOT NULL DEFAULT 0; -ALTER TABLE `player_vehicles` -ADD COLUMN `paymentamount` int(11) NOT NULL DEFAULT 0; -ALTER TABLE `player_vehicles` -ADD COLUMN `paymentsleft` int(11) NOT NULL DEFAULT 0; -ALTER TABLE `player_vehicles` -ADD COLUMN `financetime` int(11) NOT NULL DEFAULT 0; +CREATE TABLE IF NOT EXISTS `vehicle_financing` ( + `vehicleId` int(11) NOT NULL, + `balance` int(11) DEFAULT NULL, + `paymentamount` int(11) DEFAULT NULL, + `paymentsleft` int(11) DEFAULT NULL, + `financetime` int(11) DEFAULT NULL, + PRIMARY KEY (`vehicleId`), + FOREIGN KEY `vehicleId` (`vehicleId`) REFERENCES `player_vehicles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; \ No newline at end of file