Zum Inhalt springen

SOKA-Bau-Meldungen — Winterbeschäftigungs-Umlage, ZVK, Winterausfallgeld §101 SGB III mit DWD-Wetter-Nachweis

In Planung

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


feature_id: handwerk/09-soka-bau
title: SOKA-Bau-Meldungen — Winterbeschäftigungs-Umlage, ZVK, Winterausfallgeld §101 SGB III mit DWD-Wetter-Nachweis
funktionsumfang_ref: §4.9
roadmap_horizont: V2
plattformen:
mobile: aus # reine Backoffice-Mechanik, Wettererfassung auf Baustelle siehe §4.1 Bautagebuch
web: vollständig mit-Bulk
desktop: aus # Web deckt alle Bulk-Use-Cases
owner_rolle: [Admin, Buchhaltung, Bauleitung]
modul_gate_flag: module.handwerk.soka
compliance_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-27
estimate_eng_tage: 22
abhä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 konsumiert

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.

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“.

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.

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.

  • F-W-01SOKA-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-02Lohngruppen-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-03Mitarbeiter-Eingruppierung pro Bau-MA: LG, Betriebsstätte, Eintrittsdatum, SOKA-Meldung aktiv/inaktiv (Geringfügige, Azubis, Angestellte fallen anders).
  • F-W-04Monatsabschluss-Workflow (vorläufig → Korrektur → Freigabe → SOKA-Lieferung → DATEV-Lieferung → Sperre):
    1. Stundenlauf aus Zeiterfassung (Brutto, inkl. Schlechtwetter-Ausfallzeiten).
    2. WBU (1,2 % AG + 0,8 % AN) berechnet pro Bau-MA.
    3. ZVK (regional) berechnet.
    4. ULAK + BBF berechnet.
    5. Tarif-Mindestlohn-Prüfung (Soll-vs-Ist je LG).
    6. Monatsmeldung-PDF + XML-Datei (SOKA-Bau-eXtra-Format).
    7. Elektronische Lieferung über SOKA-Bau-Upload-Portal (UPM) mit AG-Signatur.
  • F-W-05Witterungs-Protokoll pro Baustelle pro Kalendertag: DWD-Daten automatisch gezogen (Temperatur, Niederschlag, Windgeschwindigkeit), Arbeitsausfall-Entscheidung per Checkbox, Kommentar des Bauleiters.
  • F-W-06DWD-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-07Saison-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-08Tarif-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-09Jahresmeldung SOKA. Zum 31.01. des Folgejahres Jahresabschluss-Paket für SOKA-Bau (Gesamtbrutto, alle Beiträge). PDF + XML.
  • F-W-10Beitragsbescheid-Inbox. Rückläufer von SOKA-Bau (Bescheide, Korrektur-Aufforderungen) werden als Aufgaben in Werkszeit getrackt (V2.5).
  • F-X-01Audit-Log-Eintrag pro Monatsmeldung, pro DWD-Datenabruf, pro Saison-KuG-Antrag.
  • F-X-02Webhooks soka.monthly_report.generated, soka.monthly_report.delivered, wettergeld.application.exported.
  • F-A-01Tarif-Eskalations-Stichtage im Kalender: 30 Tage vor TV-Stichtag erinnert Werkszeit automatisch („Tarifrunde TV Bau 01.04.2027 rückt näher“).
  • F-A-02Betriebsrat-Freigabe nicht nötig (keine Leistungs-/Verhaltenskontrolle), aber Aktivierung erfordert Vier-Augen-Prinzip (Admin + Buchhaltung), weil lohnrelevant.
  • F-A-03SOKA-Lieferweg auswählen: UPM-Upload (Webportal), SFTP-Batch (Großkunden), papierhaft (nur Fallback).
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

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.

┌──────────────────────────────────────────────────────────────────────────────────────────┐
│ 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 ] ││
│ │ └───────────────────────────────────────────────────────────────────────────┘│
└──────────────────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────────────────┐
│ 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 UTC
SHA-256 Roh-Datensatz: 4f82…d91e · GoBD-Archiv 10 J in S3 Object Lock
┌──────────────────────────────────────────────────────────────────────────────────────────┐
│ ⚠ 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 ]
┌──────────────────────────────────────────────────────────────────────────────────────────┐
│ 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) ] │
└──────────────────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────────────────┐
│ 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 │
└──────────────────────────────────────────────────────────────────────────────────────────┘

apps/api/src/db/schema/soka.ts
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.


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.rejected
  • soka.witterung.logged, soka.witterung.dwd_unreachable
  • soka.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' }

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.


  • Append-only. soka_monthly_reports, soka_witterung, soka_wettergeld_applications, soka_lohngruppen tragen 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.

nicht zutreffend, weil SOKA-Modul keinerlei Arbeitszeit-Prüfung enthält. Arbeitszeit-Compliance läuft in ../kern/01-zeiterfassung.md.

nicht zutreffend, weil SOKA eine arbeitsrechtliche/sozialversicherungsrechtliche Schiene ist, keine bauvertragliche.

  • 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.

nicht zutreffend, weil Admin-UI (keine ESS-Flows). Trotzdem Werkszeit-interner Baseline: axe-core ohne Critical/Serious, Tastatur-Navigation, Kontrast ≥ 4.5:1.

nicht zutreffend, weil SOKA-Modul keine Leistungs-/Verhaltenskontrolle ermöglicht. Es verarbeitet lohn- und tarifrechtliche Pflichtdaten, nicht performance- oder aktivitätsbasiert.

  • TV Bau Mindestlohn. Versionierte soka_lohngruppen mit 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.

# 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.

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"

  • U-01calcWbu(brutto, region) → AG 1,2 % + AN 0,8 %, kaufmännische Rundung 2 Nachkommastellen, nie negativ.
  • U-02calcZvk(brutto, region): West 18,0 %, Ost abweichend (aus soka_lohngruppen.sourceTvVersion-gebundenem Satz), Grenzwerte.
  • U-03checkWeatherThreshold(temp, precip, wind) — Property-based über alle Grenzfälle (−1 °C Schwelle inklusiv, 4,2 mm/h, 10 m/s).
  • U-04parseDwdResponse(csv) — Parser für DWD Open Data Stunden-CSV; Fallback bei fehlenden Messwerten.
  • U-05validateSokaExtraXml(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 mit validFrom = TV-Stichtag angelegt.
  • 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-DE Light für Monats-Übersicht.
  • I-01 — Multi-Tenant-Hard-Test (EC-05): 2 Tenants, je 1 Report, Cross-Read = 404.
  • I-02 — RLS team Scope (Bauleiter): sieht nur Witterung seiner Projekte.
  • I-03 — Outbox-Idempotenz: doppelter Generate-Call mit gleichem Key → 409.
  • I-04GenerateSokaReport gegen 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.
  • 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.
  • C-01 — Hash-Chain-Integrität über soka_monthly_reports: Mutation einer grossTotalEur-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.

Lokaler 20×-Re-Run von E-01 und E-02 grün.


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.

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).

  • 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).

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:

  1. Wie viele Schlechtwettertage hatten Sie im letzten Winter, für wie viele haben Sie KuG beantragt?
  2. Wer in Ihrem Team erstellt aktuell die SOKA-Monatsmeldung? Wie viel Zeit kostet das?
  3. Hatten Sie in den letzten 3 Jahren eine Zoll- oder SOKA-Betriebsprüfung? Mit welchem Befund?
  4. Wären Sie bereit, das Feature im Beta-Status zu testen, mit Bonus: 1 Jahr kostenloser Business-Plan als Gegenleistung?

Build-Sequenz:

  1. Datenmodell + RLS + Multi-Tenant-Testsoka_tenant_config, soka_lohngruppen, soka_monthly_reports, soka_witterung. Hash-Chain. (3 Tage)
  2. DWD-Adapter + S3-Archiv — Open-Data-Client, CSV-Parse, SHA-256-Stempel, Cron für Nacht-Fetch. (3 Tage)
  3. Lohngruppen + Tarif-Versionierung + F-W-08 Vorabprüfung (read-only Simulation). (2 Tage)
  4. Monatsabschluss-Workflow Backend — Generate → Approve (Vier-Augen) → Deliver. OpenAPI + Dart-Client. (4 Tage)
  5. Web-UI Monats-Übersicht + Stepper + KPI-Block. (3 Tage)
  6. Witterungs-Protokoll UI + DWD-Nachweis-PDF (Typst-Template). (2 Tage)
  7. Saison-KuG-Bundle (ZIP-Export) + BA-Kiehl-Formular-PDF. (2 Tage)
  8. SOKA-eXtra-XML-Generator + XSD-Validator + UPM-Upload-Client. (2 Tage)
  9. 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_witterung oder soka_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
MethodePfadAuthZweck
GET/v1/handwerk/soka-bau/beitragssaetzebearerAuthSOKA-BAU Beitragssätze (paginiert)
POST/v1/handwerk/soka-bau/beitragssaetzebearerAuthSOKA-BAU Beitragssatz anlegen
GET/v1/handwerk/soka-bau/beitragssaetze/{id}bearerAuthSOKA-BAU Beitragssatz Detail
PUT/v1/handwerk/soka-bau/beitragssaetze/{id}bearerAuthSOKA-BAU Beitragssatz aktualisieren
GET/v1/handwerk/soka-bau/meldelaufebearerAuthSOKA-BAU Meldeläufe (paginiert)
POST/v1/handwerk/soka-bau/meldelaufebearerAuthSOKA-BAU Meldelauf anlegen
GET/v1/handwerk/soka-bau/meldelaufe/{id}bearerAuthSOKA-BAU Meldelauf Detail
POST/v1/handwerk/soka-bau/meldelaufe/{id}/berechnenbearerAuthSOKA-BAU Meldelauf berechnen (Aggregation)
POST/v1/handwerk/soka-bau/meldelaufe/{id}/bestaetigenbearerAuthSOKA-BAU Meldelauf bestätigen
GET/v1/handwerk/soka-bau/meldelaufe/{id}/csv-exportbearerAuthSOKA-BAU Meldelauf als CSV exportieren
POST/v1/handwerk/soka-bau/meldelaufe/{id}/freigebenbearerAuthSOKA-BAU Meldelauf freigeben
POST/v1/handwerk/soka-bau/meldelaufe/{id}/gemeldet-markierenbearerAuthSOKA-BAU Meldelauf als gemeldet markieren
GET/v1/handwerk/soka-bau/meldungenbearerAuthSOKA-BAU Meldungen (paginiert)
POST/v1/handwerk/soka-bau/meldungenbearerAuthSOKA-BAU Meldung anlegen
PUT/v1/handwerk/soka-bau/meldungen/{id}bearerAuthSOKA-BAU Meldung aktualisieren