forked from Simnation/Main
ed
This commit is contained in:
parent
1301dfa7ad
commit
64fecd2fa0
2 changed files with 267 additions and 240 deletions
|
@ -26,52 +26,52 @@ function DrawText3D(x, y, z, text)
|
|||
ClearDrawOrigin()
|
||||
end
|
||||
|
||||
-- Get machine data with proper callback handling
|
||||
-- Get machine data with optimized callback handling
|
||||
function GetMachineData(entity)
|
||||
local coords = GetEntityCoords(entity)
|
||||
local entityId = tostring(coords.x) .. tostring(coords.y) .. tostring(coords.z)
|
||||
|
||||
-- Check if we need to refresh the data
|
||||
-- Return cached data if available and not expired
|
||||
local currentTime = GetGameTimer()
|
||||
if not machineData[entityId] or (currentTime - machineData[entityId].lastCheck > 10000) then
|
||||
-- Initialize with default values
|
||||
if machineData[entityId] and (currentTime - machineData[entityId].lastCheck < 10000) then
|
||||
return machineData[entityId]
|
||||
end
|
||||
|
||||
-- Initialize with default values
|
||||
if not machineData[entityId] then
|
||||
machineData[entityId] = {
|
||||
isRegistered = false,
|
||||
canManage = false,
|
||||
lastCheck = currentTime,
|
||||
checking = true
|
||||
}
|
||||
|
||||
-- Check if machine is registered
|
||||
QBCore.Functions.TriggerCallback('vending:server:machineExists', function(exists)
|
||||
if exists then
|
||||
machineData[entityId].isRegistered = true
|
||||
|
||||
-- Only check management if registered
|
||||
QBCore.Functions.TriggerCallback('vending:server:canManage', function(result)
|
||||
machineData[entityId].canManage = result
|
||||
machineData[entityId].checking = false
|
||||
end, coords)
|
||||
else
|
||||
machineData[entityId].isRegistered = false
|
||||
machineData[entityId].canManage = false
|
||||
machineData[entityId].checking = false
|
||||
end
|
||||
end, coords)
|
||||
else
|
||||
machineData[entityId].checking = true
|
||||
machineData[entityId].lastCheck = currentTime
|
||||
end
|
||||
|
||||
-- Wait for callbacks to complete if currently checking
|
||||
if machineData[entityId].checking then
|
||||
local timeout = 0
|
||||
while machineData[entityId].checking and timeout < 50 do
|
||||
Wait(10)
|
||||
timeout = timeout + 1
|
||||
end
|
||||
|
||||
-- If timeout reached, set checking to false to avoid deadlock
|
||||
if timeout >= 50 then
|
||||
machineData[entityId].checking = false
|
||||
-- Single callback to get all machine data at once (more efficient)
|
||||
QBCore.Functions.TriggerCallback('vending:server:getMachineStatus', function(status)
|
||||
if status then
|
||||
machineData[entityId].isRegistered = status.exists
|
||||
machineData[entityId].canManage = status.canManage
|
||||
else
|
||||
machineData[entityId].isRegistered = false
|
||||
machineData[entityId].canManage = false
|
||||
end
|
||||
machineData[entityId].checking = false
|
||||
end, coords)
|
||||
|
||||
-- Short wait for callback to complete
|
||||
local timeout = 0
|
||||
while machineData[entityId].checking and timeout < 20 do -- Reduced timeout
|
||||
Wait(5) -- Shorter wait
|
||||
timeout = timeout + 1
|
||||
end
|
||||
|
||||
-- If timeout reached, set checking to false to avoid deadlock
|
||||
if timeout >= 20 then
|
||||
machineData[entityId].checking = false
|
||||
end
|
||||
|
||||
return machineData[entityId]
|
||||
|
@ -229,49 +229,48 @@ RegisterNetEvent('vending:client:openBuyMenu', function(data)
|
|||
local entity = data.entity
|
||||
local coords = GetEntityCoords(entity)
|
||||
|
||||
-- Double-check if machine is registered before proceeding
|
||||
QBCore.Functions.TriggerCallback('vending:server:machineExists', function(exists)
|
||||
if not exists then
|
||||
QBCore.Functions.Notify('Dieser Automat ist nicht registriert!', 'error')
|
||||
-- Fast check using cached data
|
||||
local entityId = tostring(coords.x) .. tostring(coords.y) .. tostring(coords.z)
|
||||
if machineData[entityId] and not machineData[entityId].isRegistered then
|
||||
QBCore.Functions.Notify('Dieser Automat ist nicht registriert!', 'error')
|
||||
return
|
||||
end
|
||||
|
||||
QBCore.Functions.TriggerCallback('vending:server:getStashItems', function(items)
|
||||
if #items == 0 then
|
||||
QBCore.Functions.Notify('Dieser Automat ist leer!', 'error')
|
||||
return
|
||||
end
|
||||
|
||||
QBCore.Functions.TriggerCallback('vending:server:getStashItems', function(items)
|
||||
if #items == 0 then
|
||||
QBCore.Functions.Notify('Dieser Automat ist leer!', 'error')
|
||||
return
|
||||
local options = {}
|
||||
|
||||
for i = 1, #items do
|
||||
local item = items[i]
|
||||
if item.amount > 0 then
|
||||
local itemLabel = QBCore.Shared.Items[item.name] and QBCore.Shared.Items[item.name].label or item.name
|
||||
table.insert(options, {
|
||||
title = itemLabel,
|
||||
description = 'Preis: $' .. item.price .. ' | Verfügbar: ' .. item.amount,
|
||||
icon = 'fas fa-shopping-cart',
|
||||
onSelect = function()
|
||||
openQuantityDialog(coords, item.name, item.price, item.amount, itemLabel)
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
local options = {}
|
||||
|
||||
for i = 1, #items do
|
||||
local item = items[i]
|
||||
if item.amount > 0 then
|
||||
local itemLabel = QBCore.Shared.Items[item.name] and QBCore.Shared.Items[item.name].label or item.name
|
||||
table.insert(options, {
|
||||
title = itemLabel,
|
||||
description = 'Preis: $' .. item.price .. ' | Verfügbar: ' .. item.amount,
|
||||
icon = 'fas fa-shopping-cart',
|
||||
onSelect = function()
|
||||
openQuantityDialog(coords, item.name, item.price, item.amount, itemLabel)
|
||||
end
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
if #options == 0 then
|
||||
QBCore.Functions.Notify('Keine Artikel verfügbar!', 'error')
|
||||
return
|
||||
end
|
||||
|
||||
lib.registerContext({
|
||||
id = 'vending_buy_menu',
|
||||
title = 'Verkaufsautomat',
|
||||
options = options
|
||||
})
|
||||
|
||||
lib.showContext('vending_buy_menu')
|
||||
end, coords)
|
||||
end
|
||||
|
||||
if #options == 0 then
|
||||
QBCore.Functions.Notify('Keine Artikel verfügbar!', 'error')
|
||||
return
|
||||
end
|
||||
|
||||
lib.registerContext({
|
||||
id = 'vending_buy_menu',
|
||||
title = 'Verkaufsautomat',
|
||||
options = options
|
||||
})
|
||||
|
||||
lib.showContext('vending_buy_menu')
|
||||
end, coords)
|
||||
end)
|
||||
|
||||
|
@ -304,84 +303,83 @@ RegisterNetEvent('vending:client:openOwnerMenu', function(data)
|
|||
local entity = data.entity
|
||||
local coords = GetEntityCoords(entity)
|
||||
|
||||
-- Double-check if player can manage this machine before proceeding
|
||||
QBCore.Functions.TriggerCallback('vending:server:canManage', function(canManage)
|
||||
if not canManage then
|
||||
QBCore.Functions.Notify('Du hast keine Berechtigung diesen Automaten zu verwalten!', 'error')
|
||||
-- Fast check using cached data
|
||||
local entityId = tostring(coords.x) .. tostring(coords.y) .. tostring(coords.z)
|
||||
if machineData[entityId] and not machineData[entityId].canManage then
|
||||
QBCore.Functions.Notify('Du hast keine Berechtigung diesen Automaten zu verwalten!', 'error')
|
||||
return
|
||||
end
|
||||
|
||||
QBCore.Functions.TriggerCallback('vending:server:getMachineByCoords', function(machine)
|
||||
if not machine then
|
||||
QBCore.Functions.Notify('Automat nicht gefunden!', 'error')
|
||||
return
|
||||
end
|
||||
|
||||
QBCore.Functions.TriggerCallback('vending:server:getMachineByCoords', function(machine)
|
||||
if not machine then
|
||||
QBCore.Functions.Notify('Automat nicht gefunden!', 'error')
|
||||
return
|
||||
end
|
||||
|
||||
local options = {
|
||||
{
|
||||
title = 'Inventar verwalten',
|
||||
description = 'Items hinzufügen/entfernen',
|
||||
icon = 'fas fa-box',
|
||||
onSelect = function()
|
||||
TriggerServerEvent('vending:server:openStash', coords)
|
||||
end
|
||||
},
|
||||
{
|
||||
title = 'Preise festlegen',
|
||||
description = 'Verkaufspreise für Items setzen',
|
||||
icon = 'fas fa-tags',
|
||||
onSelect = function()
|
||||
openPriceMenu(coords)
|
||||
end
|
||||
},
|
||||
{
|
||||
title = 'Geld abheben',
|
||||
description = 'Verfügbar: $' .. machine.money,
|
||||
icon = 'fas fa-money-bill',
|
||||
onSelect = function()
|
||||
openWithdrawMenu(coords, machine.money)
|
||||
end
|
||||
},
|
||||
{
|
||||
title = 'Statistiken',
|
||||
description = 'Verkaufsstatistiken anzeigen',
|
||||
icon = 'fas fa-chart-bar',
|
||||
onSelect = function()
|
||||
openStatsMenu(machine)
|
||||
end
|
||||
}
|
||||
local options = {
|
||||
{
|
||||
title = 'Inventar verwalten',
|
||||
description = 'Items hinzufügen/entfernen',
|
||||
icon = 'fas fa-box',
|
||||
onSelect = function()
|
||||
TriggerServerEvent('vending:server:openStash', coords)
|
||||
end
|
||||
},
|
||||
{
|
||||
title = 'Preise festlegen',
|
||||
description = 'Verkaufspreise für Items setzen',
|
||||
icon = 'fas fa-tags',
|
||||
onSelect = function()
|
||||
openPriceMenu(coords)
|
||||
end
|
||||
},
|
||||
{
|
||||
title = 'Geld abheben',
|
||||
description = 'Verfügbar: $' .. machine.money,
|
||||
icon = 'fas fa-money-bill',
|
||||
onSelect = function()
|
||||
openWithdrawMenu(coords, machine.money)
|
||||
end
|
||||
},
|
||||
{
|
||||
title = 'Statistiken',
|
||||
description = 'Verkaufsstatistiken anzeigen',
|
||||
icon = 'fas fa-chart-bar',
|
||||
onSelect = function()
|
||||
openStatsMenu(machine)
|
||||
end
|
||||
}
|
||||
|
||||
-- Add manager options only for owner
|
||||
if machine.isOwner then
|
||||
table.insert(options, {
|
||||
title = 'Verwalter',
|
||||
description = 'Verwalter hinzufügen/entfernen',
|
||||
icon = 'fas fa-users-cog',
|
||||
onSelect = function()
|
||||
openManagersMenu(coords)
|
||||
end
|
||||
})
|
||||
|
||||
-- Add sell option only for owner
|
||||
table.insert(options, {
|
||||
title = 'Automaten verkaufen',
|
||||
description = 'Verkaufe den Automaten für ' .. math.floor(Config.VendingMachinePrice * Config.SellBackPercentage / 100) .. '$',
|
||||
icon = 'fas fa-dollar-sign',
|
||||
onSelect = function()
|
||||
sellVendingMachine(coords, machine.id)
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
lib.registerContext({
|
||||
id = 'vending_owner_menu',
|
||||
title = 'Verkaufsautomat Verwaltung',
|
||||
options = options
|
||||
}
|
||||
|
||||
-- Add manager options only for owner
|
||||
if machine.isOwner then
|
||||
table.insert(options, {
|
||||
title = 'Verwalter',
|
||||
description = 'Verwalter hinzufügen/entfernen',
|
||||
icon = 'fas fa-users-cog',
|
||||
onSelect = function()
|
||||
openManagersMenu(coords)
|
||||
end
|
||||
})
|
||||
|
||||
lib.showContext('vending_owner_menu')
|
||||
end, coords)
|
||||
-- Add sell option only for owner
|
||||
table.insert(options, {
|
||||
title = 'Automaten verkaufen',
|
||||
description = 'Verkaufe den Automaten für ' .. math.floor(Config.VendingMachinePrice * Config.SellBackPercentage / 100) .. '$',
|
||||
icon = 'fas fa-dollar-sign',
|
||||
onSelect = function()
|
||||
sellVendingMachine(coords, machine.id)
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
lib.registerContext({
|
||||
id = 'vending_owner_menu',
|
||||
title = 'Verkaufsautomat Verwaltung',
|
||||
options = options
|
||||
})
|
||||
|
||||
lib.showContext('vending_owner_menu')
|
||||
end, coords)
|
||||
end)
|
||||
|
||||
|
@ -406,43 +404,42 @@ end
|
|||
|
||||
-- Open price menu
|
||||
function openPriceMenu(coords)
|
||||
-- Double-check if player can manage this machine before proceeding
|
||||
QBCore.Functions.TriggerCallback('vending:server:canManage', function(canManage)
|
||||
if not canManage then
|
||||
QBCore.Functions.Notify('Du hast keine Berechtigung diesen Automaten zu verwalten!', 'error')
|
||||
-- Fast check using cached data
|
||||
local entityId = tostring(coords.x) .. tostring(coords.y) .. tostring(coords.z)
|
||||
if machineData[entityId] and not machineData[entityId].canManage then
|
||||
QBCore.Functions.Notify('Du hast keine Berechtigung diesen Automaten zu verwalten!', 'error')
|
||||
return
|
||||
end
|
||||
|
||||
QBCore.Functions.TriggerCallback('vending:server:getStashItems', function(items)
|
||||
if #items == 0 then
|
||||
QBCore.Functions.Notify('Keine Items im Automaten!', 'error')
|
||||
return
|
||||
end
|
||||
|
||||
QBCore.Functions.TriggerCallback('vending:server:getStashItems', function(items)
|
||||
if #items == 0 then
|
||||
QBCore.Functions.Notify('Keine Items im Automaten!', 'error')
|
||||
return
|
||||
end
|
||||
|
||||
local options = {}
|
||||
|
||||
for i = 1, #items do
|
||||
local item = items[i]
|
||||
local itemLabel = QBCore.Shared.Items[item.name] and QBCore.Shared.Items[item.name].label or item.name
|
||||
table.insert(options, {
|
||||
title = itemLabel,
|
||||
description = 'Aktueller Preis: $' .. item.price,
|
||||
icon = 'fas fa-tag',
|
||||
onSelect = function()
|
||||
setPriceForItem(coords, item.name, itemLabel)
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
lib.registerContext({
|
||||
id = 'vending_price_menu',
|
||||
title = 'Preise festlegen',
|
||||
menu = 'vending_owner_menu',
|
||||
options = options
|
||||
local options = {}
|
||||
|
||||
for i = 1, #items do
|
||||
local item = items[i]
|
||||
local itemLabel = QBCore.Shared.Items[item.name] and QBCore.Shared.Items[item.name].label or item.name
|
||||
table.insert(options, {
|
||||
title = itemLabel,
|
||||
description = 'Aktueller Preis: $' .. item.price,
|
||||
icon = 'fas fa-tag',
|
||||
onSelect = function()
|
||||
setPriceForItem(coords, item.name, itemLabel)
|
||||
end
|
||||
})
|
||||
|
||||
lib.showContext('vending_price_menu')
|
||||
end, coords)
|
||||
end
|
||||
|
||||
lib.registerContext({
|
||||
id = 'vending_price_menu',
|
||||
title = 'Preise festlegen',
|
||||
menu = 'vending_owner_menu',
|
||||
options = options
|
||||
})
|
||||
|
||||
lib.showContext('vending_price_menu')
|
||||
end, coords)
|
||||
end
|
||||
|
||||
|
@ -466,33 +463,32 @@ end
|
|||
|
||||
-- Open withdraw menu
|
||||
function openWithdrawMenu(coords, availableMoney)
|
||||
-- Double-check if player can manage this machine before proceeding
|
||||
QBCore.Functions.TriggerCallback('vending:server:canManage', function(canManage)
|
||||
if not canManage then
|
||||
QBCore.Functions.Notify('Du hast keine Berechtigung diesen Automaten zu verwalten!', 'error')
|
||||
return
|
||||
end
|
||||
|
||||
if availableMoney <= 0 then
|
||||
QBCore.Functions.Notify('Kein Geld im Automaten!', 'error')
|
||||
return
|
||||
end
|
||||
|
||||
local input = lib.inputDialog('Geld abheben', {
|
||||
{
|
||||
type = 'number',
|
||||
label = 'Betrag (Verfügbar: $' .. availableMoney .. ')',
|
||||
description = 'Wie viel möchtest du abheben?',
|
||||
required = true,
|
||||
min = 1,
|
||||
max = availableMoney
|
||||
}
|
||||
})
|
||||
|
||||
if input and input[1] then
|
||||
TriggerServerEvent('vending:server:withdrawMoney', coords, tonumber(input[1]))
|
||||
end
|
||||
end, coords)
|
||||
-- Fast check using cached data
|
||||
local entityId = tostring(coords.x) .. tostring(coords.y) .. tostring(coords.z)
|
||||
if machineData[entityId] and not machineData[entityId].canManage then
|
||||
QBCore.Functions.Notify('Du hast keine Berechtigung diesen Automaten zu verwalten!', 'error')
|
||||
return
|
||||
end
|
||||
|
||||
if availableMoney <= 0 then
|
||||
QBCore.Functions.Notify('Kein Geld im Automaten!', 'error')
|
||||
return
|
||||
end
|
||||
|
||||
local input = lib.inputDialog('Geld abheben', {
|
||||
{
|
||||
type = 'number',
|
||||
label = 'Betrag (Verfügbar: $' .. availableMoney .. ')',
|
||||
description = 'Wie viel möchtest du abheben?',
|
||||
required = true,
|
||||
min = 1,
|
||||
max = availableMoney
|
||||
}
|
||||
})
|
||||
|
||||
if input and input[1] then
|
||||
TriggerServerEvent('vending:server:withdrawMoney', coords, tonumber(input[1]))
|
||||
end
|
||||
end
|
||||
|
||||
-- Open stats menu
|
||||
|
@ -525,7 +521,7 @@ end
|
|||
|
||||
-- Open managers menu
|
||||
function openManagersMenu(coords)
|
||||
-- Double-check if player is owner of this machine before proceeding
|
||||
-- Fast check for owner status
|
||||
QBCore.Functions.TriggerCallback('vending:server:isOwner', function(isOwner)
|
||||
if not isOwner then
|
||||
QBCore.Functions.Notify('Nur der Besitzer kann Verwalter verwalten!', 'error')
|
||||
|
@ -598,7 +594,7 @@ end
|
|||
|
||||
-- Open add manager menu
|
||||
function openAddManagerMenu(coords)
|
||||
-- Double-check if player is owner of this machine before proceeding
|
||||
-- Fast check for owner status
|
||||
QBCore.Functions.TriggerCallback('vending:server:isOwner', function(isOwner)
|
||||
if not isOwner then
|
||||
QBCore.Functions.Notify('Nur der Besitzer kann Verwalter hinzufügen!', 'error')
|
||||
|
@ -644,42 +640,39 @@ RegisterNetEvent('vending:client:startRobbery', function(data)
|
|||
local entity = data.entity
|
||||
local coords = GetEntityCoords(entity)
|
||||
|
||||
-- Double-check if machine is registered and player cannot manage it
|
||||
QBCore.Functions.TriggerCallback('vending:server:machineExists', function(exists)
|
||||
if not exists then
|
||||
-- Fast check using cached data
|
||||
local entityId = tostring(coords.x) .. tostring(coords.y) .. tostring(coords.z)
|
||||
if machineData[entityId] then
|
||||
if not machineData[entityId].isRegistered then
|
||||
QBCore.Functions.Notify('Dieser Automat ist nicht registriert!', 'error')
|
||||
return
|
||||
elseif machineData[entityId].canManage then
|
||||
QBCore.Functions.Notify('Du kannst deinen eigenen Automaten nicht aufbrechen!', 'error')
|
||||
return
|
||||
end
|
||||
|
||||
QBCore.Functions.TriggerCallback('vending:server:canManage', function(canManage)
|
||||
if canManage then
|
||||
QBCore.Functions.Notify('Du kannst deinen eigenen Automaten nicht aufbrechen!', 'error')
|
||||
return
|
||||
end
|
||||
|
||||
lib.registerContext({
|
||||
id = 'vending_robbery_confirm',
|
||||
title = 'Verkaufsautomat aufbrechen',
|
||||
options = {
|
||||
{
|
||||
title = 'Aufbrechen',
|
||||
description = 'Versuche den Automaten aufzubrechen',
|
||||
icon = 'fas fa-mask',
|
||||
onSelect = function()
|
||||
TriggerServerEvent('vending:server:startRobbery', coords)
|
||||
end
|
||||
},
|
||||
{
|
||||
title = 'Abbrechen',
|
||||
description = 'Aufbruch abbrechen',
|
||||
icon = 'fas fa-times'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
lib.showContext('vending_robbery_confirm')
|
||||
end, coords)
|
||||
end, coords)
|
||||
end
|
||||
|
||||
lib.registerContext({
|
||||
id = 'vending_robbery_confirm',
|
||||
title = 'Verkaufsautomat aufbrechen',
|
||||
options = {
|
||||
{
|
||||
title = 'Aufbrechen',
|
||||
description = 'Versuche den Automaten aufzubrechen',
|
||||
icon = 'fas fa-mask',
|
||||
onSelect = function()
|
||||
TriggerServerEvent('vending:server:startRobbery', coords)
|
||||
end
|
||||
},
|
||||
{
|
||||
title = 'Abbrechen',
|
||||
description = 'Aufbruch abbrechen',
|
||||
icon = 'fas fa-times'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
lib.showContext('vending_robbery_confirm')
|
||||
end)
|
||||
|
||||
-- Start robbery animation and progress
|
||||
|
@ -747,7 +740,7 @@ end)
|
|||
|
||||
-- Management menu (alternative opening method)
|
||||
RegisterNetEvent('vending:client:openManagement', function(machine)
|
||||
-- Double-check if player can manage this machine
|
||||
-- Fast check for management permissions
|
||||
QBCore.Functions.TriggerCallback('vending:server:canManage', function(canManage)
|
||||
if not canManage then
|
||||
QBCore.Functions.Notify('Du hast keine Berechtigung diesen Automaten zu verwalten!', 'error')
|
||||
|
@ -871,4 +864,3 @@ AddEventHandler('onResourceStop', function(resourceName)
|
|||
-- Nothing to do here, but good to have for completeness
|
||||
end
|
||||
end)
|
||||
|
||||
|
|
|
@ -712,4 +712,39 @@ QBCore.Commands.Add('vendingdebug', 'Debug vending machines (Admin Only)', {}, f
|
|||
end
|
||||
end, 'admin')
|
||||
|
||||
-- Combined callback for faster machine status checks
|
||||
QBCore.Functions.CreateCallback('vending:server:getMachineStatus', function(source, cb, coords)
|
||||
local src = source
|
||||
local Player = QBCore.Functions.GetPlayer(src)
|
||||
if not Player then
|
||||
cb(nil)
|
||||
return
|
||||
end
|
||||
|
||||
local machineId = getMachineIdByCoords(coords)
|
||||
if not machineId then
|
||||
cb({exists = false, canManage = false})
|
||||
return
|
||||
end
|
||||
|
||||
local machine = vendingMachines[machineId]
|
||||
local canManage = false
|
||||
|
||||
-- Check if player is owner
|
||||
if machine.owner == Player.PlayerData.citizenid then
|
||||
canManage = true
|
||||
else
|
||||
-- Check if player is manager
|
||||
if machine.managers then
|
||||
for _, manager in pairs(machine.managers) do
|
||||
if manager == Player.PlayerData.citizenid then
|
||||
canManage = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
cb({exists = true, canManage = canManage})
|
||||
end)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue