diff --git a/server/mess-manager-ziele.mjs b/server/mess-manager-ziele.mjs
index 9883cc5a..8a99f418 100644
--- a/server/mess-manager-ziele.mjs
+++ b/server/mess-manager-ziele.mjs
@@ -229,6 +229,51 @@ await bild("formular-handy", keksScout, 412, 915, async (seite) => {
if (knopf) { await knopf.click(); await seite.waitForTimeout(400); }
});
+/* ---------- EIN KLICK, UND ES STEHT DRIN ----------------------------
+ Der Kern von Filipes Wunsch („so wenig wie moeglich zu tippen").
+ Gemessen wird nicht, ob der Knopf da ist, sondern ob danach eine
+ andere Zahl dasteht -- und ob „Rueckgaengig" sie zurueckholt. */
+{
+ const kontext = await browser.newContext({ viewport: { width: 412, height: 915 }, hasTouch: true });
+ await kontext.addCookies(keksScout.split("; ").map((k) => {
+ const [n, ...r] = k.split("=");
+ return { name: n, value: r.join("="), domain: "127.0.0.1", path: "/" };
+ }));
+ const seite = await kontext.newPage();
+ const konsole = [];
+ seite.on("pageerror", (e) => konsole.push("Absturz: " + e.message));
+ seite.on("console", (e) => {
+ if (e.type() === "error" && !/favicon|net::ERR/i.test(e.text())) konsole.push(e.text().slice(0, 160));
+ });
+ await seite.goto(`http://127.0.0.1:${PORT}/workspace/manager-ziele.html`, { waitUntil: "networkidle" });
+ await seite.waitForTimeout(1200);
+
+ const zahlVon = (schluessel) => seite.evaluate((k) => document
+ .querySelector(`.mz-zeile[data-aufgabe="${k}"] .mz-zeile__zahl`)?.textContent, schluessel);
+
+ const vorher = await zahlVon("schulung");
+ await seite.click('.mz-zeile[data-aufgabe="schulung"] .mz-schnell .knopf');
+ await seite.waitForTimeout(900);
+ const nachher = await zahlVon("schulung");
+ const meldung = await seite.evaluate(() =>
+ document.querySelector(".mz-gelungen")?.textContent || "-");
+ await seite.screenshot({ path: "bild-manager-ziele-einklick.png" });
+
+ /* Und zurueck. */
+ const zurueckDa = await seite.$(".mz-gelungen__zurueck");
+ if (zurueckDa) { await zurueckDa.click(); await seite.waitForTimeout(900); }
+ const zurueckZahl = await zahlVon("schulung");
+
+ console.log("");
+ console.log("--- Ein Klick, ein Eintrag (412px) ---");
+ console.log(` vorher: ${vorher}`);
+ console.log(` nach EINEM Klick: ${nachher} ${nachher !== vorher ? "" : "— KEINE Aenderung, Befund!"}`);
+ console.log(` Meldung: ${meldung}`);
+ console.log(` nach Rueckgaengig: ${zurueckZahl} ${zurueckZahl === vorher ? "(wieder wie vorher)" : "— NICHT zurueck, Befund!"}`);
+ if (konsole.length) console.log(` KONSOLE: ${konsole.join(" | ")}`);
+ await kontext.close();
+}
+
/* ---------- Und die Kachelwand davor --------------------------------
Gemessen wird nicht, wie es aussieht, sondern WAS wirklich gesetzt
ist: das Merkmal am Element und der Schatten, den der Browser
diff --git a/server/pruef-manager-ziele.mjs b/server/pruef-manager-ziele.mjs
index 43986567..f8309d8c 100644
--- a/server/pruef-manager-ziele.mjs
+++ b/server/pruef-manager-ziele.mjs
@@ -731,6 +731,155 @@ if (heuteNr === 1) {
"…alle seine Zeilen stehen auf neutral, keine einzige warnt");
}
+/* =====================================================================
+ 12b · DER SCHNELL-EINTRAG
+ =====================================================================
+
+ Filipe am 02.10.2026: „so wenig wie moeglich zu tippen. fertige
+ sachen, bereit um ab zu gehen."
+
+ DER WICHTIGSTE FALL IST DIE TEILWEISE ABLEHNUNG. Drei Namen
+ einwerfen, von denen einer schon dasteht -- wenn dann die ganze
+ Liste zurueckgewiesen wird, tippt man alles noch einmal. Genau
+ deshalb wird hier geprueft, dass die anderen zwei durchgehen UND
+ dass gesagt wird, welcher nicht.
+ ===================================================================== */
+melde("\n=== Der Schnell-Eintrag ===");
+
+/* Ein eigener Mensch dafuer, damit die Zaehlungen der vorherigen
+ Abschnitte nicht durcheinandergeraten. */
+const SCHNELL_CODE = "CODE-Z90-000001";
+const dbS = new DatabaseSync(process.env.WORKSPACE_DB);
+{
+ const salz = randomBytes(16).toString("hex");
+ const h = scryptSync(SCHNELL_CODE, salz, 64,
+ { N: 32768, r: 8, p: 1, maxmem: 96 * 1024 * 1024 }).toString("hex");
+ dbS.prepare("INSERT INTO personen (name, rolle, code_hash, code_salt, code_n,"
+ + " code_kennung, aktiv, erstellt) VALUES (?,?,?,?,?,?,1,?)")
+ .run("Schnell Schnell", "scout", h, salz, 32768,
+ createHmac("sha256", kennSchluessel).update(SCHNELL_CODE).digest("hex"), ALT);
+}
+dbS.close();
+CODES.schnell = SCHNELL_CODE;
+kekse.schnell = await anmelden("schnell", "scout");
+braucht(!!kekse.schnell, "der Testzugang fuer den Schnell-Eintrag kam nicht herein");
+
+/* ---- Ohne jede Angabe: ein einziger Aufruf, ein Eintrag ---------- */
+const ohneAngabe = await schick("schnell", `${API}/eintraege`, "POST",
+ { aufgabe: "meeting", datum: HEUTE });
+const ohneAngabeD = await ohneAngabe.json();
+ok(ohneAngabe.status === 201,
+ `ein Meeting ohne eine einzige Angabe (${ohneAngabe.status})`);
+ok(ohneAngabeD.angelegt.length === 1, "genau ein Eintrag entstanden");
+ok(ohneAngabeD.stand.aufgaben.find((a) => a.schluessel === "meeting").zahl === 1,
+ "der Zaehler steht sofort auf 1 — ohne Dialog, ohne Neuladen");
+
+/* ---- Zweimal am selben Tag: Hinweis, keine Sperre ---------------- */
+const zweimal = await schick("schnell", `${API}/eintraege`, "POST",
+ { aufgabe: "meeting", datum: HEUTE });
+const zweimalD = await zweimal.json();
+ok(zweimal.status === 201,
+ `ein zweites Meeting am selben Tag wird NICHT verboten (${zweimal.status})`
+ + " — zwei an einem Tag sind moeglich");
+ok(zweimalD.warnungen?.[0]?.art === "zweimal_am_tag",
+ "…aber es wird gesagt, dass jetzt zwei dastehen (Doppeltipper am Ein-Klick-Knopf)");
+/* GEGENPROBE: Beim ERSTEN kommt diese Warnung nicht -- sonst waere sie
+ keine Warnung, sondern ein Begleittext. */
+const erstmal = await schick("schnell", `${API}/eintraege`, "POST",
+ { aufgabe: "schulung", datum: HEUTE, art: "talk" });
+const erstmalD = await erstmal.json();
+ok(!erstmalD.warnungen?.length,
+ "beim ersten Eintrag des Tages kommt keine Warnung");
+/* Und aufgeraeumt, damit die Zaehlungen danach stimmen. */
+for (const e of [...zweimalD.angelegt, ...erstmalD.angelegt]) {
+ await hol("schnell", `${API}/eintrag/${e.id}`, { method: "DELETE" });
+}
+
+/* ---- Drei Namen in einem Wurf ----------------------------------- */
+const drei = await schick("schnell", `${API}/eintraege`, "POST",
+ { aufgabe: "creator", datum: HEUTE, namen: ["@eins", "@zwei", "@drei"] });
+const dreiD = await drei.json();
+ok(drei.status === 201, `drei Namen auf einmal (${drei.status})`);
+ok(dreiD.angelegt.length === 3, `drei Eintraege entstanden (${dreiD.angelegt.length})`);
+ok(dreiD.stand.aufgaben.find((a) => a.schluessel === "creator").zahl === 3,
+ "…und der Zaehler steht auf 3");
+ok(dreiD.stand.aufgaben.find((a) => a.schluessel === "creator").stufe === "gruen",
+ "…womit das Ziel in EINEM Vorgang erreicht ist");
+
+/* ---- Teilweise Ablehnung: der wichtigste Fall -------------------- */
+const teils = await schick("schnell", `${API}/eintraege`, "POST",
+ { aufgabe: "creator", datum: HEUTE, namen: ["@eins", "@vier", "@fuenf"] });
+const teilsD = await teils.json();
+ok(teils.status === 201,
+ `die Liste geht durch, obwohl einer schon dasteht (${teils.status})`);
+ok(teilsD.angelegt.length === 2,
+ `die beiden neuen sind drin (${teilsD.angelegt.length})`);
+ok(teilsD.abgelehnt.length === 1,
+ `der doppelte ist einzeln abgelehnt (${teilsD.abgelehnt.length})`);
+ok(teilsD.abgelehnt[0]?.was === "@eins",
+ `…und es steht da, WELCHER (${teilsD.abgelehnt[0]?.was})`);
+ok(String(teilsD.abgelehnt[0]?.text || "").includes("@eins"),
+ "…im Klartext, nicht als Fehlerschluessel");
+
+/* GEGENPROBE: Geht gar nichts durch, ist es kein Erfolg. Ein 201
+ ueber eine leere Liste waere eine Zusage, die nicht stimmt. */
+const nichts = await schick("schnell", `${API}/eintraege`, "POST",
+ { aufgabe: "creator", datum: HEUTE, namen: ["@eins"] });
+ok(nichts.status === 409,
+ `eine Liste, von der nichts durchgeht, meldet KEINEN Erfolg (${nichts.status})`);
+
+/* ---- Die Grenze von zwanzig ------------------------------------- */
+const viele = Array.from({ length: 26 }, (_, i) => `@menge${i}`);
+const zuviel = await schick("schnell", `${API}/eintraege`, "POST",
+ { aufgabe: "creator", datum: HEUTE, namen: viele });
+const zuvielD = await zuviel.json();
+ok(zuvielD.angelegt.length === 20,
+ `aus 26 Namen werden 20 (${zuvielD.angelegt.length}) — ein versehentlich`
+ + " eingefuegter Textblock legt keine 300 Zeilen an");
+ok(!!zuvielD.zuviel, "…und es wird gesagt, dass gekuerzt wurde");
+
+/* ---- Ein ungueltiger Link in einer Liste ------------------------ */
+const linkListe = await schick("schnell", `${API}/eintraege`, "POST",
+ { aufgabe: "werbung", datum: HEUTE, art: "agentur",
+ links: ["https://www.tiktok.com/@x/video/1", "kaputt", "https://example.org/v/2"] });
+const linkListeD = await linkListe.json();
+ok(linkListeD.angelegt.length === 2,
+ `zwei gueltige Links durch (${linkListeD.angelegt.length})`);
+ok(linkListeD.abgelehnt.length === 1 && linkListeD.abgelehnt[0].fehler === "link_ungueltig",
+ "der kaputte einzeln abgelehnt, mit Grund");
+
+/* ---- Die Schranke gilt auch hier -------------------------------- */
+const creatorSchnell = await schick("creator", `${API}/eintraege`, "POST",
+ { aufgabe: "meeting", datum: HEUTE });
+ok(creatorSchnell.status === 404,
+ `ein Creator kommt auch ueber den Schnellweg nicht hinein (${creatorSchnell.status})`);
+
+/* ---- Und die Datumsgrenzen ebenfalls ---------------------------- */
+const schnellZukunft = await schick("schnell", `${API}/eintraege`, "POST",
+ { aufgabe: "meeting", datum: morgen });
+const schnellZukunftD = await schnellZukunft.json();
+ok(schnellZukunft.status === 409
+ && schnellZukunftD.abgelehnt?.[0]?.fehler === "datum_zukunft",
+ `ein Datum in der Zukunft wird auch hier abgelehnt (${schnellZukunft.status})`);
+
+/* ---- Die zuletzt gewaehlte Art kommt zurueck -------------------- */
+const standSchnell = await (await hol("schnell", `${API}/stand`)).json();
+const werbungZeile = standSchnell.aufgaben.find((a) => a.schluessel === "werbung");
+ok(werbungZeile?.letzteArt === "agentur",
+ `die zuletzt benutzte Art wird mitgeschickt (${werbungZeile?.letzteArt})`
+ + " — damit ist sie beim naechsten Mal schon gewaehlt");
+const schulungZeile = standSchnell.aufgaben.find((a) => a.schluessel === "schulung");
+ok(schulungZeile?.letzteArt === "schulung",
+ `ohne eigenen Eintrag gilt die erste Art als Vorgabe (${schulungZeile?.letzteArt})`);
+
+/* ---- Rueckgaengig: was der Schnellweg anlegt, nimmt er zurueck --- */
+const zumZuruecknehmen = ohneAngabeD.angelegt[0].id;
+const zurueck = await hol("schnell", `${API}/eintrag/${zumZuruecknehmen}`, { method: "DELETE" });
+const zurueckD = await zurueck.json().catch(() => ({}));
+ok(zurueck.status === 200, `Rueckgaengig geht (${zurueck.status})`);
+ok(zurueckD.stand?.aufgaben.find((a) => a.schluessel === "meeting")?.zahl === 0,
+ "…und der Zaehler steht wieder bei 0");
+
/* =====================================================================
13 · WER GEHT, NIMMT SEINE ZAHLEN NICHT MIT
=====================================================================
diff --git a/server/workspace-manager-ziele.js b/server/workspace-manager-ziele.js
index f9f2e83a..037011b2 100644
--- a/server/workspace-manager-ziele.js
+++ b/server/workspace-manager-ziele.js
@@ -317,6 +317,19 @@ export function standFuer(person, monat = monatJetzt(), heute = tagBerlin()) {
sehen; er soll nur nicht dafür gemahnt werden. */
const stufe = pflichtig ? stufeFuer(n, ziel, tag, letzter)
: (ziel > 0 && n >= ziel ? "gruen" : "neutral");
+ /* WAS DIESE PERSON ZULETZT GEWAEHLT HAT -- abgeleitet aus ihrem
+ letzten Eintrag, nicht in einer Einstellung gespeichert.
+
+ Zweck: Der Schnell-Eintrag kann die Art vorbelegen, und damit
+ faellt bei „Werbung" und „Schulung" je ein Handgriff weg. Eine
+ eigene Spalte „Lieblingsart" waere ein zweiter Bestand, der
+ pflegebeduerftig ist und veralten kann; der letzte Eintrag
+ veraltet nie, er ist der letzte Eintrag. */
+ const zuletzt = a.arten
+ ? db().prepare(`SELECT art FROM mz_eintrag
+ WHERE person_id = ? AND aufgabe = ? AND art IS NOT NULL
+ ORDER BY id DESC LIMIT 1`).get(person.id, a.schluessel)?.art
+ : null;
return {
schluessel: a.schluessel,
name: a.name,
@@ -324,6 +337,7 @@ export function standFuer(person, monat = monatJetzt(), heute = tagBerlin()) {
hinweis: a.hinweis,
feld: a.feld,
arten: a.arten || null,
+ letzteArt: zuletzt || a.arten?.[0]?.wert || null,
einheit: a.einheit,
zahl: n,
ziel,
@@ -613,6 +627,159 @@ managerZieleRouter.get(`${BASIS}/eintraege`, (req, res) => {
}
});
+/** Legt EINEN Eintrag an und gibt zurueck, was daraus wurde.
+ *
+ * HERAUSGEZOGEN (02.10.2026), weil es den Weg jetzt zweimal gibt:
+ * einzeln (Dialog) und als Liste (Schnell-Eintrag). Zwei Fassungen
+ * derselben Pruefungen waeren zwei Fassungen, von denen eine beim
+ * naechsten Nachschaerfen zurueckbleibt -- und gerade hier geht es
+ * um Dublettenschutz und Datumsgrenzen, also genau um das, was
+ * niemand doppelt pflegen sollte. */
+function eintragAnlegen(person, rumpf, monat, heute) {
+ const gepruefte = eintragPruefen(rumpf, monat, heute);
+ if (gepruefte.fehler) return { fehler: gepruefte.fehler };
+ const w = gepruefte.werte;
+
+ const doppel = dublette(person.id, monat, w);
+ if (doppel.eigen) {
+ return {
+ fehler: "schon_eingetragen",
+ text: w.name
+ ? `„${w.name}" steht diesen Monat schon in deiner Liste.`
+ : "Dieses Video steht diesen Monat schon in deiner Liste.",
+ };
+ }
+
+ /* Der Lead wird nur uebernommen, wenn er wirklich existiert und zu
+ dieser Person gehoert. Eine Nummer in der Adresse ist kein
+ Besitznachweis -- dieselbe Luecke, die am 03.09.2026 bei
+ `darfCreator` steckte. */
+ if (w.lead_id) {
+ const l = db().prepare("SELECT id, scout_id, erstellt_von FROM leads WHERE id = ?")
+ .get(w.lead_id);
+ const meiner = l && (l.scout_id === person.id || l.erstellt_von === person.id);
+ if (!l || (!meiner && !siehtAlles(person))) w.lead_id = null;
+ }
+
+ const info = db().prepare(
+ `INSERT INTO mz_eintrag
+ (person_id, von_name, von_rolle, aufgabe, monat, datum, name, name_klein,
+ link, link_klein, art, notiz, lead_id, doppelt_bei, erstellt)
+ VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`).run(
+ person.id, person.name || null, person.rolle || null,
+ w.aufgabe, monat, w.datum, w.name, w.name_klein,
+ w.link, w.link_klein, w.art, w.notiz, w.lead_id,
+ doppel.fremd?.person_id ?? null, jetzt());
+
+ /* ZWEIMAL AM SELBEN TAG -- ein Hinweis, keine Sperre (02.10.2026).
+
+ Seit es den Ein-Klick-Weg gibt, ist ein Doppeltipper die
+ wahrscheinlichste Fehleingabe: Der Knopf „Heute eingetragen"
+ braucht keine Angabe, also merkt man nichts davon. Zwei Meetings
+ an einem Tag sind aber moeglich -- es zu VERBIETEN waere falsch.
+
+ Also gesagt, was dasteht, und das Zuruecknehmen liegt ohnehin
+ daneben. Nur fuer die Aufgaben OHNE eigenes Erkennungsmerkmal;
+ Creator und Link haben ihren Dublettenschutz schon. */
+ let tageswarnung = null;
+ if (!w.name_klein && !w.link_klein) {
+ const schonHeute = db().prepare(
+ `SELECT COUNT(*) n FROM mz_eintrag
+ WHERE person_id = ? AND monat = ? AND aufgabe = ? AND datum = ?`)
+ .get(person.id, monat, w.aufgabe, w.datum)?.n ?? 0;
+ if (schonHeute > 1) {
+ tageswarnung = {
+ art: "zweimal_am_tag",
+ text: `Achtung: Für diesen Tag stehen jetzt ${schonHeute}. War das Absicht?`,
+ };
+ }
+ }
+
+ return {
+ id: Number(info.lastInsertRowid),
+ was: w.name || w.link || null,
+ warnung: doppel.fremd
+ ? { art: "auch_bei", name: doppel.fremd.name,
+ text: `Hinweis: ${doppel.fremd.name || "jemand anderes"} hat das diesen Monat auch eingetragen.` }
+ : tageswarnung,
+ };
+}
+
+/* =====================================================================
+ DER SCHNELL-EINTRAG (02.10.2026)
+ =====================================================================
+
+ Filipe: „will dass es viel einfacher ist. am besten so wenig wie
+ moeglich zu tippen. fertige sachen, bereit um ab zu gehen."
+
+ GEZAEHLT, WAS EIN EINTRAG VORHER KOSTETE: Knopf „+ Eintragen",
+ Dialog, Knopf „Speichern" -- zwei Klicks und ein Fenster fuer die
+ Aussage „ich war heute im Meeting". Bei vier Aufgaben im Monat
+ sind das acht Klicks und acht Fenster fuer etwas, das keine
+ einzige Angabe braucht.
+
+ DIESER WEG NIMMT EINE LISTE. Damit ist „drei Creator auf einmal"
+ ein Vorgang statt drei, und das ist der eigentliche Gewinn: Wer
+ drei Namen aus Discord kopiert, hat den Monat in einem Zug
+ erledigt.
+
+ JEDER EINTRAG WIRD EINZELN GEPRUEFT und einzeln beantwortet. Ein
+ Abbruch der ganzen Liste, weil ein Name schon dasteht, waere die
+ bequeme und die falsche Loesung: Dann weiss man nicht, welcher der
+ drei das Problem war, und tippt alles noch einmal.
+ ===================================================================== */
+managerZieleRouter.post(`${BASIS}/eintraege`, (req, res) => {
+ try {
+ const heute = tagBerlin();
+ const monat = monatJetzt();
+ monatSichern(monat);
+
+ const vorlage = req.body || {};
+ const liste = [];
+ const namen = Array.isArray(vorlage.namen) ? vorlage.namen : null;
+ const links = Array.isArray(vorlage.links) ? vorlage.links : null;
+
+ /* HOECHSTENS ZWANZIG AUF EINMAL. Nicht aus Angst vor Last --
+ zwanzig Zeilen sind nichts --, sondern weil ein versehentlich
+ eingefuegter Textblock sonst dreihundert Eintraege erzeugt,
+ die jemand einzeln wieder loeschen muss. */
+ const GRENZE = 20;
+ if (namen) {
+ for (const n of namen.slice(0, GRENZE)) liste.push({ ...vorlage, name: n });
+ } else if (links) {
+ for (const l of links.slice(0, GRENZE)) liste.push({ ...vorlage, link: l });
+ } else {
+ liste.push({ ...vorlage });
+ }
+ if (!liste.length) return res.status(400).json({ fehler: "nichts_angegeben" });
+
+ const angelegt = [], abgelehnt = [], warnungen = [];
+ for (const einzeln of liste) {
+ const e = eintragAnlegen(req.person, einzeln, monat, heute);
+ if (e.fehler) abgelehnt.push({ was: einzeln.name || einzeln.link || null, ...e });
+ else {
+ angelegt.push({ id: e.id, was: e.was });
+ if (e.warnung) warnungen.push(e.warnung);
+ }
+ }
+
+ /* 201 nur, wenn wirklich etwas entstanden ist. Ein 201 ueber eine
+ leere Liste waere eine Zusage, die nicht stimmt. */
+ res.status(angelegt.length ? 201 : 409).json({
+ angelegt, abgelehnt, warnungen,
+ zuviel: (namen || links || []).length > GRENZE
+ ? `Nur die ersten ${GRENZE} wurden übernommen.` : null,
+ stand: standFuer(req.person, monat),
+ });
+ } catch (fehler) {
+ if (String(fehler?.message || "").includes("monat_gesperrt")) {
+ return res.status(409).json({ fehler: "monat_gesperrt" });
+ }
+ console.error("[manager-ziele] Schnell-Eintrag:", fehler?.message);
+ res.status(503).json({ fehler: "nicht_gespeichert" });
+ }
+});
+
/** Einen Eintrag anlegen. */
managerZieleRouter.post(`${BASIS}/eintrag`, (req, res) => {
try {
@@ -620,56 +787,15 @@ managerZieleRouter.post(`${BASIS}/eintrag`, (req, res) => {
const monat = monatJetzt();
monatSichern(monat);
- const gepruefte = eintragPruefen(req.body, monat, heute);
- if (gepruefte.fehler) return res.status(400).json({ fehler: gepruefte.fehler });
- const w = gepruefte.werte;
-
- const doppel = dublette(req.person.id, monat, w);
- if (doppel.eigen) {
- return res.status(409).json({
- fehler: "schon_eingetragen",
- text: w.name
- ? `„${w.name}" steht diesen Monat schon in deiner Liste.`
- : "Dieses Video steht diesen Monat schon in deiner Liste.",
- });
+ const e = eintragAnlegen(req.person, req.body, monat, heute);
+ if (e.fehler) {
+ const code = e.fehler === "schon_eingetragen" ? 409 : 400;
+ return res.status(code).json({ fehler: e.fehler, text: e.text });
}
-
- /* Der Lead wird nur übernommen, wenn er wirklich existiert und zu
- dieser Person gehört. Eine Nummer in der Adresse ist kein
- Besitznachweis — dieselbe Lücke, die am 03.09.2026 bei
- `darfCreator` steckte. */
- if (w.lead_id) {
- const l = db().prepare("SELECT id, scout_id, erstellt_von FROM leads WHERE id = ?")
- .get(w.lead_id);
- const meiner = l && (l.scout_id === req.person.id || l.erstellt_von === req.person.id);
- if (!meiner && !siehtAlles(req.person)) w.lead_id = null;
- else if (!l) w.lead_id = null;
- }
-
- /* NAME UND ROLLE WERDEN MITGESCHRIEBEN, nicht nur verknuepft.
- Zwei Gruende, und beide stehen in der Vorlage: „Wer die Rolle
- verliert, sieht die Kachel nicht mehr; die Daten bleiben fuer
- den DogFather erhalten." Ohne diese zwei Spalten staende nach
- einem Rollenwechsel ein abgeschlossener Monat ploetzlich an den
- Zielzahlen der NEUEN Rolle -- und nach einer Loeschung ohne
- Namen da. */
- const info = db().prepare(
- `INSERT INTO mz_eintrag
- (person_id, von_name, von_rolle, aufgabe, monat, datum, name, name_klein,
- link, link_klein, art, notiz, lead_id, doppelt_bei, erstellt)
- VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`).run(
- req.person.id, req.person.name || null, req.person.rolle || null,
- w.aufgabe, monat, w.datum, w.name, w.name_klein,
- w.link, w.link_klein, w.art, w.notiz, w.lead_id,
- doppel.fremd?.person_id ?? null, jetzt());
-
res.status(201).json({
- id: Number(info.lastInsertRowid),
+ id: e.id,
stand: standFuer(req.person, monat),
- warnung: doppel.fremd
- ? { art: "auch_bei", name: doppel.fremd.name,
- text: `Hinweis: ${doppel.fremd.name || "jemand anderes"} hat das diesen Monat auch eingetragen.` }
- : null,
+ warnung: e.warnung,
});
} catch (fehler) {
if (String(fehler?.message || "").includes("monat_gesperrt")) {
diff --git a/workspace/anruf-probe.html b/workspace/anruf-probe.html
index 5666d22e..4a0d9f8f 100644
--- a/workspace/anruf-probe.html
+++ b/workspace/anruf-probe.html
@@ -16,8 +16,8 @@
verdeckt. Gefunden hat es pruef-struktur.
Richtig sind `gate.css` (die Farbwerte des Hauses) und `haus.css`. -->
-
-
+
+
-
+