Neue Stammdaten-Tabelle nummernschema (Migration 0032): Präfix + Stellenzahl
einmal je Objekttyp festlegen statt bei jeder Objekt-Anlage manuell
einzutippen (Fehlerquelle - Tippfehler, uneinheitliche Schreibweise,
falsches Präfix für den Typ). GET /objekte/naechster-code akzeptiert jetzt
objekttyp_id als Alternative zu praefix; ohne konfiguriertes Schema bleibt
der bisherige manuelle Präfix-Weg nutzbar (Rückwärtskompatibilität).
Admin-UI: Nummernschema-Pflege direkt in der Objekttyp-Verwaltung
(ObjekttypSection). ObjektAnlegenFormular versucht zuerst das Schema des
gewählten Objekttyps, fällt bei 404 auf manuelles Präfix zurück.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Neue Stammdaten-Tabelle einsatzfunktion (Migration 0031), benutzer bekommt
optionales einsatzfunktion_id-Feld - analog zu einheit_id modelliert, eine
Person hat im Regelfall genau eine primäre Einsatzfunktion. Bewusst
getrennt von den technischen App-Rollen (RolleTyp), die nur steuern, was
jemand in MABEA tun darf, nicht welche Funktion er im Einsatz hat.
CRUD-Endpunkt /einsatzfunktionen nach Kategorie-Muster, Duplikat-Prüfung
vorab (Projekt-Konvention). Admin-UI: Auswahl + Inline-Neuanlage in der
Benutzerverwaltung (BenutzerSection).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Neue Tabellen hersteller/modell (Migration 0030), CRUD-Endpunkte
(/hersteller, /modelle) nach bestehendem Kategorie-Muster, inkl. 409 bei
Duplikaten (IntegrityError abgefangen wie in personal.py). geraet_instanz
bekommt optionales modell_id-Feld - verknüpft eine konkrete Geräteinstanz
(mit Seriennummer, IDENT-004) mit ihrem Hersteller/Modell.
Ergänzt Material.hersteller (bleibt unverändert als Freitext) um eine
normalisierte Variante gezielt für Geräte mit Seriennummer - keine
Datenmigration bestehender Freitext-Werte (Scope dieser Kachel).
Bewusst kein neues Admin-UI-Screen für die Pflege (Scope-Grenze der
Kachel) - Hersteller/Modell aktuell nur über die API verwaltbar.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Neues Feld ist_original (Boolean, Migration 0029) - explizit beim Upload
und beim Ersetzen (DOC-003) setzbar, kein Default in eine Richtung erraten.
Wird beim Ersetzen bewusst NICHT vom Vorgänger übernommen (neue Version
könnte ein nachgereichtes Original sein, das eine bisherige Kopie ersetzt,
oder umgekehrt). Frontend: Checkbox "Original (nicht nur Kopie/Scan)" im
Upload-Formular, "Original"-Badge in der Dokumentenliste.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Dokument-Versionierung: vorgaenger_id verkettet Versionen (Migration 0027),
POST /dokumente/{id}/ersetzen legt neue Version an statt zu überschreiben,
alte Fassung bleibt als Historie erhalten. GET /dokumente/{id}/versionen
liefert die Kette (neueste zuerst), respektiert DOC-005-Zugriffsrechte.
Liste zeigt nur die jeweils aktuelle Version. Löschen der aktuellen Version
gibt automatisch den Vorgänger als neue "aktuelle" frei. Frontend:
"Ersetzen"-Button + aufklappbare Versionshistorie im DokumentePanel.
QR-Code-Etiketten: bisher nur Code128-Barcode möglich. generiere_qr_label_pdf
(reportlab-eigenes QR-Widget, keine neue Abhängigkeit) als Alternative,
?format=qr|code128 an /objekte/{id}/label.pdf und /objektpositionen/{id}/
label.pdf. AktePage bekommt "Etikett (Barcode)"/"Etikett (QR)"-Buttons
(vorher gab es dafür noch gar keinen UI-Zugang im Web-Frontend).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Rechnungen sind finanziell sensibel - nur Materialverantwortliche/
Leitungsverantwortliche/Administration dürfen sie in der Liste sehen und
herunterladen (403 bei direktem Downloadversuch), Mitarbeiter nicht.
Hochladen bleibt für alle offen (z.B. Wareneingang direkt scannen). Andere
Dokumenttypen bleiben unverändert für jeden mit Aktenzugriff sichtbar - die
werden im Feldeinsatz gebraucht. EINGESCHRAENKTE_DOKUMENTTYPEN als zentrale
Stelle für künftige weitere Einschränkungen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Neues Pflichtfeld dokumenttyp (Enum: Prüfprotokoll/Wartungsbericht/
Bedienungsanleitung/Rechnung/Zulassungsdokument/Sonstiges, Migration 0026)
- macht Dokumente kategorisier- und filterbar statt nur per Freitext-
Beschreibung auffindbar zu sein. Backend: Pflichtfeld beim Upload, optionaler
Query-Filter bei GET /dokumente. Frontend: Auswahl-Dropdown beim Upload,
Typ-Badge + Filter-Dropdown in der Liste (DokumentePanel).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Duplikat-Erkennung: SHA-256-Hash je Dokument (Migration 0024), Upload wird
mit 409 abgelehnt, wenn dieselbe Datei bereits an derselben Entität liegt
(Vergleich bewusst pro Entität, nicht global - gleiche Datei an zwei
Objekten ist kein Duplikat). Frontend zeigt die Backend-Fehlermeldung
(Dateiname/Datum des bestehenden Dokuments) statt generischem Text.
Nebenbei gefunden+gefixt: "Ansehen" (PDF/JPG/PNG online statt Download)
funktionierte nicht zuverlässig, weil window.open() erst nach einem await
fetch() aufgerufen wurde - Popup-Blocker werten das nicht mehr als direkte
Nutzeraktion. Jetzt öffnet der Tab sofort synchron, die Blob-URL wird
nachgeladen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Neues Wartungskonzept: Wartungsplan+Position+Intervall (zeit/km/
Betriebsstunden, kombinierbar - fällig sobald ein Kriterium erreicht
ist), Wartungsauftrag mit intern/externer Werkstatt, Status offen/
in_arbeit/erledigt, Kosten, Historie, Dokument-Anhang. Ad-hoc-Aufträge
ohne Plan möglich (z.B. kleinere Reparatur). Admin-Tab "Wartungspläne".
Objektakte-Redesign (Epic 21, Nutzer-Feedback "wirkt nicht modern"):
CSS-Grid statt Karten-Stapel, einspaltig mobil, zweispaltig ab 900px;
Fahrzeug-Objekte zeigen Wartung/Fahrzeugdaten prominent, Geräte/
Rucksäcke die Beladung. Dabei gefundene Lücke: Beladung (Soll/Ist je
Position) fehlte in der Akte komplett, nur über eine laufende
Kontrolle sichtbar - jetzt eigene Karte.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Personenbezogene Ausgabe ergänzt die reine Standort-/Lagerplatz-Sicht
um "wer hat das Ding gerade": neues Modell ausgabe (Material +
optional geraet_instanz_id), POST /ausgaben, GET /ausgaben (Filter
status/empfaenger), POST /ausgaben/{id}/rueckgabe mit Schutz vor
Doppel-Rueckgabe. Frontend als Admin-Tab "Ausgabe/Rückgabe" +
Command-Palette-Eintrag.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Bisher wurde die Sollmenge einer Objektposition live aus der Vorlagenposition
gelesen (objekt.vorlage_id -> vorlagenposition.sollmenge), Bearbeiten der
Vorlage erzeugte deshalb zwingend eine neue Version - sonst hätte sich jede
Änderung rückwirkend auf alle Objekte ausgewirkt, die diese Vorlage nutzen.
- Neue Spalte objektposition.sollmenge_vorlage (Migration 0020): kopiert die
Sollmenge fest bei Anlage/Duplizieren statt live nachzuschlagen
- sollmenge_effektiv(position) liest jetzt nur noch override/sollmenge_vorlage,
kein DB-Lookup mehr nötig (sollmenge_map() entfernt)
- Neuer Endpoint PUT /vorlagen/{id}/positionen: ersetzt Positionen direkt,
ohne neue Version. Bestehende Objekte unbetroffen, nur neu angelegte nutzen
die aktualisierten Werte (Test: test_aktualisiere_positionen_wirkt_nicht_
rueckwirkend_auf_bestehendes_objekt)
- POST /vorlagen/{id}/neue-version bleibt zusätzlich bestehen (nicht entfernt)
- Frontend: "Neue Version speichern" -> "Positionen speichern" (PUT statt POST)
- 140 Tests grün
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Lagerort-Hierarchie (organisation/standort/gebaeude/raum/lager, self-referenzierend),
Lagerplätze mit eindeutigem Code, Bestand je Lagerplatz+Material, Ein-/Auslagerung
mit lückenlosem Materialbewegungsprotokoll (append-only, analog Lagerbewegung
auf Objekt-Ebene, hier aber Material-/Lagerplatz-fein).
- Migration 0019: 4 neue Tabellen (lagerort, lagerplatz, bestand, materialbewegung)
- Backend: CRUD Lagerort/Lagerplatz, Bestandsabfrage, Ein-/Auslagern-Endpunkte,
Negativbestand vorab abgefangen (kein DB-Constraint-Exception-Pfad, gleiches
Muster wie objekte.py-Fix vom 2026-09-05)
- Frontend: neuer Admin-Tab "Lager" (Lagerort/-platz-Verwaltung, Bestand+Buchung
je Lagerplatz)
- 2 neue Tests, 139 Tests weiterhin grün
WH-005 (Umlagerung) und WH-006 (Inventur) bewusst zurückgestellt - Kern zuerst,
laut Nutzer-Entscheidung.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Nutzer-Fund: funkkenner (z.B. OPTA-Nummer) fehlte in fahrzeugdetails komplett
- eigenes Feld, nicht dasselbe wie funkrufname (gesprochener Rufname vs.
formale Funk-Identifikationsnummer im Digitalfunknetz).
Migration 0018, Model/Schema/Frontend-Formular ergänzt. Auch im Kachel-
Backlog (arbeitskacheln/05_fleet.md, FLEET-002) nachgezogen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
Nicht mitgezogen seit Migration 0012: personal, mangel, fahrzeugdetails,
lagerbewegung, dokument, permission. Live-Testlauf von scripts/create_admin.py
brach mit NoReferencedTableError ab (Benutzer.einheit_id -> einheit war nicht
registriert, da das Skript nur app.models.auth statt des vollständigen
Registry-Imports nutzt). App selbst lief nur zufällig fehlerfrei, weil
api.py alle Endpunkt-Module importiert und die dortigen Modell-Importe die
Lücke im Betrieb kaschiert haben.
Fix: __init__.py vervollständigt + create_admin.py/eskalation_pruefen.py
(täglicher Timer!) bekommen zusätzlich ein explizites "import app.models" als
Schutz, unabhängig davon ob die Registry künftig wieder unvollständig wird.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
Additiv neben dem bestehenden 4-Rollen-System (mitarbeiter/material-
verantwortlicher/leitungsverantwortlicher/administration bleiben unverändert,
kein Breaking Change): Admin kann jetzt eigene Rollen (z.B. "Materialwart",
"Helfer" aus der Ursprungs-Anforderung) mit frei wählbaren Einzelrechten aus
einem Berechtigungs-Katalog anlegen und Benutzern zuweisen - auch Benutzern
ganz ohne feste RolleTyp-Zuordnung.
Neue Tabellen: berechtigung (Katalog), rolle (custom, admin-anlegbar),
rolle_berechtigung (M:N), benutzer_rolle_zuordnung (M:N, eigene Tabelle statt
Wiederverwendung des ENUM-basierten benutzer_rolle).
require_roles_or_permission() kombiniert beide Systeme: bestehende feste
Rollen ODER eine passende granulare Berechtigung. Auf die vom Nutzer genannten
Beispiel-Endpunkte angewendet: Material anlegen/bearbeiten, Lagerbewegung
durchführen, Mangel melden/lesen/bearbeiten, Prüfung durchführen - weitere
Endpunkte folgen bei Bedarf nach demselben Muster (require_permission()/
require_roles_or_permission() stehen jetzt als Bausteine bereit).
Neue Endpunkte: GET /berechtigungen, CRUD /rollen, PUT/DELETE
/rollen/{id}/berechtigungen/{id}, PUT/DELETE /benutzer/{id}/rollen/{id}.
Frontend: neuer Admin-Tab "Rollen & Rechte" (Rolle anlegen, Rechte togglen,
Benutzer zuweisen/entfernen).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
Objekt bleibt der Materialträger (Rucksack/Gerät/Fahrzeug, Karte 11) -
"Material verschieben" heißt: das Objekt bekommt einen neuen Standort, jede
Bewegung wird append-only protokolliert (Wer/Wann/Von/Nach/Grund, Nutzer-
Vorgabe Modul 5).
POST /objekte/{id}/lagerbewegungen setzt Standort + protokolliert in einem
Zug, GET /lagerbewegungen (Filter objekt_id/standort_id) für die Historie.
Rollen wie beim Mangel-Modul: Materialverantwortliche+Leitung+Admin dürfen
verschieben, alle Eingeloggten dürfen lesen.
Frontend: "Verschieben"-Aktion in ObjektSection, neuer Admin-Tab
"Lagerbewegungen" (reine Anzeige, analog HistorieSection).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
Neue Tabelle fahrzeugdetails (1:1 zu Objekt, additiv/optional): Kennzeichen,
Funkrufname, Baujahr, Kilometerstand, Betriebsstunden, nächste HU/UVV.
objekt_status um 'in_wartung' erweitert (Nutzer-Vorgabe: einsatzbereit/
eingeschränkt/nicht einsatzbereit/in Wartung/außer Dienst - die ersten drei
bleiben weiterhin rein rechnerisch über dashboard.einsatzbereitschaft(),
in_wartung/ausser_dienst sind die beiden manuell gesetzten Zustände).
dashboard.einsatzbereitschaft(): ausser_dienst-Objekte zählen nicht mehr mit,
in_wartung und überfällige HU/UVV sind neue Gründe für "nicht einsatzbereit".
dashboard.bevorstehende_prueftermine() (Kachel 2) zeigt jetzt Geräte- UND
Fahrzeugprüfungen (HU/UVV) in einer Liste (Nutzer-Vorgabe: ein Konzept für
"welche Prüfungen bald fällig sind") - Schema dafür verallgemeinert
(typ/bezeichnung/faelligkeitsdatum statt geräte-spezifischer Felder).
Frontend: Status-Dropdown + Fahrzeugdetails-Formular in ObjektSection
(nur für Objekttypen mit ist_zugfahrzeug=True), Dashboard-Kachel 2 zeigt
gemischte Liste.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
Eigener Workflow für Schäden/Defekte, getrennt von Fehlbestand (Mengen-
Abweichung Soll/Ist). Status: neu → in_bearbeitung → ersatzteil_bestellt/
reparatur_geplant → erledigt. entitaet_typ/entitaet_id optional (Objektposition
oder Geräteinstanz), analog zum bestehenden historie.entitaet_typ-Muster,
objekt_id immer gesetzt für Dashboard-Abfragen.
Ein offener Mangel mit prioritaet=hoch macht das Objekt "nicht einsatzbereit"
(neuer Grund mangel_kritisch_offen in dashboard.einsatzbereitschaft) -
niedrige/normale Mängel blockieren nicht.
Melden/Lesen: alle Mitarbeiter+. Status ändern: Materialverantwortliche+.
Neue Seite /maengel für alle eingeloggten Nutzer (Nav-Link "Mängel").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
Neues Modul für die BOS-Ressourcenplattform-Erweiterung: Einheit (Zug/Gruppe,
self-referenzierend), Qualifikationstyp (Führerschein/Lehrgang/Berechtigung),
BenutzerQualifikation (mit Gültigkeit) und ObjekttypQualifikationsanforderung
(M:N) - beantwortet "wer darf dieses Fahrzeug fahren?" über
GET /objekte/{id}/berechtigung/{benutzer_id}.
Benutzer und Objekt bekommen optionale einheit_id (additiv, analog
fahrzeug_id-Muster). Nebenbei Bugfix: PATCH /benutzer konnte einheit_id nicht
auf null setzen (Feld-vorhanden-Check via model_fields_set statt is not None).
Frontend: neuer Admin-Tab "Personal" (Einheiten, Qualifikationstypen,
Benutzer-Qualifikationszuordnung), Einheit-Auswahl im Benutzer-Formular.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
Objekttyp.ist_zugfahrzeug (Migration 0011) grenzt die Fahrzeug-Zuordnung
serverseitig auf tatsächliche Zugfahrzeuge ein (422 bei Verstoß), Frontend
filtert die Fahrzeug-Dropdowns entsprechend.
Neue Dashboard-Endpunkte /dashboard/einsatzbereitschaft und
/dashboard/prueftermine plus DashboardPage mit drei Kacheln
(Einsatzbereitschaft, Prüftermine, Ablaufdaten). Aktive
Mindermengen-Genehmigung zählt als "eingeschränkt einsatzbereit", nicht als
voll einsatzbereit (U2/Karte 08).
Neue FehlbestandListePage: Fehlbestände eigenständig nachfüllen/Mindermenge
genehmigen, ohne komplette Kontrolle des Objekts zu starten (Backend
existierte bereits, nur das Frontend fehlte).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
objektposition.seriennummer war ein Einzelfeld, konnte nur EIN Gerät pro
Materialtyp/Objekt abbilden (z.B. 2 Pulsoxymeter im selben Rucksack nicht
darstellbar). Neue Tabelle geraet_instanz erlaubt beliebig viele Exemplare
pro Position (Seriennummer, Prüfdatum, nächste Prüfung, Status). Zusätzlich
objektposition.pruefintervall_monate (individuell überschreibbar).
Bewusst additiv: seriennummer-Spalte bleibt vorerst stehen (Service/
Endpunkte/Frontend hängen noch daran), bestehende Werte werden per
INSERT...SELECT nach geraet_instanz kopiert. Drop + Umbau der abhängigen
Schichten folgt in Umsetzungsschritt 2 (siehe arbeitskarten/14_...).
Migration von postgres-expert gegengeprüft (ON CONFLICT DO NOTHING als
Sicherheitsnetz, DROP TYPE IF EXISTS ergänzt), Tests decken Mehrfach-
Instanzen, UNIQUE-Constraint je Position, gleiche SN an verschiedenen
Positionen und pruefintervall_monate ab.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Zwei Stufen (Erinnerung an Materialverantwortliche, Eskalation an
Leitungsverantwortliche/Administration) mit Tracking-Zeitstempel je Stufe
gegen Mehrfachversand. Zeitschwellen in eskalation_konfiguration (DB,
Singleton), admin-editierbar über GET/PUT /eskalation/konfiguration - bewusst
nicht fix im Code. Prüflauf per POST /eskalation/pruefen angestoßen,
Scheduling (Cron/systemd-Timer) bleibt Betriebsaufgabe.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
objektposition.code (nullable, unique) für Geräte-Etiketten, separat von
seriennummer. Lookup-Endpunkt für Geräte-Scan (GET /objektpositionen/code/{code})
sowie Auto-Vorschlag PRAEFIX-NNN für Objekt- und Objektposition-Codes
(GET /objekte/naechster-code, GET /objektpositionen/naechster-code).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Nutzer-Vorgabe: Rucksack-Fächer sollen aus einer festen, je Objekttyp
gepflegten Liste gewählt werden können statt jedes Mal Freitext einzutippen.
Neue Tabelle fach (Migration 0004), CRUD-Endpoints /faecher, Admin-Tab
Struktur bekommt Fach-Verwaltung, Vorlagen-Editor nutzt Dropdown mit
"Sonstiges"-Freitext-Fallback.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L85hmKbvX7Cqkq47KnQhFt
- benutzer.email ergänzt (additive Migration 0003) - Prompt 20 hatte kein E-Mail-
Feld vorgesehen, aber Karte 05 braucht es für Benachrichtigungen
- Dashboard-Kennzahlen (Prompt 12.2): offen/in_bearbeitung/nachgefuellt_teilweise
zählen gemeinsam als "offen", genehmigte Mindermengen separat, kürzlich erledigt
(7 Tage), problematische Objekte
- Ablaufdaten-Warnungen (Prompt 14): serverseitige Statusberechnung
gueltig/bald_ablaufend/abgelaufen je Objektposition, Standard-Warnzeitraum 30
Tage falls am Material nicht gesetzt, sortiert nach verbleibenden Tagen (E1:
einzige Zeit-/Fälligkeits-Sicht in V1, keine Kontrollintervall-Logik)
- GET /fehlbestaende um Filter (Standort/Objekt/Material/genehmigt) und
Alter-Sortierung erweitert (Prompt 12.3)
- Asynchrone E-Mail-Benachrichtigung bei neuem Fehlbestand (Karte 05): fire-and-
forget an aktive Materialverantwortliche/Leitungsverantwortliche mit hinterlegter
E-Mail; kein SMTP konfiguriert -> wird nur geloggt, kein harter Fehler
- Tests: Aggregationsregel, Ablaufdaten-Filterung/Sortierung, Rollenrechte,
E-Mail-Versand (SMTP gemockt, kein Docker/Test-Mailserver im Host-Runner
verfügbar - Aufruf mit korrekten Empfängern/Betreff wird geprüft)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L85hmKbvX7Cqkq47KnQhFt
- MindermengenGenehmigung-Modell/Service (Prompt 04): reine fachliche Bewertung,
ändert NIEMALS Ist-Menge/Fehlmenge/Fehlbestand-Status (Leitplanke, U2)
- Automatischer Ablauf bei Start der nächsten Kontrolle desselben Objekts (Karte 08,
U6) - kein Zeit-Trigger, der Kontrollstart selbst ist der Ablauf-Zeitpunkt
- Genehmigung endet gemeinsam mit dem Fehlbestand, wenn dieser durch Nachfüllung
erledigt wird (U7), unabhängig davon ob vorher eine neue Kontrolle stattfand
- Historie-Service (app/services/historie.py) + Verdrahtung in die komplette
Kernkette: kontrolle_gestartet/-abgeschlossen/-abgebrochen/-uebernommen,
istmenge_erfasst, fehlbestand_entstanden/-erledigt, nachfuellung_erfasst,
mindermenge_genehmigt/-abgelaufen/-beendet_durch_erledigung (U14)
Hinweis: Stammdaten-/Vorlagen-/Benutzerverwaltung noch nicht retrofittet -
Sprint 5 deckt bewusst die im Testkonzept referenzierte Kernkette ab, keine
flächendeckende Audit-Abdeckung aller CRUD-Endpunkte.
- Tests: U2, U6, U7, vollständige Historie-Kette nach Prompt-13.3-Beispiel (U14)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L85hmKbvX7Cqkq47KnQhFt
- Nachfuellung-Modell: einzige Aktion, die Objektposition.istmenge tatsächlich
ändert (Vier-Kernbegriffe). Deckt Sofort-Nachfüllung während Kontrolle (Karte 07)
und spätere/externe Nachfüllung (Prompt 02.5) mit derselben Logik ab
- Wichtiger Fix vor dem ersten Testlauf: Objektposition.istmenge wird bei Nachfüllung
auf den neuen Wert GESETZT (fehlbestand.istmenge + menge), nicht unabhängig
inkrementiert - sonst bliebe sie beim initialen "nicht kontrolliert"-Stand (0)
stehen, obwohl die Kontrolle bereits einen realen Zählwert kennt
- Statusübergänge: offen -> nachgefuellt_teilweise (Fehlmenge>0) -> erledigt
(Fehlmenge=0), ausschließlich automatisch bei Ist=Soll (U3)
- Überbestand durch Nachfüllung erlaubt, als Info gekennzeichnet, kein Fehler (E4)
- KontrollpositionRead liefert jetzt fehlbestand_id mit, damit die UI direkt eine
Sofort-Nachfüllung anbieten kann, ohne separat nachzufragen
- Tests: U3, U4 (Sofort-Nachfüllung ohne Zwischenstatus), U5 (Teilnachfüllung),
Überbestand-Fall, Nachfüllung auf bereits erledigten Fehlbestand -> 409
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L85hmKbvX7Cqkq47KnQhFt
- Kontrolle/Kontrollposition-Modelle: Kontrolle ändert NIEMALS Objektposition.istmenge
(Vier-Kernbegriffe-Tabelle, Prompt 02.9) - reine Erfassung/Snapshot
- Objekt-Sperre (Prompt 02.8): Kontrolle.status='in_bearbeitung' + benutzer_id IST die
Sperre, kein separates Sperr-Modell. Zweiter Zugriff -> 409 mit wer/seit; Übernahme
bricht alte Kontrolle ab (Datensatz bleibt, Prompt 16.6-Analogie) und protokolliert
das Ereignis in einer neuen, minimalen Historie-Tabelle (volle Ausbaustufe Sprint 5)
- Automatische Fehlbestand-Erzeugung bei Ist < Soll (U1), Überbestand erzeugt
ausdrücklich KEINEN Fehlbestand (Prompt 02.4); Korrektur vor Abschluss möglich
(von derselben Kontrolle erzeugter Fehlbestand ist bis zum Abschluss nicht final)
- Abschluss verweigert bei unbestätigten Positionen, liefert deren IDs (U11)
- Abbruch verwirft Kontrollpositionen UND von dieser Kontrolle erzeugte Fehlbestände,
Kontrolle selbst bleibt als Datensatz mit Status "abgebrochen" erhalten (U12)
- Tests: U1, U11, U12, Sperre/409, Übernahme, Fremdzugriff/403, parallele Objekte
Mitarbeiter-UI (PWA-Frontend) ist bewusst noch nicht Teil dieses Commits - eigenes
Techstack-Setup, wird als nächster Schritt separat angegangen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L85hmKbvX7Cqkq47KnQhFt
- Beladungsvorlage/Vorlagenposition-Modelle + Endpunkte: erstellen, neue Version
(Prompt 08 Punkt 4 - alte Version bleibt unverändert lesbar statt überschrieben)
- Objekt-Anlage aus Vorlage: Objektpositionen werden initialisiert, Ist-Menge startet
bei 0 ("nicht kontrolliert", Prompt 09)
- Duplizieren: Struktur/Sollmengen-Override kopiert, Ist-Menge/SN/Ablauf/Charge NICHT,
neuer Code Pflicht (409 bei Kollision)
- Sollmengen-Auflösung (Prompt 10): Override schlägt Vorlagenwert, sonst dynamisch aus
aktueller Vorlagenposition des Objekts
- GET /objekte/code/{code} bewusst vor /objekte/{id} registriert (Routing-Reihenfolge)
- Bugfix: app.models muss vollständig importiert werden (main.py + conftest.py), sonst
schlägt FK-Auflösung zwischen Modellen fehl, die noch von keinem Endpoint berührt
werden (Fund im dritten echten CI-Lauf)
- Tests: U8 (Override schlägt Vorlage), U9 (Duplizieren-Kopierregeln + Code-Konflikt),
U10 (neue Version, alte bleibt lesbar), Rollenrechte
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L85hmKbvX7Cqkq47KnQhFt
1. passlib 1.7.4 liest bcrypt.__about__.__version__, das bcrypt>=4.1 entfernt hat -
führte zu irreführendem "password cannot be longer than 72 bytes". Fix: bcrypt<4.1 pinnen.
2. Globaler async engine in conftest.py + pytest-asyncios default function-scoped Event-Loop
führte zu "Task ... attached to a different loop" bei asyncpg. Fix: session-weiter
Loop-Scope für Fixtures und Tests.
Erster echter CI-Lauf (Host-Runner ohne Docker) kam bis zum pytest-Schritt durch,
beide Fehler dort aufgedeckt.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L85hmKbvX7Cqkq47KnQhFt
Gitea-Actions-Runner läuft im Host-Modus (kein Docker installiert, Nutzerentscheidung
Prompt 19). Service-Container für PostgreSQL funktionieren damit nicht - CI nutzt
stattdessen die auf dem Runner-Host ohnehin laufende System-PostgreSQL und legt sich
eine eigene Test-Datenbank an/räumt sie danach wieder weg.
Hinweis: Runner läuft auf demselben Host wie die Produktionsinstanz (192.168.1.238) -
keine Isolation zwischen CI und Prod, akzeptabler Kompromiss für V1/Einzelserver-Setup.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L85hmKbvX7Cqkq47KnQhFt