Files
cannamanage/docs/sprint-9/cannamanage-sprint9-plan.md
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

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.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:

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

/**
 * 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 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:

  • ReportTypeANNUAL_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
  • DestructionMethodINCINERATION, COMPOSTING, CHEMICAL, OTHER
  • TransportStatusPLANNED, AUTHORITY_NOTIFIED, IN_TRANSIT, COMPLETED
  • ComplianceAreaKCANG, FINANCE, DSGVO, VEREIN
  • ComplianceStatusGREEN, YELLOW, RED
  • RetentionCategoryKCANG_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):

{
  "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:

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

// 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:

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