Files
aufmass-web/_aufmass_web/ANFORDERUNGEN.md
T

16 KiB
Raw Blame History

Anforderungen AufmaßWeb v2.35

Projektübersicht

Flask-basierte Webanwendung zur Verwaltung von Aufmaßen, Projekten und Positionen.


1. Projektübersicht (/projekt/)

1.1 Sortierung

  • Projekte werden nach Bezeichnung A-Z aufsteigend sortiert angezeigt
  • NULL-Werte werden als leerer String behandelt (sortieren ans Ende)

1.2 Projektsuche

  • Suchleiste oben auf der Seite
  • Clientseitige Filterung nach Projektname (Bezeichnung / SM-Nr)
  • Echtzeit-Filterung bei Eingabe, Clear-Button vorhanden

1.3 Baumansicht

  • Jedes Projekt als aufklappbarer <details>-Block
  • Zeigt: Bezeichnung, Summe (€), Anzahl Aufmaße, Anzahl Positionen, Status
  • Doppelklick auf Projekt-Header navigiert zur Listenansicht (/projekt/<id>)

1.4 Aufmaß-Suche

  • Suchleiste filtert Aufmaß-Namen innerhalb der Baumansicht

1.5 Inline-Bearbeitung Aufmaß-Name

  • ✏️-Button pro Aufmaß → Inline-Input für Name
  • Speichern: Blur oder Enter
  • Abbrechen: Escape
  • Kein OK/Abbrechen-Button, kein setTimeout

1.6 Inline-Bearbeitung Projekt-Name

  • ✏️-Button im Projekt-Header → Inline-Input für Name
  • Speichern: Blur oder Enter
  • Abbrechen: Escape
  • POST an /projekt/<id>/update-name

1.7 Aufmaß löschen

  • ✕-Button pro Aufmaß, keine Bestätigung (ohne confirm())
  • Redirect per request.referrer: in Baumansicht → bleibt in index, in Detailansicht → bleibt in list

1.8 Aufmaß duplizieren

  • Duplizieren-Button (📋) pro Aufmaß, keine Bestätigung
  • Erstellt Kopie mit allen Positionen, hängt (Kopie) an den Namen
  • Bei Namenskonflikt: automatisch (1), (2) etc.
  • Redirect per request.referrer (wie Löschen)

1.9 Baum-Status persistent

  • <details> open/close-Zustand in localStorage
  • Schlüssel: tree_open_{projectId}
  • Überlebt Seitenneuladungen nach Aktionen

2. Neues Aufmaß erstellen

2.1 Button

  • "+ Neues Aufmaß"-Button unter jedem Projekt in der Baumansicht
  • Klick klappt ein vollständiges Formular auf (slide-down)

2.2 Formular-Felder (Reihenfolge)

  1. Vertrag (Dropdown, optional)
  2. LV-Name (Text, optional)
  3. Typ (Dropdown, optional)
  4. Bezeichnung (Baustelle) (Text, leer)
  5. Bauabschnitt (Text, pre-filled aus Projekt)
  6. SM-Nr (Text, pre-filled aus Projekt)
  7. Abruf-Nr (Text, pre-filled aus Projekt)
  8. Kopfdaten EV holen (Button, bedingt sichtbar siehe Abschnitt 11)
  9. Ansprechpartner (Trennlinie)
    • Vorname
    • Name
    • Tel
    • Email
  10. Startdatum (Date)
  11. Endedatum (Date)
  12. Aufmaß-Name (readonly, automatisch generiert)

2.3 Automatische Aufmaß-Namensgenerierung

  • Zusammensetzung: Bezeichnung + " - " + Bauabschnitt + " - " + SM-Nr + " - " + Abruf-Nr
  • Nur nicht-leere Felder werden berücksichtigt
  • Wird live bei Eingabe aktualisiert (readonly-Feld)
  • Nur OS/Nextcloud-sichere Zeichen: < > : " / \ | ? * & # % { } ~ [ ] werden entfernt, Mehrfachleerzeichen → eines

2.4 Speichern

  • Aktualisiert Projekt-Metadaten (SM-Nr, Vertrag, LV-Name, Abruf-Nr, Daten, Ansprechpartner)
  • project.bezeichnung wird NICHT überschrieben
  • Erstellt neues Aufmaß mit generiertem Namen und gewähltem Typ
  • Redirect zur Projektübersicht

