# KanzlAI-mGMT: Full System Roadmap

**Author:** cronus (inventor)
**Date:** 2026-03-28
**Status:** Design proposal — awaiting review

---

## Vision

KanzlAI-mGMT becomes the **first Kanzleimanagement system built for UPC patent litigation** — with full general-purpose law firm management that rivals RA-MICRO and Kleos, plus a UPC-specific intelligence layer that no competitor can match.

The competitive moat: m's existing infrastructure (youpcms scraper, 1,600+ structured UPC cases, legal knowledge graph, mLex research tools) feeds directly into KanzlAI, making it the only software that **understands** UPC proceedings — not just manages them.

---

## What Exists (MVP — Shipped)

| Area | Status |
|------|--------|
| Multi-tenant auth (Supabase Auth + RLS) | Done |
| Case management (CRUD, parties, timeline) | Done |
| Deadline management (CRUD, UPC+ZPO rules, calculator, holiday adjustment) | Done |
| Appointments (CRUD, list + calendar view) | Done |
| AI extraction (Claude API — PDF/text to deadlines) | Done |
| Dashboard (traffic lights, timeline, AI summary, quick actions) | Done |
| CalDAV bidirectional sync (deadlines as VTODO, appointments as VEVENT) | Done |
| Document upload (Supabase Storage, 50MB max) | Done |
| Notes system (polymorphic, attached to cases/deadlines/appointments/events) | Done |
| Detail pages with breadcrumbs and URL-based navigation | Done |
| Team management (invite by email, owner/admin/member roles) | Done |
| Settings page (CalDAV config, tenant info) | Done |

---

## Priority Tiers

- **P0 (Must-Have)** — Without these, it's not a real Kanzleimanagement. Required for any law firm to consider adopting.
- **P1 (Should-Have)** — Makes KanzlAI competitive with established players. Required to win against RA-MICRO/Advoware.
- **P2 (Differentiator)** — UPC-specific intelligence and advanced AI. What makes a patent lawyer say "I need THIS, not RA-MICRO."
- **P3 (Nice-to-Have)** — Polish, integrations, and extended capabilities.

---

## P0: Must-Have Features

### 1. Audit Trail (Revisionsprotokoll)

**Why:** Legal requirement. § 50 BRAO mandates orderly file-keeping. DSGVO Art. 5(2) requires demonstrable compliance. Professional liability insurance (Berufshaftpflicht) expects change traceability. Without this, no serious firm will adopt the software.

**Design:**

The existing `case_events` table handles case-level events but is not a proper audit log. We need a dedicated, **append-only** audit log that captures every mutation across the system.

**New table: `kanzlai.audit_log`**

```sql
CREATE TABLE kanzlai.audit_log (
    id          BIGSERIAL PRIMARY KEY,
    tenant_id   UUID NOT NULL REFERENCES kanzlai.tenants(id),
    user_id     UUID,                          -- auth.users, NULL for system actions
    action      TEXT NOT NULL,                  -- 'create', 'update', 'delete', 'view', 'export'
    entity_type TEXT NOT NULL,                  -- 'case', 'deadline', 'appointment', 'document', ...
    entity_id   UUID,                          -- ID of affected entity
    old_values  JSONB,                         -- Previous state (for updates/deletes)
    new_values  JSONB,                         -- New state (for creates/updates)
    ip_address  INET,                          -- Client IP
    user_agent  TEXT,                           -- Browser/client info
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- Append-only: NO UPDATE or DELETE policies
-- Partitioned by month for performance at scale
-- Index on (tenant_id, entity_type, entity_id) for entity history
-- Index on (tenant_id, user_id, created_at) for user activity
-- Index on (tenant_id, created_at) for chronological browsing
```

**What to log:**

| Event | entity_type | Details |
|-------|-------------|---------|
| Case create/update/archive | `case` | Full old/new state diff |
| Deadline create/update/complete/delete | `deadline` | Including calculated vs manual source |
| Appointment CRUD | `appointment` | Time changes especially |
| Document upload/download/delete | `document` | File name, size, who accessed |
| Party add/remove/update | `party` | Name changes, role changes |
| Note create/update/delete | `note` | Content diff |
| Conflict check performed | `conflict_check` | Query, results, clearance decision |
| beA message sent/received | `bea_message` | Sender, recipient, subject |
| Invoice generated/sent | `invoice` | Amount, recipient |
| User login/logout | `auth` | IP, success/failure |
| Data export (DATEV etc.) | `export` | What was exported |
| Settings change | `settings` | CalDAV config, tenant settings |
| Team member invite/remove | `membership` | Who, role |

**Backend implementation:**
- Middleware-based: wrap all handlers with an audit logger that captures before/after state
- `AuditService.Log(ctx, action, entityType, entityID, oldValues, newValues)` — used by all services
- `GET /api/audit-log?entity_type=X&entity_id=Y&from=DATE&to=DATE` — paginated query
- `GET /api/audit-log/entity/{type}/{id}` — full history of one entity

**Frontend:**
- "Verlauf" (History) tab on every detail page showing change log
- Global audit log page at `/protokoll` for admins/owners
- Each entry shows: timestamp, user, action, human-readable diff

**Retention:** 6 years per § 50 BRAO, with automated cleanup jobs for expired data.

---

### 2. Conflict of Interest Checks (Interessenkollisionsprüfung)

**Why:** Statutory obligation under § 43a Abs. 4 BRAO. Failure to check = professional misconduct. Every competitor has this. A law firm cannot ethically operate without it.

**Design:**

**Trigger points:**
1. New case creation — check all party names against the database
2. New party added to existing case — re-run check
3. Manual check from a search interface

**New table: `kanzlai.conflict_checks`**

```sql
CREATE TABLE kanzlai.conflict_checks (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID NOT NULL REFERENCES kanzlai.tenants(id),
    case_id     UUID REFERENCES kanzlai.cases(id),      -- which case triggered the check
    query_names TEXT[] NOT NULL,                          -- names that were checked
    results     JSONB NOT NULL,                           -- matched parties with scores
    status      TEXT NOT NULL DEFAULT 'pending',          -- 'pending', 'cleared', 'conflict_found'
    cleared_by  UUID,                                     -- user who approved
    cleared_at  TIMESTAMPTZ,
    notes       TEXT,                                     -- explanation for clearance
    created_by  UUID NOT NULL,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);
```

**Search algorithm (phased):**

| Phase | Method | Coverage |
|-------|--------|----------|
| 1 | Exact name match (case-insensitive) | Direct hits |
| 2 | Trigram similarity (`pg_trgm` extension, threshold 0.3) | Typos, variations |
| 3 | Cologne phonetic algorithm | Sound-alike German names |
| 4 | Corporate relationship graph | Subsidiaries, affiliated entities |

