Aufgaben: eine Bewerbung auf eine erledigte Aufgabe ist keine mehr

Beim Durchsehen der echten Daten am 30.09. gefunden, Filipe am
01.10.: „mach alles los."

GEMESSEN: Zwei Bewerbungen von Miss standen auf „beworben" -- an
Aufgaben, die laengst `erledigt` bzw. `review` waren. Bei ihr stand
weiter „wartet auf Antwort", und in der Liste der Leitung stand eine
Entscheidung an, die es nicht mehr gibt.

DAS MUSTER GIBT ES IM HAUS SCHON. `uebernahmeAbschliessen` raeumt
genau so die offenen Bewerbungen weg, wenn jemand anderes eine
Pool-Aufgabe bekommt -- samt dem Kommentar daneben: „Ohne diese Zeile
blieb eine Bewerbung auf ,beworben' stehen, nachdem jemand anders die
Aufgabe bekommen hat." Derselbe Fall, ein anderer Ausloeser, dieselbe
Behandlung.

ZWEI WEGE FUEHREN IN DEN ENDZUSTAND -- erledigen und abbrechen. Beide
rufen jetzt dieselbe Funktion; nur einen zu bedienen waere die
Haelfte, die man spaeter sucht. Der SATZ ist verschieden:
„abgebrochen" ist nicht „erledigt", und wer gewartet hat, soll den
Unterschied lesen koennen.

KEIN `entschieden_von`. Niemand hat entschieden, die Frage hat sich
erledigt. Dadurch faellt die Zeile auch aus der Absagen-Uebersicht
von gestern heraus (die fragt `entschieden_von IS NOT NULL`) --
richtig, es ist keine Absage an diesen Menschen.

KEINE BENACHRICHTIGUNG. „Deine Bewerbung: diesmal nicht" waere
falsch -- es hat niemand nein gesagt. Der Satz steht an der Zeile.
Wenn Filipe hier doch eine Meldung will, ist es eine eigene Art mit
eigenem Wortlaut, kein Anhaengsel an die bestehende.

GEPRUEFT -- pruef-bewerbung-aufgaben 163/0 (9 neue):

  bewirbt sich          -> „beworben"
  Aufgabe erledigt      -> faellt weg, mit Satz, ohne Entscheider
  Aufgabe abgebrochen   -> ebenso, mit anderem Satz
  Aufgabe noch offen    -> Bewerbung bleibt   <- die Gegenprobe

Ohne die letzte Zeile hiesse „faellt weg" womoeglich nur, dass jede
Bewerbung wegfaellt.

Ein eigener Messfehler unterwegs: Mein Lesehelfer fragte
`/api/aufgaben/:id` und bekam `undefined` -- die Antwort dort hat eine
andere Form. Vier Pruefungen waren rot, waehrend der Mechanismus im
Protokoll nachweislich lief. Jetzt ueber die Liste, die in dieser
Datei erprobt ist.

Die eine vorhandene Zeile wird nachgetragen; auf einer Kopie der
echten Datenbank durchgespielt (danach 0 offene Bewerbungen auf
durchgelaufenen Aufgaben, integrity_check ok).

