1
0
Fork 0
forked from Simnation/Main
This commit is contained in:
Nordi98 2025-08-05 14:07:51 +02:00
parent 2fe7be05aa
commit 12740ce5d5
2 changed files with 246 additions and 57 deletions

View file

@ -45,7 +45,7 @@ function OpenCreateBillMenu()
if cooldown then if cooldown then
lib.notify({ lib.notify({
title = 'Billing System', title = 'Billing System',
description = 'Please wait before using the billing system again', description = 'Bitte warte, bevor du das Abrechnungssystem erneut verwendest',
type = 'error' type = 'error'
}) })
return return
@ -56,7 +56,7 @@ function OpenCreateBillMenu()
if #players == 0 then if #players == 0 then
lib.notify({ lib.notify({
title = 'Billing System', title = 'Billing System',
description = 'No players nearby to bill', description = 'Keine Spieler in der Nähe zum Abrechnen',
type = 'error' type = 'error'
}) })
return return
@ -69,7 +69,7 @@ function OpenCreateBillMenu()
-- Add default bank account -- Add default bank account
table.insert(accountOptions, { table.insert(accountOptions, {
value = 'personal', value = 'personal',
label = 'Personal Bank Account' label = 'Persönliches Bankkonto'
}) })
-- Add other accounts -- Add other accounts
@ -97,7 +97,7 @@ function OpenCreateBillMenu()
-- Register and show the player selection menu -- Register and show the player selection menu
lib.registerContext({ lib.registerContext({
id = 'nearby_players_menu', id = 'nearby_players_menu',
title = 'Select Player to Bill', title = 'Spieler zum Abrechnen auswählen',
menu = 'main_billing_menu', -- Add back button to main menu menu = 'main_billing_menu', -- Add back button to main menu
options = playerOptions options = playerOptions
}) })
@ -107,11 +107,11 @@ end
-- Function to show the billing form after selecting a player -- Function to show the billing form after selecting a player
function ShowBillingForm(selectedPlayer, accountOptions) function ShowBillingForm(selectedPlayer, accountOptions)
local input = lib.inputDialog('Create Bill for ' .. selectedPlayer.name, { local input = lib.inputDialog('Rechnung erstellen für ' .. selectedPlayer.name, {
{ {
type = 'number', type = 'number',
label = 'Amount ($)', label = 'Betrag ($)',
description = 'Enter the bill amount', description = 'Gib den Rechnungsbetrag ein',
icon = 'dollar-sign', icon = 'dollar-sign',
required = true, required = true,
min = 1, min = 1,
@ -119,14 +119,14 @@ function ShowBillingForm(selectedPlayer, accountOptions)
}, },
{ {
type = 'input', type = 'input',
label = 'Reason', label = 'Grund',
description = 'Enter the reason for the bill', description = 'Gib den Grund für die Rechnung ein',
placeholder = 'Services provided...', placeholder = 'Erbrachte Dienstleistungen...',
required = true required = true
}, },
{ {
type = 'select', type = 'select',
label = 'Receiving Account', label = 'Empfangskonto',
options = accountOptions, options = accountOptions,
required = true required = true
} }
@ -148,7 +148,7 @@ function ShowBillingForm(selectedPlayer, accountOptions)
if success then if success then
lib.notify({ lib.notify({
title = 'Billing System', title = 'Billing System',
description = 'Bill sent successfully to ' .. selectedPlayer.name, description = 'Rechnung erfolgreich an ' .. selectedPlayer.name .. ' gesendet',
type = 'success' type = 'success'
}) })
@ -163,7 +163,7 @@ function ShowBillingForm(selectedPlayer, accountOptions)
else else
lib.notify({ lib.notify({
title = 'Billing System', title = 'Billing System',
description = 'Failed to send bill', description = 'Fehler beim Senden der Rechnung',
type = 'error' type = 'error'
}) })
end end
@ -176,7 +176,7 @@ function ViewBills()
if not bills or #bills == 0 then if not bills or #bills == 0 then
lib.notify({ lib.notify({
title = 'Billing System', title = 'Billing System',
description = 'You have no unpaid bills', description = 'Du hast keine unbezahlten Rechnungen',
type = 'info' type = 'info'
}) })
@ -190,38 +190,52 @@ function ViewBills()
local billOptions = {} local billOptions = {}
for _, bill in ipairs(bills) do for _, bill in ipairs(bills) do
local timestamp = os.date('%Y-%m-%d %H:%M', bill.date) local timestamp = os.date('%Y-%m-%d %H:%M', bill.date)
-- Check if bill is declined in our custom table
local billData = lib.callback.await('billing:server:getBillStatus', false, bill.id)
local isDeclined = billData and billData.declined == 1
local status = 'Unbezahlt'
if bill.isPaid then
status = 'Bezahlt'
elseif isDeclined then
status = 'Abgelehnt'
end
table.insert(billOptions, { table.insert(billOptions, {
title = bill.description, title = bill.description,
description = ('Amount: $%s | Date: %s'):format(bill.amount, timestamp), description = ('Betrag: $%s | Datum: %s'):format(bill.amount, timestamp),
icon = 'file-invoice', icon = 'file-invoice',
metadata = { metadata = {
{label = 'Bill ID', value = bill.id}, {label = 'Rechnungs-ID', value = bill.id},
{label = 'Type', value = bill.type}, {label = 'Typ', value = bill.type},
{label = 'Status', value = bill.isPaid and 'Paid' or 'Unpaid'} {label = 'Status', value = status}
}, },
onSelect = function() onSelect = function()
local confirm = lib.alertDialog({ if not bill.isPaid and not isDeclined then
header = 'Pay Bill', local confirm = lib.alertDialog({
content = ('Do you want to pay $%s for %s?'):format(bill.amount, bill.description), header = 'Rechnung bezahlen',
centered = true, content = ('Möchtest du $%s für %s bezahlen?'):format(bill.amount, bill.description),
cancel = true centered = true,
}) cancel = true
})
if confirm == 'confirm' then if confirm == 'confirm' then
local success = lib.callback.await('ps-banking:server:payBill', false, bill.id) local success = lib.callback.await('ps-banking:server:payBill', false, bill.id)
if success then if success then
lib.notify({ lib.notify({
title = 'Billing System', title = 'Billing System',
description = 'Bill paid successfully', description = 'Rechnung erfolgreich bezahlt',
type = 'success' type = 'success'
}) })
ViewBills() -- Refresh the list ViewBills() -- Refresh the list
else else
lib.notify({ lib.notify({
title = 'Billing System', title = 'Billing System',
description = 'Failed to pay bill. Insufficient funds.', description = 'Fehler beim Bezahlen der Rechnung. Unzureichendes Guthaben.',
type = 'error' type = 'error'
}) })
end
end end
end end
end end
@ -230,7 +244,7 @@ function ViewBills()
lib.registerContext({ lib.registerContext({
id = 'billing_menu', id = 'billing_menu',
title = 'Your Bills', title = 'Deine Rechnungen',
menu = 'main_billing_menu', -- Add back button to main menu menu = 'main_billing_menu', -- Add back button to main menu
options = billOptions options = billOptions
}) })
@ -290,3 +304,98 @@ function PlayBillingAnimation()
ClearPedTasks(playerPed) ClearPedTasks(playerPed)
DeleteEntity(prop) DeleteEntity(prop)
end end
-- Event to show payment prompt when receiving a bill
RegisterNetEvent('billing:client:showPaymentPrompt', function(data)
-- Play a notification sound
PlaySound(-1, "Event_Start_Text", "GTAO_FM_Events_Soundset", 0, 0, 1)
-- Create the payment prompt
lib.registerContext({
id = 'bill_payment_prompt',
title = 'Neue Rechnung erhalten',
options = {
{
title = 'Rechnung Details',
description = 'Von: ' .. data.sender,
metadata = {
{label = 'Betrag', value = '$' .. data.amount},
{label = 'Grund', value = data.reason},
}
},
{
title = 'Bezahlen',
description = 'Rechnung sofort bezahlen',
icon = 'money-bill',
onSelect = function()
local success = lib.callback.await('billing:server:handleBillResponse', false, {
action = 'pay',
billId = data.billId
})
if success then
lib.notify({
title = 'Rechnung bezahlt',
description = 'Du hast die Rechnung erfolgreich bezahlt',
type = 'success'
})
else
lib.notify({
title = 'Zahlung fehlgeschlagen',
description = 'Du hast nicht genug Geld auf deinem Konto',
type = 'error'
})
end
end
},
{
title = 'Ablehnen',
description = 'Rechnung ablehnen',
icon = 'times-circle',
onSelect = function()
local confirm = lib.alertDialog({
header = 'Rechnung ablehnen',
content = 'Bist du sicher, dass du diese Rechnung ablehnen möchtest?',
centered = true,
cancel = true
})
if confirm == 'confirm' then
local success = lib.callback.await('billing:server:handleBillResponse', false, {
action = 'decline',
billId = data.billId
})
if success then
lib.notify({
title = 'Rechnung abgelehnt',
description = 'Du hast die Rechnung abgelehnt',
type = 'info'
})
end
end
end
},
{
title = 'Später bezahlen',
description = 'Rechnung für später speichern',
icon = 'clock',
onSelect = function()
lib.callback.await('billing:server:handleBillResponse', false, {
action = 'later',
billId = data.billId
})
lib.notify({
title = 'Rechnung gespeichert',
description = 'Die Rechnung wurde für später gespeichert',
type = 'info'
})
end
}
}
})
-- Show the payment prompt
lib.showContext('bill_payment_prompt')
end)

View file

@ -1,3 +1,6 @@
-- Initialize QBCore
local QBCore = exports['qb-core']:GetCoreObject()
-- Register callback for sending bills -- Register callback for sending bills
lib.callback.register('billing:server:sendBill', function(source, data) lib.callback.register('billing:server:sendBill', function(source, data)
local src = source local src = source
@ -9,7 +12,7 @@ lib.callback.register('billing:server:sendBill', function(source, data)
end end
local senderName = player.PlayerData.charinfo.firstname .. ' ' .. player.PlayerData.charinfo.lastname local senderName = player.PlayerData.charinfo.firstname .. ' ' .. player.PlayerData.charinfo.lastname
local description = data.reason .. ' (From: ' .. senderName .. ')' local description = data.reason .. ' (Von: ' .. senderName .. ')'
-- Create the bill -- Create the bill
exports["ps-banking"]:createBill({ exports["ps-banking"]:createBill({
@ -19,9 +22,16 @@ lib.callback.register('billing:server:sendBill', function(source, data)
amount = data.amount, amount = data.amount,
}) })
-- Store additional data about the bill if needed -- Get the latest bill ID for this user (assuming it's the one we just created)
-- You could create a separate table to track which account the payment should go to local result = MySQL.query.await('SELECT id FROM ps_banking_bills WHERE identifier = ? ORDER BY id DESC LIMIT 1', {
MySQL.insert.await('INSERT INTO billing_accounts (bill_description, sender_id, receiver_id, account_id, amount, created_at) VALUES (?, ?, ?, ?, ?, NOW())', { target.PlayerData.citizenid
})
local billId = result[1] and result[1].id or nil
-- Store additional data about the bill
MySQL.insert.await('INSERT INTO billing_accounts (bill_id, bill_description, sender_id, receiver_id, account_id, amount, created_at) VALUES (?, ?, ?, ?, ?, ?, NOW())', {
billId,
description, description,
player.PlayerData.citizenid, player.PlayerData.citizenid,
target.PlayerData.citizenid, target.PlayerData.citizenid,
@ -29,18 +39,77 @@ lib.callback.register('billing:server:sendBill', function(source, data)
data.amount data.amount
}) })
-- Notify the target player -- Send payment prompt to target player
TriggerClientEvent('QBCore:Notify', data.playerId, 'You received a bill for $' .. data.amount .. ' - ' .. data.reason, 'primary', 7500) TriggerClientEvent('billing:client:showPaymentPrompt', data.playerId, {
billId = billId,
amount = data.amount,
reason = data.reason,
sender = senderName
})
return true return true
end) end)
-- Event handler for when a bill is paid -- Add a callback to get bill status
RegisterNetEvent('billing:server:billPaid', function(billId) lib.callback.register('billing:server:getBillStatus', function(source, billId)
local src = source local src = source
local player = QBCore.Functions.GetPlayer(src) local player = QBCore.Functions.GetPlayer(src)
if not player then return end if not player then return nil end
local result = MySQL.query.await('SELECT * FROM billing_accounts WHERE bill_id = ?', {billId})
if result and #result > 0 then
return result[1]
end
return nil
end)
-- Add a new callback for handling bill responses
lib.callback.register('billing:server:handleBillResponse', function(source, data)
local src = source
local player = QBCore.Functions.GetPlayer(src)
if not player then return false end
if data.action == 'pay' then
-- Process payment
local success = lib.callback.await('ps-banking:server:payBill', src, data.billId)
if success then
-- Payment successful
return true
else
-- Payment failed (likely insufficient funds)
return false
end
elseif data.action == 'decline' then
-- Mark as declined in our system
MySQL.update.await('UPDATE billing_accounts SET declined = 1 WHERE bill_id = ?', {data.billId})
-- Find the sender to notify them
local billInfo = MySQL.query.await('SELECT * FROM billing_accounts WHERE bill_id = ?', {data.billId})
if billInfo and #billInfo > 0 then
local senderId = billInfo[1].sender_id
local sender = QBCore.Functions.GetPlayerByCitizenId(senderId)
if sender then
TriggerClientEvent('QBCore:Notify', sender.PlayerData.source, 'Deine Rechnung wurde abgelehnt', 'error')
end
end
return true
elseif data.action == 'later' then
-- No action needed, bill remains in system
return true
end
return false
end)
-- Event handler for when a bill is paid
RegisterNetEvent('ps-banking:server:billPaid', function(billId)
local src = source
-- Find the bill in our custom table -- Find the bill in our custom table
local billData = MySQL.query.await('SELECT * FROM billing_accounts WHERE bill_id = ?', {billId}) local billData = MySQL.query.await('SELECT * FROM billing_accounts WHERE bill_id = ?', {billId})
@ -57,13 +126,13 @@ RegisterNetEvent('billing:server:billPaid', function(billId)
if receiver then if receiver then
-- Add money directly to the receiver's bank account -- Add money directly to the receiver's bank account
receiver.Functions.AddMoney('bank', amount, 'bill-payment') receiver.Functions.AddMoney('bank', amount, 'bill-payment')
TriggerClientEvent('QBCore:Notify', receiver.PlayerData.source, 'You received $' .. amount .. ' from a bill payment', 'success') TriggerClientEvent('QBCore:Notify', receiver.PlayerData.source, 'Du hast $' .. amount .. ' von einer Rechnungszahlung erhalten', 'success')
else else
-- Handle offline player (could store in a separate table for when they log in) -- Handle offline player
MySQL.insert.await('INSERT INTO offline_payments (citizen_id, amount, reason) VALUES (?, ?, ?)', { MySQL.insert.await('INSERT INTO offline_payments (citizen_id, amount, reason) VALUES (?, ?, ?)', {
receiverId, receiverId,
amount, amount,
'Bill payment' 'Rechnungszahlung'
}) })
end end
else else
@ -92,6 +161,7 @@ MySQL.ready(function()
amount DECIMAL(10,2), amount DECIMAL(10,2),
created_at DATETIME, created_at DATETIME,
paid TINYINT DEFAULT 0, paid TINYINT DEFAULT 0,
declined TINYINT DEFAULT 0,
paid_at DATETIME paid_at DATETIME
) )
]]) ]])
@ -127,7 +197,7 @@ RegisterNetEvent('QBCore:Server:PlayerLoaded', function()
if totalAmount > 0 then if totalAmount > 0 then
player.Functions.AddMoney('bank', totalAmount, 'offline-bill-payments') player.Functions.AddMoney('bank', totalAmount, 'offline-bill-payments')
TriggerClientEvent('QBCore:Notify', src, 'You received $' .. totalAmount .. ' from bill payments while offline', 'success') TriggerClientEvent('QBCore:Notify', src, 'Du hast $' .. totalAmount .. ' von Rechnungszahlungen erhalten, während du offline warst', 'success')
-- Mark payments as processed -- Mark payments as processed
MySQL.update.await('UPDATE offline_payments SET processed = 1 WHERE citizen_id = ? AND processed = 0', {citizenId}) MySQL.update.await('UPDATE offline_payments SET processed = 1 WHERE citizen_id = ? AND processed = 0', {citizenId})
@ -135,7 +205,17 @@ RegisterNetEvent('QBCore:Server:PlayerLoaded', function()
end end
end) end)
-- Listen for bill payment events from ps-banking -- Alternative hook method if you can't modify ps-banking
RegisterNetEvent('ps-banking:server:billPaid', function(billId) -- This listens for the MySQL query that deletes a bill (which happens when a bill is paid)
TriggerEvent('billing:server:billPaid', billId) AddEventHandler('oxmysql:query', function(query, params)
if string.find(query, "DELETE FROM ps_banking_bills WHERE id = ?") then
-- This is likely a bill payment
local billId = params[1]
if billId then
-- Small delay to ensure the deletion completes
SetTimeout(100, function()
TriggerEvent('ps-banking:server:billPaid', billId)
end)
end
end
end) end)