PostgreSQL `pg_trgm` is already available in Supabase. Cologne phonetic can be implemented as a PL/pgSQL function or in Go.

**API:**
- `POST /api/conflict-check` — run check against names, return matches with confidence scores
- `POST /api/conflict-check/{id}/clear` — mark as cleared (partner/admin only)
- `GET /api/conflict-checks?case_id=X` — list checks for a case

**Frontend:**
- Automatic check on case creation: after saving, show results in a modal before the case can be worked on
- Manual check page at `/kollision` with a multi-name search form
- Results display: matched party name, matched case (case number + title), role on that case, match confidence, match method (exact/fuzzy/phonetic)
- "Freigeben" (clear) button for authorized users with mandatory notes field

**Corporate relationships (Phase 4 — later):**

New table for tracking corporate structures:

```sql
CREATE TABLE kanzlai.party_relationships (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id       UUID NOT NULL,
    party_id        UUID NOT NULL REFERENCES kanzlai.parties(id),
    related_party_id UUID NOT NULL REFERENCES kanzlai.parties(id),
    relationship    TEXT NOT NULL,  -- 'subsidiary', 'parent', 'affiliate', 'successor'
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);
```

---

### 3. Role-Based Permissions (Rollenverwaltung)

**Why:** Law firms have strict hierarchies. A Sekretärin should not modify fee agreements. A Referendar should not clear conflict checks. The current system has owner/admin/member, but no **functional** role differentiation.

**Design:**

Extend the existing `user_tenants.role` from simple owner/admin/member to a proper permission system:

**Roles:**

| Role | German | Permissions |
|------|--------|-------------|
| `partner` | Partner/Sozius | Full access, clear conflicts, approve invoices, manage team, view audit log |
| `associate` | Anwalt/Anwältin | Case work, create/edit cases/deadlines/documents, time tracking, view own cases |
| `paralegal` | Rechtsanwaltsfachangestellte/r | Create/edit cases, manage deadlines, upload documents, no billing access |
| `secretary` | Sekretär/in | Manage appointments, deadlines, upload documents, no case creation |
| `trainee` | Referendar/in | View access, add notes, limited editing (supervised) |
| `extern` | Externer Zugang | Read-only view of assigned cases (for client portal) |

**New table: `kanzlai.permissions`**

```sql
CREATE TABLE kanzlai.permissions (
    role        TEXT NOT NULL,
    resource    TEXT NOT NULL,      -- 'cases', 'deadlines', 'billing', 'audit', 'team', 'conflicts'
    action      TEXT NOT NULL,      -- 'view', 'create', 'edit', 'delete', 'approve'
    allowed     BOOLEAN NOT NULL DEFAULT false,
    PRIMARY KEY (role, resource, action)
);
```

Seed with a sensible default permission matrix. Tenants can override per-role if needed (stored in `tenants.settings.permissions` JSONB).

**Backend changes:**
- Authorization middleware checks `(role, resource, action)` before handler execution
- Replace simple `role == "owner" || role == "admin"` checks with permission lookups
- `GET /api/permissions` — list permissions for current user
- `PUT /api/permissions` — update role permissions (owner only)

**Frontend changes:**
- Settings page gets a "Rollen & Berechtigungen" section
- UI elements hidden/disabled based on user permissions
- Permission-aware API client that prevents unauthorized requests

---

### 4. Email Notifications & Reminders (Benachrichtigungen)

**Why:** A deadline management system without reminders is a liability. If a Frist is missed because nobody was notified, the software is actively harmful. This is the most dangerous gap in the current MVP.

**Design:**

**Notification types:**

| Trigger | Recipients | Channel | Timing |
|---------|------------|---------|--------|
| Deadline warning date reached | Case assigned users | Email + in-app | Morning of warning_date |
| Deadline due tomorrow | Case assigned users | Email + in-app | Morning before due_date |
| Deadline overdue | Case assigned users + partners | Email + in-app | Morning after due_date |
| Appointment in 1 hour | Appointment attendees | In-app push | 1 hour before |
| New case event | Case assigned users | In-app | Immediate |
| Conflict check needed | Partners | Email + in-app | On case creation |
| beA message received | Assigned lawyer | Email + in-app | Immediate |
| Team member invited | Invitee | Email | Immediate |

**New tables:**

```sql
-- Notification preferences per user
CREATE TABLE kanzlai.notification_preferences (
    user_id     UUID NOT NULL,
    tenant_id   UUID NOT NULL,
    channel     TEXT NOT NULL,           -- 'email', 'in_app', 'push'
    event_type  TEXT NOT NULL,           -- 'deadline_warning', 'deadline_due', 'deadline_overdue', ...
    enabled     BOOLEAN NOT NULL DEFAULT true,
    PRIMARY KEY (user_id, tenant_id, channel, event_type)
);

-- In-app notification inbox
CREATE TABLE kanzlai.notifications (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID NOT NULL,
    user_id     UUID NOT NULL,           -- recipient
    title       TEXT NOT NULL,
    body        TEXT,
    link        TEXT,                    -- deep link to relevant page
    event_type  TEXT NOT NULL,
    entity_type TEXT,
    entity_id   UUID,
    read_at     TIMESTAMPTZ,             -- NULL = unread
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- Case user assignments (who works on which case)
CREATE TABLE kanzlai.case_assignments (
    case_id     UUID NOT NULL REFERENCES kanzlai.cases(id) ON DELETE CASCADE,
    user_id     UUID NOT NULL,
    role        TEXT NOT NULL DEFAULT 'assigned',  -- 'lead', 'assigned', 'observer'
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    PRIMARY KEY (case_id, user_id)
);
```

**Backend:**
- **Notification worker:** Background goroutine (like CalDAV sync) that runs every minute:
  1. Query deadlines where `warning_date = today` or `due_date = tomorrow` or `due_date < today AND status != 'completed'`
  2. Look up case assignments to find recipients
  3. Create in-app notifications + send emails via `m mail send`
  4. Track sent notifications to avoid duplicates (dedup by entity_id + event_type + date)
- **API:** `GET /api/notifications` (paginated, unread first), `PATCH /api/notifications/{id}/read`, `PATCH /api/notifications/read-all`
- **Email service:** Uses `m mail send` for reliable delivery with Sent folder copy

**Frontend:**
- Bell icon in header with unread count badge
- Notification dropdown/panel showing recent notifications
- Notification preferences page in settings
- Case detail: "Zugewiesene Personen" section for managing case assignments

---

### 5. Time Tracking (Zeiterfassung)

**Why:** Every Kanzleimanagement system has this. Lawyers bill by the hour. Without time tracking, they need a separate system, breaking the workflow.

**Design:**

**New table: `kanzlai.time_entries`**

