Zwei Entscheidungen, die offenstanden. Beide getroffen, nachdem
gemessen war, was wirklich da ist.
1. WIE LANGE BLEIBEN ABGESCHLOSSENE FAELLE? 90 Tage.
Die Entscheidung war bereits getroffen: `AUFBEWAHRUNG_TAGE = 90` steht
seit dem 19.09. in hilfe-tabellen.js, mit Begruendung ("ein Fall kommt
manchmal wieder auf"). Nur hat sie NIEMAND durchgesetzt --
`hilfeAufraeumen()` gab es, und der einzige Aufrufer war ihre eigene
Pruefung. Der Kommentar darueber behauptete "Wird beim Start
aufgerufen (siehe index.js)"; das war nie wahr.
Jetzt steht die Regel in workspace-aufbewahrung.js, dem Loeschkonzept,
das sich selbst durchsetzt -- beim Start und danach taeglich. Die
zweite Fassung in workspace-hilfe.js ist WEG, nicht doppelt: Zwei
DELETEs auf dieselben Daten waeren morgen verschieden, und der
Unterschied fiele erst auf, wenn er zaehlt.
GEMESSEN VOR DEM EINSCHALTEN:
In der echten Datenbank steht heute KEIN einziger Fall. Der erste
Lauf loescht nichts, und der erste Fall kann fruehestens in drei
Monaten 90 Tage alt werden. Jetzt einschalten ist frei -- spaeter
waere es der riskante Moment gewesen.
PRAGMA foreign_keys steht im Dienst auf 1, und hilfe_nachrichten
traegt ON DELETE CASCADE. An einem echten Fall mit Nachricht
nachgemessen: vorher 1/1, nachher 0/0. Ohne diese Messung waere
der Fall verschwunden und der eigentliche Text liegengeblieben.
2. SOLL EIN GESCHLOSSENER FALL WIEDER ZU OEFFNEN SEIN? Nein.
Im Kopf von workspace-hilfe.js steht: "EIN GESCHLOSSENER FALL IST
GESCHLOSSEN. Auch fuer die Leitung -- sonst ist 'zugemacht' eine
Meinung und keine Tatsache." Das ist eine gute Regel, und sie bleibt.
Nachgemessen, dass sie auch traegt: Ein Melder kann seinen
geschlossenen Fall weiter LESEN, und ein geschlossener Fall zaehlt
nicht gegen das Limit von drei offenen. Wer eine Wiederholung melden
will, kann das also jederzeit -- die Bauweise ist stimmig.
NUR WUSSTE DAS NIEMAND. Dort stand ein Satz: "Dieser Fall ist
abgeschlossen (Datum)." Jetzt stehen drei -- und sie beantworten die
drei Fragen, die man in dem Moment hat:
Kann ich noch schreiben? Nein, und er laesst sich nicht oeffnen.
Bleibt das hier stehen? Bis zum TT.MM.JJJJ, dann geloescht.
Und wenn es wieder passiert? Neu melden, der alte zaehlt nicht mit.
Das Datum kommt vom Server (`lesbar_bis`), die Frist ebenso -- sie
steht nur an EINER Stelle. Und sie steht jetzt auch im Dialog BEIM
Schliessen: Wer eine Uhr startet, soll das vorher wissen, nicht
danach.
OHNE UHRZEIT, und das ist kein Schoenheitsgrund: Ein Fall, der am
20.09. um 14:04 (Sommerzeit) geschlossen wird, verfaellt 90 Tage
spaeter um 13:04 -- die Uhr wird dazwischen zurueckgestellt. Richtig
gerechnet, sieht aus wie ein Fehler. Wer eine Stunde sucht, die es
nicht gibt, hat Zeit verloren.
UND DIE URSACHE, DAMIT ES NICHT WIEDER PASSIERT:
hilfe_faelle kam am 19.09. dazu, das Loeschkonzept ist vom 15.09., und
nichts hat die beiden je verglichen. Neue Pruefung in
pruef-aufbewahrung: Jede Tabelle mit einer Spalte, die "hier ist etwas
zu Ende" sagt, MUSS im Konzept stehen.
Kein "jede Tabelle muss drinstehen": 46 Tabellen, 41 mit
Personenbezug -- das gaebe 38 Meldungen, von denen fast alle falsch
waeren (sie sind ueber personen_geloescht gedeckt). Eine Pruefung,
die 38-mal meldet, wo einmal richtig waere, wird abgeschaltet.
Gesucht wird das schmale Merkmal: GENAU ZWEI Tabellen im Haus tragen
so eine Spalte. Beide jetzt im Konzept -- hilfe_faelle mit Frist,
aufgaben ausdruecklich OHNE (erledigte Aufgaben sind
Arbeitsdokumentation, keine Meldung ueber einen Menschen).
pruef-hilfe 84/0 (war 59) -- darunter zehn neue am Bildschirm:
"drei Saetze statt einem", "sie stehen untereinander, nicht
nebeneinander" (ein <p> in einem <p> waere ungueltig, der Container
ist jetzt ein <div>), "und WANN, mit Datum".
pruef-aufbewahrung 45/0 (war 41), mit Gegenprobe.
367 lines
16 KiB
JavaScript
367 lines
16 KiB
JavaScript
/* =====================================================================
|
|
DER VERTRAULICHE MELDEWEG (19.09.2026)
|
|
|
|
Die Regeln stehen samt Begründung im Kopf von hilfe-tabellen.js.
|
|
Hier sind die Wege, und die drei Stellen, an denen etwas schiefgehen
|
|
kann:
|
|
|
|
1. EIN FALL DARF NIE BEIM FALSCHEN LANDEN. Jede Leseroute fragt
|
|
zuerst `darfSehen()`. Es gibt keinen Weg, der eine Fallnummer
|
|
ohne diese Frage nimmt -- auch nicht die Antwortroute.
|
|
|
|
2. DER WECHSEL MUSS ERZWUNGEN SEIN, nicht nur in der Oberfläche
|
|
angedeutet. Ein ausgegrauter Knopf ist keine Regel; wer die
|
|
Route direkt aufruft, käme daran vorbei.
|
|
|
|
3. EIN GESCHLOSSENER FALL IST GESCHLOSSEN. Auch für die Leitung --
|
|
sonst ist "zugemacht" eine Meinung und keine Tatsache.
|
|
===================================================================== */
|
|
|
|
import express from "express";
|
|
import { db, protokolliere } from "./workspace.js";
|
|
import { FALL_STAND, AUFBEWAHRUNG_TAGE } from "./hilfe-tabellen.js";
|
|
import { benachrichtige } from "./workspace-push.js";
|
|
|
|
export const hilfeRouter = express.Router();
|
|
|
|
/** Wer alle Fälle sieht und beantworten darf.
|
|
*
|
|
* GENAU ZWEI ROLLEN, wie bestellt: DogFather und die rechte Hand.
|
|
* Modis ausdrücklich NICHT -- sehr oft geht es in diesen Meldungen um
|
|
* eine Moderationsentscheidung, und wer beteiligt ist, darf die
|
|
* Beschwerde über sich nicht lesen. */
|
|
const LEITUNG = new Set(["admin", "hand"]);
|
|
|
|
const istLeitung = (person) => LEITUNG.has(person?.rolle);
|
|
|
|
/** Grenzen. Nicht schön, aber ohne sie ist eine Textbox ein Einfallstor. */
|
|
const BETREFF_MAX = 120;
|
|
const TEXT_MAX = 4000;
|
|
/** Wie viele offene Fälle eine Person gleichzeitig haben darf.
|
|
*
|
|
* Drei. Nicht als Misstrauen, sondern weil ein Mensch mit acht
|
|
* offenen Meldungen keine Antwort mehr zuordnen kann -- und weil der,
|
|
* der antwortet, dann keine mehr ernst nimmt. */
|
|
const OFFEN_MAX = 3;
|
|
|
|
const jetzt = () => new Date().toISOString();
|
|
|
|
function sauber(s, max) {
|
|
return String(s ?? "").replace(/\s+/g, " ").trim().slice(0, max);
|
|
}
|
|
|
|
/** Darf diese Person diesen Fall sehen?
|
|
*
|
|
* Gibt den Fall zurück oder `null`. NIE `true`/`false` -- so kann
|
|
* keine Route "darf sehen" beantworten und danach trotzdem den
|
|
* falschen Datensatz laden. */
|
|
function fallFuer(person, id) {
|
|
const fall = db().prepare("SELECT * FROM hilfe_faelle WHERE id = ?").get(Number(id));
|
|
if (!fall) return null;
|
|
if (istLeitung(person)) return fall;
|
|
return fall.person_id === person.id ? fall : null;
|
|
}
|
|
|
|
/* ---------- Was jemand sehen darf ------------------------------------ */
|
|
|
|
/** Die eigenen Fälle -- oder für die Leitung alle.
|
|
*
|
|
* EINE ROUTE FÜR BEIDE, nicht zwei. Zwei Routen hießen zwei Stellen,
|
|
* an denen die Sichtbarkeitsregel gepflegt werden muss. */
|
|
hilfeRouter.get("/workspace/api/hilfe", (req, res) => {
|
|
try {
|
|
if (!req.person) return res.status(401).json({ fehler: "nicht_angemeldet" });
|
|
const leitung = istLeitung(req.person);
|
|
|
|
const zeilen = leitung
|
|
? db().prepare(`
|
|
SELECT f.*, p.name AS melder_name, p.rolle AS melder_rolle,
|
|
(SELECT COUNT(*) FROM hilfe_nachrichten n WHERE n.fall_id = f.id) AS nachrichten
|
|
FROM hilfe_faelle f
|
|
LEFT JOIN personen p ON p.id = f.person_id
|
|
ORDER BY CASE f.stand WHEN 'wartet_auf_antwort' THEN 0
|
|
WHEN 'antwort_da' THEN 1 ELSE 2 END,
|
|
f.geaendert DESC`).all()
|
|
: db().prepare(`
|
|
SELECT f.*,
|
|
(SELECT COUNT(*) FROM hilfe_nachrichten n WHERE n.fall_id = f.id) AS nachrichten
|
|
FROM hilfe_faelle f
|
|
WHERE f.person_id = ?
|
|
ORDER BY CASE f.stand WHEN 'antwort_da' THEN 0
|
|
WHEN 'wartet_auf_antwort' THEN 1 ELSE 2 END,
|
|
f.geaendert DESC`).all(req.person.id);
|
|
|
|
res.json({
|
|
faelle: zeilen.map((z) => ({
|
|
id: z.id,
|
|
betreff: z.betreff,
|
|
stand: z.stand,
|
|
erstellt: z.erstellt,
|
|
geaendert: z.geaendert,
|
|
nachrichten: z.nachrichten,
|
|
/* DER NAME NUR FÜR DIE LEITUNG. Er wird gar nicht erst in die
|
|
Antwort geschrieben, wenn jemand anderes fragt -- eine
|
|
Oberfläche, die ihn "nicht anzeigt", wäre kein Schutz. */
|
|
...(leitung ? { melder: z.melder_name || "(gelöscht)" } : {}),
|
|
})),
|
|
leitung,
|
|
/* Damit die Oberfläche weiß, ob noch ein Fall aufgemacht werden
|
|
darf, ohne es durch einen Fehlschlag herauszufinden. */
|
|
offen_eigene: leitung ? null : zeilen.filter((z) => z.stand !== FALL_STAND.zu).length,
|
|
offen_max: OFFEN_MAX,
|
|
/* Damit der Abschluss-Dialog die Frist nennen kann, ohne sie
|
|
ein zweites Mal im Browser stehen zu haben. Eine Zahl, die an
|
|
zwei Stellen steht, ist morgen an einer davon falsch. */
|
|
aufbewahrung_tage: AUFBEWAHRUNG_TAGE,
|
|
});
|
|
} catch (fehler) {
|
|
console.error("[hilfe] Liste:", fehler?.message);
|
|
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
|
}
|
|
});
|
|
|
|
/** Ein Fall mit seinem ganzen Verlauf. */
|
|
hilfeRouter.get("/workspace/api/hilfe/:id(\\d+)", (req, res) => {
|
|
try {
|
|
if (!req.person) return res.status(401).json({ fehler: "nicht_angemeldet" });
|
|
const fall = fallFuer(req.person, req.params.id);
|
|
/* 404 UND NICHT 403: Ein "darfst du nicht" verrät, dass es diesen
|
|
Fall gibt. Bei vertraulichen Meldungen ist schon das zu viel. */
|
|
if (!fall) return res.status(404).json({ fehler: "nicht_gefunden" });
|
|
|
|
const leitung = istLeitung(req.person);
|
|
const nachrichten = db().prepare(
|
|
"SELECT id, von_leitung, text, erstellt, gelesen_am FROM hilfe_nachrichten"
|
|
+ " WHERE fall_id = ? ORDER BY id").all(fall.id);
|
|
|
|
/* GELESEN-STEMPEL SETZEN. Nur für die Nachrichten der jeweils
|
|
anderen Seite -- die eigenen hat man selbst geschrieben. */
|
|
const fremd = leitung ? 0 : 1;
|
|
db().prepare(
|
|
"UPDATE hilfe_nachrichten SET gelesen_am = ? WHERE fall_id = ?"
|
|
+ " AND von_leitung = ? AND gelesen_am IS NULL").run(jetzt(), fall.id, fremd);
|
|
|
|
const melder = leitung
|
|
? db().prepare("SELECT name FROM personen WHERE id = ?").get(fall.person_id)
|
|
: null;
|
|
|
|
res.json({
|
|
fall: {
|
|
id: fall.id, betreff: fall.betreff, stand: fall.stand,
|
|
erstellt: fall.erstellt, geaendert: fall.geaendert,
|
|
geschlossen_am: fall.geschlossen_am,
|
|
/* BIS WANN ER NOCH DA IST (20.09.2026).
|
|
----------------------------------------------------------
|
|
Ein abgeschlossener Fall wird nach 90 Tagen geloescht --
|
|
das steht im Loeschkonzept und laeuft taeglich. Bis heute
|
|
stand auf der Seite nur „Dieser Fall ist abgeschlossen".
|
|
Niemand konnte wissen, dass er verschwindet, und erst recht
|
|
nicht, wann.
|
|
|
|
Das ist keine Kleinigkeit: Wer eine Meldung gemacht hat,
|
|
hat ein Recht darauf zu erfahren, wie lange das gespeichert
|
|
bleibt (Art. 13 Abs. 2 lit. a DSGVO). Und praktisch braucht
|
|
er es auch -- wer nachlesen will, was vereinbart wurde,
|
|
soll nicht erst merken, dass es weg ist.
|
|
|
|
GERECHNET WIRD HIER, nicht im Browser: Sonst stuende
|
|
dieselbe Frist an zwei Stellen, und eine davon altert. */
|
|
lesbar_bis: fall.geschlossen_am
|
|
? new Date(Date.parse(fall.geschlossen_am) + AUFBEWAHRUNG_TAGE * 86400000).toISOString()
|
|
: null,
|
|
...(leitung ? { melder: melder?.name || "(gelöscht)" } : {}),
|
|
},
|
|
nachrichten: nachrichten.map((n) => ({
|
|
id: n.id, von_leitung: !!n.von_leitung, text: n.text,
|
|
erstellt: n.erstellt, gelesen: !!n.gelesen_am,
|
|
})),
|
|
leitung,
|
|
/* WER IST DRAN. Vom Server gerechnet, nicht im Browser -- sonst
|
|
steht in zwei Dateien dieselbe Regel, und eine davon altert. */
|
|
ich_bin_dran: fall.stand === FALL_STAND.zu ? false
|
|
: leitung ? fall.stand === FALL_STAND.wartet
|
|
: fall.stand === FALL_STAND.antwort,
|
|
});
|
|
} catch (fehler) {
|
|
console.error("[hilfe] Fall lesen:", fehler?.message);
|
|
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
|
}
|
|
});
|
|
|
|
/* ---------- Einen Fall aufmachen -------------------------------------- */
|
|
|
|
hilfeRouter.post("/workspace/api/hilfe", express.json(), (req, res) => {
|
|
try {
|
|
if (!req.person) return res.status(401).json({ fehler: "nicht_angemeldet" });
|
|
/* DIE LEITUNG MELDET SICH NICHT SELBST. Sie hat andere Wege --
|
|
und ein Fall, den man sich selbst beantwortet, verwirrt die
|
|
Liste für alle. */
|
|
if (istLeitung(req.person)) {
|
|
return res.status(400).json({ fehler: "leitung_meldet_nicht" });
|
|
}
|
|
|
|
const betreff = sauber(req.body?.betreff, BETREFF_MAX);
|
|
const text = String(req.body?.text ?? "").trim().slice(0, TEXT_MAX);
|
|
if (betreff.length < 3) return res.status(400).json({ fehler: "betreff_fehlt" });
|
|
if (text.length < 10) return res.status(400).json({ fehler: "text_zu_kurz" });
|
|
|
|
const offen = db().prepare(
|
|
"SELECT COUNT(*) AS n FROM hilfe_faelle WHERE person_id = ? AND stand != ?")
|
|
.get(req.person.id, FALL_STAND.zu).n;
|
|
if (offen >= OFFEN_MAX) {
|
|
return res.status(429).json({ fehler: "zu_viele_offen", offen, max: OFFEN_MAX });
|
|
}
|
|
|
|
const t = jetzt();
|
|
db().prepare("INSERT INTO hilfe_faelle (person_id, betreff, stand, erstellt, geaendert)"
|
|
+ " VALUES (?,?,?,?,?)").run(req.person.id, betreff, FALL_STAND.wartet, t, t);
|
|
const id = db().prepare("SELECT last_insert_rowid() AS id").get().id;
|
|
db().prepare("INSERT INTO hilfe_nachrichten (fall_id, von_id, von_leitung, text, erstellt)"
|
|
+ " VALUES (?,?,0,?,?)").run(id, req.person.id, text, t);
|
|
|
|
/* IM PROTOKOLL STEHT NUR, DASS ES EINEN FALL GIBT -- nicht, worum
|
|
es geht. Das Protokoll lesen mehr Leute als den Fall. */
|
|
protokolliere(req.person.id, "hilfe_fall_neu", `Fall ${id}`);
|
|
leitungBenachrichtigen(id, betreff).catch(() => { /* Push ist Zugabe */ });
|
|
|
|
res.status(201).json({ id });
|
|
} catch (fehler) {
|
|
console.error("[hilfe] anlegen:", fehler?.message);
|
|
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
|
}
|
|
});
|
|
|
|
/* ---------- Antworten -------------------------------------------------- */
|
|
|
|
hilfeRouter.post("/workspace/api/hilfe/:id(\\d+)/antwort", express.json(), (req, res) => {
|
|
try {
|
|
if (!req.person) return res.status(401).json({ fehler: "nicht_angemeldet" });
|
|
const fall = fallFuer(req.person, req.params.id);
|
|
if (!fall) return res.status(404).json({ fehler: "nicht_gefunden" });
|
|
if (fall.stand === FALL_STAND.zu) {
|
|
return res.status(409).json({ fehler: "geschlossen" });
|
|
}
|
|
|
|
const leitung = istLeitung(req.person);
|
|
/* DER WECHSEL, ERZWUNGEN. Die Oberfläche blendet den Knopf aus --
|
|
das ist Höflichkeit. Diese Zeile ist die Regel. */
|
|
const dran = leitung ? fall.stand === FALL_STAND.wartet
|
|
: fall.stand === FALL_STAND.antwort;
|
|
if (!dran) return res.status(409).json({ fehler: "nicht_dran" });
|
|
|
|
const text = String(req.body?.text ?? "").trim().slice(0, TEXT_MAX);
|
|
if (text.length < 2) return res.status(400).json({ fehler: "text_zu_kurz" });
|
|
|
|
const t = jetzt();
|
|
db().prepare("INSERT INTO hilfe_nachrichten (fall_id, von_id, von_leitung, text, erstellt)"
|
|
+ " VALUES (?,?,?,?,?)").run(fall.id, req.person.id, leitung ? 1 : 0, text, t);
|
|
db().prepare("UPDATE hilfe_faelle SET stand = ?, geaendert = ? WHERE id = ?")
|
|
.run(leitung ? FALL_STAND.antwort : FALL_STAND.wartet, t, fall.id);
|
|
|
|
if (leitung) {
|
|
antwortBenachrichtigen(fall).catch(() => { /* Zugabe */ });
|
|
} else {
|
|
leitungBenachrichtigen(fall.id, fall.betreff).catch(() => { /* Zugabe */ });
|
|
}
|
|
|
|
res.json({ ok: true });
|
|
} catch (fehler) {
|
|
console.error("[hilfe] antworten:", fehler?.message);
|
|
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
|
}
|
|
});
|
|
|
|
/* ---------- Zumachen --------------------------------------------------- */
|
|
|
|
hilfeRouter.post("/workspace/api/hilfe/:id(\\d+)/schliessen", express.json(), (req, res) => {
|
|
try {
|
|
if (!req.person) return res.status(401).json({ fehler: "nicht_angemeldet" });
|
|
/* NUR DIE LEITUNG. Siehe hilfe-tabellen.js: Ein Fall, den der
|
|
Melder selbst zumachen kann, wird in einem schlechten Moment
|
|
zugemacht. */
|
|
if (!istLeitung(req.person)) return res.status(403).json({ fehler: "nur_leitung" });
|
|
const fall = fallFuer(req.person, req.params.id);
|
|
if (!fall) return res.status(404).json({ fehler: "nicht_gefunden" });
|
|
if (fall.stand === FALL_STAND.zu) return res.json({ ok: true, schon: true });
|
|
|
|
const t = jetzt();
|
|
const wort = String(req.body?.text ?? "").trim().slice(0, TEXT_MAX);
|
|
/* EIN SCHLUSSWORT IST ERLAUBT, ABER NICHT PFLICHT. Manche Fälle
|
|
enden mit "ist geklärt, danke" -- andere brauchen kein Wort
|
|
mehr, und dann ist ein Pflichtfeld nur eine Hürde. */
|
|
if (wort.length >= 2) {
|
|
db().prepare("INSERT INTO hilfe_nachrichten (fall_id, von_id, von_leitung, text, erstellt)"
|
|
+ " VALUES (?,?,1,?,?)").run(fall.id, req.person.id, wort, t);
|
|
}
|
|
db().prepare("UPDATE hilfe_faelle SET stand = ?, geaendert = ?,"
|
|
+ " geschlossen_am = ?, geschlossen_von = ? WHERE id = ?")
|
|
.run(FALL_STAND.zu, t, t, req.person.id, fall.id);
|
|
|
|
protokolliere(req.person.id, "hilfe_fall_zu", `Fall ${fall.id}`);
|
|
schlussBenachrichtigen(fall).catch(() => { /* Zugabe */ });
|
|
res.json({ ok: true });
|
|
} catch (fehler) {
|
|
console.error("[hilfe] schliessen:", fehler?.message);
|
|
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
|
}
|
|
});
|
|
|
|
/* ---------- Benachrichtigungen ----------------------------------------- */
|
|
|
|
/** Die Leitung anstupsen, dass etwas hereingekommen ist.
|
|
*
|
|
* OHNE DEN BETREFF IM TEXT. Eine Benachrichtigung erscheint auf einem
|
|
* Sperrbildschirm, den auch andere sehen -- und der Betreff ist hier
|
|
* regelmäßig das Empfindliche. Es steht nur, DASS etwas da ist. */
|
|
async function leitungBenachrichtigen(fallId) {
|
|
const leute = db().prepare(
|
|
"SELECT id FROM personen WHERE rolle IN ('admin','hand') AND aktiv = 1").all();
|
|
for (const p of leute) {
|
|
await benachrichtige(p.id, "hilfe", {
|
|
titel: "Eine vertrauliche Meldung",
|
|
text: "Jemand aus der Community braucht etwas.",
|
|
ziel: `/workspace/hilfe.html?fall=${fallId}`,
|
|
}).catch(() => { /* einer weniger ist kein Grund abzubrechen */ });
|
|
}
|
|
}
|
|
|
|
async function antwortBenachrichtigen(fall) {
|
|
await benachrichtige(fall.person_id, "hilfe", {
|
|
titel: "Antwort auf deine Meldung",
|
|
text: "Es gibt eine Antwort für dich.",
|
|
ziel: `/workspace/hilfe.html?fall=${fall.id}`,
|
|
});
|
|
}
|
|
|
|
async function schlussBenachrichtigen(fall) {
|
|
await benachrichtige(fall.person_id, "hilfe", {
|
|
titel: "Deine Meldung ist erledigt",
|
|
text: "Der Fall wurde abgeschlossen.",
|
|
ziel: `/workspace/hilfe.html?fall=${fall.id}`,
|
|
});
|
|
}
|
|
|
|
/* ---------- Aufräumen: steht jetzt im Löschkonzept --------------------
|
|
|
|
Hier stand `hilfeAufraeumen(tage)` -- mit einem Kommentar darüber,
|
|
sie werde „beim Start aufgerufen (siehe index.js)". Das war nie
|
|
wahr: Gesucht im ganzen Verzeichnis war der einzige Aufrufer ihre
|
|
eigene Prüfung. Die Prüfung war grün und bewies nichts über den
|
|
Betrieb.
|
|
|
|
Am 20.09.2026 ist die Regel dorthin gewandert, wo sie sich selbst
|
|
durchsetzt: `workspace-aufbewahrung.js`, Eintrag
|
|
`hilfe_geschlossen`. Diese Datei ist beides -- die Übersicht, was
|
|
wie lange aufgehoben wird, UND das Programm, das es tut. Sie läuft
|
|
beim Start und danach täglich.
|
|
|
|
ZWEI STELLEN WÄREN SCHLIMMER ALS KEINE. Eine zweite Fassung
|
|
desselben DELETE hier wäre morgen anders als die dort, und der
|
|
Unterschied fiele erst auf, wenn er zählt -- beim Löschen der
|
|
empfindlichsten Texte des Hauses. Deshalb ist sie weg, nicht
|
|
doppelt.
|
|
|
|
Die Frist selbst (`AUFBEWAHRUNG_TAGE = 90`) steht unverändert in
|
|
`hilfe-tabellen.js`, samt Begründung. */
|
|
|