A4: Bewerben statt selbst nehmen

Filipe, 22.09.2026: „die modis und linke hand sollen da nichts
uebernehmen koennen von aufgaben, ueberhaupt ueberall sollen die keine
aufgaben selber uebernehmen die ihnen nicht zugetragen sind. ich will
dass die sich fuer aufgaben bewerben koennen aber die rechte hand oder
dogfather muessen annehmen oder ablehnen koennen und das mit einem
kommentar als moeglichkeit sogar noch zum hinzufuegen."

ZWEI FRAGEN, DIE MAN AUSEINANDERHALTEN MUSS -- und das war der Schluessel:

    verteilen    eine Aufgabe an ANDERE geben
    entscheiden  bestimmen, wer sie am Ende macht

Sein Satz vom selben Tag („rechte hand linke hand und dogfather ...
koennen alle verteilen") widerspricht dem nicht, er beantwortet die
erste Frage. Die linke Hand verteilt, entscheidet aber nicht -- sie
bewirbt sich wie ein Modi. `entscheidetUeberAufgaben` steht neben
`darfAufgabenVerteilen`, und drei Stellen fragen ab jetzt dieselbe
Funktion.

DAS VERBOT HATTE DREI TUEREN, und die zweite und dritte waren beim
Planen nicht zu sehen:

  1. aus dem Pool „uebernehmen"  -- die offensichtliche
  2. beim Pool „annehmen"        -- dasselbe unter anderem Namen: Wer
                                    zusagt, nimmt sie den anderen weg.
                                    Bei „einzeln"/„mehrere" bleibt es
                                    erlaubt -- dort wurde sie ihm
                                    ZUGETRAGEN, und genau das Wort
                                    steht in seinem Satz.
  3. beim Verteilen sich selbst eintragen -- die linke Hand darf
                                    verteilen, haette sich also selbst
                                    nehmen koennen. Abgelehnt statt
                                    still gefiltert: Wer sich eintraegt
                                    und sich danach nicht findet, sucht
                                    den Fehler bei sich.

EIN FUND, OHNE DEN A4 GAR NICHT FUNKTIONIERT HAETTE
Die rechte Hand soll entscheiden -- und sah die Aufgabe nicht, um die
es ging. Gemessen an einer Pool-Aufgabe, die DogFather fuer zwei Modis
angelegt hat:

    DogFather    sieht sie    1 Bewerbung
    Modi         sieht sie    1 Bewerbung
    rechte Hand  SIEHT SIE NICHT (Liste leer)

Ihre Sichtregel sammelte Menschen mit einer TEAM_DOGI_ROLLE und fragte,
ob einer als Creator, Verantwortlicher oder Ersteller eingetragen ist.
Bei einer Pool-Aufgabe bleibt `verantwortlich_id` leer (das ist ihr
Sinn), und der Ersteller war DogFather -- der in dieser Liste nicht
steht. Alle drei Bedingungen liefen ins Leere.

Auf der Adresse von Team Dogi sehen die Haende jetzt alles. Das ist
zugleich sein Satz „sehen alle aufgaben". Die Haustrennung bleibt:
`sichtbar()` haengt fuer crew weiterhin `AND ohneAgentur(...)` davor.

WIE DIE BEWERBUNG GEBAUT IST
Kein zweiter Tisch, sondern ein weiterer Zustand derselben Zeile
(`beworben`). Die Frage „wie steht diese Aufgabe bei DIESEM Menschen"
wird dort schon beantwortet; eine zweite Tabelle haette zwei Wahrheiten
ueber dieselbe Beziehung.

Die Worte der Person (`grund`) und der Kommentar der Leitung
(`entscheid_text`) stehen in ZWEI Feldern. In dasselbe waere bequemer
und wuerde die Frage mit der Antwort ueberschreiben -- danach wuesste
niemand mehr, worum jemand gebeten hat. Dasselbe gilt, wenn eine
Bewerbung wegfaellt, weil jemand anders die Aufgabe bekommt: Der
Hinweis kommt in das Feld der Leitung, ihre Worte bleiben stehen.

Der Kommentar ist FREIWILLIG. Die Begruendung beim Ablehnen einer
zugeteilten Aufgabe bleibt Pflicht -- dort sagt jemand ab, der gefragt
wurde. Hier bittet jemand; ein „bitte" braucht keine Begruendung.
Filipes Wort ist „als moeglichkeit".

Die CHECK-Regel wurde ueber `checkListeErweitern` erweitert -- den Weg,
der seit dem 09.09. an einer Stelle steht, mit Sicherung vorher,
Spaltenliste aus PRAGMA und Indizes, die mitgehen.

UND EIN SATZ, DER NICHT MEHR STIMMTE
„Frei fuer 3 Leute — wer zuerst Zeit hat" war die Beschreibung des
Pools, solange sich jeder selbst bedienen durfte. Er sagt jetzt jedem,
was FUER IHN gilt.

GEPRUEFT
Neu: server/pruef-bewerbung-aufgaben.mjs, 66/0 -- die Regel selbst,
alle drei Tueren, der erlaubte Weg, die zwei Felder, das Zurueckziehen,
und ein Abschnitt am echten Bildschirm (ein Modi sieht „Ich bewerbe
mich" statt „Ich uebernehme das"; die rechte Hand sieht die Bewerbung
mit Annehmen und Ablehnen; die Bewerberin sieht KEINEN Block zum
Entscheiden -- sonst waere das Verbot einen Knopf weiter offen).

pruef-zuteilung auf die neue Regel umgestellt (sie hielt „Bea
uebernimmt sie" fest und wurde zu Recht rot) -- misst jetzt denselben
Effekt ueber den neuen Weg. Dazu gruen: pruef-aufgabenbrett,
pruef-meldungen, pruef-sicht, pruef-css-klassen.

ALTLAST, nicht von hier: pruef-rollen meldet einen Fehler an der
Kachel „Zu Team Dogi" (absolute Adresse). Mit `git stash` nachgemessen
-- vorher und nachher identisch.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-22 18:55:06 +02:00
co-authored by Claude Opus 5
parent b79b75ab70
commit 43545e4acd
47 changed files with 1857 additions and 600 deletions
+288 -3
View File
@@ -53,7 +53,8 @@
import express from "express";
import {
db, protokolliere, echteIp, darfAufgabenVerteilen, istDogFather, istHand,
db, protokolliere, echteIp, darfAufgabenVerteilen, entscheidetUeberAufgaben,
istDogFather, istHand,
sitzungLesen,
} from "./workspace.js";
import { TEAM_DOGI_ROLLEN } from "./crew-adresse.js";
@@ -100,6 +101,10 @@ export const BEWERTUNGEN = new Set(["gut", "nicht_gut", "verbessern"]);
* erfinden — sonst heißt derselbe Zustand an zwei Stellen anders. */
export const ZUSTAND_TEXT = {
offen: "Noch nicht beantwortet",
/* „Beworben" ist kein halbes „Angenommen", sondern ein eigener
Zustand: Es liegt an jemand anderem, nicht mehr an der Person
selbst. Der Satz sagt das, statt es offen zu lassen. */
beworben: "Beworben – wartet auf Antwort",
angenommen: "Angenommen",
arbeit: "In Bearbeitung",
erledigt: "Erledigt",
@@ -129,11 +134,14 @@ export function zuteilungenZu(aufgabeIds) {
SELECT z.id, z.aufgabe_id, z.person_id, z.zustand, z.grund,
z.zugeteilt_am, z.geantwortet_am, z.erledigt_am,
z.bewertung, z.bewertung_text, z.bewertet_am,
z.entscheid_text, z.entschieden_am,
p.name AS person_name, p.rolle AS person_rolle,
pb.name AS bewertet_von_name
pb.name AS bewertet_von_name,
pe.name AS entschieden_von_name
FROM aufgaben_zuteilung z
LEFT JOIN personen p ON p.id = z.person_id
LEFT JOIN personen pb ON pb.id = z.bewertet_von
LEFT JOIN personen pe ON pe.id = z.entschieden_von
WHERE z.aufgabe_id IN (${platz})
ORDER BY p.name COLLATE NOCASE`).all(...ids);
@@ -168,8 +176,18 @@ export function mitZuteilung(reihen, ich) {
verlangt ausdrücklich, dass die anderen das erkennen, "damit
keine doppelte Bearbeitung entsteht". */
r.uebernommen_von = r.verteilart === "pool"
? (zu.find((z) => z.zustand !== "offen" && z.zustand !== "abgelehnt")?.person_name || null)
? (zu.find((z) => z.zustand !== "offen" && z.zustand !== "abgelehnt"
&& z.zustand !== "beworben")?.person_name || null)
: null;
/* DIE OFFENEN BEWERBUNGEN, abgeleitet statt danebengefuehrt.
Die Oberflaeche soll nicht dieselbe Rechnung noch einmal machen
-- zwei Rechnungen fuer dieselbe Frage laufen auseinander. */
r.bewerbungen = zu.filter((z) => z.zustand === "beworben");
/* UND OB ICH MICH UEBERHAUPT NOCH BEWERBEN KANN. Ein Knopf, der
beim Druecken eine Absage bringt, ist schlimmer als keiner. */
r.ich_beworben = !!(r.meine_zuteilung
&& r.meine_zuteilung.zustand === "beworben");
}
return liste;
}
@@ -196,6 +214,31 @@ export function zuteilen(aufgabeId, personIds, art, vonId) {
`SELECT id FROM personen WHERE id IN (${platz}) AND aktiv = 1`).all(...roh).map((z) => z.id);
if (!echt.length) return { fehler: "keine_person" };
/* NIEMAND TRAEGT SICH SELBST EIN, DER NICHT ENTSCHEIDEN DARF
(22.09.2026).
Ohne diese Zeile waere das Verbot von oben ein Umweg mit einem
Klick mehr: Die linke Hand darf verteilen -- sie haette sich die
Aufgabe also einfach selbst zugeteilt und damit genau das getan,
was Filipe ausgeschlossen hat („keine aufgaben selber uebernehmen
die ihnen nicht zugetragen sind").
DIE ROLLE WIRD NACHGESCHLAGEN statt durchgereicht: `zuteilen`
bekommt nur eine Nummer, und eine zusaetzliche Angabe im Aufruf
waere die, die beim dritten Aufrufer fehlt. Eine Abfrage mehr
kostet nichts und kann nicht vergessen werden.
ES WIRD ABGELEHNT UND NICHT STILL HERAUSGEFILTERT: Wer sich
einträgt und sich danach nicht in der Liste findet, sucht den
Fehler bei sich. */
const verteiler = vonId
? db().prepare("SELECT id, rolle FROM personen WHERE id = ?").get(vonId)
: null;
if (verteiler && echt.includes(verteiler.id)
&& !entscheidetUeberAufgaben(verteiler)) {
return { fehler: "nicht_an_dich_selbst" };
}
/* DIE ART WIRD ABGELEITET, WO SIE SICH ABLEITEN LÄSST. Eine Person
kann kein Pool sein — "der erste, der Zeit hat" braucht mindestens
zwei, die es sehen. Wer trotzdem "pool" schickt, bekommt
@@ -269,6 +312,23 @@ zuteilungRouter.post("/workspace/api/aufgaben/:id/annehmen", (req, res) => {
if (!z) return res.status(403).json({ fehler: "nicht_zugeteilt" });
if (z.zustand === "erledigt") return res.status(409).json({ fehler: "schon_erledigt" });
/* BEIM POOL IST „ANNEHMEN" DASSELBE WIE „UEBERNEHMEN" (22.09.2026).
Das ist beim Bauen aufgefallen, nicht beim Planen: Der Weg
`uebernehmen` wurde fuer Modis und die linke Hand geschlossen --
und `annehmen` stand daneben weit offen. Bei einer Pool-Aufgabe
heisst annehmen aber genau, sie den anderen wegzunehmen
(`uebernahmeAbschliessen` weiter unten in dieser Funktion). Ein
Verbot mit einer zweiten, offenen Tuer ist keins.
BEI „einzeln" UND „mehrere" BLEIBT ES, WIE ES WAR. Dort wurde
die Aufgabe jemandem ZUGETRAGEN -- und genau die darf er
annehmen. Filipes Satz nennt den Unterschied selbst: „keine
aufgaben selber uebernehmen DIE IHNEN NICHT ZUGETRAGEN SIND." */
if (a.verteilart === "pool" && !entscheidetUeberAufgaben(req.person)) {
return res.status(403).json({ fehler: "erst_bewerben" });
}
db().prepare(`UPDATE aufgaben_zuteilung
SET zustand = 'angenommen', grund = NULL, geantwortet_am = ?
WHERE id = ?`).run(jetzt(), z.id);
@@ -331,6 +391,27 @@ function uebernahmeAbschliessen(aufgabeId, personId) {
geantwortet_am = ?
WHERE aufgabe_id = ? AND person_id <> ? AND zustand = 'offen'`)
.run(jetzt(), aufgabeId, personId);
/* DIE OFFENEN BEWERBUNGEN FALLEN AUCH WEG (22.09.2026).
Ohne diese Zeile blieb eine Bewerbung auf „beworben" stehen,
nachdem jemand anders die Aufgabe bekommen hat -- die Person
haette weiter gewartet, und in der Liste der Leitung staende eine
Entscheidung an, die es nicht mehr gibt. Gefunden hat es die
Pruefung.
EIGENE ANWEISUNG UND NICHT DIESELBE, und der Grund ist das Feld
`grund`: Dort stehen bei einer Bewerbung die WORTE DER PERSON
(„Ich habe Freitag Zeit."). Sie mit „Von jemand anderem
uebernommen" zu ueberschreiben hiesse, die Frage mit der Antwort
zu loeschen -- genau das, was die Trennung der zwei Felder
verhindern soll. Der Hinweis kommt deshalb in `entscheid_text`. */
db().prepare(`UPDATE aufgaben_zuteilung
SET zustand = 'abgelehnt',
entscheid_text = 'Von jemand anderem übernommen',
entschieden_am = ?, geantwortet_am = ?
WHERE aufgabe_id = ? AND person_id <> ? AND zustand = 'beworben'`)
.run(jetzt(), jetzt(), aufgabeId, personId);
db().prepare("UPDATE aufgaben SET verantwortlich_id = ? WHERE id = ?")
.run(personId, aufgabeId);
}
@@ -340,6 +421,14 @@ zuteilungRouter.post("/workspace/api/aufgaben/:id/uebernehmen", (req, res) => {
try {
const a = holen(req, res); if (!a) return;
if (a.verteilart !== "pool") return res.status(409).json({ fehler: "kein_pool" });
/* WER NICHT ENTSCHEIDET, NIMMT SICH AUCH NICHTS (22.09.2026).
Filipe: „ueberhaupt ueberall sollen die keine aufgaben selber
uebernehmen die ihnen nicht zugetragen sind."
Fuer Modis und die linke Hand fuehrt der Weg jetzt ueber eine
Bewerbung -- der Fehlercode sagt das, statt nur nein. */
if (!entscheidetUeberAufgaben(req.person)) {
return res.status(403).json({ fehler: "erst_bewerben" });
}
const z = meine(a.id, req.person.id);
if (!z) return res.status(403).json({ fehler: "nicht_zugeteilt" });
@@ -368,6 +457,202 @@ zuteilungRouter.post("/workspace/api/aufgaben/:id/uebernehmen", (req, res) => {
}
});
/* =====================================================================
SICH BEWERBEN — und wer darueber entscheidet (22.09.2026)
=====================================================================
Filipe: „ich will dass die sich fuer aufgaben bewerben koennen aber
die rechte hand oder dogfather muessen annehmen oder ablehnen koennen
und das mit einem kommentar als moeglichkeit sogar noch zum
hinzufuegen."
DREI ENTSCHEIDUNGEN, die nicht auf der Hand lagen:
(1) DER KOMMENTAR IST FREIWILLIG, die Begruendung beim ABLEHNEN einer
zugeteilten Aufgabe bleibt Pflicht. Das ist kein Widerspruch:
Dort sagt jemand ab, der gefragt wurde -- der Fragende muss
wissen, warum, sonst fragt er nach. Hier entscheidet die
Leitung ueber eine Bitte; ein „ja" braucht keine Begruendung.
Filipes Wort ist „als moeglichkeit", nicht „als pflicht".
(2) DER TEXT DER BEWERBUNG UND DER KOMMENTAR DER LEITUNG STEHEN IN
ZWEI FELDERN. `grund` traegt die Worte der Person selbst, der
Kommentar kommt in `entscheid_text`. In dasselbe Feld waere
bequemer und wuerde die Frage mit der Antwort ueberschreiben --
danach weiss niemand mehr, worum jemand gebeten hat.
(3) WER SICH BEWIRBT, IST NICHT ZUGETEILT. Es entsteht eine Zeile
mit `zustand = 'beworben'` und ohne `zugeteilt_von` -- sonst
staende in der Liste, jemand haette ihm die Aufgabe gegeben.
===================================================================== */
const BEWERBUNG_TEXT_MAX = 500;
/* ---- Sich bewerben --------------------------------------------------- */
zuteilungRouter.post("/workspace/api/aufgaben/:id/bewerben", (req, res) => {
try {
const a = holen(req, res); if (!a) return;
/* WER SELBST ENTSCHEIDET, BEWIRBT SICH NICHT. Er teilt sich die
Aufgabe zu -- eine Bewerbung an sich selbst waere ein Umweg mit
zwei Klicks und demselben Ergebnis. */
if (entscheidetUeberAufgaben(req.person)) {
return res.status(409).json({ fehler: "du_entscheidest_selbst" });
}
if (a.status === "erledigt" || a.status === "abgebrochen") {
return res.status(409).json({ fehler: "schon_erledigt" });
}
const z = meine(a.id, req.person.id);
if (z && z.zustand === "beworben") {
return res.status(409).json({ fehler: "schon_beworben" });
}
/* WER SIE SCHON HAT, BEWIRBT SICH NICHT -- mit einer Ausnahme,
und die ist der Kern der Sache:
einzeln/mehrere, Zustand „offen" -> sie wurde IHM zugetragen.
Er nimmt sie an oder lehnt
sie ab. Keine Bewerbung.
pool, Zustand „offen" -> sie wurde ANGEBOTEN, nicht
zugetragen. Frueher hiess
die Antwort darauf „ich
nehme sie mir"; seit heute
heisst sie „ich bewerbe
mich".
Ohne diese Unterscheidung haette der erlaubte Weg fuer genau die
Aufgaben nicht funktioniert, fuer die er gedacht ist. Gefunden
hat es die Pruefung, nicht das Nachdenken. */
const angeboten = z && z.zustand === "offen" && a.verteilart === "pool";
if (z && !angeboten && z.zustand !== "abgelehnt") {
return res.status(409).json({ fehler: "schon_zugeteilt" });
}
/* HAT SIE SCHON JEMAND? Dann ist eine Bewerbung eine Enttaeuschung
mit Anlauf. Dieselbe Frage wie beim Uebernehmen, damit beide
Wege dasselbe sagen. */
const schon = db().prepare(`SELECT p.name FROM aufgaben_zuteilung z
LEFT JOIN personen p ON p.id = z.person_id
WHERE z.aufgabe_id = ? AND z.person_id <> ?
AND z.zustand IN ('angenommen','arbeit','erledigt')`).get(a.id, req.person.id);
if (schon && a.verteilart === "pool") {
return res.status(409).json({ fehler: "schon_vergeben", wer: schon.name });
}
const text = String(req.body?.text ?? "").trim().slice(0, BEWERBUNG_TEXT_MAX);
const nun = jetzt();
if (z) {
db().prepare(`UPDATE aufgaben_zuteilung
SET zustand = 'beworben', grund = ?, geantwortet_am = ?,
entscheid_text = NULL, entschieden_von = NULL, entschieden_am = NULL
WHERE id = ?`).run(text || null, nun, z.id);
} else {
/* `zugeteilt_von` bleibt leer: Niemand hat sie ihm gegeben. */
db().prepare(`INSERT INTO aufgaben_zuteilung
(aufgabe_id, person_id, zustand, grund, zugeteilt_am, geantwortet_am)
VALUES (?,?,'beworben',?,?,?)`)
.run(a.id, req.person.id, text || null, nun, nun);
}
protokolliere("aufgabe_beworben", {
personId: req.person.id, rolle: req.person.rolle, ip: echteIp(req),
detail: `#${a.id} ${a.titel}`.slice(0, 120),
});
res.json({ ok: true, zustand: "beworben" });
} catch (f) {
console.error("[zuteilung] bewerben:", f?.message);
res.status(503).json({ fehler: "nicht_verfuegbar" });
}
});
/* ---- Eine Bewerbung zuruecknehmen ------------------------------------ */
zuteilungRouter.post("/workspace/api/aufgaben/:id/bewerbung-zurueck", (req, res) => {
try {
const a = holen(req, res); if (!a) return;
const z = meine(a.id, req.person.id);
if (!z || z.zustand !== "beworben") {
return res.status(409).json({ fehler: "keine_bewerbung" });
}
/* GANZ WEG, nicht „zurueckgezogen". Wer es sich anders ueberlegt,
hat nichts getan, was jemand nachlesen muesste -- und eine Zeile,
die nur noch sagt „hier stand mal eine Bewerbung", waere in der
Liste der Leitung Laerm. */
db().prepare("DELETE FROM aufgaben_zuteilung WHERE id = ?").run(z.id);
protokolliere("bewerbung_zurueckgezogen", {
personId: req.person.id, rolle: req.person.rolle, ip: echteIp(req),
detail: `#${a.id} ${a.titel}`.slice(0, 120),
});
res.json({ ok: true });
} catch (f) {
console.error("[zuteilung] bewerbung-zurueck:", f?.message);
res.status(503).json({ fehler: "nicht_verfuegbar" });
}
});
/* ---- Ueber eine Bewerbung entscheiden -------------------------------- */
for (const [weg, entscheidung] of [["annehmen", "ja"], ["ablehnen", "nein"]]) {
zuteilungRouter.post(`/workspace/api/aufgaben/:id/bewerbung/${weg}`, (req, res) => {
try {
const a = holen(req, res); if (!a) return;
/* NUR DIE RECHTE HAND UND DOGFATHER. Die linke Hand darf
verteilen, aber nicht ueber Bewerbungen entscheiden -- sie
bewirbt sich ja selbst. */
if (!entscheidetUeberAufgaben(req.person)) {
return res.status(403).json({ fehler: "nicht_erlaubt" });
}
const wer = Number(req.body?.person_id);
if (!Number.isInteger(wer)) return res.status(400).json({ fehler: "ungueltig" });
const z = db().prepare(`SELECT * FROM aufgaben_zuteilung
WHERE aufgabe_id = ? AND person_id = ?`).get(a.id, wer);
if (!z || z.zustand !== "beworben") {
return res.status(409).json({ fehler: "keine_bewerbung" });
}
const kommentar = String(req.body?.kommentar ?? "").trim()
.slice(0, BEWERBUNG_TEXT_MAX) || null;
const nun = jetzt();
if (entscheidung === "ja") {
/* HAT INZWISCHEN JEMAND ANDERES? Die Frage muss VOR dem
Schreiben stehen -- sonst sagen zwei Zusagen im selben
Moment beide „hat geklappt". */
const schon = db().prepare(`SELECT p.name FROM aufgaben_zuteilung z
LEFT JOIN personen p ON p.id = z.person_id
WHERE z.aufgabe_id = ? AND z.person_id <> ?
AND z.zustand IN ('angenommen','arbeit','erledigt')`).get(a.id, wer);
if (schon && a.verteilart === "pool") {
return res.status(409).json({ fehler: "schon_vergeben", wer: schon.name });
}
db().prepare(`UPDATE aufgaben_zuteilung
SET zustand = 'angenommen', geantwortet_am = ?,
entscheid_text = ?, entschieden_von = ?, entschieden_am = ?
WHERE id = ?`).run(nun, kommentar, req.person.id, nun, z.id);
if (a.verteilart === "pool") uebernahmeAbschliessen(a.id, wer);
else {
db().prepare("UPDATE aufgaben SET verantwortlich_id = ? WHERE id = ?")
.run(wer, a.id);
}
} else {
db().prepare(`UPDATE aufgaben_zuteilung
SET zustand = 'abgelehnt', geantwortet_am = ?,
entscheid_text = ?, entschieden_von = ?, entschieden_am = ?
WHERE id = ?`).run(nun, kommentar, req.person.id, nun, z.id);
}
protokolliere(entscheidung === "ja" ? "bewerbung_angenommen" : "bewerbung_abgelehnt", {
personId: req.person.id, rolle: req.person.rolle, ip: echteIp(req),
detail: `#${a.id} fuer ${wer}`.slice(0, 120),
});
res.json({ ok: true, zustand: entscheidung === "ja" ? "angenommen" : "abgelehnt" });
} catch (f) {
console.error(`[zuteilung] bewerbung/${weg}:`, f?.message);
res.status(503).json({ fehler: "nicht_verfuegbar" });
}
});
}
/* ---- Meinen eigenen Stand setzen ------------------------------------ */
zuteilungRouter.post("/workspace/api/aufgaben/:id/mein-stand", (req, res) => {
try {