```sql
CREATE TABLE kanzlai.time_entries (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID NOT NULL REFERENCES kanzlai.tenants(id),
    case_id     UUID NOT NULL REFERENCES kanzlai.cases(id),
    user_id     UUID NOT NULL,
    date        DATE NOT NULL,
    duration    INTEGER NOT NULL,                  -- minutes
    description TEXT NOT NULL,
    activity    TEXT,                               -- 'research', 'drafting', 'hearing', 'call', 'review', 'travel'
    billable    BOOLEAN NOT NULL DEFAULT true,
    billed      BOOLEAN NOT NULL DEFAULT false,     -- linked to invoice
    invoice_id  UUID,                               -- set when billed
    hourly_rate NUMERIC(10,2),                      -- rate at time of entry (snapshot)
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- User hourly rates (can change over time)
CREATE TABLE kanzlai.hourly_rates (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID NOT NULL,
    user_id     UUID NOT NULL,
    rate        NUMERIC(10,2) NOT NULL,            -- EUR per hour
    valid_from  DATE NOT NULL,
    valid_to    DATE,                               -- NULL = current
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);
```

**API:**
- `GET /api/time-entries?case_id=X&user_id=Y&from=DATE&to=DATE`
- `POST /api/time-entries` — create entry
- `PUT /api/time-entries/{id}` — update (only if not billed)
- `DELETE /api/time-entries/{id}` — delete (only if not billed)
- `GET /api/time-entries/summary?group_by=case|user|month` — aggregated view

**Frontend:**
- Timer widget in header — click to start/stop, auto-fills duration
- Case detail: "Zeiterfassung" tab showing time entries for that case
- Global time entry page at `/zeit` with weekly view
- Quick entry: case selector + duration + description (one-line form)
- Activity type dropdown for categorization
- Monthly summary view grouped by case

**Running timer:**
- Store active timer in localStorage (case_id, start_time)
- Show elapsed time in header
- On stop: pre-fill a time entry creation form

---

### 6. RVG Fee Calculator (Gebuehrenrechner)

**Why:** Table stakes for any German law firm software. The RVG Gebührenrechner is what turns time tracking into invoices.

**Design:**

**New tables:**

```sql
-- RVG fee table (Anlage 2 zu § 13 RVG) — versioned for updates
CREATE TABLE kanzlai.rvg_fee_table (
    id              SERIAL PRIMARY KEY,
    version         TEXT NOT NULL,                  -- '2025-06-01' (KostBRAeG 2025)
    value_up_to     NUMERIC(12,2) NOT NULL,         -- Gegenstandswert bis
    base_fee        NUMERIC(10,2) NOT NULL,         -- Gebühr
    valid_from      DATE NOT NULL,
    valid_to        DATE                            -- NULL = current
);

-- Fee type catalog (VV-Nummern)
CREATE TABLE kanzlai.rvg_fee_types (
    id              SERIAL PRIMARY KEY,
    vv_number       TEXT NOT NULL,                  -- 'VV 3100', 'VV 3104', etc.
    name            TEXT NOT NULL,                  -- 'Verfahrensgebühr 1. Instanz'
    rate            NUMERIC(4,2) NOT NULL,          -- Gebührensatz (e.g., 1.3)
    rate_min        NUMERIC(4,2),                   -- For Rahmengebühren (range fees)
    rate_max        NUMERIC(4,2),
    context         TEXT,                           -- 'civil_first_instance', 'out_of_court', etc.
    category        TEXT NOT NULL                   -- 'procedural', 'hearing', 'settlement', 'business'
);

-- Invoices
CREATE TABLE kanzlai.invoices (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id       UUID NOT NULL,
    case_id         UUID NOT NULL REFERENCES kanzlai.cases(id),
    invoice_number  TEXT NOT NULL,                  -- Sequential: RE-2026-001
    recipient_party_id UUID REFERENCES kanzlai.parties(id),
    invoice_date    DATE NOT NULL,
    due_date        DATE,
    gegenstandswert NUMERIC(12,2),                  -- Dispute value
    fee_items       JSONB NOT NULL,                 -- Array of {vv_number, description, rate, base_fee, amount}
    expenses        JSONB,                          -- Array of {description, amount}
    subtotal        NUMERIC(10,2) NOT NULL,
    vat_rate        NUMERIC(4,2) NOT NULL DEFAULT 19.00,
    vat_amount      NUMERIC(10,2) NOT NULL,
    total           NUMERIC(10,2) NOT NULL,
    status          TEXT NOT NULL DEFAULT 'draft',  -- 'draft', 'sent', 'paid', 'cancelled'
    paid_at         TIMESTAMPTZ,
    notes           TEXT,
    created_by      UUID NOT NULL,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);
```

**RVG calculation logic (Go service):**

```
Input: Gegenstandswert, []VV-Nummer
Output: fee breakdown with individual items + total

1. Look up base_fee for Gegenstandswert in rvg_fee_table
   - Linear interpolation for values between steps
   - Above 500,000: +192 EUR per 50,000 EUR increment
2. For each VV-Nummer:
   a. Look up rate from rvg_fee_types
   b. Calculate: base_fee × rate = fee amount
3. Apply Anrechnungsvorschriften:
   - VV 2300 (Geschäftsgebühr) credited against VV 3100 (Verfahrensgebühr) up to 0.75×
4. Add Auslagen:
   - Postpauschale: 20% of total fees, max 20 EUR (VV 7002)
   - Dokumentenpauschale: optional per-page calculation
   - Custom expenses (travel, etc.)
5. Calculate 19% USt on everything
6. Return structured breakdown
```

**API:**
- `POST /api/rvg/calculate` — calculate fees from Gegenstandswert + VV-Nummern
- `GET /api/invoices?case_id=X&status=Y` — list invoices
- `POST /api/invoices` — create invoice (from RVG calculation or manual)
- `PUT /api/invoices/{id}` — update draft invoice
- `POST /api/invoices/{id}/send` — mark as sent (+ email to client if portal enabled)
- `POST /api/invoices/{id}/pay` — mark as paid

**Frontend:**
- Fee calculator page at `/abrechnung/rechner` — input Gegenstandswert, select VV numbers, see breakdown
- Invoice list at `/abrechnung` with status filters
- Invoice creation wizard: select case → choose billing method (RVG or hourly) → review → save
- Invoice PDF generation (server-side, using Go HTML→PDF or LaTeX template)
- Case detail: "Abrechnung" tab showing invoices + unbilled time entries

---

### 7. DATEV Export (DATEV-Schnittstelle)

**Why:** Every German law firm has a Steuerberater who needs DATEV-format data. Without this export, the firm needs to manually re-enter financial data — a dealbreaker.

**Design:**

**EXTF format export:** Generate Buchungsstapel CSV files from invoices.

