DMN/Software

ProjekteZeiterfassung

Zeiterfassung

Arbeitszeit, Urlaub, Gleitzeit und Rechnungen für kleine Betriebe und Freelancer, nach Arbeitszeitgesetz, Bundesurlaubsgesetz und § 14 UStG. Jede Zahl lässt sich aufklappen und zeigt, woraus sie besteht.

Wochenansicht im erfundenen Musterbetrieb, Kalenderwoche 40: je Tag ein Balken von 6 bis 22 Uhr mit Kommen, Gehen, Pause, Ist, Soll und Differenz. Der Donnerstag ist aufgeklappt und zeigt die Buchungen, die Rechnung Ist 7:56 Stunden minus Soll 7:48 Stunden gleich plus 8 Minuten und einen Hinweis nach § 5 Arbeitszeitgesetz: Ruhezeit nur 9:34 Stunden statt 11
Die Woche einer Beschäftigten in der Demo. Ein aufgeklappter Tag zeigt Buchungen, Rechnung und Hinweise.

Eckdaten

Stand

Tests
1703 Unit-Tests, 1000 Integrationstests gegen ein echtes PostgreSQL, 20 Ende-zu-Ende-Läufe auf Desktop und Handy
E-Rechnung
KoSIT-Validator 16 von 16 Dateien akzeptiert, veraPDF 7 von 7 bestanden
Betrieb
Live und Demo seit 02.10.2026, zwei getrennte Instanzen auf dem eigenen Server

Jede Zahl zeigt, woraus sie besteht

Ein Resturlaub von zehn Tagen ist erst dann glaubwürdig, wenn man sieht, wie er zustande kommt: welcher Anspruch aus welchem Vertrag, welche Anträge, welche Stornierung. Das gilt genauso für die Tageszeit oben im Bild, für das Gleitzeitkonto und für den Rechnungsbetrag.

Die Fachregeln liegen in einem eigenen Paket ohne Datenbank und ohne Uhr. Jede Regel liefert neben dem Ergebnis die Begründung mit, die Oberfläche setzt sie nur noch in Sätze.

Arbeitsrecht als Hinweis, nicht als Sperre

Höchstarbeitszeit, Pausen, Ruhezeit und Sonntagsarbeit nach §§ 3, 4, 5 und 9 ArbZG werden geprüft und als Hinweis am Tag angezeigt. Gebucht wird trotzdem, denn die Zeit wurde ja gearbeitet. Die Regeln sind versioniert, jeder Hinweis nennt die Fassung, nach der er entstanden ist.

Urlaub folgt dem Bundesurlaubsgesetz: Wartezeit, Teilurlaub im Ein- und Austrittsjahr, Übertrag bis zum 31. März und Verfall nur nach einem dokumentierten Hinweis an die Beschäftigte. Feiertage gibt es für alle 16 Bundesländer.

Wer eine Zeit ändern will, stellt einen Antrag mit Begründung. Die Personalverwaltung kann sofort korrigieren, aber nur mit Pflichtgrund, Eintrag im Protokoll und Mail an die Betroffene. Am Monatsende friert ein Abschluss mit Checkliste den Stand ein.

Monatsabschluss September 2026 in der Rolle Personalverwaltung: Checkliste mit dem blockierenden Punkt offene Abwesenheitsanträge und dem Hinweis auf nicht quittierte Befunde nach dem Arbeitszeitgesetz. Der Knopf Monat abschließen bleibt gesperrt, bis die blockierenden Punkte erledigt sind
Monatsabschluss in der Rolle Personalverwaltung. Ein offener Antrag blockiert, ein Hinweis nicht.

Rechnungen nach § 14 UStG

Aus erfassten Projektzeiten entsteht eine Rechnung mit PDF nach DIN 5008, auf Deutsch oder Englisch je nach Kunde. Unterstützt sind Regelbesteuerung, Kleinunternehmer nach § 19, Reverse Charge in der EU, Drittland und Kleinbetragsrechnung. Dazu kommt die E-Rechnung als XRechnung oder ZUGFeRD.

Rechnungsnummern laufen je Jahr lückenlos. Eine Postgres-Sequenz würde bei einem Abbruch Nummern verbrauchen, deshalb zählt eine Zähler-Zeile, die bis zum Ende der Transaktion gesperrt bleibt. Ein Test stellt 20 Rechnungen gleichzeitig aus und erwartet jede Nummer von 1 bis 20 genau einmal. Ausgestellte Rechnungen bleiben unverändert, korrigiert wird per Storno.

Ausgestellte Musterrechnung MUSTER-2026-0009 einer erfundenen Freelancerin an die erfundenen Stadtwerke Lindental: links Datum, Fälligkeit, Netto, Umsatzsteuer 19 Prozent und Rechnungsbetrag, darunter zwei Positionen aus erfassten Stunden, rechts die PDF-Vorschau mit Anschriftfeld, Infoblock, Positionstabelle und dem Wasserzeichen Musterrechnung
Als XRechnung ausgestellte Rechnung im Bereich für Freelancer. In der Demo tragen Rechnungen ein Wasserzeichen und eine Nummer mit MUSTER-.

Was die Datenbank selbst sicherstellt

Zwei Buchungen derselben Person dürfen sich nicht überschneiden. Das prüft nicht die API, sondern ein Exclusion Constraint in Postgres, auch bei zwei Anfragen im selben Moment.

Mehrere Betriebe teilen sich eine Datenbank. Jede Anfrage setzt Betrieb, Rolle und Person nur für ihre eigene Transaktion, Row Level Security mit FORCE filtert danach jede Tabelle. Ohne diesen Kontext liefert die Datenbank keine einzige Zeile, auch nicht dem Eigentümer der Tabellen.

Auszüge aus dem echten Code, gekürzt.

Echter Code aus der Zeiterfassung

db/migrations/20260924000100_time.sql

alter table app.time_entries  add constraint time_entries_no_overlap  exclude using gist (    employee_id with =,    tstzrange(started_at, ended_at, '[)') with &&  )  where (voided_at is null);

packages/rules/src/arbzg/index.ts

const { version, limits } = ruleSetFor(shift.date);const flag = (code, severity, facts = {}) => {  findings.push({ code, severity, date: shift.date,    ruleVersion: version, facts });};if (restMinutes !== null && restMinutes < limits.minRestMinutes) {  flag('ARBZG_5_REST_UNDER_11H', 'warning', { restMinutes });}

apps/api/src/db/database.ts

withTenant<T>(ctx: TenantContext, work: (trx: Trx) => Promise<T>): Promise<T> {  return this.transaction().execute(async (trx) => {    await sql`select set_config('app.tenant_id', ${ctx.tenantId}, true),      set_config('app.user_id', ${ctx.userId ?? ''}, true),      set_config('app.role', ${ctx.role}, true),      …`.execute(trx);    return work(trx);  });}

db/migrations/20260926000200_invoices.sql

insert into app.invoice_counters as c  (tenant_id, year, last_seq)values (app.tenant_id(), year_num, 1)on conflict (tenant_id, year)  do update set last_seq = c.last_seq + 1returning c.last_seq into seq;

Eine Demo für jeden Besuch

Ein Klick auf „Demo starten“ legt einen eigenen Musterbetrieb an: ein erfundenes Steuerbüro in NRW mit zwölf Beschäftigten in drei Teams, Buchungen seit Januar, offenen Anträgen und Hinweisen nach dem Arbeitszeitgesetz. Daneben liegt der Bereich einer erfundenen Freelancerin mit Kunden, Projekten und Rechnungen.

Sicherheit

Zwei Tests halten die Rechte zusammen: Einer liest jede Route der API aus und vergleicht sie mit einer Tabelle der Mindestrollen. Der andere prüft, dass jede Tabelle mit Betriebsbezug durch Row Level Security abgesichert ist. Eine neue Route oder Tabelle ohne Eintrag lässt den Test scheitern.

Technik

Frontend
React 19 mit Vite, TanStack Router und Query, React Aria, i18next für Deutsch und Englisch
Backend
NestJS 11 mit Kysely und zod, PDF mit pdfkit in Worker-Threads
Daten
PostgreSQL 17 mit Row Level Security, Migrationen mit dbmate
Tests
Vitest mit fast-check, Testcontainers, Playwright mit axe, KoSIT-Validator und veraPDF
Betrieb
Live und Demo als getrennte Docker-Compose-Projekte hinter Caddy, verschlüsselte Sicherungen mit age

Grenzen

Die Hinweise nach dem Arbeitszeitgesetz ersetzen keine Rechtsberatung. Bewusst nicht enthalten sind Schichtplanung, Stempeln per GPS oder Terminal, automatischer Pausenabzug, Mahnwesen und eine Schnittstelle zu DATEV. Offen sind Antworten einer Steuerberatung zu Rundung und Rechnungstexten sowie ein grüner CI-Lauf auf GitHub. Bisher laufen die Prüfungen lokal.

Ausprobieren

Kein Konto nötig. Auf der Startseite „Demo starten“ wählen, die Sprache lässt sich oben umschalten.