3. Neues Projekt erstellen (/projekt/neu)

3.1 Formular (vereinfacht)

  • Projektname / Bezeichnung (Pflichtfeld)
  • Vertrag (Dropdown, optional)
  • LV-Name (Dropdown, optional, abhängig von Vertrag)
  • Kein Standard-Aufmaß wird automatisch erstellt

4. Projekt-Listenansicht (/projekt/<id>)

4.1 Aufmaß-Liste

  • Alle Aufmaße eines Projekts als Tabelle
  • Inline-Editing für Name, Typ, Status
  • Sofortige visuelle Aktualisierung vor async-fetch
  • Kein resp.status-Check (Server-Response enthält Aufmaß-Status, nicht 'ok')

4.2 Projekt-Name inline bearbeiten

  • ✏️-Button in der Projektkopfzeile
  • Gleiche Funktionalität wie in Baumansicht (POST /projekt/<id>/update-name)

4.3 Aufmaß erstellen (in Listenansicht)

  • Formular mit Name + Typ + Sortierung

4.4 TXT-Import

  • 📥 Import-Button → Lädt TXT-Datei
  • Parst [Kopfdaten] + [Aufmaßdaten]
  • Erstellt Aufmaß mit Positionen
  • Bei doppeltem Namen: automatisch (1), (2) etc. anfügen

5. Aufmaß-Editor (/projekt/<id>/<aufmass_id>)

5.1 Positionstabelle

  • Inline-Editing für alle Positionsfelder
  • Speichern: Blur oder Enter
  • Sofortige visuelle Aktualisierung vor async-fetch
  • Kein resp.status-Check (Server-Response enthält Aufmaß-Status, nicht 'ok')

5.2 Zahlenformat

  • Alle Zahleneingaben: type="text" inputmode="decimal"
  • Deutsches Format: Komma = Dezimaltrenner, Punkt = Tausendertrenner
  • germanNum(): Zahl → deutsche Anzeige (z.B. 1.234,56)
  • parseNum(): Deutsche Eingabe → Zahl (z.B. 1.234,561234.56)
  • sanitizeNum(): Entfernt ungültige Zeichen live bei Eingabe
  • validateNumField(): Rote Border bei ungültigem Format
  • Validierung: Erlaubt sind 123, 1.234, 1,5, 1.234,56, -1,5
    • Ungültig: Buchstaben, 1,2,3, 1.2.3, etc.