**Tenant settings additions:**
```json
{
  "datev": {
    "beraternummer": "12345",
    "mandantennummer": "67890",
    "kontenrahmen": "SKR04",
    "wirtschaftsjahr_beginn": "01.01",
    "erloes_konto": "4400",
    "forderungen_konto": "1200"
  }
}
```

**API:**
- `POST /api/export/datev` — generate EXTF file for a date range
  - Input: `{ from: "2026-01-01", to: "2026-03-31" }`
  - Output: CSV file download
- Each invoice maps to one Buchungssatz:
  - Umsatz = invoice total
  - Soll/Haben = 'S'
  - Konto = Forderungskonto (1200)
  - Gegenkonto = Erlöskonto (4400)
  - Belegdatum = invoice_date
  - Belegfeld 1 = invoice_number
  - Buchungstext = case_number + " " + party name

**Frontend:**
- Export button in `/abrechnung` page
- Date range picker + download button
- Settings page: DATEV configuration section

---

## P1: Should-Have Features

### 8. Document Templates (Schriftsatz-Vorlagen)

**Why:** Lawyers write the same document types repeatedly. Templates with auto-fill from case data save hours per case.

**Design:**

**New table: `kanzlai.document_templates`**

```sql
CREATE TABLE kanzlai.document_templates (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID NOT NULL,
    name        TEXT NOT NULL,                  -- 'Klageerwiderung UPC', 'Mahnschreiben'
    category    TEXT NOT NULL,                  -- 'schriftsatz', 'brief', 'intern', 'vertrag'
    content     TEXT NOT NULL,                  -- Markdown/HTML with {{variable}} placeholders
    variables   JSONB NOT NULL DEFAULT '[]',    -- [{name: "mandant", label: "Mandant", source: "party.plaintiff"}]
    language    TEXT NOT NULL DEFAULT 'de',
    is_active   BOOLEAN NOT NULL DEFAULT true,
    created_by  UUID,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);
```

**Template variables (auto-filled from case data):**

| Variable | Source | Example |
|----------|--------|---------|
| `{{mandant}}` | Party with role 'plaintiff' or 'defendant' (depending on side) | "TechCorp GmbH" |
| `{{gegner}}` | Opposing party | "PatentTroll AG" |
| `{{az}}` | Case number | "UPC_CFI_123/2026" |
| `{{gericht}}` | Court | "UPC München Local Division" |
| `{{gericht_az}}` | Court reference | "ACT_789456/2026" |
| `{{anwalt}}` | Current user's name | "RA Dr. Max Mustermann" |
| `{{datum}}` | Today's date | "28. März 2026" |
| `{{kanzlei}}` | Tenant name | "Mustermann & Partner" |
| `{{naechste_frist}}` | Next deadline title + date | "Statement of Defence — 15.04.2026" |

**API:**
- `GET /api/templates` — list templates
- `POST /api/templates` — create template
- `PUT /api/templates/{id}` — update template
- `POST /api/templates/{id}/render?case_id=X` — render template with case data, return filled document
- `POST /api/templates/{id}/render-pdf?case_id=X` — render and convert to PDF

**Frontend:**
- Template management page at `/vorlagen`
- Template editor with live preview and variable insertion toolbar
- "Schriftsatz erstellen" button on case detail page → select template → review filled version → download as PDF or save as document

---

### 9. beA Integration (Elektronisches Anwaltspostfach)

**Why:** Since January 2022, German lawyers must use beA for all court filings. It's the primary communication channel with courts. Without beA, the firm still needs RA-MICRO just for filing.

**Design:**

**Integration path:** Use the **bea.expert REST API** — a third-party wrapper that provides ~40 REST endpoints, avoiding the BRAK Java KSW toolkit dependency.

**New tables:**

```sql
-- beA account configuration (per-user, per-tenant)
CREATE TABLE kanzlai.bea_accounts (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID NOT NULL,
    user_id     UUID NOT NULL,
    safe_id     TEXT NOT NULL,              -- SAFE-ID of the lawyer's mailbox
    display_name TEXT NOT NULL,             -- "RA Dr. Max Mustermann"
    config      JSONB NOT NULL DEFAULT '{}', -- API endpoint, auth config (encrypted)
    is_active   BOOLEAN NOT NULL DEFAULT true,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- beA messages (cached locally for search and case linking)
CREATE TABLE kanzlai.bea_messages (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id       UUID NOT NULL,
    bea_account_id  UUID NOT NULL REFERENCES kanzlai.bea_accounts(id),
    bea_message_id  TEXT NOT NULL,          -- ID from beA system
    direction       TEXT NOT NULL,          -- 'inbound', 'outbound'
    sender_safe_id  TEXT NOT NULL,
    recipient_safe_id TEXT NOT NULL,
    subject         TEXT NOT NULL,
    body            TEXT,
    attachments     JSONB,                  -- [{name, size, mime_type, storage_path}]
    case_id         UUID REFERENCES kanzlai.cases(id),  -- linked case
    status          TEXT NOT NULL DEFAULT 'new', -- 'new', 'read', 'processed', 'filed'
    received_at     TIMESTAMPTZ NOT NULL,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);
```

**Workflow:**
1. **Configuration:** User enters beA credentials in settings, system validates via bea.expert API
2. **Inbox sync:** Background job polls PostboxOverview every N minutes, caches new messages
3. **Message view:** User sees beA inbox within KanzlAI, can read messages and download attachments
4. **Case linking:** User can link a beA message to a case (drag-and-drop or dropdown). Attachments auto-import as case documents.
5. **Sending:** Compose a message (or use a template), attach documents from case file, send via bea.expert API. Only PDF/TIFF attachments per ERVV.
6. **eEB (elektronische Empfangsbekenntnis):** Handle structured acknowledgments of receipt (XJustiz format)

**API:**
- `GET /api/bea/inbox` — list cached beA messages
- `GET /api/bea/messages/{id}` — get message with attachments
- `POST /api/bea/messages/{id}/link` — link message to case
- `POST /api/bea/send` — send message via beA
- `POST /api/bea/sync` — trigger manual sync

**Frontend:**
- beA inbox page at `/bea` — email-style list with case assignment
- Compose view with template selection and PDF-only attachment validation
- Case detail: "beA" tab showing linked messages
- Settings: beA account configuration

**Licensing:** bea.expert requires a commercial license. Budget for this.

---

### 10. Full-Text Search (Volltextsuche)

**Why:** Law firms accumulate thousands of documents and notes. Without search, finding information means clicking through cases one by one.

**Design:**

**PostgreSQL tsvector approach** (no external search engine needed):

