Files
aufmass-web/_aufmass_web/ANFORDERUNGEN.md
T

389 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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,56``1234.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)