Was unten im Brett steht, steht jetzt auch oben im Band

Filipe am 22.09.: "die aufgaben die man unten sieht soll man auch
oben sehen."

GEMESSEN an den echten Daten: aufgaben_zuteilung hatte NULL Zeilen,
waehrend im Brett zwei Aufgaben standen (OFFEN 1, IN ARBEIT 1). Jede
Person las oben "nichts zugeteilt". Die Aufgaben hingen am aelteren
Feld aufgaben.verantwortlich_id -- genau dem Feld, nach dem das Brett
gruppiert. Die Uebersicht las eine andere Quelle als die Liste
darunter.

resuemeeFuer() zaehlt jetzt BEIDE Wege und keinen doppelt: Gibt es zu
einer Aufgabe eine Zuteilungszeile fuer die Person, gewinnt die
Zuteilung (genauerer Zustand). Nur wo keine Zeile existiert, zaehlt
das alte Feld. Die Zuordnung status->zustand steht an EINER Stelle;
"review" zaehlt wie "arbeit", "abgebrochen" zaehlt nirgends -- so wie
im Brett auch.

Die Personenliste wird nicht mehr aufgezaehlt, sondern abgeleitet:
die Rollen des Hauses (immer, auch mit null Aufgaben -- wer frei ist,
ist die haeufigste Frage) PLUS jede aktive Person, der eine Aufgabe
gehoert. Ohne den zweiten Teil koennte im Brett eine Aufgabe stehen,
deren Mensch oben fehlt.

