80 lines
5.7 KiB
Markdown
80 lines
5.7 KiB
Markdown
# #011 M6 – Mitarbeiter, Abteilungen, Geräte
|
||
|
||
**Status:** erledigt
|
||
**Meilenstein:** v0.1.0
|
||
**Labels:** phase-1, backend, frontend
|
||
**Geschätzt:** 9 h **Tatsächlich:** 2 h 15 min
|
||
|
||
## Ziel
|
||
|
||
Die Stammdaten, an denen die Historie hängt. Mitarbeiter und Geräte werden nie gelöscht, damit archivierte Läufe für immer auf sie zeigen können.
|
||
|
||
## Aufgaben
|
||
|
||
- [x] Mitarbeiterliste: Suche über Anzeigename, Mail, UPN und `sam`, Filter nach Abteilung und Aktiv-Status
|
||
- [x] Mitarbeitersuche als wiederverwendbare Komponente: standardmäßig nur aktive, Umschalter „auch deaktivierte“ fürs Offboarding
|
||
- [x] Mitarbeiterseite: Stammdaten, Abteilung, Vorgesetzter, seine Geräte, alle Läufe chronologisch (Onboarding, Übergaben, Offboarding)
|
||
- [x] Mitarbeiter manuell anlegen und bearbeiten (`herkunft = MANUELL`); synchronisierte Felder sind bei `herkunft = ABGLEICH` gesperrt
|
||
- [x] Deaktivieren statt löschen (`aktiv = false`)
|
||
- [x] Abteilungen: Name (eindeutig je Kunde), Ziel-OU, Standardgruppen (`String[]`), Mail-Domain, Notizen
|
||
- [x] Geräte: Stammdaten nach Vorgabe, Pflichtregel „`hostname` oder `bezeichnung` muss gesetzt sein“, Besitzer optional
|
||
- [x] Geräteliste mit Suche über Hostname, Bezeichnung, Seriennummer und `tagCode`, Filter nach Typ
|
||
- [x] CSV-Import für Geräte (`hostname`, `label`, `serialNumber`, `inventoryNumber`, `type`, `model`), `tagCode` wird beim Import erzeugt; eigener `src/lib/csv.ts`
|
||
- [x] Archivieren statt Löschen, sobald Läufe an einem Gerät oder einer Abteilung hängen
|
||
- [x] Screenshots Mitarbeiterliste, Mitarbeiterseite, Geräteliste, Abteilungen in `docs/screenshots/m6/`
|
||
|
||
## Akzeptanzkriterien
|
||
|
||
- [x] Test: Mitarbeitersuche liefert standardmäßig nur `aktiv = true`; mit Umschalter auch deaktivierte
|
||
- [x] Test: ein Gerät ohne `hostname` und ohne `bezeichnung` wird abgelehnt
|
||
- [x] Test: ein Gerät mit Läufen lässt sich nicht löschen, nur archivieren
|
||
- [x] Test: `tagCode` eines bestehenden Geräts lässt sich nicht ändern
|
||
- [x] Test: CSV-Import erzeugt je Zeile einen `tagCode`, meldet fehlerhafte Zeilen einzeln und bricht nicht ab
|
||
- [x] Test: bei `herkunft = ABGLEICH` lehnt der Service Änderungen an synchronisierten Feldern ab
|
||
|
||
## Beim Bauen aufgefallen
|
||
|
||
- **Der CSV-Import hat Zeilen still verloren und Erfolg gemeldet.** Ein Anführungszeichen, das in
|
||
einer Zelle beginnt und nicht schließt, verschluckte alle Folgezeilen bis zum nächsten `"` — der
|
||
Import sagte „3 von 3 Zeilen angelegt“, zwei Geräte fehlten, und in einer Bezeichnung stand der
|
||
Text der verschluckten Zeilen. Umgekehrt starb der ganze Import an einer Datei, in der nirgends
|
||
mehr ein `"` folgte. Beides ist genau das Gegenteil des Akzeptanzkriteriums. Der Zerleger setzt
|
||
jetzt am Zeilenende neu auf: **ein Wert reicht standardmäßig nicht über die Zeile hinaus**. Das
|
||
Format erlaubt es, für Hostnamen und Seriennummern ist ein Umbruch aber praktisch immer ein
|
||
vergessenes Anführungszeichen — und mit dieser Grenze endet der Schaden an der Zeile, in der er
|
||
beginnt. Wer eine selbst erzeugte Datei zurückliest, schaltet `werteUeberZeilen` ein.
|
||
- **`ungepflegteAbteilungen` kannte die Kundengrenze nicht.** Gruppiert wurde nach dem Namen
|
||
allein, also galt „Vertrieb“ als gepflegt, sobald **irgendein** Kunde eine Abteilung dieses
|
||
Namens hatte. Beim Kunden ohne sie blieben `{{ou}}` und `{{gruppen}}` im Lauf leer, ohne dass die
|
||
Seite es je sagte. Die Funktion direkt darüber machte es seit jeher richtig — zwei Funktionen im
|
||
selben Modul, die sich widersprachen.
|
||
- **Die Besitzerauswahl bot Personen fremder Kunden an.** Die Liste wurde einmal für die ganze
|
||
Seite geholt und in jede Karte gegeben. Der Dienst wies die Wahl ab — aber erst nach dem Klick,
|
||
und die Oberfläche hatte etwas angeboten, das sie nicht einlösen konnte.
|
||
- **Ein Techniker konnte ein Gerät endgültig löschen.** Abteilungen und Mitarbeiter haben gar
|
||
keinen Löschweg, Geräte schon — und der Tag-Code ist danach für immer verbrannt. Löschen verlangt
|
||
jetzt `verwaltung`; die Ausnahme steht ausdrücklich in `CLAUDE.md`.
|
||
- **Derselbe Kunde wurde zweimal verschieden dargestellt.** Die Mitarbeiterliste blendete die
|
||
eigene Organisation immer aus, die Geräteliste zeigte sie immer an. Im Modus Dienstleistung hieß
|
||
„keine Kundenangabe“ damit nicht „kein Kunde“, sondern „ausgerechnet dieser eine“. Jetzt liefern
|
||
beide Lesemodelle den Namen, und **die Seite** entscheidet anhand des Betriebsmodus, ob sie ihn
|
||
zeigt.
|
||
- **Ein Kommentar hat wieder eine Zusage gemacht, die der Code nicht hielt** („zwei verschiedene
|
||
Gründe, aber nicht dieselbe Meldung“) — gerendert wurde nur eine, und wer die Rolle nicht hatte,
|
||
sah gesperrte Felder ohne jede Erklärung. Vierter Meilenstein in Folge mit genau dieser Sorte
|
||
Fehler.
|
||
- **`FELD` stand neunmal wortgleich in neun Dateien.** Jetzt einmal in `src/lib/formular.ts`.
|
||
- **Die E2E-Tests haben ihre Daten liegen gelassen.** Sie können nichts löschen — die Anwendung
|
||
kennt bewusst keinen Löschweg für Personen und Abteilungen —, also räumen sie am Ende über die
|
||
Datenbank auf. Ohne das sähe jede Datenbank, gegen die sie laufen, nach ein paar Durchläufen aus
|
||
wie ein Messie-Keller.
|
||
- **Die Mitarbeitersuche ist ein eigenes Bauteil** (`src/components/mitarbeiter-suche.tsx`), weil
|
||
#013 sie beim Starten eines Laufs und #014 beim Zuweisen eines Geräts wieder braucht.
|
||
|
||
## Offen geblieben
|
||
|
||
- **Ab 501 aktiven Personen** lässt sich ein Besitzer nicht mehr über das Auswahlfeld zuordnen. Die
|
||
Grenze schützt davor, ein Verzeichnis mit zehntausend Personen in ein `<select>` zu gießen; die
|
||
Zuweisung über die Geräteseite mit Suche kommt mit #014. Bis dahin sagt das Formular es
|
||
ausdrücklich, statt einen Weg zu versprechen, den es nicht gibt.
|