Vorlagenbrett: Modis bewerben sich, DogFather und die rechte Hand entscheiden
Filipe: "die modis sollen bei all diesen voschlaegen auch nur bewerben
koennen. die aufgaben aus der vorlage, da sollen die modis sich nur
bewerben koennen und nur dogfather und die rechte hand sollen annehmen
oder ablehnen koennen, mit einem text als notiz."
WAS SICH AENDERT
----------------
Auf dem Vorlagenbrett steht fuer einen Modi jetzt "Bewerben" statt
"Uebernehmen". Wer sich beworben hat, sieht das an der Karte -- samt
dem Satz, WER antwortet, und einem Weg zurueck. DogFather und die
rechte Hand sehen die Bewerbung an derselben Karte, mit Namen und dem
Wort dazu, und daneben "Annehmen" und "Ablehnen". Beide fragen nach
einer Notiz.
"ALSO NUR" GILT AUCH AM SERVER, nicht nur im Browser: Die alte Tuer
antwortet einem Modi mit 403 und dem Satz, was stattdessen geht. Ein
ausgeblendeter Knopf ist eine Bitte, abgelehnt wird in der Route.
DIE LINKE HAND STEHT ABSICHTLICH NICHT BEI DEN ENTSCHEIDERN
------------------------------------------------------------
Sie gehoert seit dem 22.09. ueberall dazu ("ich will dass die linke
hand auch ueberall zu sehen ist"). Hier hat Filipe genau zwei genannt.
Das ist keine Vergesslichkeit von mir, sondern seine Aufzaehlung -- und
dieselbe Grenze zieht das Haus schon bei den Aufgaben-Bewerbungen
(entscheidetUeberAufgaben). Sie darf weiter VERTEILEN; das hat er nicht
angefasst.
Sie ist deshalb die schaerfste Probe in der Pruefung: Wer statt "darf
entscheiden" nur "darf verteilen" abfragt, laesst sie mitentscheiden --
und niemandem faellt es auf, weil alles funktioniert.
DIE AUFGABE ENTSTEHT ERST MIT DER ZUSAGE
----------------------------------------
Der naheliegende Weg waere gewesen, beim Bewerben gleich die Aufgabe
anzulegen und die vorhandene Bewerbung aus aufgaben_zuteilung
daranzuhaengen. Dann stuende nach zwoelf Absagen zwoelfmal Arbeit auf
dem Brett, die niemand bestellt hat -- und um das einzufangen, muesste
das Ablehnen Aufgaben LOESCHEN. Loeschen als Nebenwirkung einer Absage
ist genau die Sorte Regel, die irgendwann das Falsche trifft.
Also eine eigene, kleine Tabelle (vorlagen_bewerbungen). Bis jemand ja
sagt, gibt es nur eine Zeile. Die Woerter sind dieselben wie drueben
(zustand, entscheid_text, entschieden_von) -- zwei Namen fuer dieselbe
Sache waeren zwei Sprachen im selben Haus.
Und die Zusage legt die Aufgabe ueber DIESELBE Funktion an wie das
Uebernehmen (katalogAufgabeAnlegen, neu, aus dem Katalog-Zweig
herausgeloest). Damit sieht eine erbetene Aufgabe aus wie eine
verteilte: gleiche Frist, gleiche Kategorie, gleiche Kennung. Ein
zweiter Weg waere ein zweiter Satz Regeln.
KLEINIGKEITEN, DIE SONST WEHTUN
-------------------------------
* "Alle 12 uebernehmen" gibt es nur fuer die, die verteilen. Ein
"Alle bewerben" waere der schnellste Weg, zwoelf Bitten auf einmal
loszuschicken -- und damit zwoelf Entscheidungen fuer jemand anderen.
* Wer eine Aufgabe schon hat, bekommt keinen Bewerben-Knopf. Der
Server lehnt das ohnehin ab; ein Knopf, der eine Absage holt, ist
schlimmer als keiner.
* Nach einer Absage darf man sich wieder bewerben. Der eindeutige
Index gilt deshalb nur fuer OFFENE Bewerbungen -- ueber alle
Zustaende waere eine Absage ein Bann.
* Gesucht wird ueber den SCHLUESSEL der Vorlage, nicht ueber die
Nummer in der Liste. Die Nummer verschiebt sich, sobald jemand eine
Vorlage einfuegt -- genau dieser Fehler ist am 16.09. schon einmal
passiert.
GEPRUEFT
--------
pruef-modi-katalog: 95 Pruefungen, 0 Fehler (vorher 49).
Die Pruefung ist beim Umbau ROT geworden -- 9 Zeilen, alle dort, wo ein
Modi sich selbst etwas nahm. Richtig so, sie hat die Aenderung bemerkt.
Sie steht jetzt auf dem neuen Weg und misst ihn ganz:
* der Modi kommt an die alte Tuer nicht mehr heran (403, erst_bewerben)
* die Bewerbung legt NOCH KEINE Aufgabe an
* die linke Hand darf verteilen, aber nicht entscheiden (403)
* der Bewerber selbst erst recht nicht (403)
* die Zusage erzeugt die Aufgabe -- mit Kategorie, Frist, Besitzer
* die Notizen stehen in der Datenbank, samt WER entschieden hat
(direkt gelesen: ein Feld, das der Server annimmt und nirgends
speichert, saehe von aussen genauso aus)
* nach einer Absage geht es wieder
* am Bildschirm: alle Knoepfe heissen "Bewerben", kein einziger
"Uebernehmen" mehr, die wartende Karte nennt, wer antwortet --
und DogFather klickt sich durch Annehmen samt Notizfeld, bis die
Aufgabe auf dem Brett steht
Die Gegenprobe in Abschnitt 7 lief mit dem Zugang des Modis und haette
ab heute nur noch bewiesen, dass die Rechtepruefung greift -- sie
benutzt jetzt DogFather. Genau so verliert eine Pruefung still ihren
Sinn.
pruef-aufgaben-vorlagen: unveraendert gruen.
ZWEI FUNDE NEBENHER, BEIDE AELTER ALS DIESE AENDERUNG -- gemessen, nicht
vermutet (mit gestashten Aenderungen gegengeprueft):
* pruef-modi-wortleck ist seit dem 22.09. rot: Der Rollenname steht
in team.css und teamlage.js, also in Dateien, die jeder bekommt.
* pruef-zuteilung stuerzt seit laengerem ab (#neu-oeffnen ist
verborgen). Beides kommt als naechstes, getrennt.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+300
-16
@@ -36,8 +36,18 @@
|
||||
|
||||
import express from "express";
|
||||
import {
|
||||
db, sitzungLesen, istLeitung, darfCreator, protokolliere, echteIp, kategorienFuer,
|
||||
termineSichtbar, MODI_KATEGORIEN, MODI_STUFEN,
|
||||
db,
|
||||
sitzungLesen,
|
||||
istLeitung,
|
||||
darfCreator,
|
||||
protokolliere,
|
||||
echteIp,
|
||||
kategorienFuer,
|
||||
termineSichtbar,
|
||||
MODI_KATEGORIEN,
|
||||
MODI_STUFEN,
|
||||
entscheidetUeberAufgaben,
|
||||
darfAufgabenVerteilen,
|
||||
} from "./workspace.js";
|
||||
|
||||
export const vorlagenRouter = express.Router();
|
||||
@@ -1431,6 +1441,26 @@ vorlagenRouter.get("/workspace/api/vorlagen", (req, res) => {
|
||||
aufbauplan: kategorienFuer(req.person).length
|
||||
? { gruppen: MODI_GRUPPEN, aufgaben: MODI_KATALOG }
|
||||
: null,
|
||||
/* ==== DIE BEWERBUNGEN KOMMEN MIT (23.09.2026) =================
|
||||
|
||||
Sie stehen in DERSELBEN Antwort wie der Katalog und nicht in
|
||||
einer zweiten Abfrage. Das Brett zeichnet sich bei jedem
|
||||
Kategoriewechsel neu; eine eigene Abfrage daneben waere ein
|
||||
zweiter Weg, auf dem die beiden auseinanderlaufen koennen --
|
||||
und man saehe eine Karte als "beworben", die es nicht mehr ist.
|
||||
|
||||
WER WAS SIEHT, entscheidet vorlagenBewerbungenFuer(): Wer
|
||||
entscheidet, bekommt alle offenen; alle anderen nur ihre
|
||||
eigenen. Im Browser zu filtern hiesse, die fremden Namen
|
||||
trotzdem auszuliefern.
|
||||
|
||||
UND OB ICH ENTSCHEIDEN DARF, sagt ebenfalls der Server. Die
|
||||
Oberflaeche vergleicht keine Rollennamen -- assets/js/aufgaben.js
|
||||
bekommt jeder, der die Seite oeffnet. */
|
||||
bewerbungen: kategorienFuer(req.person).length
|
||||
? vorlagenBewerbungenFuer(req.person) : null,
|
||||
darf_entscheiden: entscheidetUeberAufgaben(req.person),
|
||||
darf_verteilen: darfAufgabenVerteilen(req.person),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1454,6 +1484,253 @@ function gleicheHerkunft(req, res, next) {
|
||||
next();
|
||||
}
|
||||
|
||||
/* =====================================================================
|
||||
AUS EINER VORLAGE WIRD EINE AUFGABE -- an EINER Stelle (23.09.2026)
|
||||
|
||||
Dieselben acht Zeilen standen im Zweig "katalog" der Uebernehmen-
|
||||
Route. Seit eine Zusage auf eine Bewerbung dasselbe tut, braucht es
|
||||
sie zweimal -- und zwei Fassungen desselben INSERT sind zwei
|
||||
Gelegenheiten, dass eine davon beim naechsten Feld nicht mitgeht.
|
||||
|
||||
DIE FRIST WIRD IN ORTSZEIT GERECHNET. Ueber toISOString kaeme
|
||||
zwischen Mitternacht und zwei Uhr der Vortag heraus -- derselbe
|
||||
Fehler, der im Haus schon einmal eine Pruefung falsch rot gemacht
|
||||
hat.
|
||||
|
||||
DIE KENNUNG DER VORLAGE WANDERT MIT. Nur so kann das Brett spaeter
|
||||
zeigen, was schon uebernommen ist -- der Titel taugt dafuer nicht,
|
||||
er darf geaendert werden. */
|
||||
function katalogAufgabeAnlegen(v, zielId, vonId) {
|
||||
const d = new Date(Date.now() + (v.tage || 7) * 86400_000);
|
||||
const frist = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`
|
||||
+ `-${String(d.getDate()).padStart(2, "0")}`;
|
||||
db().prepare(`INSERT INTO aufgaben
|
||||
(titel, beschreibung, status, prioritaet, verantwortlich_id, frist,
|
||||
kategorie, vorlage, erstellt, erstellt_von)
|
||||
VALUES (?,?,'offen',?,?,?,?,?,?,?)`)
|
||||
.run(v.titel, v.text, v.tage <= 1 ? "hoch" : "mittel",
|
||||
zielId, frist, v.kategorie, v.schluessel, jetzt(), vonId);
|
||||
return db().prepare("SELECT last_insert_rowid() AS id").get().id;
|
||||
}
|
||||
|
||||
/* Wie lang darf ein Wort zu einer Bewerbung sein? Dieselbe Groesse wie
|
||||
drueben bei den Aufgaben -- ein Satz, kein Aufsatz. */
|
||||
const BEWERBUNG_TEXT_MAX = 600;
|
||||
|
||||
/** Die offenen Bewerbungen, so wie sie die Seite braucht.
|
||||
*
|
||||
* WER WAS SIEHT, ENTSCHEIDET HIER DER SERVER: Wer entscheidet, sieht
|
||||
* alle offenen; alle anderen sehen nur die eigenen. Im Browser zu
|
||||
* filtern hiesse, die fremden Namen trotzdem auszuliefern -- und
|
||||
* assets/js/aufgaben.js bekommt jeder, der die Seite oeffnet.
|
||||
*/
|
||||
function vorlagenBewerbungenFuer(person) {
|
||||
const darf = entscheidetUeberAufgaben(person);
|
||||
const zeilen = db().prepare(`
|
||||
SELECT b.id, b.vorlage, b.kategorie, b.person_id, b.text, b.zustand,
|
||||
b.entscheid_text, b.entschieden_am, b.erstellt, p.name AS person_name
|
||||
FROM vorlagen_bewerbungen b
|
||||
LEFT JOIN personen p ON p.id = b.person_id
|
||||
WHERE b.zustand = 'beworben' ${darf ? "" : "AND b.person_id = ?"}
|
||||
ORDER BY b.id`).all(...(darf ? [] : [person.id]));
|
||||
return zeilen.map((z) => ({
|
||||
id: z.id, vorlage: z.vorlage, kategorie: z.kategorie,
|
||||
person_id: z.person_id, person_name: z.person_name || "Jemand",
|
||||
text: z.text || null, erstellt: z.erstellt,
|
||||
selbst: z.person_id === person.id,
|
||||
}));
|
||||
}
|
||||
|
||||
/* =====================================================================
|
||||
SICH AUF EINE VORLAGE BEWERBEN (23.09.2026)
|
||||
|
||||
Filipe: "die modis sollen bei all diesen voschlaegen auch nur
|
||||
bewerben koennen. die aufgaben aus der vorlage, da sollen die modis
|
||||
sich nur bewerben koennen und nur dogfather und die rechte hand
|
||||
sollen annehmen oder ablehnen koennen, mit einem text als notiz."
|
||||
|
||||
WER DARF WAS -- und die zweite Zeile ist die, die er ausdruecklich
|
||||
gesagt hat:
|
||||
|
||||
bewerben wer NICHT verteilen darf (die Modis)
|
||||
entscheiden DogFather und die rechte Hand
|
||||
uebernehmen wer verteilen darf, wie bisher
|
||||
|
||||
DIE LINKE HAND STEHT ABSICHTLICH NICHT BEI DEN ENTSCHEIDERN. Sie
|
||||
gehoert sonst ueberall dazu (Auftrag vom 22.09.: "ich will dass die
|
||||
linke hand auch ueberall zu sehen ist"), hier hat Filipe genau zwei
|
||||
genannt. Das ist keine Vergesslichkeit von mir, sondern seine
|
||||
Aufzaehlung -- und dieselbe Grenze zieht das Haus schon bei den
|
||||
Aufgaben-Bewerbungen (entscheidetUeberAufgaben).
|
||||
|
||||
DER TEXT IST FREIWILLIG BEIM BEWERBEN und freiwillig beim
|
||||
Entscheiden. Beim ABLEHNEN einer zugeteilten Aufgabe ist eine
|
||||
Begruendung Pflicht -- dort sagt jemand ab, der gefragt wurde. Hier
|
||||
bittet jemand; ein "bitte" braucht keine Begruendung. Filipes Wort
|
||||
ist "mit einem text als notiz", also eine Moeglichkeit, keine Huerde.
|
||||
===================================================================== */
|
||||
|
||||
/** Die Vorlage zu einer Kennung -- oder null.
|
||||
* Gesucht wird ueber den SCHLUESSEL und nicht ueber die Nummer in der
|
||||
* Liste: Die Nummer verschiebt sich, sobald jemand eine Vorlage
|
||||
* einfuegt, und dann bewirbt man sich auf etwas anderes, als man
|
||||
* gesehen hat. Genau dieser Fehler ist am 16.09. schon einmal
|
||||
* passiert. */
|
||||
function vorlageZuSchluessel(schluessel) {
|
||||
return MODI_AUFGABEN_FLACH.find((v) => v.schluessel === schluessel) || null;
|
||||
}
|
||||
|
||||
vorlagenRouter.post("/workspace/api/vorlagen/bewerben", gleicheHerkunft,
|
||||
express.json({ limit: "4kb" }), (req, res) => {
|
||||
try {
|
||||
if (!kategorienFuer(req.person).length) {
|
||||
/* Wortgleich mit einer erfundenen Vorlage -- wer den Katalog
|
||||
nicht bekommt, soll nicht erfahren, dass es ihn gibt. */
|
||||
return res.status(400).json({ fehler: "Unbekannte Vorlage." });
|
||||
}
|
||||
/* WER VERTEILEN DARF, BEWIRBT SICH NICHT. Er traegt sich ein --
|
||||
eine Bitte an sich selbst waere ein Vorgang ohne Gegenueber,
|
||||
und im schlimmsten Fall entscheidet jemand ueber die eigene
|
||||
Bewerbung. */
|
||||
if (darfAufgabenVerteilen(req.person)) {
|
||||
return res.status(403).json({ fehler: "du_entscheidest_selbst" });
|
||||
}
|
||||
const v = vorlageZuSchluessel(String(req.body?.vorlage || ""));
|
||||
if (!v) return res.status(400).json({ fehler: "Unbekannte Vorlage." });
|
||||
|
||||
const text = String(req.body?.text ?? "").trim().slice(0, BEWERBUNG_TEXT_MAX) || null;
|
||||
const d = db();
|
||||
|
||||
/* SCHON BEWORBEN? Die Datenbank sagt es ohnehin (der Index ist
|
||||
eindeutig) -- aber eine saubere Antwort ist besser als ein
|
||||
Fehler aus der Tiefe. */
|
||||
const schon = d.prepare(`SELECT id FROM vorlagen_bewerbungen
|
||||
WHERE vorlage = ? AND person_id = ? AND zustand = 'beworben'`)
|
||||
.get(v.schluessel, req.person.id);
|
||||
if (schon) return res.status(409).json({ fehler: "schon_beworben" });
|
||||
|
||||
/* UND LIEGT SIE SCHON AUF DEM BRETT? Sich auf etwas zu bewerben,
|
||||
das man bereits hat, ist eine Bitte ins Leere. */
|
||||
const liegtSchon = d.prepare(`SELECT id FROM aufgaben
|
||||
WHERE vorlage = ? AND verantwortlich_id = ? AND status <> 'erledigt'`)
|
||||
.get(v.schluessel, req.person.id);
|
||||
if (liegtSchon) return res.status(409).json({ fehler: "liegt_schon_bei_dir" });
|
||||
|
||||
d.prepare(`INSERT INTO vorlagen_bewerbungen
|
||||
(vorlage, kategorie, person_id, text, zustand, erstellt)
|
||||
VALUES (?,?,?,?,'beworben',?)`)
|
||||
.run(v.schluessel, v.kategorie, req.person.id, text, jetzt());
|
||||
|
||||
protokolliere("vorlage_beworben", {
|
||||
personId: req.person.id, rolle: req.person.rolle, ip: echteIp(req),
|
||||
detail: `${v.schluessel} ${v.titel}`.slice(0, 120),
|
||||
});
|
||||
res.status(201).json({ ok: true, bewerbungen: vorlagenBewerbungenFuer(req.person) });
|
||||
} catch (f) {
|
||||
console.error("[vorlagen] bewerben:", f?.message);
|
||||
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||
}
|
||||
});
|
||||
|
||||
/* ---- Die eigene Bewerbung zuruecknehmen ------------------------------
|
||||
Sie wird GELOESCHT und nicht auf "zurueckgezogen" gesetzt: Wer sich
|
||||
nicht mehr bewirbt, 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. Dieselbe Entscheidung wie bei
|
||||
den Aufgaben-Bewerbungen. */
|
||||
vorlagenRouter.post("/workspace/api/vorlagen/bewerbung-zurueck", gleicheHerkunft,
|
||||
express.json({ limit: "2kb" }), (req, res) => {
|
||||
try {
|
||||
const id = Number(req.body?.id);
|
||||
if (!Number.isInteger(id)) return res.status(400).json({ fehler: "ungueltig" });
|
||||
const b = db().prepare("SELECT * FROM vorlagen_bewerbungen WHERE id = ?").get(id);
|
||||
/* NICHT MEINE = GIBT ES NICHT. Ein 403 verriete, dass es sie
|
||||
gibt und wem sie gehoert. */
|
||||
if (!b || b.person_id !== req.person.id || b.zustand !== "beworben") {
|
||||
return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||
}
|
||||
db().prepare("DELETE FROM vorlagen_bewerbungen WHERE id = ?").run(id);
|
||||
protokolliere("vorlage_bewerbung_zurueck", {
|
||||
personId: req.person.id, rolle: req.person.rolle, ip: echteIp(req),
|
||||
detail: String(b.vorlage).slice(0, 120),
|
||||
});
|
||||
res.json({ ok: true, bewerbungen: vorlagenBewerbungenFuer(req.person) });
|
||||
} catch (f) {
|
||||
console.error("[vorlagen] bewerbung-zurueck:", f?.message);
|
||||
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||
}
|
||||
});
|
||||
|
||||
/* ---- Entscheiden: annehmen oder ablehnen ------------------------------
|
||||
|
||||
EINE SCHLEIFE FUER BEIDE WEGE. Zwei fast gleiche Routen waeren zwei
|
||||
Gelegenheiten, dass eine die Rechtepruefung oder das Protokoll
|
||||
vergisst -- und es ist immer die zweite.
|
||||
|
||||
BEI DER ZUSAGE ENTSTEHT DIE AUFGABE, und zwar ueber dieselbe
|
||||
Funktion wie beim Uebernehmen (katalogAufgabeAnlegen). Damit sieht
|
||||
eine erbetene Aufgabe genauso aus wie eine verteilte: gleiche Frist,
|
||||
gleiche Kategorie, gleiche Kennung. Ein zweiter Weg waere ein
|
||||
zweiter Satz Regeln. */
|
||||
for (const [weg, ja] of [["annehmen", true], ["ablehnen", false]]) {
|
||||
vorlagenRouter.post(`/workspace/api/vorlagen/bewerbung/${weg}`, gleicheHerkunft,
|
||||
express.json({ limit: "4kb" }), (req, res) => {
|
||||
try {
|
||||
/* NUR DOGFATHER UND DIE RECHTE HAND -- woertlich nach Filipes
|
||||
Aufzaehlung, und dieselbe Schranke wie bei den
|
||||
Aufgaben-Bewerbungen. */
|
||||
if (!entscheidetUeberAufgaben(req.person)) {
|
||||
return res.status(403).json({ fehler: "nicht_erlaubt" });
|
||||
}
|
||||
const id = Number(req.body?.id);
|
||||
if (!Number.isInteger(id)) return res.status(400).json({ fehler: "ungueltig" });
|
||||
|
||||
const d = db();
|
||||
const b = d.prepare("SELECT * FROM vorlagen_bewerbungen WHERE id = ?").get(id);
|
||||
if (!b || b.zustand !== "beworben") {
|
||||
return res.status(409).json({ fehler: "keine_bewerbung" });
|
||||
}
|
||||
const v = vorlageZuSchluessel(b.vorlage);
|
||||
if (!v) return res.status(409).json({ fehler: "vorlage_weg" });
|
||||
|
||||
/* GIBT ES DIE PERSON NOCH? Zwischen Bewerbung und Antwort
|
||||
koennen Tage liegen. Eine Aufgabe an einen abgeschalteten
|
||||
Zugang waere Arbeit, die nie jemand sieht. */
|
||||
const wer = d.prepare(
|
||||
"SELECT id, name FROM personen WHERE id = ? AND aktiv = 1").get(b.person_id);
|
||||
if (!wer) return res.status(409).json({ fehler: "person_weg" });
|
||||
|
||||
const kommentar = String(req.body?.kommentar ?? "").trim()
|
||||
.slice(0, BEWERBUNG_TEXT_MAX) || null;
|
||||
const nun = jetzt();
|
||||
let aufgabeId = null;
|
||||
|
||||
if (ja) {
|
||||
aufgabeId = katalogAufgabeAnlegen(v, wer.id, req.person.id);
|
||||
}
|
||||
d.prepare(`UPDATE vorlagen_bewerbungen
|
||||
SET zustand = ?, entscheid_text = ?, entschieden_von = ?,
|
||||
entschieden_am = ?, aufgabe_id = ?
|
||||
WHERE id = ?`)
|
||||
.run(ja ? "angenommen" : "abgelehnt", kommentar, req.person.id,
|
||||
nun, aufgabeId, id);
|
||||
|
||||
protokolliere(ja ? "vorlage_bewerbung_angenommen" : "vorlage_bewerbung_abgelehnt", {
|
||||
personId: req.person.id, rolle: req.person.rolle, ip: echteIp(req),
|
||||
detail: `${b.vorlage} fuer ${wer.name}`.slice(0, 120),
|
||||
});
|
||||
res.json({
|
||||
ok: true, zustand: ja ? "angenommen" : "abgelehnt",
|
||||
aufgabe_id: aufgabeId,
|
||||
bewerbungen: vorlagenBewerbungenFuer(req.person),
|
||||
});
|
||||
} catch (f) {
|
||||
console.error(`[vorlagen] bewerbung/${weg}:`, f?.message);
|
||||
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
vorlagenRouter.post("/workspace/api/vorlagen/uebernehmen", gleicheHerkunft,
|
||||
express.json({ limit: "16kb" }), (req, res) => {
|
||||
try {
|
||||
@@ -1572,9 +1849,26 @@ vorlagenRouter.post("/workspace/api/vorlagen/uebernehmen", gleicheHerkunft,
|
||||
bekommt, soll nicht erfahren, dass es ihn gibt. */
|
||||
return res.status(400).json({ fehler: "Unbekannte Art." });
|
||||
}
|
||||
const zielId = req.person.rolle === "modi"
|
||||
? req.person.id
|
||||
: Number(req.body.person_id) || null;
|
||||
/* ==== WER SICH HIER SELBST ETWAS NIMMT (23.09.2026) ======
|
||||
|
||||
Filipe: "die modis sollen bei all diesen voschlaegen auch
|
||||
nur bewerben koennen."
|
||||
|
||||
Bis heute stand hier: Ein Modi nimmt sich die Aufgabe
|
||||
selbst. Jetzt geht das ueber eine Bewerbung -- und zwar
|
||||
HIER abgelehnt und nicht nur im Browser ausgeblendet. Ein
|
||||
ausgeblendeter Knopf ist eine Bitte; abgelehnt wird an
|
||||
dieser Stelle.
|
||||
|
||||
GEPRUEFT WIRD "DARF VERTEILEN" UND NICHT "IST MODI": Die
|
||||
Frage ist nicht, wer jemand IST, sondern was er darf. So
|
||||
gilt die Regel auch fuer jede Rolle, die morgen dazukommt --
|
||||
und in dieser Datei steht kein Rollenname, der nicht
|
||||
hineingehoert. */
|
||||
if (!darfAufgabenVerteilen(req.person)) {
|
||||
return res.status(403).json({ fehler: "erst_bewerben" });
|
||||
}
|
||||
const zielId = Number(req.body.person_id) || null;
|
||||
if (!zielId) return res.status(400).json({ fehler: "Bitte zuerst eine Person wählen." });
|
||||
/* Nur an jemanden, fuer den die Kategorien ueberhaupt gelten --
|
||||
sonst landete eine Modi-Aufgabe samt Kategorie an einem
|
||||
@@ -1607,17 +1901,7 @@ vorlagenRouter.post("/workspace/api/vorlagen/uebernehmen", gleicheHerkunft,
|
||||
return res.status(400).json({ fehler: "Unbekannte Vorlage." });
|
||||
}
|
||||
for (const v of auswahl) {
|
||||
/* Frist in ORTSZEIT gerechnet. Ueber toISOString kaeme
|
||||
zwischen Mitternacht und zwei Uhr der Vortag heraus. */
|
||||
const d = new Date(Date.now() + (v.tage || 7) * 86400_000);
|
||||
const frist = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`
|
||||
+ `-${String(d.getDate()).padStart(2, "0")}`;
|
||||
db().prepare(`INSERT INTO aufgaben
|
||||
(titel, beschreibung, status, prioritaet, verantwortlich_id, frist,
|
||||
kategorie, vorlage, erstellt, erstellt_von)
|
||||
VALUES (?,?,'offen',?,?,?,?,?,?,?)`)
|
||||
.run(v.titel, v.text, v.tage <= 1 ? "hoch" : "mittel",
|
||||
zielPerson.id, frist, v.kategorie, v.schluessel, jetzt(), req.person.id);
|
||||
katalogAufgabeAnlegen(v, zielPerson.id, req.person.id);
|
||||
angelegt.push(v.titel);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user