feat(sprint-3): Phase 2 — club settings controller
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
package de.cannamanage.api.controller;
|
||||
|
||||
import de.cannamanage.api.dto.club.ClubResponse;
|
||||
import de.cannamanage.api.dto.club.ClubStatsResponse;
|
||||
import de.cannamanage.api.dto.club.UpdateClubRequest;
|
||||
import de.cannamanage.domain.entity.Club;
|
||||
import de.cannamanage.domain.entity.TenantContext;
|
||||
import de.cannamanage.service.ClubService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/clubs")
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "Club Settings", description = "Club configuration and statistics")
|
||||
public class ClubController {
|
||||
|
||||
private final ClubService clubService;
|
||||
|
||||
@GetMapping("/me")
|
||||
@Operation(summary = "Get current club", description = "Returns the club for the current tenant")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public ResponseEntity<ClubResponse> getMyClub() {
|
||||
UUID tenantId = TenantContext.getCurrentTenant();
|
||||
Club club = clubService.getClubByTenantId(tenantId);
|
||||
return ResponseEntity.ok(toResponse(club));
|
||||
}
|
||||
|
||||
@PutMapping("/me")
|
||||
@Operation(summary = "Update club settings", description = "Updates the club configuration for the current tenant")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public ResponseEntity<ClubResponse> updateMyClub(@Valid @RequestBody UpdateClubRequest request) {
|
||||
UUID tenantId = TenantContext.getCurrentTenant();
|
||||
Club updated = clubService.updateClub(
|
||||
tenantId,
|
||||
request.name(),
|
||||
request.registrationNumber(),
|
||||
request.contactEmail(),
|
||||
request.contactPhone(),
|
||||
request.addressStreet(),
|
||||
request.addressCity(),
|
||||
request.addressPostalCode(),
|
||||
request.addressState(),
|
||||
request.foundedDate(),
|
||||
request.maxPreventionOfficers(),
|
||||
request.allowedEmailPattern()
|
||||
);
|
||||
return ResponseEntity.ok(toResponse(updated));
|
||||
}
|
||||
|
||||
@GetMapping("/me/stats")
|
||||
@Operation(summary = "Get club statistics", description = "Returns aggregated club statistics")
|
||||
@PreAuthorize("hasRole('ADMIN') or @staffPermissions.has(authentication, T(de.cannamanage.domain.enums.StaffPermission).VIEW_COMPLIANCE_REPORT)")
|
||||
public ResponseEntity<ClubStatsResponse> getMyClubStats() {
|
||||
UUID tenantId = TenantContext.getCurrentTenant();
|
||||
ClubService.ClubStats stats = clubService.getClubStats(tenantId);
|
||||
return ResponseEntity.ok(new ClubStatsResponse(
|
||||
stats.totalMembers(),
|
||||
stats.activeMembers(),
|
||||
stats.totalStaff(),
|
||||
stats.activeStaff(),
|
||||
stats.totalDistributionsThisMonth(),
|
||||
stats.totalGramsDistributedThisMonth(),
|
||||
stats.activeBatches(),
|
||||
stats.preventionOfficerCount()
|
||||
));
|
||||
}
|
||||
|
||||
private ClubResponse toResponse(Club club) {
|
||||
return new ClubResponse(
|
||||
club.getId(),
|
||||
club.getName(),
|
||||
club.getRegistrationNumber(),
|
||||
club.getContactEmail(),
|
||||
club.getContactPhone(),
|
||||
club.getAddressStreet(),
|
||||
club.getAddressCity(),
|
||||
club.getAddressPostalCode(),
|
||||
club.getAddressState(),
|
||||
club.getFoundedDate(),
|
||||
club.getMaxPreventionOfficers(),
|
||||
club.getAllowedEmailPattern(),
|
||||
club.getStatus(),
|
||||
club.getCreatedAt()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package de.cannamanage.api.dto.club;
|
||||
|
||||
import de.cannamanage.domain.enums.ClubStatus;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.util.UUID;
|
||||
|
||||
public record ClubResponse(
|
||||
UUID id,
|
||||
String name,
|
||||
String registrationNumber,
|
||||
String contactEmail,
|
||||
String contactPhone,
|
||||
String addressStreet,
|
||||
String addressCity,
|
||||
String addressPostalCode,
|
||||
String addressState,
|
||||
LocalDate foundedDate,
|
||||
Integer maxPreventionOfficers,
|
||||
String allowedEmailPattern,
|
||||
ClubStatus status,
|
||||
Instant createdAt
|
||||
) {}
|
||||
@@ -0,0 +1,14 @@
|
||||
package de.cannamanage.api.dto.club;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public record ClubStatsResponse(
|
||||
long totalMembers,
|
||||
long activeMembers,
|
||||
long totalStaff,
|
||||
long activeStaff,
|
||||
long totalDistributionsThisMonth,
|
||||
BigDecimal totalGramsDistributedThisMonth,
|
||||
long activeBatches,
|
||||
long preventionOfficerCount
|
||||
) {}
|
||||
@@ -0,0 +1,34 @@
|
||||
package de.cannamanage.api.dto.club;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
public record UpdateClubRequest(
|
||||
@NotBlank(message = "Club name is required")
|
||||
String name,
|
||||
|
||||
String registrationNumber,
|
||||
|
||||
@Email(message = "Must be a valid email address")
|
||||
String contactEmail,
|
||||
|
||||
String contactPhone,
|
||||
|
||||
String addressStreet,
|
||||
|
||||
String addressCity,
|
||||
|
||||
String addressPostalCode,
|
||||
|
||||
String addressState,
|
||||
|
||||
LocalDate foundedDate,
|
||||
|
||||
@Min(value = 1, message = "Must have at least 1 prevention officer slot")
|
||||
Integer maxPreventionOfficers,
|
||||
|
||||
String allowedEmailPattern
|
||||
) {}
|
||||
@@ -0,0 +1,12 @@
|
||||
-- Sprint 3 Phase 2: Club settings extended columns
|
||||
-- Additional address fields, contact info, and allowed email pattern for clubs
|
||||
|
||||
ALTER TABLE clubs ADD COLUMN registration_number VARCHAR(100);
|
||||
ALTER TABLE clubs ADD COLUMN contact_email VARCHAR(255);
|
||||
ALTER TABLE clubs ADD COLUMN contact_phone VARCHAR(50);
|
||||
ALTER TABLE clubs ADD COLUMN address_street VARCHAR(255);
|
||||
ALTER TABLE clubs ADD COLUMN address_city VARCHAR(100);
|
||||
ALTER TABLE clubs ADD COLUMN address_postal_code VARCHAR(20);
|
||||
ALTER TABLE clubs ADD COLUMN address_state VARCHAR(100);
|
||||
ALTER TABLE clubs ADD COLUMN founded_date DATE;
|
||||
ALTER TABLE clubs ADD COLUMN allowed_email_pattern VARCHAR(255);
|
||||
@@ -0,0 +1,113 @@
|
||||
package de.cannamanage.api.controller;
|
||||
|
||||
import de.cannamanage.api.dto.club.UpdateClubRequest;
|
||||
import de.cannamanage.domain.entity.Club;
|
||||
import de.cannamanage.domain.entity.TenantContext;
|
||||
import de.cannamanage.domain.enums.ClubStatus;
|
||||
import de.cannamanage.service.ClubService;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class ClubControllerTest {
|
||||
|
||||
@Mock
|
||||
private ClubService clubService;
|
||||
|
||||
@InjectMocks
|
||||
private ClubController clubController;
|
||||
|
||||
private UUID tenantId;
|
||||
private Club club;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
tenantId = UUID.randomUUID();
|
||||
TenantContext.setCurrentTenant(tenantId);
|
||||
|
||||
club = new Club();
|
||||
club.setId(UUID.randomUUID());
|
||||
club.setTenantId(tenantId);
|
||||
club.setName("Green Garden Club");
|
||||
club.setRegistrationNumber("REG-2024-001");
|
||||
club.setContactEmail("info@greengardenclub.de");
|
||||
club.setContactPhone("+49 30 12345678");
|
||||
club.setAddressStreet("Hanfweg 42");
|
||||
club.setAddressCity("Berlin");
|
||||
club.setAddressPostalCode("10115");
|
||||
club.setAddressState("Berlin");
|
||||
club.setFoundedDate(LocalDate.of(2024, 7, 1));
|
||||
club.setMaxPreventionOfficers(2);
|
||||
club.setAllowedEmailPattern(".*@greengardenclub\\.de");
|
||||
club.setStatus(ClubStatus.ACTIVE);
|
||||
club.setCreatedAt(Instant.now());
|
||||
club.setLicenseNumber("LIC-001");
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
TenantContext.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMyClub_returnsClubResponse() {
|
||||
when(clubService.getClubByTenantId(tenantId)).thenReturn(club);
|
||||
|
||||
ResponseEntity<?> response = clubController.getMyClub();
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(response.getBody()).isNotNull();
|
||||
verify(clubService).getClubByTenantId(tenantId);
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateMyClub_updatesAndReturns() {
|
||||
UpdateClubRequest request = new UpdateClubRequest(
|
||||
"Updated Club", "REG-NEW", "new@club.de", "+49111",
|
||||
"Newstreet 1", "Hamburg", "20095", "Hamburg",
|
||||
LocalDate.of(2024, 1, 1), 3, ".*@club\\.de"
|
||||
);
|
||||
when(clubService.updateClub(
|
||||
eq(tenantId), eq("Updated Club"), eq("REG-NEW"),
|
||||
eq("new@club.de"), eq("+49111"),
|
||||
eq("Newstreet 1"), eq("Hamburg"), eq("20095"), eq("Hamburg"),
|
||||
eq(LocalDate.of(2024, 1, 1)), eq(3), eq(".*@club\\.de")
|
||||
)).thenReturn(club);
|
||||
|
||||
ResponseEntity<?> response = clubController.updateMyClub(request);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(response.getBody()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMyClubStats_returnsStats() {
|
||||
ClubService.ClubStats stats = new ClubService.ClubStats(
|
||||
50, 42, 5, 4, 120, new BigDecimal("1500.50"), 8, 2
|
||||
);
|
||||
when(clubService.getClubStats(tenantId)).thenReturn(stats);
|
||||
|
||||
ResponseEntity<?> response = clubController.getMyClubStats();
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(response.getBody()).isNotNull();
|
||||
verify(clubService).getClubStats(tenantId);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user