Annehmen war bisher nur ein Etikett: Man stellte den Status um, und es
passierte nichts. Kunde, Projekt, Aufgabenliste, Termin und Zugang musste
man danach von Hand in fünf Schritten nachbauen. Der alte Code gab das im
Kommentar selbst zu -- schlug der zweite von zwei Aufrufen fehl, stand der
Kunde schon in der Datenbank und man durfte es nicht noch einmal
versuchen.
Jetzt macht das EIN Aufruf, ganz oder gar nicht:
Kunde finden oder anlegen, Projekt anlegen, die komplette Aufgabenliste
des Pakets einsetzen, Liefertermin berechnen, Einladungslink erzeugen.
Der Kunde verfolgt ab diesem Moment alles in seinem Portal.
Die Zeit läuft wirklich:
- Eigenes Datumsfeld (termin_am) neben dem freien Text. Aus 'Mitte
Oktober' kann man keine verbleibenden Tage rechnen.
- Werktage statt Kalendertage, inklusive luxemburgischer Feiertage. Die
beweglichen werden über die Osterformel berechnet statt gepflegt --
eine Liste ist im übernächsten Jahr lautlos falsch.
- Vorschlag je Paket (Onepager 10, Website 20, Shop 30 Werktage),
überschreibbar vor dem Bestätigen.
- Verbleibende Zeit wird SERVERSEITIG gerechnet. Der Browser kennt die
Feiertage nicht; zwei verschiedene Zahlen für denselben Termin wären
schlimmer als gar keine.
Ablehnen mit Grund und vorbereiteter Absage in fünf Sprachen, Text
serverseitig erzeugt und vor dem Abschicken lesbar. Ein laufendes
Projekt lässt sich nicht nachträglich als Anfrage ablehnen.
Sechs Fehler dabei gefunden:
- Ein unbekanntes Paket hätte GAR KEINEN Termin bekommen statt des
Ersatzwerts. Zwei Funktionen lasen dieselbe Tabelle, nur eine hatte
einen Rückfallwert. Eine fehlende Zahl fällt nirgends auf.
- wd_anfragen hat keine Spalte 'firma' -- better-sqlite3 weist undefined
ab, die ganze Annahme wäre gescheitert.
- Migrationen stehen in einer ausdrücklichen Liste; 0015 fehlte darin.
- datumKurz() wurde aufgerufen, gab es aber nicht. Der Fehler wäre erst
NACH dem Anlegen aufgetreten.
- Der Installations-Hinweis lag fest über dem Annehmen-Knopf und machte
ihn auf dem Handy untreffbar. Die Seite reserviert jetzt Platz dafür.
- 'richttermin' ist freier Text, lief aber durch einen Datumsformatierer:
Der Kunde las 'Invalid Date' an der wichtigsten Stelle seines Projekts.
Geprüft: 49 Prüfungen der Terminrechnung gegen nachschlagbare Osterdaten
und Wochentage, 70 gegen eine echte Datenbank (darunter: zweiter Klick
legt nichts doppelt an, Abbruch mittendrin lässt NICHTS zurück, gesperrter
Kunde wird nicht still entsperrt), 42 im Browser auf Computer und Handy,
40 im Portal. Alle bestehenden Prüfungen weiter grün.
Co-Authored-By: Claude Opus 5 <[email protected]>
Masterplan S.12. Anmeldung, Uebersicht, Projektdetail, Aenderungswuensche,
Zusatzangebote annehmen/ablehnen, Nachrichten, Profil.
DIE ZENTRALE ENTSCHEIDUNG: Die Trennung zwischen Kunden sitzt NICHT in der
Oberflaeche, sondern in jeder einzelnen Abfrage. Ueberall steht
"WHERE id = ? AND kunde_id = ?" statt "WHERE id = ?", wobei die kunde_id
IMMER aus der Sitzung kommt, nie aus der Anfrage. Wer eine fremde
Projektkennung errät, bekommt dadurch "nicht gefunden" statt Daten. Die
Oberflaeche liegt im Browser des Kunden und ist beliebig manipulierbar --
sie kann diese Aufgabe grundsaetzlich nicht uebernehmen.
Weitere bewusste Entscheidungen:
* KEINE Selbstregistrierung. Masterplan S.12 verlangt, dass Projektkauf
oder Betreuung VOR der Freischaltung geprueft werden. Ein Portal, in das
sich jeder selbst eintraegt, waere das Gegenteil. Kunden werden in der
Verwaltung angelegt und bekommen einen Einladungslink.
* Passwoerter mit scrypt (in Node eingebaut, kein Zusatzpaket). Ein
SHA-256 ueber ein Passwort ist milliardenfach pro Sekunde durchprobierbar;
scrypt ist absichtlich langsam UND speicherhungrig.
* Mindestanforderung ist LAENGE, nicht Zeichenklassen. "Hund1234!" erfuellt
jede Klassenregel und ist trotzdem schlecht; "mein blauer stuhl steht
krumm" erfuellt keine und ist ausgezeichnet.
* Gleiche Fehlermeldung bei unbekannter Adresse und falschem Passwort --
sonst lassen sich Kundenadressen durchprobieren.
* Die Anmeldesperre haengt an der E-Mail, nicht an der IP. Eine IP-Sperre
wuerde mehrere Kunden hinter demselben Firmenanschluss gemeinsam
aussperren -- genau der Fehler, der im Universe am 19.08.2026 auftrat.
* Sperre und Passwortwechsel beenden laufende Sitzungen SOFORT, nicht erst
nach zwoelf Stunden.
* Ein Zusatzangebot laesst sich nur annehmen, wenn es beziffert ist --
sonst entstuende eine Zahlungspflicht ohne Preis.
Dazu der wichtigste Test des Bereichs (test-webdesign-portal.mjs): zwei
echte Kunden, und Kunde B versucht systematisch mit Kunde As echten
Kennungen an dessen Projekt, Nachrichten, Dateien und Zusatzangebote zu
kommen. Prueft ausserdem, dass interne Notizen und interne Arbeitsdateien
auch dem BERECHTIGTEN Kunden verborgen bleiben.
Co-Authored-By: Claude Opus 5 <[email protected]>