ed
This commit is contained in:
		
							parent
							
								
									4d24104e50
								
							
						
					
					
						commit
						05be118a84
					
				
					 2 changed files with 477 additions and 691 deletions
				
			
		| 
						 | 
				
			
			@ -6,6 +6,8 @@ local currentTarget = nil
 | 
			
		|||
local nearbyPlayers = {}
 | 
			
		||||
local isLicenseShowing = false
 | 
			
		||||
local pendingRequests = {}
 | 
			
		||||
local currentLicenseData = nil
 | 
			
		||||
local currentTargetId = nil
 | 
			
		||||
 | 
			
		||||
-- Hilfsfunktionen
 | 
			
		||||
local function debugPrint(message)
 | 
			
		||||
| 
						 | 
				
			
			@ -116,6 +118,20 @@ local openDriversLicenseClassMenu
 | 
			
		|||
local openRevokeLicenseMenu
 | 
			
		||||
local openPlayerLicenseMenu
 | 
			
		||||
local openLicenseMenu
 | 
			
		||||
local openManualLicenseEntry
 | 
			
		||||
local openReactivateLicenseMenu
 | 
			
		||||
 | 
			
		||||
-- Helper function to get license type options
 | 
			
		||||
local function getLicenseTypeOptions()
 | 
			
		||||
    local options = {}
 | 
			
		||||
    for licenseType, config in pairs(Config.LicenseTypes) do
 | 
			
		||||
        table.insert(options, {
 | 
			
		||||
            value = licenseType,
 | 
			
		||||
            label = config.label
 | 
			
		||||
        })
 | 
			
		||||
    end
 | 
			
		||||
    return options
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Lizenz-Ausstellung bestätigen (FRÜH DEFINIERT)
 | 
			
		||||
confirmIssueLicense = function(targetId, targetName, licenseType, classes)
 | 
			
		||||
| 
						 | 
				
			
			@ -409,6 +425,51 @@ openRevokeLicenseMenu = function(targetId, targetName, licenses)
 | 
			
		|||
    lib.showContext('revoke_license')
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Manual License Entry Menu
 | 
			
		||||