pruef-zuteilung, pruef-aufgabenbrett, pruef-zwischenspeicher.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-10-01 00:59:27 +02:00
co-authored by Claude Opus 5
parent 09375047a7
commit 8e18c7bcf0
49 changed files with 861 additions and 671 deletions
+83
View File
@@ -867,6 +867,89 @@ melde("=== 6. Am echten Bildschirm ===");
} finally { await h2.ctx.close(); }
}
/* =================================================================
EINE BEWERBUNG AUF EINE ERLEDIGTE AUFGABE IST KEINE (01.10.2026)
Beim Durchsehen der echten Daten gefunden: Zwei Bewerbungen
standen auf „beworben" an Aufgaben, die laengst durch waren.
Bei der Person stand weiter „wartet auf Antwort", bei der
Leitung eine Entscheidung, die es nicht mehr gibt.
DAS MUSTER GIBT ES IM HAUS SCHON -- `uebernahmeAbschliessen`
raeumt genauso auf, wenn jemand anderes eine Pool-Aufgabe
bekommt. Derselbe Fall, ein anderer Ausloeser.
================================================================= */
console.log("");
console.log("=== Bewerbungen auf durchgelaufene Aufgaben ===");
{
/* UEBER DIE LISTE, nicht ueber die Einzelaufgabe. Der erste
Anlauf las `/api/aufgaben/:id` und bekam `undefined` -- die
Antwort dort hat eine andere Form. Die Liste ist in dieser
Datei erprobt, und „erprobt" schlaegt „naheliegend". */
const stand = async (id, wer) => {
const a = await roh("/workspace/api/aufgaben", { keks: hand.keks });
const auf = (a.json?.aufgaben || a.json?.liste || []).find((x) => x.id === id);
return (auf?.zuteilung || []).find((x) => x.person_id === wer) || null;
};
/* ---- Weg 1: die Aufgabe wird ERLEDIGT ---------------------- */
const e1 = await roh("/workspace/api/aufgaben", {
methode: "POST", keks: hand.keks,
body: { titel: "Kurz die Liste sortieren", prioritaet: "niedrig" } });
const eId = e1.json?.aufgabe?.id ?? e1.json?.id;
const b1 = await roh(`/workspace/api/aufgaben/${eId}/bewerben`, {
methode: "POST", keks: modi.keks, body: { text: "Kann ich machen." } });
ok(b1.code === 200, `Frida bewirbt sich (${b1.code})`);
ok((await stand(eId, idModi))?.zustand === "beworben",
"und steht auf „beworben\"");
const fertig = await roh(`/workspace/api/aufgaben/${eId}`, {
methode: "PATCH", keks: hand.keks, body: { status: "erledigt" } });
ok(fertig.code === 200, `die rechte Hand erledigt sie (${fertig.code})`);
const nach1 = await stand(eId, idModi);
ok(nach1?.zustand !== "beworben",
`danach wartet Frida nicht mehr (${nach1?.zustand})`);
ok(/inzwischen erledigt/.test(nach1?.entscheid_text || ""),
`und liest, warum (${JSON.stringify(nach1?.entscheid_text)})`);
/* NIEMAND HAT ENTSCHIEDEN. Dadurch faellt die Zeile aus der
Absagen-Uebersicht heraus -- es ist keine Absage an sie. */
ok(!nach1?.entschieden_von,
`niemand hat ihr abgesagt (entschieden_von=${JSON.stringify(nach1?.entschieden_von)})`);
/* ---- Weg 2: die Aufgabe wird ABGEBROCHEN ------------------- */
const e2 = await roh("/workspace/api/aufgaben", {
methode: "POST", keks: hand.keks,
body: { titel: "Etwas, das sich erledigt hat", prioritaet: "niedrig" } });
const aId = e2.json?.aufgabe?.id ?? e2.json?.id;
await roh(`/workspace/api/aufgaben/${aId}/bewerben`, {
methode: "POST", keks: modi.keks, body: { text: "Mach ich." } });
const ab = await roh(`/workspace/api/aufgaben/${aId}/abbrechen`, {
methode: "POST", keks: chef.keks,
body: { grund: "Brauchen wir doch nicht." } });
const nach2 = await stand(aId, idModi);
ok(ab.code === 200 && nach2?.zustand !== "beworben",
`beim Abbrechen ebenso (${ab.code}, ${nach2?.zustand})`);
/* DER SATZ IST EIN ANDERER -- „abgebrochen" ist nicht
„erledigt", und wer gewartet hat, soll den Unterschied
lesen koennen. */
ok(/abgebrochen/.test(nach2?.entscheid_text || ""),
`mit dem passenden Satz (${JSON.stringify(nach2?.entscheid_text)})`);
/* ---- GEGENPROBE: eine OFFENE Aufgabe behaelt ihre Bewerbung.
Ohne sie hiesse „faellt weg" womoeglich nur, dass jede
Bewerbung wegfaellt. */
const e3 = await roh("/workspace/api/aufgaben", {
methode: "POST", keks: hand.keks,
body: { titel: "Laeuft noch", prioritaet: "niedrig" } });
const oId = e3.json?.aufgabe?.id ?? e3.json?.id;
await roh(`/workspace/api/aufgaben/${oId}/bewerben`, {
methode: "POST", keks: modi.keks, body: { text: "Gern." } });
const nach3 = await stand(oId, idModi);
ok(nach3?.zustand === "beworben",
`eine offene Aufgabe behaelt ihre Bewerbung (${nach3?.zustand})`);
}
/* =================================================================
DAUERHAFTE AUFGABEN (30.09.2026)
+25 -1
View File
@@ -28,7 +28,7 @@ const TEXT_MAX = 4000;
const jetzt = () => new Date().toISOString();
import { zuteilen, mitZuteilung } from "./workspace-zuteilung.js";
import { zuteilen, mitZuteilung, bewerbungenSchliessen } from "./workspace-zuteilung.js";
/* ---------- Schranke ---------------------------------------------------- */
@@ -1064,6 +1064,17 @@ aufgabenRouter.patch("/workspace/api/aufgaben/:id", gleicheHerkunft, (req, res)
if (aus.status === "erledigt" && aufgabe.status !== "erledigt") {
setz.push("erledigt_am = ?"); daten.push(jetzt());
/* WER SICH BEWORBEN HAT, WARTET NICHT WEITER (01.10.2026).
Eine erledigte Aufgabe hat keine offenen Bewerbungen mehr --
sonst steht bei dem Menschen „wartet auf Antwort" auf etwas,
das es nicht mehr gibt, und bei der Leitung eine
Entscheidung, die sich erledigt hat. */
const weg = bewerbungenSchliessen(aufgabe.id,
"Die Aufgabe wurde inzwischen erledigt.");
if (weg) {
console.log(`[aufgaben] ${weg} offene Bewerbung(en) zu #${aufgabe.id} `
+ "sind mit der Erledigung weggefallen.");
}
} else if (aus.status && aus.status !== "erledigt") {
setz.push("erledigt_am = NULL");
}
@@ -1161,6 +1172,19 @@ aufgabenRouter.post("/workspace/api/aufgaben/:id/abbrechen", gleicheHerkunft, (r
WHERE id = ?`)
.run(aufgabe.status, grund, jetzt(), req.person.id, jetzt(), id);
/* AUCH HIER FALLEN DIE OFFENEN BEWERBUNGEN WEG (01.10.2026).
Zwei Wege fuehren in den Endzustand -- erledigen und abbrechen
--, und beide brauchen dieselbe Aufraeumung. Nur einen zu
bedienen waere die Haelfte, die man spaeter sucht.
DER SATZ IST EIN ANDERER: „abgebrochen" ist nicht „erledigt",
und wer gewartet hat, soll den Unterschied lesen koennen. */
const wegAb = bewerbungenSchliessen(id, "Die Aufgabe wurde abgebrochen.");
if (wegAb) {
console.log(`[aufgaben] ${wegAb} offene Bewerbung(en) zu #${id} `
+ "sind mit dem Abbruch weggefallen.");
}
protokolliere("aufgabe_abgebrochen", {
personId: req.person.id, rolle: req.person.rolle, ip: echteIp(req),
detail: `#${id} (${aufgabe.status}) ${grund}`.slice(0, 120),
+36
View File
@@ -334,6 +334,42 @@ function holen(req, res) {
return a;
}
/** Eine erledigte oder abgebrochene Aufgabe hat keine offenen
* Bewerbungen mehr (01.10.2026).
*
* GEFUNDEN AN DEN ECHTEN DATEN: Zwei Bewerbungen standen auf
* `beworben` an Aufgaben, die laengst durch waren. Bei den
* Bewerbern stand weiter „wartet auf Antwort", und in der Liste der
* Leitung stand eine Entscheidung an, die es nicht mehr gibt.
*
* DAS MUSTER GIBT ES HIER SCHON, eine Funktion weiter oben:
* `uebernahmeAbschliessen` raeumt genau so auf, wenn jemand anderes
* eine Pool-Aufgabe bekommt. Derselbe Fall, ein anderer Ausloeser --
* deshalb dieselbe Behandlung und dasselbe Feld.
*
* KEIN `entschieden_von`: Niemand hat entschieden, die Frage hat
* sich erledigt. Dadurch faellt die Zeile auch aus der
* Absagen-Uebersicht heraus (die fragt `entschieden_von IS NOT
* NULL`) -- richtig, denn es ist keine Absage an diesen Menschen.
*
* @returns {number} wie viele weggefallen sind -- die Zahl braucht
* die Pruefung, und der Aufrufer soll nicht raten muessen.
*/
export function bewerbungenSchliessen(aufgabeId, grund) {
try {
const nun = jetzt();
const e = db().prepare(`UPDATE aufgaben_zuteilung
SET zustand = 'abgelehnt', entscheid_text = ?,
entschieden_am = ?, geantwortet_am = ?
WHERE aufgabe_id = ? AND zustand = 'beworben'`)
.run(grund, nun, nun, Number(aufgabeId));
return Number(e?.changes) || 0;
} catch (fehler) {
console.error("[zuteilung] Bewerbungen schliessen:", fehler?.message);
return 0;
}
}
/** Meine eigene Zuteilung zu dieser Aufgabe — oder nichts. */
function meine(aufgabeId, personId) {
return db().prepare(
+47
View File
@@ -4445,6 +4445,53 @@ function umstellungen(d) {
console.error("[workspace] Zuteilung nachtragen:", fehler?.message);
}
/* =====================================================================
BEWERBUNGEN AUF DURCHGELAUFENE AUFGABEN (01.10.2026)
=====================================================================
Beim Durchsehen der echten Daten am 30.09. gefunden: Zwei
Bewerbungen standen auf `beworben` an Aufgaben, die laengst
`erledigt` bzw. `review` waren. Bei der Person stand weiter
„wartet auf Antwort", und in der Liste der Leitung stand eine
Entscheidung an, die es nicht mehr gibt.
Die Ursache ist behoben (`bewerbungenSchliessen` laeuft jetzt an
beiden Wegen in den Endzustand). Dieser Block holt nach, was
schon dasteht.
OHNE `entschieden_von`: Niemand hat entschieden, die Frage hat
sich erledigt. Dadurch faellt die Zeile auch aus der
Absagen-Uebersicht heraus -- richtig, es ist keine Absage an
diesen Menschen.
WIEDERHOLBAR: Beim zweiten Start gibt es nichts mehr mit
`zustand = 'beworben'` an einer durchgelaufenen Aufgabe. */
try {
const vorher = d.prepare(`SELECT COUNT(*) AS n FROM aufgaben_zuteilung z
JOIN aufgaben a ON a.id = z.aufgabe_id
WHERE z.zustand = 'beworben'
AND a.status IN ('erledigt', 'abgebrochen', 'review')`).get().n;
if (vorher > 0) {
const nun = new Date().toISOString();
d.prepare(`UPDATE aufgaben_zuteilung
SET zustand = 'abgelehnt',
entscheid_text = (
SELECT CASE a.status WHEN 'abgebrochen'
THEN 'Die Aufgabe wurde abgebrochen.'
ELSE 'Die Aufgabe wurde inzwischen erledigt.' END
FROM aufgaben a WHERE a.id = aufgaben_zuteilung.aufgabe_id),
entschieden_am = ?, geantwortet_am = ?
WHERE zustand = 'beworben'
AND aufgabe_id IN (SELECT id FROM aufgaben
WHERE status IN ('erledigt', 'abgebrochen', 'review'))`)
.run(nun, nun);
console.log(`[workspace] ${vorher} Bewerbung(en) auf durchgelaufene `
+ "Aufgaben sind weggefallen.");
}
} catch (fehler) {
console.error("[workspace] Bewerbungen aufraeumen:", fehler?.message);
}
loeschspurenAufraeumen(d);
/* =====================================================================