- 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>
541 lines
22 KiB
Lua
541 lines
22 KiB
Lua
-- =====================================================================
|
|
-- aether-medical / Modul: Labor
|
|
--
|
|
-- Das Labor speichert AUSSCHLIESSLICH Ergebnisse.
|
|
--
|
|
-- Deshalb sind zwei Dinge strikt getrennt:
|
|
-- * `ergebnis` — die gemessenen Werte. Trägt das Labor ein.
|
|
-- * `befund` — die Bewertung. Trägt ein Mediziner ein.
|
|
--
|
|
-- Das System vergleicht keine Werte mit Referenzbereichen, markiert
|
|
-- nichts als auffällig und leitet aus keinem Ergebnis eine Diagnose
|
|
-- ab. Ein Messwert ist eine Zahl, keine Aussage.
|
|
--
|
|
-- Auch eine im Labor bestimmte Blutgruppe wandert NICHT von selbst in
|
|
-- die Stammdaten — das muss ein Mediziner ausdrücklich übernehmen.
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereModul({
|
|
id = 'labor', name = 'Labor', icon = '🧪',
|
|
reihenfolge = 6,
|
|
-- Ein Recht daraus genügt: Auftraggeber, Labor und Befunder teilen sich das Modul
|
|
recht = { 'labor.beauftragen', 'labor.bearbeiten', 'labor.befunden' },
|
|
beschreibung = 'Laboraufträge, Ergebnisse und Befunde',
|
|
})
|
|
|
|
-- Schreibhilfen je Probenart. Reine Textvorlagen — sie enthalten keine
|
|
-- Referenzbereiche und bewerten nichts, sie geben nur die Struktur vor.
|
|
local VORLAGEN = {
|
|
blut = 'Hb:\nLeukozyten:\nThrombozyten:\nCRP:\nKreatinin:\nNatrium:\nKalium:\n'
|
|
.. 'Blutgruppe:\n\nBemerkung des Labors:',
|
|
urin = 'Farbe:\nEiweiß:\nGlukose:\nBlut:\nLeukozyten:\nNitrit:\n\nBemerkung des Labors:',
|
|
dna = 'Probenart:\nProfil vollständig (ja/nein):\nÜbereinstimmung:\n\nBemerkung des Labors:',
|
|
gewebe = 'Entnahmeort:\nMakroskopisch:\nMikroskopisch:\n\nBemerkung des Labors:',
|
|
toxikologie = 'Alkohol:\nOpiate:\nAmphetamine:\nCannabinoide:\nBenzodiazepine:\n'
|
|
.. '\nBemerkung des Labors:',
|
|
}
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Hilfsfunktionen
|
|
-- ---------------------------------------------------------------------
|
|
|
|
--- Begrenzt einen Text und liefert nil bei leerer Eingabe
|
|
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
|
|
|
|
--- Prüft eine Probenart gegen die Konfiguration
|
|
--- @param kennung any Eingabe
|
|
--- @return string|nil
|
|
local function Probenart(kennung)
|
|
for _, eintrag in ipairs(Medizin.Config.Laborarten) do
|
|
if eintrag.id == kennung then return eintrag.id end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
--- Name eines Mitarbeiters für die Anzeige
|
|
local function NameVon(charakterId)
|
|
if not charakterId then return 'unbekannt' end
|
|
local zeilen = MySQL.query.await([[
|
|
SELECT c.firstname, c.lastname FROM aether_characters c WHERE c.id = ?
|
|
]], { charakterId })
|
|
if not zeilen or not zeilen[1] then return 'unbekannt' end
|
|
return zeilen[1].firstname .. ' ' .. zeilen[1].lastname
|
|
end
|
|
|
|
-- =====================================================================
|
|
-- ÜBERSICHT
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereAktion('labor', 'uebersicht', nil, function(quelle, mitarbeiter, daten)
|
|
local ordner = tostring(daten.ordner or 'offen')
|
|
local bedingungen, werte = {}, {}
|
|
|
|
if ordner == 'meine' then
|
|
bedingungen[#bedingungen + 1] = 'l.auftrag_von = ?'
|
|
werte[#werte + 1] = mitarbeiter.charakterId
|
|
elseif ordner == 'befund' then
|
|
-- Ergebnis liegt vor, die Bewertung fehlt noch
|
|
bedingungen[#bedingungen + 1] = "l.status = 'fertig' AND l.befund IS NULL"
|
|
elseif ordner == 'archiv' then
|
|
bedingungen[#bedingungen + 1] = "(l.befund IS NOT NULL OR l.status = 'verworfen')"
|
|
else
|
|
bedingungen[#bedingungen + 1] = "l.status IN ('offen', 'in_arbeit')"
|
|
end
|
|
|
|
if daten.art and daten.art ~= '' then
|
|
bedingungen[#bedingungen + 1] = 'l.art = ?'
|
|
werte[#werte + 1] = daten.art
|
|
end
|
|
|
|
if type(daten.begriff) == 'string' and #daten.begriff >= 2 then
|
|
bedingungen[#bedingungen + 1] =
|
|
'(l.kennung LIKE ? OR c.firstname LIKE ? OR c.lastname LIKE ?)'
|
|
local muster = '%' .. daten.begriff .. '%'
|
|
for _ = 1, 3 do werte[#werte + 1] = muster end
|
|
end
|
|
|
|
local liste = MySQL.query.await(([[
|
|
SELECT l.id, l.kennung, l.patient, l.art, l.fragestellung, l.dringend,
|
|
l.status, l.auftrag_am, l.auftrag_von, l.bearbeitet_am,
|
|
l.ergebnis IS NOT NULL AS hatErgebnis,
|
|
l.befund IS NOT NULL AS hatBefund,
|
|
TIMESTAMPDIFF(MINUTE, l.auftrag_am, NOW()) AS offenSeit,
|
|
c.firstname, c.lastname, c.dob
|
|
FROM medical_labor l
|
|
JOIN aether_characters c ON c.id = l.patient
|
|
WHERE %s
|
|
ORDER BY l.dringend DESC, l.id DESC
|
|
LIMIT 120
|
|
]]):format(table.concat(bedingungen, ' AND ')), werte) or {}
|
|
|
|
local artNamen = {}
|
|
for _, eintrag in ipairs(Medizin.Config.Laborarten) do artNamen[eintrag.id] = eintrag.name end
|
|
|
|
for _, eintrag in ipairs(liste) do
|
|
eintrag.patientName = eintrag.firstname .. ' ' .. eintrag.lastname
|
|
eintrag.geburt = tostring(eintrag.dob):sub(1, 10)
|
|
eintrag.artName = artNamen[eintrag.art] or eintrag.art
|
|
eintrag.auftragVon = NameVon(eintrag.auftrag_von)
|
|
eintrag.eigene = eintrag.auftrag_von == mitarbeiter.charakterId
|
|
eintrag.dringend = eintrag.dringend == 1
|
|
eintrag.hatErgebnis = tonumber(eintrag.hatErgebnis) == 1
|
|
eintrag.hatBefund = tonumber(eintrag.hatBefund) == 1
|
|
eintrag.firstname, eintrag.lastname, eintrag.dob = nil, nil, nil
|
|
eintrag.auftrag_von = nil
|
|
end
|
|
|
|
return {
|
|
ordner = ordner,
|
|
liste = liste,
|
|
arten = Medizin.Config.Laborarten,
|
|
zaehler = {
|
|
offen = tonumber(MySQL.scalar.await(
|
|
"SELECT COUNT(*) FROM medical_labor WHERE status IN ('offen', 'in_arbeit')")) or 0,
|
|
befund = tonumber(MySQL.scalar.await(
|
|
"SELECT COUNT(*) FROM medical_labor WHERE status = 'fertig' AND befund IS NULL")) or 0,
|
|
meine = tonumber(MySQL.scalar.await([[
|
|
SELECT COUNT(*) FROM medical_labor
|
|
WHERE auftrag_von = ? AND status <> 'verworfen'
|
|
]], { mitarbeiter.charakterId })) or 0,
|
|
dringend = tonumber(MySQL.scalar.await([[
|
|
SELECT COUNT(*) FROM medical_labor
|
|
WHERE dringend = 1 AND status IN ('offen', 'in_arbeit')
|
|
]])) or 0,
|
|
},
|
|
rechte = {
|
|
beauftragen = Rechte.Hat(mitarbeiter, 'labor.beauftragen'),
|
|
bearbeiten = Rechte.Hat(mitarbeiter, 'labor.bearbeiten'),
|
|
befunden = Rechte.Hat(mitarbeiter, 'labor.befunden'),
|
|
stammdaten = Rechte.Hat(mitarbeiter, 'patient.stammdaten'),
|
|
},
|
|
}
|
|
end)
|
|
|
|
Kern.RegistriereAktion('labor', 'oeffnen', nil, function(quelle, mitarbeiter, daten)
|
|
local laborId = tonumber(daten.id)
|
|
if not laborId then return nil, 'Ungültige Kennung.' end
|
|
|
|
local zeilen = MySQL.query.await([[
|
|
SELECT l.*, c.firstname, c.lastname, c.dob,
|
|
m.blutgruppe, m.allergien, m.hinweis
|
|
FROM medical_labor l
|
|
JOIN aether_characters c ON c.id = l.patient
|
|
LEFT JOIN medical_patient m ON m.`character` = l.patient
|
|
WHERE l.id = ?
|
|
]], { laborId })
|
|
|
|
if not zeilen or not zeilen[1] then return nil, 'Laborauftrag nicht gefunden.' end
|
|
local l = zeilen[1]
|
|
|
|
local artName = l.art
|
|
for _, eintrag in ipairs(Medizin.Config.Laborarten) do
|
|
if eintrag.id == l.art then artName = eintrag.name end
|
|
end
|
|
|
|
-- Frühere Aufträge desselben Patienten zum Vergleich
|
|
local frueher = MySQL.query.await([[
|
|
SELECT id, kennung, art, status, auftrag_am, ergebnis IS NOT NULL AS hatErgebnis
|
|
FROM medical_labor
|
|
WHERE patient = ? AND id <> ?
|
|
ORDER BY id DESC LIMIT 10
|
|
]], { l.patient, laborId }) or {}
|
|
|
|
for _, eintrag in ipairs(frueher) do
|
|
eintrag.hatErgebnis = tonumber(eintrag.hatErgebnis) == 1
|
|
end
|
|
|
|
return {
|
|
auftrag = {
|
|
id = l.id, kennung = l.kennung, patient = l.patient,
|
|
patientName = l.firstname .. ' ' .. l.lastname,
|
|
geburt = tostring(l.dob):sub(1, 10),
|
|
art = l.art, artName = artName,
|
|
fragestellung = l.fragestellung,
|
|
dringend = l.dringend == 1,
|
|
status = l.status,
|
|
auftragAm = l.auftrag_am,
|
|
auftragVon = NameVon(l.auftrag_von),
|
|
istMeinAuftrag = l.auftrag_von == mitarbeiter.charakterId,
|
|
bearbeitetAm = l.bearbeitet_am,
|
|
bearbeitetVon = l.bearbeitet_von and NameVon(l.bearbeitet_von) or nil,
|
|
ergebnis = l.ergebnis,
|
|
befund = l.befund,
|
|
befundAm = l.befund_am,
|
|
befundVon = l.befund_von and NameVon(l.befund_von) or nil,
|
|
},
|
|
stamm = {
|
|
blutgruppe = l.blutgruppe, allergien = l.allergien, hinweis = l.hinweis,
|
|
},
|
|
frueher = frueher,
|
|
vorlage = VORLAGEN[l.art],
|
|
rechte = {
|
|
bearbeiten = Rechte.Hat(mitarbeiter, 'labor.bearbeiten'),
|
|
befunden = Rechte.Hat(mitarbeiter, 'labor.befunden'),
|
|
stammdaten = Rechte.Hat(mitarbeiter, 'patient.stammdaten'),
|
|
},
|
|
blutgruppen = Medizin.Config.Blutgruppen,
|
|
}
|
|
end)
|
|
|
|
-- =====================================================================
|
|
-- AUFTRAG STELLEN
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereAktion('labor', 'patientSuche', 'labor.beauftragen', function(quelle, mitarbeiter, 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, m.blutgruppe, m.hinweis
|
|
FROM aether_characters c
|
|
LEFT JOIN medical_patient m ON m.`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.dob = tostring(eintrag.dob):sub(1, 10)
|
|
end
|
|
|
|
return { treffer = treffer }
|
|
end)
|
|
|
|
Kern.RegistriereAktion('labor', 'beauftragen', 'labor.beauftragen', function(quelle, mitarbeiter, daten)
|
|
local patientId = tonumber(daten.patient)
|
|
if not patientId then return nil, 'Kein Patient gewählt.' end
|
|
|
|
local person = MySQL.query.await(
|
|
'SELECT firstname, lastname FROM aether_characters WHERE id = ? AND deleted = 0',
|
|
{ patientId })
|
|
if not person or not person[1] then return nil, 'Diese Person ist nicht erfasst.' end
|
|
|
|
local art = Probenart(daten.art)
|
|
if not art then return nil, 'Unbekannte Probenart.' end
|
|
|
|
local fragestellung = Text(daten.fragestellung, 512)
|
|
if not fragestellung then
|
|
return nil, 'Bitte angeben, wonach gesucht werden soll.'
|
|
end
|
|
|
|
local dringend = daten.dringend == true
|
|
local kennung = Kern.ErzeugeKennung('LAB', 'medical_labor', 'kennung')
|
|
|
|
local id = MySQL.insert.await([[
|
|
INSERT INTO medical_labor
|
|
(kennung, patient, art, fragestellung, dringend, status, auftrag_von)
|
|
VALUES (?, ?, ?, ?, ?, 'offen', ?)
|
|
]], { kennung, patientId, art, fragestellung, dringend and 1 or 0,
|
|
mitarbeiter.charakterId })
|
|
|
|
-- Optionale Verknüpfung mit einer Behandlung
|
|
local behandlungId = tonumber(daten.behandlung)
|
|
if behandlungId then
|
|
local gueltig = MySQL.scalar.await(
|
|
'SELECT id FROM medical_behandlung WHERE id = ? AND patient = ?',
|
|
{ behandlungId, patientId })
|
|
if gueltig then
|
|
MySQL.insert.await([[
|
|
INSERT INTO medical_verknuepfung (von_typ, von_id, zu_typ, zu_id, von)
|
|
VALUES ('labor', ?, 'behandlung', ?, ?)
|
|
]], { tostring(id), tostring(behandlungId), mitarbeiter.charakterId })
|
|
end
|
|
end
|
|
|
|
Protokoll.Schreibe(mitarbeiter, 'labor', 'Laborauftrag gestellt',
|
|
{ typ = 'labor', id = id },
|
|
{ kennung = kennung, art = art, patient = patientId, dringend = dringend })
|
|
|
|
-- Das Labor erfährt davon. Bearbeitet wird nichts von selbst.
|
|
Hinweise.Erstelle(nil, {
|
|
kategorie = 'labor',
|
|
prioritaet = dringend and 'hoch' or 'normal',
|
|
titel = dringend and ('Dringender Laborauftrag · %s'):format(kennung)
|
|
or ('Neuer Laborauftrag · %s'):format(kennung),
|
|
text = ('%s %s — %s'):format(person[1].firstname, person[1].lastname, fragestellung),
|
|
zielTyp = 'labor', zielId = id,
|
|
})
|
|
|
|
Kern.Loese('labor.beauftragt', id, mitarbeiter)
|
|
|
|
return { id = id, kennung = kennung }
|
|
end)
|
|
|
|
-- =====================================================================
|
|
-- BEARBEITUNG IM LABOR
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereAktion('labor', 'annehmen', 'labor.bearbeiten', function(quelle, mitarbeiter, daten)
|
|
local laborId = tonumber(daten.id)
|
|
if not laborId then return nil, 'Ungültige Kennung.' end
|
|
|
|
local zeilen = MySQL.query.await(
|
|
'SELECT id, kennung, status FROM medical_labor WHERE id = ?', { laborId })
|
|
if not zeilen or not zeilen[1] then return nil, 'Laborauftrag nicht gefunden.' end
|
|
if zeilen[1].status ~= 'offen' then
|
|
return nil, 'Dieser Auftrag ist bereits in Arbeit oder abgeschlossen.'
|
|
end
|
|
|
|
MySQL.update.await([[
|
|
UPDATE medical_labor
|
|
SET status = 'in_arbeit', bearbeitet_von = ?, bearbeitet_am = NOW()
|
|
WHERE id = ?
|
|
]], { mitarbeiter.charakterId, laborId })
|
|
|
|
Protokoll.Schreibe(mitarbeiter, 'labor', 'Auftrag in Arbeit genommen',
|
|
{ typ = 'labor', id = laborId }, { kennung = zeilen[1].kennung })
|
|
|
|
return { ok = true }
|
|
end)
|
|
|
|
--- Trägt die gemessenen Werte ein. Bewertet wird hier nichts.
|
|
Kern.RegistriereAktion('labor', 'ergebnis', 'labor.bearbeiten', function(quelle, mitarbeiter, daten)
|
|
local laborId = tonumber(daten.id)
|
|
if not laborId then return nil, 'Ungültige Kennung.' end
|
|
|
|
local ergebnis = Text(daten.ergebnis, 8000)
|
|
if not ergebnis or #ergebnis < 3 then return nil, 'Bitte ein Ergebnis eintragen.' end
|
|
|
|
local zeilen = MySQL.query.await([[
|
|
SELECT l.id, l.kennung, l.status, l.dringend, l.auftrag_von, l.art,
|
|
c.firstname, c.lastname
|
|
FROM medical_labor l
|
|
JOIN aether_characters c ON c.id = l.patient
|
|
WHERE l.id = ?
|
|
]], { laborId })
|
|
if not zeilen or not zeilen[1] then return nil, 'Laborauftrag nicht gefunden.' end
|
|
|
|
local auftrag = zeilen[1]
|
|
if auftrag.status == 'verworfen' then
|
|
return nil, 'Dieser Auftrag wurde verworfen.'
|
|
end
|
|
|
|
MySQL.update.await([[
|
|
UPDATE medical_labor
|
|
SET ergebnis = ?, status = 'fertig',
|
|
bearbeitet_von = COALESCE(bearbeitet_von, ?),
|
|
bearbeitet_am = COALESCE(bearbeitet_am, NOW())
|
|
WHERE id = ?
|
|
]], { ergebnis, mitarbeiter.charakterId, laborId })
|
|
|
|
Protokoll.Schreibe(mitarbeiter, 'labor', 'Ergebnis eingetragen',
|
|
{ typ = 'labor', id = laborId }, { kennung = auftrag.kennung, art = auftrag.art })
|
|
|
|
-- Den Auftraggeber sofort informieren. Bewertet ist damit nichts.
|
|
Hinweise.Erstelle({ charakterId = auftrag.auftrag_von }, {
|
|
kategorie = 'labor',
|
|
prioritaet = auftrag.dringend == 1 and 'hoch' or 'normal',
|
|
titel = ('Laborergebnis verfügbar · %s'):format(auftrag.kennung),
|
|
text = ('%s %s — die Bewertung steht noch aus.')
|
|
:format(auftrag.firstname, auftrag.lastname),
|
|
zielTyp = 'labor', zielId = laborId,
|
|
})
|
|
|
|
-- Die Fristenprüfung soll das nicht gleich noch einmal melden
|
|
Hinweise.AlsGemeldet('labor_' .. laborId, 'fertig')
|
|
|
|
Kern.Loese('labor.fertig', laborId, mitarbeiter)
|
|
|
|
return { ok = true }
|
|
end)
|
|
|
|
--- Probe unbrauchbar: Auftrag verwerfen, immer mit Begründung
|
|
Kern.RegistriereAktion('labor', 'verwerfen', 'labor.bearbeiten', function(quelle, mitarbeiter, daten)
|
|
local laborId = tonumber(daten.id)
|
|
if not laborId then return nil, 'Ungültige Kennung.' end
|
|
|
|
local grund = Text(daten.grund, 512)
|
|
if not grund then return nil, 'Bitte begründen, warum der Auftrag verworfen wird.' end
|
|
|
|
local zeilen = MySQL.query.await([[
|
|
SELECT l.id, l.kennung, l.status, l.auftrag_von, c.firstname, c.lastname
|
|
FROM medical_labor l
|
|
JOIN aether_characters c ON c.id = l.patient
|
|
WHERE l.id = ?
|
|
]], { laborId })
|
|
if not zeilen or not zeilen[1] then return nil, 'Laborauftrag nicht gefunden.' end
|
|
|
|
local auftrag = zeilen[1]
|
|
if auftrag.status == 'verworfen' then
|
|
return nil, 'Dieser Auftrag ist bereits verworfen.'
|
|
end
|
|
|
|
MySQL.update.await([[
|
|
UPDATE medical_labor
|
|
SET status = 'verworfen', ergebnis = ?,
|
|
bearbeitet_von = COALESCE(bearbeitet_von, ?),
|
|
bearbeitet_am = COALESCE(bearbeitet_am, NOW())
|
|
WHERE id = ?
|
|
]], { 'Verworfen: ' .. grund, mitarbeiter.charakterId, laborId })
|
|
|
|
Protokoll.Schreibe(mitarbeiter, 'labor', 'Auftrag verworfen',
|
|
{ typ = 'labor', id = laborId }, { kennung = auftrag.kennung }, grund)
|
|
|
|
Hinweise.Erstelle({ charakterId = auftrag.auftrag_von }, {
|
|
kategorie = 'labor', prioritaet = 'normal',
|
|
titel = ('Laborauftrag verworfen · %s'):format(auftrag.kennung),
|
|
text = ('%s %s — %s. Ein neuer Auftrag muss von Hand gestellt werden.')
|
|
:format(auftrag.firstname, auftrag.lastname, grund),
|
|
zielTyp = 'labor', zielId = laborId,
|
|
})
|
|
|
|
Hinweise.AlsGemeldet('labor_' .. laborId, 'fertig')
|
|
|
|
return { ok = true }
|
|
end)
|
|
|
|
-- =====================================================================
|
|
-- BEFUND
|
|
--
|
|
-- Die Bewertung eines Ergebnisses. Sie stammt immer von einem
|
|
-- Mediziner — das System schreibt hier nie etwas hinein.
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereAktion('labor', 'befund', 'labor.befunden', function(quelle, mitarbeiter, daten)
|
|
local laborId = tonumber(daten.id)
|
|
if not laborId then return nil, 'Ungültige Kennung.' end
|
|
|
|
local befund = Text(daten.befund, 8000)
|
|
if not befund or #befund < 3 then return nil, 'Bitte einen Befund eintragen.' end
|
|
|
|
local zeilen = MySQL.query.await(
|
|
'SELECT id, kennung, status, ergebnis FROM medical_labor WHERE id = ?', { laborId })
|
|
if not zeilen or not zeilen[1] then return nil, 'Laborauftrag nicht gefunden.' end
|
|
|
|
local auftrag = zeilen[1]
|
|
if not auftrag.ergebnis then
|
|
return nil, 'Es liegt noch kein Ergebnis vor, das sich bewerten ließe.'
|
|
end
|
|
|
|
MySQL.update.await([[
|
|
UPDATE medical_labor SET befund = ?, befund_von = ?, befund_am = NOW()
|
|
WHERE id = ?
|
|
]], { befund, mitarbeiter.charakterId, laborId })
|
|
|
|
Protokoll.Schreibe(mitarbeiter, 'labor', 'Befund erstellt',
|
|
{ typ = 'labor', id = laborId }, { kennung = auftrag.kennung })
|
|
|
|
Kern.Loese('labor.befundet', laborId, mitarbeiter)
|
|
|
|
return { ok = true }
|
|
end)
|
|
|
|
-- =====================================================================
|
|
-- BLUTGRUPPE ÜBERNEHMEN
|
|
--
|
|
-- Bewusst ein eigener, ausdrücklicher Schritt. Das Labor kann eine
|
|
-- Blutgruppe bestimmt haben — in die Stammdaten trägt sie ein
|
|
-- Mediziner ein, weil er dafür geradesteht.
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereAktion('labor', 'blutgruppeUebernehmen', 'patient.stammdaten', function(quelle, mitarbeiter, daten)
|
|
local laborId = tonumber(daten.id)
|
|
if not laborId then return nil, 'Ungültige Kennung.' end
|
|
|
|
local zeilen = MySQL.query.await([[
|
|
SELECT l.id, l.kennung, l.patient, l.ergebnis, c.firstname, c.lastname
|
|
FROM medical_labor l
|
|
JOIN aether_characters c ON c.id = l.patient
|
|
WHERE l.id = ?
|
|
]], { laborId })
|
|
if not zeilen or not zeilen[1] then return nil, 'Laborauftrag nicht gefunden.' end
|
|
|
|
local auftrag = zeilen[1]
|
|
if not auftrag.ergebnis then
|
|
return nil, 'Zu diesem Auftrag liegt kein Ergebnis vor.'
|
|
end
|
|
|
|
-- Die Blutgruppe wird nicht aus dem Text gelesen, sondern gewählt
|
|
local blutgruppe = nil
|
|
for _, gruppe in ipairs(Medizin.Config.Blutgruppen) do
|
|
if gruppe == daten.blutgruppe then blutgruppe = gruppe break end
|
|
end
|
|
if not blutgruppe then return nil, 'Bitte eine gültige Blutgruppe wählen.' end
|
|
|
|
local alt = MySQL.scalar.await(
|
|
'SELECT blutgruppe FROM medical_patient WHERE `character` = ?', { auftrag.patient })
|
|
|
|
MySQL.query.await([[
|
|
INSERT INTO medical_patient (`character`, blutgruppe, aktualisiert_von, aktualisiert_am)
|
|
VALUES (?, ?, ?, NOW())
|
|
ON DUPLICATE KEY UPDATE
|
|
blutgruppe = VALUES(blutgruppe),
|
|
aktualisiert_von = VALUES(aktualisiert_von),
|
|
aktualisiert_am = NOW()
|
|
]], { auftrag.patient, blutgruppe, mitarbeiter.charakterId })
|
|
|
|
Protokoll.Schreibe(mitarbeiter, 'labor', 'Blutgruppe in Stammdaten übernommen',
|
|
{ typ = 'patient', id = auftrag.patient },
|
|
{ quelle = auftrag.kennung, von = alt, nach = blutgruppe })
|
|
|
|
Kern.Loese('patient.stammdaten', auftrag.patient, mitarbeiter)
|
|
|
|
return { ok = true, blutgruppe = blutgruppe, vorher = alt }
|
|
end)
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Schnittstelle für andere Ressourcen
|
|
-- ---------------------------------------------------------------------
|
|
|
|
--- Liefert die Laboraufträge eines Patienten, roh und unbewertet
|
|
--- @param patientId number Charakter
|
|
--- @return table
|
|
exports('HoleLaborwerte', function(patientId)
|
|
if not tonumber(patientId) then return {} end
|
|
return MySQL.query.await([[
|
|
SELECT kennung, art, status, fragestellung, ergebnis, befund,
|
|
auftrag_am, befund_am
|
|
FROM medical_labor
|
|
WHERE patient = ?
|
|
ORDER BY id DESC LIMIT 50
|
|
]], { tonumber(patientId) }) or {}
|
|
end)
|