forked from Simnation/Main
ed
This commit is contained in:
parent
2fe7be05aa
commit
12740ce5d5
2 changed files with 246 additions and 57 deletions
|
@ -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,19 +190,32 @@ 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()
|
||||||
|
if not bill.isPaid and not isDeclined then
|
||||||
local confirm = lib.alertDialog({
|
local confirm = lib.alertDialog({
|
||||||
header = 'Pay Bill',
|
header = 'Rechnung bezahlen',
|
||||||
content = ('Do you want to pay $%s for %s?'):format(bill.amount, bill.description),
|
content = ('Möchtest du $%s für %s bezahlen?'):format(bill.amount, bill.description),
|
||||||
centered = true,
|
centered = true,
|
||||||
cancel = true
|
cancel = true
|
||||||
})
|
})
|
||||||
|
@ -212,25 +225,26 @@ function ViewBills()
|
||||||
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
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue