Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package backend.fullstack.alcohol.api;

import java.time.LocalDateTime;
import java.util.List;

import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import backend.fullstack.alcohol.api.dto.AgeVerificationRequest;
import backend.fullstack.alcohol.api.dto.AgeVerificationResponse;
import backend.fullstack.alcohol.application.AgeVerificationService;
import backend.fullstack.config.ApiResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;

/**
* REST endpoints for age verification logging under IK-Alkohol.
*/
@RestController
@RequestMapping("/api/alcohol/age-verifications")
@Tag(name = "Age Verifications", description = "Log and query age verification checks for alcohol service")
@SecurityRequirement(name = "Bearer Auth")
public class AgeVerificationController {

private final AgeVerificationService verificationService;

public AgeVerificationController(AgeVerificationService verificationService) {
this.verificationService = verificationService;
}

@PostMapping
@PreAuthorize("hasAnyRole('ADMIN','MANAGER','STAFF','SUPERVISOR')")
@Operation(summary = "Log a new age verification check")
@ApiResponses(value = {
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "201", description = "Verification logged"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "403", description = "Forbidden")
})
public ResponseEntity<ApiResponse<AgeVerificationResponse>> create(
@Valid @RequestBody AgeVerificationRequest request
) {
return ResponseEntity.status(201)
.body(ApiResponse.success("Age verification logged", verificationService.create(request)));
}

@GetMapping
@PreAuthorize("hasAnyRole('ADMIN','MANAGER','SUPERVISOR')")
@Operation(summary = "List age verification logs with optional filters")
public ApiResponse<List<AgeVerificationResponse>> list(
@RequestParam(required = false) Long locationId,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime from,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime to
) {
return ApiResponse.success("Verifications fetched", verificationService.list(locationId, from, to));
}