```sql
-- Search index materialized from multiple tables
CREATE MATERIALIZED VIEW kanzlai.search_index AS
SELECT
    c.tenant_id,
    'case' AS entity_type,
    c.id AS entity_id,
    c.case_number || ' ' || c.title || ' ' || COALESCE(c.ai_summary, '') AS content,
    to_tsvector('german', c.case_number || ' ' || c.title || ' ' || COALESCE(c.ai_summary, '')) AS search_vector
FROM kanzlai.cases c
UNION ALL
SELECT
    p.tenant_id, 'party', p.id,
    p.name || ' ' || COALESCE(p.representative, ''),
    to_tsvector('german', p.name || ' ' || COALESCE(p.representative, ''))
FROM kanzlai.parties p
UNION ALL
SELECT
    d.tenant_id, 'deadline', d.id,
    d.title || ' ' || COALESCE(d.description, '') || ' ' || COALESCE(d.notes, ''),
    to_tsvector('german', d.title || ' ' || COALESCE(d.description, '') || ' ' || COALESCE(d.notes, ''))
FROM kanzlai.deadlines d
UNION ALL
SELECT
    n.tenant_id, 'note', n.id,
    n.content,
    to_tsvector('german', n.content)
FROM kanzlai.notes n;

CREATE INDEX idx_search_vector ON kanzlai.search_index USING gin(search_vector);
CREATE INDEX idx_search_tenant ON kanzlai.search_index(tenant_id);
```

Refresh the materialized view periodically or on relevant mutations.

**API:**
- `GET /api/search?q=TERM&type=case,party,deadline` — cross-entity search with type filtering
- Returns: entity type, entity ID, title/name, highlighted snippet, relevance score

**Frontend:**
- Global search bar in header (Ctrl+K shortcut)
- Results grouped by entity type
- Click result to navigate to detail page
- Search suggestions from recent searches

---

### 11. Reporting & Analytics (Berichte)

**Why:** Partners need to see: Are we meeting deadlines? How is workload distributed? Which cases are profitable? Without reporting, the software is a filing cabinet, not a management tool.

**Design:**

**Report types:**

| Report | Description | Audience |
|--------|-------------|----------|
| Fristeneinhaltung | Deadline compliance rate — % completed on time vs overdue | Partners |
| Arbeitsbelastung | Hours by user, by case, by period — who's overloaded? | Partners |
| Fallstatistik | Cases by status, type, court — pipeline view | Partners |
| Umsatzbericht | Revenue by case, client, period — from invoices | Partners |
| Aktivitätsbericht | Actions per user per period — productivity view | Partners |
| Fälligkeitsvorschau | Upcoming deadlines across all cases — planning view | All |

**API:**
- `GET /api/reports/deadline-compliance?from=DATE&to=DATE` — deadline stats
- `GET /api/reports/workload?from=DATE&to=DATE&group_by=user|case` — time entry aggregation
- `GET /api/reports/case-stats?from=DATE&to=DATE` — case pipeline metrics
- `GET /api/reports/revenue?from=DATE&to=DATE&group_by=case|month` — invoice aggregation

**Frontend:**
- Reports page at `/berichte` — dashboard-style with charts
- Date range filter + grouping options
- Charts: bar charts for workload, line charts for trends, pie charts for distribution
- Export to CSV/PDF for offline sharing
- Chart library: lightweight — Recharts or Chart.js (via react-chartjs-2)

---

### 12. Wiedervorlagen (Follow-Up System)

**Why:** A fundamental pattern in German law firm workflow — "remind me about this case/document on date X." Different from deadlines: Wiedervorlagen are internal reminders, not procedural deadlines.

**Design:**

**New table: `kanzlai.follow_ups`**

```sql
CREATE TABLE kanzlai.follow_ups (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID NOT NULL,
    case_id     UUID REFERENCES kanzlai.cases(id),
    user_id     UUID NOT NULL,              -- who should be reminded
    title       TEXT NOT NULL,
    description TEXT,
    due_date    DATE NOT NULL,
    priority    TEXT NOT NULL DEFAULT 'normal', -- 'low', 'normal', 'high', 'urgent'
    status      TEXT NOT NULL DEFAULT 'pending', -- 'pending', 'done', 'snoozed'
    snoozed_to  DATE,                        -- if snoozed, new date
    created_by  UUID NOT NULL,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    completed_at TIMESTAMPTZ
);
```

**Integration with notifications:** The notification worker checks follow_ups alongside deadlines.

**Frontend:**
- Quick-create button on any entity detail page: "Wiedervorlage"
- Follow-up list at `/wiedervorlagen` with date filtering
- Morning email digest: today's follow-ups alongside today's deadlines

---

### 13. Email Integration (E-Mail-Anbindung)

**Why:** Most client and opponent communication happens via email. Without email integration, the firm operates in two separate worlds — the email client and KanzlAI.

**Design:**

