16 KiB
16 KiB
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 inindex, in Detailansicht → bleibt inlist
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 inlocalStorage- 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)
- Vertrag (Dropdown, optional)
- LV-Name (Text, optional)
- Typ (Dropdown, optional)
- Bezeichnung (Baustelle) (Text, leer)
- Bauabschnitt (Text, pre-filled aus Projekt)
- SM-Nr (Text, pre-filled aus Projekt)
- Abruf-Nr (Text, pre-filled aus Projekt)
- Kopfdaten EV holen (Button, bedingt sichtbar – siehe Abschnitt 11)
- Ansprechpartner (Trennlinie)
- Vorname
- Name
- Tel
- Startdatum (Date)
- Endedatum (Date)
- 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.bezeichnungwird 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 EingabevalidateNumField(): 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.
- Ungültig: Buchstaben,
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()setztcurrentLVRow/currentLVIdpro Zeile- Klick auf Positionstabelle löscht NICHT
currentLVRowoder 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,3zum 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
localStoragegespeichert - 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
- Fixiert die aktuellen Spaltenbreiten als
- 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_idsm_nr,bezeichnung,vertrag,abruf_nr,lv_namedatum_start,datum_endeansprechpartner_vorname,ansprechpartner_nachname,ansprechpartner_tel,ansprechpartner_emailbauabschnitt,statuserstellt_von,erstellt_am,geaendert_am
6.2 Lizenz-Modell (lizenzen)
uid(auto-generiert SHA256)max_mitarbeiter,max_module_slotsunlimited_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_URLUmgebungsvariable - Docker-Setup mit PostgreSQL-Service vorhanden
7. Docker
7.1 Services
db: PostgreSQL 16-alpine mit Healthcheckweb: Flask-App mit Gunicorn (4 Worker)
7.2 Konfiguration
DATABASE_URLwird via Environment Variable gesetztSECRET_KEYmuss vor Produktion geändert werden- Volume
pgdatafü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 | 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.statusist das Aufmaß-Statusfeld ('aktiv'), NICHT ein Erfolgsindikator- Kein
resp.status !== 'ok'-Check
10.4 Projekt-Name schützen
project.bezeichnungwird inaufmass_neu_vollundaufmass_importNICHT überschrieben- Nur explizit via
POST /projekt/<id>/update-nameänderbar
10.5 Redirect-Verhalten
- Löschen, Duplizieren, Import:
request.referrerprüfen - Baumansicht (
/projekt/) → Redirect zuaufmass.index - Detailansicht (
/projekt/<id>) → Redirect zuaufmass.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)
- Benutzer (aus
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_nutzenUNDdarf_kopfdaten_holen - Firma hat
evergabe_aktiviert = True
- User hat
- Anklickbar nur wenn:
- Firma hat
evergabe_benutzerUNDevergabe_passworthinterlegt
- Firma hat
- 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 freigeschaltetevergabe_benutzer(String) – Login-Benutzernameevergabe_passwort(String) – Login-Passwortevergabe_name(String) – Name/Kennung
11.6 E-Vergabe Service (evergabe_service.py)
Python-Port der AutoIt EVvergabeWebobj.au3 Funktionen:
EVergabeClientKlasse mitrequests.Session()für Cookie-Handlinglogin()– Login via POST/public/loginmit CSRF-Token-Extraktionsearch_sm(sm_nr)– SM-Nr-Suche via GET/framework-agreement-call, extrahiert Title, Bedarfsnr, Belegnr, DetailsID, RV, Daten, Statusget_aspa(details_id)– Ansprechpartner-Daten (Name, Email, Tel) aus Details-Seitehole_kopfdaten(sm_nr)– Kombiniert Login + Suche + ASPA → Rückgabe aller Kopfdatenaufmass_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
- Erstellt Sheet via
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)