-- ===================================================================== -- aether-medical / Modul: Übersicht (Dashboard) -- -- Zeigt den Stand der Klinik. Jede Zahl wird bei jedem Aufruf frisch -- aus der Datenbank gelesen — es gibt keine fest verdrahteten Werte -- und keine Kennzahl, die eine Handlung auslöst. -- ===================================================================== Kern.RegistriereModul({ id = 'dashboard', name = 'Übersicht', icon = '⬛', reihenfolge = 1, beschreibung = 'Lage der Klinik auf einen Blick', }) --- Zählt und liefert immer eine Zahl zurück --- @param sql string Abfrage --- @param werte table|nil Parameter --- @return number local function Zaehle(sql, werte) return tonumber(MySQL.scalar.await(sql, werte or {})) or 0 end Kern.RegistriereAktion('dashboard', 'laden', nil, function(quelle, mitarbeiter) local charakterId = mitarbeiter.charakterId -- --- Dienst --- local imDienst, notaerzte, aufRtw, imOp = 0, 0, 0, 0 local leitstelleName = nil for zielQuelle, eintrag in pairs(Dienst.AlleImDienst()) do imDienst = imDienst + 1 if eintrag.funktion == 'notarzt' then notaerzte = notaerzte + 1 end if eintrag.funktion == 'rtw' then aufRtw = aufRtw + 1 end if eintrag.funktion == 'op' then imOp = imOp + 1 end if Dienst.Leitstelle() == zielQuelle then leitstelleName = eintrag.name end end -- --- Einsätze --- local einsaetze = { offen = Zaehle("SELECT COUNT(*) FROM medical_einsatz WHERE status = 'offen'"), laufend = Zaehle("SELECT COUNT(*) FROM medical_einsatz WHERE status = 'laufend'"), dringend = Zaehle([[ SELECT COUNT(*) FROM medical_einsatz WHERE status IN ('offen', 'laufend') AND prioritaet <= 2 ]]), } -- Die jüngsten offenen Einsätze für die Kurzliste einsaetze.liste = MySQL.query.await([[ SELECT id, nummer, titel, kategorie, prioritaet, ort, status, erstellt_am FROM medical_einsatz WHERE status IN ('offen', 'laufend') ORDER BY prioritaet, id DESC LIMIT 6 ]]) or {} -- --- Patienten --- local patienten = { wartend = Zaehle("SELECT COUNT(*) FROM medical_aufnahme WHERE status = 'wartend'"), aufgenommen = Zaehle("SELECT COUNT(*) FROM medical_aufnahme WHERE status = 'aufgenommen'"), intensiv = Zaehle('SELECT COUNT(*) FROM medical_intensiv WHERE beendet_am IS NULL'), kritisch = Zaehle([[ SELECT COUNT(*) FROM medical_intensiv WHERE beendet_am IS NULL AND zustand = 'kritisch' ]]), } -- Die längste Wartezeit in der Notaufnahme patienten.laengsteWartezeit = Zaehle([[ SELECT COALESCE(MAX(TIMESTAMPDIFF(MINUTE, aufgenommen_am, NOW())), 0) FROM medical_aufnahme WHERE status = 'wartend' ]]) -- Wartende Patienten für die Kurzliste patienten.warteliste = MySQL.query.await([[ SELECT a.id, a.patient, a.grund, a.prioritaet, TIMESTAMPDIFF(MINUTE, a.aufgenommen_am, NOW()) AS wartet, c.firstname, c.lastname FROM medical_aufnahme a JOIN aether_characters c ON c.id = a.patient WHERE a.status = 'wartend' ORDER BY a.prioritaet, a.aufgenommen_am LIMIT 6 ]]) or {} -- --- Belegung --- local bettenGesamt = Zaehle([[ SELECT COALESCE(SUM(betten), 0) FROM medical_zimmer WHERE art = 'bett' AND status <> 'gesperrt' ]]) local bettenBelegt = Zaehle([[ SELECT COUNT(*) FROM medical_belegung b JOIN medical_zimmer z ON z.id = b.zimmer WHERE b.frei_am IS NULL AND z.art = 'bett' ]]) local belegung = { bettenGesamt = bettenGesamt, bettenBelegt = bettenBelegt, bettenFrei = math.max(0, bettenGesamt - bettenBelegt), behandlungFrei = Zaehle([[ SELECT COUNT(*) FROM medical_zimmer WHERE art = 'behandlung' AND status = 'frei' ]]), behandlungGesamt = Zaehle("SELECT COUNT(*) FROM medical_zimmer WHERE art = 'behandlung'"), opSaeleGesamt = Zaehle("SELECT COUNT(*) FROM medical_zimmer WHERE art = 'op'"), opLaufend = Zaehle("SELECT COUNT(*) FROM medical_operation WHERE status = 'laufend'"), opGeplant = Zaehle([[ SELECT COUNT(*) FROM medical_operation WHERE status = 'geplant' AND geplant_fuer >= NOW() ]]), } -- --- Offene Arbeit --- local offen = { dokumentationen = Zaehle("SELECT COUNT(*) FROM medical_behandlung WHERE status = 'laufend'"), eigeneDoku = Zaehle([[ SELECT COUNT(*) FROM medical_behandlung WHERE status = 'laufend' AND erstellt_von = ? ]], { charakterId }), laborOffen = Zaehle([[ SELECT COUNT(*) FROM medical_labor WHERE status IN ('offen', 'in_arbeit') ]]), laborUnbefundet = Zaehle([[ SELECT COUNT(*) FROM medical_labor WHERE status = 'fertig' AND befund IS NULL ]]), berichteEntwurf = Zaehle([[ SELECT COUNT(*) FROM medical_bericht WHERE status = 'entwurf' AND erstellt_von = ? ]], { charakterId }), } -- --- Kritische Warnungen --- -- Bewusst nur Bestands- und Fristenlage. Kein Patientenzustand, -- den das System selbst bewertet hätte. local warnungen = {} local leereMedikamente = Zaehle([[ SELECT COUNT(*) FROM medical_medikament WHERE aktiv = 1 AND bestand <= 0 ]]) if leereMedikamente > 0 then warnungen[#warnungen + 1] = { art = 'kritisch', titel = 'Medikamente nicht vorrätig', text = ('%d Positionen sind auf null.'):format(leereMedikamente), ziel = 'medikament', } end local knappeMedikamente = Zaehle([[ SELECT COUNT(*) FROM medical_medikament WHERE aktiv = 1 AND bestand > 0 AND bestand <= mindestbestand ]]) if knappeMedikamente > 0 then warnungen[#warnungen + 1] = { art = 'warn', titel = 'Medikamente unter Mindestbestand', text = ('%d Positionen sollten nachbestellt werden.'):format(knappeMedikamente), ziel = 'medikament', } end local ablaufendesBlut = Zaehle([[ SELECT COUNT(*) FROM medical_blut WHERE status IN ('vorraetig', 'reserviert') AND verfaellt_am IS NOT NULL AND verfaellt_am <= DATE_ADD(CURDATE(), INTERVAL ? DAY) ]], { Medizin.Config.Erinnerungen.blutVorlaufTage }) if ablaufendesBlut > 0 then warnungen[#warnungen + 1] = { art = 'warn', titel = 'Blutkonserven laufen ab', text = ('%d Konserven erreichen bald ihr Ablaufdatum.'):format(ablaufendesBlut), ziel = 'blutbank', } end if patienten.kritisch > 0 then warnungen[#warnungen + 1] = { art = 'kritisch', titel = 'Kritische Intensivpatienten', text = ('%d Patienten sind als kritisch eingetragen.'):format(patienten.kritisch), ziel = 'intensiv', } end if einsaetze.offen > 0 and imDienst == 0 then warnungen[#warnungen + 1] = { art = 'kritisch', titel = 'Offene Einsätze ohne Besetzung', text = 'Es ist niemand im Dienst.', ziel = 'leitstelle', } end if not leitstelleName and einsaetze.offen > 0 then warnungen[#warnungen + 1] = { art = 'warn', titel = 'Leitstelle unbesetzt', text = 'Offene Einsätze werden zurzeit von niemandem verteilt.', ziel = 'leitstelle', } end return { mitarbeiter = { name = mitarbeiter.name, rolleName = mitarbeiter.rolleName, station = mitarbeiter.station, funktion = mitarbeiter.funktion, }, dienst = { imDienst = imDienst, notaerzte = notaerzte, aufRtw = aufRtw, imOp = imOp, leitstelle = leitstelleName, liste = Dienst.BaueListe(), }, einsaetze = einsaetze, patienten = patienten, belegung = belegung, offen = offen, warnungen = warnungen, hinweise = Hinweise.Lade(charakterId, 15), stationen = Medizin.Config.Stationen, } end) --- Markiert einen Hinweis als gelesen Kern.RegistriereAktion('dashboard', 'hinweisGelesen', nil, function(quelle, mitarbeiter, daten) local id = tonumber(daten.id) if not id then return nil, 'Ungültige Kennung.' end Hinweise.MarkiereGelesen(mitarbeiter.charakterId, id) return { ok = true } end)