pruef-zuteilung 71/0 (war 65). Der neue Abschnitt legt eine Aufgabe
GENAU SO an wie die echten alten -- verantwortlich_id, keine
Zuteilungszeile -- und misst Filipes Satz direkt: "unten im Brett
stehen genauso viele wie oben (2 = 2)". Zahl in der Bedingung, nicht
nur im Meldetext.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-22 09:12:20 +02:00
co-authored by Claude Opus 5
parent 06f3fecaa7
commit 5bde49e0cd
2 changed files with 130 additions and 3 deletions
+68 -3
View File
@@ -56,6 +56,7 @@ import {
db, protokolliere, echteIp, darfAufgabenVerteilen, istDogFather, istHand,
sitzungLesen,
} from "./workspace.js";
import { TEAM_DOGI_ROLLEN } from "./crew-adresse.js";
export const zuteilungRouter = express.Router();
@@ -450,13 +451,59 @@ zuteilungRouter.post("/workspace/api/aufgaben/:id/bewerten", (req, res) => {
* ABGELEITET AUS DEN ZUSTÄNDEN, nicht aus einer zweiten Tabelle: Eine
* mitgeführte Zahl ist eine Zahl von gestern, sobald jemand etwas
* ändert und die Stelle vergisst. */
/** Der Zustand einer Aufgabe, die jemandem auf dem ALTEN Weg gehoert.
*
* Die Aufgabentabelle kennt offen/arbeit/review/erledigt/abgebrochen,
* die Zuteilung kennt offen/angenommen/arbeit/erledigt/abgelehnt.
* "review" heisst: liegt zur Freigabe, ist also noch nicht vom Tisch --
* deshalb zaehlt es wie "arbeit". "abgebrochen" ist weder dran noch
* erledigt und zaehlt gar nicht mit; es erscheint auch im Brett nicht
* in den Spalten OFFEN und IN ARBEIT.
*
* Die Zuordnung steht hier an EINER Stelle. Wer sie an zwei Stellen
* schreibt, hat beim naechsten Statuswert eine, die niemand nachzieht. */
const STATUS_ALS_ZUSTAND = {
offen: "offen", arbeit: "arbeit", review: "arbeit", erledigt: "erledigt",
};
export function resuemeeFuer(personId) {
/* ZWEI QUELLEN, EIN ERGEBNIS (22.09.2026).
==========================================================
Filipe: "die aufgaben die man unten sieht soll man auch oben
sehen."
Gemessen an den echten Daten: aufgaben_zuteilung hatte NULL
Zeilen, waehrend im Brett zwei Aufgaben standen (OFFEN 1,
IN ARBEIT 1). Jede Person las oben "nichts zugeteilt" -- weil
diese Zaehlung nur die neue Tabelle kannte. Die Aufgaben hingen
am aelteren Feld `aufgaben.verantwortlich_id`, und das ist genau
das Feld, nach dem das BRETT gruppiert.
Eine Uebersicht, die eine andere Quelle liest als die Liste
darunter, widerspricht ihr frueher oder spaeter. Deshalb zaehlen
hier BEIDE Wege -- und keiner doppelt: Gibt es zu einer Aufgabe
eine Zuteilungszeile fuer diese Person, gewinnt die Zuteilung
(sie hat den genaueren Zustand: angenommen, abgelehnt, bewertet).
Nur wo keine Zeile existiert, zaehlt das alte Feld. */
const zeilen = db().prepare(`
SELECT z.zustand, z.bewertung, a.frist, a.status AS aufgabe_status
FROM aufgaben_zuteilung z
JOIN aufgaben a ON a.id = z.aufgabe_id
WHERE z.person_id = ?`).all(Number(personId));
const alte = db().prepare(`
SELECT a.status, a.frist
FROM aufgaben a
WHERE a.verantwortlich_id = ?
AND NOT EXISTS (SELECT 1 FROM aufgaben_zuteilung z
WHERE z.aufgabe_id = a.id AND z.person_id = a.verantwortlich_id)
`).all(Number(personId));
for (const a of alte) {
const zustand = STATUS_ALS_ZUSTAND[a.status];
if (!zustand) continue; /* abgebrochen: zaehlt nirgends */
zeilen.push({ zustand, bewertung: null, frist: a.frist, aufgabe_status: a.status });
}
const heute = new Date().toISOString().slice(0, 10);
const zaehl = { offen: 0, angenommen: 0, arbeit: 0, erledigt: 0, abgelehnt: 0 };
let ueberfaellig = 0, gelobt = 0, zuVerbessern = 0;
@@ -510,11 +557,29 @@ zuteilungRouter.get("/workspace/api/aufgaben/team-resuemee", (req, res) => {
Abgeleitet aus der Rollenliste dieses Hauses statt aus einer
eigenen Aufzählung — wer morgen eine Rolle hinzufügt, muss hier
nichts nachziehen. */
/* WER IN DER LISTE STEHT -- ABGELEITET, NICHT AUFGEZAEHLT.
==========================================================
Die Rollen des Hauses stehen immer da, auch mit null Aufgaben:
Wer frei ist, ist die haeufigste Frage beim Verteilen.
Dazu kommt JEDE aktive Person, der im Brett eine Aufgabe
gehoert. Ohne diesen zweiten Teil koennte im Brett eine Aufgabe
stehen, deren Mensch oben fehlt -- genau der Widerspruch, den
Filipe am 22.09. gemeldet hat, nur an anderer Stelle. So kann
das nicht wieder entstehen: Die Liste folgt den Aufgaben. */
const rollen = [...TEAM_DOGI_ROLLEN];
const platz = rollen.map(() => "?").join(",");
const leute = db().prepare(`
SELECT id, name, rolle FROM personen
WHERE aktiv = 1 AND rolle IN ('modi','hand','linke')
ORDER BY CASE rolle WHEN 'hand' THEN 0 WHEN 'linke' THEN 1 ELSE 2 END,
name COLLATE NOCASE`).all();
WHERE aktiv = 1
AND (rolle IN (${platz})
OR id IN (SELECT verantwortlich_id FROM aufgaben
WHERE verantwortlich_id IS NOT NULL)
OR id IN (SELECT person_id FROM aufgaben_zuteilung))
AND rolle NOT IN ('creator','scout','manager','spicy','gast')
ORDER BY CASE rolle WHEN 'admin' THEN 0 WHEN 'hand' THEN 1
WHEN 'linke' THEN 2 ELSE 3 END,
name COLLATE NOCASE`).all(...rollen);
res.json({
leute: leute.map((p) => ({ ...p, ...resuemeeFuer(p.id) })),
darf_bewerten: true,