SOKA-Bau-Meldungen — Winterbeschäftigungs-Umlage, ZVK, Winterausfallgeld §101 SGB III mit DWD-Wetter-Nachweis
Feinkonzept · SOKA-Bau — Winterbeschäftigungs-Umlage, ZVK & Winterausfallgeld §101 SGB III
Abschnitt betitelt „Feinkonzept · SOKA-Bau — Winterbeschäftigungs-Umlage, ZVK & Winterausfallgeld §101 SGB III“Modul: Handwerk · Quelle: FUNKTIONSUMFANG §4.9 · Roadmap: V2 (Monat 15–22) Stand: 2026-04-19 · Branch:
feinkonzepte/v0.1
1. Header & Metadaten
Abschnitt betitelt „1. Header & Metadaten“feature_id: handwerk/09-soka-bautitle: SOKA-Bau-Meldungen — Winterbeschäftigungs-Umlage, ZVK, Winterausfallgeld §101 SGB III mit DWD-Wetter-Nachweisfunktionsumfang_ref: §4.9roadmap_horizont: V2plattformen: mobile: aus # reine Backoffice-Mechanik, Wettererfassung auf Baustelle siehe §4.1 Bautagebuch web: vollständig mit-Bulk desktop: aus # Web deckt alle Bulk-Use-Casesowner_rolle: [Admin, Buchhaltung, Bauleitung]modul_gate_flag: module.handwerk.sokacompliance_flags: gobd: true # Beitragsmeldungen + Bescheide sind aufbewahrungspflichtig arbzg: false vob: false dsgvo: true # SV-Nummer, Lohngruppen, Ausfallzeiten sind personenbezogen bfsg: false # Admin-UI, kein ESS betrvg: false stvg: false weitere: [SOKA-Bau TV, §101 SGB III, §175 SGB III, TV Mindestlohn Baugewerbe, Saison-Kurzarbeitergeld]referenzkunde: status: TBD name: "zu klären mit Sales — Kandidat: Bauunternehmen Schmidt GmbH & Co. KG (Koblenz, 80 MA, Hochbau)" quelle: Sales-Call-Notiz 2026-02-27estimate_eng_tage: 22abhängigkeiten: - kern/10-datev-export # SOKA-Beiträge fließen in DATEV-Lohnlauf - kern/01-zeiterfassung # Witterungsbedingte Ausfallstunden werden als Zeitart erfasst - handwerk/01-bautagebuch # DWD-Wetterdaten je Baustelle werden dort konsumiert2. Kontext & Problem
Abschnitt betitelt „2. Kontext & Problem“Marktrealität (DE-Bau). Jeder gewerbliche Bau-Arbeitnehmer in Deutschland ist tarif-verpflichtet bei der SOKA-Bau (Sozialkasse der Bauwirtschaft, Wiesbaden) gemeldet — einschließlich Urlaubskasse (ULAK), Berufsbildung (BBF) und Zusatzversorgungskasse (ZVK). Die Meldepflichten sind dicht:
- Winterbeschäftigungs-Umlage (WBU): Seit der Gesetzes-Reform 2006 tragen Arbeitgeber und Arbeitnehmer gemeinsam die Finanzierung der Winterbeschäftigungs-Förderung. Aktueller Satz (Tarifrunde 2025): 1,2 % AG-Anteil + 0,8 % AN-Anteil vom Brutto-Lohn, monatliche Meldung bis zum 15. des Folgemonats an SOKA-Bau.
- ZVK-Beitrag (Zusatzversorgungskasse des Baugewerbes): tarifrechtlich verpflichtende betriebliche Altersversorgung, ca. 18,0 % vom Brutto (AG-finanziert, regional variabel West/Ost), monatliche Meldung.
- ULAK-Urlaubsvergütung: 14,45 % Brutto (West) / 13,85 % (Ost) — die SOKA-Bau zahlt dem AN den Urlaubslohn direkt, AG meldet und beitragszahlt.
- Berufsbildungs-Umlage (BBF): 2,5 % (West) / 2,1 % (Ost) — finanziert überbetriebliche Ausbildung.
- Winterausfallgeld §101 SGB III (Saison-Kurzarbeitergeld): Für die Schlechtwetterzeit vom 01.12. bis 31.03. kann der AG für witterungsbedingten Arbeitsausfall Saison-KuG bei der Bundesagentur für Arbeit beantragen. Voraussetzung: nachweisbar witterungsbedingt (Temperatur ≤ −1 °C, Niederschlag, Windgeschwindigkeit, die objektive Arbeit unmöglich machen). DWD-Wetterdaten dienen als amtlicher Nachweis.
- TV Mindestlohn Baugewerbe: Lohngruppen 1–6 (West) und 1–4 (Ost), regional unterschiedlich, mit jährlicher Tarif-Anpassung (typischerweise April oder Oktober). Verstoß = SOKA-Meldung + Bußgeld durch Zoll (FKS).
Heute wird das in mittelständischen Bau-Betrieben (50–200 MA) mit einer Mischung aus DATEV-Lohnlauf + manueller SOKA-Excel + telefonischer Schlechtwetter-Anzeige beim Bauleiter abgewickelt. Fehlerquote typisch: 1–2 verpasste Meldungen pro Jahr, 3–8 falsche Eingruppierungen pro Tarifrunde, Winterausfallgeld wird in 40 % der belegbaren Fälle nicht beantragt, weil „niemand die DWD-Daten zusammenkriegt“.
Schmerzpunkt der Alt-App (../../LESSONS-LEARNED.md §5). Die Alt-App kannte SOKA-Bau gar nicht. Für Bau-Betriebe war damit der komplette Monatsabschluss ein separater Excel-Prozess neben der Zeiterfassung. Ergebnis: Kein Bau-Mittelständler war in der Alt-App ein ernsthafter Kunde.
Erwarteter Outcome.
- SOKA-Meldung wird aus Werkszeit heraus erzeugt (keine parallele Excel).
- Winterausfallgeld-Anträge mit DWD-Datennachweis als PDF-Beleg pro Kalendertag + Baustelle: 3–5 zusätzliche bewilligte Anträge pro Saison pro 50-MA-Betrieb (je nach Region 2.000–8.000 € Mehr-Erlös aus bereits eingezahltem U1-Umlage-Topf).
- Tarif-Mindestlohn-Unterschreitung wird pre-commit detektiert → 0 Zoll-Bußgelder statt branchenüblicher 1 pro 3 Jahre.
3. Personas & Rollen
Abschnitt betitelt „3. Personas & Rollen“| Rolle | Aktion | Scope | Plattform |
|---|---|---|---|
| Admin | SOKA-Tenant-Setup: BG-Nr., ZVK-Nr., Kassen-IDs hinterlegen | all | 🌐 |
| Buchhaltung | Monatsmeldung prüfen + freigeben, ELSTER-/SOKA-Export auslösen, Winterausfallgeld-Antrag erstellen | all | 🌐 |
| Bauleitung | Witterungsbedingten Arbeitsausfall anzeigen, DWD-Nachweis zuordnen | team | 🌐 |
| HR | Lohngruppen-Eingruppierung pflegen, Tarif-Anpassungen anwenden | all | 🌐 |
| Mitarbeiter | — (implizit durch Zeitart „Schlechtwetter“ in der Zeiterfassung betroffen) | own | 📱 |
Persona-Skizzen:
- Thomas Schmidt (Geschäftsführer, 58, Hochbau 80 MA) — schickt Lohn-Frau seit 30 Jahren in die SOKA-Rundschreiben-Pflege. Will das nicht mehr.
- Frau Dr. Erika Vogel (Steuerberaterin, 61) — macht DATEV-Lohnlauf für 14 Bau-Betriebe. SOKA-Meldung ist ihr wöchentlicher Pain.
- Jens Walter (Bauleiter, 47) — sitzt morgens im Baucontainer, schickt WhatsApp an die Kollegen „heute keine Arbeit, −4 °C“.
4. User-Stories
Abschnitt betitelt „4. User-Stories“US-01 [V2] Als Buchhaltung möchte ich, dass am Monatsende automatisch eine SOKA-Meldung mit WBU, ZVK, ULAK und BBF pro Bau-MA generiert wird, die ich nur noch freigeben muss.
US-02 [V2] Als Bauleitung möchte ich für einen Schlechtwetter-Tag die DWD-Daten meiner Baustelle als Nachweis automatisch abrufen und an die Ausfallstunden anhängen, damit die Buchhaltung Winterausfallgeld beantragen kann.
US-03 [V2] Als Buchhaltung möchte ich den Saison-KuG-Antrag §101 SGB III mit DWD-Beleg-PDF als Bundle für die Bundesagentur für Arbeit exportieren können.
US-04 [V2] Als HR möchte ich bei jeder Tarifrunde die neuen Mindestlöhne pro Lohngruppe zentral pflegen und eine Vorab-Prüfung fahren, welche Mitarbeiter unter dem neuen Satz liegen würden.
US-05 [V2] Als Admin möchte ich für unseren Tenant den regionalen Geltungs- bereich (West/Ost) korrekt hinterlegen, damit alle Sätze stimmen.
US-06 [V2] Als Buchhaltung möchte ich in einem Monatsabschluss-Workflow SOKA-Meldung, DATEV-Lohnlauf und Zahlungsanweisung als zusammenhängenden Prozess haben — nicht drei getrennte Tools.
US-07 [V2.5] Als Geschäftsführer möchte ich eine Übersicht, wie viel Winterausfallgeld wir im vergangenen Winter bezogen haben und welches Potenzial wir durch fehlende Nachweise verloren haben.5. Funktionale Anforderungen
Abschnitt betitelt „5. Funktionale Anforderungen“5.1 Mobile App (📱)
Abschnitt betitelt „5.1 Mobile App (📱)“— nicht zutreffend, weil SOKA-Backoffice-Mechanik. Der Bezug zur Mobile-App erfolgt ausschließlich indirekt über die Zeitart „Schlechtwetter“ im Zeiterfassungs-Feature (../kern/01-zeiterfassung.md §4.9) — dort wird von Hannes K. eingebucht, hier ausgewertet.
5.2 Web-App (🌐)
Abschnitt betitelt „5.2 Web-App (🌐)“- F-W-01 — SOKA-Tenant-Konfiguration. BG-Nummer (Sozialversicherung Bau), ZVK-Nr., regionaler Geltungsbereich (West = PLZ-Präfix 0–4, 6–9 gemäß TV West; Ost = PLZ-Präfix 01–10), Betriebsstättenzuordnung bei mehreren Standorten.
- F-W-02 — Lohngruppen-Katalog (LG1–LG6 West, LG1–LG4 Ost) mit aktuellem Tarif-Mindestlohn, Stichtags-Versionierung (TV Mindestlohn Baugewerbe hat typisch 01.04. als Wirksamkeits-Stichtag). Historie der Sätze append-only.
- F-W-03 — Mitarbeiter-Eingruppierung pro Bau-MA: LG, Betriebsstätte, Eintrittsdatum, SOKA-Meldung aktiv/inaktiv (Geringfügige, Azubis, Angestellte fallen anders).
- F-W-04 — Monatsabschluss-Workflow (vorläufig → Korrektur → Freigabe → SOKA-Lieferung → DATEV-Lieferung → Sperre):
- Stundenlauf aus Zeiterfassung (Brutto, inkl. Schlechtwetter-Ausfallzeiten).
- WBU (1,2 % AG + 0,8 % AN) berechnet pro Bau-MA.
- ZVK (regional) berechnet.
- ULAK + BBF berechnet.
- Tarif-Mindestlohn-Prüfung (Soll-vs-Ist je LG).
- Monatsmeldung-PDF + XML-Datei (SOKA-Bau-eXtra-Format).
- Elektronische Lieferung über SOKA-Bau-Upload-Portal (UPM) mit AG-Signatur.
- F-W-05 — Witterungs-Protokoll pro Baustelle pro Kalendertag: DWD-Daten automatisch gezogen (Temperatur, Niederschlag, Windgeschwindigkeit), Arbeitsausfall-Entscheidung per Checkbox, Kommentar des Bauleiters.
- F-W-06 — DWD-Nachweis-PDF generiert für jeden Schlechtwetter-Tag: Stations-Nr., Messwerte 06:00 / 09:00 / 12:00 / 15:00 Ortszeit, amtliche Quelle „Deutscher Wetterdienst (DWD) Open Data“ mit Datum des Abrufs + SHA-256-Hash des Roh-Datensatzes.
- F-W-07 — Saison-KuG-Antragsassistent §101 SGB III. Zeitraum-Auswahl (01.12.–31.03.), Baustellen-Auswahl, Ausfall-Tage aus F-W-05, Export als Bundle (PDF-Antrag + DWD-Nachweise + Lohnabrechnungs-Auszug) für BA-Kiehl-Formular Kug 107 (papierhaft einzureichen) bzw. BA-Online-Kanal.
- F-W-08 — Tarif-Vorabprüfung. Vor Tarifrunden-Wirksamkeit: Simulations-Lauf „Wer läge nach dem neuen Satz unter Mindestlohn?“ — Liste mit Handlungs-Empfehlung (Eingruppierung heben / Stundensatz anpassen).
- F-W-09 — Jahresmeldung SOKA. Zum 31.01. des Folgejahres Jahresabschluss-Paket für SOKA-Bau (Gesamtbrutto, alle Beiträge). PDF + XML.
- F-W-10 — Beitragsbescheid-Inbox. Rückläufer von SOKA-Bau (Bescheide, Korrektur-Aufforderungen) werden als Aufgaben in Werkszeit getrackt (V2.5).
5.3 Cross-Plattform (🔄)
Abschnitt betitelt „5.3 Cross-Plattform (🔄)“- F-X-01 — Audit-Log-Eintrag pro Monatsmeldung, pro DWD-Datenabruf, pro Saison-KuG-Antrag.
- F-X-02 — Webhooks
soka.monthly_report.generated,soka.monthly_report.delivered,wettergeld.application.exported.
5.4 Admin-Konfiguration
Abschnitt betitelt „5.4 Admin-Konfiguration“- F-A-01 — Tarif-Eskalations-Stichtage im Kalender: 30 Tage vor TV-Stichtag erinnert Werkszeit automatisch („Tarifrunde TV Bau 01.04.2027 rückt näher“).
- F-A-02 — Betriebsrat-Freigabe nicht nötig (keine Leistungs-/Verhaltenskontrolle), aber Aktivierung erfordert Vier-Augen-Prinzip (Admin + Buchhaltung), weil lohnrelevant.
- F-A-03 — SOKA-Lieferweg auswählen: UPM-Upload (Webportal), SFTP-Batch (Großkunden), papierhaft (nur Fallback).
5.5 Roadmap-Schichtung
Abschnitt betitelt „5.5 Roadmap-Schichtung“| Anforderung-ID | MVP | V1 | V1.5 | V2 | V2.5 |
|---|---|---|---|---|---|
| F-W-01 bis F-W-09, F-X-01, F-X-02, F-A-01, F-A-02, F-A-03 | ✅ | ||||
| F-W-10 (Bescheid-Inbox), Saison-KuG Auswertung US-07 | ✅ |
6. Mockups & Flows
Abschnitt betitelt „6. Mockups & Flows“HTML-Hero-Mockup: 09-soka-bau.html — Mobile (Zeitart Schlechtwetter aus Bauleiter-Perspektive Jens W.) + Web (Monatsmeldung Januar 2026 aus Sicht von Buchhaltung Frau Müller) — Tenant shk-gebruder-schmidt, 80 MA, Wintermonat Januar 2026.
6.1 Web · SOKA-Monatsmeldung Januar 2026
Abschnitt betitelt „6.1 Web · SOKA-Monatsmeldung Januar 2026“┌──────────────────────────────────────────────────────────────────────────────────────────┐│ Werkszeit · shk-gebruder-schmidt Frau Müller · Buchhaltung [Profil ▾] │├──────────────────────────────────────────────────────────────────────────────────────────┤│ Sidebar │ Monatsabschluss · Januar 2026 Fällig: Fr 15.02.2026 ││ ──────── │ ┌───────────────────────────────────────────────────────────────────────────┐││ Zeit │ │ ① Stundenlauf │ ② SOKA │ ③ Tarif-Check │ ④ DATEV │ ⑤ Freigabe │││ Plan │ │ ✓ grün │ ● aktiv │ ⚠ 1 Befund │ ○ offen │ ○ offen │││ ▶ SOKA │ ├───────────────────────────────────────────────────────────────────────────┤││ Rechnungen│ │ SOKA-Monatsmeldung · Gewerblich 67 Bau-MA │││ DATEV │ │ │││ │ │ Bruttolohn gesamt 312.840,00 € │││ │ │ Winterbeschäft. AG 1,2% 3.754,08 € → SOKA-Bau │││ │ │ Winterbeschäft. AN 0,8% 2.502,72 € → einbehalten / DATEV │││ │ │ ZVK 18,0 % West 56.311,20 € → ZVK-BAU-West │││ │ │ ULAK 14,45 % West 45.205,38 € → ULAK │││ │ │ BBF 2,5 % West 7.821,00 € → BBF │││ │ │ ───────────────────────────────── │││ │ │ Gesamt-Beitrag 115.594,38 € │││ │ │ │││ │ │ Winterausfallstd. 428,0 h (3 Baustellen · 7 Schlechtwettertage) │││ │ │ Saison-KuG bewilligt noch offen (Antrag siehe ⬇) │││ │ │ │││ │ │ [ ⤓ SOKA-XML ] [ ⤓ Monatsmeldung PDF ] [ → SOKA-Bau UPM hochladen ] │││ │ └───────────────────────────────────────────────────────────────────────────┘│└──────────────────────────────────────────────────────────────────────────────────────────┘6.2 Web · Witterungs-Protokoll mit DWD-Nachweis
Abschnitt betitelt „6.2 Web · Witterungs-Protokoll mit DWD-Nachweis“┌──────────────────────────────────────────────────────────────────────────────────────────┐│ Witterungs-Protokoll · Rathausplatz 3, Köln 01.–15. Jan 2026 │├──────────────────────────────────────────────────────────────────────────────────────────┤│ Datum │ Temp │ Niedersch.│ Wind │ Arbeit? │ DWD-Beleg │ Saison-KuG │├──────────┼─────────┼───────────┼────────────┼───────────┼──────────────┼────────────────┤│ Do 01.01 │ −2,4 °C │ 4,2 mm │ 8 m/s │ 🔴 Ausfall│ dwd-10513-01 │ ✓ 8 MA · 64 h ││ Fr 02.01 │ −5,8 °C │ 0,0 mm │ 12 m/s │ 🔴 Ausfall│ dwd-10513-02 │ ✓ 8 MA · 64 h ││ Mo 05.01 │ −1,1 °C │ 0,8 mm │ 6 m/s │ 🟡 Prüf. │ dwd-10513-05 │ offen ││ Di 06.01 │ 0,4 °C │ 0,0 mm │ 4 m/s │ 🟢 OK │ — │ — ││ …… │ … │ … │ … │ … │ … │ … │└──────────────────────────────────────────────────────────────────────────────────────────┘Quelle: DWD Open Data · Station 10513 Köln-Bonn · Abruf 15.01.2026 03:15 UTCSHA-256 Roh-Datensatz: 4f82…d91e · GoBD-Archiv 10 J in S3 Object Lock6.3 Web · Tarif-Mindestlohn-Check
Abschnitt betitelt „6.3 Web · Tarif-Mindestlohn-Check“┌──────────────────────────────────────────────────────────────────────────────────────────┐│ ⚠ Tarif-Mindestlohn-Prüfung · Baugewerbe West · Stand 01.01.2026 │├──────────────────────────────────────────────────────────────────────────────────────────┤│ LG │ TV-Mindestlohn │ MA-Anzahl │ Unter MiLo-Grenze │ Handlung │├──────┼──────────────────┼───────────┼───────────────────┼────────────────────────────────┤│ LG 1 │ 13,40 €/h │ 8 │ 0 │ OK ││ LG 2 │ 16,80 €/h │ 21 │ 0 │ OK ││ LG 3 │ 18,15 €/h │ 17 │ 1 🔴 │ Patryk L. 17,90 €/h anpassen ││ LG 4 │ 20,05 €/h │ 14 │ 0 │ OK ││ LG 5 │ 22,60 €/h │ 5 │ 0 │ OK ││ LG 6 │ 26,30 €/h │ 2 │ 0 │ OK │└──────┴──────────────────┴───────────┴───────────────────┴────────────────────────────────┘ [ MA-Detailansicht ] [ Alle auf neuen Satz anheben — PDF-Vertrag Änderung ]6.4 Web · Saison-KuG-Antragsassistent
Abschnitt betitelt „6.4 Web · Saison-KuG-Antragsassistent“┌──────────────────────────────────────────────────────────────────────────────────────────┐│ Saison-KuG §101 SGB III · Antragsassistent · Januar 2026 │├──────────────────────────────────────────────────────────────────────────────────────────┤│ Zeitraum: 01.01.2026 – 31.01.2026 (Schlechtwetterzeit 01.12.–31.03.) ││ Agentur für │ Köln (Agentur-Schlüssel 555) ││ Arbeit: │ ││ Baustellen: │ ☑ Rathausplatz 3, Köln (7 Schlechtwettertage, 8 MA, 256 h) ││ │ ☑ Aachener Str. 42, Köln (5 Schlechtwettertage, 6 MA, 120 h) ││ │ ☐ Fritz-Henkel-Str. 5, Köln (keine Ausfälle) ││ │ ││ Ausfall total: │ 12 Schlechtwettertage · 14 MA betroffen · 376 h Ausfall ││ KuG-Erwartung: │ ≈ 17.200 € Erstattung U1-Topf (vorl.) ││ DWD-Nachweise: │ 12 PDFs, verkettet via SHA-256 │├──────────────────────────────────────────────────────────────────────────────────────────┤│ [ Antrag als ZIP-Bundle exportieren ] [ Per BA-Online einreichen (V2.5) ] │└──────────────────────────────────────────────────────────────────────────────────────────┘6.5 Web · Monatsabschluss-Workflow-Zustand
Abschnitt betitelt „6.5 Web · Monatsabschluss-Workflow-Zustand“┌──────────────────────────────────────────────────────────────────────────────────────────┐│ Monatsabschluss Jan 2026 · Workflow-Statusstream │├──────────────────────────────────────────────────────────────────────────────────────────┤│ [✓] Stundenlauf erzeugt 12.02.2026 08:14 · Hannes K., Sabine M. bestätigt ││ [✓] SOKA-Meldung vorl. 12.02.2026 08:22 · Berechnung nach TV Bau West ││ [⚠] Tarif-Check 12.02.2026 08:24 · 1 Befund (Patryk L., LG 3) ││ [●] Freigabe Vier-Augen ● offen · Admin-Bestätigung + Buchhaltung ││ [○] SOKA-UPM-Upload ○ offen · ausstehend (Fälligkeit Fr 15.02.) ││ [○] DATEV-LODAS-Export ○ offen · nach SOKA-Lieferung ││ [○] Sperre Monat ○ offen · am 16.02. automatisch │└──────────────────────────────────────────────────────────────────────────────────────────┘7. Datenmodell-Skizze
Abschnitt betitelt „7. Datenmodell-Skizze“export const sokaTenantConfigTable = pgTable('soka_tenant_config', { tenantId: uuid('tenant_id').primaryKey().references(() => tenantsTable.id), bgNumber: varchar('bg_number', { length: 20 }).notNull(), // Sozialversicherung Bau zvkNumber: varchar('zvk_number', { length: 20 }).notNull(), ulakNumber: varchar('ulak_number', { length: 20 }), bbfNumber: varchar('bbf_number', { length: 20 }), region: varchar('region', { length: 4 }).notNull(), // 'WEST' | 'OST' deliveryChannel: varchar('delivery_channel', { length: 20 }).notNull(), // 'upm' | 'sftp' | 'paper' upmCredentialsRef: varchar('upm_credentials_ref', { length: 100 }), // AWS Secrets Manager ARN enabledAt: timestamp('enabled_at', { withTimezone: true }), enabledBy: uuid('enabled_by').references(() => usersTable.id), enabledCountersign: uuid('enabled_countersign').references(() => usersTable.id), // Vier-Augen hashPrev: bytea('hash_prev'), hashSelf: bytea('hash_self').notNull(),});
export const sokaLohngruppenTable = pgTable('soka_lohngruppen', { id: uuid('id').primaryKey().defaultRandom(), tenantId: uuid('tenant_id').notNull().references(() => tenantsTable.id), region: varchar('region', { length: 4 }).notNull(), groupCode: varchar('group_code', { length: 10 }).notNull(), // LG1, LG2, … validFrom: date('valid_from').notNull(), validUntil: date('valid_until'), // null = aktuell minWageEurPerHour: numeric('min_wage_eur_per_hour', { precision: 6, scale: 2 }).notNull(), sourceTvVersion: varchar('source_tv_version', { length: 30 }).notNull(), // z.B. "TV-MiLo-Bau 2025-10" hashPrev: bytea('hash_prev'), hashSelf: bytea('hash_self').notNull(),}, (t) => ({ regionGroupValidIdx: index('soka_lg_region_group_valid_idx').on(t.region, t.groupCode, t.validFrom.desc()),}));
export const sokaMitarbeiterEingruppierungTable = pgTable('soka_ma_eingruppierung', { id: uuid('id').primaryKey().defaultRandom(), tenantId: uuid('tenant_id').notNull().references(() => tenantsTable.id), userId: uuid('user_id').notNull().references(() => usersTable.id), lohngruppeId: uuid('lohngruppe_id').notNull().references(() => sokaLohngruppenTable.id), validFrom: date('valid_from').notNull(), validUntil: date('valid_until'), sokaRelevant: boolean('soka_relevant').default(true).notNull(), // false: Angestellte, Azubis, Gfügige region: varchar('region', { length: 4 }).notNull(), hashPrev: bytea('hash_prev'), hashSelf: bytea('hash_self').notNull(),}, (t) => ({ userValidIdx: index('soka_ma_user_valid_idx').on(t.userId, t.validFrom.desc()),}));
export const sokaMonthlyReportsTable = pgTable('soka_monthly_reports', { id: uuid('id').primaryKey().defaultRandom(), tenantId: uuid('tenant_id').notNull().references(() => tenantsTable.id), period: varchar('period', { length: 7 }).notNull(), // 'YYYY-MM' status: varchar('status', { length: 20 }).notNull(), // draft|pending_review|approved|delivered|rejected|locked grossTotalEur: numeric('gross_total_eur', { precision: 14, scale: 2 }).notNull(), wbuAgEur: numeric('wbu_ag_eur', { precision: 14, scale: 2 }).notNull(), // 1,2 % wbuAnEur: numeric('wbu_an_eur', { precision: 14, scale: 2 }).notNull(), // 0,8 % zvkEur: numeric('zvk_eur', { precision: 14, scale: 2 }).notNull(), ulakEur: numeric('ulak_eur', { precision: 14, scale: 2 }).notNull(), bbfEur: numeric('bbf_eur', { precision: 14, scale: 2 }).notNull(), totalBeitragEur: numeric('total_beitrag_eur', { precision: 14, scale: 2 }).notNull(), xmlS3Key: varchar('xml_s3_key', { length: 300 }), // SOKA-eXtra-XML pdfS3Key: varchar('pdf_s3_key', { length: 300 }), deliveredAt: timestamp('delivered_at', { withTimezone: true }), deliveryReceiptRef:varchar('delivery_receipt_ref', { length: 100 }), // UPM-Quittung approvedBy: uuid('approved_by').references(() => usersTable.id), approvedCountersign: uuid('approved_countersign').references(() => usersTable.id), hashPrev: bytea('hash_prev'), hashSelf: bytea('hash_self').notNull(),}, (t) => ({ tenantPeriodIdx: uniqueIndex('soka_reports_tenant_period_idx').on(t.tenantId, t.period),}));
export const sokaWitterungTable = pgTable('soka_witterung', { id: uuid('id').primaryKey().defaultRandom(), tenantId: uuid('tenant_id').notNull().references(() => tenantsTable.id), projectId: uuid('project_id').notNull().references(() => projectsTable.id), workDate: date('work_date').notNull(), dwdStationId: varchar('dwd_station_id', { length: 10 }).notNull(), dwdFetchedAt: timestamp('dwd_fetched_at', { withTimezone: true }).notNull(), dwdRawS3Key: varchar('dwd_raw_s3_key', { length: 300 }).notNull(), dwdRawSha256: bytea('dwd_raw_sha256').notNull(), tempMin: numeric('temp_min', { precision: 5, scale: 2 }), tempAt0900: numeric('temp_at_0900', { precision: 5, scale: 2 }), precipitationMm: numeric('precipitation_mm', { precision: 6, scale: 2 }), windSpeedMax: numeric('wind_speed_max', { precision: 5, scale: 2 }), workStopped: boolean('work_stopped').notNull(), stopDecidedBy: uuid('stop_decided_by').notNull().references(() => usersTable.id), stopComment: text('stop_comment'), affectedUserIds: uuid('affected_user_ids').array(), affectedHours: numeric('affected_hours', { precision: 6, scale: 2 }), proofPdfS3Key: varchar('proof_pdf_s3_key', { length: 300 }), hashPrev: bytea('hash_prev'), hashSelf: bytea('hash_self').notNull(),}, (t) => ({ projectDateIdx: uniqueIndex('soka_witterung_project_date_idx').on(t.projectId, t.workDate),}));
export const sokaWettergeldApplicationsTable = pgTable('soka_wettergeld_applications', { id: uuid('id').primaryKey().defaultRandom(), tenantId: uuid('tenant_id').notNull().references(() => tenantsTable.id), period: varchar('period', { length: 7 }).notNull(), agenturSchluessel: varchar('agentur_schluessel', { length: 10 }).notNull(), witterungIds: uuid('witterung_ids').array().notNull(), affectedUsers: integer('affected_users').notNull(), totalHours: numeric('total_hours', { precision: 8, scale: 2 }).notNull(), expectedEur: numeric('expected_eur', { precision: 12, scale: 2 }), bundleZipS3Key: varchar('bundle_zip_s3_key', { length: 300 }), bundleSha256: bytea('bundle_sha256'), submittedAt: timestamp('submitted_at', { withTimezone: true }), submissionChannel: varchar('submission_channel', { length: 20 }), // 'ba_online' | 'post' | 'fax' baResponseRef: varchar('ba_response_ref', { length: 100 }), status: varchar('status', { length: 20 }).notNull(), // draft|exported|submitted|approved|rejected hashPrev: bytea('hash_prev'), hashSelf: bytea('hash_self').notNull(),});RLS-Policy-Sketch:
ALTER TABLE soka_monthly_reports ENABLE ROW LEVEL SECURITY;CREATE POLICY soka_reports_tenant_isolation ON soka_monthly_reports USING (tenant_id = current_setting('app.tenant_id')::uuid);
CREATE POLICY soka_reports_scope_all ON soka_monthly_reports FOR ALL USING ( tenant_id = current_setting('app.tenant_id')::uuid AND current_setting('app.scope') = 'all' -- nur Admin / Buchhaltung AND current_setting('app.role') IN ('admin', 'buchhaltung') );
ALTER TABLE soka_witterung ENABLE ROW LEVEL SECURITY;CREATE POLICY witterung_scope ON soka_witterung 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 id FROM projects WHERE manager_id = current_setting('app.user_id')::uuid )) ) );ER-Bezug. tenants, users, projects, time_entries (Zeitart „Schlechtwetter“), payroll_runs (DATEV-Export-Lauf), audit_log.
8. API-Endpunkte
Abschnitt betitelt „8. API-Endpunkte“| Methode | Pfad | Scope | Rate | Idempotenz | Beschreibung |
|---|---|---|---|---|---|
POST |
/v1/handwerk/soka/config |
soka:admin |
Privileged | Pflicht | Tenant-Setup (Vier-Augen) |
POST |
/v1/handwerk/soka/lohngruppen |
soka:admin |
Standard | Pflicht | Neue LG-Version anlegen |
GET |
/v1/handwerk/soka/lohngruppen?region=WEST&at=2026-01-01 |
soka:read:all |
Standard | — | Aktueller TV-Stand |
PATCH |
/v1/handwerk/soka/eingruppierung/{userId} |
soka:write |
Standard | Pflicht | Eingruppierung versionieren |
POST |
/v1/handwerk/soka/reports/{period}/generate |
soka:write |
Privileged | Pflicht | Monatsmeldung rechnen |
POST |
/v1/handwerk/soka/reports/{period}/approve |
soka:approve |
Privileged | Pflicht | Vier-Augen-Freigabe |
POST |
/v1/handwerk/soka/reports/{period}/deliver |
soka:approve |
Privileged | Pflicht | UPM-Upload triggern |
POST |
/v1/handwerk/soka/witterung |
witterung:write |
Standard | Pflicht | DWD-Fetch + Ausfall-Entscheidung |
POST |
/v1/handwerk/soka/wettergeld/generate |
soka:write |
Standard | Pflicht | Saison-KuG-Bundle erzeugen |
GET |
/v1/handwerk/soka/reports/{period} |
soka:read:all |
Standard | — | Detail mit Beiträgen |
Webhook-Events:
soka.monthly_report.generated,soka.monthly_report.approved,soka.monthly_report.delivered,soka.monthly_report.rejectedsoka.witterung.logged,soka.witterung.dwd_unreachablesoka.wettergeld.bundle_generated,soka.wettergeld.submitted
OpenAPI-Skizze:
paths: /v1/handwerk/soka/reports/{period}/generate: post: operationId: generateSokaMonthlyReport x-werkszeit-scope: soka:write parameters: - in: path name: period schema: { type: string, pattern: '^\d{4}-(0[1-9]|1[0-2])$' } required: true requestBody: content: application/json: schema: type: object properties: idempotencyKey: { type: string, format: uuid } recompute: { type: boolean, default: false } required: [idempotencyKey] responses: '201': { description: 'Report in state draft' } '409': { description: 'Report for period already exists; use recompute=true' } '422': { description: 'Missing eingruppierung for active bau-MA' }9. Offline-Profil
Abschnitt betitelt „9. Offline-Profil“| Flow | Profil | Mechanik |
|---|---|---|
| SOKA-Monatsmeldung erzeugen | Online-only | Benötigt Aggregation über Zeiterfassung + DATEV-Stammdaten |
| DWD-Wetter-Fetch | Online-only | DWD Open Data ist REST-Service |
| Witterungs-Entscheidung „Arbeit steht“ | Best-effort-offline | Bauleiter kann Entscheidung im Web-Formular vormerken, DWD-Beleg wird beim nächsten Online-State ergänzt; UI zeigt „Beleg folgt“ bis DWD-Abruf erfolgt |
| UPM-Upload zur SOKA-Bau | Online-only | TLS-Upload, keine Zwischenablage |
| Saison-KuG-Bundle-Export | Online-only | ZIP-Generierung serverseitig |
Konflikt-Strategie. SOKA-Meldungen sind append-only: korrigierte Version erzeugt neuen soka_monthly_report mit Verweis (corrects_report_id), Original bleibt erhalten (GoBD §147 AO). Witterungs-Entscheidung: nach Freigabe der Monatsmeldung ist die Witterungs-Zuordnung für den Monat gesperrt (locked_for_period), Änderungen nur als Korrektur-Meldung.
10. Compliance-Mapping
Abschnitt betitelt „10. Compliance-Mapping“10.1 GoBD
Abschnitt betitelt „10.1 GoBD“- Append-only.
soka_monthly_reports,soka_witterung,soka_wettergeld_applications,soka_lohngruppentragen Hash-Chain. Mutation erzeugt neue Zeile mit Verweis auf Vorgänger. - Aufbewahrung. SOKA-XML, Monatsmeldung-PDF, DWD-Roh-Daten, DWD-Nachweis-PDF, UPM-Quittungen in S3 Object Lock (Compliance-Mode, 10 Jahre, §147 AO).
- DWD-Integrität. SHA-256 des Roh-CSV/JSON von DWD wird bei jedem Fetch gespeichert. Bei späterer Prüfung rekonstruiert ein Integritätstest den Nachweis-PDF aus dem archivierten Roh-Datensatz und vergleicht den Hash.
- Nachvollziehbarkeit. Audit-Log bei jedem Monatsabschluss-Statuswechsel, bei jedem DWD-Fetch (Zeitpunkt + Station + User) und bei jedem UPM-Upload.
- Verfahrensdoku-Textbaustein „SOKA-Bau-Meldungen + Winterausfallgeld §101 SGB III“ wird generiert mit konkreter BG-Nr., ZVK-Nr., Region, DWD-Station-Zuordnung pro Projekt.
10.2 ArbZG
Abschnitt betitelt „10.2 ArbZG“— nicht zutreffend, weil SOKA-Modul keinerlei Arbeitszeit-Prüfung enthält. Arbeitszeit-Compliance läuft in ../kern/01-zeiterfassung.md.
10.3 VOB/B
Abschnitt betitelt „10.3 VOB/B“— nicht zutreffend, weil SOKA eine arbeitsrechtliche/sozialversicherungsrechtliche Schiene ist, keine bauvertragliche.
10.4 DSGVO
Abschnitt betitelt „10.4 DSGVO“- Art. 5 Datenminimierung. Wir speichern für Bau-MA: SV-Nummer, Lohngruppe, Brutto-Lohn, Betriebsstätte. Kein Geburtsdatum jenseits des für SOKA-Meldung Zwingenden (Eintritts-Datum genügt). Keine Gesundheitsdaten.
- Art. 6 Rechtsgrundlage. (a) Vertrag (Art. 6 Abs. 1 b — Arbeitsvertrag), (b) rechtliche Verpflichtung (Art. 6 Abs. 1 c) aus Tarifvertrag Bau + §101 SGB III + §14 SGB IV.
- Art. 9 Besondere Kategorien. Keine Verarbeitung.
- Art. 17 Löschung. Nach Ausscheiden des MA: Sperre für SOKA-Meldung sofort, Maskierung personenbezogener Felder nach Ablauf GoBD-Frist (10 Jahre ab letzter Meldung).
- Art. 20 Datenexport. Bau-MA erhält JSON-Export seiner Eingruppierungs-Historie + aller ihn betreffenden Witterungs-Einträge + Urlaubsvergütungs-Daten.
- Art. 30 Verarbeitungsverzeichnis. Eintrag „SOKA-Bau-Meldungen“. Empfänger-Kategorien: SOKA-Bau (Wiesbaden), ZVK-Bau, ULAK, BBF, Bundesagentur für Arbeit, Steuerberater (DATEV-Pfad).
- DSFA. Nicht pflichtig — Verarbeitung dient ausschließlich gesetzlicher/tariflicher Pflicht, keine Bewertung persönlicher Aspekte, kein Scoring, keine automatisierte Entscheidung im Sinne Art. 22.
10.5 BFSG / WCAG 2.2 AA
Abschnitt betitelt „10.5 BFSG / WCAG 2.2 AA“— nicht zutreffend, weil Admin-UI (keine ESS-Flows). Trotzdem Werkszeit-interner Baseline: axe-core ohne Critical/Serious, Tastatur-Navigation, Kontrast ≥ 4.5:1.
10.6 BetrVG §87(1)6
Abschnitt betitelt „10.6 BetrVG §87(1)6“— nicht zutreffend, weil SOKA-Modul keine Leistungs-/Verhaltenskontrolle ermöglicht. Es verarbeitet lohn- und tarifrechtliche Pflichtdaten, nicht performance- oder aktivitätsbasiert.
10.7 Spezial-Compliance
Abschnitt betitelt „10.7 Spezial-Compliance“- TV Bau Mindestlohn. Versionierte
soka_lohngruppenmit Stichtag, bei Tarif-Anpassung lauft F-W-08 (Vorab-Prüfung). Bei Ist-Lohn < TV-MiLo → Rechnung darf nicht freigegeben werden. - §101 SGB III (Saison-Kurzarbeitergeld). Schlechtwetterzeit 01.12.–31.03. Voraussetzungen: (a) witterungsbedingter Arbeitsausfall, (b) wirtschaftliche Gründe mittelbar aus Witterung, (c) unvermeidbar + erheblich. DWD-Datennachweis: Temperatur ≤ −1 °C UND/ODER Niederschlag > Schwellenwert UND/ODER Windgeschwindigkeit > Schwellenwert. UI markiert einen Tag nur automatisch dann als potentiell
workStopped, wenn mindestens eines der Kriterien erfüllt ist; finale Entscheidung trägt der Bauleiter (nicht automatisch). - §14 SGB IV (Arbeitsentgelt-Definition). Brutto-Basis der SOKA-Beiträge ist das sozialversicherungspflichtige Arbeitsentgelt gemäß §14 SGB IV. Unter-Bruttogrenzen (steuerfreie Zuschläge §3b EStG) werden korrekt abgegrenzt.
- SOKA-eXtra-Datensatzformat (XML-Schema, jährlich von SOKA-Bau publiziert). Valibot-Schema + XSD-Validierung am Ende der Generate-Pipeline.
11. Edge-Cases
Abschnitt betitelt „11. Edge-Cases“| # | Szenario | Erwartetes Verhalten |
|---|---|---|
| EC-01 | Winterausfallgeld-Antrag ohne DWD-Nachweis. Buchhaltung will Saison-KuG für 10.01. anlegen, DWD-Daten liegen nicht vor (Fetch-Fehler, Station außer Betrieb). | UI blockt Export: „DWD-Nachweis für 10.01. fehlt. Bitte DWD-Wetter für Station 10513 manuell abrufen oder Ausfallgrund anders belegen (z. B. Bauherr-Bestätigung).“ Manueller Beleg-Upload-Pfad als Fallback mit Vier-Augen-Signatur Admin + Buchhaltung, dann Flag proof_source='manual' im soka_witterung-Eintrag. Audit-Log dokumentiert Abweichung vom DWD-Default. |
| EC-02 | ZVK-Meldefrist verpasst. Monat Januar wurde erst am 22.02. freigegeben (Frist 15.02.). | Status bleibt approved, UPM-Upload wird möglich, aber der Report trägt delivery_late=true. Warnung im UI: „Meldung ging nach Frist 15.02. Mögliche Säumnis-Zuschläge 1 % (SOKA-Satzung §22). Bitte ggf. kurzschreiben.“ Eskalations-Push an Admin + Geschäftsführer. Nächster Report wird 30 Tage vor Frist mit steigender Eskalation im Dashboard angezeigt. |
| EC-03 | Tarif-Mindestlohn-Unterschreitung. Ein MA in LG 3 wird mit 17,90 € geführt, TV-MiLo ab 01.01.2026 = 18,15 €. | F-W-08-Vorabprüfung erzeugt vor Generate-Lauf ein rotes Ticket. Wenn Generate trotz offener Tickets gestartet wird → Hard-Fail mit HTTP 422 und Body {"errors":["min_wage_violation","userId":"..."]}. Kein Draft-Report wird erzeugt. Auflösung nur durch Tariferhöhung des MA (neues Eingruppierungs-Datum). Audit-Log dokumentiert Versuch. |
| EC-04 | DWD-Station außer Betrieb. Station 10513 Köln-Bonn liefert an 13.01. keinen Wert (nicht gemeldet). | soka_witterung.dwd_raw_s3_key fehlt. UI zeigt 🟡 „Ersatzstation verwenden“ + nächste 3 Stationen aus Nominatim-Nähe-Suche (max. 30 km). Bauleiter wählt Ersatz, Flag substitute_station_used=true + Begründung. |
| EC-05 | Multi-Tenant-Bleed. User von Tenant A fordert /v1/handwerk/soka/reports/2026-01 für Report-ID aus Tenant B. |
RLS liefert 0 Zeilen → API 404, kein Audit-Eintrag in B. Negativtest in Compliance-Suite. |
| EC-06 | Rollen-Demotion mid-approval. Frau Müller startet approve, Admin demotet sie zum Mitarbeiter. |
Bei Request-Eingang re-validiert RLS Rolle → 403 „Rollenwechsel erkannt, bitte neu anmelden“. Report bleibt in pending_review. Audit-Log-Eintrag mit Reason. |
| EC-07 | SOKA-UPM-Upload fehlschlägt (SOKA-Server 503). | Retry-Policy BullMQ: 3x exponentiell bis 30 min, dann Queue soka-upload-failed. Push an Buchhaltung + Admin. Report bleibt in approved (nicht delivered), bis Wiederholung erfolgreich. |
| EC-08 | Betriebsrat nicht gefragt, aber Tenant ohne BR. | §87(1)6 BetrVG greift nur bei Existenz eines BR. Tenant-Flag hat_betriebsrat=false akzeptiert, aber UI blendet eine Rückfrage bei Aktivierung ein: „Kein Betriebsrat gemeldet — bitte bestätigen. SOKA-Modul ist ohnehin nicht §87(1)6-pflichtig, aber wir erinnern, dass Beschäftigte generell gem. §81 BetrVG zu unterrichten sind.“ |
| EC-09 | Zeitzonenwechsel (MA auf Montage in der Schweiz). Schweizer Baustelle, MA-Stunden in CH-ZZ erfasst. | SOKA-Meldung wird nur für DE-gemeldete MA erzeugt. Nicht-SOKA-pflichtige MA-Zeiten (Auslandstage) werden nicht in den Brutto-Pool des SOKA-Reports aufgenommen, sondern getrennt ausgewiesen. |
| EC-10 | Rückwirkende Korrektur Monat Dezember nach Januar-Sperre. | Korrektur-Report corrects_report_id, SOKA-eXtra-Änderungsmeldung wird generiert, UPM-Upload mit Typ „Korrekturlieferung“. Alte Lieferung bleibt im Archiv. |
12. Akzeptanzkriterien (Gherkin)
Abschnitt betitelt „12. Akzeptanzkriterien (Gherkin)“Funktionalität: SOKA-Bau Monatsmeldung + Saison-KuG mit DWD-Nachweis
Hintergrund: Angenommen ein Tenant "shk-gebruder-schmidt" mit aktivem Modul-Gate "handwerk.soka" Und eine SOKA-Konfiguration Region "WEST", BG-Nr "1234", ZVK-Nr "5678" Und 67 Bau-MA mit gültiger SOKA-Eingruppierung im Januar 2026 Und eine Buchhaltung-Userin "Frau Müller" mit Rolle "Buchhaltung" und Scope "all" Und ein Admin "Thomas Schmidt"
Szenario: Happy Path — Monatsmeldung Januar 2026 generieren und liefern Wenn Frau Müller am 12.02.2026 auf "Monatsabschluss Januar 2026" klickt Und "Generate" wählt Dann wird ein Report mit Status "draft" erzeugt Und der Brutto-Pool beträgt 312.840,00 € Und WBU-AG = 3.754,08 € (1,2 %), WBU-AN = 2.502,72 € (0,8 %) Und ZVK = 56.311,20 € (18,0 %), ULAK = 45.205,38 € (14,45 %), BBF = 7.821,00 € (2,5 %) Und Tarif-Check ergibt 0 Befunde Wenn Frau Müller "Freigabe" klickt Und Admin Thomas Schmidt im Vier-Augen-Dialog bestätigt Dann wechselt Status zu "approved" Wenn "SOKA-Bau UPM hochladen" ausgelöst wird Dann erfolgt TLS-Upload an upm.soka-bau.de mit AG-Signatur Und die UPM-Quittung wird in delivery_receipt_ref gespeichert Und Status wechselt zu "delivered" Und ein Audit-Log-Eintrag "soka.monthly_report.delivered" wird erzeugt
Szenario: Grenzfall — Winterausfallgeld-Antrag ohne DWD-Nachweis Angenommen ein witterungsbedingter Arbeitsausfall am 10.01.2026 auf "Rathausplatz 3" Und der DWD-Fetch für Station 10513 am 10.01. ist fehlgeschlagen (Timeout) Und das Feld dwd_raw_s3_key für diesen Tag ist leer Wenn Frau Müller ein Saison-KuG-Bundle für Januar 2026 exportieren will Dann blockiert die UI mit Meldung "DWD-Nachweis für 10.01. fehlt" Und bietet Fallback "Manuellen Beleg hochladen (Vier-Augen)" Wenn Frau Müller ein Bauherr-Bestätigungs-PDF hochlädt Und Admin Thomas bestätigt im Vier-Augen-Dialog Dann wird der Witterungs-Eintrag mit proof_source='manual' angelegt Und das ZIP-Bundle enthält diesen Beleg statt DWD-PDF Und ein Audit-Log-Eintrag "soka.witterung.manual_proof" wird erzeugt
Szenario: Grenzfall — ZVK-Meldefrist verpasst Angenommen der Januar-2026-Report wird erst am 22.02.2026 10:00 freigegeben Und die tarifliche Frist ist der 15.02.2026 Wenn Frau Müller "SOKA-Bau UPM hochladen" auslöst Dann erfolgt der Upload Und das Feld delivery_late = true Und eine Warnung "Meldung nach Frist — mögliche Säumnis-Zuschläge 1 % gem. SOKA-Satzung §22" erscheint Und eine Push-Benachrichtigung geht an Admin + Geschäftsführer Und der Februar-Report-Dashboard zeigt 30 Tage vor Frist einen Eskalations-Banner
Szenario: Grenzfall — Tarif-Mindestlohn-Unterschreitung (Hard-Fail) Angenommen ein MA "Patryk L." ist in LG 3 eingruppiert Und sein hinterlegter Stundensatz ist 17,90 €/h Und der TV-Mindestlohn für LG 3 West ab 01.01.2026 ist 18,15 €/h Wenn Frau Müller den Januar-2026-Report generieren will Dann antwortet die API mit HTTP 422 Und dem Body '{"errors":[{"code":"min_wage_violation","userId":"...","expected":"18.15","actual":"17.90"}]}' Und KEIN Draft-Report wird erzeugt Und ein Audit-Log-Eintrag "soka.monthly_report.rejected_min_wage" wird erzeugt Und Frau Müller sieht im Dashboard ein rotes Ticket "Patryk L. — Anhebung auf 18,15 €/h nötig"13. Test-Cases
Abschnitt betitelt „13. Test-Cases“13.1 Unit-Tests
Abschnitt betitelt „13.1 Unit-Tests“- U-01 —
calcWbu(brutto, region)→ AG 1,2 % + AN 0,8 %, kaufmännische Rundung 2 Nachkommastellen, nie negativ. - U-02 —
calcZvk(brutto, region): West 18,0 %, Ost abweichend (aussoka_lohngruppen.sourceTvVersion-gebundenem Satz), Grenzwerte. - U-03 —
checkWeatherThreshold(temp, precip, wind)— Property-based über alle Grenzfälle (−1 °C Schwelle inklusiv, 4,2 mm/h, 10 m/s). - U-04 —
parseDwdResponse(csv)— Parser für DWD Open Data Stunden-CSV; Fallback bei fehlenden Messwerten. - U-05 —
validateSokaExtraXml(payload)— XSD-Validator gegen amtliches Schema; Muster-XML aus SOKA-Bau-Testbench. - U-06 — Eingruppierungs-Historie: bei Tarif-Stichtagswechsel wird automatisch neue
ma_eingruppierung-Zeile mitvalidFrom= TV-Stichtag angelegt.
13.2 Widget-/Component-Tests
Abschnitt betitelt „13.2 Widget-/Component-Tests“- W-01 — Monatsabschluss-Stepper zeigt 5 Stati korrekt (Grün / Gelb / Rot / Aktiv / Offen).
- W-02 — DWD-Nachweis-Karte rendert Temperatur mit Kelvin-Fallback bei Null-Werten, a11y-Label „Temperatur minus zwei Komma vier Grad Celsius“.
- W-03 — Golden-Test
de-DELight für Monats-Übersicht.
13.3 Integrations-Tests
Abschnitt betitelt „13.3 Integrations-Tests“- I-01 — Multi-Tenant-Hard-Test (EC-05): 2 Tenants, je 1 Report, Cross-Read = 404.
- I-02 — RLS
teamScope (Bauleiter): sieht nur Witterung seiner Projekte. - I-03 — Outbox-Idempotenz: doppelter Generate-Call mit gleichem Key → 409.
- I-04 —
GenerateSokaReportgegen 67-MA-Mock-Stundenlauf, erwartetes Ergebnis nach TV Bau West 01.01.2026. - I-05 — DWD-Fetch-Adapter gegen Mock-Server: CSV-Parse, SHA-256-Stempel, S3-Upload.
13.4 E2E-Tests
Abschnitt betitelt „13.4 E2E-Tests“- E-01 — Happy Path (§12) auf Chromium + Firefox + WebKit (Web-only, kein Mobile).
- E-02 — Saison-KuG-Bundle-Export mit 12 DWD-Belegen → ZIP enthält alle PDFs + Antragsformular + Hash-Manifest.
- E-03 — Visuelle Regression: Monatsmeldung-Übersicht + DWD-Nachweis-Karte.
- E-04 — axe-core ohne Critical/Serious.
13.5 Compliance-Tests
Abschnitt betitelt „13.5 Compliance-Tests“- C-01 — Hash-Chain-Integrität über
soka_monthly_reports: Mutation einergrossTotalEur-Zelle per SQL → Hash-Chain-Verify schlägt an. - C-02 — SOKA-eXtra-XSD-Validator: Generierte XMLs gegen offizielles XSD 2026.
- C-03 — DWD-Archive-Integrität: Rohdaten aus S3 neu hashen, Vergleich mit
dwdRawSha256. - C-04 — Tarif-Stichtagswechsel: Eingruppierung am 31.03. auf 01.04. erzeugt automatisch neue
ma_eingruppierung-Version, rückwirkende Änderung am 31.03. greift nicht. - C-05 — DATEV-Validator: Der an DATEV gelieferte Monat enthält WBU-AN-Abzug als eigene Lohnart 3060 (Beispiel) korrekt.
13.6 Flakiness-Schutz
Abschnitt betitelt „13.6 Flakiness-Schutz“Lokaler 20×-Re-Run von E-01 und E-02 grün.
14. Nicht-Ziele
Abschnitt betitelt „14. Nicht-Ziele“| Nicht-Ziel | Begründung |
|---|---|
| Eigenständige Lohnabrechnung ohne DATEV | Werkszeit macht Voraufzeichnung + SOKA-Meldung, Lohnlauf bleibt DATEV LODAS. |
| SOKA-Bau-Urlaubskassen-Auszahlung an MA | Macht SOKA-Bau selbst direkt an MA. Wir melden nur. |
| Automatische BA-Online-Einreichung Saison-KuG | V2.5 nach BA-API-Zertifizierung. Im V2 nur ZIP-Export zur manuellen Einreichung. |
| Schwarzarbeits-Check (FKS, Zoll-Vorab) | Nicht unser Zuständigkeitsbereich. Wir liefern GoBD-konforme Belege; Zoll-Prüfungen laufen dort. |
| Nicht-Bau-Tarif-Verträge (TV Metall, TV IGBCE) | Scope Phase 1 = Bau. Branchen-Erweiterung später. |
| Österreich (BUAK) / Schweiz (PKG Bau) | DACH-Expansion Phase 2. |
15. Risiken & offene Annahmen
Abschnitt betitelt „15. Risiken & offene Annahmen“| Risiko / Annahme | Impact | Wahrscheinlichkeit | Gegenmaßnahme |
|---|---|---|---|
| SOKA-eXtra-XSD ändert sich jährlich | mittel | hoch | Schema als versioniertes Paket @werkszeit/soka-extra-2026 mit jährlichem Release-Cycle. |
| Tarif-Sätze (WBU, ZVK, ULAK, BBF) ändern sich durch Tarifrunde | hoch | hoch | soka_lohngruppen + getrennte soka_rates-Tabelle versioniert; ADR bei Satzänderung. |
| DWD Open Data nicht Vertrags-SLA — Ausfall möglich | mittel | niedrig | Cache + Retry, EC-04 manueller Fallback. |
| BA-KuG-Online-API-Zertifizierung zieht sich | niedrig | mittel | V2.5-Feature; ZIP-Export genügt für V2. |
| Unterschiedliche West/Ost-Sätze in Übergangszeiträumen komplex | mittel | mittel | Region wird pro MA (nicht tenant-weit) geführt; Property-based Tests mit 50 Grenzfällen. |
| Referenzkunde nicht gesichert | hoch | mittel | Sales-Call „Bauunternehmen Schmidt, Koblenz“ vor Sprint. Ohne Kunde kein Start (DOR §1.1.1). |
16. Abhängigkeiten
Abschnitt betitelt „16. Abhängigkeiten“- Vorbedingung:
kern/01-zeiterfassung(Zeitart „Schlechtwetter“ muss existieren),kern/10-datev-export(Lohnart-Mapping für WBU-AN-Abzug),kern/11-audit-log(Hash-Chain-Framework). - Schnittstelle zu: DWD Open Data REST (Wetter), SOKA-Bau UPM (HTTPS-Upload + TLS-Client-Cert), optional BA-Online (V2.5), DATEV LODAS.
- Wird konsumiert von:
kern/07-reporting-auswertung(SOKA-KPIs im Geschäftsführer-Dashboard),handwerk/05-bau-abrechnung(SOKA-Beitrags-Positionen in Kalkulation).
17. Referenzkunde-Slot
Abschnitt betitelt „17. Referenzkunde-Slot“Status: TBD — zu klären mit Sales.
DOR §1.1.1: Mindestens ein zahlender Design-Partner namentlich.
Kandidaten-Profile:
- Bauunternehmen Schmidt GmbH & Co. KG (Koblenz, 80 MA, Hochbau). Aus Sales-Call 2026-02-27: „Wenn ihr SOKA-Meldung automatisiert und ich Winterausfallgeld nicht mehr mit Excel zusammensuchen muss, sind wir sofort Testkunde.“
- Tiefbau Hensel GmbH (Bremen, 52 MA) — Geschäftsführer hat nach Zoll-Prüfung 2025 ein 4.200-€-Bußgeld wegen Mindestlohn-Unterschreitung bekommen.
Validierungs-Fragen:
- Wie viele Schlechtwettertage hatten Sie im letzten Winter, für wie viele haben Sie KuG beantragt?
- Wer in Ihrem Team erstellt aktuell die SOKA-Monatsmeldung? Wie viel Zeit kostet das?
- Hatten Sie in den letzten 3 Jahren eine Zoll- oder SOKA-Betriebsprüfung? Mit welchem Befund?
- Wären Sie bereit, das Feature im Beta-Status zu testen, mit Bonus: 1 Jahr kostenloser Business-Plan als Gegenleistung?
18. Senior-Berater-Empfehlung
Abschnitt betitelt „18. Senior-Berater-Empfehlung“Build-Sequenz:
- Datenmodell + RLS + Multi-Tenant-Test —
soka_tenant_config,soka_lohngruppen,soka_monthly_reports,soka_witterung. Hash-Chain. (3 Tage) - DWD-Adapter + S3-Archiv — Open-Data-Client, CSV-Parse, SHA-256-Stempel, Cron für Nacht-Fetch. (3 Tage)
- Lohngruppen + Tarif-Versionierung + F-W-08 Vorabprüfung (read-only Simulation). (2 Tage)
- Monatsabschluss-Workflow Backend — Generate → Approve (Vier-Augen) → Deliver. OpenAPI + Dart-Client. (4 Tage)
- Web-UI Monats-Übersicht + Stepper + KPI-Block. (3 Tage)
- Witterungs-Protokoll UI + DWD-Nachweis-PDF (Typst-Template). (2 Tage)
- Saison-KuG-Bundle (ZIP-Export) + BA-Kiehl-Formular-PDF. (2 Tage)
- SOKA-eXtra-XML-Generator + XSD-Validator + UPM-Upload-Client. (2 Tage)
- Compliance-Test-Suite erweitern (C-01 bis C-05). (1 Tag)
Risiko-Reihenfolge. Multi-Tenant-Isolation + Tarif-Mindestlohn-Hard-Fail sind nicht verhandelbar. Bescheid-Inbox (F-W-10) und BA-Online-API sind Streich-Kandidaten nach V2.5.
Stop-the-Bus-Triggers:
- Multi-Tenant-Bleed in
soka_witterungodersoka_monthly_reports. - Hash-Chain-Bruch auf Monatsmeldungen in der Compliance-Suite.
- SOKA-eXtra-XML wird von offiziellem XSD abgelehnt.
- DWD-Nachweis-PDF lässt sich aus archivierten Rohdaten nicht bitgenau rekonstruieren.
- Tarif-Mindestlohn-Unterschreitung wird trotz Hard-Fail in Generate-Lauf übersehen.
Was uns 2027 dankbar macht. Der SHA-256-Stempel über DWD-Rohdaten macht 10 Jahre später noch beweisbar „so sah das Wetter wirklich aus“ — jede Betriebsprüfung mit Fokus auf Saison-KuG ist damit trivial. Die append-only-Monatsmeldung mit corrects_report_id erlaubt Korrektur-Lieferungen ohne Original-Datenverlust (anders als Excel). Die Versionierung der soka_lohngruppen erlaubt Re-Berechnung für Vorjahre, wenn ein rückwirkender Tarifschluss kommt — statt „jemand muss das in der alten DB anfassen“ gilt „neue Zeile, Hash weiter, gut“.
Letzte Aktualisierung: 2026-04-19 · Branch feinkonzepte/v0.1
Für Entwickler — API-Endpoints15
| Methode | Pfad | Auth | Zweck |
|---|---|---|---|
| GET | /v1/handwerk/soka-bau/beitragssaetze | bearerAuth | SOKA-BAU Beitragssätze (paginiert) |
| POST | /v1/handwerk/soka-bau/beitragssaetze | bearerAuth | SOKA-BAU Beitragssatz anlegen |
| GET | /v1/handwerk/soka-bau/beitragssaetze/{id} | bearerAuth | SOKA-BAU Beitragssatz Detail |
| PUT | /v1/handwerk/soka-bau/beitragssaetze/{id} | bearerAuth | SOKA-BAU Beitragssatz aktualisieren |
| GET | /v1/handwerk/soka-bau/meldelaufe | bearerAuth | SOKA-BAU Meldeläufe (paginiert) |
| POST | /v1/handwerk/soka-bau/meldelaufe | bearerAuth | SOKA-BAU Meldelauf anlegen |
| GET | /v1/handwerk/soka-bau/meldelaufe/{id} | bearerAuth | SOKA-BAU Meldelauf Detail |
| POST | /v1/handwerk/soka-bau/meldelaufe/{id}/berechnen | bearerAuth | SOKA-BAU Meldelauf berechnen (Aggregation) |
| POST | /v1/handwerk/soka-bau/meldelaufe/{id}/bestaetigen | bearerAuth | SOKA-BAU Meldelauf bestätigen |
| GET | /v1/handwerk/soka-bau/meldelaufe/{id}/csv-export | bearerAuth | SOKA-BAU Meldelauf als CSV exportieren |
| POST | /v1/handwerk/soka-bau/meldelaufe/{id}/freigeben | bearerAuth | SOKA-BAU Meldelauf freigeben |
| POST | /v1/handwerk/soka-bau/meldelaufe/{id}/gemeldet-markieren | bearerAuth | SOKA-BAU Meldelauf als gemeldet markieren |
| GET | /v1/handwerk/soka-bau/meldungen | bearerAuth | SOKA-BAU Meldungen (paginiert) |
| POST | /v1/handwerk/soka-bau/meldungen | bearerAuth | SOKA-BAU Meldung anlegen |
| PUT | /v1/handwerk/soka-bau/meldungen/{id} | bearerAuth | SOKA-BAU Meldung aktualisieren |