**Approach:** IMAP-based email import (not a full email client — that's scope creep).

**New tables:**

```sql
CREATE TABLE kanzlai.email_accounts (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID NOT NULL,
    user_id     UUID NOT NULL,
    email       TEXT NOT NULL,
    imap_host   TEXT NOT NULL,
    imap_port   INTEGER NOT NULL DEFAULT 993,
    smtp_host   TEXT,
    smtp_port   INTEGER DEFAULT 587,
    username    TEXT NOT NULL,
    password    TEXT NOT NULL,               -- encrypted at rest
    is_active   BOOLEAN NOT NULL DEFAULT true,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE kanzlai.emails (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id       UUID NOT NULL,
    email_account_id UUID NOT NULL REFERENCES kanzlai.email_accounts(id),
    message_id      TEXT NOT NULL,           -- RFC 2822 Message-ID
    direction       TEXT NOT NULL,           -- 'inbound', 'outbound'
    from_address    TEXT NOT NULL,
    to_addresses    TEXT[] NOT NULL,
    cc_addresses    TEXT[],
    subject         TEXT NOT NULL,
    body_text       TEXT,
    body_html       TEXT,
    attachments     JSONB,
    case_id         UUID REFERENCES kanzlai.cases(id),
    received_at     TIMESTAMPTZ NOT NULL,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);
```

**Workflow:**
1. Configure IMAP account in settings
2. Background sync fetches new emails periodically
3. User views emails in KanzlAI and links them to cases
4. Auto-suggestion: match sender/recipient against party contacts to suggest case assignment
5. Email attachments can be saved as case documents

**Frontend:**
- Email page at `/email` — inbox-style list
- Case detail: "E-Mail" tab showing linked emails
- "Zur Akte" button on each email to link to a case
- Settings: email account configuration

---

## P2: Differentiator Features (UPC-Specific Intelligence)

These features leverage m's existing infrastructure (youpcms, mLex, youpc.org data) to create an unbeatable competitive advantage for UPC patent litigation.

### 14. Patent Family Tracking

**Why:** UPC cases revolve around patents. A case management system for patent litigation that doesn't track patents is like a hospital system that doesn't track patients.

**Design:**

**New tables:**

```sql
CREATE TABLE kanzlai.patents (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id       UUID NOT NULL,
    patent_number   TEXT NOT NULL,           -- 'EP1234567'
    title           TEXT,
    applicant       TEXT,
    filing_date     DATE,
    grant_date      DATE,
    expiry_date     DATE,
    status          TEXT NOT NULL DEFAULT 'active', -- 'active', 'expired', 'withdrawn', 'revoked'
    patent_type     TEXT,                    -- 'EP', 'UP', 'DE', 'US', 'WO'
    opt_out_status  TEXT,                    -- 'opted_out', 'not_opted_out', 'opt_out_withdrawn'
    opt_out_date    DATE,
    claims_count    INTEGER,
    abstract        TEXT,
    metadata        JSONB,                   -- Classifications, designations, priority data
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- Patent family relationships
CREATE TABLE kanzlai.patent_family (
    parent_patent_id UUID NOT NULL REFERENCES kanzlai.patents(id),
    child_patent_id  UUID NOT NULL REFERENCES kanzlai.patents(id),
    relationship     TEXT NOT NULL,          -- 'national_validation', 'divisional', 'continuation', 'priority'
    PRIMARY KEY (parent_patent_id, child_patent_id)
);

-- Link patents to cases
CREATE TABLE kanzlai.case_patents (
    case_id     UUID NOT NULL REFERENCES kanzlai.cases(id) ON DELETE CASCADE,
    patent_id   UUID NOT NULL REFERENCES kanzlai.patents(id),
    role        TEXT NOT NULL,              -- 'asserted', 'challenged', 'prior_art'
    PRIMARY KEY (case_id, patent_id)
);
```

**Integration with youpc.org/EPO:**
- Auto-lookup patent data from EPO Open Patent Services (OPS) API by patent number
- Import patent family tree from EPO
- Track opt-out status from UPC opt-out register

**Frontend:**
- Case detail: "Patente" tab showing linked patents with family tree visualization
- Patent detail page showing full patent info + all cases where it appears
- Auto-fill case data when a patent number is entered

---

### 15. Claim Chart Management

**Why:** Claim charts (Merkmalsgliederung) are the core analytical tool in patent litigation — mapping claim elements to accused products or prior art. No existing Kanzleimanagement system handles this.

**Design:**

**New tables:**

```sql
CREATE TABLE kanzlai.claim_charts (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID NOT NULL,
    case_id     UUID NOT NULL REFERENCES kanzlai.cases(id),
    patent_id   UUID REFERENCES kanzlai.patents(id),
    title       TEXT NOT NULL,              -- 'Claim 1 Infringement Analysis'
    chart_type  TEXT NOT NULL,              -- 'infringement', 'invalidity', 'comparison'
    status      TEXT NOT NULL DEFAULT 'draft',
    created_by  UUID,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE kanzlai.claim_chart_entries (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    chart_id        UUID NOT NULL REFERENCES kanzlai.claim_charts(id) ON DELETE CASCADE,
    claim_element   TEXT NOT NULL,          -- 'a mounting bracket configured to...'
    element_number  TEXT,                   -- '1a', '1b', '1c'
    evidence        TEXT,                   -- 'Accused product uses X-shaped bracket (see Exhibit 3, p.12)'
    assessment      TEXT,                   -- 'literally_met', 'equivalent', 'not_met', 'unclear'
    documents       JSONB,                  -- [{document_id, page, highlight}]
    sort_order      INTEGER NOT NULL,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);
```

**Frontend:**
- Case detail: "Claim Charts" tab
- Two-column view: claim element (left) | evidence (right)
- Color-coded assessment: green (met), yellow (equivalent/unclear), red (not met)
- Drag-drop reordering of elements
- Link evidence to uploaded documents with page references
- Export to Word/PDF for court submissions

---

### 16. UPC Case Intelligence (mLex Integration)

**Why:** m has 1,600+ structured UPC decisions with holdings, legal principles, citation networks, and division analytics. No competitor has this data. This turns KanzlAI from "case file management" into "case strategy intelligence."

**Design:**

**Integration approach:** Connect to the existing youpc.org/mLex Supabase database (same flexsiebels instance) via cross-schema queries or a dedicated API endpoint.

**Features:**

**A) Similar Case Finder**
- Input: case type, court, patent classification, key issues
- Output: relevant UPC decisions from the mLex database, ranked by similarity
- Uses pgvector embeddings on holdings + legal principles for semantic search
- "Wie hat die Münchner Lokalkammer bisher über Softwarepatente entschieden?"

**B) Division Analytics**
- Per-division statistics: success rates, average timeline, judge panel compositions
- "München grants PI in 40% of cases, median time to hearing: 8 months"
- Updated automatically as new decisions are scraped

**C) Legal Principle Lookup**
- Search legal principles extracted from UPC case law
- "Show me all decisions on claim construction of functional features"
- Returns structured holdings with citations

**D) Precedent Suggestions**
- When drafting a Schriftsatz, suggest relevant precedents based on case type and issues
- AI-powered: Claude analyzes the case context and retrieves relevant mLex decisions

**API:**
- `GET /api/upc/similar-cases?case_id=X` — find similar cases
- `GET /api/upc/division-stats?division=MUNICH` — division analytics
- `GET /api/upc/principles?query=TERM` — search legal principles
- `POST /api/upc/suggest-precedents` — AI-powered precedent suggestion

**Frontend:**
- Case detail: "UPC Intelligence" tab (only for UPC-type cases)
- Global UPC research page at `/upc-recherche`
- Division comparison tool (already partially exists in youpc.org)

---

### 17. AI Document Drafting

**Why:** Combine case data + templates + legal intelligence to generate first drafts. This is the "wow factor" that gets users to switch from RA-MICRO.

**Design:**

**Workflow:**
1. User selects a document type (e.g., "Statement of Defence") and a case
2. System loads: case data, parties, patent info, relevant deadlines, linked documents, relevant precedents from mLex
3. Claude generates a structured first draft using the template + case context
4. User reviews and edits in a rich text editor
5. Export to PDF for filing

**API:**
- `POST /api/ai/draft` — generate document draft
  - Input: `{ case_id, template_id, instructions, include_precedents: true }`
  - Output: structured document in Markdown/HTML
- Rate-limited: 3 requests/minute (more expensive than extraction)

**Frontend:**
- "KI-Entwurf" button on the template rendering page
- Split view: generated draft (left) | case context (right)
- Edit capabilities before saving as document

**Privacy consideration:** Case data sent to Claude API. Tenants must opt-in, with clear data processing agreement. Consider self-hosted Claude via AWS Bedrock for sensitive firms.

---

### 18. AI Case Strategy Analysis

**Why:** Beyond document management — help lawyers think about their cases. "What are the strengths and weaknesses of our position?"

**Design:**

**AI analysis types:**

