Files
cannamanage/docs/sprint-9/cannamanage-sprint9-plan.md
T
Patrick Plate 26a77dd269 feat(sprint9): Phase 1 — Data model + ReportGenerator infrastructure
- 7 new enums: ReportType, ExportFormat, DestructionMethod, TransportStatus,
  ComplianceArea, ComplianceStatus, RetentionCategory
- Extended: StaffPermission (+3), AuditEventType (+5), NotificationType (+2)
- Flyway V23-V29: destruction_records, transport_records, propagation_sources,
  prevention_activities, generated_reports, compliance_deadlines, distribution THC/CBD
- 6 new JPA entities extending AbstractTenantEntity
- 6 new Spring Data repositories with tenant-scoped queries
- ReportGenerator<T> interface + ReportGeneratorService (auto-discovery, format dispatch)
- ComplianceRecordsController (CRUD for destruction/transport/propagation/prevention)
- ComplianceDeadlineController (create, list, complete, overdue)
- DateRangeReportParameters record for report generation
2026-06-15 12:01:06 +02:00

786 lines
32 KiB
Markdown

# Sprint 9 Implementation Plan — Berichtszentrale (Report Center)
**Date:** 2026-06-15
**Author:** Patrick Plate / Lumen (Architect)
**Status:** Draft v2 (incorporates panel review advisory items)
**Basis:** cannamanage-sprint9-analysis.md
**Sprint Goal:** Complete reporting & documentation module with authority-ready exports
---
## Implementation Phases
Sprint 9 is organized into 6 phases, building from backend infrastructure to frontend polish.
```mermaid
graph LR
P1[Phase 1: Data Model + Backend Services] --> P2[Phase 2: Report Generators - Financial]
P2 --> P3[Phase 3: Report Generators - KCanG Compliance]
P3 --> P4[Phase 4: DSGVO Templates + Verein Reports]
P4 --> P5[Phase 5: Frontend - Berichtszentrale + Sidebar Reorg]
P5 --> P6[Phase 6: Compliance Dashboard + Retention + Integration Testing]
```
---
## Phase 1: Data Model & Backend Infrastructure
### Step 1.1 — Database Migrations (V23-V28)
**Files:**
- `cannamanage-api/src/main/resources/db/migration/V23__destruction_records.sql`
- `cannamanage-api/src/main/resources/db/migration/V24__transport_records.sql`
- `cannamanage-api/src/main/resources/db/migration/V25__propagation_sources.sql`
- `cannamanage-api/src/main/resources/db/migration/V26__prevention_activities.sql`
- `cannamanage-api/src/main/resources/db/migration/V27__generated_reports.sql`
- `cannamanage-api/src/main/resources/db/migration/V28__compliance_deadlines.sql`
- `cannamanage-api/src/main/resources/db/migration/V29__distribution_thc_cbd.sql`
**V29 specifically:**
```sql
ALTER TABLE distributions ADD COLUMN IF NOT EXISTS thc_percentage NUMERIC(4,2);
ALTER TABLE distributions ADD COLUMN IF NOT EXISTS cbd_percentage NUMERIC(4,2);
ALTER TABLE distributions ADD COLUMN IF NOT EXISTS strain_name VARCHAR(200);
```
### Step 1.2 — JPA Entities
**New entities:**
- `de.cannamanage.domain.entity.DestructionRecord`
- `de.cannamanage.domain.entity.TransportRecord`
- `de.cannamanage.domain.entity.PropagationSource`
- `de.cannamanage.domain.entity.PreventionActivity`
- `de.cannamanage.domain.entity.GeneratedReport`
- `de.cannamanage.domain.entity.ComplianceDeadline`
**Modified entities:**
- `Distribution` — add `thcPercentage`, `cbdPercentage`, `strainName` fields
### Step 1.3 — Repositories
- `DestructionRecordRepository`
- `TransportRecordRepository`
- `PropagationSourceRepository`
- `PreventionActivityRepository`
- `GeneratedReportRepository`
- `ComplianceDeadlineRepository`
Each with standard tenant-scoped queries + date range filters.
### Step 1.4 — Base Report Service Infrastructure
**New file:** `cannamanage-service/src/main/java/de/cannamanage/service/report/ReportGenerator.java`
```java
/**
* Base interface for all report generators.
* Standardizes the contract across 18+ report types.
* Each implementation handles one ReportType.
*/
public interface ReportGenerator<T extends ReportParameters> {
ReportType getType();
byte[] generatePdf(T params);
default byte[] generateCsv(T params) {
throw new UnsupportedOperationException("CSV not supported for " + getType());
}
default byte[] generateJson(T params) {
throw new UnsupportedOperationException("JSON not supported for " + getType());
}
Set<ExportFormat> supportedFormats();
}
```
**New file:** `cannamanage-service/src/main/java/de/cannamanage/service/ReportGeneratorService.java`
```java
@Service
public class ReportGeneratorService {
// Central orchestrator — delegates to typed ReportGenerator implementations
// Auto-discovers all ReportGenerator beans via Spring injection
// Handles: format dispatch, audit logging, document storage
// Rate-limited: max 5 generations per minute per tenant (via @RateLimiter)
private final Map<ReportType, ReportGenerator<?>> generators;
public ReportGeneratorService(List<ReportGenerator<?>> allGenerators) {
this.generators = allGenerators.stream()
.collect(Collectors.toMap(ReportGenerator::getType, Function.identity()));
}
@RateLimiter(name = "reportGeneration", fallbackMethod = "rateLimitFallback")
public GeneratedReport generateReport(ReportType type, ReportParameters params);
public byte[] exportAsPdf(ReportType type, ReportParameters params);
public byte[] exportAsCsv(ReportType type, ReportParameters params);
public byte[] exportAsJson(ReportType type, ReportParameters params);
private GeneratedReport rateLimitFallback(ReportType type, ReportParameters params, Exception ex) {
throw new TooManyRequestsException("Report generation rate limit exceeded. Max 5 per minute.");
}
}
```
> **Advisory implementation (v2):** The `ReportGenerator<T>` interface provides a standardized contract across all 18+ report generators. This enables consistent error handling, format negotiation, and future plugin extensibility without requiring every generator to implement all formats. Rate limiting (max 5/min/tenant) is enforced at the orchestrator level via Resilience4j `@RateLimiter`.
**New file:** `cannamanage-service/src/main/java/de/cannamanage/service/RetentionService.java`
```java
@Service
public class RetentionService {
// Scheduled daily: checks retention periods
// Flags records approaching end-of-retention
// Never auto-deletes — requires admin confirmation
@Scheduled(cron = "0 0 2 * * *") // 2 AM daily
public void checkRetentionPeriods();
public List<RetentionAlert> getPendingAlerts(UUID tenantId);
}
```
### Step 1.5 — REST Controllers for New Entities
- `DestructionRecordController` — CRUD + PDF generation
- `TransportRecordController` — CRUD + certificate generation
- `PropagationSourceController` — CRUD
- `PreventionActivityController` — CRUD
- `ComplianceDeadlineController` — CRUD + status updates
- `ReportController` (enhanced) — all report generation endpoints
### Step 1.6 — Enums
**New enums in `cannamanage-domain`:**
- `ReportType``ANNUAL_AUTHORITY`, `DISTRIBUTION_LOG`, `STOCK_INVENTORY`, `DESTRUCTION_PROTOCOL`, `CULTIVATION_REPORT`, `TRANSPORT_CERTIFICATE`, `FULL_AUTHORITY_EXPORT`, `EUR`, `ANNUAL_FINANCIAL`, `KASSENBUCH_EXPORT`, `FEE_CONFIRMATION`, `MEMBER_LIST_REGISTRY`, `BOARD_CHANGE_NOTICE`, `ANNUAL_BOARD_REPORT`, `VVT`, `TOM`, `DSFA`, `DELETION_CONCEPT`, `BREACH_NOTIFICATION`
- `DestructionMethod``INCINERATION`, `COMPOSTING`, `CHEMICAL`, `OTHER`
- `TransportStatus``PLANNED`, `AUTHORITY_NOTIFIED`, `IN_TRANSIT`, `COMPLETED`
- `ComplianceArea``KCANG`, `FINANCE`, `DSGVO`, `VEREIN`
- `ComplianceStatus``GREEN`, `YELLOW`, `RED`
- `RetentionCategory``KCANG_5Y`, `AO_6Y`, `AO_8Y`, `AO_10Y`, `INDEFINITE`
---
## Phase 2: Financial Report Generators
### Step 2.1 — EÜR (Einnahmen-Überschuss-Rechnung) Generator
**File:** `cannamanage-service/src/main/java/de/cannamanage/service/report/EurReportGenerator.java`
Logic:
1. Query all `financial_transactions` for the selected calendar year
2. Group into Einnahmen (INCOME transactions) and Ausgaben (EXPENSE transactions)
3. Sub-group by `expense_category` / income source
4. Calculate: Total income, total expenses, Überschuss/Fehlbetrag
5. Include: Opening balance (Jan 1) and closing balance (Dec 31)
6. Generate PDF in standard EÜR format (Anlage EÜR compatible structure)
7. Generate CSV with semicolons, ISO-8859-1, German decimal format
**PDF Structure:**
```
EINNAHMEN-ÜBERSCHUSS-RECHNUNG
Kalenderjahr: 2025
Verein: Grüner Daumen e.V.
I. EINNAHMEN
Mitgliedsbeiträge ................ 54.000,00 €
Sonstige Einnahmen ............... 2.400,00 €
─────────────────────────────────────────────
Summe Einnahmen .................. 56.400,00 €
II. AUSGABEN
Miete/Pacht ...................... 18.000,00 €
Strom/Energie .................... 4.800,00 €
Cannabis-Anbaumaterial ........... 12.000,00 €
Verwaltung ....................... 3.600,00 €
Versicherungen ................... 2.400,00 €
Sonstige Ausgaben ................ 5.200,00 €
─────────────────────────────────────────────
Summe Ausgaben ................... 46.000,00 €
III. ERGEBNIS
Überschuss ....................... 10.400,00 €
```
### Step 2.2 — Enhanced Kassenbuch Export
**File:** `cannamanage-service/src/main/java/de/cannamanage/service/report/KassenbuchExportGenerator.java`
Enhance existing Kassenbuch with:
- DATEV-compatible CSV format (optional export)
- GoBD-compliant: immutable entries, sequential numbering, no gaps
- Period selection (monthly, quarterly, annual)
- Running balance per entry
- Category totals at bottom
### Step 2.3 — Beitragsbescheinigung (Fee Confirmation per Member)
**File:** `cannamanage-service/src/main/java/de/cannamanage/service/report/FeeConfirmationGenerator.java`
- Per-member annual PDF: confirms total fees paid in calendar year
- Letterhead with club name, address, Vereinsregisternummer
- Suitable for member's tax records (Sonderausgaben)
- Batch generation: one click → all active members get a PDF
### Step 2.4 — Jahresabschluss Enhancement
Enhance existing Sprint 8 annual report with:
- Comparison to previous year (if data exists)
- Category-wise breakdown with percentages
- Chart data (for frontend visualization)
- Kassenprüfer-ready format (signature lines)
---
## Phase 3: KCanG Compliance Report Generators
### Step 3.1 — Annual Authority Report (§26 Abs. 3 KCanG)
**File:** `cannamanage-service/src/main/java/de/cannamanage/service/report/AnnualAuthorityReportGenerator.java`
**THE most important report.** Due January 31 each year.
Logic:
1. Query all harvests (from grow module) in calendar year → sum by strain + THC/CBD
2. Query all distributions in calendar year → sum by strain + THC/CBD
3. Query all destruction records in calendar year → sum by strain + THC/CBD
4. Query stock snapshot as of Dec 31 → by strain + THC/CBD
5. Validate: cultivated - distributed - destroyed ≈ stock change (flag discrepancies)
6. Format into authority-mandated structure
**Output format (PDF + JSON):**
```json
{
"anbauvereinigung": { "name": "...", "erlaubnisnummer": "...", "anschrift": "..." },
"berichtszeitraum": { "von": "2025-01-01", "bis": "2025-12-31" },
"angebaute_mengen": [
{ "sorte": "Amnesia Haze", "menge_gramm": 5000, "thc_prozent": 18.5, "cbd_prozent": 0.8 }
],
"weitergegebene_mengen": [...],
"vernichtete_mengen": [...],
"bestand_jahresende": [...]
}
```
### Step 3.2 — Distribution Log (§26 Abs. 1 Nr. 5)
**File:** `cannamanage-service/src/main/java/de/cannamanage/service/report/DistributionLogGenerator.java`
- Date-range selectable log of all distributions
- Per entry: Datum, Mitglied (Name, Vorname, Geburtsjahr), Menge (g), THC%, Sorte
- Summary: total grams, unique members, average per member
- Flags any quota violations (>25g/day, >50g/month, >30g/month for 18-21, >10% THC for 18-21)
- PDF + CSV export
### Step 3.3 — Stock Inventory Report (§26 Abs. 1 Nr. 2)
**File:** `cannamanage-service/src/main/java/de/cannamanage/service/report/StockInventoryReportGenerator.java`
- Point-in-time snapshot of all cannabis + propagation material on premises
- By strain: grams available, THC%, CBD%, harvest date
- Propagation material: count by type (seeds, clones, cuttings)
- Includes stock movements for selected period (in/out/destroyed)
### Step 3.4 — Destruction Protocol (§26 Abs. 1 Nr. 4)
**File:** `cannamanage-service/src/main/java/de/cannamanage/service/report/DestructionProtocolGenerator.java`
- Official Vernichtungsprotokoll PDF
- Per destruction event: date, batch, strain, grams, method, reason, witness
- Dual signature lines (destroying person + witness)
- Sequential protocol number
- Cumulative annual destruction total
### Step 3.5 — Cultivation Report (§26 Abs. 1 Nr. 3)
**File:** `cannamanage-service/src/main/java/de/cannamanage/service/report/CultivationReportGenerator.java`
- Harvest data from grow module
- Per harvest: strain, grams harvested, THC%, CBD%, harvest date, grow cycle ID
- Annual total by strain
- Growth cycle overview (planted → harvested → distributed/destroyed)
### Step 3.6 — Transport Certificate (§22 Abs. 4 KCanG)
**File:** `cannamanage-service/src/main/java/de/cannamanage/service/report/TransportCertificateGenerator.java`
Per §22(4), the certificate must contain:
1. Name und Anschrift des Sitzes der Anbauvereinigung
2. Datum des Transports
3. Start- und Zieladresse des Transports
4. Mengen in Gramm und Sorten des transportierten Cannabis
5. Name und Kontaktdaten der zuständigen Behörde
Generate as a single-page PDF, suitable for printing and carrying during transport.
### Step 3.7 — Full Authority Export (§26 Abs. 2 + §27)
**File:** `cannamanage-service/src/main/java/de/cannamanage/service/report/FullAuthorityExportGenerator.java`
The "panic button" — generates EVERYTHING the authority needs in one package:
1. All distribution records (complete §26(1) Nr. 5 data)
2. Stock movement history
3. All destruction records
4. All cultivation records
5. All transport records
6. Propagation material sources
7. Member register (name + birth year only, per DSGVO minimization)
**Security:** Requires re-authentication (password re-entry) before generation. This is a high-sensitivity operation containing all member distribution histories (Art. 9 DSGVO health data). The confirmation dialog includes a mandatory reason field for the audit trail.
> **Advisory implementation (v2):** Re-authentication gate added per Security Expert recommendation. The endpoint requires a fresh authentication token (max 30 seconds old) obtained via `POST /api/auth/reconfirm` before the export can proceed.
**Output:** ZIP file — **streamed** (not buffered in memory):
- Uses `StreamingResponseBody` to write ZIP entries directly to the HTTP response
- For clubs with 5+ years and 500 members, exports can reach 50MB+
- Streaming avoids `OutOfMemoryError` on the server
> **Advisory implementation (v2):** Streaming ZIP generation per Architecture Expert recommendation. Uses `ZipOutputStream` wrapping `ServletOutputStream` directly — entries are written sequentially without buffering the entire archive in heap memory.
**ZIP contents:**
- `README.txt` — explains contents and legal basis
- `distributions.json` + `distributions.csv`
- `stock.json`
- `destructions.json`
- `cultivation.json`
- `transports.json`
- `members.json` (anonymized: name + birth year only)
- `summary.pdf` — human-readable overview
### Step 3.8 — Distribution Info Sheet (§21 Abs. 2 KCanG)
**File:** `cannamanage-service/src/main/java/de/cannamanage/service/report/DistributionInfoSheetGenerator.java`
Printable info sheet that must be handed out at every distribution:
- Weight in grams
- Harvest date
- Best-before date
- Strain name
- Average THC%
- Average CBD%
- Health warnings (§21(3) — standardized text)
Generate as small PDF (A5 or 1/3 A4) — can be batch-printed.
---
## Phase 4: DSGVO Templates & Verein Administrative Reports
### Step 4.1 — Verarbeitungsverzeichnis (VVT) Generator
**File:** `cannamanage-service/src/main/java/de/cannamanage/service/report/VvtGenerator.java`
Pre-filled template for Anbauvereinigungen:
- 8 standard processing activities pre-defined (member mgmt, distributions, finance, video, etc.)
- Club-specific data filled in (name, address, DPO name if applicable)
- Output: PDF (A4, multi-page table format per Art. 30 requirements)
### Step 4.2 — TOM (Technisch-Organisatorische Maßnahmen) Document
**File:** `cannamanage-service/src/main/java/de/cannamanage/service/report/TomGenerator.java`
Checklist-style PDF with:
- Zutrittskontrolle (physical access)
- Zugangskontrolle (system access — passwords, 2FA)
- Zugriffskontrolle (data access — roles, permissions)
- Trennungskontrolle (purpose limitation)
- Pseudonymisierung/Verschlüsselung
- Verfügbarkeit und Belastbarkeit (backups, disaster recovery)
- Verfahren zur regelmäßigen Überprüfung
Pre-filled with what CannaManage provides (encryption, role-based access, audit log, etc.).
### Step 4.3 — DSFA (Datenschutz-Folgenabschätzung) Template
**File:** `cannamanage-service/src/main/java/de/cannamanage/service/report/DsfaGenerator.java`
Required because cannabis distribution data = health-related data (Art. 9 DSGVO).
Structure:
1. Beschreibung der Verarbeitung (automated from VVT)
2. Bewertung der Notwendigkeit (pre-filled: §26 KCanG mandates the processing)
3. Risikobewertung (template with common risks pre-identified)
4. Abhilfemaßnahmen (auto-filled from TOM document)
### Step 4.4 — Löschkonzept (Deletion Concept)
**File:** `cannamanage-service/src/main/java/de/cannamanage/service/report/DeletionConceptGenerator.java`
Documents when each category of data is deleted:
- Maps data categories → retention periods → deletion triggers
- References §26(2) KCanG (5 years), §147 AO (6/8/10 years)
- Describes the automated retention checking (RetentionService)
- Lists manual review process
### Step 4.4b — Breach Notification Template (Art. 33/34 DSGVO) — **P1**
**File:** `cannamanage-service/src/main/java/de/cannamanage/service/report/BreachNotificationGenerator.java`
> **Advisory implementation (v2):** Elevated from P2 to P1 per Risk/Compliance Expert recommendation. Cannabis distribution data is Art. 9 DSGVO health data — a breach could cause significant harm (employment discrimination, social stigma). Having the template ready before an incident occurs is critical.
Pre-filled breach notification PDF with:
- Art. 33 fields: nature of breach, categories of data subjects affected, approximate number, DPO contact, likely consequences, measures taken
- Art. 34 fields: communication to affected data subjects (plain language)
- Auto-fills club details, DPO info, and data category descriptions from VVT
- Checklist: 72-hour notification deadline reminder, authority contact details
- Editable fields for incident-specific details (what happened, when discovered)
**Priority justification:** A cannabis club experiencing a data breach has 72 hours to notify the Landesdatenschutzbehörde. Without a pre-prepared template, clubs will scramble under pressure and risk non-compliance with Art. 33(1) DSGVO. Given the sensitivity of cannabis health data, this is P1.
### Step 4.5 — Mitgliederliste für Vereinsregister
**File:** `cannamanage-service/src/main/java/de/cannamanage/service/report/MemberListRegistryGenerator.java`
Formatted per BGB requirements for Vereinsregister:
- Minimal data: Name, Vorname, Anschrift, Eintrittsdatum
- Sorted alphabetically
- Includes total count and date of generation
- Footer: "Erstellt gemäß §67 BGB"
### Step 4.6 — Vorstandsänderung-Meldung (Board Change Template)
**File:** `cannamanage-service/src/main/java/de/cannamanage/service/report/BoardChangeNoticeGenerator.java`
Template for notifying Registergericht of board changes:
- Old board composition
- New board composition (from board_members table)
- Date of election (from assembly records)
- Reference to MV protocol
- Signature line for new Vorstand
### Step 4.7 — Jahresbericht des Vorstands (Annual Board Report)
**File:** `cannamanage-service/src/main/java/de/cannamanage/service/report/AnnualBoardReportGenerator.java`
Combines multiple data sources into a report the Vorstand presents to members at the annual MV:
- Member statistics (new, left, total)
- Financial summary (from EÜR)
- Compliance status (KCanG obligations met)
- Key activities (from prevention log, assemblies held, etc.)
- Outlook/plans (editable text section)
---
## Phase 5: Frontend — Berichtszentrale & Sidebar Reorganization
### Step 5.1 — Sidebar Reorganization
**Modified file:** `cannamanage-frontend/src/data/navigations.ts`
Replace flat list with grouped, collapsible sections:
```typescript
export const navigationsData: NavigationType[] = [
{
title: "Betrieb",
icon: "Leaf",
items: [
{ title: "Dashboard", href: "/dashboard", iconName: "LayoutDashboard" },
{ title: "Mitglieder", href: "/members", iconName: "Users" },
{ title: "Ausgabe", href: "/distributions", iconName: "Leaf" },
{ title: "Lager", href: "/stock", iconName: "Package" },
{ title: "Anbau", href: "/grow", iconName: "Sprout" },
],
},
{
title: "Kommunikation",
icon: "MessageCircle",
items: [
{ title: "Schwarzes Brett", href: "/info-board", iconName: "Megaphone" },
{ title: "Kalender", href: "/calendar", iconName: "Calendar" },
{ title: "Forum", href: "/forum", iconName: "MessageSquare" },
],
},
{
title: "Verwaltung",
icon: "Building",
items: [
{ title: "Finanzen", href: "/finance", iconName: "Wallet" },
{ title: "Versammlungen", href: "/assemblies", iconName: "Gavel" },
{ title: "Dokumente", href: "/documents", iconName: "FileArchive" },
{ title: "Vorstand", href: "/board", iconName: "Shield" },
{ title: "Personal", href: "/settings/staff", iconName: "UserCog" },
],
},
{
title: "Compliance",
icon: "ShieldCheck",
items: [
{ title: "Berichtszentrale", href: "/reports", iconName: "FileBarChart" },
{ title: "Protokoll", href: "/audit-log", iconName: "ScrollText" },
{ title: "Einstellungen", href: "/settings", iconName: "Settings" },
],
},
]
```
**Modified component:** Sidebar must support collapsible groups with section headers and icons.
### Step 5.2 — Berichtszentrale Page (Report Center)
**New page:** `cannamanage-frontend/src/app/(app)/reports/page.tsx` (replaces existing basic reports page)
Layout:
- Top: Compliance status cards (4 cards: KCanG 🟢/🟡/🔴, Finanzen, DSGVO, Verein)
- Middle: Upcoming deadlines list (next 90 days)
- Bottom: Report categories as card grid with accordion sub-items
**Empty-state handling (new clubs):**
> **Advisory implementation (v2):** Per UX Expert recommendation — when a club first accesses the Berichtszentrale with no reports generated yet, all compliance indicators will be 🔴 RED. To prevent this from being demoralizing, display a "Compliance Setup" guided callout instead of raw red indicators.
When `generated_reports` is empty for this tenant:
- Show a "Erste Schritte" banner with 4-step setup guide:
1. "VVT erstellen" → links to DSGVO tab
2. "Jahresbericht konfigurieren" → links to KCanG tab
3. "Kassenbuch einrichten" → links to Finance tab
4. "Fristen prüfen" → links to deadlines view
- Compliance cards show "Einrichtung erforderlich" (setup required) in neutral gray instead of alarming red
- After first report is generated in any category, switch to normal traffic-light indicators
- Dismissible: admin can click "Verstanden, Dashboard anzeigen" to skip directly to normal view
Each report card shows:
- Report name
- Last generated date (or "Noch nie erstellt")
- Legal basis reference
- [Generate PDF] [Generate CSV] [Generate JSON] buttons (as applicable)
- [View History] link
### Step 5.3 — Report Category Pages
**New pages:**
- `/reports/finance` — Financial reports tab
- `/reports/compliance` — KCanG reports tab
- `/reports/verein` — Administrative reports tab
- `/reports/dsgvo` — Data protection reports tab
Each with:
- List of available reports in that category
- Generation form (date range, parameters)
- Preview before download
- Download history table
### Step 5.4 — Destruction Record Management
**New pages:**
- `/stock/destructions` — List all destruction records
- `/stock/destructions/new` — Record a new destruction event
Form fields: Batch selection, grams destroyed, propagation count, method, reason, witness name, date.
### Step 5.5 — Transport Record Management
**New pages:**
- `/transports` (or nested under operations)
- `/transports/new` — Plan a new transport (with authority notification reminder)
- `/transports/[id]/certificate` — View/download transport certificate
### Step 5.6 — Prevention Activity Log
**New page:**
- `/board/prevention` — Prevention officer activity log
List of activities + "Add Activity" form. Shows training certificate info.
### Step 5.7 — Report Services (Frontend)
**New file:** `cannamanage-frontend/src/services/report-center.ts`
```typescript
// Report generation triggers
export function useGenerateReport(type: ReportType, params: ReportParams)
export function useReportHistory(type?: ReportType)
export function useComplianceStatus()
export function useUpcomingDeadlines()
export function useDestructionRecords()
export function useTransportRecords()
export function usePreventionActivities()
```
### Step 5.8 — i18n Additions
**Modified files:**
- `cannamanage-frontend/messages/de.json` — all new report names, categories, form labels
- `cannamanage-frontend/messages/en.json` — English translations
---
## Phase 6: Compliance Dashboard + Retention + Integration
### Step 6.1 — Compliance Status Service (Backend)
**File:** `cannamanage-service/src/main/java/de/cannamanage/service/ComplianceDashboardService.java`
Calculates real-time compliance status per area:
```java
public ComplianceStatus getOverallStatus(UUID tenantId) {
// KCanG: Check if annual report was submitted, distribution records complete
// Finance: Check if EÜR generated for last fiscal year
// DSGVO: Check if VVT exists and is recent
// Verein: Check board term expiry, next MV date
// Yellow = deadline within 30 days or data gaps
// Red = deadline passed or critical gaps
// Green = everything current
}
```
### Step 6.2 — Deadline Seeding + Scheduler
**File:** `cannamanage-service/src/main/java/de/cannamanage/service/ComplianceDeadlineScheduler.java`
On tenant creation (or first report center access):
- Seed standard annual deadlines (Jan 31 authority report, MV, EÜR)
- Daily scheduler checks for approaching deadlines → creates notifications
- Auto-rolls annual deadlines to next year after completion
### Step 6.3 — Retention Service Implementation
Implements the daily retention check:
- Queries all data categories
- Compares creation date against retention period rules
- Creates admin notification when data approaches end-of-retention
- Provides "Löschprotokoll" (deletion log) export
### Step 6.4 — API Endpoint Summary
New REST endpoints:
```
# Destruction Records
POST /api/destructions
GET /api/destructions
GET /api/destructions/{id}
GET /api/destructions/{id}/pdf
# Transport Records
POST /api/transports
GET /api/transports
GET /api/transports/{id}
GET /api/transports/{id}/certificate/pdf
POST /api/transports/{id}/notify-authority
# Propagation Sources
POST /api/propagation-sources
GET /api/propagation-sources
DELETE /api/propagation-sources/{id}
# Prevention Activities
POST /api/prevention-activities
GET /api/prevention-activities
DELETE /api/prevention-activities/{id}
# Report Center
POST /api/reports/generate (body: {type, params})
GET /api/reports/history
GET /api/reports/{id}/download
# Specific Report Generators
GET /api/reports/eur/pdf?year=2025
GET /api/reports/eur/csv?year=2025
GET /api/reports/authority-annual/pdf?year=2025
GET /api/reports/authority-annual/json?year=2025
GET /api/reports/distribution-log/pdf?from=&to=
GET /api/reports/distribution-log/csv?from=&to=
GET /api/reports/stock-inventory/pdf?date=
GET /api/reports/destruction-protocol/pdf?from=&to=
GET /api/reports/cultivation/pdf?year=
GET /api/reports/transport-certificate/{id}/pdf
GET /api/reports/authority-export/zip?from=&to=
GET /api/reports/member-list-registry/pdf
GET /api/reports/board-change-notice/pdf
GET /api/reports/annual-board-report/pdf?year=
GET /api/reports/vvt/pdf
GET /api/reports/tom/pdf
GET /api/reports/dsfa/pdf
GET /api/reports/deletion-concept/pdf
GET /api/reports/fee-confirmation/pdf?memberId=&year=
GET /api/reports/fee-confirmation/batch/zip?year=
GET /api/reports/info-sheet/{distributionId}/pdf
# Compliance Dashboard
GET /api/compliance/status
GET /api/compliance/deadlines
PUT /api/compliance/deadlines/{id}/complete
```
### Step 6.5 — Integration Testing
- Test each report generator with realistic data
- Verify PDF output quality (correct German formatting, legal references)
- Verify CSV encoding (ISO-8859-1, semicolons, decimal comma)
- Verify JSON schema for authority export
- Test retention service with date manipulation
- Test compliance status calculation edge cases
- E2E: Generate report from frontend → download → verify contents
### Step 6.6 — Seed Data Enhancement
Extend existing seed data with:
- Sample destruction records
- Sample transport records
- Sample propagation sources
- Prevention activities
- Pre-seeded compliance deadlines for demo
- THC/CBD percentages on existing distribution seeds
---
## Technical Decisions
| Decision | Choice | Rationale |
|----------|--------|-----------|
| PDF library | Reuse existing OpenPDF | Proven in Sprint 5+8, already in dependencies |
| CSV encoding | ISO-8859-1 + semicolons | German DATEV standard, accountants expect this |
| CSV injection prevention | Prefix dangerous cells with single quote | Cells starting with `=`, `+`, `-`, `@` get `'` prefix to prevent Excel formula injection |
| JSON schema | Custom (no standard exists) | KCanG is too new for an official schema — we define it |
| ZIP generation | `java.util.zip.ZipOutputStream` via `StreamingResponseBody` | Standard library, streamed to avoid OOM on large exports |
| Report storage | Database record + file in documents module | Reuse existing document storage from Sprint 8 |
| Retention checking | Spring `@Scheduled` | Simple, already used for payment reminders |
| Rate limiting | Resilience4j `@RateLimiter` — 5 reports/min/tenant | Prevents DoS via report generation spam (CPU-intensive PDF rendering) |
| Authority export auth | Re-authentication required (password re-entry) | High-sensitivity: contains all member health data |
| Sidebar state | LocalStorage for collapsed/expanded | No backend needed, instant UX |
| Report preview | Server-side rendered HTML → PDF | Same template for preview and final PDF |
| Empty-state UX | Guided setup banner for new clubs | Prevents demoralizing all-red compliance dashboard on first access |
> **Advisory implementations (v2):** CSV injection prevention, rate limiting, re-authentication gate, streaming ZIP, and empty-state UX all added per panel review recommendations.
---
## File Count Estimate
| Phase | New Backend Files | New Frontend Files | Modified Files |
|-------|------------------|--------------------|----------------|
| Phase 1 | ~18 (entities, repos, controllers, enums) | 0 | 3 (Distribution entity, pom.xml) |
| Phase 2 | ~5 (report generators) | 0 | 1 (existing ReportService) |
| Phase 3 | ~9 (report generators) | 0 | 0 |
| Phase 4 | ~8 (report generators) | 0 | 0 |
| Phase 5 | 0 | ~20 (pages, components, services) | 5 (sidebar, navigations, i18n) |
| Phase 6 | ~4 (services, schedulers) | ~3 (dashboard components) | 2 (seed data) |
| **Total** | **~44** | **~23** | **~11** |
---
## Dependencies & Risks
| Risk | Probability | Impact | Mitigation |
|------|-------------|--------|------------|
| Authority report format changes | Low (KCanG is new) | Medium | JSON export is flexible, PDF regenerable |
| DSGVO template incomplete | Medium | Low | Templates are editable, clubs can customize |
| PDF formatting issues with German umlauts | Low | Low | OpenPDF handles UTF-8, already proven |
| Retention period legal ambiguity (§26 "5 Jahre" — from when?) | Medium | Medium | Conservative: 5 years from record creation + admin review |
| Large data export performance | Low | Medium | Stream responses, chunked ZIP generation |
| Sidebar reorg breaks existing bookmarks | Low | Low | URL paths stay the same, only visual grouping changes |
---
## Success Criteria
1. ✅ All P0 reports generate valid PDFs with correct German formatting
2. ✅ Authority annual report (§26(3)) produces complete, accurate data
3. ✅ Full authority export generates machine-readable JSON bundle
4. ✅ EÜR matches standard format for Finanzamt submission
5. ✅ Sidebar is grouped and collapsible with no functionality loss
6. ✅ Compliance dashboard shows correct status for all 4 areas
7. ✅ Retention service identifies records approaching end-of-life
8. ✅ All exports respect DSGVO data minimization (birth year, not full DOB in authority exports)
9. ✅ CSV exports use correct encoding (ISO-8859-1) and delimiter (semicolons)
10. ✅ Report history tracks what was generated when and by whom