Commit Graph

89 Commits

Author SHA1 Message Date
Patrick Plate 5defe42d67 feat(sprint10): Phase 3 — BankImportService + REST API
Implements the orchestrator and REST endpoints for the bank statement
import wizard (Sprint 10 Phase 3).

Service layer (cannamanage-service):
- BankImportService: upload → SHA-256 dedup → parse → match → persist
  in two transactional steps (file I/O outside @Transactional, persist
  in @Transactional helper). Methods: uploadAndParse, confirmMatch,
  confirmAllMatched (≥90% confidence), manualAssign, skipTransaction,
  completeSession, query helpers.
- GoBD §147 AO immutability guard: assertSessionMutable() rejects any
  mutation on COMPLETED/FAILED sessions with German error messages.
- Hard 5MB upload cap enforced before parsing.
- Audit events: BANK_IMPORT_STARTED / BANK_PAYMENT_CONFIRMED /
  BANK_IMPORT_COMPLETED. Uploader notified via NotificationService.

REST layer (cannamanage-api):
- BankImportController under /api/v1/finance/import/*:
  POST sessions (multipart), GET sessions/single/transactions(?status=),
  POST {id}/transactions/{txnId}/confirm|assign|skip,
  POST {id}/confirm-all, POST {id}/complete,
  GET/POST/DELETE csv-mappings.
- Permission: FINANCE_IMPORT with MANAGE_FINANCES fallback.
- Defence-in-depth tenant check on every path-parameter ID.

DTOs (cannamanage-api/dto/bankimport):
- ImportSessionResponse, TransactionResponse, ConfirmRequest,
  AssignRequest, SkipRequest, BulkConfirmResponse, CreateMappingRequest.

Persistence:
- V33__bank_import_file_hash.sql: adds file_hash VARCHAR(64) + unique
  partial index (club_id, file_hash) for duplicate-upload detection.
- BankImportSession.fileHash field, repository.existsByClubIdAndFileHash.

Configuration:
- application.properties: multipart enabled, max-file-size=5MB,
  max-request-size=6MB.

Build: mvn package -DskipTests  (cannamanage-api fat JAR 92MB).
2026-06-15 17:47:27 +02:00
Patrick Plate 527e9b1219 feat(sprint10): Phase 2 — Payment matching engine with confidence scoring 2026-06-15 17:30:28 +02:00
Patrick Plate 55110c95af feat(sprint10): Phase 1 — Data model + bank statement parsers (MT940, CAMT.053, CSV)
Implements the Sprint 10 Phase 1 foundation for the Smart Payment Import feature:

Domain layer:
- 3 new enums: BankFormat (MT940, CAMT053, CSV), ImportSessionStatus, MatchStatus
- StaffPermission.FINANCE_IMPORT
- AuditEventType: BANK_IMPORT_STARTED/COMPLETED/FAILED + BANK_PAYMENT_CONFIRMED
- NotificationType.BANK_IMPORT_COMPLETED
- ConsentType.BANK_DATA (DSGVO consent for IBAN storage)
- 3 new entities: BankImportSession, BankTransaction, CsvColumnMapping
- Member: + iban (VARCHAR 34) + ibanConsentDate
- MemberStatus.LEFT (semantic alias for RESIGNED, referenced by Sprint 9 RetentionService)

Persistence:
- V30__bank_import_sessions.sql
- V31__bank_transactions.sql
- V32__csv_column_mappings.sql (also adds iban + iban_consent_date to members)
- 3 Spring Data repositories

Parser infrastructure (cannamanage-service/src/main/java/de/cannamanage/service/bankimport):
- BankStatementParser interface (Strategy pattern, Spring-injected list)
- ParsedTransaction + ParseResult records
- BankStatementParseException (parse errors)
- Mt940Parser: custom state machine, CENTURY_BOUNDARY=70 for YY→YYYY, proprietary
  header tolerance (skips lines before first :20: for StarMoney/WISO/Hibiscus wrappers)
- Camt053Parser: StAX streaming with XXE hardening (IS_SUPPORTING_EXTERNAL_ENTITIES,
  SUPPORT_DTD, IS_REPLACING_ENTITY_REFERENCES all false); supports camt.053.001.02
  and camt.053.001.08 namespaces
- CsvBankParser: Apache Commons CSV with configurable columns per club; German number
  format ("1.234,56"); ISO-8859-1 default encoding
- BankStatementParserService: filename-extension hint + content probe; throws
  UnrecognizedFormatException when no parser claims the file

Build verified via Docker (cannamanage-api:sprint10-phase1).

Sprint 9 fix (incidental, required to compile):
- Added MemberStatus.LEFT (Sprint 9 RetentionService referenced it but the enum
  value was missing)
- MemberListRegistryGenerator: added LEFT to formatStatus() switch (mapped to
  "Ausgetreten", same as RESIGNED)

Sprint 10 docs: analysis, plan, plan-review, testplan.

Co-Authored-By: Lumen <lumen@cannamanage.de>
2026-06-15 17:21:55 +02:00
Patrick Plate 57f418f7c9 feat(sprint9): Phase 6 — Compliance dashboard, RetentionService, testing
Backend:
- ComplianceDashboardService: traffic-light status per ComplianceArea
  (KCANG/FINANCE/DSGVO/VEREIN) based on deadlines, payments, board positions
- RetentionService: scheduled anonymization of expired member data (KCanG §24,
  5 years), with dry-run preview and retention report endpoints
- ComplianceDeadlineSeeder: seeds 5 standard recurring deadlines on club creation
- ComplianceDashboardController: GET /api/v1/compliance/dashboard,
  GET /retention, POST /retention/preview
- Repository additions: countOverdue, countActive board positions/members

Frontend:
- /compliance page with traffic-light status cards per area
- Overdue deadlines section (highlighted red) with 'days overdue' badges
- Upcoming deadlines with 'days until due' badges and 'Complete' buttons
- Retention info cards (KCanG §24: 5y, AO §147: 10y, DSGVO: 2y)
- Navigation: added 'Compliance-Status' to sidebar under Compliance group
- compliance-dashboard.ts service with mock data for dev mode

Build verified: pnpm build passes clean.
2026-06-15 14:12:01 +02:00
Patrick Plate 87511e0485 feat(sprint9): Phase 5 — Berichtszentrale, sidebar reorg, dashboard enhancement
- Sidebar: reorganized into 4 collapsible groups (Betrieb, Kommunikation, Verwaltung, Compliance)
- Berichtszentrale: new /reports-center page with report cards grouped by category (Finance, KCanG, DSGVO, Admin), format selector, date range pickers, Behörden-Export dialog with password protection
- Dashboard: added Outstanding Payments and Monthly Income KPI cards, Upcoming Events widget, Latest Announcements widget, conditional alert cards
- Pricing: fixed mobile overflow at 375px viewport on comparison table
- Frontend service: new compliance-reports.ts with React Query hooks for report generation, authority export, and download
- i18n: added reportsCenter.* and dashboard widget keys to de.json and en.json
2026-06-15 13:45:48 +02:00
Patrick Plate c3722ab726 feat(sprint9): Phase 4 — DSGVO templates + Verein admin reports
Implements 7 report generators for Phase 4:

DSGVO templates:
- VvtReportGenerator: Art. 30 DSGVO Verarbeitungsverzeichnis (PDF)
- TomReportGenerator: Art. 32 DSGVO Technisch-organisatorische Maßnahmen (PDF)
- DsfaReportGenerator: Art. 35 DSGVO Datenschutz-Folgenabschätzung (PDF)
- LoeschkonzeptGenerator: Löschkonzept with retention rules (PDF + JSON)
- BreachNotificationGenerator: Art. 33/34 DSGVO 72h breach notification (PDF)

Verein administration:
- MemberListRegistryGenerator: §67 BGB Mitgliederliste for Amtsgericht (PDF)
- BoardChangeGenerator: §67 BGB Vorstandsänderung notification (PDF)

Also adds:
- BreachReportParameters record for breach notification input
- MemberRepository.findByClubIdOrderByLastNameAscFirstNameAsc()
2026-06-15 13:22:46 +02:00
Patrick Plate 3ca231dc9c feat(sprint9): Phase 3 — KCanG compliance reports + Behörden-Export
Implemented 6 KCanG compliance report generators and the hero
Behörden-Export feature:

- AnnualAuthorityReportGenerator: Multi-section §22 KCanG annual report
  (9 sections: Vereinsdaten, Mitgliederstatistik, Anbauübersicht,
  Weitergabe-Statistik, Bestandsführung, Vernichtung, Transport,
  Prävention, Jugendschutz)
- DistributionLogGenerator: §19(4) distribution log (PDF+CSV,
  anonymized member data per DSGVO)
- DestructionProtocolGenerator: §22 destruction protocol with
  signature lines and sequential numbering
- TransportCertificateGenerator: §22(4) transport documentation
- BestandsfuehrungGenerator: Stock flow report (PDF+CSV) with
  per-batch breakdown
- PreventionActivityReportGenerator: §23 prevention activities

Authority Export (Behörden-Export) — THE HERO FEATURE:
- AuthorityExportService: Streaming ZIP generation via ZipOutputStream
- Re-authentication required (password re-entry + BCrypt verification)
- Mandatory reason field stored in audit trail
- Rate limited: max 1 export per hour per tenant
- ZIP contains all compliance PDFs + anonymized member JSON + manifest
- Memory-efficient: PDFs generated and streamed sequentially

Endpoint: POST /api/v1/reports/authority-export
Request: { year, password, reason }
Response: StreamingResponseBody (application/zip)

Also enhanced repositories:
- DestructionRecordRepository: date-range queries + sum aggregation
- TransportRecordRepository: date-range queries
2026-06-15 12:53:12 +02:00
Patrick Plate a29c38756c feat(sprint9): Phase 2 — Financial report generators (EÜR, Kassenbuch, Beitragsbescheinigung)
Implements Sprint 9 Phase 2 financial report generators:

- MemberReportParameters: new parameter record for per-member reports
- EurReportGenerator: Einnahmen-Überschuss-Rechnung (§4(3) EStG)
  - PDF: professional layout with income/expense sections, monthly breakdown
  - CSV: semicolon-delimited, ISO-8859-1, German decimal format
  - JSON: ELSTER-compatible structure for Steuerberater import
- KassenbuchExportGenerator: GoBD-compliant cash book export
  - PDF: landscape A4, running balance, sequential Beleg-Nr
  - CSV: GoBD-compliant format with injection prevention
  - Includes opening balance calculation and period totals
- BeitragsbescheinigungGenerator: membership fee confirmation per member
  - PDF: club letterhead, payment table, signature lines
  - For member tax purposes (Sonderausgaben)
- ReportGeneratorService: added getAvailableTypes() method
- ReportController: added GET /api/v1/reports/types endpoint

All generators are @Service beans auto-discovered by ReportGeneratorService.
Docker build verified green.
2026-06-15 12:22:53 +02:00
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
Patrick Plate 2d83c4b8a1 fix: resolve Sprint 8 compilation issues, Docker build green 2026-06-15 09:57:32 +02:00
Patrick Plate 61b0cd92be feat(sprint8): Phase 5+6 — Integration, schedulers, tier enforcement, testing
Phase 5 — Integration:
- PaymentReminderScheduler: monthly cron at 9am, sends PAYMENT_REMINDER
  and PAYMENT_OVERDUE (30+ days) notifications, audit logged
- BoardTermScheduler: daily cron at 8am, sends BOARD_TERM_EXPIRING
  notification 30 days before term end (1-day window for single delivery)
- Assembly protocol auto-archive: completeAssembly() generates PDF via
  AssemblyProtocolService and stores it in DocumentService.archiveProtocol()
- PlanTierService: Sprint 8 limits added (Kassenbuch 3mo starter, 1 MV/year
  starter, 100MB/1GB/unlimited document storage)
- Notification wiring: PAYMENT_RECEIVED sent to member on recordPayment(),
  sendToAllMembers() added to NotificationService for assembly invitations
- Seed data: 2 fee schedules (Regular €30, Reduced €15), 4 fee assignments,
  3 sample payments, 2 board positions, 2 board members

Phase 6 — Testing infrastructure:
- V22 migration: protocol_document_id on assemblies table
- Schedulers disabled in test profile (cannamanage.schedulers.enabled=false)
- Scheduler property configurable via SCHEDULERS_ENABLED env var

Additional fixes (pre-existing Phase 4 issues):
- AssemblyProtocolService: fixed Document import ambiguity (OpenPDF vs entity)
- AuditService: added convenience overloads for UUID actorId/clubId
- ReceiptPdfService: fixed getReceiptNumber/getMemberNumber/getPaymentDate
- StaffPermissionChecker: added getUserId/getClubId/getTenantId helpers
- FinanceService: added getPaymentById, exportLedgerCsv methods
2026-06-15 09:22:49 +02:00
Patrick Plate e4698827ee feat(sprint8): Phase 4 — Dokumentenarchiv + Vorstandsverwaltung
Backend:
- V20 migration: documents table with category, access_level, file storage
- V21 migration: board_positions + board_members with term tracking
- Document entity + DocumentCategory/DocumentAccessLevel enums
- BoardPosition + BoardMember entities
- Extended AuditEventType (DOCUMENT_UPLOADED/DELETED, BOARD_MEMBER_ELECTED/REMOVED)
- Extended StaffPermission (MANAGE_DOCUMENTS)
- Extended NotificationType (BOARD_TERM_EXPIRING)
- DocumentService: upload, list, download, delete, storage usage
- BoardService: positions CRUD, elect/remove members, current/history
- DocumentController: multipart upload, filtered list, download, delete, portal
- BoardController: positions, elect, remove, current board, history, portal

Frontend:
- documents.ts + board.ts service layers
- Admin /documents page: grouped by category, upload dialog, filter, download/delete
- Admin /board page: current board cards, position management, elect member dialog
- Navigation: added Dokumente + Vorstand to sidebar
- i18n: documents.* + board.* keys in de.json + en.json
2026-06-15 08:53:38 +02:00
Patrick Plate b22702317a feat(sprint8): Phase 3 — Mitgliederversammlung (assemblies, voting, protocol PDF)
Backend:
- V19 migration: assemblies, assembly_agenda_items, assembly_attendees, assembly_votes, assembly_vote_records
- Enums: AssemblyType, AssemblyStatus, AgendaItemType, VoteType, VoteDecision, VoteResult
- Entities: Assembly, AssemblyAgendaItem, AssemblyAttendee, AssemblyVote, AssemblyVoteRecord
- Repositories: Assembly, AgendaItem, Attendee, Vote, VoteRecord
- AssemblyService: full lifecycle (create, invite, start, attend, vote, quorum, complete)
- AssemblyProtocolService: OpenPDF protocol generation (§147 AO compliant)
- AssemblyController: admin + portal endpoints
- Extended: AuditEventType, NotificationType, StaffPermission

Frontend:
- Assembly service with full API client and TypeScript types
- Admin assemblies list page with create dialog (agenda builder)
- Admin assembly detail page (quorum, agenda, votes, attendees)
- Navigation: Versammlungen with Gavel icon (after Finanzen)

Legal basis: §32-§40 BGB (Mitgliederversammlung), §147 AO (retention)
2026-06-15 08:39:10 +02:00
Patrick Plate 3211ade5be feat(sprint8): Phase 2 — Treasury frontend + PDF receipts
Backend:
- ReceiptPdfService: Generates Quittung PDF per payment (OpenPDF, A4)
- FinancialReportService: Annual financial report PDF (Jahresabschluss)
- FinanceController: Added receipt download, annual report, CSV export endpoints
- Portal receipt download with member ownership verification

Frontend:
- src/services/finance.ts: Complete React Query service (types, hooks, mutations)
- /finance: Dashboard with KPI cards, recent transactions, outstanding members
- /finance/payments: Payment list with filtering, void, receipt download
- /finance/kassenbuch: Kassenbuch ledger with date range, CSV export
- /finance/fee-schedules: Fee schedule CRUD with interval management
- /finance/reports: Annual report PDF download
- /portal/finance: Member self-service balance + payment history + receipts

Navigation & i18n:
- Added Finanzen (Wallet icon) to admin sidebar
- Portal finance page for member payments
- Comprehensive de.json + en.json finance keys (~100 translations)
2026-06-15 08:24:43 +02:00
Patrick Plate 721503b231 feat(sprint8): Phase 1 — Treasury backend (fee schedules, payments, Kassenbuch)
- Extend StaffPermission with MANAGE_FINANCES, VIEW_FINANCES
- Extend AuditEventType with PAYMENT_RECORDED, PAYMENT_VOIDED, FEE_SCHEDULE_CREATED, FEE_SCHEDULE_UPDATED, EXPENSE_RECORDED
- Extend NotificationType with PAYMENT_REMINDER, PAYMENT_OVERDUE, PAYMENT_RECEIVED
- New enums: PaymentMethod, PaymentStatus, TransactionType, FeeInterval, ExpenseCategory
- V18 Flyway migration: fee_schedules, member_fee_assignments, payments, ledger_entries tables
- Entities: FeeSchedule, MemberFeeAssignment, Payment, LedgerEntry
- Repositories with financial queries (balance, outstanding, period sums)
- FinanceService: fee schedule CRUD, record/void payments, expenses, Kassenbuch, summaries
- FinanceController: 14 admin endpoints + 2 portal self-service endpoints
- LedgerEntry is append-only per §147 AO (no update/delete)
- All amounts in cents (Integer) to avoid floating-point precision issues
2026-06-15 08:00:04 +02:00
Patrick Plate cfb38e8fc6 test: authenticated admin E2E suite + accessibility + visual regression baselines
- Global setup: authenticates as admin, saves storageState for reuse
- playwright.config.ts: 3 projects (setup, authenticated, unauthenticated)
- authenticated-admin.spec.ts: 16 admin pages tested with real auth session
- accessibility.spec.ts: axe-core scans on all admin, public, and portal pages
- visual-regression.spec.ts: dark mode baselines for key pages (toHaveScreenshot)
- @axe-core/playwright added as devDependency
- .gitignore updated: excludes .auth/ and test-results/

Full suite: 262 tests passing (setup:1, authenticated:52, unauthenticated:209)
2026-06-13 22:30:29 +02:00
Patrick Plate aabde17532 feat(sprint7): Phase 4 — Integration (SMTP, tier enforcement, WebSocket)
Phase 4 implementation:
- 4.1 IONOS SMTP email configuration (production + docker profiles)
- 4.2 Portal navigation update (info board, events, forum links)
- 4.3 Tier enforcement: PlanTierService (forum=Pro+, info board limits)
- 4.4 WebSocket real-time updates (WebSocketEventPublisher)
- 4.5 EmailService: notification, event reminder, info board templates + rate limiting
- 4.6 Enterprise custom FROM: CustomMailDomain entity, DNS verification, controller

New files:
- PlanTierService: tier checks for forum/info board/enterprise features
- NotificationDispatchService: EMAIL channel dispatch via preferences
- WebSocketEventPublisher: STOMP topic push for forum/info board/events
- CustomMailDomainService: DNS TXT record verification for custom FROM
- MailSettingsController: Enterprise custom domain API endpoints
- CustomMailDomain entity + repository
- V16 migration: email dispatch index
- V17 migration: custom_mail_domains table
- Frontend: use-forum-subscription + use-info-board-subscription hooks
- Portal navbar: added info board, events, forum navigation items
- i18n: added portal nav translations (de + en)

Also fixed pre-existing Phase 2.5/3 compilation issues:
- Member entity: added userId field
- AuditService: added convenience overloads (logEvent, 4-param log)
- AuditEventType: added INFO_BOARD_POST_UPDATED, INFO_BOARD_POST_DELETED
- QuotaViolationCode: added TIER_UPGRADE_REQUIRED
- StaffPermissionChecker: added requirePermission(UserDetails, ...)
- TenantContext: added getCurrentTenantId() alias
- MemberRepository: added findByUserId, findByClubId, findAllByClubId
- EmailServiceTest: updated for new constructor signature
2026-06-13 20:51:10 +02:00
Patrick Plate a539ed9eb2 feat(sprint7): Phase 3 — Forum MVP
- Flyway V15: forum_topics, forum_replies, forum_reactions, forum_reports tables
- Enums: ForumTargetType, ReactionType, ReportStatus
- Extended AuditEventType with FORUM_REPLY_CREATED, FORUM_REPORT_REVIEWED
- Entities: ForumTopic, ForumReply, ForumReaction, ForumReport
- Repositories: ForumTopicRepository, ForumReplyRepository, ForumReactionRepository, ForumReportRepository
- ForumService: full CRUD, moderation (lock/pin/delete), 60-min edit window,
  toggle reactions, content reporting, notifications on new topics/replies
- ForumController: admin + portal endpoints (topics, replies, reactions, reports, moderation)
- Frontend: forum.ts service with React Query hooks (admin + portal)
- Frontend: Admin forum page with topic list, moderation actions (lock/pin/delete)
- Frontend: Portal forum page with topic list, reply thread, reactions, report
- Navigation: added Forum with MessageSquare icon
- i18n: forum.* keys in de.json and en.json
2026-06-13 20:31:17 +02:00
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
Patrick Plate 4aa27cd4f9 feat(sprint7): Phase 2 — Info Board (Schwarzes Brett)
Backend:
- V13 Flyway migration: info_board_posts, post_attachments, post_read_status tables
- InfoBoardPost entity with category enum (EVENT, RULE, GENERAL, MAINTENANCE)
- PostAttachment entity (table created, upload deferred to later)
- PostReadStatus entity with composite key (post_id, member_id)
- InfoBoardPostRepository with paginated queries + unread count
- InfoBoardService: CRUD, pin/archive, mark-as-read, notification dispatch
- InfoBoardController: admin CRUD + portal read/unread endpoints
- Integration with NotificationService and AuditService

Frontend:
- info-board.ts service with React Query hooks for all endpoints
- Admin Info Board page at /info-board with create dialog, filters, pin/archive/delete
- Navigation: added 'Schwarzes Brett' to admin sidebar
- i18n: added infoBoard.* keys to de.json and en.json
- Fixed pre-existing prettier issues in notification-compose.ts
- Fixed BufferSource type issue in push-subscription.ts
2026-06-13 19:41:20 +02:00
Patrick Plate 706a6e257b feat(sprint7): Phase 1 — notifications enhancement + push infrastructure
Phase 1 (Notification Enhancement):
- Extended NotificationType enum (ADMIN_MESSAGE, INFO_BOARD_POST, FORUM_REPLY, FORUM_MENTION)
- Extended StaffPermission enum (SEND_NOTIFICATIONS, MANAGE_INFO_BOARD, MODERATE_FORUM)
- Extended AuditEventType with Sprint 7 events
- Flyway V11: notification_sends + notification_send_recipients tables
- NotificationSend + NotificationSendRecipient entities
- NotificationSendRepository + NotificationSendRecipientRepository
- Extended NotificationService with sendBroadcast() and sendToSelected()
- NotificationComposeController (POST /compose, GET /sends)
- ComposeNotificationRequest DTO

Phase 1B (Push Infrastructure):
- Flyway V12: device_tokens + notification_preferences tables
- DeviceToken entity + DevicePlatform enum
- NotificationPreference entity + NotificationChannel enum
- DeviceTokenRepository + NotificationPreferenceRepository
- DeviceRegistrationService (register/unregister/list devices, max 10 per user)
- NotificationPreferenceService (get/create defaults, update, IN_APP always on)
- NotificationDispatchService (multi-channel fan-out: WebSocket, Web Push, FCM, Email)
- WebPushSender (VAPID-based, simplified for MVP)
- FcmPushSender (graceful degradation if not configured)
- PushPayload DTO
- DeviceRegistrationController (POST/GET/DELETE /devices, GET /vapid-key)
- NotificationPreferenceController (GET/PUT /preferences)
- ConsentType extended (NOTIFICATION_PUSH, NOTIFICATION_EMAIL)
- TargetType enum (ALL, SELECTED)

Frontend:
- Updated sw.js with push event handler + notification click handler
- push-subscription.ts (subscribeToPush, unsubscribe, permission helpers)
- notification-compose.ts service (compose, sends, devices, preferences APIs)
- i18n keys (de.json + en.json) for compose, preferences, push, devices

Configuration:
- application-docker.properties: VAPID + FCM push config properties
- MemberRepository: added findAllActiveUserIds() for broadcast
2026-06-13 19:25:19 +02:00
Patrick Plate 329b7abb18 fix: replace shadboard.svg with Cannabis leaf icon from lucide-react
Uses the same Cannabis icon as the login page for consistent branding.
Removed unused next/image imports.
2026-06-13 17:53:02 +02:00
Patrick Plate 7fe8d4f707 fix: rebrand Shadboard → CannaManage, staff permissions UX
- Sidebar, footer, bottom-bar-header: replaced 'Shadboard' with 'CannaManage'
- Footer: removed 'Designed by Qualiora' attribution
- Staff permissions: single-column layout, alphabetically sorted by label
- Edit permissions dialog: useEffect syncs state when dialog opens
  (fixes pre-fill not working when controlled externally)
2026-06-13 17:45:31 +02:00
Patrick Plate 9aaf771469 fix: consent banner fails open on API error (500/403)
The consent check endpoint (/consent/check) returns 500 via the
proxy when the backend returns 403 (missing JWT forwarding).
Previously this caused the banner to show permanently since
consentCheck was undefined. Now isError = true hides the banner
(fail-open strategy — don't block users when backend is unavailable).
2026-06-13 17:30:19 +02:00
Patrick Plate 27690a836e fix: consent banner dismiss on decline + short viewport layout
Bug 1: Clicking 'Ablehnen' now properly dismisses the dialog by calling
the delete account mutation and signing out (previously it redirected to
/settings/privacy which re-rendered the banner in a loop).

Bug 2: Restructured the dialog layout with flex-col + overflow-y-auto on
the content area only. Header and action buttons are pinned (shrink-0)
so they're always accessible on short viewports. Added max-h constraint
with min() to cap at 600px or 90vh.
2026-06-13 17:11:20 +02:00
Patrick Plate cd77eb6448 fix: correct BCrypt hash in seed SQL and fix Playwright test selectors
Root cause: The BCrypt hash in init.sql was the famous Stack Overflow
hash of 'password' (a0),
not the hash of 'test123' as documented.

Also fixed three test issues in system-test.spec.ts:
1. waitForURL regex /dashboard|\//' matched any URL with '/' (instant resolve)
   → replaced with predicate that waits for URL to not contain /login
2. Reports locator used invalid Playwright selector syntax
   → fixed to use proper :has-text() selector for 'Berichte'
3. Navigation test used 'nav a' but app uses shadcn data-sidebar
   → broadened selector to include [data-sidebar] a
4. Console error filter excluded only favicon/maps/hydration
   → also exclude 'Failed to load resource' and 'MISSING_MESSAGE'
   (pre-existing issues from incomplete API endpoints)
2026-06-13 17:01:56 +02:00
Patrick Plate 52251cf711 fix(api): resolve consent/dsgvo 'User not found' — principal is userId not email
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
ConsentController.resolveUserId() and DsgvoController.resolveUserId() read
auth.getName() as an email and did findByEmailAndTenantId(...), but JwtAuthFilter
sets the Authentication principal to the userId (UUID) — the JWT subject is the
userId, not the email. So auth.getName() returns a UUID string, the email lookup
never matched, and every consent/dsgvo call threw 'User not found' (404/500).

This made the DSGVO consent banner unusable: /consent/check 404'd (banner always
shown) and clicking Accept POSTed /consent which 500'd with no UI feedback — the
button appeared to 'not react'.

Fix: parse auth.getName() as the userId UUID directly and verify existsById.
2026-06-13 10:52:43 +02:00
Patrick Plate 26a77b5e16 docs: record 'Oops' crash fix (intl + PWA middleware) verified via Playwright
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
2026-06-13 10:45:44 +02:00
Patrick Plate 4be9c4cf2c fix(frontend): resolve app-wide 'Oops' crash + PWA middleware interception
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
Root cause (found via Playwright browser probe — curl could not detect client-side
hydration errors):

1. ROOT-LAYOUT INTL CRASH (the 'Oops' on every page incl /login):
   app/layout.tsx renders global client components (PwaInstallPrompt → useTranslations,
   Toaster, Sonner) as siblings of {children} inside <Providers>, but only each
   route-group layout wrapped its own children in NextIntlClientProvider. So those
   global components mounted with NO intl context → 'No intl context found' → React
   hydration crash → global-error 'Oops'. Fix: wrap the root body in
   NextIntlClientProvider via getMessages() (RootLayout now async). Nested providers
   stay valid (next-intl supports nesting).

2. PWA MIDDLEWARE INTERCEPTION (manifest.json syntax error + stale cache):
   middleware matcher did not exclude /manifest.json or /sw.js, so unauthenticated
   browsers got 307→/login (HTML) for both. Browser parsed HTML as JSON
   ('manifest.json:1 Syntax error') and an HTML/old service worker kept serving
   stale bundles ('website hasn't changed' after redeploys). Fix: exclude
   manifest.json, sw.js, icons, offline from the matcher.

3. SERVICE-WORKER STALE CACHE: bump CACHE_NAME v1→v2 so the activate handler purges
   old cached bundles from clients that loaded the broken build.

Also adds scripts/debug/dashboard-probe.mjs — a Playwright probe that logs in and
captures real client-side console/network errors + screenshot.
2026-06-13 10:36:09 +02:00
Patrick Plate 2347a7a1d9 docs: record auth fixes — login verified end-to-end (admin@test.de)
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
2026-06-13 10:14:21 +02:00
Patrick Plate 281adda27c fix(frontend): align NextAuth authorize() with flat backend LoginResponse
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
Login reached the backend (HTTP 200) but NextAuth returned CredentialsSignin.
Cause: authorize() read data.member.id/email/clubName/clubId, but the backend
LoginResponse is flat — { accessToken, refreshToken, expiresIn, role } with no
member object. Accessing data.member.id on undefined threw, so authorize()
returned null.

Decode the JWT payload to recover identity claims (sub=userId, email,
tenant_id=clubId) and use the flat top-level role. Adds a small decodeJwtPayload
helper (claims only, no signature verification needed here).
2026-06-13 10:10:48 +02:00
Patrick Plate dac884c4fe fix(deploy): use valid base64 JWT secret in docker-compose
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
After a successful login the backend returned HTTP 500:
io.jsonwebtoken.io.DecodingException: Illegal base64 character: '-'.
JwtService.getSigningKey() does Decoders.BASE64.decode(secret) before building
the HMAC key (JJWT 0.12 convention). The compose secret was the plaintext
'docker-dev-secret-key-minimum-32-characters-long-for-hmac', which contains
hyphens and is not valid base64, so token signing threw once auth succeeded.

Replace with a proper base64 value (openssl rand -base64 48). The base
application.properties default was already correctly base64-encoded; only the
docker override was wrong.
2026-06-13 10:08:34 +02:00
Patrick Plate 6570ea364a docs: mark CannaManage deploy RESOLVED — live on TrueNAS:3000
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
Records the three real root causes (BASE_URL metadataBase, Spring Boot 4
Flyway starter, mail health indicator) and the 8080->8081 host port remap.
2026-06-13 10:01:39 +02:00
Patrick Plate 60844efaba fix(api): disable mail health indicator in docker profile
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
Backend now starts and Flyway migrations run, but /actuator/health returned
503 (DOWN) so Docker marked the container unhealthy and the frontend refused
to start. Cause: spring-boot-starter-mail registers a mail health indicator
that tries to connect to localhost:1025; there is no SMTP container in this
deployment, so it reports DOWN and drags the aggregate health to DOWN.

Disable the mail health indicator in the docker profile. Mail being down must
not make the whole service unhealthy in an SMTP-less deployment.
2026-06-13 09:57:01 +02:00
Patrick Plate 8490da4705 fix(api): add spring-boot-starter-flyway for Spring Boot 4 migrations
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
Backend crashed on startup with 'Schema validation: missing table
[audit_events]'. Root cause: this is Spring Boot 4.0.6, which modularized
autoconfiguration. FlywayAutoConfiguration moved out of spring-boot-autoconfigure
into a dedicated spring-boot-flyway module that is only pulled in by
spring-boot-starter-flyway. The pom only had flyway-database-postgresql
(+ transitive flyway-core) but NOT the starter, so spring.flyway.enabled=true
was inert: no migrations ran, flyway_schema_history was never created, and
Hibernate ddl-auto=validate failed on the empty schema.

Adds spring-boot-starter-flyway (autoconfigure module + flyway-core); keeps
flyway-database-postgresql for the Postgres dialect.
Ref: https://spring.io/blog/2025/10/28/modularizing-spring-boot/
2026-06-13 09:52:22 +02:00
Patrick Plate f6a7143d1b fix(frontend): guard metadataBase against undefined BASE_URL
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
Root cause of the SSG/page-data build crash: src/app/layout.tsx evaluated
new URL(process.env.BASE_URL) at module load. BASE_URL was never set as a
build-time ENV, so it was undefined -> ERR_INVALID_URL, input: 'undefined'.
Because this is the root layout, its metadata is collected for every route,
explaining why both /impressum (marketing) and /portal-login (non-marketing)
failed identically. The earlier NextAuth/middleware/force-dynamic fixes could
not help because metadata evaluation happens before any of that.

- layout.tsx: fall back to http://localhost:3000 when BASE_URL is unset
- Dockerfile: add BASE_URL build-time placeholder (matches AUTH_URL pattern)
2026-06-13 09:44:21 +02:00
Patrick Plate 1eead286ba docs: add Roo handover doc for TrueNAS Docker deploy session
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
2026-06-13 09:40:02 +02:00
Patrick Plate 9a4df56eaf fix(frontend): exclude marketing routes from NextAuth middleware matcher
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
2026-06-13 09:39:17 +02:00
Patrick Plate b57be8a4d8 fix(frontend): hardcode build-time placeholder ENVs for AUTH_URL/SECRET
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
2026-06-13 09:34:20 +02:00
Patrick Plate 3e4fdee05b fix(frontend): force-dynamic on marketing layout to skip SSG at build time
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
2026-06-13 09:31:24 +02:00
Patrick Plate 805bc4f00d fix(frontend): add AUTH_URL + AUTH_SECRET build ARGs for NextAuth v5
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
2026-06-13 09:30:13 +02:00
Patrick Plate d650987b9a fix(frontend): guard redirect callback against undefined url during SSG
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
2026-06-13 09:28:52 +02:00
Patrick Plate 106229e0e3 fix(frontend): add build-time ARG placeholders for NEXTAUTH_URL/SECRET/BACKEND_URL
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
2026-06-13 09:27:01 +02:00
Patrick Plate d0c53a912c fix(service): DsgvoService getMembershipNumber + remove non-existent setPhone
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
2026-06-13 09:23:33 +02:00
Patrick Plate 61707ffe68 fix(service): add spring-boot-starter-websocket dep for SimpMessagingTemplate
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
2026-06-13 09:21:54 +02:00
Patrick Plate 1e693e3d2a feat(sprint-6): Phase 7 — Launch checklist, pricing page, legal templates
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
- docs/sprint-6/launch-checklist.md: comprehensive pre/post-launch checklist
- /pricing: public pricing page (Starter €19, Pro €49, Enterprise)
- /impressum, /datenschutz, /agb: legal page templates (placeholder text)
- (marketing) route group: public layout without auth
- Footer links to legal pages on login + portal
- i18n for marketing namespace (de + en)
- Fix pre-existing lint errors (unused vars, missing @stomp/stompjs types)
2026-06-12 23:16:47 +02:00
Patrick Plate 599514c0db feat(sprint-6): Phase 6 — Notifications (WebSocket) + PWA
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
- WebSocket: Spring STOMP + SockJS, NotificationService, persistent notifications table
- NotificationController: GET/PUT endpoints for notification management
- Frontend: notification bell with unread badge, dropdown panel, real-time via STOMP
- PWA: manifest.json, service worker (manual sw.js), offline page, install prompt
- PWA icons (192+512), dark theme colors, standalone display
- Full i18n (de/en) for notifications and PWA
- Flyway V10 migration for notifications table
- spring-boot-starter-websocket dependency added
2026-06-12 23:02:44 +02:00
Patrick Plate 076fd6f9b3 feat(sprint-6): Phase 5 — Full grow calendar (sensors, photos, feeding, harvest traceability)
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
- V9 migration: grow_entries, grow_stage_logs, sensor_readings, grow_photos, feeding_logs
- 5 entities + GrowStage enum (7 stages) + SensorReadingType enum
- GrowCalendarService: CRUD + stage advancement + harvest-to-batch linking
- GrowCalendarController: 8 endpoints (/api/v1/grow/*)
- Frontend: /grow list + /grow/[id] detail (timeline, sensor charts, photo gallery, feeding log)
- Sensor chart (Recharts line: temp + humidity over time)
- Harvest completion links grow entry → batch (full traceability)
- React Query hooks for all grow operations
- Full i18n (de/en) with 7 grow stage labels
- Sidebar navigation updated with Anbau/Grow entry
2026-06-12 22:51:45 +02:00
Patrick Plate 05933a08ca feat(sprint-6): Phase 4 — Immutable audit log
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
- V8 migration: audit_events table (JSONB metadata, immutable by design)
- AuditEvent entity + AuditEventType enum (18 event types)
- AuditService: log events, paginated query, PDF export
- AuditController: GET /api/v1/audit (paginated, filtered), GET export
- AuditEventRepository with JPQL filtered queries
- Frontend: /audit-log page (read-only, filterable, timezone-aware)
- PDF export button for Behörde inspections
- Sidebar: 'Protokoll' under new Compliance section
- PdfReportGenerator: generateAuditReport method added
- 10-year retention, REVOKE DELETE documented
- Full i18n (de/en) with 18 event type translations
2026-06-12 22:40:40 +02:00
Patrick Plate 61e481b37b feat(sprint-6): Phase 3 — Stripe integration (SEPA + PayPal + Card)
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled
- V7 migration: subscriptions table with plan tiers
- Subscription entity + PlanTier/SubscriptionStatus enums
- StripeService: customer creation, checkout, portal, webhook handling
- SubscriptionController: /api/v1/billing endpoints
- Webhook handler: invoice.paid, payment_failed, subscription.deleted/updated
- Plan enforcement: member limit interceptor, trial expiry check
- Frontend: /settings/billing page (plan card, usage, upgrade, portal link)
- Trial expired banner on all admin pages
- React Query hooks (useSubscriptionQuery, checkout/portal mutations)
- Stripe Java SDK 28.2.0
- Full i18n (de/en) for billing namespace
2026-06-12 22:31:03 +02:00