Patrick Plate 05fd679c4d feat(sprint7): Phase 2.5 — Club Event Calendar
- Flyway V14: club_events + event_rsvps tables with reminder_sent tracking
- Enums: EventType, RsvpStatus, RecurrenceRule + extend AuditEventType/NotificationType
- Entities: ClubEvent (extends AbstractTenantEntity), EventRsvp (unique event+member)
- Repositories: ClubEventRepository, EventRsvpRepository with date-range and status queries
- EventService: CRUD, RSVP with maxAttendees enforcement (409 if full), iCal RFC 5545 generation, recurring event virtual expansion, notifications on create/cancel, auto-post to Info Board
- EventReminderScheduler: hourly check, 24h reminder to ACCEPTED/MAYBE attendees
- EventController: admin CRUD (MANAGE_INFO_BOARD permission), portal upcoming events, RSVP endpoint, iCal download (text/calendar), attendee list
- Frontend: events.ts service (React Query hooks matching apiClient pattern), admin calendar page (month grid with event dots, create dialog, event cards), portal events page (RSVP buttons, capacity display)
- Navigation: added Kalender with Calendar icon
- i18n: events.* keys in de.json and en.json
- UI: added @radix-ui/react-switch + Switch component
2026-06-13 20:16:56 +02:00

CannaManage

Multi-tenant cannabis club management platform for German Anbauvereinigungen (cultivation associations) under CanG §19.

Overview

CannaManage handles member management, distribution tracking, and legal compliance for cannabis cultivation clubs in Germany. It enforces the strict quotas mandated by the Cannabis Act (CanG) — including monthly limits (50g adult / 30g under-21), daily limits (25g), and THC restrictions for minors.

Tech Stack

Component Technology
Runtime Java 21 (Temurin)
Framework Spring Boot 4.0.6
Security Spring Security 7.0 + JWT (JJWT 0.12.6)
ORM Hibernate 7 / JPA
Database PostgreSQL (prod), H2 (test)
Migrations Flyway 10
API Docs SpringDoc OpenAPI 2.8.6
Build Maven (multi-module)
Container Docker Compose (Postgres + app)

Project Structure

cannamanage/
├── cannamanage-domain/      # JPA entities, enums, TenantContext
├── cannamanage-service/     # Business logic, repositories, ComplianceService
├── cannamanage-api/         # Spring Boot app, controllers, security, DTOs
├── docs/
│   └── sprint-2/           # Sprint planning docs
└── docker-compose.yml       # Local dev environment

Modules

cannamanage-domain

JPA entities with multi-tenant isolation via @Filter("tenantFilter"):

  • Member — club members with age tracking
  • Distribution — cannabis distribution records
  • MonthlyQuota — per-member monthly usage tracking
  • Batch / Strain / StockMovement — inventory management
  • Club — association registration
  • User — authentication accounts

cannamanage-service

  • ComplianceService — CanG §19 quota enforcement (25 unit tests)
  • Repositories for all entities

cannamanage-api

  • Auth — JWT login + refresh token rotation (SHA-256 hashed)
  • Members — CRUD for association members
  • Distributions — compliance-gated distribution recording
  • Stock — batch and inventory management
  • Compliance — quota status API
  • Multi-tenant isolation via TenantFilterAspect (Hibernate @Filter activation)

API Endpoints

Method Path Auth Description
POST /api/v1/auth/login Public Login with email + password
POST /api/v1/auth/refresh Public Refresh token rotation
GET /api/v1/compliance/quota/{memberId} ADMIN, MEMBER Monthly quota status
GET/POST/PUT /api/v1/members/** ADMIN, MEMBER Member CRUD
POST /api/v1/distributions/** ADMIN, MEMBER Record distributions
GET/POST /api/v1/stock/** ADMIN Stock management

Swagger UI: http://localhost:8080/swagger-ui.html

Running Locally

# Start PostgreSQL
docker compose up -d

# Run the app
JAVA_HOME=/path/to/jdk-21 ./mvnw spring-boot:run -pl cannamanage-api

# Run all tests (H2 in-memory)
JAVA_HOME=/path/to/jdk-21 ./mvnw clean verify

Testing

  • 37 tests total — all green
  • 25 unit tests (ComplianceServiceTest) — quota enforcement logic
  • 7 integration tests (AuthControllerIntegrationTest) — full HTTP auth flow
  • 5 integration tests (ComplianceControllerIntegrationTest) — quota API with JWT

Integration tests use @SpringBootTest(webEnvironment = RANDOM_PORT) with H2 and Spring's RestClient.

Security Model

  • Stateless JWT — no session, no UserDetailsService
  • Roles: ADMIN (full access), MEMBER (self-service), STAFF (Sprint 3)
  • Multi-tenancy: Hibernate @Filter activated per-request via AOP aspect
  • Refresh tokens: SHA-256 hashed (Spring Security 7 enforces BCrypt 72-byte limit)
  • Token rotation on refresh — old tokens invalidated

Sprint History

Sprint Focus Status
1 Domain entities, ComplianceService, 25 tests Done
2 REST API, Spring Security, JWT, OpenAPI, integration tests Done
3 Member portal, STAFF role, real-time notifications 📋 Planned

License

Private — Patrick Plate

S
Description
CannaManage — B2B SaaS for German Cannabis Social Clubs (Anbauvereinigungen)
Readme 7.8 MiB
Languages
Java 61%
TypeScript 38.1%
JavaScript 0.4%
CSS 0.3%
Shell 0.2%