-- ===================================================================== -- aether-police / Modul: Asservatenkammer -- -- Jedes Objekt hat eine Kennung, einen Lagerort, einen Verantwortlichen -- und eine lückenlose Historie. Nichts wird automatisch eingelagert, -- ausgegeben oder vernichtet — jeder Schritt ist eine Handlung eines -- Beamten und wird mit Namen festgehalten. -- ===================================================================== Kern.RegistriereModul({ id = 'asservat', name = 'Asservate', icon = '📦', reihenfolge = 7, recht = 'asservat.einlagern', beschreibung = 'Asservatenkammer und Verwahrkette', }) -- --------------------------------------------------------------------- -- Hilfsfunktionen -- --------------------------------------------------------------------- local function KategorieGueltig(kategorie) for _, eintrag in ipairs(Polizei.Config.Asservatkategorien) do if eintrag.id == kategorie then return true end end return false end --- Schreibt einen Historieneintrag zu einem Asservat --- @param asservatId number --- @param aktion string --- @param beamter table --- @param notiz string|nil local function Historie(asservatId, aktion, beamter, notiz) MySQL.insert('INSERT INTO police_asservat_historie (asservat, aktion, von, notiz) VALUES (?, ?, ?, ?)', { asservatId, aktion, beamter.charakterId, notiz }) end -- ===================================================================== -- ÜBERSICHT -- ===================================================================== Kern.RegistriereAktion('asservat', 'liste', nil, function(quelle, beamter, daten) local bedingungen, werte = { '1=1' }, {} if daten.kategorie and daten.kategorie ~= '' then bedingungen[#bedingungen + 1] = 'a.kategorie = ?' werte[#werte + 1] = daten.kategorie end if daten.status and daten.status ~= '' then bedingungen[#bedingungen + 1] = 'a.status = ?' werte[#werte + 1] = daten.status end if type(daten.begriff) == 'string' and #daten.begriff >= 2 then bedingungen[#bedingungen + 1] = '(a.bezeichnung LIKE ? OR a.kennung LIKE ? OR a.lagerort LIKE ?)' local muster = '%' .. daten.begriff .. '%' werte[#werte + 1] = muster werte[#werte + 1] = muster werte[#werte + 1] = muster end local asservate = MySQL.query.await(([[ SELECT a.id, a.kennung, a.kategorie, a.bezeichnung, a.menge, a.lagerort, a.status, a.eingelagert_am, a.akte, a.beweis, c.firstname, c.lastname, p.dienstnummer, k.aktenzeichen FROM police_asservat a LEFT JOIN aether_characters c ON c.id = a.verantwortlich LEFT JOIN police_personal p ON p.`character` = a.verantwortlich LEFT JOIN police_akte k ON k.id = a.akte WHERE %s ORDER BY CASE a.status WHEN 'eingelagert' THEN 0 WHEN 'ausgegeben' THEN 1 ELSE 2 END, a.id DESC LIMIT 80 ]]):format(table.concat(bedingungen, ' AND ')), werte) or {} -- Bestandszahlen für die Übersicht local zahlen = MySQL.query.await([[ SELECT status, COUNT(*) AS anzahl FROM police_asservat GROUP BY status ]]) or {} local bestand = { eingelagert = 0, ausgegeben = 0, vernichtet = 0, zurueckgegeben = 0 } for _, zeile in ipairs(zahlen) do bestand[zeile.status] = zeile.anzahl end return { asservate = asservate, kategorien = Polizei.Config.Asservatkategorien, bestand = bestand, rechte = { einlagern = Rechte.Hat(beamter, 'asservat.einlagern'), ausgeben = Rechte.Hat(beamter, 'asservat.ausgeben'), vernichten = Rechte.Hat(beamter, 'asservat.vernichten'), }, } end) Kern.RegistriereAktion('asservat', 'oeffnen', nil, function(quelle, beamter, daten) local asservatId = tonumber(daten.id) if not asservatId then return nil, 'Ungültige Kennung.' end local asservat = MySQL.query.await([[ SELECT a.*, c.firstname, c.lastname, p.dienstnummer, k.aktenzeichen, k.titel AS akte_titel, b.kennung AS beweis_kennung, b.bezeichnung AS beweis_bezeichnung FROM police_asservat a LEFT JOIN aether_characters c ON c.id = a.verantwortlich LEFT JOIN police_personal p ON p.`character` = a.verantwortlich LEFT JOIN police_akte k ON k.id = a.akte LEFT JOIN police_beweis b ON b.id = a.beweis WHERE a.id = ? ]], { asservatId }) if not asservat or not asservat[1] then return nil, 'Asservat nicht gefunden.' end -- Lückenlose Verwahrkette local historie = MySQL.query.await([[ SELECT h.aktion, h.am, h.notiz, c.firstname, c.lastname, p.dienstnummer FROM police_asservat_historie h LEFT JOIN aether_characters c ON c.id = h.von LEFT JOIN police_personal p ON p.`character` = h.von WHERE h.asservat = ? ORDER BY h.id ASC ]], { asservatId }) or {} return { asservat = asservat[1], historie = historie, rechte = { ausgeben = Rechte.Hat(beamter, 'asservat.ausgeben'), vernichten = Rechte.Hat(beamter, 'asservat.vernichten'), }, } end) -- ===================================================================== -- EINLAGERN -- ===================================================================== Kern.RegistriereAktion('asservat', 'einlagern', 'asservat.einlagern', function(quelle, beamter, daten) if not KategorieGueltig(daten.kategorie) then return nil, 'Unbekannte Kategorie.' end local bezeichnung = type(daten.bezeichnung) == 'string' and daten.bezeichnung:gsub('^%s+', ''):gsub('%s+$', '') or '' if #bezeichnung < 3 or #bezeichnung > 160 then return nil, 'Die Bezeichnung muss 3 bis 160 Zeichen lang sein.' end local lagerort = type(daten.lagerort) == 'string' and daten.lagerort:sub(1, 64) or '' if #lagerort < 2 then return nil, 'Bitte einen Lagerort angeben.' end local kennung = Kern.ErzeugeKennung('AS', 'police_asservat', 'kennung') local asservatId = MySQL.insert.await([[ INSERT INTO police_asservat (kennung, kategorie, bezeichnung, menge, lagerort, verantwortlich, akte, beweis, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'eingelagert') ]], { kennung, daten.kategorie, bezeichnung, type(daten.menge) == 'string' and daten.menge:sub(1, 48) or nil, lagerort, beamter.charakterId, tonumber(daten.akte), tonumber(daten.beweis), }) Historie(asservatId, 'Eingelagert', beamter, type(daten.notiz) == 'string' and daten.notiz:sub(1, 255) or nil) Protokoll.Schreibe(beamter, 'asservat', 'Asservat eingelagert', { typ = 'asservat', id = asservatId }, { kennung = kennung, kategorie = daten.kategorie, lagerort = lagerort }) Kern.Loese('asservat.eingelagert', asservatId, beamter) return { id = asservatId, kennung = kennung } end) -- ===================================================================== -- AUSGEBEN -- -- Wer etwas herausgibt, übernimmt die Verantwortung dafür — deshalb -- sind Empfänger und Grund Pflicht. -- ===================================================================== Kern.RegistriereAktion('asservat', 'ausgeben', 'asservat.ausgeben', function(quelle, beamter, daten) local asservatId = tonumber(daten.id) if not asservatId then return nil, 'Ungültige Kennung.' end local asservat = MySQL.query.await( 'SELECT kennung, bezeichnung, status FROM police_asservat WHERE id = ?', { asservatId }) if not asservat or not asservat[1] then return nil, 'Asservat nicht gefunden.' end if asservat[1].status ~= 'eingelagert' then return nil, 'Nur eingelagerte Asservate können ausgegeben werden.' end local empfaenger = type(daten.empfaenger) == 'string' and daten.empfaenger:gsub('^%s+', ''):gsub('%s+$', '') or '' if #empfaenger < 2 then return nil, 'Bitte den Empfänger angeben.' end local grund = type(daten.grund) == 'string' and daten.grund:sub(1, 255) or '' if #grund < 3 then return nil, 'Bitte einen Grund für die Ausgabe angeben.' end MySQL.update.await("UPDATE police_asservat SET status = 'ausgegeben' WHERE id = ?", { asservatId }) Historie(asservatId, 'Ausgegeben an ' .. empfaenger, beamter, grund) Protokoll.Schreibe(beamter, 'asservat', 'Asservat ausgegeben', { typ = 'asservat', id = asservatId }, { kennung = asservat[1].kennung, empfaenger = empfaenger }, grund) return { ok = true } end) --- Rückgabe in die Kammer Kern.RegistriereAktion('asservat', 'zurueckgeben', 'asservat.ausgeben', function(quelle, beamter, daten) local asservatId = tonumber(daten.id) if not asservatId then return nil, 'Ungültige Kennung.' end local asservat = MySQL.query.await( 'SELECT kennung, status FROM police_asservat WHERE id = ?', { asservatId }) if not asservat or not asservat[1] then return nil, 'Asservat nicht gefunden.' end if asservat[1].status ~= 'ausgegeben' then return nil, 'Nur ausgegebene Asservate können zurückgenommen werden.' end local lagerort = type(daten.lagerort) == 'string' and daten.lagerort:sub(1, 64) or nil MySQL.update.await([[ UPDATE police_asservat SET status = 'eingelagert', lagerort = COALESCE(?, lagerort), verantwortlich = ? WHERE id = ? ]], { lagerort, beamter.charakterId, asservatId }) Historie(asservatId, 'Zurückgenommen', beamter, type(daten.notiz) == 'string' and daten.notiz:sub(1, 255) or nil) Protokoll.Schreibe(beamter, 'asservat', 'Asservat zurückgenommen', { typ = 'asservat', id = asservatId }, { kennung = asservat[1].kennung }) return { ok = true } end) -- ===================================================================== -- VERNICHTEN -- -- Endgültig und nur mit eigenem Recht. Der Datensatz bleibt bestehen — -- vernichtet wird der Gegenstand, nicht seine Dokumentation. -- ===================================================================== Kern.RegistriereAktion('asservat', 'vernichten', 'asservat.vernichten', function(quelle, beamter, daten) local asservatId = tonumber(daten.id) if not asservatId 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, 'Die Vernichtung erfordert eine ausführliche Begründung.' end local zeuge = type(daten.zeuge) == 'string' and daten.zeuge:sub(1, 64) or '' if #zeuge < 2 then return nil, 'Bitte einen zweiten Beamten als Zeugen angeben.' end local asservat = MySQL.query.await( 'SELECT kennung, bezeichnung, status FROM police_asservat WHERE id = ?', { asservatId }) if not asservat or not asservat[1] then return nil, 'Asservat nicht gefunden.' end if asservat[1].status == 'vernichtet' then return nil, 'Dieses Asservat wurde bereits vernichtet.' end MySQL.update.await("UPDATE police_asservat SET status = 'vernichtet' WHERE id = ?", { asservatId }) Historie(asservatId, 'Vernichtet (Zeuge: ' .. zeuge .. ')', beamter, grund) Protokoll.Schreibe(beamter, 'asservat', 'Asservat vernichtet', { typ = 'asservat', id = asservatId }, { kennung = asservat[1].kennung, bezeichnung = asservat[1].bezeichnung, zeuge = zeuge }, grund) Hinweise.Erstelle(nil, { kategorie = 'beweis', prioritaet = 'normal', titel = 'Asservat vernichtet', text = ('%s (%s) durch %s, Zeuge: %s') :format(asservat[1].kennung, asservat[1].bezeichnung, beamter.dienstnummer, zeuge), zielTyp = 'asservat', zielId = asservatId, }) return { ok = true } end) -- ===================================================================== -- LAGERORT ÄNDERN -- ===================================================================== Kern.RegistriereAktion('asservat', 'umlagern', 'asservat.einlagern', function(quelle, beamter, daten) local asservatId = tonumber(daten.id) local lagerort = type(daten.lagerort) == 'string' and daten.lagerort:sub(1, 64) or '' if not asservatId then return nil, 'Ungültige Kennung.' end if #lagerort < 2 then return nil, 'Bitte einen Lagerort angeben.' end local asservat = MySQL.query.await( 'SELECT kennung, lagerort, status FROM police_asservat WHERE id = ?', { asservatId }) if not asservat or not asservat[1] then return nil, 'Asservat nicht gefunden.' end if asservat[1].status ~= 'eingelagert' then return nil, 'Nur eingelagerte Asservate können umgelagert werden.' end MySQL.update.await('UPDATE police_asservat SET lagerort = ? WHERE id = ?', { lagerort, asservatId }) Historie(asservatId, ('Umgelagert: %s → %s'):format(asservat[1].lagerort, lagerort), beamter) Protokoll.Schreibe(beamter, 'asservat', 'Asservat umgelagert', { typ = 'asservat', id = asservatId }, { von = asservat[1].lagerort, nach = lagerort }) return { ok = true } end)