Aufbau: Hero mit Plantafel-Vorschau, Leitgedanken, 12 Funktionen, vertiefte Abschnitte zu
Planung, Baustelle und Lotse, Ablauf, Gewerke, Sicherheit, Testphase, häufige Fragen und
Fußzeile. Texte auf Deutsch und Englisch, Vorschauen als Markup statt Screenshots.
Angemeldete Nutzer leitet die Route weiterhin auf Dashboard bzw. mobile Ansicht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Benachrichtigungs- und Mailversand lief im Handler noch in der offenen Transaktion und
zählte gegen deren Zeitlimit; unter Volllast brachen dadurch wechselnde Tests ab
(test-einsatz-field 31 s, test-planung-recommend). withDeferredEvents puffert Ereignisse
innerhalb von inTransaction und stellt sie nach dem Commit zu, bei Rollback gar nicht —
analog zu withDeferredAudit und passend zum dokumentierten Vertrag in events.ts.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Neues Skript scripts/planning-demo.ts plant über createWorkOrder + scheduleWorkOrder eine
Woche für beide Kolonnen (Auslastung, Überbuchung, Überschneidung, Mehrtagesauftrag,
ungeplante Aufträge); die Demo-Termine aus dem Seed liegen relativ zum Seed-Tag und wandern
sonst aus dem Standardzeitraum.
Live-Lage: Umschalter Karte · Kacheln · Liste, Kachelansicht mit Status, Auftrag und Ort;
LiveMap meldet nicht erreichbare Kartenkacheln und die Ansicht wechselt automatisch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Upload-Antwort 422 trial_expired gilt als vorübergehend: Datei bleibt auf dem Gerät,
wartende Op bleibt in der Warteschlange, der Sync-Durchlauf stoppt mit Backoff.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Unter paralleler Last scheiterten Tests sporadisch mit „Unable to start a transaction in the given time“.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der HTTP-Smoke zeigte 500 auf /m für abgelaufene Testmandanten: mobile Seitenkontexte (field,
emergency) und der Import-Datei-Download nutzen moduleGuard zum Lesen. moduleGuard(key, { read: true })
überspringt dort die Schreibsperre; der Guard-Check verbietet den Lese-Modus in Server-Actions.
POST /api/v1/work-orders/[id]/documents läuft nicht über withApi und prüft die Sperre jetzt explizit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fake-Provider mit geskripteten Tool-Aufrufen: Zuordnung, Vorschlag ohne Schreibzugriff,
Ausführung über Services, Blocker, Einmaligkeit/Ablauf/fremder Nutzer, Scope,
Mandantentrennung, Datenminimierung, Einstellung aus, Budget- und Rundengrenze.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
/m/lotse mit Verlauf, Chips, Sprungzielen, Aktionskarten (Bestätigen/Bearbeiten/Verwerfen),
Spracheingabe mit editierbarem Transkript und Offline-Hinweis; Navigationseintrag, Button
„Lotse fragen“ im Auftragsdetail, Schalter und Datenfluss in /settings/lotse, Audit-Labels.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Chatverlauf (LotseConversation/-Message) und Aktionskarten (LotseActionProposal) als
Mandantentabellen mit RLS; Schalter lotseChatEnabled. Tool-Use-Schleife mit Runden- und
Tokengrenze, Datenminimierung per Platzhalter, Auftragszuordnung im Sichtbarkeits-Scope,
Bestätigen/Verwerfen/Alle bestätigen mit Hash, Ablauf und Idempotenz, Transkriptions-API.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Direkter Übergang nach billed → invalid use_billing_overview (nach der Rechteprüfung),
Button im Auftragsdetail ausgeblendet, markBilled läuft über den Abrechnungseintrag.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
test-abrechnung-service (118 Prüfungen inkl. Mandantentrennung und PDF-Render-Smoke), test-abrechnung-sync (21), Fixture-Zeilen für die neuen Tenant-Modelle, Rollen-Matrix billing:*, smoke-auth um /billing, Detail, Druckansicht, Auftrags-Tab und mobile Meilensteine erweitert, scripts/billing-backfill.ts, docs/craftvia/lanes/abrechnung.md, ABNAHME §3 „Abrechnungsübersicht“ (keine Buchhaltung). Gate 69/69, RLS 69/69.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
/billing mit Tabs Offen/Abgerechnet/Storniert, Filtern und Mehrfachdruck; /billing/[id] mit Abrechnungsblatt und Aktionen (abgerechnet mit Rechnungsnummer, PDF, Rechnungsnummer ändern, stornieren); /billing/print; Auftrags-Tab „Meilensteine & Abrechnung“; mobiler Abschnitt „Erreicht melden“ (offline); Dashboard-Kachel „Bereit zur Abrechnung“; /api/v1/billing*, Meilenstein-Endpunkte und OpenAPI.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
syncBillingCandidates (idempotent, Event-Hook für work_order.released_for_billing und report.approved), Aufstellung ohne Preise (nur freigegebene Zeiten, Fahrzeit getrennt, Pausen informativ, Anfahrten-Zählung, Material), markBilled mit eingefrorenem Snapshot und Positionszuordnung, voidBilling mit Grund und neuem offenen Eintrag (billed → released_for_billing nur über applyTransition-Option billingVoid), Meilenstein-Flow mit Events, Job billing-pdf (Dokument other/backoffice_only), Sync-Op milestone.reach.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Neue Tenant-Tabellen work_order_milestones und billing_records (RLS, TENANT_MODELS, PII-Felder), Partial-Unique-Indizes je Quelle, billing_record_id an time_entries/material_usages. Rechte billing:read/billing:write (Backoffice, Mandantenadmin), Modul billing, Events milestone.*.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Seit L12 darf ein Nutzer nur eine laufende Uhr haben. DEMO-06/DEMO-07 (laufende Uhren) werden deshalb nach den vergangenen Einsätzen derselben Monteure angelegt; Inhalte unverändert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Plantafel: Standard heute + nächste 4 Werktage (Kolonnen als Zeilen, heutige Spalte mit Live-Status), Heute mit Kolonnen als parallelen Spalten (6–20 Uhr), Woche/nächste Woche, Auslastung, Konflikte/Hinweise, Verzugs- und Gefährdungs-Badges, Drag & Drop (@dnd-kit/core) mit Bestätigungs-Popover und Tastatur-Alternative, ungeplante Aufträge mit Vorschlägen, früher fertig mit Vorziehen, Kolonnenkapazität-Popup. Live-Lage: Leaflet/OSM-Karte mit einem Marker je Kolonne, Liste, Polling 30 s, Hinweis keine GPS-Ortung; CSP img-src für Kacheln. Dashboard-Kacheln Planung heute und Konflikte diese Woche, Einsatz-Vorschläge im Auftragsdetail, Navigation mit Unterpunkten, Smoke-Prüfungen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Provider-Interface (nominatim|none), strukturierte Suche mit User-Agent und Accept-Language, Drosselung 1/s je Prozess, Job geocode-site (Worker: Concurrency 1 + Limiter), Cache am Objekt, manuelle Koordinaten bleiben, Auslöser Anlage/Adressänderung/Import-Bestätigung nur per Queue, Backfill-Skript, Env-Beispiele. Tests mit Fake-Provider, Nominatim nie aufgerufen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Backoffice-Rolle erhält field:correct_time (User-Entscheidung): Zeiten für
Monteure/Teamleiter direkt freigegeben anlegen und korrigieren
- Eigene Zeiten erfordern weiterhin field:record_own_time (Backoffice erfasst
keine eigenen Zeiten)
- Auftragsdetail › Zeiten: Formular „Zeit für Mitarbeiter erfassen“ (Mitarbeiter,
Art, Datum, von–bis oder Dauer, Begründung; Mandanten-Zeitzone)
- Server Action recordTimeForUserAction (moduleGuard work_orders)
- Test test-backoffice-time (Backoffice, fremdes Team, Monteur, Mandantentrennung)
Demo-Daten lokal bereinigt: je Monteur nur noch eine aktive Uhr.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
docs/craftvia/lanes/zeiterfassung.md (Umfang, Dateien, Tests, Gate/RLS, Lücken),
ABNAHME §4 „Arbeitszeitkorrektur“ auf die Freigaberegel aktualisiert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
test-zeiterfassung-service (78 Prüfungen) und test-zeiterfassung-sync (29 Prüfungen),
Rollen-Matrix um field:record_own_time und time:approve ergänzt, smoke-auth um
/m/time, /m/time/new, /m/approvals, Uhr-Leiste und /work-orders/time-approvals.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Laufende-Uhr-Leiste auf allen /m-Seiten, Start/Pause direkt auf den Auftragskarten,
Auto-Wechsel-Dialog, Auftragsdetail mit Pause / Für heute beenden / Abschließen-Link
und Segmentwechseln, /m/time (Tagesübersicht, Nachtragen, Korrektur vorschlagen),
/m/approvals für Teamleiter, /work-orders/time-approvals mit Sammelfreigabe,
Zeiten-Tab mit Badges und Inline-Freigabe, Dashboard-Kachel, Texte de/en.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
time-entries.ts (manuelle Einträge, Korrekturvorschläge, Freigabe/Ablehnung mit
Team-Scope), Sessions in inTransaction mit switchFromOther, stopForToday,
Segmentwechsel und getMyActiveSession. Sync-Ops time.add_manual,
time.propose_correction, session.stop_day, session.segment inkl. optimistischer
Offline-Ansicht. Bericht, Zeiten-Tab, Dashboard und Lotse zählen nur freigegebene
Zeiten; Abrechnungsfreigabe blockiert bei offenen Zeiten. Empfänger und Links der
Zeit-Events. Notdienst-Start pausiert eine laufende Uhr automatisch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
17 Abnahmekriterien und 12 User Stories mit Status und Nachweis (Testskript, Route,
Lane-Bericht); Soll-/Kann-Funktionen; getroffene MVP-Annahmen zu den offenen
fachlichen Entscheidungen; bekannte Einschränkungen und nicht verifizierte Punkte.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- rate-limit: neuer Scope login (20/15 min, LOGIN_RATE_LIMIT_PER_15_MIN) je IP und
je Konto; geprüft in verifyIdentityPassword (Login-Seite + Credentials-Provider),
gedrosselt verhält sich wie Fehlanmeldung (generisch, konstante Laufzeit) – L10a
- mail/worker: MailAttachmentError wie MailNotConfiguredError unrecoverable
(fremder Mandant/Prüfsumme/Größe ändern sich nicht durch Warten) – L11
- field/uploads: Dokument-Audit nur noch in storeFile (vorher doppelt) – L10a
- Test test-login-rate-limit
Gate 65/65 grün; komplette Suite mit RLS_ENFORCED=true 65/65 grün.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- MailJob/EnqueueInput: attachments als { documentId } (keine Bytes in Redis, nur mit tenantId)
- deliverMail: Anhänge mandantengebunden aus MailLog.tenantId laden, SHA-256 prüfen,
Größenlimit MAIL_MAX_ATTACHMENT_BYTES (Default 10 MB); Fehler -> failed ohne Versand
- SMTP-Provider reicht Anhänge an nodemailer durch; optionaler Provider für Tests
- Template craftvia_report_customer (de/en) ohne App-Link, eigene CUSTOMER_TEMPLATE_KEYS
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- test-tenant-isolation: Compound-Key mit fremdem Mandanten – im Owner-Betrieb Throw
(Tenant-Guard), unter scharfer RLS liefert die DB null; beides = kein Datenabfluss
- run-tests.ts: lädt .env und leitet RLS_DATABASE_URL (Rolle craftvia_app) aus
DATABASE_URL ab, wenn RLS_ENFORCED=true und keine URL gesetzt ist
Nachweis: RLS_ENFORCED=true npm run test → 52/52; npm run gate → 52/52.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- docker-compose.coolify(.prebuilt).yml: Service craftvia-worker (Target worker, Chromium,
shm_size 1gb, gleiche Härtung), Craftvia-Variablen für app und worker.
- Dockerfile: worker-Stage mit HOME=/home/app (Chromium-Profil für non-root); lokaler
docker build der Targets runner und worker erfolgreich, PDF-Erzeugung im Image geprüft.
- .env.example/.env.prod.example/.env.coolify.example: alle Craftvia-Variablen inkl. RLS,
KI-Provider, PDF_CHROMIUM_PATH, OFFLINE_MAX_DAYS, API_RATE_LIMIT_*, AI_GENERATION_RETENTION_DAYS,
AI_MONTHLY_TOKEN_LIMIT.
- CI (.github, .gitea): Job gate mit Postgres (pgvector) und Redis als Service: migrate deploy,
seed, Passwort für craftvia_app, tsc, lint, build, npm run test.
- docs/craftvia/DEPLOY.md (aus den Certvia-Deploy-Docs abgeleitet): Architektur, Domains, Secrets,
Worker, Migrationen, RLS-Aktivierung, Backup/Restore, KI, Rate Limits, Aufbewahrung, Smoke,
Update/Rollback. build-and-push-images.sh baut craftvia-worker.
- Certvia-/ISMS-Dokumente aus docs/ nach docs/_certvia-archiv/ (mit README); Verweise in README.md
und Skript-Kommentaren angepasst.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Aufräumpunkt g (L1 offener Punkt 10): components/backoffice-frame.tsx – unter 1024 px ist die
Sidebar ein Drawer hinter einem Menü-Button (44 px, aria-expanded, schließt bei Navigation,
Hintergrund, Escape); ab 1024 px statisch wie bisher. Header kompakter auf schmalen Screens.
- Aufräumpunkt i (L7 offener Punkt 7): mobiler Berichtseditor speichert ungesicherte Eingaben
über useOfflineDraft (IndexedDB je Mandant/Nutzer). Ein Entwurf wird nur wiederhergestellt,
solange die Servertexte unverändert sind (sonst gewinnt der Server, z. B. nach Übernahme eines
Lotse-Vorschlags); nach Speichern/Absenden gelöscht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>