Mobiles Aufmaß nach REB 23.003 (DA11 / DA12) mit Formel-Interpreter, Offline, Foto-Pin und Signatur
Feinkonzept — Mobiles Aufmaß nach REB 23.003
Abschnitt betitelt „Feinkonzept — Mobiles Aufmaß nach REB 23.003“Modul: Handwerk · Referenz:
FUNKTIONSUMFANG.md §4.4Cross-Links:03-gaeb-lv.md(referenziert OZ aus LV-Version) ·05-bau-abrechnung.md(Aufmaß-Mengen → Schlussrechnung nach VOB/B §14) Status: Feinkonzept v0.1 · 2026-04-19
1. Header & Metadaten
Abschnitt betitelt „1. Header & Metadaten“feature_id: handwerk/04-mobile-aufmasstitle: Mobiles Aufmaß nach REB 23.003 (DA11 / DA12) mit Formel-Interpreter, Offline, Foto-Pin und Signaturfunktionsumfang_ref: §4.4roadmap_horizont: V1plattformen: mobile: vollständig # Hauptarbeitsplatz: Baustelle, Tablet, Handschuhe web: mit-Bulk # Review und Freigabe im Büro desktop: ab V1.5 bei Bedarfowner_rolle: Bauleitung # erfassen + Bauherrn signieren lassenmodul_gate_flag: module.handwerk.aufmasscompliance_flags: gobd: true # Mengen-Nachweis Grundlage für Rechnung arbzg: false vob: true # VOB/C (DIN 18299 ff.) Aufmaß-Regeln pro Gewerk dsgvo: true # Signatur = personenbezogen bfsg: false betrvg: false stvg: false weitere: [REB 23.003 (BMV-Bund, elektronische Bauabrechnung), VOB/C DIN 18299 ff., §147 AO, StGB §267 (Urkundenfälschung bei Signatur-Manipulation)]referenzkunde: status: TBD name: "zu klären mit Sales — Kandidat: shk-gebruder-schmidt oder Hochbau-Partner mit öffentlichen AG" quelle: Interview-Protokoll 2026-02-20, Bauleiter bemängelt Excel-Aufmaß-Chaosestimate_eng_tage: 62 # Formel-Interpreter 10 · Mobile-UI 14 · PDF-Pin + Signatur 10 · Sync/Konflikt 10 · REB-Export 8 · Tests 10abhängigkeiten: - handwerk/03-gaeb-lv # LV-Version als Quelle der OZ - kern/11-rollen-rls - kern/12-audit-log - kern/offline-outbox # Idempotency-Key-Infrastruktur2. Kontext & Problem
Abschnitt betitelt „2. Kontext & Problem“2.1 Marktrealität DE-Bau
Abschnitt betitelt „2.1 Marktrealität DE-Bau“REB 23.003 („Regelungen für die Elektronische Bauabrechnung“, herausgegeben vom Bundesministerium für Verkehr und digitale Infrastruktur, Stand 1979 mit Updates bis 2009/2022) ist der verbindliche Austausch-Standard zwischen AG (Bauamt, DB Netz, Kommune) und AN (Bauunternehmen) für Aufmaß- und Abrechnungs-Daten. Zwei Datenarten sind zentral:
- REB DA11 — fertige Aufmaßzeilen (OZ, Menge, Formel-Ergebnis, optional Textbeschreibung). Wird vom AN an den AG geliefert.
- REB DA12 — Mengenberechnung mit expliziten Formeln, Variablen und Kommentaren. Der AG kann jede Aufmaßzeile gegen die Formel nachrechnen — ohne dass der AN eine Excel-Datei mitschickt.
REB 23.003 ist im Bundesstraßenbau (Bundesfernstraßen, Bundeswasserstraßen), bei der DB AG und in vielen Landesbauverwaltungen vertraglich erzwungen. Öffentliche AG lehnen reine PDF-Aufmaße für Abschlagsrechnungen ab; nur DA11/DA12 ist prüfbar. Kommunale AG akzeptieren teilweise noch Papier, verlangen aber zunehmend DA-Format.
2.2 Das Problem auf der Baustelle heute
Abschnitt betitelt „2.2 Das Problem auf der Baustelle heute“Der Monteur oder Bauleiter misst heute:
- Zollstock + Notizblock → Block voll mit
L × B – Abzug Tür × ..., Skizze nebenan. - Abends ins Büro → Excel-Tabelle, Formel reingetippt, Kollege prüft am nächsten Tag.
- Büro ruft an: „Was ist hier OZ 01.02.030, wurde das wirklich so gemessen?“ → Diskussion, niemand weiß es sicher, kein Foto vom Messort, keine Skizze.
- Bauherr-Streitfall später: „Das stand nicht so im Auftrag“ → ohne Signatur des Bauherrn vor Ort ist das Aufmaß rechtlich schwach (VOB/B §14 Abs. 2 — Feststellung mit AG).
Alternativen heute:
- Papier-Aufmaßblatt (DIN A4-Vordrucke von Feldhaus, Rößler) — klassisch, dokumentarisch ok, aber nicht REB-11/12-kompatibel.
- RIB iTWO Aufmaß mobil — funktional, aber Lizenz 4-stellig/Jahr.
- Elektro-Aufmaß (AutoCAD Tools, Revu Bluebeam) — sehr spezialisiert, viel zu teuer fürs Gewerk.
- Eigen-Excel — am häufigsten. Ohne REB-Export, ohne Signatur, ohne OZ-Bindung, ohne Foto-Referenz. Grenzwertig GoBD (leicht manipulierbar).
2.3 Schmerzpunkt der Alt-App
Abschnitt betitelt „2.3 Schmerzpunkt der Alt-App“In zeitapp gab es keinen Aufmaß-Screen. Der Versuch, Mengen frei in den Zeiteintrag zu klemmen („Tätigkeit: 128 m² Putz abreißen“), ist bei VOB/B §14-Abrechnung vor Gericht bedeutungslos.
LESSONS-LEARNED §5: „Business-Substanz fehlt im Code — GAEB, REB-23.003-Aufmaß, VOB/B Teil-/Schlussrechnung mit Kumulation: damit fehlt die Zahlungsbereitschaft der lautstärksten Zielgruppe (Bau-Betriebe).“
LESSONS-LEARNED §4: „Offline-First war nur halb offline — Konfliktauflösung ist im Kern Last-Write-Wins ohne Diff-UI, keine Idempotenz-Keys.“ → Für Aufmaß inakzeptabel: Wenn Monteur A und B parallel offline messen und dieselbe OZ betreffen, darf der letzte Sync nicht den ersten überschreiben.
2.4 Erwarteter Outcome
Abschnitt betitelt „2.4 Erwarteter Outcome“| Metrik | Ist | Ziel mit Werkszeit |
|---|---|---|
| Zeit für Aufmaß-Erfassung (100 Zeilen) | 2–3 h (Messen + abends Excel) | ≤ 45 min (mobil in einem Rutsch) |
| Aufmaß-Streit mit Bauherrn (pro Projekt) | 20–30 % der Abschlagsrechnungen | < 5 % (Vor-Ort-Signatur + Foto-Pin) |
| Zeit bis REB-DA11/12-Abgabe | 3–5 Tage (Büro-Übersetzung) | sofort (Export-Button) |
| Formel-Fehler in Aufmaß-Zeile | 3–5 % (manuelle Excel) | < 0,1 % (Parser validiert) |
3. Personas & Rollen
Abschnitt betitelt „3. Personas & Rollen“| Rolle | Aktion | Scope | Plattform |
|---|---|---|---|
| Bauleitung | Aufmaß erfassen, Formel tippen, Foto-Pin setzen, Bauherr signieren lassen | team |
📱 (primär) + 🌐 (Review) |
| Monteur | Eigene Aufmaßzeilen erfassen (ohne Bauherr-Signatur — Signatur ist Bauleitungs-Aufgabe) | own |
📱 |
| Manager / Bauleiter Büro | Aufmaß-Review, Freigabe, Änderungs-Protokoll einsehen, DA11/DA12 exportieren | team |
🌐 |
| Kalkulator | Aufmaß-Abweichungen zum kalkulierten Mengenansatz sehen (Soll-/Ist-Vergleich) | team |
🌐 |
| Buchhaltung | Aufmaß-Summen je OZ als Grundlage für Rechnungsposition (§4.5) | all |
🌐 |
| Bauherr / AG | Signatur per Touch, optional: DA12 per Portal-Link nachvollziehen | — (extern) | 📱 (Touch) + 🌐 (Portal-Link) |
Persona-Skizzen:
- Hannes K. (Monteur, 38, SHK) — arbeitet mit Handschuhen, Tablet fällt regelmäßig auf Baustellen-Boden, hat kein LTE im Heizungskeller, möchte „möglichst nur Zollstock lesen und tippen“.
- Sabine M. (Bauleiterin, 45, Hochbau) — 3 Baustellen parallel, erledigt Aufmaß auf den Baustellen direkt. Braucht am Abend den Sync-Konflikt-Dialog, wenn Monteur und sie dieselbe OZ gemessen haben.
- Herr Özdemir (Bauherr, 63, privat) — misstrauisch gegenüber Tablets, will aber nicht wochenlang warten. Akzeptiert Signatur, wenn Bauleiter die Zahlen vorliest und er den Messort auf dem Foto sieht.
4. User-Stories
Abschnitt betitelt „4. User-Stories“US-01 [V1] Als Monteur möchte ich offline im Heizungskeller eine Aufmaßzeile mit der Formel "4,80*2,60-1,20*2,10" erfassen, damit ich auch ohne LTE arbeiten kann und die Formel später für den AG nachvollziehbar bleibt.
US-02 [V1] Als Bauleitung möchte ich zu jeder Aufmaßzeile ein Foto vom Messort machen (optional Pin auf dem Grundriss-PDF), damit bei späterer Rückfrage klar ist, wo gemessen wurde.
US-03 [V1] Als Bauleitung möchte ich den Bauherrn vor Ort auf dem Tablet signieren lassen, damit das Aufmaß nach VOB/B §14 Abs. 2 als einvernehmlich festgestellt gilt.
US-04 [V1] Als Bauleitung möchte ich eine Aufmaßzeile als Negativ-Menge erfassen (z. B. "minus Türöffnung"), damit der Netto-Flächen-Abzug korrekt in die Position eingeht, ohne eine separate OZ dafür zu benötigen.
US-05 [V1] Als Büro-Bauleiter möchte ich beim Sync erkennen, wenn zwei Geräte parallel offline zur selben OZ Aufmaß erfasst haben, damit ich keine stille Überschreibung habe und explizit entscheide, welche Zeilen behalten werden.
US-06 [V1] Als Kalkulator möchte ich auf einen Blick sehen, wenn die Aufmaß-Summe einer OZ vom LV-Soll abweicht (über ±5 %), damit ich rechtzeitig einen Nachtrag anstoßen kann.
US-07 [V1] Als Buchhaltung möchte ich den Aufmaß-Stand einer OZ als REB-DA11 und DA12 exportieren, damit ich die Datei mit der Abschlagsrechnung an den öffentlichen AG schicken kann.
US-08 [V1.5] Als Admin möchte ich Aufmaß-Vorlagen definieren (z. B. "Raum ausmessen" erzeugt automatisch Länge, Breite, Höhe mit Variablen-Namen), damit Monteure nicht immer wieder dieselben Formeln tippen.5. Funktionale Anforderungen
Abschnitt betitelt „5. Funktionale Anforderungen“5.1 Mobile App (📱) — iOS + Android
Abschnitt betitelt „5.1 Mobile App (📱) — iOS + Android“- F-M-01 — OZ-Picker aus beauftragter LV-Version. Aufmaß ist nur möglich, wenn Projekt ein LV mit Status
beauftragthat. Picker zeigt Baum mit OZ + Kurztext + verbleibende Menge (Soll minus bisher aufgemessen). - F-M-02 — Aufmaßzeile erfassen. Felder: Raum/Position (Freitext), ME (aus LV übernommen, read-only), Formel (REB-Syntax), Ergebnis (auto berechnet + editierbar), Notiz (optional). Pflichtfelder: OZ + Formel oder direktes Ergebnis + Messdatum.
- F-M-03 — Formel-Interpreter inline. Siehe §5.6 unten. Live-Evaluation während der Eingabe (< 50 ms), rote Unterstreichung bei Syntaxfehler.
- F-M-04 — Variablen-Kontext. Innerhalb derselben Aufmaßzeile können Variablen definiert werden:
$L=4,80 ; $B=2,60 ; $L*$B. Variablen sind zeilen-lokal, nicht global (gem. REB-Semantik). - F-M-05 — Negativ-Menge. Toggle oder Präfix
-vor der Formel macht das Ergebnis negativ. UI zeigt Zeile mit(Abzug)-Badge. - F-M-06 — Foto-Pin auf Grundriss. Für eine OZ kann der Bauleiter den zuvor hochgeladenen Grundriss-PDF öffnen (siehe F-W-02) und per Tap einen Pin setzen. Foto-Anhang direkt aus Kamera (EXIF-GPS + Zeitstempel Pflicht). Bis 10 Fotos je Zeile.
- F-M-07 — Touch-Signatur Bauherr. Nach Abschluss eines Aufmaß-Blocks (eine oder mehrere OZ zusammen) → Signatur-Pad, Name des Unterschreibenden, Rolle („Bauherr“ / „Bauleitung AG“), optional Handy-Nr. für DSGVO-Rückruf. Signatur als PNG + SVG-Pfade (beide, für Druck und für UI). Signatur-Hash wird in Audit-Log aufgenommen.
- F-M-08 — Offline-Vollständig. Aufmaß ist voll-offline nach Profil DOR §1.2 (9): alle Mutationen gehen in Outbox mit Idempotency-Key (UUID v7). Foto-Upload per S3-Multipart mit presigned URL, SQLite hält Thumbnail + Referenz.
- F-M-09 — Sync-Konflikt-Diff. Beim Sync: Wenn Server-Version eine Zeile mit derselben (lv_version_id, oz, source_device ≠ eigenes) erhalten hat, UI zeigt Diff-View Seite-an-Seite (meine Zeile vs. Server-Zeile). User muss manuell auflösen: Beide behalten, Nur meine, Nur Server, Merge-Dialog. Kein Last-Write-Wins.
- F-M-10 — Geo-Tag. Optional (konfigurierbar Admin + BetrVG-Hinweis): Aufmaßzeile bekommt GPS-Stempel bei Erstellung.
- F-M-11 — Werkzeug-Messen per Bluetooth Laser (V1.5). Anbindung an Leica Disto / Bosch GLM — Maß direkt in Formel-Variable übernommen.
5.2 Web-App (🌐)
Abschnitt betitelt „5.2 Web-App (🌐)“- F-W-01 — Aufmaß-Review-Tabelle. Sortier-/filterbare Tabelle: OZ, Zeile-Nr, Formel, Ergebnis, ME, Raum, Erfasst-von, Signatur-Status, Foto-Anzahl, Konflikt-Status. Bulk-Freigabe möglich für Zeilen ohne Konflikte.
- F-W-02 — Grundriss-PDF-Upload pro Projekt. Admin lädt PDF (mehrseitig) hoch. System erzeugt PNG-Kacheln für Mobile-Anzeige (DPI ≥ 150). Pins können nachträglich am Web-Tablet verschoben werden.
- F-W-03 — REB-Export DA11 + DA12. Job-basiert: User wählt OZ-Auswahl oder „alle seit letzter Abrechnung“ + Phase (Teilaufmaß / Schlussaufmaß). Lambda erzeugt beide Dateien (ASCII fixed-width, CP850 per default, UTF-8 optional), Download als ZIP.
- F-W-04 — Soll-/Ist-Vergleich. Dashboard pro Projekt: Säulendiagramm OZ — Soll (aus LV) vs. Ist (aus Aufmaß). Abweichung > 5 % wird hervorgehoben → CTA „Nachtrag anstoßen“.
- F-W-05 — Manuelle Nachbearbeitung einer Aufmaßzeile. Nur möglich, wenn Zeile nicht signiert und nicht exportiert. Signierte Zeilen sind append-only: Korrektur = neue Zeile mit Verweis auf alte + Begründung.
- F-W-06 — Bauherr-Portal-Link. Freigabe-E-Mail an Bauherrn mit Token-URL: Bauherr sieht Aufmaß-Zeilen + Fotos + Unterschrift, kann als PDF herunterladen (DA12-menschlich-lesbar-transformiert).
- F-W-07 — Bulk-Export für Buchhaltung. Aufmaß-Summen je OZ als CSV/JSON für §4.5-Rechnungs-Import (
GET /v1/handwerk/aufmass/summary?lvVersionId=...).
5.3 Cross-Plattform (🔄)
Abschnitt betitelt „5.3 Cross-Plattform (🔄)“- F-X-01 — Formel-Interpreter in Dart (
packages/reb_formula/) — ausführbar auf Mobile, Web und Server. Deterministisch, bit-identische Ergebnisse auf allen Plattformen. Property-Tests auf beiden Seiten. - F-X-02 — OZ-Resolver (Shared mit §4.3) — konsumiert
packages/oz-core. - F-X-03 — Signatur-Rendering — SVG-Path-Rendering identisch Mobile/Web/PDF-Generator.
5.4 Admin-Konfiguration
Abschnitt betitelt „5.4 Admin-Konfiguration“- F-A-01 — Konflikt-Auflösungs-Profil. Pro Tenant:
manuell-Pflicht(default) /server-priorität-wenn-später-erfasst/erstes-Gerät-wins(nur mit BetrVG-Zustimmung, da Leistungsvergleich-Risiko). - F-A-02 — Foto-Pflicht-Profil. Pro Gewerk: 0/1/N Fotos Pflicht. Default SHK: 1 Foto pro Zeile; Hochbau: 0 (nicht blockierend).
- F-A-03 — REB-Export-Profil. Zeichensatz (CP850 default / ISO-8859-15 / UTF-8), Zeilenende (CR-LF / LF), DA12-Kommentar-Präfix.
- F-A-04 — Aufmaß-Vorlagen (V1.5). Benannte Formel-Templates mit Variablen-Defaults.
- F-A-05 — Geo-Tag aktivieren — BetrVG-relevant (nicht automatisch aktiv, braucht BR-Freischaltung + Tenant-Toggle).
5.5 Roadmap-Schichtung
Abschnitt betitelt „5.5 Roadmap-Schichtung“| ID | MVP | V1 | V1.5 | V2 |
|---|---|---|---|---|
| F-M-01 bis F-M-09 (Kern-Aufmaß-Flow) | ✅ | |||
| F-M-10 Geo-Tag (opt.) | ✅ | |||
| F-M-11 Laser-Disto-Bluetooth | ✅ | |||
| F-W-01 Review-Tabelle | ✅ | |||
| F-W-02 Grundriss-PDF-Upload | ✅ | |||
| F-W-03 REB-Export DA11+12 | ✅ | |||
| F-W-04 Soll-/Ist-Dashboard | ✅ | |||
| F-W-05 Nachbearbeitung | ✅ | |||
| F-W-06 Bauherr-Portal | ✅ | |||
| F-W-07 Bulk-Export Buchhaltung | ✅ | |||
| F-X-01 Formel-Interpreter | ✅ | |||
| F-A-01 Konflikt-Profil | ✅ | |||
| F-A-02 Foto-Pflicht | ✅ | |||
| F-A-04 Aufmaß-Vorlagen | ✅ |
5.6 REB-Formel-Interpreter — Spezifikation
Abschnitt betitelt „5.6 REB-Formel-Interpreter — Spezifikation“Grammatik (EBNF-nah):
formula = assignment ( ";" assignment )* ";" expression | expression ;assignment = variable "=" expression ;variable = "$" ( letter | digit )+ ; /* $L, $B, $1, $h2 */expression = term ( ("+" | "-") term )* ;term = factor ( ("*" | "/") factor )* ;factor = number | variable | func | "(" expression ")" | "-" factor ;func = ident "(" arglist? ")" ;ident = "sqrt" | "pi" | "abs" | "pow" | "round" | "floor" | "ceil" ;arglist = expression ( "," expression )* ;number = digit+ ( ("," | ".") digit+ )? ; /* de-DE Komma + en-US Punkt beide akzeptiert */Semantik (REB 23.003, §5 Datenart 12):
- Dezimaltrennzeichen:
,(de-DE) oder.— intern immer alsdecimal.Decimal(arbiträre Präzision) mit 6 Nachkomma, Ausgabe auf 4 Nachkomma gerundetHALF_EVEN(REB-Standard: Rundung auf die in der LV-Position festgelegte ME-Genauigkeit, default 4 Nachkommastellen für Fläche, 2 für Länge). - Operatoren:
+ - * /(binär, linksassoziativ),-unär. - Klammern:
(,)beliebige Verschachtelung. - Funktionen:
sqrt(x),pi()(= 3.14159265358979),abs(x),pow(x, y),round(x, n),floor(x),ceil(x). - Variablen:
$<ident>, zeilenlokal.$1, $2, ...$99= numerisch indexiert, gem. REB-Tradition oft$1=Länge,$2=Breite. - Keine Kontrollstrukturen, keine Schleifen, keine arbitrary code execution — reiner Ausdrucks-Interpreter.
- Ausführungs-Limit: max 500 Tokens, 10 Assignments, 50 Operatoren pro Zeile. Verhindert ReDoS-ähnliche Angriffe.
Referenz-Implementierung: Dart-Package packages/reb_formula auf Basis petitparser (gem. TECH-STACK.md §2.93). Gleicher Code Mobile/Web/Server (auch TS-Port für Server-Validierung — Cross-Check der Ergebnisse).
Akzeptierte Beispiele:
4,80*2,60-1,20*2,10→ 9,96 m² (Wand minus Tür)$L=4,80; $B=2,60; $L*$B→ 12,48 m²(2,40+3,10)*2,60→ 14,30 m²sqrt(3,5*3,5 + 2,5*2,5)→ 4,3012 m (Diagonale)pi()*pow(0,5, 2)→ 0,7854 m² (Kreis r=0,5 m)-1,20*2,10→ −2,52 m² (Abzug Türöffnung)
Abgelehnt:
rm -rf /→ Parser-Errorimport os; os.system('x')→ Parser-Errorwhile(1){}→ Parser-Error0/0→ Laufzeit-Fehler mit Meldung „Division durch 0“
6. Mockups & Flows
Abschnitt betitelt „6. Mockups & Flows“HTML-Hero-Mockup: 04-mobile-aufmass.html — Mobile dominant, Web als schmale Review-Sicht (Aufmaß ist Feldarbeit).
6.1 Mobile — Aufmaßzeile erfassen
Abschnitt betitelt „6.1 Mobile — Aufmaßzeile erfassen“┌────────────────────────────────┐│ ← Aufmaß · Schule A ✓ ⋯ │├────────────────────────────────┤│ OZ: 01.02.030 ││ Putz-Innenwand abreißen · m² ││ LV-Soll: 128,50 m² ││ Bisher: 45,20 m² (35,2 %) ││ ────────────────────────────── ││ Raum: ▼ Klassenzimmer 1.04 ││ ││ 🟢 Formel ││ ┌──────────────────────────┐ ││ │ 4,80*2,60-1,20*2,10 │ │ ││ └──────────────────────────┘ ││ = 9,96 m² ││ ││ [ ] Negativ-Menge (Abzug) ││ ││ Notiz (optional): ││ ┌──────────────────────────┐ ││ │ Nordwand, ohne Heizung │ ││ └──────────────────────────┘ ││ ││ Fotos · 2/10 ││ [📷 IMG_0412] [📷 IMG_0413] ││ [📷 + Pin auf Grundriss] ││ ││ 📍 48.1371°N 11.5754°E ±4 m ││ ││ [ Speichern ] [+ Neue Zeile] │└────────────────────────────────┘6.2 Mobile — Bauherr-Signatur
Abschnitt betitelt „6.2 Mobile — Bauherr-Signatur“┌────────────────────────────────┐│ ← Aufmaß-Signatur │├────────────────────────────────┤│ Aufmaß-Block · 6 Zeilen ││ Gesamt: 64,20 m² für 01.02.030││ Gesamt: 4 Stk für 02.01.010 ││ ││ Anwesend: ││ Bauleitung · Sabine Maier ✓ ││ Bauherr · ▢ Name Pflicht ││ ││ Name Bauherr: ││ ┌──────────────────────────┐ ││ │ Herbert Özdemir │ ││ └──────────────────────────┘ ││ Rolle: ( ) AG (•) Bauherr ││ ││ ✍️ Unterschrift: ││ ┌──────────────────────────┐ ││ │ │ ││ │ ╱╲ ╱╲ │ ││ │ ╱ ╲ ╱ ╲_ _ │ ││ │ ╱ ╲╱ ╲╱ ╲╱ │ ││ │ │ ││ └──────────────────────────┘ ││ [Löschen] [Signatur-Hash 4a..] ││ ││ ⚠ Mit der Unterschrift bestä- ││ tigt der Bauherr die Maße ││ gem. VOB/B §14 Abs. 2. ││ ││ [ Signieren & Abschließen ] │└────────────────────────────────┘6.3 Mobile — Sync-Konflikt-Diff
Abschnitt betitelt „6.3 Mobile — Sync-Konflikt-Diff“┌────────────────────────────────┐│ ⚠ Sync-Konflikt · 2 Zeilen │├────────────────────────────────┤│ OZ 01.02.030 · Raum 1.04 ││ ││ ── Meine Zeile (Hannes) ││ Formel: 4,80*2,60 ││ = 12,48 m² ││ erfasst 19.04. 09:12 ││ 📷 1 Foto · nicht signiert ││ ││ ── Server-Zeile (Sabine) ││ Formel: 4,80*2,60-1,20*2,10 ││ = 9,96 m² ││ erfasst 19.04. 09:08 ││ 📷 2 Fotos · nicht signiert ││ ││ Auflösung: ││ ( ) Beide behalten (als A/B) ││ (•) Nur meine behalten ││ ( ) Nur Server-Zeile behalten ││ ( ) Merge (manuell bearbeiten) ││ ││ Begründung 🔴 (für Audit-Log): ││ ┌──────────────────────────┐ ││ │ Türöffnung bereits in │ ││ │ separater Zeile erfasst │ ││ └──────────────────────────┘ ││ ││ [Weiter zur nächsten] [Lösen]│└────────────────────────────────┘6.4 Web — Aufmaß-Review-Tabelle
Abschnitt betitelt „6.4 Web — Aufmaß-Review-Tabelle“┌────────────────────────────────────────────────────────────────────────────────┐│ Werkszeit · shk-gebruder-schmidt Sabine M. · Bauleitung [Profil ▾]│├────────────────────────────────────────────────────────────────────────────────┤│ Sidebar │ Aufmaß · Schule A · LV v3 beauftragt ││ ───────── │ [⤓ DA11] [⤓ DA12] [Bulk-Freigabe ▾] [+ PDF-Grundriss] ││ Zeit │ ┌──────────────────────────────────────────────────────────────┐ ││ LV │ │ OZ │ Zeile │ Formel │ Menge │ Status │ ││ ▶ Aufmaß │ ├─────────────┼───────┼──────────────────┼─────────┼──────────┤ ││ Nachtr. │ │ 01.02.030 │ 001 │ 4,80*2,60-1,20*2,10 │ 9,96 m²│ 🟢 sig. │ ││ Rechn. │ │ 01.02.030 │ 002 │ 5,20*2,60 │ 13,52 m²│ 🟢 sig. │ ││ Doku │ │ 01.02.030 │ 003 │ -1,20*2,10 │ -2,52 m²│ 🟢 sig. │ ││ │ │ 01.02.030 │ 004A │ 4,80*2,60 │ 12,48 m²│ 🟡 Konf.│ ││ │ │ 01.02.030 │ 004B │ 4,80*2,60-1,20*2,10 │ 9,96 m²│ 🟡 Konf.│ ││ │ │ 02.01.010 │ 001 │ $1=2,5; pi()*$1*$1 │19,63 Stk│ 🔴 offen│ ││ │ └──────────────────────────────────────────────────────────────┘ ││ │ ││ │ Soll/Ist · 01.02.030 ││ │ ▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐ 64,20 m² Ist (50,0 %) ││ │ ▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐▐ 128,50 m² Soll (LV) ││ │ ⚠ Abweichung −16,3% → Nachtrag prüfen? [Nachtrag anstoßen] │└────────────────────────────────────────────────────────────────────────────────┘6.5 Web — PDF-Grundriss mit Pins
Abschnitt betitelt „6.5 Web — PDF-Grundriss mit Pins“┌────────────────────────────────────────────────────────────────────────────────┐│ Grundriss · Schule A · EG.pdf · Seite 1/3 [📐 Pin setzen] │├────────────────────────────────────────────────────────────────────────────────┤│ ││ ┌────────────┬────────────┬────────────┐ ││ │ │ │ │ ││ │ Klassen- │ Flur │ Klassen- │ ││ │ zimmer 1.04│ │ zimmer 1.05│ ││ │ 📍1 │ │ 📍3 │ ││ │ │ │ │ ││ ├────────────┴────────────┴────────────┤ ││ │ │ ││ │ Aula 📍4│ ││ │ │ ││ └───────────────────────────────────────┘ ││ ││ Pins: 1: OZ 01.02.030 · Zeile 001 2: OZ 01.02.030 · Zeile 002 ││ 3: OZ 01.02.030 · Zeile 003 4: OZ 01.02.040 · Zeile 001 ││ ││ [Pin verschieben] [Pin löschen] [Alle Pins als PDF-Overlay] │└────────────────────────────────────────────────────────────────────────────────┘6.6 Web — REB-DA12-Export-Dialog
Abschnitt betitelt „6.6 Web — REB-DA12-Export-Dialog“┌────────────────────────────────────────────────────────────────┐│ REB-Export · Aufmaß Schule A │├────────────────────────────────────────────────────────────────┤│ Abrechnungs-Phase: (•) Teilaufmaß ( ) Schlussaufmaß ││ Auswahl: ││ (•) Alle seit letzter Abrechnung (62 Zeilen) ││ ( ) OZ-Auswahl: ______________ ││ ( ) Zeitraum: 19.04.2026 − 19.04.2026 ││ ││ Format: ││ [✓] DA11 (Aufmaßzeilen) ││ [✓] DA12 (Mengenberechnung mit Formeln) ││ ││ Zeichensatz: ( ) UTF-8 (•) CP850 ( ) ISO-8859-15 ││ Zeilenende: (•) CR-LF ( ) LF ││ ││ Profil: ▼ Standard (BMV-Bund) ││ ││ Unsignierte Zeilen (7): ││ ⚠ 7 Zeilen sind noch nicht vom Bauherrn signiert. ││ Export trotzdem? (üblich bei Teilaufmaß) ││ [✓] Ja, unsignierte einschließen ││ ││ [Abbrechen] [Export starten] │└────────────────────────────────────────────────────────────────┘Symbol-Konvention: 🟢 signiert / OK, 🟡 Sync-Konflikt, 🔴 offen / Pflichtfeld, 📷 Foto, ✍️ Signatur, 📍 Grundriss-Pin, 📐 Messen.
7. Datenmodell-Skizze
Abschnitt betitelt „7. Datenmodell-Skizze“export const aufmassBlockTable = pgTable('aufmass_block', { id: uuid('id').primaryKey().defaultRandom(), // UUID v7 tenantId: uuid('tenant_id').notNull(), projectId: uuid('project_id').notNull().references(() => projectsTable.id), lvVersionId: uuid('lv_version_id').notNull(), // muss 'beauftragt' sein (FK-Check) bezeichnung: text('bezeichnung'), // "Klassenzimmer-Abriss W1" phase: text('phase').notNull(), // 'teilaufmass'|'schlussaufmass' status: text('status').notNull(), // 'entwurf'|'signiert'|'exportiert'|'abgerechnet' signaturPng: text('signatur_png'), // base64 (oder S3-Ref) signaturSvg: text('signatur_svg'), // SVG-Path für Druck/PDF signaturName: text('signatur_name'), signaturRolle: text('signatur_rolle'), // 'bauherr'|'ag'|'bauleitung_ag' signaturHash: bytea('signatur_hash'), // SHA-256 über (png+name+rolle+timestamp) signaturAt: timestamp('signatur_at', { withTimezone: true }), hashPrev: bytea('hash_prev'), hashSelf: bytea('hash_self').notNull(), immutableAt: timestamp('immutable_at', { withTimezone: true }), createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), createdBy: uuid('created_by').notNull(),}, (t) => ({ tenantProjIdx: index('aufmass_block_tenant_proj_idx').on(t.tenantId, t.projectId), lvVerIdx: index('aufmass_block_lv_ver_idx').on(t.lvVersionId),}));
export const aufmassZeileTable = pgTable('aufmass_zeile', { id: uuid('id').primaryKey().defaultRandom(), // UUID v7 == Idempotency-Key tenantId: uuid('tenant_id').notNull(), blockId: uuid('block_id').references(() => aufmassBlockTable.id), // nullable: Zeile kann lose existieren projectId: uuid('project_id').notNull(), lvVersionId: uuid('lv_version_id').notNull(), oz: text('oz').notNull(), zeileNr: text('zeile_nr').notNull(), // '001', '001A', '002.neg' raum: text('raum'), // 'Klassenzimmer 1.04' mengeneinheit: text('mengeneinheit').notNull(), // 'm2', aus lv_knoten formel: text('formel'), // Original-Formel-Text variablen: jsonb('variablen'), // { "$L": "4,80", "$B": "2,60" } ergebnis: numeric('ergebnis', { precision: 14, scale: 4 }).notNull(), // auto + manuell override ergebnisCalc: numeric('ergebnis_calc', { precision: 14, scale: 6 }), // aus Interpreter (6 Nachkomma) negativ: boolean('negativ').notNull().default(false), notiz: text('notiz'), fotos: jsonb('fotos'), // [{s3Key, sha256, exifLat, exifLon, exifTs}] grundrissPin: jsonb('grundriss_pin'), // { pdfId, page, x, y } (normalized 0..1) geoLat: numeric('geo_lat', { precision: 9, scale: 6 }), geoLon: numeric('geo_lon', { precision: 9, scale: 6 }), geoAccuracyM: integer('geo_accuracy_m'), sourceDevice: text('source_device'), // 'device:uuid' (FPR-Fingerprint) sourceAppVersion: text('source_app_version'), messzeitpunkt: timestamp('messzeitpunkt', { withTimezone: true }).notNull(),
// Append-only / Korrektur-Kette replacesZeileId: uuid('replaces_zeile_id').references((): any => aufmassZeileTable.id), replacedReason: text('replaced_reason'),
// GoBD hashPrev: bytea('hash_prev'), hashSelf: bytea('hash_self').notNull(), immutableAt: timestamp('immutable_at', { withTimezone: true }),
// Sync clientCreatedAt: timestamp('client_created_at', { withTimezone: true }), syncedAt: timestamp('synced_at', { withTimezone: true }), conflictWith: uuid('conflict_with').references((): any => aufmassZeileTable.id), conflictResolution: text('conflict_resolution'), // 'both_kept'|'mine'|'theirs'|'merged'
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), createdBy: uuid('created_by').notNull(),}, (t) => ({ lvOzIdx: index('aufmass_zeile_lv_oz_idx').on(t.lvVersionId, t.oz), blockIdx: index('aufmass_zeile_block_idx').on(t.blockId), replacesIdx: index('aufmass_zeile_replaces_idx').on(t.replacesZeileId),}));
// PDF-Grundrisse pro Projektexport const grundrissPdfTable = pgTable('grundriss_pdf', { id: uuid('id').primaryKey().defaultRandom(), tenantId: uuid('tenant_id').notNull(), projectId: uuid('project_id').notNull(), name: text('name').notNull(), // "Schule A · EG" s3Key: text('s3_key').notNull(), seitenCount: integer('seiten_count').notNull(), tilesS3Prefix: text('tiles_s3_prefix'), // PNG-Kacheln für Mobile});
// Aufmaß-Vorlage (V1.5)export const aufmassVorlageTable = pgTable('aufmass_vorlage', { id: uuid('id').primaryKey().defaultRandom(), tenantId: uuid('tenant_id').notNull(), name: text('name').notNull(), // "Raum ausmessen" formelTemplate: text('formel_template').notNull(), // "$L=?; $B=?; $L*$B" beschreibung: text('beschreibung'),});7.1 RLS-Policy
Abschnitt betitelt „7.1 RLS-Policy“ALTER TABLE aufmass_block ENABLE ROW LEVEL SECURITY;ALTER TABLE aufmass_zeile ENABLE ROW LEVEL SECURITY;ALTER TABLE grundriss_pdf ENABLE ROW LEVEL SECURITY;
-- Tenant-IsolationCREATE POLICY aufmass_zeile_tenant_iso ON aufmass_zeile USING (tenant_id = current_setting('app.tenant_id')::uuid);
-- Scope: own = nur eigene erfasste, team = Projekt-Mitglieder, all = AdminCREATE POLICY aufmass_zeile_scope ON aufmass_zeile FOR SELECT USING ( tenant_id = current_setting('app.tenant_id')::uuid AND ( current_setting('app.scope') = 'all' OR (current_setting('app.scope') = 'team' AND project_id IN (SELECT project_id FROM project_member WHERE user_id = current_setting('app.user_id')::uuid)) OR (current_setting('app.scope') = 'own' AND created_by = current_setting('app.user_id')::uuid) ) );
-- GoBD-Sperre: nach Signatur / Export keine UPDATEs auf ZeileCREATE POLICY aufmass_zeile_no_update_after_immutable ON aufmass_zeile FOR UPDATE USING (immutable_at IS NULL);
-- Korrektur nur über INSERT neue Zeile mit replaces_zeile_idCREATE POLICY aufmass_zeile_no_delete ON aufmass_zeile FOR DELETE USING (false); -- nie löschen
-- aufmass_block: nur Bauleitung + Manager dürfen signierenCREATE POLICY aufmass_block_sign ON aufmass_block FOR UPDATE USING ( current_setting('app.role') IN ('bauleitung', 'manager', 'admin') AND tenant_id = current_setting('app.tenant_id')::uuid );7.2 ER-Bezug
Abschnitt betitelt „7.2 ER-Bezug“aufmass_zeile.lv_version_id→lv_version.id(mussphase='auftrag'undimmutable_at IS NOT NULL)aufmass_zeile.oz+lv_version_id→ logisch eine Position auslv_knotenaufmass_block.signatur_hashfließt in globale Audit-Hash-Chainaufmass_zeile.fotos[].s3Key→ S3 mit Object Lock (Compliance, 10 Jahre)
8. API-Endpunkte
Abschnitt betitelt „8. API-Endpunkte“Pfad-Konvention: /v1/handwerk/aufmass/…, Gate module.handwerk.aufmass.
| Methode | Pfad | Auth-Scope | Rate-Limit | Idempotenz | Beschreibung |
|---|---|---|---|---|---|
POST |
/v1/handwerk/aufmass/zeilen |
aufmass:write |
Standard | Idem-Key Pflicht | Aufmaßzeile anlegen (ID = Idem-Key) |
GET |
/v1/handwerk/aufmass/zeilen |
aufmass:read:<scope> |
Standard | — | Liste (Filter OZ, Projekt, Zeitraum) |
GET |
/v1/handwerk/aufmass/zeilen/{id} |
aufmass:read:<scope> |
Standard | — | Detail |
POST |
/v1/handwerk/aufmass/zeilen/{id}/replace |
aufmass:write |
Standard | Idem-Key | Korrektur-Zeile anlegen (Begründung Pflicht) |
POST |
/v1/handwerk/aufmass/zeilen/{id}/conflict |
aufmass:write |
Standard | Idem-Key | Konflikt auflösen (both/mine/theirs/merged) |
POST |
/v1/handwerk/aufmass/bloecke |
aufmass:write |
Standard | Idem-Key | Block anlegen, Zeilen zuordnen |
POST |
/v1/handwerk/aufmass/bloecke/{id}/sign |
aufmass:sign |
Privileged | Idem-Key | Signatur hinterlegen |
POST |
/v1/handwerk/aufmass/validate-formula |
aufmass:read |
Standard | — | Formel trocken auswerten |
GET |
/v1/handwerk/aufmass/summary |
aufmass:read:<scope> |
Standard | — | Aggregation pro OZ für Rechnung (§4.5) |
POST |
/v1/handwerk/aufmass/export |
aufmass:export |
Privileged | Idem-Key | DA11/DA12-Export starten |
GET |
/v1/handwerk/aufmass/export/jobs/{jobId} |
aufmass:export |
Standard | — | Job-Status |
POST |
/v1/handwerk/aufmass/grundriss |
admin:write |
Admin | Idem-Key | Grundriss-PDF hochladen |
POST |
/v1/handwerk/aufmass/fotos/presigned-url |
aufmass:write |
Standard | — | S3-Multipart-URL (Foto-Upload) |
8.1 OpenAPI-Schema-Skizze
Abschnitt betitelt „8.1 OpenAPI-Schema-Skizze“paths: /v1/handwerk/aufmass/validate-formula: post: operationId: validateRebFormula x-werkszeit-scope: aufmass:read x-werkszeit-rate-limit: standard requestBody: required: true content: application/json: schema: type: object required: [formel, mengeneinheit] properties: formel: { type: string, example: "4,80*2,60-1,20*2,10" } variablen: { type: object, additionalProperties: { type: string } } mengeneinheit: { type: string, enum: [m, m2, m3, Stk, h, kg, t, psch, l] } rundung: { type: integer, default: 4, minimum: 0, maximum: 6 } responses: '200': content: application/json: schema: type: object properties: ok: { type: boolean } ergebnis: { type: number, format: double, example: 9.96 } ergebnisStr: { type: string, example: "9,9600" } tokenCount: { type: integer } warnungen: { type: array, items: { type: string } } '422': content: application/json: schema: type: object properties: fehlerCode: { type: string, example: "PARSE_ERR" } position: { type: integer, example: 12 } meldung: { type: string }8.2 Webhook-Events
Abschnitt betitelt „8.2 Webhook-Events“aufmass.zeile.createdaufmass.zeile.replaced(Korrektur-Zeile)aufmass.zeile.conflict(Detection)aufmass.zeile.conflict.resolvedaufmass.block.signedaufmass.export.done(Payload: jobId, format, s3Key)
9. Offline-Profil
Abschnitt betitelt „9. Offline-Profil“| Profil | Begründung | Mechanik |
|---|---|---|
| Voll-offline (Mobile Aufmaß-Erfassung) | Baustellen haben regelmäßig kein LTE; Arbeitsfluss muss komplett ohne Netz laufen | SQLite lokal; Outbox + Idempotency-Key (UUID v7 = Zeile-ID); Foto-Multipart-Upload queued; Formel-Interpreter in Dart lokal; Konflikt-Resolution bei Sync (Diff-UI) |
| Online-Review + Export (Web) | Admin-Review benötigt Server-Konsistenz | REB-Export läuft als Lambda-Job, UI blockt Export bei connectivity=none |
9.1 Konflikt-Strategie (kritisch!)
Abschnitt betitelt „9.1 Konflikt-Strategie (kritisch!)“Expliziter Nicht-LWW-Algorithmus:
- Jede Zeile hat
(lv_version_id, oz, source_device, zeile_nr).zeile_nrwird lokal pro Device vergeben,source_deviceaus Flutter-Fingerprint. - Beim Sync sendet der Client:
POST /zeilenmitIdempotency-Key: <zeile.id>(UUID v7). - Server prüft:
- Existiert
idbereits? → 200 mit bestehender Zeile (reine Idempotenz). - Existiert andere Zeile mit gleicher
(lv_version_id, oz, raum, sourceDevice != input.sourceDevice, messzeitpunkt ± 15 min)? → Conflict-Detection, Server markiert beide Zeilen mitconflict_with=<id>und antwortet 201 mit Conflict-Flag (keine Fehlermeldung, Zeile wird gespeichert — beide existieren). - Wenn nur eine existiert: Insert, 201.
- Existiert
- UI zeigt Konflikt-Diff-Screen (§6.3). User wählt Auflösung.
POST /zeilen/{id}/conflictmit Resolutionboth_kept|mine|theirs|merged+ Begründung → Audit-Log.- Bei
mine/theirs: jeweils andere Zeile bekommtreplacedReason='konflikt_aufloesung'undimmutableAt=NOW()(GoBD-append-only — nicht gelöscht, nur markiert). - Bei
merged: User bearbeitet Felder manuell, neue Zeile mitreplaces_zeile_idauf beide Vorgänger.
Warum nicht LWW? Weil zwei Monteure, die parallel offline zur selben Wand messen, wahrscheinlich unterschiedliche Wand-Abschnitte meinten — beide Zeilen sind gültig, nur der Raum-Bezug war uneindeutig. LWW würde stillschweigend einen Messwert vernichten. (LESSONS-LEARNED §4.)
9.2 Foto-Sync
Abschnitt betitelt „9.2 Foto-Sync“- Foto wird in SQLite als BLOB-Thumbnail (≤ 20 KB) + S3-Key-Platzhalter angelegt
- Outbox-Queue: Multipart-Upload gegen presigned URL (Retry mit exponential backoff, max 5)
- Nach Upload:
fotos[i].sha256gesetzt, Thumbnail bleibt als Cache - Bei Upload-Fehler (> 5×): User bekommt „Foto-Upload hängt“-Banner, kann manuell retriggern
10. Compliance-Mapping
Abschnitt betitelt „10. Compliance-Mapping“10.1 GoBD
Abschnitt betitelt „10.1 GoBD“- Append-only
aufmass_zeile: Signierte oder exportierte Zeilen sind unveränderlich (immutable_atgesetzt, RLS-Policy blockt UPDATE). Korrektur erzeugt neue Zeile mitreplaces_zeile_id+replaced_reasonPflichtfeld. - Hash-Chain: Pro Block und pro Zeile
hash_self = SHA-256(canonicalJSON). Kette an globalen Audit-Hash. - S3 Object Lock (Compliance-Mode): Fotos, Signatur-PNG, PDF-Grundrisse 10 Jahre ab
immutable_at. Schlüssels3://werkszeit-gobd/{tenantId}/aufmass/{projectId}/{blockId}/.... - Verfahrensdokumentation:
gobd-verfahrensdoku.mdAbschnitt „Aufmaß-Erfassung mobil“.
10.2 ArbZG
Abschnitt betitelt „10.2 ArbZG“Nicht zutreffend (keine Arbeitszeit-Erfassung im Aufmaß-Flow). Randnotiz: Wenn Monteur 90 min lang Aufmaß macht, sollte in der regulären Zeiterfassung parallel gestempelt sein (Verantwortung §3.1).
10.3 VOB/B und VOB/C
Abschnitt betitelt „10.3 VOB/B und VOB/C“- VOB/C (DIN 18299 ff.): Gewerke-spezifische Aufmaß-Regeln (z.B. DIN 18350 Putz-Arbeiten: „Putzflächen werden ohne Aussparungen bis 2,5 m² abgerechnet“ — das ist Geschäftslogik des Kalkulators, nicht Werkszeit-Feature). Werkszeit liefert die Roh-Zeilen, die Anwendung der Regeln erfolgt in der Abrechnung (§4.5) oder im Kalkulations-Regelwerk (§4.3).
- VOB/B §14 Abs. 1 (prüfbare Schlussrechnung): Aufmaß-Summen pro OZ müssen aus DA11/DA12 rückrechenbar sein — das stellt unser REB-Export sicher.
- VOB/B §14 Abs. 2 (gemeinsames Aufmaß): Signatur durch Bauherr/AG vor Ort ist die digitale Umsetzung des „gemeinsamen Aufmaßes“. UI-Disclaimer gem. §6.2 weist auf rechtliche Wirkung hin.
10.4 DSGVO
Abschnitt betitelt „10.4 DSGVO“- Art. 5 Datenminimierung: Signatur-Name + Rolle ist ausreichend; keine E-Mail / Geburtsdatum des Bauherrn im Standard-Flow.
- Art. 6 Rechtsgrundlage: Art. 6 Abs. 1 lit. b (Vertrag mit AG) + Abs. 1 lit. f (berechtigtes Interesse Dokumentation).
- Art. 17 Löschung: Signatur-Name kann nach Ablauf Aufbewahrungsfrist maskiert werden (
signatur_name='[gelöscht]'), Signatur-Hash bleibt. - Art. 20 Export: Bauherr kann per Portal seine signierten Zeilen + Fotos als PDF/ZIP herunterladen.
- DSFA: Prüfung nötig, wenn Geo-Tag aktiviert ist (Bewegungsprofil-Risiko Mitarbeiter); Default deaktiviert (siehe F-A-05).
10.5 BFSG / WCAG 2.2 AA
Abschnitt betitelt „10.5 BFSG / WCAG 2.2 AA“Nicht zwingend Pflicht (kein ESS-Flow, kein öffentlicher Zugang im Kern). Aber: Mobile-App-A11y (TalkBack, VoiceOver, Kontrast) ist für den Feld-Monteur praktisch — Semantics-Labels auf Stempel-/Signatur-Buttons Pflicht.
10.6 BetrVG §87(1)6
Abschnitt betitelt „10.6 BetrVG §87(1)6“Relevant bei aktivierter Geo-Tag-Funktion (F-A-05): Betriebsrat-Freischaltungs-Workflow mit dokumentierter BR-Zustimmung, Default deaktiviert. Audit-Eintrag bei Aktivierung mit BR-Beschluss-Nummer.
10.7 Weitere Spezial-Compliance
Abschnitt betitelt „10.7 Weitere Spezial-Compliance“- REB 23.003 (BMV-Bund): DA11- und DA12-Export gegen amtliche Muster-Dateien validieren (siehe C-04). Zeichensatz CP850 default (historische Kompatibilität), UTF-8 opt-in.
- §147 AO: 10-Jahre-Aufbewahrung Aufmaß-Dateien als Buchungsgrundlage.
- StGB §267 (Urkundenfälschung): Signatur-Manipulation wäre strafbar. Werkszeit’s Maßnahmen: Signatur-Hash in Audit-Log, Mobile-Gerät-Fingerprint, Zeitstempel + Geo-Tag (wenn aktiviert). Dokumentation im Verfahrensdoku-Textbaustein.
11. Edge-Cases
Abschnitt betitelt „11. Edge-Cases“| # | Szenario | Erwartetes Verhalten |
|---|---|---|
| EC-01 | Formel mit rm -rf / |
Parser-Error PARSE_ERR, Zeile wird nicht gespeichert, UI zeigt rote Inline-Markierung |
| EC-02 | Division durch 0 (10/0 in Formel) |
Laufzeit-Fehler „Division durch 0“, Zeile nicht speicherbar |
| EC-03 | Offline 4 h, 60 Zeilen erfasst, dann Sync | Outbox-Queue arbeitet Batch-weise; Konflikte pro Zeile einzeln gemeldet; Fotos hintergrund-synchronisiert |
| EC-04 | Sync-Konflikt: dieselbe OZ, beide Geräte, ± 2 min Zeitunterschied | Conflict-Detection-Heuristik (15 min + Device-Fingerprint); UI-Dialog §6.3; Begründung Pflicht |
| EC-05 | Multi-Tenant-Bleed: User versucht Aufmaß-Zeile aus Tenant B zu lesen | 404, kein Audit-Log in B |
| EC-06 | Bauherr verweigert Signatur | Block bleibt im Status entwurf, UI-Notiz „Signatur optional nachholen“. Teilaufmaß-Export trotzdem möglich (mit Warnung im PDF) |
| EC-07 | Signatur-Hash Bruch (manuelle DB-Änderung am Signatur-PNG) | Compliance-Test C-01: Re-Hash-Check schlägt Alarm, Block wird als verdacht markiert, Tech-Lead-Eskalation |
| EC-08 | Formel mit 2.000 Zeichen (Copy-Paste-Angriff) | Token-Limit 500, Parser bricht mit Meldung „Formel zu komplex (max 500 Tokens)“ ab |
| EC-09 | Foto-Upload bricht permanent (kein Netz 3 Tage) | UI-Banner „1 Foto wartet auf Upload“, User kann manuell retriggern; Zeile bleibt nutzbar mit Thumbnail-only |
| EC-10 | LV-Version wird nach Aufmaß-Erfassung geändert (z.B. durch Nachtrag, neue Version v4) | Aufmaß-Zeilen bleiben an ursprünglicher v3 gebunden; Rechnungs-Engine muss entscheiden, wohin sie zählt |
| EC-11 | Grundriss-Pin außerhalb der PDF-Seite | Validierung: Pin-Koordinaten normalisiert 0..1 pro Seite; ungültige Koords werden auf Rand geklemmt |
| EC-12 | EXIF-GPS der Foto-Datei weicht > 500 m von geo_lat/lon der Zeile ab |
Audit-Warnung (nicht blockierend), Badge „GPS-Abweichung“ in Review-Tabelle |
| EC-13 | REB-DA12-Export mit Zeilen, die Kommentar-Zeichen # im Raum-Feld enthalten |
Escape bei Serialisierung, Round-Trip-Test |
| EC-14 | Plan-Limit Starter: Kunde hat GAEB (V1) nicht gekauft → LV-Version fehlt → Aufmaß-Zugriff | Modul-Gate module.handwerk.aufmass hat harte Abhängigkeit auf module.handwerk.gaeb; UI blockiert Aufmaß ohne LV, zeigt Upgrade-Modal |
12. Akzeptanzkriterien (Gherkin)
Abschnitt betitelt „12. Akzeptanzkriterien (Gherkin)“Funktionalität: Mobiles Aufmaß nach REB 23.003
Hintergrund: Angenommen ein Tenant "shk-gebruder-schmidt" mit aktivem Modul-Gate "module.handwerk.aufmass" Und ein Projekt "Sanierung Schule A" mit beauftragter LV-Version v3 Und die OZ "01.02.030" ("Putz-Innenwand abreißen", ME=m², Menge=128,50) Und ein Bauleiter "Sabine" mit Rolle "Bauleitung" und Scope "team"
Szenario: Happy Path — Offline-Erfassung mit Formel und Signatur Angenommen Sabines Tablet ist offline Wenn sie die Aufmaß-App öffnet und OZ "01.02.030" wählt Und eine neue Zeile für Raum "Klassenzimmer 1.04" anlegt Und die Formel "4,80*2,60-1,20*2,10" eingibt Dann wird das Ergebnis mit 9,96 m² (ROUND_HALF_EVEN, 4 NK) live angezeigt Und ein Foto wird lokal an die Zeile gehängt Und die Zeile wird in SQLite gespeichert mit source_device="device:UUID"
Wenn Sabine 5 weitere Zeilen erfasst (gesamt 64,20 m²) Und den Bauherrn "Herbert Özdemir" signieren lässt Dann wird ein aufmass_block mit status="signiert" lokal angelegt Und signatur_hash ist SHA-256 über png+name+rolle+timestamp
Wenn das Tablet wieder Netz hat Dann synchronisieren 6 Zeilen + 1 Block mit Idempotency-Key ohne Duplikate Und die Fotos werden per S3-Multipart hochgeladen Und ein Audit-Log-Eintrag "aufmass.block.signed" wird erzeugt
Szenario: Grenzfall — Formel-Validator-Fehler Wenn Sabine die Formel "4,80*(2,60-" eingibt Dann zeigt der Interpreter den Fehler "PARSE_ERR: unerwartetes Dateiende nach '('" Und das Eingabefeld wird rot markiert Und die Zeile kann nicht gespeichert werden
Szenario: Grenzfall — Sync-Konflikt zwei Geräte parallel offline Angenommen Hannes (Monteur) hat offline für OZ "01.02.030" eine Zeile "001" mit 12,48 m² erfasst Und Sabine (Bauleitung) hat offline für dieselbe OZ eine Zeile "001" mit 9,96 m² erfasst Beide messzeitpunkt innerhalb 4 min Wenn beide Geräte synchronisieren Dann werden beide Zeilen als "conflict_with" verknüpft gespeichert Und beide Clients zeigen den Konflikt-Diff-Screen Und Sabine wählt "Beide behalten (als A/B)" mit Begründung "getrennte Wandabschnitte" Dann werden die Zeile-Nummern automatisch auf "001A" und "001B" umnummeriert Und ein Audit-Log-Eintrag "aufmass.zeile.conflict.resolved" wird mit der Begründung erzeugt
Szenario: Grenzfall — Versions-Migration LV ändert sich nach Aufmaß-Erfassung Angenommen Aufmaß-Zeile für OZ "01.02.030" (LV v3) existiert und ist signiert Wenn der Kalkulator einen Nachtrag auf OZ "01.02.030.N1" in LV v4 anlegt Dann bleibt die bestehende Aufmaß-Zeile an v3 gebunden Und die Summary-API "/v1/handwerk/aufmass/summary?lvVersionId=v3" zeigt sie weiterhin Und die Rechnungs-Engine kann beide Versionen korrekt zuordnen
Szenario: Grenzfall — Multi-Tenant-Isolation Angenommen ein zweiter Tenant "musterbetrieb-maler" Wenn Sabine (Tenant "shk-gebruder-schmidt") per API eine Aufmaß-Zeile-ID aus "musterbetrieb-maler" anfragt Dann antwortet die API mit 404 Und es entsteht KEIN Audit-Log-Eintrag in "musterbetrieb-maler"
Szenario: Grenzfall — REB-DA12-Export gegen amtliches Muster Angenommen 62 Aufmaß-Zeilen sind signiert und exportbereit Wenn Sabine den Export als "DA12, CP850, CR-LF" startet Dann erzeugt die Lambda eine Datei mit dem REB-23.003-Header und genau 62 Datensätzen Und die Compliance-Test-Suite (C-04) validiert diese Datei gegen das amtliche Muster mit 0 Abweichungen Und jeder Datensatz enthält OZ + Formel + Ergebnis + Raum Und ein Audit-Log-Eintrag "aufmass.export.done" wird erzeugt13. Test-Cases
Abschnitt betitelt „13. Test-Cases“13.1 Unit-Tests (Dart + TypeScript)
Abschnitt betitelt „13.1 Unit-Tests (Dart + TypeScript)“- U-01 — Formel-Parser: 50 Referenz-Formeln aus REB-Muster-Dokumentation, beide Sprachen liefern identisches Ergebnis (Property-Test, ±0 in 6. Nachkommastelle)
- U-02 — Formel-Injektion: 30 Angriffs-Strings (
eval,system,import, SQL-Injection-Patterns) — allePARSE_ERR - U-03 — Rundungs-Property-Test: 100.000 Zufalls-Tripel
(l, b, abz)— 4 NK HALF_EVEN identisch zwischen Dart/TS/Postgres - U-04 — OZ-Scope-Filter:
lv_knotenfilter fürlv_version.phase=='auftrag' AND immutable_at IS NOT NULLliefert keine Entwürfe - U-05 — Hash-Chain: Mutation an
aufmass_zeile.ergebnisnach Setzen vonimmutable_atbrichthash_self-Kette — Detector erkennt
13.2 Widget-/Component-Tests (Flutter)
Abschnitt betitelt „13.2 Widget-/Component-Tests (Flutter)“- W-01 — Formel-Input: Eingabe erzeugt Live-Ergebnis innerhalb 50 ms (Stopwatch-Assertion)
- W-02 — Signatur-Pad: Touch-Events werden zu SVG-Path konvertiert, Hash stabil
- W-03 — Foto-Pin auf Grundriss: Tap auf PDF-Tile erzeugt normalisierte Koords 0..1
- W-04 — Golden-Tests: Aufmaß-Detail (Light/Dark/de-DE), Konflikt-Diff-Screen, Signatur-Pad
- W-05 — Keyboard-Input auf Numeric-Pad: Komma und Punkt beide akzeptiert
13.3 Integrations-Tests (Backend)
Abschnitt betitelt „13.3 Integrations-Tests (Backend)“- I-01 — Multi-Tenant-Isolation (Pflicht): Tenant A legt Zeile an, Tenant B bekommt 404 auf allen Pfaden (GET, PATCH, POST-replace, POST-conflict, DELETE, export)
- I-02 — RLS-Scope
own: Monteur sieht nur eigene Zeilen, Bauleiter (team) sieht Team-Zeilen - I-03 — Idempotenz: POST /zeilen mit identischem Idem-Key → 2. Request liefert bestehende Zeile, kein 2. DB-Insert
- I-04 — Konflikt-Detection: Zwei POSTs mit gleicher (lv_version_id, oz, raum) ±15 min von verschiedenen Devices → conflict_with auf beiden
- I-05 — Append-Only: UPDATE auf
aufmass_zeile.ergebnisnach Signatur → 409, kein DB-Write - I-06 — LV-Gate: POST /zeilen auf LV-Version mit
phase!='auftrag'→ 422LV_NOT_AUTHORIZED
13.4 E2E-Tests
Abschnitt betitelt „13.4 E2E-Tests“- E-01 — Patrol iOS+Android + Playwright Web: Happy Path §12, Offline-Switch simuliert (Patrol
network off → on) - E-02 — Konflikt-Flow: 2 Emulatoren gleichzeitig, gleiche OZ, Sync, Diff-Screen-Flow
- E-03 — Foto-Upload bei wiederkehrender Netzstörung (Patrol-Net-Throttle)
- E-04 — Visual-Regression: 8 Golden-Screens (Light/Dark × 4 Haupt-Flows)
- E-05 — A11y: axe-core-Lauf auf Web-Review-Tabelle; Flutter-Semantics-Tests auf Mobile-Erfassung
13.5 Compliance-Tests (apps/api/test/compliance/)
Abschnitt betitelt „13.5 Compliance-Tests (apps/api/test/compliance/)“- C-01 — Hash-Chain-Integrität (Blöcke + Zeilen)
- C-02 — Signatur-Hash-Integrität: Manipulation Signatur-PNG → Detection
- C-03 — GoBD-Append-Only: automatische Prüfung, dass keine UPDATE-Statements auf
aufmass_zeilemitimmutable_at IS NOT NULLlaufen können (INFORMATION_SCHEMA + Permission-Tests) - C-04 — REB-23.003-Referenz-Muster: Import+Export-Round-Trip mit amtlichen BMV-Bund-Mustern (3 Muster aus REB-Spezifikations-Anhang) — bit-identisch (modulo CR-LF-Canonical)
- C-05 — Formel-Evaluations-Property-Test (Cross-Sprache Dart/TS)
- C-06 — Foto-EXIF-Stripping (bevor S3-Upload entfernt: GPS-EXIF wird in separate Spalte extrahiert und aus Datei entfernt, damit DSGVO-Datenminimierung greift)
13.6 Flakiness-Schutz
Abschnitt betitelt „13.6 Flakiness-Schutz“- Lokaler 20× Re-Run der neuen E2E-Tests grün
- Konflikt-Szenario E-02 läuft deterministisch (kontrollierte Zeit-Stubs)
14. Nicht-Ziele
Abschnitt betitelt „14. Nicht-Ziele“| Nicht-Ziel | Begründung |
|---|---|
| CAD-Import (DWG, DXF) mit Auto-Vermessung | Viel zu teuer (Autodesk-Library-Lizenz), nicht die Zielgruppe. PDF-Grundriss mit manuellen Pins genügt. |
| Automatisches Raum-Erkennen aus Foto / AR-Vermessung | Reife nicht ausreichend für verbindliche Abrechnung. Ggf. V3 nach Benchmarking. |
| Komplexe Gewerke-spezifische Regeln (DIN 18350 ff.) automatisiert anwenden | Das ist Aufgabe des Kalkulators / der Abrechnung — Werkszeit liefert Roh-Zeilen, nicht die Regel-Engine. |
| Aufmaß-Historie mehrerer LV-Versionen kombinieren | Komplex und fehlerträchtig; in V1 bleibt Aufmaß-Zeile an einer LV-Version. Rechnungs-Engine aggregiert. |
| Kollaboratives Live-Editing (Google Docs-artig) | Overkill für 1–3 Bauleiter pro Baustelle. Sync-Konflikt-Auflösung ist der gewählte Pfad. |
| Integration externer REB-Aufmaß-Tools (MENG, iTWO Messen) | V2 bei Bedarf. Zielgruppe nutzt diese Tools nicht primär. |
| Blockchain für Signatur-Nachweis | Buzzword, kein rechtlicher Mehrwert — SHA-256-Hash-Chain + Object Lock ist GoBD-konform und ausreichend. |
| Video-Aufzeichnung Messung | Speicher-Overhead, DSGVO-riskant; Fotos genügen. |
15. Risiken & offene Annahmen
Abschnitt betitelt „15. Risiken & offene Annahmen“| Risiko / Annahme | Impact | Wahrscheinlichkeit | Gegenmaßnahme |
|---|---|---|---|
| Formel-Interpreter-Divergenz Dart ↔ TS (Floating-Point-Verhalten) | hoch | mittel | decimal.Decimal (arbitrage Präzision) statt IEEE-754; Property-Tests als CI-Gate; Server ist Source of Truth bei Divergenz |
| Signatur-rechtliche Anerkennung (EES/AES-Frage) | mittel | niedrig | Bauherr-Signatur ist EES (einfache elektronische Signatur) gem. eIDAS — für VOB/B §14 Abs. 2 mit gleichzeitigem Foto + Zeitstempel + Geo-Tag ausreichend. Rechtsgutachten vor V1 eingeplant. |
| Offline-Konflikt-Diff-UX ist für Monteure unverständlich | mittel | hoch | User-Testing mit 3 Monteuren vor V1-Freeze; Fallback: Bauleitung löst Konflikte im Web, Monteur sieht nur Hinweis |
| PDF-Grundriss-Upload (größere Pläne > 100 MB) | mittel | mittel | Chunk-Upload + serverseitige Tile-Generation via Lambda; Limits dokumentiert |
| BMV-REB-Muster-Dateien kostenpflichtig / NDA | niedrig | niedrig | Kostenlos beim BMV beziehbar; Klärung vor Sprint-Start |
| Kompatibilität mit AG-System (iTWO Validate): unser DA12-Export wird abgelehnt | hoch | mittel | Muster-Export gegen RIB iTWO Import im Monat 3 testen, Schematron-Profil anpassen |
| Bluetooth-Laser-Disto Bluetooth-Pairing-Stabilität | niedrig | mittel | Feature V1.5, Prototyping-Phase mit 2 Geräten (Leica Disto D2, Bosch GLM 50-27C) |
16. Abhängigkeiten
Abschnitt betitelt „16. Abhängigkeiten“- Vorbedingung:
handwerk/03-gaeb-lv— LV-Version (beauftragt) als Datenquellekern/11-rollen-rls— Rolle „Bauleitung“, Scope-Modellkern/12-audit-log— Hash-Chain-Infrastrukturkern/offline-outbox— Idempotency-Key, Outbox-Queue
- Schnittstelle zu:
handwerk/05-bau-abrechnung.md— Aufmaß-Summary liefert Mengen für Rechnungspositionenhandwerk/04.2-nachtrag— OZ-Bezug + Aufmaß-Mengen → Nachtrags-Anker- Bautagebuch (§4.1) — Foto-Referenz auf OZ-Ebene
- Wird konsumiert von:
- Schlussrechnung (§4.5) — prüfbare Menge je OZ
- Soll/Ist-Dashboard Kalkulator (§4.3)
- Nachtrags-Modul
17. Referenzkunde-Slot
Abschnitt betitelt „17. Referenzkunde-Slot“Status: TBD — zu klären mit Sales.
DOR §1.1.1 verlangt: „Mindestens ein zahlender Design-Partner ist namentlich dokumentiert, der das Feature konkret fordert.“
Kandidaten-Profile:
- 20–100-MA-Bauunternehmen (Hochbau, SHK, Trockenbau) mit öffentlichen AG (Stadt, DB, Landesbau)
- Betrieb mit bereits vorhandener mobiler IT (iPads/Android-Tablets im Feld)
- Klarer Pain-Punkt: „Zollstock + Notizblock + Excel“ oder „iTWO-Aufmaß zu teuer“
Validierungs-Fragen:
- Wie viele Aufmaß-Zeilen erfasst Ihr Betrieb pro Monat? Wie lange dauert die Büro-Nachbereitung?
- Welche AG verlangen DA11/DA12-Format? Gab es 2024/2025 Rechnungs-Ablehnungen wegen fehlender REB-Daten?
- Wie oft kommt es zum Aufmaß-Streit mit Bauherrn? Was würde eine Vor-Ort-Signatur wert sein?
- Bereit für 30-Tage-Beta mit 1 Bauleiter auf 1 Baustelle und einem Probe-Export an einen echten AG?
18. Senior-Berater-Empfehlung
Abschnitt betitelt „18. Senior-Berater-Empfehlung“18.1 Build-Sequenz
Abschnitt betitelt „18.1 Build-Sequenz“- Datenmodell + RLS + Multi-Tenant-Test (wie immer zuerst). Inkl. Hash-Chain-Tabellen.
- Formel-Interpreter (
packages/reb_formulain Dart + TS-Port) — mit Property-Tests bevor UI gebaut wird. Das ist die kritischste Single-Piece-of-Logic. - Backend-API + OpenAPI → Dart-Client-Generierung.
- Mobile-UI Kern-Flow: OZ wählen · Zeile anlegen · Formel tippen · Foto · Speichern · Block + Signatur · Sync.
- Offline-Outbox-Integration (Idempotency-Key, Foto-Multipart, Konflikt-Detection-Client-Side).
- Web-Review-Tabelle + REB-Export-Lambda (DA11 + DA12, CP850-Serializer, C-04-Muster-Tests).
- Grundriss-PDF-Upload + Tile-Generation + Pin-Flow.
- Konflikt-Diff-UI Mobile + Web (die UX-Risiko-Komponente, nimmt mindestens 3 Iterationen).
- Soll-/Ist-Dashboard als Zusatz, nach dem Kern-Flow.
- Compliance-Tests C-01…C-06 vollständig, Gate vor V1-Freeze.
- E2E-Suite + Doku-PR mit auto-generierten Screenshots.
18.2 Risiko-Reihenfolge (wenn Zeitnot)
Abschnitt betitelt „18.2 Risiko-Reihenfolge (wenn Zeitnot)“- Formel-Interpreter + Multi-Tenant + Hash-Chain + Konflikt-Auflösung = nicht verhandelbar.
- Zuerst einsparbar: Bluetooth-Laser (V1.5), Aufmaß-Vorlagen (V1.5), Bauherr-Portal (V1.5), Geo-Tag (opt.), Soll-/Ist-Dashboard-Visualisierung (Tabelle allein reicht anfangs).
18.3 Stop-the-Bus-Triggers
Abschnitt betitelt „18.3 Stop-the-Bus-Triggers“- Multi-Tenant-Bleed in irgendeinem Test.
- Hash-Chain-Bruch in C-01 / C-02.
- Formel-Interpreter-Divergenz Dart↔TS > 1 ppm.
- REB-DA12-Muster-Round-Trip scheitert für ein amtliches Muster.
- Signatur-Hash ist nicht reproduzierbar nach Re-Serialisierung.
18.4 Was uns 2027 dankbar macht
Abschnitt betitelt „18.4 Was uns 2027 dankbar macht“packages/reb_formulaals kleines, stabiles, property-getestes Paket: Der nächste Anwendungsfall (Kalkulations-Formeln in §4.3, ggf. Nachtrags-Automatik) konsumiert es ohne Neuaufbau.- Konflikt-First statt LWW: Wir werden nicht in 6 Monaten panisch rückwärts-migrieren, wenn der erste Kunde merkt, dass Messungen still gelöscht wurden.
- Signatur-Hash-Chain: spart vor Gericht Tage an Nachweis-Arbeit, falls ein Bauherr behauptet, er habe nie unterschrieben.
- Voll-offline von Tag 1: Jeder Feature-Nachrüstungs-Versuch für Offline-Zwang ist teurer als der Initialaufwand. Die Alt-App hat das genau umgekehrt gelernt (LESSONS-LEARNED §4).
Letzte Aktualisierung: 2026-04-19. Änderungen erfordern PR-Review durch Tech-Lead + PO.
Für Entwickler — API-Endpoints14
| Methode | Pfad | Auth | Zweck |
|---|---|---|---|
| POST | /v1/handwerk/aufmass/bloecke | bearerAuth | Aufmaß-Block anlegen |
| POST | /v1/handwerk/aufmass/bloecke/{id}/sign | bearerAuth | Bauherr-Signatur hinterlegen (VOB/B §14 Abs. 2) |
| POST | /v1/handwerk/aufmass/export | bearerAuth | REB-Export DA11/DA12 starten [out-of-scope v1 → 501] |
| GET | /v1/handwerk/aufmass/export/jobs/{jobId} | bearerAuth | Export-Job-Status [out-of-scope v1 → 501] |
| POST | /v1/handwerk/aufmass/fotos/presigned-url | bearerAuth | S3-Foto-Presigned-URL [out-of-scope v1 → 501] |
| POST | /v1/handwerk/aufmass/grundriss | bearerAuth | Grundriss-PDF hochladen [out-of-scope v1 → 501] |
| POST | /v1/handwerk/aufmass/reb-validate | bearerAuth | REB-Formel validieren (Variante reb-validate). |
| GET | /v1/handwerk/aufmass/summary | bearerAuth | Aufmaß-Aggregation pro OZ (Rechnungs-Import §4.5) |
| POST | /v1/handwerk/aufmass/validate-formula | bearerAuth | REB-Formel validieren und auswerten (§5.6 Spec) |
| GET | /v1/handwerk/aufmass/zeilen | bearerAuth | Aufmaßzeilen abrufen (gefiltert) |
| POST | /v1/handwerk/aufmass/zeilen | bearerAuth | Aufmaßzeile anlegen (Offline-First, Idempotency-Key = Zeile-UUID v7) |
| GET | /v1/handwerk/aufmass/zeilen/{id} | bearerAuth | Aufmaßzeile (Detail) |
| POST | /v1/handwerk/aufmass/zeilen/{id}/conflict | bearerAuth | Sync-Konflikt auflösen (both_kept|mine|theirs|merged) |
| POST | /v1/handwerk/aufmass/zeilen/{id}/replace | bearerAuth | Korrektur-Zeile anlegen (GoBD append-only) |