| Analysis | Input | Output |
|----------|-------|--------|
| Strengths/Weaknesses | Case data + claim charts | Bullet-point analysis with confidence |
| Timeline Prediction | Case type + division | Estimated timeline based on historical data |
| Cost Estimate | Case type + Gegenstandswert | Estimated legal costs (own + opponent) |
| Strategy Options | Full case data | Recommended approaches with pros/cons |

**API:**
- `POST /api/ai/analyze-strategy` — comprehensive case analysis
  - Input: `{ case_id, analysis_type }`
  - Output: structured analysis with sections

**Frontend:**
- Case detail: "KI-Analyse" tab
- Analysis results cached and versioned (re-run as case evolves)
- Each analysis shows the date it was generated and what data it was based on

---

## P3: Nice-to-Have Features

### 19. Client Portal (Mandantenportal)

External-facing view where clients can see the status of their cases.

**Design:**
- Separate authentication (email + password, no Supabase Auth dependency)
- Read-only access to assigned cases: status, upcoming deadlines, recent events
- Secure document exchange: client can upload documents, lawyer can share documents
- Invoice viewing and payment status
- Accessible via subdomain: `portal.kanzlai.msbls.de`

**Data model:** Use the `extern` role from the permissions system. Client portal users are linked to specific cases via `case_assignments`.

---

### 20. Mobile Optimization / PWA

**Design:**
- Progressive Web App with offline capability for viewing case data
- Service worker for push notifications (deadline reminders)
- Optimized touch targets and mobile layouts (already partially done with responsive Tailwind)
- Add `manifest.json` with installable app metadata
- Minimal — not a native app, but installable from browser

---

### 21. Multi-Language Support (Mehrsprachigkeit)

**Design:**
- `next-intl` for frontend internationalization
- German primary, English for UPC work
- Language selector in settings
- Template system already supports per-template language
- Backend API responses use language-neutral keys; frontend maps to locale

---

### 22. EDA Integration (Elektronisches Mahnverfahren)

**Design:**
- Generate EDA-format files for electronic dunning proceedings
- Submit to the relevant Mahngericht
- Track Mahnbescheid and Vollstreckungsbescheid status
- Lower priority — UPC firms rarely do Mahnverfahren

---

### 23. Advanced Integrations

| Integration | Priority | Effort | Notes |
|-------------|----------|--------|-------|
| Microsoft 365 Calendar | P3 | Medium | Alternative to CalDAV for firms using Outlook |
| Google Workspace | P3 | Medium | Alternative auth + calendar |
| Schufa/Creditreform | P3 | High | Credit checks on parties — requires contracts |
| EPO OPS API | P2 | Medium | Auto-fetch patent data by number |
| DPMA DPMAregister | P3 | Medium | German patent/trademark status |
| Slack/Teams webhooks | P3 | Low | Notification delivery alternative |

---

## Data Model Summary: New Tables

| Table | Priority | Purpose |
|-------|----------|---------|
| `audit_log` | P0 | Append-only change history |
| `conflict_checks` | P0 | Conflict of interest check results |
| `permissions` | P0 | Role-action permission matrix |
| `notifications` | P0 | In-app notification inbox |
| `notification_preferences` | P0 | Per-user notification settings |
| `case_assignments` | P0 | Who works on which case |
| `time_entries` | P0 | Billable time tracking |
| `hourly_rates` | P0 | Per-user billing rates |
| `rvg_fee_table` | P0 | RVG Gebührentabelle (reference) |
| `rvg_fee_types` | P0 | VV-Nummern catalog (reference) |
| `invoices` | P0 | Fee invoices |
| `follow_ups` | P1 | Wiedervorlagen |
| `document_templates` | P1 | Schriftsatz templates |
| `bea_accounts` | P1 | beA mailbox config |
| `bea_messages` | P1 | Cached beA messages |
| `email_accounts` | P1 | IMAP email config |
| `emails` | P1 | Cached emails linked to cases |
| `patents` | P2 | Patent tracking |
| `patent_family` | P2 | Patent family relationships |
| `case_patents` | P2 | Case-patent linking |
| `claim_charts` | P2 | Patent claim analysis |
| `claim_chart_entries` | P2 | Claim chart rows |
| `party_relationships` | P1 | Corporate structures for conflict checks |
| `search_index` | P1 | Full-text search materialized view |

**Total new tables:** 24 (on top of existing 12)

---

## Implementation Phases

### Phase 1: Foundation (P0 core — audit, permissions, notifications)

**Goal:** Make the existing MVP production-safe.

| Task | Effort | Dependencies |
|------|--------|--------------|
| Audit trail table + middleware | 3-4 days | None |
| Role-based permissions (extend existing roles) | 2-3 days | None |
| Case assignments table + UI | 1-2 days | None |
| Notification system (in-app + email) | 3-4 days | Case assignments |
| Notification preferences UI | 1 day | Notifications |

**Deliverable:** Every change is logged. Users have proper roles. Deadlines trigger reminders.

### Phase 2: Money (P0 — time tracking, RVG, invoicing, DATEV)

**Goal:** The billing pipeline — from time entry to DATEV export.

| Task | Effort | Dependencies |
|------|--------|--------------|
| Time entries table + CRUD + API | 2-3 days | None |
| Timer UI (header widget + case tab) | 2 days | Time entries API |
| Hourly rates configuration | 1 day | None |
| RVG fee table + calculator service | 2-3 days | None |
| RVG calculator UI | 1-2 days | Calculator service |
| Invoice model + CRUD + API | 2-3 days | RVG calculator |
| Invoice creation wizard UI | 2-3 days | Invoice API |
| Invoice PDF generation | 1-2 days | Invoice model |
| DATEV EXTF export | 1-2 days | Invoices |

**Deliverable:** Complete billing pipeline. Lawyers can track time, calculate fees, generate invoices, export to DATEV.

### Phase 3: Compliance (P0 — conflict checks)

**Goal:** Statutory compliance for conflict of interest.

| Task | Effort | Dependencies |
|------|--------|--------------|
| pg_trgm extension + party search index | 1 day | None |
| Conflict check service (exact + fuzzy) | 2-3 days | None |
| Conflict check on case creation (auto-trigger) | 1 day | Conflict service |
| Conflict check UI (results + clearance) | 2 days | Conflict API |
| Cologne phonetic matching | 1-2 days | Conflict service |

**Deliverable:** Automatic conflict checking on every new case/party. Auditable clearance workflow.

### Phase 4: Communication (P1 — beA, email, templates)

**Goal:** KanzlAI becomes the central communication hub.

| Task | Effort | Dependencies |
|------|--------|--------------|
| Document template engine | 3-4 days | None |
| Template editor UI | 2-3 days | Template engine |
| Full-text search (materialized view + API) | 2-3 days | None |
| Search UI (global search bar) | 1-2 days | Search API |
| Follow-up (Wiedervorlagen) system | 2 days | Notifications |
| beA integration (bea.expert API) | 5-7 days | License agreement |
| Email integration (IMAP sync) | 4-5 days | None |

