L14 Abrechnungsübersicht: Datenmodell, Migration, Rechte und Modul

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>
This commit is contained in:
2026-09-15 10:35:20 +02:00
co-authored by Claude Opus 5
parent 9b087168d1
commit 6358d5762c
11 changed files with 220 additions and 3 deletions
+2
View File
@@ -71,6 +71,8 @@ const ENTITY_LABEL: Record<string, string> = {
import_job: "Auftragsimport",
material_usage: "Material",
time_entry: "Arbeitszeit",
billing_record: "Abrechnungseintrag",
work_order_milestone: "Meilenstein",
work_session: "Einsatzzeit",
photo: "Foto",
voice_note: "Sprachnotiz",
+5 -1
View File
@@ -23,13 +23,17 @@ export const EVENT_TYPES = [
"time.approval_requested",
"time.approved",
"time.rejected",
// L14 Abrechnungsübersicht (Meilensteine)
"milestone.reached",
"milestone.confirmed",
"milestone.rejected",
] as const;
export type EventType = (typeof EVENT_TYPES)[number];
export type DomainEvent = {
type: EventType;
entityType: "work_order" | "report" | "import_job" | "sync_operation" | "time_entry";
entityType: "work_order" | "report" | "import_job" | "sync_operation" | "time_entry" | "milestone";
entityId: string;
/** Short, human-readable facts for templates (no PII beyond what the recipient may see). */
data?: Record<string, string | number | boolean | null>;
+1
View File
@@ -28,6 +28,7 @@ export const MODULES = [
{ key: "documents", name: "Dokumente", href: "/documents", nav: "documents" },
{ key: "notifications", name: "Benachrichtigungen", href: "/notifications", nav: "notifications" },
{ key: "lotse", name: "Lotse (KI-Assistent)", href: null, nav: "lotse" },
{ key: "billing", name: "Abrechnungsübersicht", href: "/billing", nav: "billing" },
] as const satisfies readonly ModuleDef[];
export type ModuleKey = (typeof MODULES)[number]["key"];
+3
View File
@@ -72,6 +72,9 @@ export const TENANT_MODELS: readonly string[] = [
"Notification",
"SyncOperation",
"AiGeneration",
// L14 Abrechnungsübersicht
"WorkOrderMilestone",
"BillingRecord",
];
/**
+3
View File
@@ -123,6 +123,9 @@ const TENANT_MODELS = new Set<string>([
"Notification",
"SyncOperation",
"AiGeneration",
// L14 Abrechnungsübersicht
"WorkOrderMilestone",
"BillingRecord",
// WebAuthnCredential/Identity sind identitäts-global (kein tenant_id) → NICHT hier.
// Craftvia-Fachmodelle hier ergänzen — UND in src/server/backup/topology.ts
// (TENANT_MODELS) sowie per `SELECT enable_tenant_rls('<table>')` in der Migration
+7
View File
@@ -48,6 +48,13 @@ export const PII_REFERENCE_FIELDS: readonly PiiReference[] = [
{ model: "SyncOperation", field: "userId" },
{ model: "SyncOperation", field: "resolvedById" },
{ model: "AiGeneration", field: "createdById" },
// L14 Abrechnungsübersicht
{ model: "WorkOrderMilestone", field: "createdById" },
{ model: "WorkOrderMilestone", field: "reachedById" },
{ model: "WorkOrderMilestone", field: "confirmedById" },
{ model: "WorkOrderMilestone", field: "rejectedById" },
{ model: "BillingRecord", field: "billedById" },
{ model: "BillingRecord", field: "voidedById" },
// Free-text person data of END CUSTOMERS (Customer/Contact/Site/Signature.signerName) is
// tenant business data under data processing — not part of the employee subject export.
];
+3
View File
@@ -57,6 +57,9 @@ export const PERMISSIONS = [
"settings:templates",
// Lotse (KI-Assistent)
"lotse:use",
// L14 Abrechnungsübersicht (keine Buchhaltung): Übersicht lesen / abgerechnet markieren, stornieren, Meilensteine bestätigen
"billing:read",
"billing:write",
] as const;
export type Permission = (typeof PERMISSIONS)[number];