feat(sprint-1): CannaManage foundation — compliance engine, JPA entities, tests TC-001→TC-025
- Maven multi-module project (parent + domain + service + api) - AbstractTenantEntity with Hibernate @Filter for multi-tenancy (explicit getters/setters, Java 25 compatible) - TenantContext ThreadLocal for request-scoped tenant isolation - 8 JPA entities: Club, Member, Strain, Batch, Distribution, MonthlyQuota, StockMovement, User - ComplianceConstants with CanG §19 limits (25g/day adult, 50g/month adult, 30g/month under-21, 10% THC cap) - ComplianceService: checkDistributionAllowed() with fail-fast sequential CanG checks - Unit tests TC-001→TC-025: 25/25 passing, 100% line+branch coverage on ComplianceService (JaCoCo 0.8.13) - Flyway V1__initial_schema.sql: all 8 tables + indexes - docker-compose.yml: PostgreSQL 16 local dev - application-local.properties: local profile configuration Closes #1 #2 #3 #4 #5 #6 #7 #8 #9 #10
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>de.cannamanage</groupId>
|
||||
<artifactId>cannamanage-parent</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>cannamanage-domain</artifactId>
|
||||
<name>CannaManage — Domain (JPA Entities)</name>
|
||||
|
||||
<dependencies>
|
||||
<!-- JPA API only — implementation provided by Spring Boot in runtime -->
|
||||
<dependency>
|
||||
<groupId>jakarta.persistence</groupId>
|
||||
<artifactId>jakarta.persistence-api</artifactId>
|
||||
</dependency>
|
||||
<!-- Hibernate core for @FilterDef, @Filter -->
|
||||
<dependency>
|
||||
<groupId>org.hibernate.orm</groupId>
|
||||
<artifactId>hibernate-core</artifactId>
|
||||
</dependency>
|
||||
<!-- Validation API -->
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
</dependency>
|
||||
<!-- Lombok for boilerplate reduction -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package de.cannamanage.domain.constants;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* CanG (Cannabisgesetz) compliance limits.
|
||||
* All limits are defined as per §19 CanG (Cannabisgesetz) for social cannabis clubs.
|
||||
* These are immutable constants — changes require legal review.
|
||||
*/
|
||||
public final class ComplianceConstants {
|
||||
|
||||
// CanG §19(2) — adult daily distribution limit
|
||||
public static final BigDecimal ADULT_DAILY_LIMIT_GRAMS = new BigDecimal("25.0");
|
||||
|
||||
// CanG §19(2) — adult monthly distribution limit
|
||||
public static final BigDecimal ADULT_MONTHLY_LIMIT_GRAMS = new BigDecimal("50.0");
|
||||
|
||||
// CanG §19(3) — under-21 monthly distribution limit
|
||||
public static final BigDecimal UNDER21_MONTHLY_LIMIT_GRAMS = new BigDecimal("30.0");
|
||||
|
||||
// CanG §19(4) — maximum THC percentage allowed for under-21 members
|
||||
public static final BigDecimal UNDER21_MAX_THC_PERCENTAGE = new BigDecimal("10.0");
|
||||
|
||||
// Minimum age for club membership (§5 CanG)
|
||||
public static final int MINIMUM_MEMBERSHIP_AGE = 18;
|
||||
|
||||
// Age threshold below which stricter limits apply
|
||||
public static final int UNDER21_THRESHOLD_AGE = 21;
|
||||
|
||||
private ComplianceConstants() {
|
||||
// Utility class — do not instantiate
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package de.cannamanage.domain.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import org.hibernate.annotations.Filter;
|
||||
import org.hibernate.annotations.FilterDef;
|
||||
import org.hibernate.annotations.ParamDef;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Base class for all tenant-scoped entities.
|
||||
* Applies a Hibernate @Filter to ensure all queries automatically scope to the current tenant.
|
||||
* The tenantId is set automatically from TenantContext on persist and cannot be updated.
|
||||
*/
|
||||
@MappedSuperclass
|
||||
@FilterDef(
|
||||
name = "tenantFilter",
|
||||
parameters = @ParamDef(name = "tenantId", type = UUID.class)
|
||||
)
|
||||
@Filter(name = "tenantFilter", condition = "tenant_id = :tenantId")
|
||||
public abstract class AbstractTenantEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
@Column(name = "id", nullable = false, updatable = false)
|
||||
private UUID id;
|
||||
|
||||
@Column(name = "tenant_id", nullable = false, updatable = false)
|
||||
private UUID tenantId;
|
||||
|
||||
@Column(name = "created_at", nullable = false, updatable = false)
|
||||
private Instant createdAt;
|
||||
|
||||
@PrePersist
|
||||
void onCreate() {
|
||||
this.tenantId = TenantContext.getCurrentTenant();
|
||||
this.createdAt = Instant.now();
|
||||
}
|
||||
|
||||
public UUID getId() { return id; }
|
||||
public void setId(UUID id) { this.id = id; }
|
||||
|
||||
public UUID getTenantId() { return tenantId; }
|
||||
public void setTenantId(UUID tenantId) { this.tenantId = tenantId; }
|
||||
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package de.cannamanage.domain.entity;
|
||||
|
||||
import de.cannamanage.domain.enums.BatchStatus;
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "batches",
|
||||
uniqueConstraints = {
|
||||
@UniqueConstraint(columnNames = {"batch_code", "tenant_id"})
|
||||
}
|
||||
)
|
||||
public class Batch extends AbstractTenantEntity {
|
||||
|
||||
@Column(name = "strain_id", nullable = false)
|
||||
private UUID strainId;
|
||||
|
||||
@Column(name = "quantity_grams", nullable = false, precision = 10, scale = 2)
|
||||
private BigDecimal quantityGrams;
|
||||
|
||||
@Column(name = "harvest_date")
|
||||
private LocalDate harvestDate;
|
||||
|
||||
@Column(name = "batch_code", nullable = false, length = 100)
|
||||
private String batchCode;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "status", nullable = false, length = 50)
|
||||
private BatchStatus status = BatchStatus.AVAILABLE;
|
||||
|
||||
@Column(name = "contamination_flag", nullable = false)
|
||||
private boolean contaminationFlag = false;
|
||||
|
||||
public UUID getStrainId() { return strainId; }
|
||||
public void setStrainId(UUID strainId) { this.strainId = strainId; }
|
||||
|
||||
public BigDecimal getQuantityGrams() { return quantityGrams; }
|
||||
public void setQuantityGrams(BigDecimal quantityGrams) { this.quantityGrams = quantityGrams; }
|
||||
|
||||
public LocalDate getHarvestDate() { return harvestDate; }
|
||||
public void setHarvestDate(LocalDate harvestDate) { this.harvestDate = harvestDate; }
|
||||
|
||||
public String getBatchCode() { return batchCode; }
|
||||
public void setBatchCode(String batchCode) { this.batchCode = batchCode; }
|
||||
|
||||
public BatchStatus getStatus() { return status; }
|
||||
public void setStatus(BatchStatus status) { this.status = status; }
|
||||
|
||||
public boolean isContaminationFlag() { return contaminationFlag; }
|
||||
public void setContaminationFlag(boolean contaminationFlag) { this.contaminationFlag = contaminationFlag; }
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package de.cannamanage.domain.entity;
|
||||
|
||||
import de.cannamanage.domain.enums.ClubStatus;
|
||||
import jakarta.persistence.*;
|
||||
|
||||
@Entity
|
||||
@Table(name = "clubs")
|
||||
public class Club extends AbstractTenantEntity {
|
||||
|
||||
@Column(name = "name", nullable = false, length = 255)
|
||||
private String name;
|
||||
|
||||
@Column(name = "address")
|
||||
private String address;
|
||||
|
||||
@Column(name = "license_number", nullable = false, unique = true, length = 100)
|
||||
private String licenseNumber;
|
||||
|
||||
@Column(name = "max_members", nullable = false)
|
||||
private Integer maxMembers = 500;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "status", nullable = false, length = 50)
|
||||
private ClubStatus status = ClubStatus.ACTIVE;
|
||||
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
|
||||
public String getAddress() { return address; }
|
||||
public void setAddress(String address) { this.address = address; }
|
||||
|
||||
public String getLicenseNumber() { return licenseNumber; }
|
||||
public void setLicenseNumber(String licenseNumber) { this.licenseNumber = licenseNumber; }
|
||||
|
||||
public Integer getMaxMembers() { return maxMembers; }
|
||||
public void setMaxMembers(Integer maxMembers) { this.maxMembers = maxMembers; }
|
||||
|
||||
public ClubStatus getStatus() { return status; }
|
||||
public void setStatus(ClubStatus status) { this.status = status; }
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package de.cannamanage.domain.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Immutable distribution record — all fields are updatable=false.
|
||||
* Required for CanG §26 record-keeping obligations.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "distributions")
|
||||
public class Distribution extends AbstractTenantEntity {
|
||||
|
||||
@Column(name = "member_id", nullable = false, updatable = false)
|
||||
private UUID memberId;
|
||||
|
||||
@Column(name = "batch_id", nullable = false, updatable = false)
|
||||
private UUID batchId;
|
||||
|
||||
@Column(name = "quantity_grams", nullable = false, updatable = false, precision = 10, scale = 2)
|
||||
private BigDecimal quantityGrams;
|
||||
|
||||
@Column(name = "distributed_at", nullable = false, updatable = false)
|
||||
private Instant distributedAt;
|
||||
|
||||
@Column(name = "recorded_by", nullable = false, updatable = false)
|
||||
private UUID recordedBy;
|
||||
|
||||
@Column(name = "notes", updatable = false)
|
||||
private String notes;
|
||||
|
||||
public UUID getMemberId() { return memberId; }
|
||||
public void setMemberId(UUID memberId) { this.memberId = memberId; }
|
||||
|
||||
public UUID getBatchId() { return batchId; }
|
||||
public void setBatchId(UUID batchId) { this.batchId = batchId; }
|
||||
|
||||
public BigDecimal getQuantityGrams() { return quantityGrams; }
|
||||
public void setQuantityGrams(BigDecimal quantityGrams) { this.quantityGrams = quantityGrams; }
|
||||
|
||||
public Instant getDistributedAt() { return distributedAt; }
|
||||
public void setDistributedAt(Instant distributedAt) { this.distributedAt = distributedAt; }
|
||||
|
||||
public UUID getRecordedBy() { return recordedBy; }
|
||||
public void setRecordedBy(UUID recordedBy) { this.recordedBy = recordedBy; }
|
||||
|
||||
public String getNotes() { return notes; }
|
||||
public void setNotes(String notes) { this.notes = notes; }
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package de.cannamanage.domain.entity;
|
||||
|
||||
import de.cannamanage.domain.enums.MemberStatus;
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "members",
|
||||
uniqueConstraints = {
|
||||
@UniqueConstraint(columnNames = {"email", "tenant_id"}),
|
||||
@UniqueConstraint(columnNames = {"membership_number", "tenant_id"})
|
||||
}
|
||||
)
|
||||
public class Member extends AbstractTenantEntity {
|
||||
|
||||
@Column(name = "club_id", nullable = false)
|
||||
private UUID clubId;
|
||||
|
||||
@Column(name = "first_name", nullable = false, length = 100)
|
||||
private String firstName;
|
||||
|
||||
@Column(name = "last_name", nullable = false, length = 100)
|
||||
private String lastName;
|
||||
|
||||
@Column(name = "email", nullable = false, length = 255)
|
||||
private String email;
|
||||
|
||||
@Column(name = "date_of_birth", nullable = false)
|
||||
private LocalDate dateOfBirth;
|
||||
|
||||
@Column(name = "membership_date", nullable = false)
|
||||
private LocalDate membershipDate;
|
||||
|
||||
@Column(name = "membership_number", nullable = false, length = 50)
|
||||
private String membershipNumber;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "status", nullable = false, length = 50)
|
||||
private MemberStatus status = MemberStatus.ACTIVE;
|
||||
|
||||
@Column(name = "is_under_21", nullable = false)
|
||||
private boolean under21 = false;
|
||||
|
||||
@Column(name = "prevention_officer", nullable = false)
|
||||
private boolean preventionOfficer = false;
|
||||
|
||||
public UUID getClubId() { return clubId; }
|
||||
public void setClubId(UUID clubId) { this.clubId = clubId; }
|
||||
|
||||
public String getFirstName() { return firstName; }
|
||||
public void setFirstName(String firstName) { this.firstName = firstName; }
|
||||
|
||||
public String getLastName() { return lastName; }
|
||||
public void setLastName(String lastName) { this.lastName = lastName; }
|
||||
|
||||
public String getEmail() { return email; }
|
||||
public void setEmail(String email) { this.email = email; }
|
||||
|
||||
public LocalDate getDateOfBirth() { return dateOfBirth; }
|
||||
public void setDateOfBirth(LocalDate dateOfBirth) { this.dateOfBirth = dateOfBirth; }
|
||||
|
||||
public LocalDate getMembershipDate() { return membershipDate; }
|
||||
public void setMembershipDate(LocalDate membershipDate) { this.membershipDate = membershipDate; }
|
||||
|
||||
public String getMembershipNumber() { return membershipNumber; }
|
||||
public void setMembershipNumber(String membershipNumber) { this.membershipNumber = membershipNumber; }
|
||||
|
||||
public MemberStatus getStatus() { return status; }
|
||||
public void setStatus(MemberStatus status) { this.status = status; }
|
||||
|
||||
public boolean isUnder21() { return under21; }
|
||||
public void setUnder21(boolean under21) { this.under21 = under21; }
|
||||
|
||||
public boolean isPreventionOfficer() { return preventionOfficer; }
|
||||
public void setPreventionOfficer(boolean preventionOfficer) { this.preventionOfficer = preventionOfficer; }
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package de.cannamanage.domain.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Tracks monthly distribution totals per member per calendar month.
|
||||
* One row per (member_id, year, month) — unique constraint enforced at DB level.
|
||||
* @Version for optimistic locking — concurrent distribution processing must not corrupt totals.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "monthly_quotas",
|
||||
uniqueConstraints = {
|
||||
@UniqueConstraint(columnNames = {"member_id", "year", "month"})
|
||||
}
|
||||
)
|
||||
public class MonthlyQuota extends AbstractTenantEntity {
|
||||
|
||||
@Column(name = "member_id", nullable = false)
|
||||
private UUID memberId;
|
||||
|
||||
@Column(name = "year", nullable = false)
|
||||
private Integer year;
|
||||
|
||||
@Column(name = "month", nullable = false)
|
||||
private Integer month;
|
||||
|
||||
@Column(name = "total_distributed", nullable = false, precision = 10, scale = 2)
|
||||
private BigDecimal totalDistributed = BigDecimal.ZERO;
|
||||
|
||||
@Column(name = "max_allowed", nullable = false, precision = 10, scale = 2)
|
||||
private BigDecimal maxAllowed;
|
||||
|
||||
@Version
|
||||
@Column(name = "version", nullable = false)
|
||||
private Long version = 0L;
|
||||
|
||||
public UUID getMemberId() { return memberId; }
|
||||
public void setMemberId(UUID memberId) { this.memberId = memberId; }
|
||||
|
||||
public Integer getYear() { return year; }
|
||||
public void setYear(Integer year) { this.year = year; }
|
||||
|
||||
public Integer getMonth() { return month; }
|
||||
public void setMonth(Integer month) { this.month = month; }
|
||||
|
||||
public BigDecimal getTotalDistributed() { return totalDistributed; }
|
||||
public void setTotalDistributed(BigDecimal totalDistributed) { this.totalDistributed = totalDistributed; }
|
||||
|
||||
public BigDecimal getMaxAllowed() { return maxAllowed; }
|
||||
public void setMaxAllowed(BigDecimal maxAllowed) { this.maxAllowed = maxAllowed; }
|
||||
|
||||
public Long getVersion() { return version; }
|
||||
public void setVersion(Long version) { this.version = version; }
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package de.cannamanage.domain.entity;
|
||||
|
||||
import de.cannamanage.domain.enums.StockMovementType;
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "stock_movements")
|
||||
public class StockMovement extends AbstractTenantEntity {
|
||||
|
||||
@Column(name = "batch_id", nullable = false)
|
||||
private UUID batchId;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "movement_type", nullable = false, length = 50)
|
||||
private StockMovementType movementType;
|
||||
|
||||
@Column(name = "quantity_grams", nullable = false, precision = 10, scale = 2)
|
||||
private BigDecimal quantityGrams;
|
||||
|
||||
@Column(name = "reason")
|
||||
private String reason;
|
||||
|
||||
public UUID getBatchId() { return batchId; }
|
||||
public void setBatchId(UUID batchId) { this.batchId = batchId; }
|
||||
|
||||
public StockMovementType getMovementType() { return movementType; }
|
||||
public void setMovementType(StockMovementType movementType) { this.movementType = movementType; }
|
||||
|
||||
public BigDecimal getQuantityGrams() { return quantityGrams; }
|
||||
public void setQuantityGrams(BigDecimal quantityGrams) { this.quantityGrams = quantityGrams; }
|
||||
|
||||
public String getReason() { return reason; }
|
||||
public void setReason(String reason) { this.reason = reason; }
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package de.cannamanage.domain.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Entity
|
||||
@Table(name = "strains")
|
||||
public class Strain extends AbstractTenantEntity {
|
||||
|
||||
@Column(name = "name", nullable = false, length = 255)
|
||||
private String name;
|
||||
|
||||
@Column(name = "thc_percentage", nullable = false, precision = 5, scale = 2)
|
||||
private BigDecimal thcPercentage;
|
||||
|
||||
@Column(name = "cbd_percentage", nullable = false, precision = 5, scale = 2)
|
||||
private BigDecimal cbdPercentage = BigDecimal.ZERO;
|
||||
|
||||
@Column(name = "description")
|
||||
private String description;
|
||||
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
|
||||
public BigDecimal getThcPercentage() { return thcPercentage; }
|
||||
public void setThcPercentage(BigDecimal thcPercentage) { this.thcPercentage = thcPercentage; }
|
||||
|
||||
public BigDecimal getCbdPercentage() { return cbdPercentage; }
|
||||
public void setCbdPercentage(BigDecimal cbdPercentage) { this.cbdPercentage = cbdPercentage; }
|
||||
|
||||
public String getDescription() { return description; }
|
||||
public void setDescription(String description) { this.description = description; }
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package de.cannamanage.domain.entity;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* ThreadLocal holder for the current tenant's UUID.
|
||||
* Must be set at the start of each request (e.g., via a servlet filter or Spring interceptor)
|
||||
* and cleared at the end to prevent tenant leakage.
|
||||
*/
|
||||
public final class TenantContext {
|
||||
|
||||
private static final ThreadLocal<UUID> CURRENT_TENANT = new ThreadLocal<>();
|
||||
|
||||
private TenantContext() {}
|
||||
|
||||
public static UUID getCurrentTenant() {
|
||||
return CURRENT_TENANT.get();
|
||||
}
|
||||
|
||||
public static void setCurrentTenant(UUID tenantId) {
|
||||
CURRENT_TENANT.set(tenantId);
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
CURRENT_TENANT.remove();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package de.cannamanage.domain.entity;
|
||||
|
||||
import de.cannamanage.domain.enums.UserRole;
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "users",
|
||||
uniqueConstraints = {
|
||||
@UniqueConstraint(columnNames = {"email", "tenant_id"})
|
||||
}
|
||||
)
|
||||
public class User extends AbstractTenantEntity {
|
||||
|
||||
@Column(name = "member_id")
|
||||
private UUID memberId;
|
||||
|
||||
@Column(name = "email", nullable = false, length = 255)
|
||||
private String email;
|
||||
|
||||
@Column(name = "password_hash", nullable = false, length = 255)
|
||||
private String passwordHash;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "role", nullable = false, length = 50)
|
||||
private UserRole role = UserRole.ROLE_MEMBER;
|
||||
|
||||
@Column(name = "last_login")
|
||||
private Instant lastLogin;
|
||||
|
||||
@Column(name = "active", nullable = false)
|
||||
private boolean active = true;
|
||||
|
||||
@Column(name = "refresh_token_hash", length = 255)
|
||||
private String refreshTokenHash;
|
||||
|
||||
public UUID getMemberId() { return memberId; }
|
||||
public void setMemberId(UUID memberId) { this.memberId = memberId; }
|
||||
|
||||
public String getEmail() { return email; }
|
||||
public void setEmail(String email) { this.email = email; }
|
||||
|
||||
public String getPasswordHash() { return passwordHash; }
|
||||
public void setPasswordHash(String passwordHash) { this.passwordHash = passwordHash; }
|
||||
|
||||
public UserRole getRole() { return role; }
|
||||
public void setRole(UserRole role) { this.role = role; }
|
||||
|
||||
public Instant getLastLogin() { return lastLogin; }
|
||||
public void setLastLogin(Instant lastLogin) { this.lastLogin = lastLogin; }
|
||||
|
||||
public boolean isActive() { return active; }
|
||||
public void setActive(boolean active) { this.active = active; }
|
||||
|
||||
public String getRefreshTokenHash() { return refreshTokenHash; }
|
||||
public void setRefreshTokenHash(String refreshTokenHash) { this.refreshTokenHash = refreshTokenHash; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package de.cannamanage.domain.enums;
|
||||
|
||||
public enum BatchStatus {
|
||||
AVAILABLE,
|
||||
EXHAUSTED,
|
||||
RECALLED,
|
||||
QUARANTINED
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package de.cannamanage.domain.enums;
|
||||
|
||||
public enum ClubStatus {
|
||||
ACTIVE,
|
||||
SUSPENDED,
|
||||
REVOKED,
|
||||
PENDING
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package de.cannamanage.domain.enums;
|
||||
|
||||
public enum MemberStatus {
|
||||
ACTIVE,
|
||||
SUSPENDED,
|
||||
EXPELLED,
|
||||
PENDING_APPROVAL,
|
||||
RESIGNED
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package de.cannamanage.domain.enums;
|
||||
|
||||
public enum StockMovementType {
|
||||
IN,
|
||||
OUT,
|
||||
RECALL,
|
||||
ADJUSTMENT
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package de.cannamanage.domain.enums;
|
||||
|
||||
public enum UserRole {
|
||||
ROLE_ADMIN,
|
||||
ROLE_MANAGER,
|
||||
ROLE_MEMBER,
|
||||
ROLE_PREVENTION_OFFICER
|
||||
}
|
||||
Reference in New Issue
Block a user