openManualLicenseEntry = function(targetId, targetName)
 | 
			
		||||
    debugPrint("Öffne manuelle Lizenz-Eingabe für: " .. targetName)
 | 
			
		||||
    
 | 
			
		||||
    local input = lib.inputDialog('Lizenz manuell ausstellen', {
 | 
			
		||||
        {type = 'select', label = 'Lizenztyp', options = getLicenseTypeOptions()},
 | 
			
		||||
        {type = 'input', label = 'Name', default = targetName},
 | 
			
		||||
        {type = 'input', label = 'Geburtsdatum', description = 'Format: DD.MM.YYYY'},
 | 
			
		||||
        {type = 'select', label = 'Geschlecht', options = {
 | 
			
		||||
            {value = 'male', label = 'Männlich'},
 | 
			
		||||
            {value = 'female', label = 'Weiblich'},
 | 
			
		||||
            {value = 'other', label = 'Divers'}
 | 
			
		||||
        }},
 | 
			
		||||
        {type = 'date', label = 'Ausstellungsdatum', default = os.date("%Y-%m-%d")},
 | 
			
		||||
        {type = 'date', label = 'Ablaufdatum', default = os.date("%Y-%m-%d", os.time() + 365*24*60*60)},
 | 
			
		||||
        {type = 'checkbox', label = 'Foto aufnehmen?'}
 | 
			
		||||
    })
 | 
			
		||||
    
 | 
			
		||||
    if not input then return end
 | 
			
		||||
    
 | 
			
		||||
    local licenseType = input[1]
 | 
			
		||||
    local licenseData = {
 | 
			
		||||
        name = input[2],
 | 
			
		||||
        birthday = input[3],
 | 
			
		||||
        gender = input[4],
 | 
			
		||||
        issue_date = os.date("%d.%m.%Y", os.time(os.date("!*t", input[5] / 1000))),
 | 
			
		||||
        expire_date = os.date("%d.%m.%Y", os.time(os.date("!*t", input[6] / 1000))),
 | 
			
		||||
        license_type = licenseType
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    if input[7] then -- Take photo
 | 
			
		||||
        TriggerEvent('license-system:client:openCamera', targetId, licenseData)
 | 
			
		||||
    else
 | 
			
		||||
        TriggerServerEvent('license-system:server:issueManualLicense', targetId, licenseData)
 | 
			
		||||
    end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- License Reactivation Menu
 | 
			
		||||
openReactivateLicenseMenu = function(targetId, targetName)
 | 
			
		||||
    debugPrint("Öffne Lizenz-Reaktivierungs-Menü für: " .. targetName)
 | 
			
		||||
    
 | 
			
		||||
    -- Request all licenses including inactive ones
 | 
			
		||||
    TriggerServerEvent('license-system:server:requestAllPlayerLicenses', targetId, true)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Spieler-Lizenz-Menü
 | 
			
		||||
openPlayerLicenseMenu = function(targetId, targetName)
 | 
			
		||||
    debugPrint("=== openPlayerLicenseMenu START ===")
 | 
			
		||||
| 
						 | 
				
			
			@ -575,6 +636,15 @@ RegisterNetEvent('license-system:client:receivePlayerLicenses', function(license
 | 
			
		|||
        end
 | 
			
		||||
    })
 | 
			
		||||
    
 | 
			
		||||
    table.insert(menuOptions, {
 | 
			
		||||
        title = 'Lizenz manuell ausstellen',
 | 
			
		||||
        description = 'Lizenz mit benutzerdefinierten Daten ausstellen',
 | 
			
		||||
        icon = 'fas fa-edit',
 | 
			
		||||
        onSelect = function()
 | 
			
		||||
            openManualLicenseEntry(targetId, targetName)
 | 
			
		||||
        end
 | 
			
		||||
    })
 | 
			
		||||
    
 | 
			
		||||
    if licenses and #licenses > 0 then
 | 
			
		||||
        table.insert(menuOptions, {
 | 
			
		||||
            title = 'Lizenz entziehen',
 | 
			
		||||
| 
						 | 
				
			
			@ -586,6 +656,15 @@ RegisterNetEvent('license-system:client:receivePlayerLicenses', function(license
 | 
			
		|||
        })
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    table.insert(menuOptions, {
 | 
			
		||||
        title = 'Lizenz reaktivieren',
 | 
			
		||||
        description = 'Eine inaktive Lizenz wieder aktivieren',
 | 
			
		||||
        icon = 'fas fa-redo',
 | 
			
		||||
        onSelect = function()
 | 
			
		||||
            openReactivateLicenseMenu(targetId, targetName)
 | 
			
		||||
        end
 | 
			
		||||
    })
 | 
			
		||||
    
 | 
			
		||||
    table.insert(menuOptions, {
 | 
			
		||||
        title = '← Zurück',
 | 
			
		||||
        icon = 'fas fa-arrow-left',
 | 
			
		||||
| 
						 | 
				
			
			@ -603,6 +682,100 @@ RegisterNetEvent('license-system:client:receivePlayerLicenses', function(license
 | 
			
		|||
    lib.showContext('player_licenses')
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
-- EVENT HANDLER: Alle Spieler-Lizenzen (inkl. inaktive) erhalten
 | 
			
		||||
RegisterNetEvent('license-system:client:receiveAllPlayerLicenses', function(licenses, targetId, targetName)
 | 
			
		||||
    debugPrint("=== Event: receiveAllPlayerLicenses ===")
 | 
			
		||||
    debugPrint("Erhaltene Lizenzen: " .. #licenses)
 | 
			
		||||
    
 | 
			
		||||
    local menuOptions = {}
 | 
			
		||||
    
 | 
			
		||||
    if licenses and #licenses > 0 then
 | 
			
		||||
        for _, license in ipairs(licenses) do
 | 
			
		||||
            if license.is_active == 0 then -- Only show inactive licenses
 | 
			
		||||
                local licenseConfig = Config.LicenseTypes[license.license_type] or {
 | 
			
		||||
                    label = license.license_type,
 | 
			
		||||
                    icon = 'fas fa-id-card',
 | 
			
		||||
                    color = '#667eea'
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                table.insert(menuOptions, {
 | 
			
		||||
                    title = licenseConfig.label .. ' ❌',
 | 
			
		||||
                    description = 'Status: Ungültig | Ausgestellt: ' .. (license.issue_date or 'Unbekannt'),
 | 
			
		||||
                    icon = licenseConfig.icon,
 | 
			
		||||
                    onSelect = function()
 | 
			
		||||
                        -- Confirmation dialog
 | 
			
		||||
                        lib.registerContext({
 | 
			
		||||
                            id = 'confirm_reactivate_license',
 | 
			
		||||
                            title = 'Lizenz reaktivieren bestätigen',
 | 
			
		||||
                            options = {
 | 
			
		||||
                                {
 | 
			
		||||
                                    title = 'Spieler: ' .. targetName,
 | 
			
		||||
                                    disabled = true,
 | 
			
		||||
                                    icon = 'fas fa-user'
 | 
			
		||||
                                },
 | 
			
		||||
                                {
 | 
			
		||||
                                    title = 'Lizenztyp: ' .. licenseConfig.label,
 | 
			
		||||
                                    disabled = true,
 | 
			
		||||
                                    icon = licenseConfig.icon
 | 
			
		||||
                                },
 | 
			
		||||
                                {
 | 
			
		||||
                                    title = '─────────────────',
 | 
			
		||||
                                    disabled = true
 | 
			
		||||
                                },
 | 
			
		||||
                                {
 | 
			
		||||
                                    title = '✅ Bestätigen',
 | 
			
		||||
                                    description = 'Lizenz jetzt reaktivieren',
 | 
			
		||||
                                    icon = 'fas fa-check',
 | 
			
		||||
                                    onSelect = function()
 | 
			
		||||
                                        debugPrint("Sende Lizenz-Reaktivierung an Server...")
 | 
			
		||||
                                        TriggerServerEvent('license-system:server:reactivateLicense', targetId, license.license_type)
 | 
			
		||||
                                        lib.hideContext()
 | 
			
		||||
                                    end
 | 
			
		||||
                                },
 | 
			
		||||
                                {
 | 
			
		||||
                                    title = '❌ Abbrechen',
 | 
			
		||||
                                    description = 'Vorgang abbrechen',
 | 
			
		||||
                                    icon = 'fas fa-times',
 | 
			
		||||
                                    onSelect = function()
 | 
			
		||||
                                        openReactivateLicenseMenu(targetId, targetName)
 | 
			
		||||
                                    end
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        })
 | 
			
		||||
                        
 | 
			
		||||
                        lib.showContext('confirm_reactivate_license')
 | 
			
		||||
                    end
 | 
			
		||||
                })
 | 
			
		||||
            end
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    if #menuOptions == 0 then
 | 
			
		||||
        table.insert(menuOptions, {
 | 
			
		||||
            title = 'Keine inaktiven Lizenzen',
 | 
			
		||||
            description = 'Dieser Spieler hat keine inaktiven Lizenzen',
 | 
			
		||||
            icon = 'fas fa-exclamation-triangle',
 | 
			
		||||
            disabled = true
 | 
			
		||||
        })
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    table.insert(menuOptions, {
 | 
			
		||||
        title = '← Zurück',
 | 
			
		||||
        icon = 'fas fa-arrow-left',
 | 
			
		||||
        onSelect = function()
 | 
			
		||||
            openPlayerLicenseMenu(targetId, targetName)
 | 
			
		||||
        end
 | 
			
		||||
    })
 | 
			
		||||
    
 | 
			
		||||
    lib.registerContext({
 | 
			
		||||
        id = 'reactivate_license',
 | 
			
		||||
        title = 'Lizenz reaktivieren: ' .. targetName,
 | 
			
		||||
        options = menuOptions
 | 
			
		||||
    })
 | 
			
		||||
    
 | 
			
		||||
    lib.showContext('reactivate_license')
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
-- EVENT HANDLER: Berechtigung erhalten
 | 
			
		||||
RegisterNetEvent('license-system:client:receivePermission', function(hasAuth, licenseType)
 | 
			
		||||
    debugPrint("=== Event: receivePermission ===")
 | 
			
		||||
| 
						 | 
				
			
			@ -639,6 +812,19 @@ RegisterNetEvent('license-system:client:licenseRevoked', function(targetId, lice
 | 
			
		|||
    end
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
-- EVENT HANDLER: Lizenz erfolgreich reaktiviert
 | 
			
		||||
RegisterNetEvent('license-system:client:licenseReactivated', function(targetId, licenseType)
 | 
			
		||||
    debugPrint("=== Event: licenseReactivated ===")
 | 
			
		||||
    debugPrint("Lizenz " .. licenseType .. " für Spieler " .. targetId .. " reaktiviert")
 | 
			
		||||
    
 | 
			
		||||
    -- Menü aktualisieren
 | 
			
		||||
    if lib.getOpenContextMenu() then
 | 
			
		||||
        lib.hideContext()
 | 
			
		||||
        Wait(100)
 | 
			
		||||
        openLicenseMenu()
 | 
			
		||||
    end
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
-- EVENT HANDLER: Lizenz anzeigen (von anderen Spielern)
 | 
			
		||||
RegisterNetEvent('license-system:client:showLicense', function(targetId)
 | 
			
		||||
    debugPrint("Event erhalten: showLicense für ID " .. tostring(targetId))
 | 
			
		||||
| 
						 | 
				
			
			@ -652,10 +838,16 @@ RegisterNetEvent('license-system:client:showMyLicense', function(licenseType)
 | 
			
		|||
end)
 | 
			
		||||
 | 
			
		||||
-- EVENT HANDLER: Kamera öffnen
 | 
			
		||||
RegisterNetEvent('license-system:client:openCamera', function()
 | 
			
		||||
    debugPrint("Event erhalten: openCamera")
 | 
			
		||||
RegisterNetEvent('license-system:client:openCamera', function(targetId, licenseData)
 | 
			
		||||
    debugPrint("Event erhalten: openCamera für Spieler " .. tostring(targetId))
 | 
			
		||||
    
 | 
			
		||||
    -- Store the data temporarily
 | 
			
		||||
    currentLicenseData = licenseData
 | 
			
		||||
    currentTargetId = targetId
 | 
			
		||||
    
 | 
			
		||||
    SendNUIMessage({
 | 
			
		||||
        action = 'openCamera'
 | 
			
		||||
        action = 'openCamera',
 | 
			
		||||
        citizenid = QBCore.Functions.GetPlayerData().citizenid
 | 
			
		||||
    })
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -682,14 +874,22 @@ end)
 | 
			
		|||
RegisterNUICallback('savePhoto', function(data, cb)
 | 
			
		||||
    debugPrint("NUI Callback: savePhoto")
 | 
			
		||||
    
 | 
			
		||||
    if data.photo and data.citizenid then
 | 
			
		||||
        TriggerServerEvent('license-system:server:savePhoto', data.citizenid, data.photo)
 | 
			
		||||
    if data.photo and currentLicenseData and currentTargetId then
 | 
			
		||||
        -- Add photo to license data
 | 
			
		||||
        currentLicenseData.photo_url = data.photo
 | 
			
		||||
        
 | 
			
		||||
        -- Issue the license with the photo
 | 
			
		||||
        TriggerServerEvent('license-system:server:issueManualLicense', currentTargetId, currentLicenseData)
 | 
			
		||||
        
 | 
			
		||||
        -- Reset temporary data
 | 
			
		||||
        currentLicenseData = nil
 | 
			
		||||
        currentTargetId = nil
 | 
			
		||||
        
 | 
			
		||||
        if cb and type(cb) == "function" then
 | 
			
		||||
            cb('ok')
 | 
			
		||||
        end
 | 
			
		||||
    else
 | 
			
		||||
        debugPrint("^1Fehler: Foto-Daten unvollständig^7")
 | 
			
		||||
        debugPrint("^1Fehler: Foto-Daten unvollständig oder keine aktuelle Lizenz-Ausstellung^7")
 | 
			
		||||
        
 | 
			
		||||
        if cb and type(cb) == "function" then
 | 
			
		||||
            cb('error')
 | 
			
		||||
| 
						 | 
				
			
			@ -773,6 +973,10 @@ AddEventHandler('onResourceStop', function(resourceName)
 | 
			
		|||
            lib.hideContext()
 | 
			
		||||
        end
 | 
			
		||||
        
 | 
			
		||||
        -- Reset temporary data
 | 
			
		||||
        currentLicenseData = nil
 | 
			
		||||
        currentTargetId = nil
 | 
			
		||||
        
 | 
			
		||||
        debugPrint("License-System Client gestoppt")
 | 
			
		||||
    end
 | 
			
		||||
end)
 | 
			
		||||
| 
						 | 
				
			
			@ -1157,6 +1361,10 @@ local function cleanup()
 | 
			
		|||
    -- Pending Requests leeren
 | 
			
		||||
    pendingRequests = {}
 | 
			
		||||
    
 | 
			
		||||
    -- Temporary data leeren
 | 
			
		||||
    currentLicenseData = nil
 | 
			
		||||
    currentTargetId = nil
 | 
			
		||||
    
 | 
			
		||||
    -- Animationen stoppen
 | 
			
		||||
    local playerPed = PlayerPedId()
 | 
			
		||||
    if DoesEntityExist(playerPed) then
 | 
			
		||||
| 
						 | 
				
			
			@ -1237,312 +1445,6 @@ RegisterCommand('licensedebug', function()
 | 
			
		|||
    showNotification('Debug-Informationen in der Konsole ausgegeben!', 'success')
 | 
			
		||||
end, false)
 | 
			
		||||
 | 
			
		||||
-- Erweiterte Keybind-Behandlung
 | 
			
		||||
CreateThread(function()
 | 
			
		||||
    while true do
 | 
			
		||||
        Wait(0)
 | 
			
		||||
        
 | 
			
		||||
        -- ESC-Taste für Lizenz schließen
 | 
			
		||||
        if isLicenseShowing then
 | 
			
		||||
            if IsControlJustPressed(0, 322) then -- ESC
 | 
			
		||||
                closeLicense()
 | 
			
		||||
            end
 | 
			
		||||
        end
 | 
			
		||||
        
 | 
			
		||||
        -- Zusätzliche Hotkeys (falls konfiguriert)
 | 
			
		||||
        if Config.Keybinds and Config.Keybinds.emergency_close then
 | 
			
		||||
            if IsControlJustPressed(0, Config.Keybinds.emergency_close.control) then
 | 
			
		||||
                cleanup()
 | 
			
		||||
                showNotification('Notfall-Schließung aktiviert!', 'info')
 | 
			
		||||
            end
 | 
			
		||||
        end
 | 
			
		||||
        
 | 
			
		||||
        -- Performance-Optimierung
 | 
			
		||||
        if not isLicenseShowing and not lib.getOpenContextMenu() then
 | 
			
		||||
            Wait(500)
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
end)
 | 
			
		||||
-- Finaler Debug-Output
 | 
			
		||||
debugPrint("License-System Client vollständig geladen - Alle Funktionen verfügbar")
 | 
			
		||||
 | 
			
		||||
-- Erweiterte Netzwerk-Events mit Retry-Mechanismus
 | 
			
		||||
local function sendEventWithRetry(eventName, data, maxRetries)
 | 
			
		||||
    maxRetries = maxRetries or 3
 | 
			
		||||
    local retries = 0
 | 
			
		||||
    
 | 
			
		||||
    local function attemptSend()
 | 
			
		||||
        retries = retries + 1
 | 
			
		||||
        debugPrint("Sende Event: " .. eventName .. " (Versuch " .. retries .. "/" .. maxRetries .. ")")
 | 
			
		||||
        
 | 
			
		||||
        TriggerServerEvent(eventName, table.unpack(data or {}))
 | 
			
		||||
        
 | 
			
		||||
        -- Timeout für Response
 | 
			
		||||
        CreateThread(function()
 | 
			
		||||
            Wait(5000) -- 5 Sekunden Timeout
 | 
			
		||||
            
 | 
			
		||||
            if retries < maxRetries then
 | 
			
		||||
                debugPrint("^3Timeout für Event " .. eventName .. " - Wiederhole...^7")
 | 
			
		||||
                attemptSend()
 | 
			
		||||
            else
 | 
			
		||||
                debugPrint("^1Maximale Wiederholungen für Event " .. eventName .. " erreicht^7")
 | 
			
		||||
                showNotification('Netzwerk-Fehler! Bitte versuche es später erneut.', 'error')
 | 
			
		||||
            end
 | 
			
		||||
        end)
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    attemptSend()
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Erweiterte Export-Funktionen für andere Resources
 | 
			
		||||
exports('hasLicense', function(licenseType)
 | 
			
		||||
    -- Diese Funktion kann von anderen Resources verwendet werden
 | 
			
		||||
    local PlayerData = QBCore.Functions.GetPlayerData()
 | 
			
		||||
    if not PlayerData or not PlayerData.citizenid then return false end
 | 
			
		||||
    
 | 
			
		||||
    -- Hier würde normalerweise eine Server-Anfrage gemacht werden
 | 
			
		||||
    -- Für jetzt geben wir false zurück
 | 
			
		||||
    return false
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
exports('showPlayerLicense', function(targetId, licenseType)
 | 
			
		||||
    -- Export für andere Resources um Lizenzen anzuzeigen
 | 
			
		||||
    if licenseType then
 | 
			
		||||
        TriggerServerEvent('license-system:server:requestSpecificLicense', targetId, licenseType)
 | 
			
		||||
    else
 | 
			
		||||
        showPlayerLicense(targetId)
 | 
			
		||||
    end
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
exports('openLicenseMenu', function()
 | 
			
		||||
    -- Export für andere Resources um das Lizenz-Menü zu öffnen
 | 
			
		||||
    openLicenseMenu()
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
-- Neuer Event-Handler für benutzerdefinierte Lizenzen
 | 
			
		||||
RegisterNetEvent('license-system:server:issueCustomLicense', function(targetId, licenseType, customData, classes)
 | 
			
		||||
    local src = source
 | 
			
		||||
    local Player = QBCore.Functions.GetPlayer(src)
 | 
			
		||||
    local TargetPlayer = QBCore.Functions.GetPlayer(targetId)
 | 
			
		||||
    
 | 
			
		||||
    if not Player or not TargetPlayer then
 | 
			
		||||
        debugPrint("Spieler nicht gefunden: " .. src .. " -> " .. targetId)
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    -- Berechtigung prüfen
 | 
			
		||||
    if not isAuthorized(Player.PlayerData.job.name) then
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, Config.Notifications.no_permission.message, Config.Notifications.no_permission.type)
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    -- Lizenz-Konfiguration prüfen
 | 
			
		||||
    local config = Config.LicenseTypes[licenseType]
 | 
			
		||||
    if not config then
 | 
			
		||||
        debugPrint("Unbekannter Lizenztyp: " .. licenseType)
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    debugPrint("Erstelle benutzerdefinierte Lizenz: " .. licenseType .. " für " .. TargetPlayer.PlayerData.citizenid)
 | 
			
		||||
    
 | 
			
		||||
    -- Benutzerdefinierte Daten validieren und bereinigen
 | 
			
		||||
    local validatedData = {}
 | 
			
		||||
    
 | 
			
		||||
    for _, field in ipairs(config.custom_fields or {}) do
 | 
			
		||||
        local value = customData[field.name]
 | 
			
		||||
        
 | 
			
		||||
        -- Pflichtfeld-Prüfung
 | 
			
		||||
        if field.required and (not value or value == "") then
 | 
			
		||||
            TriggerClientEvent('QBCore:Notify', src, "Feld '" .. field.label .. "' ist erforderlich", "error")
 | 
			
		||||
            return
 | 
			
		||||
        end
 | 
			
		||||
        
 | 
			
		||||
        -- Wert bereinigen und validieren
 | 
			
		||||
        if value and value ~= "" then
 | 
			
		||||
            value = string.gsub(value, "'", "''") -- SQL-Injection Schutz
 | 
			
		||||
            
 | 
			
		||||
            -- Typ-spezifische Validierung
 | 
			
		||||
            if field.type == "url" and not string.match(value, "^https?://") then
 | 
			
		||||
                TriggerClientEvent('QBCore:Notify', src, "Ungültige URL in Feld '" .. field.label .. "'", "error")
 | 
			
		||||
                return
 | 
			
		||||
            end
 | 
			
		||||
            
 | 
			
		||||
            if field.type == "date" and not string.match(value, "^%d%d%.%d%d%.%d%d%d%d$") then
 | 
			
		||||
                TriggerClientEvent('QBCore:Notify', src, "Ungültiges Datum in Feld '" .. field.label .. "'", "error")
 | 
			
		||||
                return
 | 
			
		||||
            end
 | 
			
		||||
            
 | 
			
		||||
            validatedData[field.name] = value
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    -- Klassen validieren
 | 
			
		||||
    local validatedClasses = {}
 | 
			
		||||
    if config.classes and classes then
 | 
			
		||||
        for _, class in ipairs(classes) do
 | 
			
		||||
            if config.classes[class.key] then
 | 
			
		||||
                table.insert(validatedClasses, class)
 | 
			
		||||
            end
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    -- Lizenz in Datenbank speichern
 | 
			
		||||
    local success = saveCustomLicenseToDB(
 | 
			
		||||
        TargetPlayer.PlayerData.citizenid,
 | 
			
		||||
        licenseType,
 | 
			
		||||
        Player.PlayerData.charinfo.firstname .. " " .. Player.PlayerData.charinfo.lastname,
 | 
			
		||||
        validatedData,
 | 
			
		||||
        validatedClasses
 | 
			
		||||
    )
 | 
			
		||||
    
 | 
			
		||||
    if success then
 | 
			
		||||
        debugPrint("Benutzerdefinierte Lizenz erfolgreich gespeichert")
 | 
			
		||||
        
 | 
			
		||||
        -- Cache invalidieren
 | 
			
		||||
        invalidateCache(TargetPlayer.PlayerData.citizenid, licenseType)
 | 
			
		||||
        
 | 
			
		||||
        -- Benachrichtigungen
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, Config.Notifications.license_issued.message, Config.Notifications.license_issued.type)
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', targetId, "Du hast eine " .. config.label .. " erhalten!", "success")
 | 
			
		||||
        
 | 
			
		||||
        -- Events
 | 
			
		||||
        TriggerClientEvent('license-system:client:licenseIssued', src, targetId, licenseType)
 | 
			
		||||
        TriggerClientEvent('license-system:client:refreshMenu', src)
 | 
			
		||||
        
 | 
			
		||||
        -- Log
 | 
			
		||||
        debugPrint("Lizenz ausgestellt: " .. licenseType .. " von " .. Player.PlayerData.name .. " an " .. TargetPlayer.PlayerData.name)
 | 
			
		||||
    else
 | 
			
		||||
        debugPrint("Fehler beim Speichern der benutzerdefinierten Lizenz")
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, "Fehler beim Ausstellen der Lizenz", "error")
 | 
			
		||||
    end
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
-- Funktion zum Speichern benutzerdefinierter Lizenzen
 | 
			
		||||
function saveCustomLicenseToDB(citizenid, licenseType, issuedBy, customData, classes)
 | 
			
		||||
    local config = Config.LicenseTypes[licenseType]
 | 
			
		||||
    if not config then return false end
 | 
			
		||||
    
 | 
			
		||||
    -- Ablaufdatum berechnen
 | 
			
		||||
    local expireDate = nil
 | 
			
		||||
    if config.validity_days then
 | 
			
		||||
        expireDate = os.date('%Y-%m-%d %H:%M:%S', os.time() + (config.validity_days * 24 * 60 * 60))
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    -- Holder-Name aus Custom-Data oder Standard
 | 
			
		||||
    local holderName = customData.holder_name or "Unbekannt"
 | 
			
		||||
    
 | 
			
		||||
    return safeDBOperation(function()
 | 
			
		||||
        -- Alte Lizenzen deaktivieren
 | 
			
		||||
        local deactivateQuery = "UPDATE player_licenses SET is_active = 0 WHERE citizenid = ? AND license_type = ?"
 | 
			
		||||
        MySQL.query.await(deactivateQuery, {citizenid, licenseType})
 | 
			
		||||
        
 | 
			
		||||
        -- Neue Lizenz einfügen
 | 
			
		||||
        local insertQuery = [[
 | 
			
		||||
            INSERT INTO player_licenses 
 | 
			
		||||
            (citizenid, license_type, name, issue_date, expire_date, issued_by, is_active, classes, custom_data, holder_name) 
 | 
			
		||||
            VALUES (?, ?, ?, NOW(), ?, ?, 1, ?, ?, ?)
 | 
			
		||||
        ]]
 | 
			
		||||
        
 | 
			
		||||
        local result = MySQL.insert.await(insertQuery, {
 | 
			
		||||
            citizenid,
 | 
			
		||||
            licenseType,
 | 
			
		||||
            config.label,
 | 
			
		||||
            expireDate,
 | 
			
		||||
            issuedBy,
 | 
			
		||||
            json.encode(classes or {}),
 | 
			
		||||
            json.encode(customData or {}),
 | 
			
		||||
            holderName
 | 
			
		||||
        })
 | 
			
		||||
        
 | 
			
		||||
        return result and result > 0
 | 
			
		||||
    end, "Benutzerdefinierte Lizenz speichern")
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Erweiterte Lizenz-Abruf-Funktion
 | 
			
		||||
function getLicenseFromDB(citizenid, licenseType, skipCache)
 | 
			
		||||
    -- Cache prüfen
 | 
			
		||||
    local cacheKey = citizenid .. "_" .. licenseType
 | 
			
		||||
    if not skipCache and licenseCache[cacheKey] then
 | 
			
		||||
        debugPrint("Lizenz aus Cache geladen: " .. licenseType)
 | 
			
		||||
        return licenseCache[cacheKey]
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    local license = safeDBOperation(function()
 | 
			
		||||
        local query = [[
 | 
			
		||||
            SELECT *, 
 | 
			
		||||
                   CASE 
 | 
			
		||||
                       WHEN expire_date IS NULL THEN 1
 | 
			
		||||
                       WHEN expire_date > NOW() THEN 1
 | 
			
		||||
                       ELSE 0
 | 
			
		||||
                   END as is_valid
 | 
			
		||||
            FROM player_licenses 
 | 
			
		||||
            WHERE citizenid = ? AND license_type = ? AND is_active = 1 
 | 
			
		||||
            ORDER BY created_at DESC 
 | 
			
		||||
            LIMIT 1
 | 
			
		||||
        ]]
 | 
			
		||||
        
 | 
			
		||||
        local result = MySQL.query.await(query, {citizenid, licenseType})
 | 
			
		||||
        return result and result[1] or nil
 | 
			
		||||
    end, "Lizenz abrufen")
 | 
			
		||||
    
 | 
			
		||||
    if license then
 | 
			
		||||
        -- Custom-Data und Classes parsen
 | 
			
		||||
        if license.custom_data then
 | 
			
		||||
            license.custom_data_parsed = json.decode(license.custom_data)
 | 
			
		||||
        end
 | 
			
		||||
        
 | 
			
		||||
        if license.classes then
 | 
			
		||||
            license.classes_parsed = json.decode(license.classes)
 | 
			
		||||
        end
 | 
			
		||||
        
 | 
			
		||||
        -- Cache speichern
 | 
			
		||||
        licenseCache[cacheKey] = license
 | 
			
		||||
        debugPrint("Lizenz in Cache gespeichert: " .. licenseType)
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    return license
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
-- Add to client/main.lua
 | 
			
		||||
RegisterNetEvent('license-system:client:openCamera', function(targetId, licenseData)
 | 
			
		||||
    debugPrint("Event erhalten: openCamera für Spieler " .. tostring(targetId))
 | 
			
		||||
    
 | 
			
		||||
    -- Store the data temporarily
 | 
			
		||||
    currentLicenseData = licenseData
 | 
			
		||||
    currentTargetId = targetId
 | 
			
		||||
    
 | 
			
		||||
    SendNUIMessage({
 | 
			
		||||
        action = 'openCamera',
 | 
			
		||||
        citizenid = QBCore.Functions.GetPlayerData().citizenid
 | 
			
		||||
    })
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
-- Enhance the NUI callback for photo saving
 | 
			
		||||
RegisterNUICallback('savePhoto', function(data, cb)
 | 
			
		||||
    debugPrint("NUI Callback: savePhoto")
 | 
			
		||||
    
 | 
			
		||||
    if data.photo and currentLicenseData and currentTargetId then
 | 
			
		||||
        -- Add photo to license data
 | 
			
		||||
        currentLicenseData.photo_url = data.photo
 | 
			
		||||
        
 | 
			
		||||
        -- Issue the license with the photo
 | 
			
		||||
        TriggerServerEvent('license-system:server:issueManualLicense', currentTargetId, currentLicenseData)
 | 
			
		||||
        
 | 
			
		||||
        -- Reset temporary data
 | 
			
		||||
        currentLicenseData = nil
 | 
			
		||||
        currentTargetId = nil
 | 
			
		||||
        
 | 
			
		||||
        if cb and type(cb) == "function" then
 | 
			
		||||
            cb('ok')
 | 
			
		||||
        end
 | 
			
		||||
    else
 | 
			
		||||
        debugPrint("^1Fehler: Foto-Daten unvollständig oder keine aktuelle Lizenz-Ausstellung^7")
 | 
			
		||||
        
 | 
			
		||||
        if cb and type(cb) == "function" then
 | 
			
		||||
            cb('error')
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
debugPrint("License-System Server erweitert geladen (Custom License Support)")
 | 
			
		||||
| 
						 | 
				
			
			@ -722,11 +722,268 @@ RegisterNetEvent('license-system:server:revokeLicense', function(targetId, licen
 | 
			
		|||
        debugPrint("Lizenz " .. licenseType .. " entzogen von " .. issuerName .. " für " .. targetName)
 | 
			
		||||
    else
 | 
			
		||||
        debugPrint("^1Fehler beim Entziehen der Lizenz^7")
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Entziehen der Lizenz!', 
 | 
			
		||||
        'error')
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Entziehen der Lizenz!', 'error')
 | 
			
		||||
    end
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
-- EVENT HANDLER: Lizenz reaktivieren
 | 
			
		||||
RegisterNetEvent('license-system:server:reactivateLicense', function(targetId, licenseType)
 | 
			
		||||
    local src = source
 | 
			
		||||
    debugPrint("=== Event: reactivateLicense ===")
 | 
			
		||||
    debugPrint("Source: " .. src .. ", Target: " .. targetId .. ", Type: " .. licenseType)
 | 
			
		||||
    
 | 
			
		||||
    -- Check if player has permission to reactivate this license type
 | 
			
		||||
    local Player = QBCore.Functions.GetPlayer(src)
 | 
			
		||||
    if not Player then return end
 | 
			
		||||
    
 | 
			
		||||
    local job = Player.PlayerData.job.name
 | 
			
		||||
    local canReactivate = false
 | 
			
		||||
    
 | 
			
		||||
    -- Check if job can reactivate this license type
 | 
			
		||||
    if Config.ReactivationPermissions and Config.ReactivationPermissions[job] then
 | 
			
		||||
        for _, allowedType in ipairs(Config.ReactivationPermissions[job]) do
 | 
			
		||||
            if allowedType == licenseType then
 | 
			
		||||
                canReactivate = true
 | 
			
		||||
                break
 | 
			
		||||
            end
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    if not canReactivate then
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, 'Du darfst diesen Lizenztyp nicht reaktivieren!', 'error')
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    -- Get target player
 | 
			
		||||
    local targetPlayer = QBCore.Functions.GetPlayer(targetId)
 | 
			
		||||
    if not targetPlayer then
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, 'Spieler nicht gefunden!', 'error')
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    local targetCitizenId = targetPlayer.PlayerData.citizenid
 | 
			
		||||
    
 | 
			
		||||
    -- Find the most recent license of this type (even if inactive)
 | 
			
		||||
    local query = [[
 | 
			
		||||
        SELECT * FROM player_licenses 
 | 
			
		||||
        WHERE citizenid = ? AND license_type = ? 
 | 
			
		||||
        ORDER BY created_at DESC 
 | 
			
		||||
        LIMIT 1
 | 
			
		||||
    ]]
 | 
			
		||||
    
 | 
			
		||||
    local result = MySQL.query.await(query, {targetCitizenId, licenseType})
 | 
			
		||||
    
 | 
			
		||||
    if not result or #result == 0 then
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, 'Keine Lizenz dieses Typs gefunden!', 'error')
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    -- Reactivate the license
 | 
			
		||||
    local updateQuery = "UPDATE player_licenses SET is_active = 1 WHERE id = ?"
 | 
			
		||||
    local success = MySQL.update.await(updateQuery, {result[1].id})
 | 
			
		||||
    
 | 
			
		||||
    if success then
 | 
			
		||||
        -- Invalidate cache
 | 
			
		||||
        invalidateCache(targetCitizenId, licenseType)
 | 
			
		||||
        
 | 
			
		||||
        local targetName = getPlayerName(targetId)
 | 
			
		||||
        local issuerName = getPlayerName(src)
 | 
			
		||||
        local config = Config.LicenseTypes[licenseType]
 | 
			
		||||
        
 | 
			
		||||
        -- Notifications
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, 'Lizenz erfolgreich reaktiviert für ' .. targetName, 'success')
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', targetId, 'Deine ' .. (config.label or licenseType) .. ' wurde reaktiviert!', 'success')
 | 
			
		||||
        
 | 
			
		||||
        -- Events
 | 
			
		||||
        TriggerClientEvent('license-system:client:licenseReactivated', src, targetId, licenseType)
 | 
			
		||||
        TriggerClientEvent('license-system:client:refreshMenu', src)
 | 
			
		||||
        
 | 
			
		||||
        -- Log
 | 
			
		||||
        debugPrint("Lizenz " .. licenseType .. " reaktiviert von " .. issuerName .. " für " .. targetName)
 | 
			
		||||
    else
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Reaktivieren der Lizenz!', 'error')
 | 
			
		||||
    end
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
-- EVENT HANDLER: Manuelle Lizenz ausstellen
 | 
			
		||||
RegisterNetEvent('license-system:server:issueManualLicense', function(targetId, licenseData)
 | 
			
		||||
    local src = source
 | 
			
		||||
    debugPrint("=== Event: issueManualLicense ===")
 | 
			
		||||
    debugPrint("Source: " .. src .. ", Target: " .. targetId)
 | 
			
		||||
    
 | 
			
		||||
    if not hasPermission(src) then
 | 
			
		||||
        debugPrint("Keine Berechtigung für Spieler: " .. src)
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, Config.Notifications.no_permission.message, Config.Notifications.no_permission.type)
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    local targetPlayer = QBCore.Functions.GetPlayer(targetId)
 | 
			
		||||
    if not targetPlayer then
 | 
			
		||||
        debugPrint("Ziel-Spieler nicht gefunden: " .. targetId)
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, 'Spieler nicht gefunden!', 'error')
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    local issuerPlayer = QBCore.Functions.GetPlayer(src)
 | 
			
		||||
    if not issuerPlayer then
 | 
			
		||||
        debugPrint("Aussteller nicht gefunden: " .. src)
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    local targetCitizenId = targetPlayer.PlayerData.citizenid
 | 
			
		||||
    local issuerCitizenId = issuerPlayer.PlayerData.citizenid
 | 
			
		||||
    
 | 
			
		||||
    -- Check if license type is valid
 | 
			
		||||
    if not Config.LicenseTypes[licenseData.license_type] then
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, 'Ungültiger Lizenztyp!', 'error')
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    -- Save photo if provided
 | 
			
		||||
    if licenseData.photo_url then
 | 
			
		||||
        -- Here you would save the photo to your storage system
 | 
			
		||||
        -- For example, to a folder or database
 | 
			
		||||
        -- This is just a placeholder
 | 
			
		||||
        debugPrint("Foto für Lizenz vorhanden")
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    -- First deactivate any existing licenses of this type
 | 
			
		||||
    local deactivateQuery = "UPDATE player_licenses SET is_active = 0 WHERE citizenid = ? AND license_type = ?"
 | 
			
		||||
    MySQL.query.await(deactivateQuery, {targetCitizenId, licenseData.license_type})
 | 
			
		||||
    
 | 
			
		||||
    -- Insert into database with manual data
 | 
			
		||||
    local query = [[
 | 
			
		||||
        INSERT INTO player_licenses 
 | 
			
		||||
        (citizenid, license_type, name, birthday, gender, issue_date, expire_date, issued_by, is_active, photo_url, created_at) 
 | 
			
		||||
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?)
 | 
			
		||||
    ]]
 | 
			
		||||
    
 | 
			
		||||
    local createdAt = os.time()
 | 
			
		||||
    
 | 
			
		||||
    local insertData = {
 | 
			
		||||
        targetCitizenId,
 | 
			
		||||
        licenseData.license_type,
 | 
			
		||||
        licenseData.name,
 | 
			
		||||
        licenseData.birthday,
 | 
			
		||||
        licenseData.gender,
 | 
			
		||||
        licenseData.issue_date,
 | 
			
		||||
        licenseData.expire_date,
 | 
			
		||||
        issuerCitizenId,
 | 
			
		||||
        licenseData.photo_url or '',
 | 
			
		||||
        createdAt
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    local result = MySQL.insert.await(query, insertData)
 | 
			
		||||
    
 | 
			
		||||
    if result then
 | 
			
		||||
        -- Invalidate cache
 | 
			
		||||
        invalidateCache(targetCitizenId)
 | 
			
		||||
        
 | 
			
		||||
        local targetName = getPlayerName(targetId)
 | 
			
		||||
        local issuerName = getPlayerName(src)
 | 
			
		||||
        local config = Config.LicenseTypes[licenseData.license_type]
 | 
			
		||||
        
 | 
			
		||||
        -- Notifications
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, 'Lizenz erfolgreich ausgestellt für ' .. targetName, 'success')
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', targetId, 'Du hast eine neue Lizenz erhalten: ' .. config.label, 'success')
 | 
			
		||||
        
 | 
			
		||||
        -- Events
 | 
			
		||||
        TriggerClientEvent('license-system:client:licenseIssued', src, targetId, licenseData.license_type)
 | 
			
		||||
        TriggerClientEvent('license-system:client:refreshMenu', src)
 | 
			
		||||
        
 | 
			
		||||
        -- Log
 | 
			
		||||
        debugPrint("Lizenz " .. licenseData.license_type .. " manuell ausgestellt von " .. issuerName .. " für " .. targetName)
 | 
			
		||||
    else
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Ausstellen der Lizenz!', 'error')
 | 
			
		||||
    end
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
-- EVENT HANDLER: Alle Lizenzen eines Spielers anfordern (inkl. inaktive)
 | 
			
		||||
RegisterNetEvent('license-system:server:requestAllPlayerLicenses', function(targetId, includeInactive)
 | 
			
		||||
    local src = source
 | 
			
		||||
    debugPrint("=== Event: requestAllPlayerLicenses ===")
 | 
			
		||||
    debugPrint("Source: " .. src .. ", Target: " .. targetId .. ", IncludeInactive: " .. tostring(includeInactive))
 | 
			
		||||
    
 | 
			
		||||
    if not hasPermission(src) then
 | 
			
		||||
        debugPrint("Keine Berechtigung für Spieler: " .. src)
 | 
			
		||||
        TriggerClientEvent('license-system:client:receiveAllPlayerLicenses', src, {}, targetId, "Unbekannt")
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    local targetPlayer = QBCore.Functions.GetPlayer(targetId)
 | 
			
		||||
    if not targetPlayer then
 | 
			
		||||
        debugPrint("Ziel-Spieler nicht gefunden: " .. targetId)
 | 
			
		||||
        TriggerClientEvent('license-system:client:receiveAllPlayerLicenses', src, {}, targetId, "Unbekannt")
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    local citizenid = targetPlayer.PlayerData.citizenid
 | 
			
		||||
    local targetName = getPlayerName(targetId)
 | 
			
		||||
    
 | 
			
		||||
    -- Query to get all licenses, including inactive ones if requested
 | 
			
		||||
    local query = "SELECT * FROM player_licenses WHERE citizenid = ? ORDER BY license_type, created_at DESC"
 | 
			
		||||
    
 | 
			
		||||
    local result = MySQL.query.await(query, {citizenid})
 | 
			
		||||
    
 | 
			
		||||
    if not result or #result == 0 then
 | 
			
		||||
        debugPrint("Keine Lizenzen gefunden für: " .. citizenid)
 | 
			
		||||
        TriggerClientEvent('license-system:client:receiveAllPlayerLicenses', src, {}, targetId, targetName)
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    -- Process licenses
 | 
			
		||||
    local licenses = {}
 | 
			
		||||
    local seenTypes = {}
 | 
			
		||||
    
 | 
			
		||||
    for _, license in ipairs(result) do
 | 
			
		||||
        -- If includeInactive is true, add all licenses
 | 
			
		||||
        -- Otherwise, only add the newest license per type
 | 
			
		||||
        local shouldAdd = includeInactive or not seenTypes[license.license_type]
 | 
			
		||||
        if shouldAdd then
 | 
			
		||||
            seenTypes[license.license_type] = true
 | 
			
		||||
            
 | 
			
		||||
            -- Add holder name
 | 
			
		||||
            license.holder_name = targetName
 | 
			
		||||
            
 | 
			
		||||
            -- Add issuer name
 | 
			
		||||
            if license.issued_by then
 | 
			
		||||
                local issuerQuery = "SELECT charinfo FROM players WHERE citizenid = ?"
 | 
			
		||||
                local issuerResult = MySQL.query.await(issuerQuery, {license.issued_by})
 | 
			
		||||
                
 | 
			
		||||
                if issuerResult and #issuerResult > 0 then
 | 
			
		||||
                    license.issued_by_name = extractPlayerName(issuerResult[1].charinfo)
 | 
			
		||||
                else
 | 
			
		||||
                    license.issued_by_name = "System"
 | 
			
		||||
                end
 | 
			
		||||
            else
 | 
			
		||||
                license.issued_by_name = "System"
 | 
			
		||||
            end
 | 
			
		||||
            
 | 
			
		||||
            -- Parse classes
 | 
			
		||||
            if license.classes then
 | 
			
		||||
                local success, classes = pcall(json.decode, license.classes)
 | 
			
		||||
                if success and type(classes) == "table" then
 | 
			
		||||
                    license.classes = classes
 | 
			
		||||
                else
 | 
			
		||||
                    license.classes = {}
 | 
			
		||||
                end
 | 
			
		||||
            else
 | 
			
		||||
                license.classes = {}
 | 
			
		||||
            end
 | 
			
		||||
            
 | 
			
		||||
            -- Normalize is_active
 | 
			
		||||
            if license.is_active == nil then
 | 
			
		||||
                license.is_active = 1
 | 
			
		||||
            end
 | 
			
		||||
            
 | 
			
		||||
            table.insert(licenses, license)
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    debugPrint("Sende " .. #licenses .. " Lizenzen für " .. targetName)
 | 
			
		||||
    TriggerClientEvent('license-system:client:receiveAllPlayerLicenses', src, licenses, targetId, targetName)
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
-- EXPORT FUNKTIONEN (KORRIGIERT)
 | 
			
		||||
exports('hasLicense', function(citizenid, licenseType)
 | 
			
		||||
    if not citizenid or not licenseType then return false end
 | 
			
		||||
| 
						 | 
				
			
			@ -984,379 +1241,6 @@ function table.count(t)
 | 
			
		|||
    return count
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Add to server/main.lua
 | 
			
		||||
RegisterNetEvent('license-system:server:reactivateLicense', function(targetId, licenseType)
 | 
			
		||||
    local src = source
 | 
			
		||||
    debugPrint("=== Event: reactivateLicense ===")
 | 
			
		||||
    debugPrint("Source: " .. src .. ", Target: " .. targetId .. ", Type: " .. licenseType)
 | 
			
		||||
    
 | 
			
		||||
    -- Check if player has permission to reactivate this license type
 | 
			
		||||
    local Player = QBCore.Functions.GetPlayer(src)
 | 
			
		||||
    if not Player then return end
 | 
			
		||||
    
 | 
			
		||||
    local job = Player.PlayerData.job.name
 | 
			
		||||
    local canReactivate = false
 | 
			
		||||
    
 | 
			
		||||
    -- Check if job can reactivate this license type
 | 
			
		||||
    if Config.ReactivationPermissions and Config.ReactivationPermissions[job] then
 | 
			
		||||
        for _, allowedType in ipairs(Config.ReactivationPermissions[job]) do
 | 
			
		||||
            if allowedType == licenseType then
 | 
			
		||||
                canReactivate = true
 | 
			
		||||
                break
 | 
			
		||||
            end
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    if not canReactivate then
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, 'Du darfst diesen Lizenztyp nicht reaktivieren!', 'error')
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    -- Get target player
 | 
			
		||||
    local targetPlayer = QBCore.Functions.GetPlayer(targetId)
 | 
			
		||||
    if not targetPlayer then
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, 'Spieler nicht gefunden!', 'error')
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    local targetCitizenId = targetPlayer.PlayerData.citizenid
 | 
			
		||||
    
 | 
			
		||||
    -- Find the most recent license of this type (even if inactive)
 | 
			
		||||
    local query = [[
 | 
			
		||||
        SELECT * FROM player_licenses 
 | 
			
		||||
        WHERE citizenid = ? AND license_type = ? 
 | 
			
		||||
        ORDER BY created_at DESC 
 | 
			
		||||
        LIMIT 1
 | 
			
		||||
    ]]
 | 
			
		||||
    
 | 
			
		||||
    local result = MySQL.query.await(query, {targetCitizenId, licenseType})
 | 
			
		||||
    
 | 
			
		||||
    if not result or #result == 0 then
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, 'Keine Lizenz dieses Typs gefunden!', 'error')
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    -- Reactivate the license
 | 
			
		||||
    local updateQuery = "UPDATE player_licenses SET is_active = 1 WHERE id = ?"
 | 
			
		||||
    local success = MySQL.update.await(updateQuery, {result[1].id})
 | 
			
		||||
    
 | 
			
		||||
    if success then
 | 
			
		||||
        -- Invalidate cache
 | 
			
		||||
        invalidateCache(targetCitizenId, licenseType)
 | 
			
		||||
        
 | 
			
		||||
        local targetName = getPlayerName(targetId)
 | 
			
		||||
        local issuerName = getPlayerName(src)
 | 
			
		||||
        local config = Config.LicenseTypes[licenseType]
 | 
			
		||||
        
 | 
			
		||||
        -- Notifications
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, 'Lizenz erfolgreich reaktiviert für ' .. targetName, 'success')
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', targetId, 'Deine ' .. (config.label or licenseType) .. ' wurde reaktiviert!', 'success')
 | 
			
		||||
        
 | 
			
		||||
        -- Events
 | 
			
		||||
        TriggerClientEvent('license-system:client:licenseReactivated', src, targetId, licenseType)
 | 
			
		||||
        TriggerClientEvent('license-system:client:refreshMenu', src)
 | 
			
		||||
        
 | 
			
		||||
        -- Log
 | 
			
		||||
        debugPrint("Lizenz " .. licenseType .. " reaktiviert von " .. issuerName .. " für " .. targetName)
 | 
			
		||||
    else
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Reaktivieren der Lizenz!', 'error')
 | 
			
		||||
    end
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
-- Add to client/main.lua
 | 
			
		||||
-- Add a reactivation option to the player license menu
 | 
			
		||||
local function openReactivateLicenseMenu(targetId, targetName)
 | 
			
		||||
    debugPrint("Öffne Lizenz-Reaktivierungs-Menü für: " .. targetName)
 | 
			
		||||
    
 | 
			
		||||
    -- Request all licenses including inactive ones
 | 
			
		||||
    TriggerServerEvent('license-system:server:requestAllPlayerLicenses', targetId, true)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- New event to receive all licenses including inactive ones
 | 
			
		||||
RegisterNetEvent('license-system:client:receiveAllPlayerLicenses', function(licenses, targetId, targetName)
 | 
			
		||||
    debugPrint("=== Event: receiveAllPlayerLicenses ===")
 | 
			
		||||
    debugPrint("Erhaltene Lizenzen: " .. #licenses)
 | 
			
		||||
    
 | 
			
		||||
    local menuOptions = {}
 | 
			
		||||
    
 | 
			
		||||
    if licenses and #licenses > 0 then
 | 
			
		||||
        for _, license in ipairs(licenses) do
 | 
			
		||||
            if license.is_active == 0 then -- Only show inactive licenses
 | 
			
		||||
                local licenseConfig = Config.LicenseTypes[license.license_type] or {
 | 
			
		||||
                    label = license.license_type,
 | 
			
		||||
                    icon = 'fas fa-id-card',
 | 
			
		||||
                    color = '#667eea'
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                table.insert(menuOptions, {
 | 
			
		||||
                    title = licenseConfig.label .. ' ❌',
 | 
			
		||||
                    description = 'Status: Ungültig | Ausgestellt: ' .. (license.issue_date or 'Unbekannt'),
 | 
			
		||||
                    icon = licenseConfig.icon,
 | 
			
		||||
                    onSelect = function()
 | 
			
		||||
                        -- Confirmation dialog
 | 
			
		||||
                        lib.registerContext({
 | 
			
		||||
                            id = 'confirm_reactivate_license',
 | 
			
		||||
                            title = 'Lizenz reaktivieren bestätigen',
 | 
			
		||||
                            options = {
 | 
			
		||||
                                {
 | 
			
		||||
                                    title = 'Spieler: ' .. targetName,
 | 
			
		||||
                                    disabled = true,
 | 
			
		||||
                                    icon = 'fas fa-user'
 | 
			
		||||
                                },
 | 
			
		||||
                                {
 | 
			
		||||
                                    title = 'Lizenztyp: ' .. licenseConfig.label,
 | 
			
		||||
                                    disabled = true,
 | 
			
		||||
                                    icon = licenseConfig.icon
 | 
			
		||||
                                },
 | 
			
		||||
                                {
 | 
			
		||||
                                    title = '─────────────────',
 | 
			
		||||
                                    disabled = true
 | 
			
		||||
                                },
 | 
			
		||||
                                {
 | 
			
		||||
                                    title = '✅ Bestätigen',
 | 
			
		||||
                                    description = 'Lizenz jetzt reaktivieren',
 | 
			
		||||
                                    icon = 'fas fa-check',
 | 
			
		||||
                                    onSelect = function()
 | 
			
		||||
                                        debugPrint("Sende Lizenz-Reaktivierung an Server...")
 | 
			
		||||
                                        TriggerServerEvent('license-system:server:reactivateLicense', targetId, license.license_type)
 | 
			
		||||
                                        lib.hideContext()
 | 
			
		||||
                                    end
 | 
			
		||||
                                },
 | 
			
		||||
                                {
 | 
			
		||||
                                    title = '❌ Abbrechen',
 | 
			
		||||
                                    description = 'Vorgang abbrechen',
 | 
			
		||||
                                    icon = 'fas fa-times',
 | 
			
		||||
                                    onSelect = function()
 | 
			
		||||
                                        openReactivateLicenseMenu(targetId, targetName)
 | 
			
		||||
                                    end
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        })
 | 
			
		||||
                        
 | 
			
		||||
                        lib.showContext('confirm_reactivate_license')
 | 
			
		||||
                    end
 | 
			
		||||
                })
 | 
			
		||||
            end
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    if #menuOptions == 0 then
 | 
			
		||||
        table.insert(menuOptions, {
 | 
			
		||||
            title = 'Keine inaktiven Lizenzen',
 | 
			
		||||
            description = 'Dieser Spieler hat keine inaktiven Lizenzen',
 | 
			
		||||
            icon = 'fas fa-exclamation-triangle',
 | 
			
		||||
            disabled = true
 | 
			
		||||
        })
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    table.insert(menuOptions, {
 | 
			
		||||
        title = '← Zurück',
 | 
			
		||||
        icon = 'fas fa-arrow-left',
 | 
			
		||||
        onSelect = function()
 | 
			
		||||
            openPlayerLicenseMenu(targetId, targetName)
 | 
			
		||||
        end
 | 
			
		||||
    })
 | 
			
		||||
    
 | 
			
		||||
    lib.registerContext({
 | 
			
		||||
        id = 'reactivate_license',
 | 
			
		||||
        title = 'Lizenz reaktivieren: ' .. targetName,
 | 
			
		||||
        options = menuOptions
 | 
			
		||||
    })
 | 
			
		||||
    
 | 
			
		||||
    lib.showContext('reactivate_license')
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
-- Add this option to the player license menu
 | 
			
		||||
-- In openPlayerLicenseMenu function, add:
 | 
			
		||||
table.insert(menuOptions, {
 | 
			
		||||
    title = 'Lizenz reaktivieren',
 | 
			
		||||
    description = 'Eine inaktive Lizenz wieder aktivieren',
 | 
			
		||||
    icon = 'fas fa-redo',
 | 
			
		||||
    onSelect = function()
 | 
			
		||||
        openReactivateLicenseMenu(targetId, targetName)
 | 
			
		||||
    end
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
-- Add to server/main.lua
 | 
			
		||||
RegisterNetEvent('license-system:server:issueManualLicense', function(targetId, licenseData)
 | 
			
		||||
    local src = source
 | 
			
		||||
    debugPrint("=== Event: issueManualLicense ===")
 | 
			
		||||
    debugPrint("Source: " .. src .. ", Target: " .. targetId)
 | 
			
		||||
    
 | 
			
		||||
    if not hasPermission(src) then
 | 
			
		||||
        debugPrint("Keine Berechtigung für Spieler: " .. src)
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, Config.Notifications.no_permission.message, Config.Notifications.no_permission.type)
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    local targetPlayer = QBCore.Functions.GetPlayer(targetId)
 | 
			
		||||
    if not targetPlayer then
 | 
			
		||||
        debugPrint("Ziel-Spieler nicht gefunden: " .. targetId)
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, 'Spieler nicht gefunden!', 'error')
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    local issuerPlayer = QBCore.Functions.GetPlayer(src)
 | 
			
		||||
    if not issuerPlayer then
 | 
			
		||||
        debugPrint("Aussteller nicht gefunden: " .. src)
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    local targetCitizenId = targetPlayer.PlayerData.citizenid
 | 
			
		||||
    local issuerCitizenId = issuerPlayer.PlayerData.citizenid
 | 
			
		||||
    
 | 
			
		||||
    -- Check if license type is valid
 | 
			
		||||
    if not Config.LicenseTypes[licenseData.license_type] then
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, 'Ungültiger Lizenztyp!', 'error')
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    -- Save photo if provided
 | 
			
		||||
    if licenseData.photo_url then
 | 
			
		||||
        -- Here you would save the photo to your storage system
 | 
			
		||||
        -- For example, to a folder or database
 | 
			
		||||
        -- This is just a placeholder
 | 
			
		||||
        debugPrint("Foto für Lizenz vorhanden")
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    -- Insert into database with manual data
 | 
			
		||||
    local query = [[
 | 
			
		||||
        INSERT INTO player_licenses 
 | 
			
		||||
        (citizenid, license_type, name, birthday, gender, issue_date, expire_date, issued_by, is_active, photo_url, created_at) 
 | 
			
		||||
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?)
 | 
			
		||||
    ]]
 | 
			
		||||
    
 | 
			
		||||
    local createdAt = os.time()
 | 
			
		||||
    
 | 
			
		||||
    local insertData = {
 | 
			
		||||
        targetCitizenId,
 | 
			
		||||
        licenseData.license_type,
 | 
			
		||||
        licenseData.name,
 | 
			
		||||
        licenseData.birthday,
 | 
			
		||||
        licenseData.gender,
 | 
			
		||||
        licenseData.issue_date,
 | 
			
		||||
        licenseData.expire_date,
 | 
			
		||||
        issuerCitizenId,
 | 
			
		||||
        licenseData.photo_url or '',
 | 
			
		||||
        createdAt
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    -- First deactivate any existing licenses of this type
 | 
			
		||||
    local deactivateQuery = "UPDATE player_licenses SET is_active = 0 WHERE citizenid = ? AND license_type = ?"
 | 
			
		||||
    MySQL.query.await(deactivateQuery, {targetCitizenId, licenseData.license_type})
 | 
			
		||||
    
 | 
			
		||||
    -- Then insert the new license
 | 
			
		||||
    local result = MySQL.insert.await(query, insertData)
 | 
			
		||||
    
 | 
			
		||||
    if result then
 | 
			
		||||
        -- Invalidate cache
 | 
			
		||||
        invalidateCache(targetCitizenId)
 | 
			
		||||
        
 | 
			
		||||
        local targetName = getPlayerName(targetId)
 | 
			
		||||
        local issuerName = getPlayerName(src)
 | 
			
		||||
        local config = Config.LicenseTypes[licenseData.license_type]
 | 
			
		||||
        
 | 
			
		||||
        -- Notifications
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, 'Lizenz erfolgreich ausgestellt für ' .. targetName, 'success')
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', targetId, 'Du hast eine neue Lizenz erhalten: ' .. config.label, 'success')
 | 
			
		||||
        
 | 
			
		||||
        -- Events
 | 
			
		||||
        TriggerClientEvent('license-system:client:licenseIssued', src, targetId, licenseData.license_type)
 | 
			
		||||
        TriggerClientEvent('license-system:client:refreshMenu', src)
 | 
			
		||||
        
 | 
			
		||||
        -- Log
 | 
			
		||||
        debugPrint("Lizenz " .. licenseData.license_type .. " manuell ausgestellt von " .. issuerName .. " für " .. targetName)
 | 
			
		||||
    else
 | 
			
		||||
        TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Ausstellen der Lizenz!', 'error')
 | 
			
		||||
    end
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
-- Add a new event to get all licenses including inactive ones
 | 
			
		||||
RegisterNetEvent('license-system:server:requestAllPlayerLicenses', function(targetId, includeInactive)
 | 
			
		||||
    local src = source
 | 
			
		||||
    debugPrint("=== Event: requestAllPlayerLicenses ===")
 | 
			
		||||
    debugPrint("Source: " .. src .. ", Target: " .. targetId .. ", IncludeInactive: " .. tostring(includeInactive))
 | 
			
		||||
    
 | 
			
		||||
    if not hasPermission(src) then
 | 
			
		||||
        debugPrint("Keine Berechtigung für Spieler: " .. src)
 | 
			
		||||
        TriggerClientEvent('license-system:client:receiveAllPlayerLicenses', src, {}, targetId, "Unbekannt")
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    local targetPlayer = QBCore.Functions.GetPlayer(targetId)
 | 
			
		||||
    if not targetPlayer then
 | 
			
		||||
        debugPrint("Ziel-Spieler nicht gefunden: " .. targetId)
 | 
			
		||||
        TriggerClientEvent('license-system:client:receiveAllPlayerLicenses', src, {}, targetId, "Unbekannt")
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    local citizenid = targetPlayer.PlayerData.citizenid
 | 
			
		||||
    local targetName = getPlayerName(targetId)
 | 
			
		||||
    
 | 
			
		||||
    -- Query to get all licenses, including inactive ones if requested
 | 
			
		||||
    local query = "SELECT * FROM player_licenses WHERE citizenid = ? ORDER BY license_type, created_at DESC"
 | 
			
		||||
    
 | 
			
		||||
    local result = MySQL.query.await(query, {citizenid})
 | 
			
		||||
    
 | 
			
		||||
    if not result or #result == 0 then
 | 
			
		||||
        debugPrint("Keine Lizenzen gefunden für: " .. citizenid)
 | 
			
		||||
        TriggerClientEvent('license-system:client:receiveAllPlayerLicenses', src, {}, targetId, targetName)
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    -- Process licenses
 | 
			
		||||
    local licenses = {}
 | 
			
		||||
    local seenTypes = {}
 | 
			
		||||
    
 | 
			
		||||
    for _, license in ipairs(result) do
 | 
			
		||||
        -- If includeInactive is true, add all licenses
 | 
			
		||||
        -- Otherwise, only add the newest license per type
 | 
			
		||||
        local shouldAdd = includeInactive or not seenTypes[license.license_type]
 | 
			
		||||
        
 | 
			
		||||
        if shouldAdd then
 | 
			
		||||
            seenTypes[license.license_type] = true
 | 
			
		||||
            
 | 
			
		||||
            -- Add holder name
 | 
			
		||||
            license.holder_name = targetName
 | 
			
		||||
            
 | 
			
		||||
            -- Add issuer name
 | 
			
		||||
            if license.issued_by then
 | 
			
		||||
                local issuerQuery = "SELECT charinfo FROM players WHERE citizenid = ?"
 | 
			
		||||
                local issuerResult = MySQL.query.await(issuerQuery, {license.issued_by})
 | 
			
		||||
                
 | 
			
		||||
                if issuerResult and #issuerResult > 0 then
 | 
			
		||||
                    license.issued_by_name = extractPlayerName(issuerResult[1].charinfo)
 | 
			
		||||
                else
 | 
			
		||||
                    license.issued_by_name = "System"
 | 
			
		||||
                end
 | 
			
		||||
            else
 | 
			
		||||
                license.issued_by_name = "System"
 | 
			
		||||
            end
 | 
			
		||||
            
 | 
			
		||||
            -- Parse classes
 | 
			
		||||
            if license.classes then
 | 
			
		||||
                local success, classes = pcall(json.decode, license.classes)
 | 
			
		||||
                if success and type(classes) == "table" then
 | 
			
		||||
                    license.classes = classes
 | 
			
		||||
                else
 | 
			
		||||
                    license.classes = {}
 | 
			
		||||
                end
 | 
			
		||||
            else
 | 
			
		||||
                license.classes = {}
 | 
			
		||||
            end
 | 
			
		||||
            
 | 
			
		||||
            -- Normalize is_active
 | 
			
		||||
            if license.is_active == nil then
 | 
			
		||||
                license.is_active = 1
 | 
			
		||||
            end
 | 
			
		||||
            
 | 
			
		||||
            table.insert(licenses, license)
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    debugPrint("Sende " .. #licenses .. " Lizenzen für " .. targetName)
 | 
			
		||||
    TriggerClientEvent('license-system:client:receiveAllPlayerLicenses', src, licenses, targetId, targetName)
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
debugPrint("License-System Server vollständig geladen (Status-Fix)")
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue