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.

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.

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.

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.
- Rollenwechsel ohne Passwort: Beschäftigte, Teamleitung, Personalverwaltung, Administration, Freelancer
- Mails gehen nicht raus, sie landen in einem Postausgang in der Demo
- Rechnungen mit Wasserzeichen und Nummern mit MUSTER-, abhängig von der Art des Mandanten, nicht von einem Schalter
- Der Musterbetrieb wird nach 24 Stunden mit allen Eingaben gelöscht
- Begrenzte Zahl an Demos je Anschluss und insgesamt
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.
- Passwörter mit argon2id und Pepper
- Passkeys, TOTP und Wiederherstellungscodes
- Erneute Bestätigung vor dem Ausstellen und Stornieren von Rechnungen
- Session-Cookie mit dem Präfix __Host- und CSRF-Token
- Content-Security-Policy ohne unsafe-inline
- Bedrohungsmodell für den Rechnungsteil, jeder Befund mit Test
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
- Demo zeit-demo.dmn-software.com
- Live zeit.dmn-software.com, nur auf Einladung
Kein Konto nötig. Auf der Startseite „Demo starten“ wählen, die Sprache lässt sich oben umschalten.