5.3 ST/LE/STD/h/Psch Menge-Verhalten

  • Faktor-Feld ist immer 1,0 (kein Highlight)
  • Bei ST/LE/STD/h/Psch wird Menge hervorgehoben (amber #fff3cd, fett) und ist leer die Menge ist hier das relevante Feld, muss aber vom Benutzer über die Berechnung (Faktor-Eingabe) bestimmt werden
  • Bei M/M2/M3 werden die Dimensionsfelder (Länge/Breite/Tiefe) hervorgehoben
  • Menge ist readonly und wird automatisch berechnet: ST/LE/STD/h/Psch → Faktor × 1, M → Länge, M2 → L×B, M3 → L×B×T

5.4 Formel-Auswahl

  • Standard: Länge × Breite × Tiefe (abhängig von Einheit)
  • Frei: Eigenes Formelfeld
  • Typabhängige Markierung der Pflichtfelder (Länge, Breite, Tiefe) mit amber

5.5 Einfüge-Position ("Am Ende anfügen")

  • Checkbox "Am Ende anfügen" (default: checked)
  • checked: Neue Positionen werden am Ende der Tabelle angefügt
  • unchecked: Neue Positionen werden unterhalb der zuletzt markierten Position eingefügt
  • Klick auf eine Position in der rechten Tabelle setzt die Einfügemarke, ohne die LV-Auswahl zu beeinflussen

5.6 LV-Tabelle (linke Seite)

  • Spalten: Pos-Nr, Kurztext, EP (deutsch formatiert via |german_number), Einheit
  • Einfachklick: LV-Zeile markieren, Formular füllen
  • Multi-Select: Strg+Klick markiert mehrere Zeilen
  • Drag & Drop: Ziehen einer LV-Zeile überträgt alle markierten IDs (nicht nur die gezogene)
  • Standard-Menge bei Drag: Nach LV-Drag bleibt menge=0 (keine automatische berechne_menge())
  • fillForm() setzt currentLVRow / currentLVId pro Zeile
  • Klick auf Positionstabelle löscht NICHT currentLVRow oder die LV-Auswahl

5.7 Hinzufügen-Button

  • "Hinzufügen" und "Auswahl hinzufügen": Beide verwenden selectedSet (alle markierten LV-Zeilen) + Formularwerte (Overrides)
  • Bei Mehrfachauswahl (>1 LV-Zeile) werden Text-Overrides (pos_nr, kurztext, abschnitt, bemerkung) nicht gesendet jede Position behält ihre LV-eigenen Textwerte
  • Dimensions-Overrides (Faktor, Länge, Breite, Tiefe, Formel) werden immer für alle markierten Positionen verwendet
  • Beide nutzen getInsertIdxFromCheckbox() für die Einfügeposition

5.8 Positionen importieren (TXT)

  • 📥 Button "Import TXT" im Tabellen-Footer
  • Lädt nur [Aufmaßdaten] aus einer TXT-Datei
  • Fügt Positionen in das aktuelle Aufmaß ein
  • Bestehende Kopfdaten bleiben unverändert

5.9 Kontext-Menü (Rechtsklick)

  • Rechtsklick auf eine Position in der Tabelle öffnet ein Kontext-Menü
  • Markierte löschen (✕) löscht alle markierten Positionen (wie Button unten)
  • Markierte kopieren (📋) kopiert alle markierten Positionen (wie Button unten)
  • Leere Zeile einfügen (📄) fügt eine leere Zeile unterhalb der angeklickten Position ein
  • Beim Rechtsklick ohne Ctrl/Shift wird die angeklickte Zeile zuerst selektiert (Single-Select)
  • Rechtsklick auf Trenner/Leerzeilen ist ebenfalls möglich (setzt Einfügepunkt)
  • Menü schließt bei Klick außerhalb oder Escape

5.10 Spalten-Filter (Excel-ähnlich)

  • Jede Spaltenüberschrift (außer Z-Art und ✕) hat einen ▾-Filterbutton
  • Klick öffnet ein Dropdown-Menü mit:
    • ↑ Aufsteigend / ↓ Absteigend sortieren (Sortierung aufheben mit ✕)
    • Suchen… (Livesuche filtert Werte-Liste + Tabelle)
    • Checkbox-Liste aller vorkommenden Werte mit Alles/Nichts
  • Mehrere Spalten können gleichzeitig gefiltert werden (Und-Verknüpfung)
  • Aktive Filter werden durch blaue ▾-Buttons angezeigt
  • ↺ Filter zurücksetzen-Button lädt die Tabelle neu und entfernt alle Filter
  • Nur Datenzeilen (mit data-id) werden gefiltert Trenner bleiben sichtbar
  • Sortieren + Filtern sind kombinierbar und wirken nur clientseitig (Ansichtsfilter)
  • Einfügen, Löschen und Bearbeiten sind auch im gefilterten Zustand möglich

5.11 Export (Excel, PDF, TXT) mit Ansichtsfilter

  • Buttons: 📊 Excel, 📄 PDF, 📄 TXT in der Kopfzeile
  • Checkbox "Aktuelle Ansicht" wenn aktiv, werden nur sichtbare (gefilterte) Positionen exportiert
  • TXT-Export erzeugt UTF-8-Datei im Format [Kopfdaten] + [Aufmaßdaten] (pipe-separiert)
  • Export-Route akzeptiert ?visible_ids=1,2,3 zum Filtern

5.12 LV-Tabelle Filter

  • Gleiches Filter-UI wie Positionstabelle (▾-Buttons in Spaltenüberschriften)
  • Spalten: Pos-Nr, Kurztext, EH, EP
  • Sortieren (↑/↓) und Suchen (Text-Livesuche)
  • Keine Checkbox-Liste (LV hat zu viele unique values)
  • Aktive Filter werden durch blaue ▾-Buttons angezeigt

5.13 Spaltenbreite anpassen (ziehen)

  • Jede Spaltenüberschrift hat rechts einen unsichtbaren Resize-Handle (5px breit)
  • Ziehen: Breite ändert sich live, bei Loslassen in localStorage gespeichert
  • Doppelklick auf Handle: automatische optimale Breite (maximale Inhaltsbreite)
  • Rechtsklick auf Spaltenkopf → Kontext-Menü mit "💾 Ansicht speichern"
    • Fixiert die aktuellen Spaltenbreiten als saved_lv_col_widths / saved_pos_col_widths
    • Nach Seitenneuladung werden gespeicherte Breiten wiederhergestellt
  • Funktion saveColumnWidths() speichert aktuelle Breiten separat als "gespeicherte Ansicht"
  • Gespeicherte Breiten überleben Seitenneuladungen (werden sofort beim Laden angewandt)

5.14 Positionstabelle Farben

  • has-background-warning-light (gelb) nur wenn Wert=0 UND passende Einheit (z.B. Menge=0 bei ST)

5.10 LV-Ergebnis EP

  • EP-Werte in der LV-Tabelle werden mit deutschem Zahlenformat angezeigt (|german_number)

6. Datenbank

6.1 Projekt-Modell (projekte)

  • id, company_id, contract_id
  • sm_nr, bezeichnung, vertrag, abruf_nr, lv_name
  • datum_start, datum_ende
  • ansprechpartner_vorname, ansprechpartner_nachname, ansprechpartner_tel, ansprechpartner_email
  • bauabschnitt, status
  • erstellt_von, erstellt_am, geaendert_am

6.2 Lizenz-Modell (lizenzen)

  • uid (auto-generiert SHA256)
  • max_mitarbeiter, max_module_slots
  • unlimited_users, unlimited_modules
  • Properties: used_users, used_module_slots, user_slots_display(), module_slots_display()

6.3 Datenbank-Engine

  • Lokal: SQLite (data/aufmass.db)
  • Produktion: PostgreSQL via DATABASE_URL Umgebungsvariable
  • Docker-Setup mit PostgreSQL-Service vorhanden

7. Docker

7.1 Services

  • db: PostgreSQL 16-alpine mit Healthcheck
  • web: Flask-App mit Gunicorn (4 Worker)

7.2 Konfiguration

  • DATABASE_URL wird via Environment Variable gesetzt
  • SECRET_KEY muss vor Produktion geändert werden
  • Volume pgdata für persistente Daten

8. Rollen & Berechtigungen

8.1 Superadmin

  • Zugriff auf alle Projekte, Firmen, Lizenzen
  • Firmenverwaltung & Lizenzverwaltung im Menü
  • Dashboard: Lizenz-Statistiken, Firmen-Übersicht

8.2 Firmadmin

  • Zugriff auf Projekte der eigenen Firma
  • Mitarbeiterverwaltung, Rechteverwaltung
  • Lizenz-Übersicht

8.3 Mitarbeiter

  • Zugriff nur auf zugewiesene Projekte (ProjectAccess)
  • Berechtigungen: lesen, schreiben, aufmass_verwalten, projekte_anlegen, darf_preise_sehen, darf_lv_verwalten

9. Login-Daten

Rolle Email Passwort
Superadmin super@admin.de admin123
Firmadmin firmadmin@kpt.de firma123

10. Wichtige technische Regeln

10.1 Inline-Edit

  • Blur/Enter speichert, Escape bricht ab
  • Keine OK/X-Buttons, kein setTimeout

10.2 Visuelle Aktualisierung

  • SOFORT vor async-fetch, nicht im Callback

10.3 Server-Response (resp.status)

  • resp.status ist das Aufmaß-Statusfeld ('aktiv'), NICHT ein Erfolgsindikator
  • Kein resp.status !== 'ok'-Check

10.4 Projekt-Name schützen

  • project.bezeichnung wird in aufmass_neu_voll und aufmass_import NICHT überschrieben
  • Nur explizit via POST /projekt/<id>/update-name änderbar

10.5 Redirect-Verhalten

  • Löschen, Duplizieren, Import: request.referrer prüfen
  • Baumansicht (/projekt/) → Redirect zu aufmass.index
  • Detailansicht (/projekt/<id>) → Redirect zu aufmass.aufmass_list
  • Erkennung: referrer.rstrip('/').endswith('/projekt')

10.6 Namensvalidierung

  • Aufmaß-Name: nur OS/Nextcloud-sichere Zeichen
  • Entfernt: < > : " / \ | ? * & # % { } ~ [ ]
  • Mehrfachleerzeichen → eines

11. E-Vergabe Addon

11.1 Firmeneinstellungen (Firmadmin)

  • Neuer Bereich "E-Vergabe" in den Firmeneinstellungen (/admin/firma)
  • Sichtbar nur wenn E-Vergabe-Addon für die Firma freigeschaltet ist (durch Superadmin)
  • Felder:
    • Benutzer (aus _legacy/daten/conf.ini[EVergabe] Benutzer)
    • Passwort (aus _legacy/daten/conf.ini[EVergabe] Passwort)
    • Name (aus _legacy/daten/conf.ini[EVergabe] Name)

11.2 Superadmin E-Vergabe Addon freigeben

  • Superadmin kann E-Vergabe-Addon pro Firma freischalten/deaktivieren
  • Dashboard: Spalte "E-Vergabe" mit Status und Toggle-Button (EV +/EV ✕)
  • Firma-Detail: E-Vergabe-Bereich mit Status und Freischalten/Deaktivieren-Button

11.3 Berechtigungen (User-Ebene)

Drei neue Berechtigungen für Mitarbeiter:

  • E-Vergabe Addon nutzen (darf_evergabe_nutzen) Grundvoraussetzung für alle E-Vergabe-Funktionen
  • Kopfdaten holen erlauben (darf_kopfdaten_holen) Erlaubt das Abrufen von Kopfdaten aus der E-Vergabe
  • Aufmaße in E-Vergabe übertragen (darf_aufmass_uebertragen) Erlaubt das Übertragen von Aufmaßen

11.4 "Kopfdaten EV holen" Button

  • Sichtbar nur wenn:
    • User hat darf_evergabe_nutzen UND darf_kopfdaten_holen
    • Firma hat evergabe_aktiviert = True
  • Anklickbar nur wenn:
    • Firma hat evergabe_benutzer UND evergabe_passwort hinterlegt
  • Mouseover-Hinweis wenn nicht anklickbar: "Es sind keine E-Vergabe Logindaten hinterlegt."
  • Funktion: Ruft Kopfdaten von der E-Vergabe-API ab und füllt die Formularfelder (Bezeichnung, Bauabschnitt, SM-Nr, Abruf-Nr, Daten, Ansprechpartner)

11.5 Company-Modell (neue Felder)

  • evergabe_aktiviert (Boolean, default=False) Addon freigeschaltet
  • evergabe_benutzer (String) Login-Benutzername
  • evergabe_passwort (String) Login-Passwort
  • evergabe_name (String) Name/Kennung

11.6 E-Vergabe Service (evergabe_service.py)

Python-Port der AutoIt EVvergabeWebobj.au3 Funktionen:

  • EVergabeClient Klasse mit requests.Session() für Cookie-Handling
  • login() Login via POST /public/login mit CSRF-Token-Extraktion
  • search_sm(sm_nr) SM-Nr-Suche via GET /framework-agreement-call, extrahiert Title, Bedarfsnr, Belegnr, DetailsID, RV, Daten, Status
  • get_aspa(details_id) Ansprechpartner-Daten (Name, Email, Tel) aus Details-Seite
  • hole_kopfdaten(sm_nr) Kombiniert Login + Suche + ASPA → Rückgabe aller Kopfdaten
  • aufmass_uebertragen(sm_nr, positionen, ...) Überträgt Positionen in E-Vergabe:
    • Erstellt Sheet via /sheet/create-sheet
    • Für jede Position: Erstellt Position, dann INSERT je nach Einheit (ST, M, M2, M3, STD, LE)
    • Richtige Headers: User-Agent, Referer, Content-Type, CSRF-Token
    • URL-Encoding aller Parameter

11.7 Aufmaß in E-Vergabe übertragen

  • Route: POST /projekt/<id>/aufmass/<aufmass_id>/evergabe-uebertragen
  • Berechtigung: darf_evergabe_nutzen + darf_aufmass_uebertragen
  • Überträgt alle Positionen des Aufmaßes in die E-Vergabe-Plattform
  • Rückgabe: JSON mit Erfolg/Fehler pro Position
  • evergabe_sub (String) Sub-Kennung

11.8 User-Modell (neue Felder)

  • darf_evergabe_nutzen (Boolean, default=False)
  • darf_kopfdaten_holen (Boolean, default=False)
  • darf_aufmass_uebertragen (Boolean, default=False)