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
This commit is contained in:
@@ -273,8 +273,8 @@ public class FinanceController {
|
||||
.orElseThrow(() -> new NoSuchElementException("Club not found"));
|
||||
|
||||
byte[] pdf = receiptPdfService.generateReceipt(payment, member, club);
|
||||
String filename = "Quittung-" + (payment.getReceiptNumber() != null
|
||||
? payment.getReceiptNumber() : id.toString().substring(0, 8)) + ".pdf";
|
||||
String filename = "Quittung-" + (payment.getReference() != null
|
||||
? payment.getReference() : id.toString().substring(0, 8)) + ".pdf";
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
|
||||
@@ -347,8 +347,8 @@ public class FinanceController {
|
||||
.orElseThrow(() -> new NoSuchElementException("Club not found"));
|
||||
|
||||
byte[] pdf = receiptPdfService.generateReceipt(payment, member, club);
|
||||
String filename = "Quittung-" + (payment.getReceiptNumber() != null
|
||||
? payment.getReceiptNumber() : id.toString().substring(0, 8)) + ".pdf";
|
||||
String filename = "Quittung-" + (payment.getReference() != null
|
||||
? payment.getReference() : id.toString().substring(0, 8)) + ".pdf";
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
|
||||
|
||||
@@ -85,4 +85,25 @@ public class StaffPermissionChecker {
|
||||
throw new org.springframework.security.access.AccessDeniedException("Missing permission: " + required);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the user ID from the authenticated principal.
|
||||
*/
|
||||
public UUID getUserId(org.springframework.security.core.userdetails.UserDetails principal) {
|
||||
return UUID.fromString(principal.getUsername());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the club ID (tenant) for the authenticated user.
|
||||
*/
|
||||
public UUID getClubId(org.springframework.security.core.userdetails.UserDetails principal) {
|
||||
return de.cannamanage.domain.entity.TenantContext.getCurrentTenant();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tenant ID for the authenticated user (alias for getClubId).
|
||||
*/
|
||||
public UUID getTenantId(org.springframework.security.core.userdetails.UserDetails principal) {
|
||||
return de.cannamanage.domain.entity.TenantContext.getCurrentTenant();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,5 +38,8 @@ management.endpoint.health.show-details=never
|
||||
# Session configuration (member portal)
|
||||
server.servlet.session.timeout=30m
|
||||
server.servlet.session.cookie.same-site=strict
|
||||
|
||||
# Schedulers
|
||||
cannamanage.schedulers.enabled=${SCHEDULERS_ENABLED:true}
|
||||
server.servlet.session.cookie.http-only=true
|
||||
server.servlet.session.cookie.secure=${SESSION_COOKIE_SECURE:false}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
-- V22: Add protocol_document_id to assemblies for auto-archive feature
|
||||
ALTER TABLE assemblies ADD COLUMN IF NOT EXISTS protocol_document_id UUID;
|
||||
@@ -18,3 +18,6 @@ cannamanage.security.jwt.refresh-token-expiry=2592000
|
||||
# AOP
|
||||
spring.aop.auto=true
|
||||
spring.aop.proxy-target-class=true
|
||||
|
||||
# Disable schedulers in tests
|
||||
cannamanage.schedulers.enabled=false
|
||||
|
||||
Reference in New Issue
Block a user