**Deliverable:** Templates, search, beA, email — all communication flows through KanzlAI.

### Phase 5: UPC Intelligence (P2 — the differentiator)

**Goal:** No competitor can match this.

| Task | Effort | Dependencies |
|------|--------|--------------|
| Patent model + family tracking | 2-3 days | None |
| EPO OPS API integration (auto-fetch patent data) | 2-3 days | Patent model |
| Claim chart management | 3-4 days | Patent model |
| mLex cross-schema queries | 2-3 days | Existing mLex data |
| Similar case finder | 2-3 days | mLex integration |
| Division analytics dashboard | 2-3 days | mLex integration |
| AI document drafting | 3-4 days | Templates + mLex |
| AI strategy analysis | 2-3 days | mLex + case data |

**Deliverable:** UPC-specific intelligence layer. Patent tracking, claim charts, case law search, AI strategy.

### Phase 6: Polish (P3)

**Goal:** Professional finish.

| Task | Effort | Dependencies |
|------|--------|--------------|
| Client portal | 5-7 days | Invoices, case assignments |
| PWA + push notifications | 2-3 days | Notification system |
| Multi-language (DE/EN) | 3-4 days | All UI pages |
| Reporting dashboard | 3-4 days | Time entries, invoices |
| EDA Mahnverfahren | 3-4 days | Invoices |

---

## New Frontend Routes (Complete)

```
Existing:
  /dashboard
  /cases, /cases/new, /cases/[id]/*
  /fristen, /fristen/neu, /fristen/rechner, /fristen/[id]
  /termine, /termine/neu, /termine/[id]
  /ai/extract
  /einstellungen, /einstellungen/team

New (P0):
  /protokoll                    Audit log viewer (admin)
  /zeit                         Time tracking overview (weekly view)
  /abrechnung                   Invoice list
  /abrechnung/rechner           RVG fee calculator
  /abrechnung/[id]              Invoice detail
  /kollision                    Manual conflict check
  /benachrichtigungen           Notification center

New (P1):
  /vorlagen                     Document template management
  /vorlagen/[id]                Template editor
  /bea                          beA inbox
  /bea/compose                  beA compose message
  /email                        Email inbox
  /wiedervorlagen               Follow-up list
  /suche                        Full search results page

New (P2):
  /upc-recherche                UPC case law research
  /upc-recherche/divisionen     Division analytics
  /patente                      Patent list
  /patente/[id]                 Patent detail + family tree

New (P3):
  /berichte                     Reporting dashboard
  portal.*                      Client portal (separate app/subdomain)
```

---

## Architecture Decisions

### 1. Stay on Go + Next.js

The current stack is solid. No framework migration needed. Go handles the backend complexity well (concurrent CalDAV sync, notification workers, beA polling). Next.js App Router is mature enough for the frontend.

### 2. Stay on Supabase

All new tables go in the `kanzlai` schema. The flexsiebels instance already has the mLex data in another schema — cross-schema queries enable the UPC intelligence layer without data duplication.

### 3. Background Workers as Goroutines

The CalDAV sync pattern (background goroutine with interval) extends to: notification dispatch, beA polling, email sync, search index refresh. Keep them in the same Go binary — no need for a separate worker process yet.

### 4. PDF Generation

Use Go's `html/template` → HTML → `wkhtmltopdf` (or `chromedp` headless) for invoice and document PDF generation. Keep it server-side.

### 5. beA via bea.expert

The bea.expert REST API is the only sane integration path. BRAK's Java KSW toolkit is not viable for a modern web app.

### 6. No External Search Engine

PostgreSQL full-text search with `tsvector` + `pg_trgm` is sufficient for the data volumes a single law firm generates. No Elasticsearch/Meilisearch needed — reduces operational complexity.

---

## Competitive Positioning

| Feature | RA-MICRO | Advoware | Kleos | **KanzlAI** |
|---------|----------|----------|-------|-------------|
| General Kanzleimanagement | Full | Full | Full | **Full (after P0-P1)** |
| beA Integration | Yes | Yes | Yes | **Yes (P1)** |
| RVG Calculator | Yes | Yes | Yes | **Yes (P0)** |
| DATEV Export | Yes | Yes | Yes | **Yes (P0)** |
| Cloud-Native | Partial | Partial | Yes | **Yes** |
| UPC Deadline Rules | No | No | No | **Yes (already shipped)** |
| UPC Case Law Database | No | No | No | **Yes (P2)** |
| AI Deadline Extraction | No | No | No | **Yes (already shipped)** |
| AI Document Drafting | Partial | No | No | **Yes (P2)** |
| Patent Family Tracking | No | No | No | **Yes (P2)** |
| Claim Chart Management | No | No | No | **Yes (P2)** |
| Division Analytics | No | No | No | **Yes (P2)** |
| Multi-Tenant SaaS | No | No | Yes | **Yes (already shipped)** |

**Tagline:** "Die einzige Kanzleisoftware, die UPC-Patentprozesse versteht."

---

## Risk Register

| Risk | Impact | Mitigation |
|------|--------|------------|
| beA API changes / bea.expert discontinuation | High | Abstract beA behind an interface; monitor BRAK announcements |
| RVG table updates (new Kostenrechtsänderungsgesetz) | Medium | Versioned fee table with effective dates; easy to update |
| DSGVO compliance for AI features (data sent to Claude) | High | Offer self-hosted Claude via Bedrock; explicit tenant opt-in |
| Scope creep on P2 features | Medium | Ship P0+P1 first; P2 only after core is solid |
| Single-developer bottleneck | High | Clean architecture, good docs, modular services |
| UPC procedural rules change | Low | Rules in database, not hardcoded; already have update pattern |

---

## Success Metrics

| Metric | Target | How to Measure |
|--------|--------|----------------|
| Deadline compliance rate | > 99% | No overdue deadlines that weren't caught by reminders |
| Time from case creation to first deadline | < 5 min | AI extraction + deadline calculator |
| Invoice generation time | < 2 min | RVG calculator + template |
| Conflict check time | < 3 sec | Automated on case creation |
| User adoption | 1 firm within 3 months of P0 | Direct outreach to patent firms |
| Feature parity with competitors | P0+P1 complete | Checklist comparison |

---

## Appendix: Prior Design Documents

- `DESIGN-dashboard-redesign.md` — t-kz-060: Dashboard interactivity + detail pages (implemented)
- This document (`ROADMAP.md`) — t-kz-070: Full system vision

---

*This roadmap is a living document. Features will be re-prioritized as user feedback comes in. The UPC intelligence layer (P2) is the strategic differentiator — but P0 must ship first to establish credibility as a serious Kanzleimanagement system.*
