-- ===================================================================== -- aether-medical / Modul: Berichte -- -- Ein Bericht ist ein Dokument mit einer Unterschrift darunter. -- -- Das System schreibt keinen Satz davon. Es formuliert nichts vor, -- fasst nichts zusammen und leitet aus keiner Behandlung selbst eine -- Diagnose ab. Wer Text aus einer Behandlung übernehmen will, löst -- das ausdrücklich aus — und was dann im Bericht steht, verantwortet -- er mit seinem Namen. -- -- Der Weg vom Entwurf zur Freigabe wird Schritt für Schritt von -- Menschen gegangen. Kein Bericht reift von selbst, keine Frist gibt -- ihn frei, und ein freigegebener Bericht wird nie wieder geändert. -- ===================================================================== -- Lesen darf, wer Akten lesen darf. Schreiben und Freigeben verlangen -- die jeweils eigenen Rechte. Kern.RegistriereModul({ id = 'bericht', name = 'Berichte', icon = '📄', reihenfolge = 14, recht = 'akte.lesen', beschreibung = 'Berichtsentwürfe, Freigabe und Unterschriften', }) -- Die inhaltlichen Felder eines Berichts, in der Reihenfolge des Bogens local FELDER = { { id = 'symptome', name = 'Symptome und Schilderung' }, { id = 'untersuchung', name = 'Untersuchungsbefund' }, { id = 'diagnose', name = 'Diagnose' }, { id = 'massnahmen', name = 'Maßnahmen' }, { id = 'empfehlung', name = 'Empfehlung' }, } -- --------------------------------------------------------------------- -- Hilfsfunktionen -- --------------------------------------------------------------------- --- Begrenzt einen Text und liefert nil bei leerer Eingabe --- @param wert any Eingabe --- @param laenge number Maximale Zeichenzahl --- @return string|nil local function Text(wert, laenge) if type(wert) ~= 'string' then return nil end local sauber = wert:gsub('^%s+', ''):gsub('%s+$', '') if sauber == '' then return nil end return sauber:sub(1, laenge) end --- Name eines Charakters für die Anzeige --- @param charakterId number|nil Charakter --- @return string local function NameVon(charakterId) if not charakterId then return 'unbekannt' end local zeilen = MySQL.query.await( 'SELECT firstname, lastname FROM aether_characters WHERE id = ?', { charakterId }) if not zeilen or not zeilen[1] then return 'unbekannt' end return zeilen[1].firstname .. ' ' .. zeilen[1].lastname end --- Liest eine JSON-Liste aus der Datenbank --- @param roh string|nil JSON --- @return table Liste local function AlsListe(roh) if not roh then return {} end local erfolg, liste = pcall(json.decode, roh) if erfolg and type(liste) == 'table' then return liste end return {} end --- Zeitstempel für Einträge in JSON-Feldern --- @return string local function Jetzt() return os.date('%Y-%m-%d %H:%M:%S') end --- Baut einen Unterschrifteneintrag --- @param mitarbeiter table Unterzeichnender --- @param art string verfasser/freigabe/gegenzeichnung --- @return table local function Unterschrift(mitarbeiter, art) return { charakterId = mitarbeiter.charakterId, name = mitarbeiter.name, personalnummer = mitarbeiter.personalnummer, rolle = mitarbeiter.rolleName, art = art, am = Jetzt(), } end --- Prüft eine Adresse für eine Anlage --- @param wert any Eingabe --- @return string|nil local function Adresse(wert) local roh = Text(wert, 500) if not roh then return nil end if not roh:match('^https?://') then return nil end return roh end --- Lädt einen Bericht mit seinen Begleitdaten --- @param berichtId number Bericht --- @return table|nil local function LadeBericht(berichtId) local zeilen = MySQL.query.await([[ SELECT b.*, c.firstname, c.lastname, c.dob, beh.kennung AS behandlungKennung, ein.nummer AS einsatzNummer, ein.titel AS einsatzTitel FROM medical_bericht b LEFT JOIN aether_characters c ON c.id = b.patient LEFT JOIN medical_behandlung beh ON beh.id = b.behandlung LEFT JOIN medical_einsatz ein ON ein.id = b.einsatz WHERE b.id = ? ]], { berichtId }) return zeilen and zeilen[1] or nil end --- Darf dieser Mitarbeiter den Inhalt ändern? --- Nur der Verfasser, und nur solange der Bericht ein Entwurf ist. --- @param eintrag table Berichtszeile --- @param mitarbeiter table Handelnder --- @return boolean, string|nil Grund der Ablehnung local function DarfAendern(eintrag, mitarbeiter) if eintrag.status == 'freigegeben' then return false, 'Ein freigegebener Bericht wird nicht mehr geändert.' end if eintrag.status == 'eingereicht' then return false, 'Der Bericht liegt zur Freigabe vor. Dafür erst zurückziehen.' end if eintrag.erstellt_von ~= mitarbeiter.charakterId then return false, 'Diesen Entwurf hat jemand anderes geschrieben.' end return true, nil end -- ===================================================================== -- ÜBERSICHT -- ===================================================================== Kern.RegistriereAktion('bericht', 'uebersicht', nil, function(quelle, mitarbeiter, daten) local ordner = tostring(daten.ordner or 'meine') local ich = mitarbeiter.charakterId local bedingung, werte if ordner == 'freigabe' then bedingung = "b.status = 'eingereicht'" werte = {} elseif ordner == 'freigegeben' then bedingung = "b.status = 'freigegeben'" werte = {} elseif ordner == 'alle' then bedingung = '1 = 1' werte = {} else -- Eigene Entwürfe: was noch niemand ausser dem Verfasser gesehen hat bedingung = "b.status = 'entwurf' AND b.erstellt_von = ?" werte = { ich } end -- Freitextsuche über Nummer, Titel und Patientenname local begriff = Text(daten.suche, 60) if begriff then local muster = '%' .. begriff .. '%' bedingung = ('(%s) AND (b.nummer LIKE ? OR b.titel LIKE ? OR CONCAT(c.firstname, " ", c.lastname) LIKE ?)') :format(bedingung) werte[#werte + 1] = muster werte[#werte + 1] = muster werte[#werte + 1] = muster end local liste = MySQL.query.await(([[ SELECT b.id, b.nummer, b.titel, b.patient, b.behandlung, b.einsatz, b.status, b.erstellt_von, b.erstellt_am, b.diagnose, b.unterschriften, c.firstname, c.lastname, beh.kennung AS behandlungKennung, ein.nummer AS einsatzNummer FROM medical_bericht b LEFT JOIN aether_characters c ON c.id = b.patient LEFT JOIN medical_behandlung beh ON beh.id = b.behandlung LEFT JOIN medical_einsatz ein ON ein.id = b.einsatz WHERE %s ORDER BY b.id DESC LIMIT 150 ]]):format(bedingung), werte) or {} for _, eintrag in ipairs(liste) do eintrag.patientName = eintrag.firstname and (eintrag.firstname .. ' ' .. eintrag.lastname) or nil eintrag.autor = NameVon(eintrag.erstellt_von) eintrag.vonMir = eintrag.erstellt_von == ich eintrag.unterschriften = #AlsListe(eintrag.unterschriften) -- Nur ein Hinweis darauf, ob überhaupt eine Diagnose im Bericht steht eintrag.hatDiagnose = eintrag.diagnose ~= nil and eintrag.diagnose ~= '' eintrag.firstname, eintrag.lastname = nil, nil eintrag.erstellt_von, eintrag.diagnose = nil, nil end local function Zaehle(sql, parameter) return tonumber(MySQL.scalar.await(sql, parameter)) or 0 end local zaehler = { meine = Zaehle([[ SELECT COUNT(*) FROM medical_bericht WHERE status = 'entwurf' AND erstellt_von = ? ]], { ich }), freigabe = Zaehle("SELECT COUNT(*) FROM medical_bericht WHERE status = 'eingereicht'"), freigegeben = Zaehle([[ SELECT COUNT(*) FROM medical_bericht WHERE status = 'freigegeben' AND DATE(erstellt_am) = CURDATE() ]]), -- Eigene Berichte, die noch bei der Freigabe liegen meineOffen = Zaehle([[ SELECT COUNT(*) FROM medical_bericht WHERE status = 'eingereicht' AND erstellt_von = ? ]], { ich }), } return { ordner = ordner, liste = liste, zaehler = zaehler, status = Medizin.Config.Berichtstatus, felder = FELDER, rechte = { erstellen = Rechte.Hat(mitarbeiter, 'bericht.erstellen'), freigeben = Rechte.Hat(mitarbeiter, 'bericht.freigeben'), }, } end) -- ===================================================================== -- EINZELNER BERICHT -- ===================================================================== Kern.RegistriereAktion('bericht', 'oeffnen', nil, function(quelle, mitarbeiter, daten) local berichtId = tonumber(daten.id) if not berichtId then return nil, 'Ungültige Kennung.' end local eintrag = LadeBericht(berichtId) if not eintrag then return nil, 'Bericht nicht gefunden.' end -- Ein fremder Entwurf ist noch niemandes Dokument ausser dem des Verfassers if eintrag.status == 'entwurf' and eintrag.erstellt_von ~= mitarbeiter.charakterId then return nil, 'Dieser Entwurf ist noch nicht eingereicht.' end -- Schreiben darf nur, wer den Entwurf verfasst hat und das Recht dazu hat local aenderbar = DarfAendern(eintrag, mitarbeiter) local darfAendern = aenderbar and Rechte.Hat(mitarbeiter, 'bericht.erstellen') local unterschriften = AlsListe(eintrag.unterschriften) local anlagen = AlsListe(eintrag.anlagen) -- Steht Verfasser und Freigabe in einer Person? Das wird gezeigt, -- nicht verhindert. local selbstFreigabe = false for _, zeile in ipairs(unterschriften) do if zeile.art == 'freigabe' and zeile.charakterId == eintrag.erstellt_von then selbstFreigabe = true end end local spur = Protokoll.Lies({ zielTyp = 'bericht', zielId = berichtId, limit = 40 }) for _, zeile in ipairs(spur) do zeile.autor = zeile.firstname and (zeile.firstname .. ' ' .. zeile.lastname) or 'System' zeile.firstname, zeile.lastname, zeile.details = nil, nil, nil end return { bericht = { id = eintrag.id, nummer = eintrag.nummer, titel = eintrag.titel, status = eintrag.status, patient = eintrag.patient, patientName = eintrag.firstname and (eintrag.firstname .. ' ' .. eintrag.lastname) or nil, patientGeburt = eintrag.dob and tostring(eintrag.dob):sub(1, 10) or nil, behandlung = eintrag.behandlung, behandlungKennung = eintrag.behandlungKennung, einsatz = eintrag.einsatz, einsatzNummer = eintrag.einsatzNummer, einsatzTitel = eintrag.einsatzTitel, symptome = eintrag.symptome, untersuchung = eintrag.untersuchung, diagnose = eintrag.diagnose, massnahmen = eintrag.massnahmen, empfehlung = eintrag.empfehlung, autor = NameVon(eintrag.erstellt_von), vonMir = eintrag.erstellt_von == mitarbeiter.charakterId, erstelltAm = eintrag.erstellt_am, selbstFreigabe = selbstFreigabe, }, unterschriften = unterschriften, anlagen = anlagen, spur = spur, felder = FELDER, status = Medizin.Config.Berichtstatus, rechte = { erstellen = Rechte.Hat(mitarbeiter, 'bericht.erstellen'), freigeben = Rechte.Hat(mitarbeiter, 'bericht.freigeben'), aendern = darfAendern, }, } end) -- ===================================================================== -- ANLEGEN UND SCHREIBEN -- ===================================================================== --- Patientensuche und die Vorgänge, an die ein Bericht hängen kann Kern.RegistriereAktion('bericht', 'quellen', 'bericht.erstellen', function(quelle, mitarbeiter, daten) local begriff = type(daten.begriff) == 'string' and daten.begriff or '' local patientId = tonumber(daten.patient) local treffer = {} if #begriff >= 2 then local muster = '%' .. begriff .. '%' treffer = MySQL.query.await([[ SELECT c.id, c.firstname, c.lastname, c.dob, p.blutgruppe FROM aether_characters c LEFT JOIN medical_patient 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 ? OR c.dob LIKE ?) ORDER BY c.lastname, c.firstname LIMIT 20 ]], { muster, muster, muster, muster }) or {} for _, eintrag in ipairs(treffer) do eintrag.name = eintrag.firstname .. ' ' .. eintrag.lastname eintrag.dob = tostring(eintrag.dob):sub(1, 10) eintrag.firstname, eintrag.lastname = nil, nil end end -- Behandlungen und Einsätze des gewählten Patienten local behandlungen, einsaetze = {}, {} if patientId then behandlungen = MySQL.query.await([[ SELECT id, kennung, diagnose, status, erstellt_am FROM medical_behandlung WHERE patient = ? ORDER BY id DESC LIMIT 25 ]], { patientId }) or {} einsaetze = MySQL.query.await([[ SELECT e.id, e.nummer, e.titel, e.kategorie, e.erstellt_am FROM medical_einsatz e WHERE EXISTS (SELECT 1 FROM medical_behandlung b WHERE b.einsatz = e.id AND b.patient = ?) ORDER BY e.id DESC LIMIT 15 ]], { patientId }) or {} end return { treffer = treffer, behandlungen = behandlungen, einsaetze = einsaetze } end) Kern.RegistriereAktion('bericht', 'anlegen', 'bericht.erstellen', function(quelle, mitarbeiter, daten) local titel = Text(daten.titel, 160) if not titel then return nil, 'Bitte einen Titel angeben.' end -- Patient, Behandlung und Einsatz sind freiwillig — nicht jeder -- Bericht gehört zu einem einzelnen Vorgang local patientId = tonumber(daten.patient) if patientId then local person = MySQL.scalar.await( 'SELECT id FROM aether_characters WHERE id = ? AND deleted = 0', { patientId }) if not person then return nil, 'Diese Person ist nicht erfasst.' end end local behandlungId = tonumber(daten.behandlung) if behandlungId then local behandlung = MySQL.query.await( 'SELECT id, patient FROM medical_behandlung WHERE id = ?', { behandlungId }) if not behandlung or not behandlung[1] then return nil, 'Unbekannte Behandlung.' end -- Die Behandlung bestimmt den Patienten, wenn keiner gewählt wurde patientId = patientId or behandlung[1].patient end local einsatzId = tonumber(daten.einsatz) if einsatzId then local einsatz = MySQL.scalar.await( 'SELECT id FROM medical_einsatz WHERE id = ?', { einsatzId }) if not einsatz then return nil, 'Unbekannter Einsatz.' end end local nummer = Kern.ErzeugeKennung('BER', 'medical_bericht', 'nummer') local berichtId = MySQL.insert.await([[ INSERT INTO medical_bericht (nummer, patient, behandlung, einsatz, titel, status, erstellt_von) VALUES (?, ?, ?, ?, ?, 'entwurf', ?) ]], { nummer, patientId, behandlungId, einsatzId, titel, mitarbeiter.charakterId }) Protokoll.Schreibe(mitarbeiter, 'bericht', 'Bericht angelegt', { typ = 'bericht', id = berichtId }, { nummer = nummer, titel = titel, patient = patientId, behandlung = behandlungId, einsatz = einsatzId }) Kern.Loese('bericht.angelegt', berichtId, mitarbeiter) return { id = berichtId, nummer = nummer } end) Kern.RegistriereAktion('bericht', 'speichern', 'bericht.erstellen', function(quelle, mitarbeiter, daten) local berichtId = tonumber(daten.id) if not berichtId then return nil, 'Ungültige Kennung.' end local eintrag = LadeBericht(berichtId) if not eintrag then return nil, 'Bericht nicht gefunden.' end local darf, fehler = DarfAendern(eintrag, mitarbeiter) if not darf then return nil, fehler end local titel = Text(daten.titel, 160) or eintrag.titel MySQL.update.await([[ UPDATE medical_bericht SET titel = ?, symptome = ?, untersuchung = ?, diagnose = ?, massnahmen = ?, empfehlung = ? WHERE id = ? ]], { titel, Text(daten.symptome, 4000), Text(daten.untersuchung, 4000), Text(daten.diagnose, 4000), Text(daten.massnahmen, 4000), Text(daten.empfehlung, 4000), berichtId, }) Protokoll.Schreibe(mitarbeiter, 'bericht', 'Entwurf gespeichert', { typ = 'bericht', id = berichtId }, { nummer = eintrag.nummer }) return { ok = true } end) --- Übernimmt den Wortlaut einer Behandlung in den Entwurf. --- --- Das ist ein Kopiervorgang, den ein Mensch auslöst — das System --- fasst nichts zusammen und formuliert nichts um. Vorhandener Text --- im Bericht wird nicht überschrieben, sondern bleibt stehen. Kern.RegistriereAktion('bericht', 'uebernehmen', 'bericht.erstellen', function(quelle, mitarbeiter, daten) local berichtId = tonumber(daten.id) local behandlungId = tonumber(daten.behandlung) if not berichtId then return nil, 'Ungültige Kennung.' end if not behandlungId then return nil, 'Keine Behandlung gewählt.' end local eintrag = LadeBericht(berichtId) if not eintrag then return nil, 'Bericht nicht gefunden.' end local darf, fehler = DarfAendern(eintrag, mitarbeiter) if not darf then return nil, fehler end local zeilen = MySQL.query.await([[ SELECT id, kennung, patient, symptome, untersuchung, diagnose, massnahmen, empfehlung FROM medical_behandlung WHERE id = ? ]], { behandlungId }) if not zeilen or not zeilen[1] then return nil, 'Behandlung nicht gefunden.' end local behandlung = zeilen[1] -- Nur leere Felder füllen: was der Verfasser geschrieben hat, bleibt local uebernommen = {} local werte = {} for _, feld in ipairs(FELDER) do local vorhanden = Text(eintrag[feld.id], 4000) local quelle_ = Text(behandlung[feld.id], 4000) if not vorhanden and quelle_ then werte[feld.id] = quelle_ uebernommen[#uebernommen + 1] = feld.name else werte[feld.id] = vorhanden end end if #uebernommen == 0 then return nil, 'Es gibt nichts zu übernehmen — alle Felder sind bereits gefüllt oder die Behandlung ist leer.' end MySQL.update.await([[ UPDATE medical_bericht SET symptome = ?, untersuchung = ?, diagnose = ?, massnahmen = ?, empfehlung = ?, behandlung = COALESCE(behandlung, ?), patient = COALESCE(patient, ?) WHERE id = ? ]], { werte.symptome, werte.untersuchung, werte.diagnose, werte.massnahmen, werte.empfehlung, behandlungId, behandlung.patient, berichtId }) Protokoll.Schreibe(mitarbeiter, 'bericht', 'Wortlaut aus Behandlung übernommen', { typ = 'bericht', id = berichtId }, { nummer = eintrag.nummer, behandlung = behandlung.kennung, felder = uebernommen }) return { ok = true, uebernommen = uebernommen } end) -- ===================================================================== -- ANLAGEN -- ===================================================================== Kern.RegistriereAktion('bericht', 'anlage', 'bericht.erstellen', function(quelle, mitarbeiter, daten) local berichtId = tonumber(daten.id) if not berichtId then return nil, 'Ungültige Kennung.' end local eintrag = LadeBericht(berichtId) if not eintrag then return nil, 'Bericht nicht gefunden.' end local darf, fehler = DarfAendern(eintrag, mitarbeiter) if not darf then return nil, fehler end local anlagen = AlsListe(eintrag.anlagen) local entfernen = tonumber(daten.entfernen) if entfernen then -- Entfernen ist nur im Entwurf möglich; ab der Einreichung -- gehört die Anlage zum Dokument if entfernen < 1 or entfernen > #anlagen then return nil, 'Diese Anlage gibt es nicht.' end local weg = table.remove(anlagen, entfernen) Protokoll.Schreibe(mitarbeiter, 'bericht', 'Anlage entfernt', { typ = 'bericht', id = berichtId }, { nummer = eintrag.nummer, titel = weg and weg.titel or nil }) else local titel = Text(daten.titel, 120) local adresse = Adresse(daten.adresse) if not titel then return nil, 'Bitte die Anlage benennen.' end if not adresse then return nil, 'Die Adresse muss mit http:// oder https:// beginnen.' end if #anlagen >= 12 then return nil, 'Mehr als zwölf Anlagen sind nicht vorgesehen.' end anlagen[#anlagen + 1] = { titel = titel, adresse = adresse, von = mitarbeiter.name, am = Jetzt(), } Protokoll.Schreibe(mitarbeiter, 'bericht', 'Anlage hinzugefügt', { typ = 'bericht', id = berichtId }, { nummer = eintrag.nummer, titel = titel }) end MySQL.update.await('UPDATE medical_bericht SET anlagen = ? WHERE id = ?', { #anlagen > 0 and json.encode(anlagen) or nil, berichtId }) return { ok = true, anzahl = #anlagen } end) -- ===================================================================== -- EINREICHEN UND ZURÜCKZIEHEN -- ===================================================================== Kern.RegistriereAktion('bericht', 'einreichen', 'bericht.erstellen', function(quelle, mitarbeiter, daten) local berichtId = tonumber(daten.id) if not berichtId then return nil, 'Ungültige Kennung.' end local eintrag = LadeBericht(berichtId) if not eintrag then return nil, 'Bericht nicht gefunden.' end local darf, fehler = DarfAendern(eintrag, mitarbeiter) if not darf then return nil, fehler end -- Ein leerer Bericht ist kein Bericht. Welche Felder fehlen, wird -- nur benannt — die Einreichung hält das nicht auf. local gefuellt, leer = 0, {} for _, feld in ipairs(FELDER) do if Text(eintrag[feld.id], 4000) then gefuellt = gefuellt + 1 else leer[#leer + 1] = feld.name end end if gefuellt == 0 then return nil, 'Der Bericht enthält noch keinen Text.' end local unterschriften = AlsListe(eintrag.unterschriften) unterschriften[#unterschriften + 1] = Unterschrift(mitarbeiter, 'verfasser') MySQL.update.await([[ UPDATE medical_bericht SET status = 'eingereicht', unterschriften = ? WHERE id = ? ]], { json.encode(unterschriften), berichtId }) Protokoll.Schreibe(mitarbeiter, 'bericht', 'Bericht eingereicht', { typ = 'bericht', id = berichtId }, { nummer = eintrag.nummer, leereFelder = leer }) -- Wer freigeben darf, erfährt davon for _, person in pairs(Dienst.AlleImDienst()) do if Rechte.Hat(person, 'bericht.freigeben') and person.charakterId ~= mitarbeiter.charakterId then Hinweise.Erstelle({ charakterId = person.charakterId }, { kategorie = 'doku', prioritaet = 'normal', titel = 'Bericht liegt zur Freigabe', text = ('%s — %s, von %s.') :format(eintrag.nummer, eintrag.titel, mitarbeiter.name), zielTyp = 'bericht', zielId = berichtId, }) end end Kern.Loese('bericht.eingereicht', berichtId, mitarbeiter) return { ok = true, leereFelder = leer } end) Kern.RegistriereAktion('bericht', 'zurueckziehen', 'bericht.erstellen', function(quelle, mitarbeiter, daten) local berichtId = tonumber(daten.id) if not berichtId then return nil, 'Ungültige Kennung.' end local grund = Text(daten.grund, 255) if not grund then return nil, 'Bitte angeben, warum der Bericht zurückgeht.' end local eintrag = LadeBericht(berichtId) if not eintrag then return nil, 'Bericht nicht gefunden.' end if eintrag.status ~= 'eingereicht' then return nil, 'Nur ein eingereichter Bericht lässt sich zurückziehen.' end if eintrag.erstellt_von ~= mitarbeiter.charakterId then return nil, 'Zurückziehen kann nur, wer den Bericht geschrieben hat.' end -- Die geleistete Unterschrift bleibt stehen: sie wurde geleistet. -- Dass der Bericht danach wieder geändert wurde, steht im Protokoll. MySQL.update.await("UPDATE medical_bericht SET status = 'entwurf' WHERE id = ?", { berichtId }) Protokoll.Schreibe(mitarbeiter, 'bericht', 'Bericht zurückgezogen', { typ = 'bericht', id = berichtId }, { nummer = eintrag.nummer }, grund) Kern.Loese('bericht.zurueckgezogen', berichtId, mitarbeiter) return { ok = true } end) -- ===================================================================== -- FREIGABE -- -- Die Freigabe ist eine Unterschrift. Sie geschieht nie automatisch, -- nie nach Ablauf einer Frist und nie durch das System. -- ===================================================================== Kern.RegistriereAktion('bericht', 'freigeben', 'bericht.freigeben', function(quelle, mitarbeiter, daten) local berichtId = tonumber(daten.id) if not berichtId then return nil, 'Ungültige Kennung.' end local eintrag = LadeBericht(berichtId) if not eintrag then return nil, 'Bericht nicht gefunden.' end if eintrag.status ~= 'eingereicht' then return nil, 'Nur ein eingereichter Bericht wird freigegeben.' end local unterschriften = AlsListe(eintrag.unterschriften) unterschriften[#unterschriften + 1] = Unterschrift(mitarbeiter, 'freigabe') -- Verfasser und Freigabe in einer Person ist möglich und wird -- festgehalten — verhindert wird es nicht. local selbstFreigabe = eintrag.erstellt_von == mitarbeiter.charakterId MySQL.update.await([[ UPDATE medical_bericht SET status = 'freigegeben', unterschriften = ? WHERE id = ? ]], { json.encode(unterschriften), berichtId }) Protokoll.Schreibe(mitarbeiter, 'bericht', 'Bericht freigegeben', { typ = 'bericht', id = berichtId }, { nummer = eintrag.nummer, selbstFreigabe = selbstFreigabe }, Text(daten.vermerk, 255)) if not selbstFreigabe then Hinweise.Erstelle({ charakterId = eintrag.erstellt_von }, { kategorie = 'doku', prioritaet = 'normal', titel = 'Dein Bericht wurde freigegeben', text = ('%s — %s, freigegeben von %s.') :format(eintrag.nummer, eintrag.titel, mitarbeiter.name), zielTyp = 'bericht', zielId = berichtId, }) end Kern.Loese('bericht.freigegeben', berichtId, mitarbeiter) return { ok = true, selbstFreigabe = selbstFreigabe } end) Kern.RegistriereAktion('bericht', 'zurueckweisen', 'bericht.freigeben', function(quelle, mitarbeiter, daten) local berichtId = tonumber(daten.id) if not berichtId then return nil, 'Ungültige Kennung.' end local grund = Text(daten.grund, 500) if not grund or #grund < 5 then return nil, 'Eine Zurückweisung braucht eine Begründung.' end local eintrag = LadeBericht(berichtId) if not eintrag then return nil, 'Bericht nicht gefunden.' end if eintrag.status ~= 'eingereicht' then return nil, 'Nur ein eingereichter Bericht lässt sich zurückweisen.' end MySQL.update.await("UPDATE medical_bericht SET status = 'entwurf' WHERE id = ?", { berichtId }) Protokoll.Schreibe(mitarbeiter, 'bericht', 'Bericht zurückgewiesen', { typ = 'bericht', id = berichtId }, { nummer = eintrag.nummer }, grund) Hinweise.Erstelle({ charakterId = eintrag.erstellt_von }, { kategorie = 'doku', prioritaet = 'hoch', titel = 'Bericht zurückgewiesen', text = ('%s — %s: %s'):format(eintrag.nummer, mitarbeiter.name, grund:sub(1, 120)), zielTyp = 'bericht', zielId = berichtId, }) Kern.Loese('bericht.zurueckgewiesen', berichtId, mitarbeiter) return { ok = true } end) --- Zeichnet einen Bericht gegen. --- Eine Gegenzeichnung ändert nichts am Wortlaut — sie fügt nur eine --- weitere Unterschrift hinzu. Zurücknehmen lässt sie sich nicht. Kern.RegistriereAktion('bericht', 'gegenzeichnen', 'bericht.freigeben', function(quelle, mitarbeiter, daten) local berichtId = tonumber(daten.id) if not berichtId then return nil, 'Ungültige Kennung.' end local eintrag = LadeBericht(berichtId) if not eintrag then return nil, 'Bericht nicht gefunden.' end if eintrag.status == 'entwurf' then return nil, 'Ein Entwurf wird nicht gegengezeichnet.' end local unterschriften = AlsListe(eintrag.unterschriften) -- Zweimal dieselbe Unterschrift sagt nichts Zusätzliches for _, zeile in ipairs(unterschriften) do if zeile.charakterId == mitarbeiter.charakterId and zeile.art == 'gegenzeichnung' then return nil, 'Du hast diesen Bericht bereits gegengezeichnet.' end end unterschriften[#unterschriften + 1] = Unterschrift(mitarbeiter, 'gegenzeichnung') MySQL.update.await('UPDATE medical_bericht SET unterschriften = ? WHERE id = ?', { json.encode(unterschriften), berichtId }) Protokoll.Schreibe(mitarbeiter, 'bericht', 'Bericht gegengezeichnet', { typ = 'bericht', id = berichtId }, { nummer = eintrag.nummer }, Text(daten.vermerk, 255)) return { ok = true } end) -- --------------------------------------------------------------------- -- Schnittstelle für andere Ressourcen -- --------------------------------------------------------------------- --- Liefert die freigegebenen Berichte eines Patienten. --- Reine Auskunft — keine Ressource kann darüber einen Bericht --- schreiben, ändern oder freigeben. Entwürfe bleiben aussen vor. --- @param patientId number Charakter --- @return table Liste exports('HoleBerichte', function(patientId) if not tonumber(patientId) then return {} end return MySQL.query.await([[ SELECT nummer, titel, diagnose, erstellt_am FROM medical_bericht WHERE patient = ? AND status = 'freigegeben' ORDER BY id DESC LIMIT 50 ]], { tonumber(patientId) }) or {} end)