- 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
32 KiB
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.
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.sqlcannamanage-api/src/main/resources/db/migration/V24__transport_records.sqlcannamanage-api/src/main/resources/db/migration/V25__propagation_sources.sqlcannamanage-api/src/main/resources/db/migration/V26__prevention_activities.sqlcannamanage-api/src/main/resources/db/migration/V27__generated_reports.sqlcannamanage-api/src/main/resources/db/migration/V28__compliance_deadlines.sqlcannamanage-api/src/main/resources/db/migration/V29__distribution_thc_cbd.sql
V29 specifically:
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.DestructionRecordde.cannamanage.domain.entity.TransportRecordde.cannamanage.domain.entity.PropagationSourcede.cannamanage.domain.entity.PreventionActivityde.cannamanage.domain.entity.GeneratedReportde.cannamanage.domain.entity.ComplianceDeadline
Modified entities:
Distribution— addthcPercentage,cbdPercentage,strainNamefields
Step 1.3 — Repositories
DestructionRecordRepositoryTransportRecordRepositoryPropagationSourceRepositoryPreventionActivityRepositoryGeneratedReportRepositoryComplianceDeadlineRepository
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
/**
* 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
@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
@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 generationTransportRecordController— CRUD + certificate generationPropagationSourceController— CRUDPreventionActivityController— CRUDComplianceDeadlineController— CRUD + status updatesReportController(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_NOTIFICATIONDestructionMethod—INCINERATION,COMPOSTING,CHEMICAL,OTHERTransportStatus—PLANNED,AUTHORITY_NOTIFIED,IN_TRANSIT,COMPLETEDComplianceArea—KCANG,FINANCE,DSGVO,VEREINComplianceStatus—GREEN,YELLOW,REDRetentionCategory—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:
- Query all
financial_transactionsfor the selected calendar year - Group into Einnahmen (INCOME transactions) and Ausgaben (EXPENSE transactions)
- Sub-group by
expense_category/ income source - Calculate: Total income, total expenses, Überschuss/Fehlbetrag
- Include: Opening balance (Jan 1) and closing balance (Dec 31)
- Generate PDF in standard EÜR format (Anlage EÜR compatible structure)
- 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:
- Query all harvests (from grow module) in calendar year → sum by strain + THC/CBD
- Query all distributions in calendar year → sum by strain + THC/CBD
- Query all destruction records in calendar year → sum by strain + THC/CBD
- Query stock snapshot as of Dec 31 → by strain + THC/CBD
- Validate: cultivated - distributed - destroyed ≈ stock change (flag discrepancies)
- Format into authority-mandated structure
Output format (PDF + 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:
- Name und Anschrift des Sitzes der Anbauvereinigung
- Datum des Transports
- Start- und Zieladresse des Transports
- Mengen in Gramm und Sorten des transportierten Cannabis
- 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:
- All distribution records (complete §26(1) Nr. 5 data)
- Stock movement history
- All destruction records
- All cultivation records
- All transport records
- Propagation material sources
- 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/reconfirmbefore the export can proceed.
Output: ZIP file — streamed (not buffered in memory):
- Uses
StreamingResponseBodyto write ZIP entries directly to the HTTP response - For clubs with 5+ years and 500 members, exports can reach 50MB+
- Streaming avoids
OutOfMemoryErroron the server
Advisory implementation (v2): Streaming ZIP generation per Architecture Expert recommendation. Uses
ZipOutputStreamwrappingServletOutputStreamdirectly — entries are written sequentially without buffering the entire archive in heap memory.
ZIP contents:
README.txt— explains contents and legal basisdistributions.json+distributions.csvstock.jsondestructions.jsoncultivation.jsontransports.jsonmembers.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:
- Beschreibung der Verarbeitung (automated from VVT)
- Bewertung der Notwendigkeit (pre-filled: §26 KCanG mandates the processing)
- Risikobewertung (template with common risks pre-identified)
- 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:
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:
- "VVT erstellen" → links to DSGVO tab
- "Jahresbericht konfigurieren" → links to KCanG tab
- "Kassenbuch einrichten" → links to Finance tab
- "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
// 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 labelscannamanage-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:
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
- ✅ All P0 reports generate valid PDFs with correct German formatting
- ✅ Authority annual report (§26(3)) produces complete, accurate data
- ✅ Full authority export generates machine-readable JSON bundle
- ✅ EÜR matches standard format for Finanzamt submission
- ✅ Sidebar is grouped and collapsible with no functionality loss
- ✅ Compliance dashboard shows correct status for all 4 areas
- ✅ Retention service identifies records approaching end-of-life
- ✅ All exports respect DSGVO data minimization (birth year, not full DOB in authority exports)
- ✅ CSV exports use correct encoding (ISO-8859-1) and delimiter (semicolons)
- ✅ Report history tracks what was generated when and by whom