26a77dd269
- 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
786 lines
32 KiB
Markdown
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
|