diff --git a/resources/[tools]/nordi_billing/billing_client.lua b/resources/[tools]/nordi_billing/billing_client.lua index ff0574eaf..b4585121c 100644 --- a/resources/[tools]/nordi_billing/billing_client.lua +++ b/resources/[tools]/nordi_billing/billing_client.lua @@ -168,8 +168,8 @@ function ShowBillingForm(selectedPlayer, accountOptions) timestamp = GetGameTimer() } - -- Show waiting screen - ShowWaitingScreen(billData.billId, selectedPlayer.name) + -- Show waiting screen with all details + ShowWaitingScreen(billData.billId, selectedPlayer.name, input[1], input[2]) else lib.notify({ title = 'Billing System', @@ -183,14 +183,27 @@ function ShowBillingForm(selectedPlayer, accountOptions) end -- Function to show waiting screen -function ShowWaitingScreen(billId, playerName) +function ShowWaitingScreen(billId, playerName, amount, reason) + -- Create a more informative waiting screen lib.registerContext({ id = 'bill_waiting_screen', - title = 'Warte auf Antwort', + title = 'Warte auf Zahlung', options = { { - title = 'Rechnung gesendet', - description = 'Warte auf Antwort von ' .. playerName, + title = 'Rechnung Details', + description = 'Gesendet an: ' .. playerName, + metadata = { + {label = 'Betrag', value = '$' .. amount}, + {label = 'Grund', value = reason}, + {label = 'Status', value = 'Warte auf Antwort...'}, + }, + icon = 'file-invoice-dollar', + disabled = true + }, + { + title = 'Warte auf Antwort', + progress = 100, -- This creates a loading bar + description = playerName .. ' entscheidet über die Rechnung', icon = 'clock', disabled = true }, @@ -207,6 +220,73 @@ function ShowWaitingScreen(billId, playerName) }) lib.showContext('bill_waiting_screen') + + -- Start a loading animation + Citizen.CreateThread(function() + local dots = 0 + local loadingText = {'Warte', 'Warte.', 'Warte..', 'Warte...'} + local progress = 100 + + while pendingBills[billId] do + Citizen.Wait(500) + dots = (dots + 1) % 4 + + -- Update the context menu with new loading text + lib.registerContext({ + id = 'bill_waiting_screen', + title = 'Warte auf Zahlung', + options = { + { + title = 'Rechnung Details', + description = 'Gesendet an: ' .. playerName, + metadata = { + {label = 'Betrag', value = '$' .. amount}, + {label = 'Grund', value = reason}, + {label = 'Status', value = 'Warte auf Antwort...'}, + }, + icon = 'file-invoice-dollar', + disabled = true + }, + { + title = loadingText[dots + 1], + progress = progress, + description = playerName .. ' entscheidet über die Rechnung', + icon = 'clock', + disabled = true + }, + { + title = 'Abbrechen', + description = 'Zurück zum Hauptmenü', + icon = 'times-circle', + onSelect = function() + pendingBills[billId] = nil + OpenMainBillingMenu() + end + } + } + }) + + -- Only refresh the context if it's still showing + if isMenuOpen('bill_waiting_screen') then + lib.showContext('bill_waiting_screen') + else + break + end + + -- Cycle the progress bar + progress = progress - 5 + if progress <= 0 then + progress = 100 + end + end + end) +end + +-- Helper function to check if a menu is open +function isMenuOpen(menuId) + -- This is a placeholder - ox_lib doesn't provide a direct way to check + -- You might need to track this yourself or use a different approach + return true end -- Function to show payment account selection @@ -585,19 +665,46 @@ RegisterNetEvent('billing:client:billResponse', function(billId, action, playerN lib.notify({ title = 'Rechnung bezahlt', description = playerName .. ' hat deine Rechnung über $' .. bill.amount .. ' bezahlt', - type = 'success' + type = 'success', + duration = 7000 + }) + + -- Show a more detailed success message + lib.alertDialog({ + header = 'Zahlung erhalten', + content = playerName .. ' hat deine Rechnung über $' .. bill.amount .. ' für "' .. bill.reason .. '" bezahlt.', + centered = true, + cancel = false }) elseif action == 'decline' then lib.notify({ title = 'Rechnung abgelehnt', description = playerName .. ' hat deine Rechnung über $' .. bill.amount .. ' abgelehnt', - type = 'error' + type = 'error', + duration = 7000 + }) + + -- Show a more detailed decline message + lib.alertDialog({ + header = 'Rechnung abgelehnt', + content = playerName .. ' hat deine Rechnung über $' .. bill.amount .. ' für "' .. bill.reason .. '" abgelehnt.', + centered = true, + cancel = false }) elseif action == 'later' then lib.notify({ title = 'Rechnung gespeichert', description = playerName .. ' wird deine Rechnung später bezahlen', - type = 'info' + type = 'info', + duration = 7000 + }) + + -- Show a more detailed "pay later" message + lib.alertDialog({ + header = 'Zahlung aufgeschoben', + content = playerName .. ' hat deine Rechnung über $' .. bill.amount .. ' für "' .. bill.reason .. '" für später gespeichert.', + centered = true, + cancel = false }) end @@ -640,5 +747,3 @@ Citizen.CreateThread(function() end end end) - - diff --git a/resources/[tools]/nordi_billing/billing_server.lua b/resources/[tools]/nordi_billing/billing_server.lua index 37f533395..462dd57ae 100644 --- a/resources/[tools]/nordi_billing/billing_server.lua +++ b/resources/[tools]/nordi_billing/billing_server.lua @@ -8,7 +8,7 @@ lib.callback.register('billing:server:sendBill', function(source, data) local target = QBCore.Functions.GetPlayer(data.playerId) if not player or not target then - return false + return { success = false } end local senderName = player.PlayerData.charinfo.firstname .. ' ' .. player.PlayerData.charinfo.lastname @@ -29,6 +29,10 @@ lib.callback.register('billing:server:sendBill', function(source, data) local billId = result[1] and result[1].id or nil + if not billId then + return { success = false } + end + -- 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, @@ -39,6 +43,13 @@ lib.callback.register('billing:server:sendBill', function(source, data) data.amount }) + -- Store sender information for notifications + MySQL.insert.await('INSERT INTO billing_senders (bill_id, sender_id, sender_source) VALUES (?, ?, ?)', { + billId, + player.PlayerData.citizenid, + src + }) + -- Send payment prompt to target player TriggerClientEvent('billing:client:showPaymentPrompt', data.playerId, { billId = billId, @@ -50,7 +61,11 @@ lib.callback.register('billing:server:sendBill', function(source, data) -- Notify the sender TriggerClientEvent('QBCore:Notify', src, 'Rechnung über $' .. data.amount .. ' an ' .. target.PlayerData.charinfo.firstname .. ' gesendet', 'success') - return true + -- Return success with bill ID + return { + success = true, + billId = billId + } end) -- Add a callback to get bill status @@ -86,7 +101,7 @@ lib.callback.register('billing:server:getAllBillStatuses', function(source) return {} end) --- Add a new callback for handling bill responses +-- Add a callback for handling bill responses lib.callback.register('billing:server:handleBillResponse', function(source, data) local src = source local player = QBCore.Functions.GetPlayer(src) @@ -348,6 +363,17 @@ MySQL.ready(function() print("^2[nordi_billing] Created billing_accounts table^7") end + -- Create billing_senders table for tracking bill senders + MySQL.query.await([[ + CREATE TABLE IF NOT EXISTS billing_senders ( + id INT AUTO_INCREMENT PRIMARY KEY, + bill_id INT, + sender_id VARCHAR(50), + sender_source INT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + ]]) + -- Create offline_payments table if it doesn't exist MySQL.query.await([[ CREATE TABLE IF NOT EXISTS offline_payments ( @@ -360,9 +386,31 @@ MySQL.ready(function() ) ]]) print("^2[nordi_billing] Database tables initialized^7") -end) -- Added closing parenthesis here - +end) +-- Event to notify bill sender +RegisterNetEvent('billing:server:notifyBillSender', function(billId, action) + local src = source + local player = QBCore.Functions.GetPlayer(src) + + if not player then return end + + -- Get sender information + local senderInfo = MySQL.query.await('SELECT * FROM billing_senders WHERE bill_id = ?', {billId}) + if not senderInfo or #senderInfo == 0 then return end + + local senderSource = senderInfo[1].sender_source + + -- Get bill information + local billInfo = MySQL.query.await('SELECT * FROM billing_accounts WHERE bill_id = ?', {billId}) + if not billInfo or #billInfo == 0 then return end + + local amount = billInfo[1].amount + local playerName = player.PlayerData.charinfo.firstname .. ' ' .. player.PlayerData.charinfo.lastname + + -- Notify the sender + TriggerClientEvent('billing:client:billResponse', senderSource, billId, action, playerName) +end) -- Handle offline payments when a player logs in RegisterNetEvent('QBCore:Server:PlayerLoaded', function() @@ -418,99 +466,3 @@ QBCore.Commands.Add('bills', 'Zeige deine unbezahlten Rechnungen an', {}, false, -- This will trigger the client to open the bills menu TriggerClientEvent('billing:client:openBillsMenu', source) end) - --- Event handler for opening bills menu from command -RegisterNetEvent('billing:client:openBillsMenu', function() - -- This is handled on the client side -end) - --- Add a server event to notify bill sender when a bill is paid -RegisterServerEvent('billing:server:notifyBillPaid', function(senderId, amount, receiverName) - local sender = QBCore.Functions.GetPlayerByCitizenId(senderId) - if sender then - TriggerClientEvent('QBCore:Notify', sender.PlayerData.source, receiverName .. ' hat deine Rechnung über $' .. amount .. ' bezahlt', 'success') - end -end) - --- Add a server event to notify bill sender when a bill is declined -RegisterServerEvent('billing:server:notifyBillDeclined', function(senderId, amount, receiverName) - local sender = QBCore.Functions.GetPlayerByCitizenId(senderId) - if sender then - TriggerClientEvent('QBCore:Notify', sender.PlayerData.source, receiverName .. ' hat deine Rechnung über $' .. amount .. ' abgelehnt', 'error') - end -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 based on selected account - if data.accountId == 'personal' then - -- Pay from personal bank account - local billResult = MySQL.query.await('SELECT * FROM ps_banking_bills WHERE id = ?', {data.billId}) - if not billResult or #billResult == 0 then return false end - - local bill = billResult[1] - local amount = tonumber(bill.amount) - - -- Check if player has enough money in their bank account - if player.PlayerData.money["bank"] < amount then - return false - end - - -- Process payment manually instead of using callback to avoid issues - player.Functions.RemoveMoney("bank", amount, "bill-payment") - - -- IMPORTANT: Delete the bill from ps_banking_bills table - MySQL.query.await('DELETE FROM ps_banking_bills WHERE id = ?', {data.billId}) - - -- Process the payment to the recipient's account - ProcessBillPayment(data.billId) - - -- Notify the player - TriggerClientEvent('QBCore:Notify', src, 'Du hast die Rechnung über $' .. amount .. ' bezahlt', 'success') - - return true - else - -- Pay from shared account - local success = PayBillFromSharedAccount(src, data.billId, data.accountId) - return success - 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}) - - -- IMPORTANT: Delete the bill from ps_banking_bills table when declined - MySQL.query.await('DELETE FROM ps_banking_bills WHERE 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 - - -- Notify the player - TriggerClientEvent('QBCore:Notify', src, 'Du hast die Rechnung abgelehnt', 'info') - - return true - elseif data.action == 'later' then - -- Simply close the prompt without any action - -- The bill will remain in the system as unpaid - - -- Notify the player - TriggerClientEvent('QBCore:Notify', src, 'Die Rechnung wurde für später gespeichert', 'info') - - return true - end - - return false -end)