- aether-medical: Stationen/Belegung, Intensivstation, Blutbank, Fuhrpark, Nachrichten/Aushänge, Berichte und Textbausteine (server + NUI) - aether-admin: vollständiges Server-Admin-Panel als echtes FiveM-Resource, an aether-core gekoppelt (Rechte über aether_users.admin_group), mit WebRTC-Live-Kameras (Bildschirmfreigabe) und separatem Media-Server (SFU) - Prototypen admin/ vervollständigt - SQL-Schema aether_admin.sql - Docker-Compose mit MariaDB und idempotenter Auto-Migration (schema_migrations) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
565 lines
22 KiB
Lua
565 lines
22 KiB
Lua
-- =====================================================================
|
|
-- aether-police / Modul: Personalverwaltung
|
|
--
|
|
-- Rollen und Einzelrechte werden hier vergeben. Es gelten drei feste
|
|
-- Schranken, die sich nicht umgehen lassen:
|
|
-- 1. Niemand verwaltet Gleichrangige oder Höherstehende.
|
|
-- 2. Niemand vergibt eine Rolle auf oder über dem eigenen Rang.
|
|
-- 3. Niemand vergibt ein Recht, das er selbst nicht besitzt.
|
|
-- Zusätzlich kann sich niemand selbst bearbeiten.
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereModul({
|
|
id = 'personal', name = 'Personal', icon = '👮',
|
|
reihenfolge = 11, recht = 'personal.sehen',
|
|
beschreibung = 'Dienstgrade, Rechte und Personalakten',
|
|
})
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Hilfsfunktionen
|
|
-- ---------------------------------------------------------------------
|
|
|
|
--- Schlägt die nächste freie Dienstnummer vor
|
|
--- @return string
|
|
local function NaechsteDienstnummer()
|
|
local praefix = Polizei.Config.Kuerzel .. '-'
|
|
|
|
-- Alle vergebenen Nummern einmal holen, dann die erste Lücke suchen
|
|
local vergeben = {}
|
|
local zeilen = MySQL.query.await(
|
|
'SELECT dienstnummer FROM police_personal WHERE dienstnummer LIKE ?',
|
|
{ praefix .. '%' }) or {}
|
|
for _, zeile in ipairs(zeilen) do
|
|
vergeben[zeile.dienstnummer] = true
|
|
end
|
|
|
|
for nummer = 1, 9999 do
|
|
local kandidat = ('%s%04d'):format(praefix, nummer)
|
|
if not vergeben[kandidat] then return kandidat end
|
|
end
|
|
|
|
return praefix .. tostring(os.time()):sub(-4)
|
|
end
|
|
|
|
--- Wandelt eine JSON-Liste in eine Menge um
|
|
local function ZuMenge(roh)
|
|
local menge = {}
|
|
if not roh then return menge end
|
|
local erfolg, liste = pcall(json.decode, roh)
|
|
if erfolg and type(liste) == 'table' then
|
|
for _, wert in ipairs(liste) do menge[wert] = true end
|
|
end
|
|
return menge
|
|
end
|
|
|
|
--- Wandelt eine Menge zurück in eine sortierte Liste
|
|
local function ZuListe(menge)
|
|
local liste = {}
|
|
for wert in pairs(menge) do liste[#liste + 1] = wert end
|
|
table.sort(liste)
|
|
return liste
|
|
end
|
|
|
|
--- Prüft, ob ein Beamter einen anderen Personaldatensatz verwalten darf
|
|
--- @param beamter table Handelnder
|
|
--- @param ziel table Zeile aus police_personal
|
|
--- @return boolean, string|nil
|
|
local function DarfVerwalten(beamter, ziel)
|
|
if ziel.character == beamter.charakterId then
|
|
return false, 'Du kannst deinen eigenen Datensatz nicht bearbeiten.'
|
|
end
|
|
if not Rechte.StehtUeber(beamter, ziel.rolle) then
|
|
return false, 'Du kannst nur rangniedrigere Beamte verwalten.'
|
|
end
|
|
return true
|
|
end
|
|
|
|
-- =====================================================================
|
|
-- ÜBERSICHT
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereAktion('personal', 'liste', nil, function(quelle, beamter, daten)
|
|
local bedingungen, werte = { '1=1' }, {}
|
|
|
|
if daten.ehemalige then
|
|
bedingungen[#bedingungen + 1] = 'p.aktiv = 0'
|
|
else
|
|
bedingungen[#bedingungen + 1] = 'p.aktiv = 1'
|
|
end
|
|
|
|
if daten.abteilung and daten.abteilung ~= '' then
|
|
bedingungen[#bedingungen + 1] = 'p.abteilung = ?'
|
|
werte[#werte + 1] = daten.abteilung
|
|
end
|
|
if daten.rolle and daten.rolle ~= '' then
|
|
bedingungen[#bedingungen + 1] = 'p.rolle = ?'
|
|
werte[#werte + 1] = daten.rolle
|
|
end
|
|
if type(daten.begriff) == 'string' and #daten.begriff >= 2 then
|
|
bedingungen[#bedingungen + 1] =
|
|
'(p.dienstnummer LIKE ? OR c.firstname LIKE ? OR c.lastname LIKE ?)'
|
|
local muster = '%' .. daten.begriff .. '%'
|
|
werte[#werte + 1] = muster
|
|
werte[#werte + 1] = muster
|
|
werte[#werte + 1] = muster
|
|
end
|
|
|
|
local personal = MySQL.query.await(([[
|
|
SELECT p.`character`, p.dienstnummer, p.rolle, p.abteilung, p.eingestellt,
|
|
p.aktiv, p.notiz, p.rechte_extra, p.rechte_entzug,
|
|
c.firstname, c.lastname,
|
|
(SELECT COALESCE(SUM(d.sekunden), 0) FROM police_dienst d
|
|
WHERE d.`character` = p.`character`) AS dienstzeit
|
|
FROM police_personal p
|
|
JOIN aether_characters c ON c.id = p.`character`
|
|
WHERE %s
|
|
ORDER BY p.dienstnummer
|
|
LIMIT 200
|
|
]]):format(table.concat(bedingungen, ' AND ')), werte) or {}
|
|
|
|
-- Wer ist gerade im Dienst?
|
|
local imDienst = {}
|
|
for _, eintrag in pairs(Dienst.AlleImDienst()) do
|
|
imDienst[eintrag.charakterId] = true
|
|
end
|
|
|
|
for _, eintrag in ipairs(personal) do
|
|
local rolle = Polizei.HoleRolle(eintrag.rolle)
|
|
eintrag.rolleName = rolle and rolle.name or eintrag.rolle
|
|
eintrag.stufe = Polizei.Stufe(eintrag.rolle)
|
|
eintrag.imDienst = imDienst[eintrag.character] == true
|
|
-- Anzahl abweichender Einzelrechte für die Übersicht
|
|
local extra = ZuMenge(eintrag.rechte_extra)
|
|
local entzug = ZuMenge(eintrag.rechte_entzug)
|
|
local anzahl = 0
|
|
for _ in pairs(extra) do anzahl = anzahl + 1 end
|
|
for _ in pairs(entzug) do anzahl = anzahl + 1 end
|
|
eintrag.abweichungen = anzahl
|
|
eintrag.rechte_extra, eintrag.rechte_entzug = nil, nil
|
|
end
|
|
|
|
-- Verteilung nach Rollen
|
|
local verteilung = MySQL.query.await(
|
|
'SELECT rolle, COUNT(*) AS anzahl FROM police_personal WHERE aktiv = 1 GROUP BY rolle') or {}
|
|
|
|
return {
|
|
personal = personal,
|
|
verteilung = verteilung,
|
|
rollen = Polizei.Rollen,
|
|
abteilungen= Polizei.Config.Abteilungen,
|
|
meineStufe = beamter.stufe,
|
|
rechte = {
|
|
einstellen = Rechte.Hat(beamter, 'personal.einstellen'),
|
|
rolle = Rechte.Hat(beamter, 'personal.rolle'),
|
|
rechte = Rechte.Hat(beamter, 'personal.rechte'),
|
|
},
|
|
}
|
|
end)
|
|
|
|
Kern.RegistriereAktion('personal', 'oeffnen', nil, function(quelle, beamter, daten)
|
|
local charakterId = tonumber(daten.charakterId)
|
|
if not charakterId then return nil, 'Ungültige Kennung.' end
|
|
|
|
local zeilen = MySQL.query.await([[
|
|
SELECT p.*, c.firstname, c.lastname, c.dob
|
|
FROM police_personal p
|
|
JOIN aether_characters c ON c.id = p.`character`
|
|
WHERE p.`character` = ?
|
|
]], { charakterId })
|
|
|
|
if not zeilen or not zeilen[1] then return nil, 'Personaldatensatz nicht gefunden.' end
|
|
|
|
local eintrag = zeilen[1]
|
|
local rolle = Polizei.HoleRolle(eintrag.rolle)
|
|
|
|
-- Rechte aufschlüsseln: woher kommt welches Recht?
|
|
local ausRolle = Polizei.RechteDerRolle(eintrag.rolle)
|
|
local extra = ZuMenge(eintrag.rechte_extra)
|
|
local entzug = ZuMenge(eintrag.rechte_entzug)
|
|
|
|
local rechteUebersicht = {}
|
|
for _, gruppe in ipairs(Polizei.Rechte) do
|
|
local eintraege = {}
|
|
for _, recht in ipairs(gruppe.eintraege) do
|
|
local herkunft = 'keins'
|
|
if entzug[recht.id] then herkunft = 'entzogen'
|
|
elseif extra[recht.id] then herkunft = 'zusaetzlich'
|
|
elseif ausRolle[recht.id] then herkunft = 'rolle' end
|
|
|
|
eintraege[#eintraege + 1] = {
|
|
id = recht.id, name = recht.name, herkunft = herkunft,
|
|
-- Darf der Handelnde dieses Recht überhaupt vergeben?
|
|
vergebbar = Rechte.Hat(beamter, recht.id),
|
|
}
|
|
end
|
|
rechteUebersicht[#rechteUebersicht + 1] = { gruppe = gruppe.gruppe, eintraege = eintraege }
|
|
end
|
|
|
|
-- Dienstzeiten
|
|
local dienste = MySQL.query.await([[
|
|
SELECT beginn, ende, sekunden, abteilung
|
|
FROM police_dienst WHERE `character` = ?
|
|
ORDER BY id DESC LIMIT 20
|
|
]], { charakterId }) or {}
|
|
|
|
local gesamt = MySQL.scalar.await(
|
|
'SELECT COALESCE(SUM(sekunden), 0) FROM police_dienst WHERE `character` = ?',
|
|
{ charakterId }) or 0
|
|
|
|
local darf, grund = DarfVerwalten(beamter, eintrag)
|
|
|
|
return {
|
|
personal = {
|
|
charakterId = eintrag.character,
|
|
name = eintrag.firstname .. ' ' .. eintrag.lastname,
|
|
geburt = tostring(eintrag.dob):sub(1, 10),
|
|
dienstnummer= eintrag.dienstnummer,
|
|
rolle = eintrag.rolle,
|
|
rolleName = rolle and rolle.name or eintrag.rolle,
|
|
abteilung = eintrag.abteilung,
|
|
eingestellt = eintrag.eingestellt,
|
|
aktiv = eintrag.aktiv == 1,
|
|
notiz = eintrag.notiz,
|
|
},
|
|
rechte = rechteUebersicht,
|
|
dienste = dienste,
|
|
gesamtzeit = gesamt,
|
|
rollen = Polizei.Rollen,
|
|
abteilungen = Polizei.Config.Abteilungen,
|
|
meineStufe = beamter.stufe,
|
|
darfVerwalten= darf,
|
|
sperrgrund = grund,
|
|
meineRechte = {
|
|
rolle = Rechte.Hat(beamter, 'personal.rolle'),
|
|
rechte = Rechte.Hat(beamter, 'personal.rechte'),
|
|
einstellen = Rechte.Hat(beamter, 'personal.einstellen'),
|
|
},
|
|
}
|
|
end)
|
|
|
|
-- =====================================================================
|
|
-- EINSTELLEN
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereAktion('personal', 'einstellen', 'personal.einstellen', function(quelle, beamter, daten)
|
|
local charakterId = tonumber(daten.charakterId)
|
|
if not charakterId then return nil, 'Keine Person ausgewählt.' end
|
|
|
|
local person = MySQL.query.await(
|
|
'SELECT firstname, lastname FROM aether_characters WHERE id = ? AND deleted = 0',
|
|
{ charakterId })
|
|
if not person or not person[1] then return nil, 'Diese Person existiert nicht.' end
|
|
|
|
-- Bereits im Bestand?
|
|
local vorhanden = MySQL.query.await(
|
|
'SELECT dienstnummer, aktiv FROM police_personal WHERE `character` = ?', { charakterId })
|
|
if vorhanden and vorhanden[1] and vorhanden[1].aktiv == 1 then
|
|
return nil, 'Diese Person ist bereits im Personalbestand.'
|
|
end
|
|
|
|
-- Rolle prüfen: niemals auf oder über dem eigenen Rang
|
|
local rollenId = tostring(daten.rolle or 'anwaerter')
|
|
local rolle = Polizei.HoleRolle(rollenId)
|
|
if not rolle then return nil, 'Unbekannte Rolle.' end
|
|
if rolle.stufe >= beamter.stufe then
|
|
return nil, 'Du kannst niemanden auf deinen Rang oder darüber einstellen.'
|
|
end
|
|
|
|
-- Abteilung prüfen
|
|
local abteilung = 'streife'
|
|
for _, eintrag in ipairs(Polizei.Config.Abteilungen) do
|
|
if eintrag.id == daten.abteilung then abteilung = daten.abteilung break end
|
|
end
|
|
|
|
-- Dienstnummer: eigene Angabe oder Vorschlag
|
|
local dienstnummer = type(daten.dienstnummer) == 'string'
|
|
and daten.dienstnummer:gsub('%s', ''):upper() or ''
|
|
if dienstnummer == '' then dienstnummer = NaechsteDienstnummer() end
|
|
if #dienstnummer > 16 then return nil, 'Die Dienstnummer ist zu lang.' end
|
|
|
|
local belegt = MySQL.scalar.await(
|
|
'SELECT `character` FROM police_personal WHERE dienstnummer = ? AND `character` <> ?',
|
|
{ dienstnummer, charakterId })
|
|
if belegt then return nil, 'Diese Dienstnummer ist bereits vergeben.' end
|
|
|
|
-- Wiedereinstellung oder Neuanlage
|
|
if vorhanden and vorhanden[1] then
|
|
MySQL.update.await([[
|
|
UPDATE police_personal
|
|
SET dienstnummer = ?, rolle = ?, abteilung = ?, aktiv = 1,
|
|
rechte_extra = NULL, rechte_entzug = NULL, eingestellt = NOW()
|
|
WHERE `character` = ?
|
|
]], { dienstnummer, rollenId, abteilung, charakterId })
|
|
else
|
|
MySQL.insert.await([[
|
|
INSERT INTO police_personal (`character`, dienstnummer, rolle, abteilung, notiz)
|
|
VALUES (?, ?, ?, ?, ?)
|
|
]], { charakterId, dienstnummer, rollenId, abteilung,
|
|
type(daten.notiz) == 'string' and daten.notiz:sub(1, 255) or nil })
|
|
end
|
|
|
|
Protokoll.Schreibe(beamter, 'personal', 'Beamten eingestellt',
|
|
{ typ = 'person', id = charakterId },
|
|
{ dienstnummer = dienstnummer, rolle = rollenId, abteilung = abteilung })
|
|
|
|
Hinweise.Erstelle(nil, {
|
|
kategorie = 'system', prioritaet = 'niedrig',
|
|
titel = 'Neuer Kollege',
|
|
text = ('%s %s (%s) wurde als %s eingestellt.')
|
|
:format(person[1].firstname, person[1].lastname, dienstnummer, rolle.name),
|
|
})
|
|
|
|
Kern.Loese('personal.eingestellt', charakterId, beamter)
|
|
|
|
return { dienstnummer = dienstnummer }
|
|
end)
|
|
|
|
--- Vorschlag für die nächste Dienstnummer
|
|
Kern.RegistriereAktion('personal', 'nummerVorschlag', 'personal.einstellen', function()
|
|
return { dienstnummer = NaechsteDienstnummer() }
|
|
end)
|
|
|
|
--- Personensuche für die Einstellung
|
|
Kern.RegistriereAktion('personal', 'personSuche', 'personal.einstellen', function(quelle, beamter, daten)
|
|
local begriff = type(daten.begriff) == 'string' and daten.begriff or ''
|
|
if #begriff < 2 then return { treffer = {} } end
|
|
|
|
local muster = '%' .. begriff .. '%'
|
|
local treffer = MySQL.query.await([[
|
|
SELECT c.id, c.firstname, c.lastname, c.dob,
|
|
p.dienstnummer, p.aktiv
|
|
FROM aether_characters c
|
|
LEFT JOIN police_personal p ON p.`character` = c.id
|
|
WHERE c.deleted = 0
|
|
AND (c.firstname LIKE ? OR c.lastname LIKE ?
|
|
OR CONCAT(c.firstname, ' ', c.lastname) LIKE ?)
|
|
LIMIT 20
|
|
]], { muster, muster, muster }) or {}
|
|
|
|
return { treffer = treffer }
|
|
end)
|
|
|
|
-- =====================================================================
|
|
-- ROLLE UND ABTEILUNG
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereAktion('personal', 'rolleAendern', 'personal.rolle', function(quelle, beamter, daten)
|
|
local charakterId = tonumber(daten.charakterId)
|
|
if not charakterId then return nil, 'Ungültige Kennung.' end
|
|
|
|
local ziel = MySQL.query.await(
|
|
'SELECT `character`, rolle, dienstnummer FROM police_personal WHERE `character` = ? AND aktiv = 1',
|
|
{ charakterId })
|
|
if not ziel or not ziel[1] then return nil, 'Personaldatensatz nicht gefunden.' end
|
|
|
|
local darf, grund = DarfVerwalten(beamter, ziel[1])
|
|
if not darf then return nil, grund end
|
|
|
|
local neueRolle = Polizei.HoleRolle(tostring(daten.rolle or ''))
|
|
if not neueRolle then return nil, 'Unbekannte Rolle.' end
|
|
|
|
-- Schranke 2: nie auf oder über den eigenen Rang befördern
|
|
if neueRolle.stufe >= beamter.stufe then
|
|
return nil, 'Du kannst niemanden auf deinen Rang oder darüber setzen.'
|
|
end
|
|
|
|
local vermerk = type(daten.vermerk) == 'string' and daten.vermerk:sub(1, 255) or ''
|
|
if #vermerk < 3 then return nil, 'Bitte einen Vermerk zur Änderung angeben.' end
|
|
|
|
local alteStufe = Polizei.Stufe(ziel[1].rolle)
|
|
local richtung = neueRolle.stufe > alteStufe and 'Beförderung' or
|
|
neueRolle.stufe < alteStufe and 'Degradierung' or 'Rollenwechsel'
|
|
|
|
MySQL.update.await('UPDATE police_personal SET rolle = ? WHERE `character` = ?',
|
|
{ neueRolle.id, charakterId })
|
|
|
|
-- Läuft die Person gerade im Dienst, Rechte sofort neu laden
|
|
local zielQuelle = Dienst.QuelleVonCharakter(charakterId)
|
|
if zielQuelle then
|
|
local neu = Dienst.LadeBeamten(zielQuelle, charakterId)
|
|
if neu then
|
|
local aktuell = Dienst.Hole(zielQuelle)
|
|
aktuell.rolle = neu.rolle
|
|
aktuell.rolleName = neu.rolleName
|
|
aktuell.stufe = neu.stufe
|
|
aktuell.rechte = neu.rechte
|
|
end
|
|
|
|
Hinweise.Erstelle({ charakterId = charakterId }, {
|
|
kategorie = 'system', prioritaet = 'normal',
|
|
titel = richtung,
|
|
text = ('Dein Dienstgrad wurde auf %s gesetzt: %s'):format(neueRolle.name, vermerk),
|
|
})
|
|
end
|
|
|
|
Protokoll.Schreibe(beamter, 'personal', richtung,
|
|
{ typ = 'person', id = charakterId },
|
|
{ von = ziel[1].rolle, nach = neueRolle.id, dienstnummer = ziel[1].dienstnummer }, vermerk)
|
|
|
|
return { ok = true }
|
|
end)
|
|
|
|
Kern.RegistriereAktion('personal', 'abteilungAendern', 'personal.rolle', function(quelle, beamter, daten)
|
|
local charakterId = tonumber(daten.charakterId)
|
|
if not charakterId then return nil, 'Ungültige Kennung.' end
|
|
|
|
local ziel = MySQL.query.await(
|
|
'SELECT `character`, rolle FROM police_personal WHERE `character` = ? AND aktiv = 1',
|
|
{ charakterId })
|
|
if not ziel or not ziel[1] then return nil, 'Personaldatensatz nicht gefunden.' end
|
|
|
|
local darf, grund = DarfVerwalten(beamter, ziel[1])
|
|
if not darf then return nil, grund end
|
|
|
|
local abteilung = nil
|
|
for _, eintrag in ipairs(Polizei.Config.Abteilungen) do
|
|
if eintrag.id == daten.abteilung then abteilung = eintrag break end
|
|
end
|
|
if not abteilung then return nil, 'Unbekannte Abteilung.' end
|
|
|
|
MySQL.update.await('UPDATE police_personal SET abteilung = ? WHERE `character` = ?',
|
|
{ abteilung.id, charakterId })
|
|
|
|
Protokoll.Schreibe(beamter, 'personal', 'Abteilung geändert',
|
|
{ typ = 'person', id = charakterId }, { abteilung = abteilung.id })
|
|
|
|
return { ok = true }
|
|
end)
|
|
|
|
-- =====================================================================
|
|
-- EINZELRECHTE
|
|
--
|
|
-- Schranke 3: Niemand kann ein Recht vergeben, das er selbst nicht hat.
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereAktion('personal', 'rechtSetzen', 'personal.rechte', function(quelle, beamter, daten)
|
|
local charakterId = tonumber(daten.charakterId)
|
|
local rechtId = tostring(daten.recht or '')
|
|
local modus = tostring(daten.modus or '') -- zusaetzlich | entzogen | rolle
|
|
|
|
if not charakterId or rechtId == '' then return nil, 'Unvollständige Angaben.' end
|
|
if modus ~= 'zusaetzlich' and modus ~= 'entzogen' and modus ~= 'rolle' then
|
|
return nil, 'Unbekannter Modus.'
|
|
end
|
|
|
|
-- Existiert das Recht überhaupt?
|
|
local bekannt = false
|
|
for _, id in ipairs(Polizei.AlleRechte()) do
|
|
if id == rechtId then bekannt = true break end
|
|
end
|
|
if not bekannt then return nil, 'Unbekanntes Recht.' end
|
|
|
|
local ziel = MySQL.query.await(
|
|
'SELECT `character`, rolle, rechte_extra, rechte_entzug, dienstnummer FROM police_personal WHERE `character` = ? AND aktiv = 1',
|
|
{ charakterId })
|
|
if not ziel or not ziel[1] then return nil, 'Personaldatensatz nicht gefunden.' end
|
|
|
|
local darf, grund = DarfVerwalten(beamter, ziel[1])
|
|
if not darf then return nil, grund end
|
|
|
|
-- Schranke 3: nur eigene Rechte weitergeben
|
|
if modus == 'zusaetzlich' and not Rechte.Hat(beamter, rechtId) then
|
|
return nil, 'Du kannst nur Rechte vergeben, die du selbst besitzt.'
|
|
end
|
|
|
|
local extra = ZuMenge(ziel[1].rechte_extra)
|
|
local entzug = ZuMenge(ziel[1].rechte_entzug)
|
|
|
|
-- Erst beide Listen bereinigen, dann neu setzen
|
|
extra[rechtId] = nil
|
|
entzug[rechtId] = nil
|
|
|
|
if modus == 'zusaetzlich' then extra[rechtId] = true
|
|
elseif modus == 'entzogen' then entzug[rechtId] = true end
|
|
-- modus == 'rolle' lässt beide leer: es gilt wieder die Rolle
|
|
|
|
MySQL.update.await(
|
|
'UPDATE police_personal SET rechte_extra = ?, rechte_entzug = ? WHERE `character` = ?',
|
|
{ json.encode(ZuListe(extra)), json.encode(ZuListe(entzug)), charakterId })
|
|
|
|
-- Im Dienst? Dann sofort wirksam machen
|
|
local zielQuelle = Dienst.QuelleVonCharakter(charakterId)
|
|
if zielQuelle then
|
|
local neu = Dienst.LadeBeamten(zielQuelle, charakterId)
|
|
if neu then Dienst.Hole(zielQuelle).rechte = neu.rechte end
|
|
end
|
|
|
|
Protokoll.Schreibe(beamter, 'personal', 'Einzelrecht geändert',
|
|
{ typ = 'person', id = charakterId },
|
|
{ recht = rechtId, modus = modus, dienstnummer = ziel[1].dienstnummer }, daten.vermerk)
|
|
|
|
return { ok = true }
|
|
end)
|
|
|
|
-- =====================================================================
|
|
-- NOTIZ UND ENTLASSUNG
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereAktion('personal', 'notiz', 'personal.rolle', function(quelle, beamter, daten)
|
|
local charakterId = tonumber(daten.charakterId)
|
|
if not charakterId then return nil, 'Ungültige Kennung.' end
|
|
|
|
local ziel = MySQL.query.await(
|
|
'SELECT `character`, rolle FROM police_personal WHERE `character` = ?', { charakterId })
|
|
if not ziel or not ziel[1] then return nil, 'Personaldatensatz nicht gefunden.' end
|
|
|
|
local darf, grund = DarfVerwalten(beamter, ziel[1])
|
|
if not darf then return nil, grund end
|
|
|
|
MySQL.update.await('UPDATE police_personal SET notiz = ? WHERE `character` = ?',
|
|
{ type(daten.notiz) == 'string' and daten.notiz:sub(1, 255) or nil, charakterId })
|
|
|
|
Protokoll.Schreibe(beamter, 'personal', 'Personalnotiz geändert',
|
|
{ typ = 'person', id = charakterId })
|
|
|
|
return { ok = true }
|
|
end)
|
|
|
|
Kern.RegistriereAktion('personal', 'entlassen', 'personal.einstellen', function(quelle, beamter, daten)
|
|
local charakterId = tonumber(daten.charakterId)
|
|
if not charakterId then return nil, 'Ungültige Kennung.' end
|
|
|
|
local grund = type(daten.grund) == 'string' and daten.grund:sub(1, 255) or ''
|
|
if #grund < 5 then return nil, 'Eine Entlassung erfordert eine Begründung.' end
|
|
|
|
local ziel = MySQL.query.await([[
|
|
SELECT p.`character`, p.rolle, p.dienstnummer, c.firstname, c.lastname
|
|
FROM police_personal p
|
|
JOIN aether_characters c ON c.id = p.`character`
|
|
WHERE p.`character` = ? AND p.aktiv = 1
|
|
]], { charakterId })
|
|
if not ziel or not ziel[1] then return nil, 'Personaldatensatz nicht gefunden.' end
|
|
|
|
local darf, sperre = DarfVerwalten(beamter, ziel[1])
|
|
if not darf then return nil, sperre end
|
|
|
|
-- Datensatz bleibt bestehen, nur inaktiv gesetzt
|
|
MySQL.update.await('UPDATE police_personal SET aktiv = 0 WHERE `character` = ?', { charakterId })
|
|
|
|
-- Läuft die Person im Dienst, wird sie abgemeldet
|
|
local zielQuelle = Dienst.QuelleVonCharakter(charakterId)
|
|
if zielQuelle then
|
|
Dienst.Beenden(zielQuelle)
|
|
TriggerClientEvent('aether:police:schliessen', zielQuelle)
|
|
end
|
|
|
|
-- Zugewiesene Dienstfahrzeuge freigeben
|
|
MySQL.update('UPDATE police_fahrzeug SET zugewiesen_an = NULL WHERE zugewiesen_an = ?',
|
|
{ charakterId })
|
|
|
|
Protokoll.Schreibe(beamter, 'personal', 'Beamten entlassen',
|
|
{ typ = 'person', id = charakterId },
|
|
{ dienstnummer = ziel[1].dienstnummer, rolle = ziel[1].rolle }, grund)
|
|
|
|
Hinweise.Erstelle(nil, {
|
|
kategorie = 'system', prioritaet = 'normal',
|
|
titel = 'Personalveränderung',
|
|
text = ('%s %s (%s) wurde aus dem Dienst entlassen.')
|
|
:format(ziel[1].firstname, ziel[1].lastname, ziel[1].dienstnummer),
|
|
})
|
|
|
|
Kern.Loese('personal.entlassen', charakterId, beamter)
|
|
|
|
return { ok = true }
|
|
end)
|