@GetMapping("/{id}")
@PreAuthorize("hasAnyRole('ADMIN','MANAGER','SUPERVISOR')")
@Operation(summary = "Get age verification log by id")
public ApiResponse<AgeVerificationResponse> getById(@PathVariable Long id) {
return ApiResponse.success("Verification fetched", verificationService.getById(id));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package backend.fullstack.alcohol.api;

import java.time.LocalDateTime;
import java.util.List;

import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import backend.fullstack.alcohol.api.dto.AlcoholIncidentRequest;
import backend.fullstack.alcohol.api.dto.AlcoholIncidentResponse;
import backend.fullstack.alcohol.api.dto.ResolveIncidentRequest;
import backend.fullstack.alcohol.application.AlcoholIncidentService;
import backend.fullstack.alcohol.domain.IncidentStatus;
import backend.fullstack.alcohol.domain.IncidentType;
import backend.fullstack.config.ApiResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;

/**
* REST endpoints for managing alcohol-related serving incidents.
*/
@RestController
@RequestMapping("/api/alcohol/incidents")
@Tag(name = "Alcohol Incidents", description = "Report and manage alcohol serving incidents")
@SecurityRequirement(name = "Bearer Auth")
public class AlcoholIncidentController {

private final AlcoholIncidentService incidentService;

public AlcoholIncidentController(AlcoholIncidentService incidentService) {
this.incidentService = incidentService;
}

@PostMapping
@PreAuthorize("hasAnyRole('ADMIN','MANAGER','STAFF','SUPERVISOR')")
@Operation(summary = "Report a new alcohol serving incident")
@ApiResponses(value = {
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "201", description = "Incident reported"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "403", description = "Forbidden")
})
public ResponseEntity<ApiResponse<AlcoholIncidentResponse>> create(
@Valid @RequestBody AlcoholIncidentRequest request
) {
return ResponseEntity.status(201)
.body(ApiResponse.success("Incident reported", incidentService.create(request)));
}

@GetMapping
@PreAuthorize("hasAnyRole('ADMIN','MANAGER','SUPERVISOR')")
@Operation(summary = "List alcohol incidents with optional filters")
public ApiResponse<List<AlcoholIncidentResponse>> list(
@RequestParam(required = false) Long locationId,
@RequestParam(required = false) IncidentStatus status,
@RequestParam(required = false) IncidentType incidentType,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime from,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime to
) {
return ApiResponse.success("Incidents fetched", incidentService.list(locationId, status, incidentType, from, to));
}

@GetMapping("/{id}")
@PreAuthorize("hasAnyRole('ADMIN','MANAGER','SUPERVISOR')")
@Operation(summary = "Get alcohol incident by id")
public ApiResponse<AlcoholIncidentResponse> getById(@PathVariable Long id) {
return ApiResponse.success("Incident fetched", incidentService.getById(id));
}

@PatchMapping("/{id}/resolve")
@PreAuthorize("hasAnyRole('ADMIN','MANAGER','SUPERVISOR')")
@Operation(summary = "Resolve an alcohol incident with corrective action")
public ApiResponse<AlcoholIncidentResponse> resolve(
@PathVariable Long id,
@Valid @RequestBody ResolveIncidentRequest request
) {
return ApiResponse.success("Incident resolved", incidentService.resolve(id, request));
}

@PatchMapping("/{id}/status")
@PreAuthorize("hasAnyRole('ADMIN','SUPERVISOR')")
@Operation(summary = "Update incident status (e.g. close an incident)")
public ApiResponse<AlcoholIncidentResponse> updateStatus(
@PathVariable Long id,
@RequestParam IncidentStatus status
) {
return ApiResponse.success("Incident status updated", incidentService.updateStatus(id, status));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package backend.fullstack.alcohol.api;

import java.util.List;

import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import backend.fullstack.alcohol.api.dto.AlcoholLicenseRequest;
import backend.fullstack.alcohol.api.dto.AlcoholLicenseResponse;
import backend.fullstack.alcohol.application.AlcoholLicenseService;
import backend.fullstack.config.ApiResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;

/**
* REST endpoints for managing alcohol licenses (bevillinger).
*/
@RestController
@RequestMapping("/api/alcohol/licenses")
@Tag(name = "Alcohol Licenses", description = "Manage organization alcohol licenses (bevillinger)")
@SecurityRequirement(name = "Bearer Auth")
public class AlcoholLicenseController {

private final AlcoholLicenseService licenseService;

public AlcoholLicenseController(AlcoholLicenseService licenseService) {
this.licenseService = licenseService;
}

@GetMapping
@PreAuthorize("hasAnyRole('ADMIN','MANAGER','SUPERVISOR')")
@Operation(summary = "List all alcohol licenses for the organization")
public ApiResponse<List<AlcoholLicenseResponse>> list() {
return ApiResponse.success("Licenses fetched", licenseService.listLicenses());
}

@GetMapping("/{id}")
@PreAuthorize("hasAnyRole('ADMIN','MANAGER','SUPERVISOR')")
@Operation(summary = "Get alcohol license by id")
public ApiResponse<AlcoholLicenseResponse> getById(@PathVariable Long id) {
return ApiResponse.success("License fetched", licenseService.getById(id));
}

@PostMapping
@PreAuthorize("hasRole('ADMIN')")
@Operation(summary = "Register a new alcohol license")
@ApiResponses(value = {
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "201", description = "License created"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "403", description = "Forbidden")
})
public ResponseEntity<ApiResponse<AlcoholLicenseResponse>> create(
@Valid @RequestBody AlcoholLicenseRequest request
) {
return ResponseEntity.status(201)
.body(ApiResponse.success("License created", licenseService.create(request)));
}

@PutMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')")
@Operation(summary = "Update an alcohol license")
public ApiResponse<AlcoholLicenseResponse> update(
@PathVariable Long id,
@Valid @RequestBody AlcoholLicenseRequest request
) {
return ApiResponse.success("License updated", licenseService.update(id, request));
}

@DeleteMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')")
@Operation(summary = "Delete an alcohol license")
public ApiResponse<Void> delete(@PathVariable Long id) {
licenseService.delete(id);
return ApiResponse.success("License deleted", null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package backend.fullstack.alcohol.api.dto;

import java.time.LocalDateTime;

import backend.fullstack.alcohol.domain.VerificationMethod;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Schema(description = "Request to log an age verification check")
public class AgeVerificationRequest {

@NotNull(message = "Location id is required")
@Schema(description = "Location where verification was performed", example = "1")
private Long locationId;

@NotNull(message = "Verification method is required")
@Schema(description = "How the age was verified", example = "ID_CHECKED")
private VerificationMethod verificationMethod;

@Schema(description = "Whether the guest appeared to be underage", example = "true")
private boolean guestAppearedUnderage = true;

@Schema(description = "Whether the ID was valid (null if no ID checked)")
private Boolean idWasValid;

@Schema(description = "Whether service was refused", example = "false")
private boolean wasRefused = false;

@Schema(description = "Additional notes about the verification")
private String note;

@Schema(description = "When the verification occurred (defaults to now)")
private LocalDateTime verifiedAt;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package backend.fullstack.alcohol.api.dto;

import java.time.LocalDateTime;

import backend.fullstack.alcohol.domain.VerificationMethod;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "Age verification log response")
public class AgeVerificationResponse {

@Schema(example = "1")
private Long id;

@Schema(example = "100")
private Long organizationId;

@Schema(example = "1")
private Long locationId;

@Schema(example = "Bar")
private String locationName;

@Schema(example = "5")
private Long verifiedByUserId;

@Schema(example = "Ola Nordmann")
private String verifiedByName;

@Schema(example = "ID_CHECKED")
private VerificationMethod verificationMethod;

private boolean guestAppearedUnderage;

private Boolean idWasValid;

private boolean wasRefused;

private String note;

private LocalDateTime verifiedAt;

private LocalDateTime createdAt;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package backend.fullstack.alcohol.api.dto;

import java.time.LocalDateTime;

import backend.fullstack.alcohol.domain.IncidentSeverity;
import backend.fullstack.alcohol.domain.IncidentType;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Schema(description = "Request to report an alcohol serving incident")
public class AlcoholIncidentRequest {

@NotNull(message = "Location id is required")
@Schema(description = "Location where the incident occurred", example = "1")
private Long locationId;

@NotNull(message = "Incident type is required")
@Schema(description = "Type of incident", example = "REFUSED_SERVICE")
private IncidentType incidentType;

@NotNull(message = "Severity is required")
@Schema(description = "Severity of the incident", example = "MEDIUM")
private IncidentSeverity severity;

@NotBlank(message = "Description is required")
@Schema(description = "Description of what happened", example = "Guest appeared intoxicated and was refused further service")
private String description;

@Schema(description = "When the incident occurred (defaults to now)")
private LocalDateTime occurredAt;
}
Loading
Loading