- 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>
444 lines
19 KiB
Lua
444 lines
19 KiB
Lua
-- =====================================================================
|
|
-- aether-police / Modul: Strafenkatalog & Anzeigen
|
|
--
|
|
-- Der Katalog ist ein NACHSCHLAGEWERK. Er berechnet einen Vorschlag —
|
|
-- entschieden wird von einem Beamten. Deshalb speichert jede Anzeige
|
|
-- beides getrennt:
|
|
-- haft_empfohlen / geld_empfohlen = Vorschlag des Katalogs
|
|
-- haft_final / geld_final = Entscheidung des Beamten
|
|
-- Weicht der Beamte ab, ist eine Begründung Pflicht.
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereModul({
|
|
id = 'anzeige', name = 'Anzeigen', icon = '⚖',
|
|
reihenfolge = 4, recht = 'anzeige.erstellen',
|
|
beschreibung = 'Strafenkatalog und Anzeigenerfassung',
|
|
})
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Grundbestand des Katalogs
|
|
-- Wird nur angelegt, wenn die Tabelle leer ist. Danach pflegt ihn die
|
|
-- Administration — hier wird nichts mehr überschrieben.
|
|
-- ---------------------------------------------------------------------
|
|
local GRUNDKATALOG = {
|
|
-- kennung, kategorie, bezeichnung, paragraph, haft (Min), geld, hinweis
|
|
{ 'v1', 'Verkehr', 'Überhöhte Geschwindigkeit', '§ 3 StVO', 0, 450,
|
|
'Bei erheblicher Überschreitung Fahrverbot prüfen.' },
|
|
{ 'v2', 'Verkehr', 'Fahren ohne Fahrerlaubnis', '§ 21 StVG', 10, 1200,
|
|
'Führerscheinstatus im MDT prüfen.' },
|
|
{ 'v3', 'Verkehr', 'Unerlaubtes Entfernen vom Unfallort', '§ 142 StGB', 30, 5000, nil },
|
|
{ 'v4', 'Verkehr', 'Trunkenheit im Verkehr', '§ 316 StGB', 20, 3500,
|
|
'Bei Gefährdung § 315c prüfen.' },
|
|
{ 'v5', 'Verkehr', 'Verbotenes Kraftfahrzeugrennen', '§ 315d StGB', 45, 9000, nil },
|
|
|
|
{ 'e1', 'Eigentum', 'Diebstahl (geringwertig)', '§ 242 StGB', 15, 800, nil },
|
|
{ 'e2', 'Eigentum', 'Einbruchdiebstahl', '§ 243 StGB', 45, 6000, nil },
|
|
{ 'e3', 'Eigentum', 'Sachbeschädigung', '§ 303 StGB', 10, 1500, nil },
|
|
{ 'e4', 'Eigentum', 'Hehlerei', '§ 259 StGB', 25, 4000, nil },
|
|
|
|
{ 'g1', 'Gewalt', 'Körperverletzung', '§ 223 StGB', 45, 8000, nil },
|
|
{ 'g2', 'Gewalt', 'Gefährliche Körperverletzung', '§ 224 StGB', 90, 15000, nil },
|
|
{ 'g3', 'Gewalt', 'Raub', '§ 249 StGB', 120, 25000, nil },
|
|
{ 'g4', 'Gewalt', 'Widerstand gegen Vollstreckungsbeamte', '§ 113 StGB', 30, 4000, nil },
|
|
{ 'g5', 'Gewalt', 'Bedrohung', '§ 241 StGB', 20, 2500, nil },
|
|
{ 'g6', 'Gewalt', 'Freiheitsberaubung', '§ 239 StGB', 60, 9000, nil },
|
|
|
|
{ 'w1', 'Waffen', 'Unerlaubter Waffenbesitz', '§ 52 WaffG', 60, 10000,
|
|
'Waffenschein im MDT prüfen — ggf. Einziehung dokumentieren.' },
|
|
{ 'w2', 'Waffen', 'Führen einer Waffe ohne Erlaubnis', '§ 52 WaffG', 30, 5000, nil },
|
|
|
|
{ 'b1', 'Betäubungsmittel', 'Besitz von Betäubungsmitteln', '§ 29 BtMG', 25, 3000, nil },
|
|
{ 'b2', 'Betäubungsmittel', 'Handel mit Betäubungsmitteln', '§ 29a BtMG', 110, 20000, nil },
|
|
|
|
{ 's1', 'Sonstige', 'Hausfriedensbruch', '§ 123 StGB', 15, 1500, nil },
|
|
{ 's2', 'Sonstige', 'Beleidigung', '§ 185 StGB', 0, 800, nil },
|
|
{ 's3', 'Sonstige', 'Falsche Verdächtigung', '§ 164 StGB', 30, 4500, nil },
|
|
{ 's4', 'Sonstige', 'Urkundenfälschung', '§ 267 StGB', 40, 6000, nil },
|
|
}
|
|
|
|
CreateThread(function()
|
|
Wait(3000)
|
|
|
|
local anzahl = MySQL.scalar.await('SELECT COUNT(*) FROM police_katalog') or 0
|
|
if anzahl > 0 then return end
|
|
|
|
for reihenfolge, eintrag in ipairs(GRUNDKATALOG) do
|
|
MySQL.insert.await([[
|
|
INSERT INTO police_katalog
|
|
(kennung, kategorie, bezeichnung, paragraph, haft, geld, hinweis, sortierung)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
]], { eintrag[1], eintrag[2], eintrag[3], eintrag[4],
|
|
eintrag[5], eintrag[6], eintrag[7], reihenfolge })
|
|
end
|
|
|
|
Aether.Info(('Strafenkatalog mit %d Einträgen angelegt.'):format(#GRUNDKATALOG))
|
|
end)
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Hilfsfunktionen
|
|
-- ---------------------------------------------------------------------
|
|
|
|
--- Lädt den aktiven Katalog, nach Kategorien gruppiert
|
|
--- @return table
|
|
local function LadeKatalog()
|
|
local eintraege = MySQL.query.await([[
|
|
SELECT id, kennung, kategorie, bezeichnung, paragraph, haft, geld, hinweis
|
|
FROM police_katalog WHERE aktiv = 1
|
|
ORDER BY sortierung ASC, id ASC
|
|
]]) or {}
|
|
|
|
local gruppen, reihenfolge = {}, {}
|
|
for _, eintrag in ipairs(eintraege) do
|
|
if not gruppen[eintrag.kategorie] then
|
|
gruppen[eintrag.kategorie] = {}
|
|
reihenfolge[#reihenfolge + 1] = eintrag.kategorie
|
|
end
|
|
table.insert(gruppen[eintrag.kategorie], eintrag)
|
|
end
|
|
|
|
local liste = {}
|
|
for _, kategorie in ipairs(reihenfolge) do
|
|
liste[#liste + 1] = { kategorie = kategorie, delikte = gruppen[kategorie] }
|
|
end
|
|
return liste
|
|
end
|
|
|
|
--- Rechnet den Vorschlag für eine Auswahl von Delikten aus.
|
|
--- Der Katalog ist die einzige Quelle — der Client schickt nur Kennungen.
|
|
--- @param kennungen table Liste von Katalog-Kennungen
|
|
--- @return table|nil { delikte, haft, geld }, string|nil Fehler
|
|
local function BerechneVorschlag(kennungen)
|
|
if type(kennungen) ~= 'table' or #kennungen == 0 then
|
|
return nil, 'Bitte mindestens ein Delikt auswählen.'
|
|
end
|
|
if #kennungen > 25 then
|
|
return nil, 'Zu viele Delikte auf einmal.'
|
|
end
|
|
|
|
local delikte, haft, geld = {}, 0, 0
|
|
|
|
for _, kennung in ipairs(kennungen) do
|
|
local eintrag = MySQL.query.await(
|
|
'SELECT kennung, bezeichnung, paragraph, haft, geld FROM police_katalog WHERE kennung = ? AND aktiv = 1',
|
|
{ tostring(kennung) })
|
|
|
|
if not eintrag or not eintrag[1] then
|
|
return nil, ('Unbekanntes Delikt: %s'):format(tostring(kennung))
|
|
end
|
|
|
|
local delikt = eintrag[1]
|
|
delikte[#delikte + 1] = {
|
|
kennung = delikt.kennung, name = delikt.bezeichnung,
|
|
paragraph = delikt.paragraph, haft = delikt.haft, geld = delikt.geld,
|
|
}
|
|
haft = haft + (delikt.haft or 0)
|
|
geld = geld + (delikt.geld or 0)
|
|
end
|
|
|
|
return { delikte = delikte, haft = haft, geld = geld }
|
|
end
|
|
|
|
-- =====================================================================
|
|
-- KATALOG ABRUFEN
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereAktion('anzeige', 'katalog', nil, function(quelle, beamter)
|
|
return {
|
|
katalog = LadeKatalog(),
|
|
rechte = {
|
|
bestaetigen = Rechte.Hat(beamter, 'anzeige.bestaetigen'),
|
|
abweichen = Rechte.Hat(beamter, 'anzeige.abweichen'),
|
|
bearbeiten = Rechte.Hat(beamter, 'katalog.bearbeiten'),
|
|
},
|
|
}
|
|
end)
|
|
|
|
--- Berechnet den Vorschlag zu einer Auswahl — reine Vorschau, speichert nichts
|
|
Kern.RegistriereAktion('anzeige', 'vorschlag', nil, function(quelle, beamter, daten)
|
|
local vorschlag, fehler = BerechneVorschlag(daten.delikte)
|
|
if not vorschlag then return nil, fehler end
|
|
|
|
-- Zusätzliche Hinweise der gewählten Delikte mitliefern
|
|
local hinweise = {}
|
|
for _, kennung in ipairs(daten.delikte) do
|
|
local hinweis = MySQL.scalar.await(
|
|
'SELECT hinweis FROM police_katalog WHERE kennung = ? AND aktiv = 1', { kennung })
|
|
if hinweis and hinweis ~= '' then hinweise[#hinweise + 1] = hinweis end
|
|
end
|
|
|
|
return {
|
|
delikte = vorschlag.delikte,
|
|
haft = vorschlag.haft,
|
|
geld = vorschlag.geld,
|
|
hinweise = hinweise,
|
|
}
|
|
end)
|
|
|
|
-- =====================================================================
|
|
-- ANZEIGE ERFASSEN
|
|
--
|
|
-- Erzeugt einen ENTWURF. Nichts wird eingezogen, niemand kommt in Haft.
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereAktion('anzeige', 'erstellen', 'anzeige.erstellen', function(quelle, beamter, daten)
|
|
local beschuldigter = tonumber(daten.beschuldigter)
|
|
if not beschuldigter then return nil, 'Kein Beschuldigter ausgewählt.' end
|
|
|
|
local person = MySQL.scalar.await(
|
|
'SELECT id FROM aether_characters WHERE id = ? AND deleted = 0', { beschuldigter })
|
|
if not person then return nil, 'Diese Person existiert nicht.' end
|
|
|
|
local vorschlag, fehler = BerechneVorschlag(daten.delikte)
|
|
if not vorschlag then return nil, fehler end
|
|
|
|
-- Werte des Beamten übernehmen; fehlen sie, gilt der Vorschlag
|
|
local haftFinal = daten.haft ~= nil and math.max(0, math.min(1440, math.floor(tonumber(daten.haft) or 0))) or vorschlag.haft
|
|
local geldFinal = daten.geld ~= nil and math.max(0, math.min(10000000, math.floor(tonumber(daten.geld) or 0))) or vorschlag.geld
|
|
|
|
local weichtAb = (haftFinal ~= vorschlag.haft) or (geldFinal ~= vorschlag.geld)
|
|
local begruendung = type(daten.begruendung) == 'string'
|
|
and daten.begruendung:gsub('^%s+', ''):gsub('%s+$', '') or ''
|
|
|
|
-- Abweichen ist ein eigenes Recht und verlangt eine Begründung
|
|
if weichtAb then
|
|
if not Rechte.Hat(beamter, 'anzeige.abweichen') then
|
|
return nil, 'Dir fehlt die Berechtigung, vom Katalogvorschlag abzuweichen.'
|
|
end
|
|
if #begruendung < 5 then
|
|
return nil, 'Bei Abweichung vom Vorschlag ist eine Begründung erforderlich.'
|
|
end
|
|
end
|
|
|
|
local anzeigeId = MySQL.insert.await([[
|
|
INSERT INTO police_anzeige
|
|
(akte, beschuldigter, delikte, haft_empfohlen, geld_empfohlen,
|
|
haft_final, geld_final, begruendung, status, erstellt_von)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'entwurf', ?)
|
|
]], {
|
|
tonumber(daten.akte), beschuldigter, json.encode(vorschlag.delikte),
|
|
vorschlag.haft, vorschlag.geld, haftFinal, geldFinal,
|
|
begruendung ~= '' and begruendung or nil, beamter.charakterId,
|
|
})
|
|
|
|
Protokoll.Schreibe(beamter, 'anzeige', 'Anzeige als Entwurf erfasst',
|
|
{ typ = 'person', id = beschuldigter },
|
|
{ anzeige = anzeigeId, haft = haftFinal, geld = geldFinal, abweichung = weichtAb },
|
|
begruendung ~= '' and begruendung or nil)
|
|
|
|
Kern.Loese('anzeige.erstellt', anzeigeId, beamter)
|
|
|
|
return { id = anzeigeId, entwurf = true }
|
|
end)
|
|
|
|
-- =====================================================================
|
|
-- BESTÄTIGEN
|
|
--
|
|
-- Erst hier wird die Entscheidung des Beamten ausgeführt: Das Bußgeld
|
|
-- wird eingezogen. Eine Haft entsteht dadurch NICHT — die Einweisung
|
|
-- in Gewahrsam ist ein eigener, bewusster Schritt.
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereAktion('anzeige', 'bestaetigen', 'anzeige.bestaetigen', function(quelle, beamter, daten)
|
|
local anzeigeId = tonumber(daten.id)
|
|
if not anzeigeId then return nil, 'Ungültige Kennung.' end
|
|
|
|
local anzeige = MySQL.query.await('SELECT * FROM police_anzeige WHERE id = ?', { anzeigeId })
|
|
if not anzeige or not anzeige[1] then return nil, 'Anzeige nicht gefunden.' end
|
|
|
|
local eintrag = anzeige[1]
|
|
if eintrag.status ~= 'entwurf' then
|
|
return nil, 'Diese Anzeige wurde bereits abschließend bearbeitet.'
|
|
end
|
|
|
|
-- Vier-Augen-Prinzip: Wer erfasst hat, darf nur mit dem Recht
|
|
-- „bestaetigen" auch selbst bestätigen — das ist hier gegeben,
|
|
-- wird aber protokolliert.
|
|
local selbstbestaetigung = eintrag.erstellt_von == beamter.charakterId
|
|
|
|
-- Bußgeld einziehen (erst Bargeld, dann Konto)
|
|
local Core = exports['aether-core']
|
|
local eingezogen = 0
|
|
|
|
if eintrag.geld_final > 0 then
|
|
local zielQuelle = Core:HoleQuelleNachCharakter(eintrag.beschuldigter)
|
|
local bezahlt = false
|
|
|
|
if zielQuelle then
|
|
bezahlt = Core:EntferneGeld(zielQuelle, 'bargeld', eintrag.geld_final, 'Bußgeld')
|
|
if not bezahlt then
|
|
bezahlt = Core:EntferneGeld(zielQuelle, 'konto', eintrag.geld_final, 'Bußgeld')
|
|
end
|
|
else
|
|
-- Offline: direkt vom Konto, auch ins Minus (Forderung bleibt bestehen)
|
|
MySQL.update.await('UPDATE aether_characters SET bank = bank - ? WHERE id = ?',
|
|
{ eintrag.geld_final, eintrag.beschuldigter })
|
|
bezahlt = true
|
|
end
|
|
|
|
if bezahlt then
|
|
eingezogen = eintrag.geld_final
|
|
MySQL.update('UPDATE aether_treasury SET betrag = betrag + ? WHERE id = 1',
|
|
{ eintrag.geld_final })
|
|
end
|
|
end
|
|
|
|
MySQL.update.await([[
|
|
UPDATE police_anzeige
|
|
SET status = 'bestaetigt', bestaetigt_von = ?, bestaetigt_am = NOW()
|
|
WHERE id = ?
|
|
]], { beamter.charakterId, anzeigeId })
|
|
|
|
Protokoll.Schreibe(beamter, 'anzeige', 'Anzeige bestätigt',
|
|
{ typ = 'person', id = eintrag.beschuldigter },
|
|
{ anzeige = anzeigeId, eingezogen = eingezogen,
|
|
selbstbestaetigung = selbstbestaetigung })
|
|
|
|
-- Erinnerung, falls eine Haft festgelegt wurde — aber NICHT ausgeführt
|
|
if eintrag.haft_final > 0 then
|
|
Hinweise.Erstelle({ charakterId = beamter.charakterId }, {
|
|
kategorie = 'gewahrsam', prioritaet = 'normal',
|
|
titel = 'Haftzeit festgelegt — Einweisung offen',
|
|
text = ('Die Anzeige sieht %d Minuten vor. Die Einweisung in Gewahrsam ist ein eigener Schritt.')
|
|
:format(eintrag.haft_final),
|
|
zielTyp = 'person', zielId = eintrag.beschuldigter,
|
|
})
|
|
end
|
|
|
|
Kern.Loese('anzeige.bestaetigt', anzeigeId, beamter)
|
|
|
|
return {
|
|
ok = true,
|
|
eingezogen = eingezogen,
|
|
haft = eintrag.haft_final,
|
|
beschuldigter = eintrag.beschuldigter,
|
|
}
|
|
end)
|
|
|
|
--- Entwurf verwerfen
|
|
Kern.RegistriereAktion('anzeige', 'verwerfen', 'anzeige.erstellen', function(quelle, beamter, daten)
|
|
local anzeigeId = tonumber(daten.id)
|
|
if not anzeigeId then return nil, 'Ungültige Kennung.' end
|
|
|
|
local anzeige = MySQL.query.await(
|
|
'SELECT erstellt_von, status, beschuldigter FROM police_anzeige WHERE id = ?', { anzeigeId })
|
|
if not anzeige or not anzeige[1] then return nil, 'Anzeige nicht gefunden.' end
|
|
if anzeige[1].status ~= 'entwurf' then
|
|
return nil, 'Nur Entwürfe können verworfen werden.'
|
|
end
|
|
|
|
-- Fremde Entwürfe darf nur verwerfen, wer bestätigen darf
|
|
if anzeige[1].erstellt_von ~= beamter.charakterId
|
|
and not Rechte.Hat(beamter, 'anzeige.bestaetigen') then
|
|
return nil, 'Du kannst nur eigene Entwürfe verwerfen.'
|
|
end
|
|
|
|
local grund = type(daten.grund) == 'string' and daten.grund:sub(1, 255) or nil
|
|
|
|
MySQL.update.await("UPDATE police_anzeige SET status = 'verworfen' WHERE id = ?", { anzeigeId })
|
|
|
|
Protokoll.Schreibe(beamter, 'anzeige', 'Anzeige verworfen',
|
|
{ typ = 'person', id = anzeige[1].beschuldigter }, { anzeige = anzeigeId }, grund)
|
|
|
|
return { ok = true }
|
|
end)
|
|
|
|
-- =====================================================================
|
|
-- ÜBERSICHT UND EINZELANSICHT
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereAktion('anzeige', 'liste', nil, function(quelle, beamter, daten)
|
|
local bedingungen, werte = { '1=1' }, {}
|
|
|
|
if daten.status and daten.status ~= '' then
|
|
bedingungen[#bedingungen + 1] = 'a.status = ?'
|
|
werte[#werte + 1] = daten.status
|
|
end
|
|
if daten.nurEigene then
|
|
bedingungen[#bedingungen + 1] = 'a.erstellt_von = ?'
|
|
werte[#werte + 1] = beamter.charakterId
|
|
end
|
|
|
|
local anzeigen = MySQL.query.await(([[
|
|
SELECT a.id, a.beschuldigter, a.delikte, a.haft_empfohlen, a.geld_empfohlen,
|
|
a.haft_final, a.geld_final, a.begruendung, a.status,
|
|
a.erstellt_am, a.bestaetigt_am,
|
|
b.firstname AS b_vorname, b.lastname AS b_nachname,
|
|
e.firstname AS e_vorname, e.lastname AS e_nachname
|
|
FROM police_anzeige a
|
|
LEFT JOIN aether_characters b ON b.id = a.beschuldigter
|
|
LEFT JOIN aether_characters e ON e.id = a.erstellt_von
|
|
WHERE %s
|
|
ORDER BY
|
|
CASE WHEN a.status = 'entwurf' THEN 0 ELSE 1 END,
|
|
a.id DESC
|
|
LIMIT 60
|
|
]]):format(table.concat(bedingungen, ' AND ')), werte) or {}
|
|
|
|
for _, anzeige in ipairs(anzeigen) do
|
|
local erfolg, liste = pcall(json.decode, anzeige.delikte)
|
|
anzeige.delikte = (erfolg and type(liste) == 'table') and liste or {}
|
|
-- Weicht die Entscheidung vom Vorschlag ab?
|
|
anzeige.abweichung = (anzeige.haft_final ~= anzeige.haft_empfohlen)
|
|
or (anzeige.geld_final ~= anzeige.geld_empfohlen)
|
|
end
|
|
|
|
return {
|
|
anzeigen = anzeigen,
|
|
rechte = {
|
|
bestaetigen = Rechte.Hat(beamter, 'anzeige.bestaetigen'),
|
|
abweichen = Rechte.Hat(beamter, 'anzeige.abweichen'),
|
|
},
|
|
}
|
|
end)
|
|
|
|
-- =====================================================================
|
|
-- KATALOGPFLEGE (Administration)
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereAktion('anzeige', 'katalogSpeichern', 'katalog.bearbeiten', function(quelle, beamter, daten)
|
|
local kennung = type(daten.kennung) == 'string' and daten.kennung:gsub('%s', '') or ''
|
|
local bezeichnung = type(daten.bezeichnung) == 'string'
|
|
and daten.bezeichnung:gsub('^%s+', ''):gsub('%s+$', '') or ''
|
|
|
|
if #kennung < 1 or #kennung > 24 then return nil, 'Ungültige Kennung.' end
|
|
if #bezeichnung < 3 then return nil, 'Bitte eine Bezeichnung angeben.' end
|
|
|
|
local haft = math.max(0, math.min(1440, math.floor(tonumber(daten.haft) or 0)))
|
|
local geld = math.max(0, math.min(10000000, math.floor(tonumber(daten.geld) or 0)))
|
|
|
|
MySQL.query.await([[
|
|
INSERT INTO police_katalog
|
|
(kennung, kategorie, bezeichnung, paragraph, haft, geld, hinweis, aktiv)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, 1)
|
|
ON DUPLICATE KEY UPDATE
|
|
kategorie = VALUES(kategorie), bezeichnung = VALUES(bezeichnung),
|
|
paragraph = VALUES(paragraph), haft = VALUES(haft), geld = VALUES(geld),
|
|
hinweis = VALUES(hinweis), aktiv = 1
|
|
]], {
|
|
kennung,
|
|
type(daten.kategorie) == 'string' and daten.kategorie:sub(1, 48) or 'Sonstige',
|
|
bezeichnung,
|
|
type(daten.paragraph) == 'string' and daten.paragraph:sub(1, 48) or nil,
|
|
haft, geld,
|
|
type(daten.hinweis) == 'string' and daten.hinweis:sub(1, 512) or nil,
|
|
})
|
|
|
|
Protokoll.Schreibe(beamter, 'anzeige', 'Katalogeintrag gespeichert',
|
|
{ typ = 'katalog', id = kennung }, { haft = haft, geld = geld })
|
|
|
|
return { ok = true }
|
|
end)
|
|
|
|
Kern.RegistriereAktion('anzeige', 'katalogDeaktivieren', 'katalog.bearbeiten', function(quelle, beamter, daten)
|
|
local kennung = tostring(daten.kennung or '')
|
|
if kennung == '' then return nil, 'Ungültige Kennung.' end
|
|
|
|
-- Nicht löschen, damit alte Anzeigen nachvollziehbar bleiben
|
|
MySQL.update.await('UPDATE police_katalog SET aktiv = 0 WHERE kennung = ?', { kennung })
|
|
|
|
Protokoll.Schreibe(beamter, 'anzeige', 'Katalogeintrag deaktiviert',
|
|
{ typ = 'katalog', id = kennung })
|
|
|
|
return { ok = true }
|
|
end)
|