mirror of
https://github.com/s-frick/effigenix.git
synced 2026-03-28 08:29:36 +01:00
refactor: OffsetDateTime-Migration, atomare Batch-Sequenznummern und Quantity.reconstitute-Overload
- LocalDateTime → OffsetDateTime (UTC) in allen Domain-Klassen, JPA Entities, DTOs und Tests - Liquibase-Migration 017: TIMESTAMP → TIMESTAMP WITH TIME ZONE für bestehende Spalten - Custom DateTimeProvider für Spring Data @CreatedDate-Kompatibilität mit OffsetDateTime - Neue Sequenztabelle (016) mit JPA Entity + PESSIMISTIC_WRITE Lock statt COUNT-basierter Batch-Nummernvergabe (Race Condition Fix) - Quantity.reconstitute(amount, uom) 2-Parameter-Overload für bessere Lesbarkeit
This commit is contained in:
parent
b06157b92c
commit
b46495e1aa
64 changed files with 414 additions and 256 deletions
|
|
@ -7,7 +7,8 @@ import de.effigenix.shared.common.Result;
|
||||||
import de.effigenix.shared.security.ActorId;
|
import de.effigenix.shared.security.ActorId;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -71,7 +72,7 @@ public class AuthenticateUser {
|
||||||
SessionToken token = sessionManager.createSession(user);
|
SessionToken token = sessionManager.createSession(user);
|
||||||
|
|
||||||
// 5. Update last login timestamp (immutable)
|
// 5. Update last login timestamp (immutable)
|
||||||
return user.withLastLogin(LocalDateTime.now())
|
return user.withLastLogin(OffsetDateTime.now(ZoneOffset.UTC))
|
||||||
.flatMap(updated -> userRepository.save(updated)
|
.flatMap(updated -> userRepository.save(updated)
|
||||||
.mapError(err -> (UserError) new UserError.RepositoryFailure(err.message()))
|
.mapError(err -> (UserError) new UserError.RepositoryFailure(err.message()))
|
||||||
.map(ignored -> {
|
.map(ignored -> {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package de.effigenix.application.usermanagement.dto;
|
package de.effigenix.application.usermanagement.dto;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JWT session token returned after successful authentication.
|
* JWT session token returned after successful authentication.
|
||||||
|
|
@ -9,7 +10,7 @@ public record SessionToken(
|
||||||
String accessToken,
|
String accessToken,
|
||||||
String tokenType,
|
String tokenType,
|
||||||
long expiresIn, // in seconds
|
long expiresIn, // in seconds
|
||||||
LocalDateTime expiresAt,
|
OffsetDateTime expiresAt,
|
||||||
String refreshToken // for future refresh token support
|
String refreshToken // for future refresh token support
|
||||||
) {
|
) {
|
||||||
public static SessionToken create(String accessToken, long expiresInMs, String refreshToken) {
|
public static SessionToken create(String accessToken, long expiresInMs, String refreshToken) {
|
||||||
|
|
@ -17,7 +18,7 @@ public record SessionToken(
|
||||||
accessToken,
|
accessToken,
|
||||||
"Bearer",
|
"Bearer",
|
||||||
expiresInMs / 1000, // convert to seconds
|
expiresInMs / 1000, // convert to seconds
|
||||||
LocalDateTime.now().plusSeconds(expiresInMs / 1000),
|
OffsetDateTime.now(ZoneOffset.UTC).plusSeconds(expiresInMs / 1000),
|
||||||
refreshToken
|
refreshToken
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package de.effigenix.application.usermanagement.dto;
|
||||||
import de.effigenix.domain.usermanagement.User;
|
import de.effigenix.domain.usermanagement.User;
|
||||||
import de.effigenix.domain.usermanagement.UserStatus;
|
import de.effigenix.domain.usermanagement.UserStatus;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
@ -18,8 +18,8 @@ public record UserDTO(
|
||||||
Set<RoleDTO> roles,
|
Set<RoleDTO> roles,
|
||||||
String branchId,
|
String branchId,
|
||||||
UserStatus status,
|
UserStatus status,
|
||||||
LocalDateTime createdAt,
|
OffsetDateTime createdAt,
|
||||||
LocalDateTime lastLogin
|
OffsetDateTime lastLogin
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* Maps a User entity to a UserDTO.
|
* Maps a User entity to a UserDTO.
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ public record MinimumLevel(Quantity quantity) {
|
||||||
// MinimumLevel erlaubt amount == 0 (kein Mindestbestand)
|
// MinimumLevel erlaubt amount == 0 (kein Mindestbestand)
|
||||||
// Quantity.of() verlangt amount > 0, daher Reconstitute verwenden
|
// Quantity.of() verlangt amount > 0, daher Reconstitute verwenden
|
||||||
if (parsedAmount.compareTo(BigDecimal.ZERO) == 0) {
|
if (parsedAmount.compareTo(BigDecimal.ZERO) == 0) {
|
||||||
return Result.success(new MinimumLevel(Quantity.reconstitute(parsedAmount, parsedUnit, null, null)));
|
return Result.success(new MinimumLevel(Quantity.reconstitute(parsedAmount, parsedUnit)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Quantity.of(parsedAmount, parsedUnit)
|
return Quantity.of(parsedAmount, parsedUnit)
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@ public class StockBatch {
|
||||||
return Result.failure(new StockError.NegativeStockNotAllowed());
|
return Result.failure(new StockError.NegativeStockNotAllowed());
|
||||||
}
|
}
|
||||||
if (remaining.compareTo(BigDecimal.ZERO) == 0) {
|
if (remaining.compareTo(BigDecimal.ZERO) == 0) {
|
||||||
return Result.success(Quantity.reconstitute(BigDecimal.ZERO, this.quantity.uom(), null, null));
|
return Result.success(Quantity.reconstitute(BigDecimal.ZERO, this.quantity.uom()));
|
||||||
}
|
}
|
||||||
switch (Quantity.of(remaining, this.quantity.uom())) {
|
switch (Quantity.of(remaining, this.quantity.uom())) {
|
||||||
case Result.Failure(var err) -> {
|
case Result.Failure(var err) -> {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@ import de.effigenix.shared.common.Result;
|
||||||
|
|
||||||
import static de.effigenix.shared.common.Result.*;
|
import static de.effigenix.shared.common.Result.*;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -26,8 +27,8 @@ public class Article {
|
||||||
private final List<SalesUnit> salesUnits;
|
private final List<SalesUnit> salesUnits;
|
||||||
private ArticleStatus status;
|
private ArticleStatus status;
|
||||||
private final Set<SupplierId> supplierReferences;
|
private final Set<SupplierId> supplierReferences;
|
||||||
private final LocalDateTime createdAt;
|
private final OffsetDateTime createdAt;
|
||||||
private LocalDateTime updatedAt;
|
private OffsetDateTime updatedAt;
|
||||||
|
|
||||||
private Article(
|
private Article(
|
||||||
ArticleId id,
|
ArticleId id,
|
||||||
|
|
@ -37,8 +38,8 @@ public class Article {
|
||||||
List<SalesUnit> salesUnits,
|
List<SalesUnit> salesUnits,
|
||||||
ArticleStatus status,
|
ArticleStatus status,
|
||||||
Set<SupplierId> supplierReferences,
|
Set<SupplierId> supplierReferences,
|
||||||
LocalDateTime createdAt,
|
OffsetDateTime createdAt,
|
||||||
LocalDateTime updatedAt
|
OffsetDateTime updatedAt
|
||||||
) {
|
) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|
@ -88,7 +89,7 @@ public class Article {
|
||||||
case Success(var val) -> salesUnit = val;
|
case Success(var val) -> salesUnit = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
var now = LocalDateTime.now();
|
var now = OffsetDateTime.now(ZoneOffset.UTC);
|
||||||
return Result.success(new Article(
|
return Result.success(new Article(
|
||||||
ArticleId.generate(),
|
ArticleId.generate(),
|
||||||
name,
|
name,
|
||||||
|
|
@ -110,8 +111,8 @@ public class Article {
|
||||||
List<SalesUnit> salesUnits,
|
List<SalesUnit> salesUnits,
|
||||||
ArticleStatus status,
|
ArticleStatus status,
|
||||||
Set<SupplierId> supplierReferences,
|
Set<SupplierId> supplierReferences,
|
||||||
LocalDateTime createdAt,
|
OffsetDateTime createdAt,
|
||||||
LocalDateTime updatedAt
|
OffsetDateTime updatedAt
|
||||||
) {
|
) {
|
||||||
return new Article(id, name, articleNumber, categoryId, salesUnits, status,
|
return new Article(id, name, articleNumber, categoryId, salesUnits, status,
|
||||||
supplierReferences, createdAt, updatedAt);
|
supplierReferences, createdAt, updatedAt);
|
||||||
|
|
@ -208,7 +209,7 @@ public class Article {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void touch() {
|
private void touch() {
|
||||||
this.updatedAt = LocalDateTime.now();
|
this.updatedAt = OffsetDateTime.now(ZoneOffset.UTC);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== Getters ====================
|
// ==================== Getters ====================
|
||||||
|
|
@ -220,8 +221,8 @@ public class Article {
|
||||||
public List<SalesUnit> salesUnits() { return Collections.unmodifiableList(salesUnits); }
|
public List<SalesUnit> salesUnits() { return Collections.unmodifiableList(salesUnits); }
|
||||||
public ArticleStatus status() { return status; }
|
public ArticleStatus status() { return status; }
|
||||||
public Set<SupplierId> supplierReferences() { return Collections.unmodifiableSet(supplierReferences); }
|
public Set<SupplierId> supplierReferences() { return Collections.unmodifiableSet(supplierReferences); }
|
||||||
public LocalDateTime createdAt() { return createdAt; }
|
public OffsetDateTime createdAt() { return createdAt; }
|
||||||
public LocalDateTime updatedAt() { return updatedAt; }
|
public OffsetDateTime updatedAt() { return updatedAt; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ import de.effigenix.shared.common.Result;
|
||||||
|
|
||||||
import static de.effigenix.shared.common.Result.*;
|
import static de.effigenix.shared.common.Result.*;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -33,8 +34,8 @@ public class Customer {
|
||||||
private FrameContract frameContract;
|
private FrameContract frameContract;
|
||||||
private final Set<CustomerPreference> preferences;
|
private final Set<CustomerPreference> preferences;
|
||||||
private CustomerStatus status;
|
private CustomerStatus status;
|
||||||
private final LocalDateTime createdAt;
|
private final OffsetDateTime createdAt;
|
||||||
private LocalDateTime updatedAt;
|
private OffsetDateTime updatedAt;
|
||||||
|
|
||||||
private Customer(
|
private Customer(
|
||||||
CustomerId id,
|
CustomerId id,
|
||||||
|
|
@ -47,8 +48,8 @@ public class Customer {
|
||||||
FrameContract frameContract,
|
FrameContract frameContract,
|
||||||
Set<CustomerPreference> preferences,
|
Set<CustomerPreference> preferences,
|
||||||
CustomerStatus status,
|
CustomerStatus status,
|
||||||
LocalDateTime createdAt,
|
OffsetDateTime createdAt,
|
||||||
LocalDateTime updatedAt
|
OffsetDateTime updatedAt
|
||||||
) {
|
) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|
@ -100,7 +101,7 @@ public class Customer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var now = LocalDateTime.now();
|
var now = OffsetDateTime.now(ZoneOffset.UTC);
|
||||||
return Result.success(new Customer(
|
return Result.success(new Customer(
|
||||||
CustomerId.generate(), name, draft.type(), billingAddress, contactInfo, paymentTerms,
|
CustomerId.generate(), name, draft.type(), billingAddress, contactInfo, paymentTerms,
|
||||||
List.of(), null, Set.of(), CustomerStatus.ACTIVE, now, now
|
List.of(), null, Set.of(), CustomerStatus.ACTIVE, now, now
|
||||||
|
|
@ -118,8 +119,8 @@ public class Customer {
|
||||||
FrameContract frameContract,
|
FrameContract frameContract,
|
||||||
Set<CustomerPreference> preferences,
|
Set<CustomerPreference> preferences,
|
||||||
CustomerStatus status,
|
CustomerStatus status,
|
||||||
LocalDateTime createdAt,
|
OffsetDateTime createdAt,
|
||||||
LocalDateTime updatedAt
|
OffsetDateTime updatedAt
|
||||||
) {
|
) {
|
||||||
return new Customer(id, name, type, billingAddress, contactInfo, paymentTerms,
|
return new Customer(id, name, type, billingAddress, contactInfo, paymentTerms,
|
||||||
deliveryAddresses, frameContract, preferences, status, createdAt, updatedAt);
|
deliveryAddresses, frameContract, preferences, status, createdAt, updatedAt);
|
||||||
|
|
@ -235,7 +236,7 @@ public class Customer {
|
||||||
// ==================== Helpers ====================
|
// ==================== Helpers ====================
|
||||||
|
|
||||||
private void touch() {
|
private void touch() {
|
||||||
this.updatedAt = LocalDateTime.now();
|
this.updatedAt = OffsetDateTime.now(ZoneOffset.UTC);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== Getters ====================
|
// ==================== Getters ====================
|
||||||
|
|
@ -250,8 +251,8 @@ public class Customer {
|
||||||
public FrameContract frameContract() { return frameContract; }
|
public FrameContract frameContract() { return frameContract; }
|
||||||
public Set<CustomerPreference> preferences() { return Collections.unmodifiableSet(preferences); }
|
public Set<CustomerPreference> preferences() { return Collections.unmodifiableSet(preferences); }
|
||||||
public CustomerStatus status() { return status; }
|
public CustomerStatus status() { return status; }
|
||||||
public LocalDateTime createdAt() { return createdAt; }
|
public OffsetDateTime createdAt() { return createdAt; }
|
||||||
public LocalDateTime updatedAt() { return updatedAt; }
|
public OffsetDateTime updatedAt() { return updatedAt; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@ import de.effigenix.shared.common.Address;
|
||||||
import de.effigenix.shared.common.ContactInfo;
|
import de.effigenix.shared.common.ContactInfo;
|
||||||
import de.effigenix.shared.common.PaymentTerms;
|
import de.effigenix.shared.common.PaymentTerms;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -31,8 +32,8 @@ public class Supplier {
|
||||||
private final List<QualityCertificate> certificates;
|
private final List<QualityCertificate> certificates;
|
||||||
private SupplierRating rating;
|
private SupplierRating rating;
|
||||||
private SupplierStatus status;
|
private SupplierStatus status;
|
||||||
private final LocalDateTime createdAt;
|
private final OffsetDateTime createdAt;
|
||||||
private LocalDateTime updatedAt;
|
private OffsetDateTime updatedAt;
|
||||||
|
|
||||||
private Supplier(
|
private Supplier(
|
||||||
SupplierId id,
|
SupplierId id,
|
||||||
|
|
@ -43,8 +44,8 @@ public class Supplier {
|
||||||
List<QualityCertificate> certificates,
|
List<QualityCertificate> certificates,
|
||||||
SupplierRating rating,
|
SupplierRating rating,
|
||||||
SupplierStatus status,
|
SupplierStatus status,
|
||||||
LocalDateTime createdAt,
|
OffsetDateTime createdAt,
|
||||||
LocalDateTime updatedAt
|
OffsetDateTime updatedAt
|
||||||
) {
|
) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|
@ -96,7 +97,7 @@ public class Supplier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var now = LocalDateTime.now();
|
var now = OffsetDateTime.now(ZoneOffset.UTC);
|
||||||
return Result.success(new Supplier(
|
return Result.success(new Supplier(
|
||||||
SupplierId.generate(), name, address, contactInfo, paymentTerms,
|
SupplierId.generate(), name, address, contactInfo, paymentTerms,
|
||||||
List.of(), null, SupplierStatus.ACTIVE, now, now
|
List.of(), null, SupplierStatus.ACTIVE, now, now
|
||||||
|
|
@ -112,8 +113,8 @@ public class Supplier {
|
||||||
List<QualityCertificate> certificates,
|
List<QualityCertificate> certificates,
|
||||||
SupplierRating rating,
|
SupplierRating rating,
|
||||||
SupplierStatus status,
|
SupplierStatus status,
|
||||||
LocalDateTime createdAt,
|
OffsetDateTime createdAt,
|
||||||
LocalDateTime updatedAt
|
OffsetDateTime updatedAt
|
||||||
) {
|
) {
|
||||||
return new Supplier(id, name, address, contactInfo, paymentTerms,
|
return new Supplier(id, name, address, contactInfo, paymentTerms,
|
||||||
certificates, rating, status, createdAt, updatedAt);
|
certificates, rating, status, createdAt, updatedAt);
|
||||||
|
|
@ -200,7 +201,7 @@ public class Supplier {
|
||||||
// ==================== Helpers ====================
|
// ==================== Helpers ====================
|
||||||
|
|
||||||
private void touch() {
|
private void touch() {
|
||||||
this.updatedAt = LocalDateTime.now();
|
this.updatedAt = OffsetDateTime.now(ZoneOffset.UTC);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== Getters ====================
|
// ==================== Getters ====================
|
||||||
|
|
@ -213,8 +214,8 @@ public class Supplier {
|
||||||
public List<QualityCertificate> certificates() { return Collections.unmodifiableList(certificates); }
|
public List<QualityCertificate> certificates() { return Collections.unmodifiableList(certificates); }
|
||||||
public SupplierRating rating() { return rating; }
|
public SupplierRating rating() { return rating; }
|
||||||
public SupplierStatus status() { return status; }
|
public SupplierStatus status() { return status; }
|
||||||
public LocalDateTime createdAt() { return createdAt; }
|
public OffsetDateTime createdAt() { return createdAt; }
|
||||||
public LocalDateTime updatedAt() { return updatedAt; }
|
public OffsetDateTime updatedAt() { return updatedAt; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@ import de.effigenix.shared.common.UnitOfMeasure;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Batch aggregate root.
|
* Batch aggregate root.
|
||||||
|
|
@ -27,8 +28,8 @@ public class Batch {
|
||||||
private final Quantity plannedQuantity;
|
private final Quantity plannedQuantity;
|
||||||
private final LocalDate productionDate;
|
private final LocalDate productionDate;
|
||||||
private final LocalDate bestBeforeDate;
|
private final LocalDate bestBeforeDate;
|
||||||
private final LocalDateTime createdAt;
|
private final OffsetDateTime createdAt;
|
||||||
private LocalDateTime updatedAt;
|
private OffsetDateTime updatedAt;
|
||||||
|
|
||||||
private Batch(
|
private Batch(
|
||||||
BatchId id,
|
BatchId id,
|
||||||
|
|
@ -38,8 +39,8 @@ public class Batch {
|
||||||
Quantity plannedQuantity,
|
Quantity plannedQuantity,
|
||||||
LocalDate productionDate,
|
LocalDate productionDate,
|
||||||
LocalDate bestBeforeDate,
|
LocalDate bestBeforeDate,
|
||||||
LocalDateTime createdAt,
|
OffsetDateTime createdAt,
|
||||||
LocalDateTime updatedAt
|
OffsetDateTime updatedAt
|
||||||
) {
|
) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.batchNumber = batchNumber;
|
this.batchNumber = batchNumber;
|
||||||
|
|
@ -88,7 +89,7 @@ public class Batch {
|
||||||
"Invalid unit: " + draft.plannedQuantityUnit()));
|
"Invalid unit: " + draft.plannedQuantityUnit()));
|
||||||
}
|
}
|
||||||
|
|
||||||
var now = LocalDateTime.now();
|
var now = OffsetDateTime.now(ZoneOffset.UTC);
|
||||||
return Result.success(new Batch(
|
return Result.success(new Batch(
|
||||||
BatchId.generate(),
|
BatchId.generate(),
|
||||||
batchNumber,
|
batchNumber,
|
||||||
|
|
@ -110,8 +111,8 @@ public class Batch {
|
||||||
Quantity plannedQuantity,
|
Quantity plannedQuantity,
|
||||||
LocalDate productionDate,
|
LocalDate productionDate,
|
||||||
LocalDate bestBeforeDate,
|
LocalDate bestBeforeDate,
|
||||||
LocalDateTime createdAt,
|
OffsetDateTime createdAt,
|
||||||
LocalDateTime updatedAt
|
OffsetDateTime updatedAt
|
||||||
) {
|
) {
|
||||||
return new Batch(id, batchNumber, recipeId, status, plannedQuantity, productionDate, bestBeforeDate, createdAt, updatedAt);
|
return new Batch(id, batchNumber, recipeId, status, plannedQuantity, productionDate, bestBeforeDate, createdAt, updatedAt);
|
||||||
}
|
}
|
||||||
|
|
@ -123,6 +124,6 @@ public class Batch {
|
||||||
public Quantity plannedQuantity() { return plannedQuantity; }
|
public Quantity plannedQuantity() { return plannedQuantity; }
|
||||||
public LocalDate productionDate() { return productionDate; }
|
public LocalDate productionDate() { return productionDate; }
|
||||||
public LocalDate bestBeforeDate() { return bestBeforeDate; }
|
public LocalDate bestBeforeDate() { return bestBeforeDate; }
|
||||||
public LocalDateTime createdAt() { return createdAt; }
|
public OffsetDateTime createdAt() { return createdAt; }
|
||||||
public LocalDateTime updatedAt() { return updatedAt; }
|
public OffsetDateTime updatedAt() { return updatedAt; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@ import de.effigenix.shared.common.Result;
|
||||||
import de.effigenix.shared.common.UnitOfMeasure;
|
import de.effigenix.shared.common.UnitOfMeasure;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -44,8 +45,8 @@ public class Recipe {
|
||||||
private RecipeStatus status;
|
private RecipeStatus status;
|
||||||
private final List<Ingredient> ingredients;
|
private final List<Ingredient> ingredients;
|
||||||
private final List<ProductionStep> productionSteps;
|
private final List<ProductionStep> productionSteps;
|
||||||
private final LocalDateTime createdAt;
|
private final OffsetDateTime createdAt;
|
||||||
private LocalDateTime updatedAt;
|
private OffsetDateTime updatedAt;
|
||||||
|
|
||||||
private Recipe(
|
private Recipe(
|
||||||
RecipeId id,
|
RecipeId id,
|
||||||
|
|
@ -59,8 +60,8 @@ public class Recipe {
|
||||||
RecipeStatus status,
|
RecipeStatus status,
|
||||||
List<Ingredient> ingredients,
|
List<Ingredient> ingredients,
|
||||||
List<ProductionStep> productionSteps,
|
List<ProductionStep> productionSteps,
|
||||||
LocalDateTime createdAt,
|
OffsetDateTime createdAt,
|
||||||
LocalDateTime updatedAt
|
OffsetDateTime updatedAt
|
||||||
) {
|
) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|
@ -123,7 +124,7 @@ public class Recipe {
|
||||||
return Result.failure(new RecipeError.ValidationFailure("Invalid output quantity: " + e.getMessage()));
|
return Result.failure(new RecipeError.ValidationFailure("Invalid output quantity: " + e.getMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
var now = LocalDateTime.now();
|
var now = OffsetDateTime.now(ZoneOffset.UTC);
|
||||||
return Result.success(new Recipe(
|
return Result.success(new Recipe(
|
||||||
RecipeId.generate(), name, draft.version(), draft.type(),
|
RecipeId.generate(), name, draft.version(), draft.type(),
|
||||||
draft.description(), yieldPercentage, shelfLifeDays, outputQuantity,
|
draft.description(), yieldPercentage, shelfLifeDays, outputQuantity,
|
||||||
|
|
@ -146,8 +147,8 @@ public class Recipe {
|
||||||
RecipeStatus status,
|
RecipeStatus status,
|
||||||
List<Ingredient> ingredients,
|
List<Ingredient> ingredients,
|
||||||
List<ProductionStep> productionSteps,
|
List<ProductionStep> productionSteps,
|
||||||
LocalDateTime createdAt,
|
OffsetDateTime createdAt,
|
||||||
LocalDateTime updatedAt
|
OffsetDateTime updatedAt
|
||||||
) {
|
) {
|
||||||
return new Recipe(id, name, version, type, description,
|
return new Recipe(id, name, version, type, description,
|
||||||
yieldPercentage, shelfLifeDays, outputQuantity, status, ingredients, productionSteps, createdAt, updatedAt);
|
yieldPercentage, shelfLifeDays, outputQuantity, status, ingredients, productionSteps, createdAt, updatedAt);
|
||||||
|
|
@ -255,8 +256,8 @@ public class Recipe {
|
||||||
public RecipeStatus status() { return status; }
|
public RecipeStatus status() { return status; }
|
||||||
public List<Ingredient> ingredients() { return Collections.unmodifiableList(ingredients); }
|
public List<Ingredient> ingredients() { return Collections.unmodifiableList(ingredients); }
|
||||||
public List<ProductionStep> productionSteps() { return Collections.unmodifiableList(productionSteps); }
|
public List<ProductionStep> productionSteps() { return Collections.unmodifiableList(productionSteps); }
|
||||||
public LocalDateTime createdAt() { return createdAt; }
|
public OffsetDateTime createdAt() { return createdAt; }
|
||||||
public LocalDateTime updatedAt() { return updatedAt; }
|
public OffsetDateTime updatedAt() { return updatedAt; }
|
||||||
|
|
||||||
// ==================== Helpers ====================
|
// ==================== Helpers ====================
|
||||||
|
|
||||||
|
|
@ -269,7 +270,7 @@ public class Recipe {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void touch() {
|
private void touch() {
|
||||||
this.updatedAt = LocalDateTime.now();
|
this.updatedAt = OffsetDateTime.now(ZoneOffset.UTC);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ package de.effigenix.domain.usermanagement;
|
||||||
|
|
||||||
import de.effigenix.shared.common.Result;
|
import de.effigenix.shared.common.Result;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
@ -33,8 +34,8 @@ public class User {
|
||||||
private final Set<Role> roles;
|
private final Set<Role> roles;
|
||||||
private final String branchId;
|
private final String branchId;
|
||||||
private final UserStatus status;
|
private final UserStatus status;
|
||||||
private final LocalDateTime createdAt;
|
private final OffsetDateTime createdAt;
|
||||||
private final LocalDateTime lastLogin;
|
private final OffsetDateTime lastLogin;
|
||||||
|
|
||||||
private User(
|
private User(
|
||||||
UserId id,
|
UserId id,
|
||||||
|
|
@ -44,8 +45,8 @@ public class User {
|
||||||
Set<Role> roles,
|
Set<Role> roles,
|
||||||
String branchId,
|
String branchId,
|
||||||
UserStatus status,
|
UserStatus status,
|
||||||
LocalDateTime createdAt,
|
OffsetDateTime createdAt,
|
||||||
LocalDateTime lastLogin
|
OffsetDateTime lastLogin
|
||||||
) {
|
) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.username = username;
|
this.username = username;
|
||||||
|
|
@ -54,7 +55,7 @@ public class User {
|
||||||
this.roles = roles != null ? Set.copyOf(roles) : Set.of();
|
this.roles = roles != null ? Set.copyOf(roles) : Set.of();
|
||||||
this.branchId = branchId;
|
this.branchId = branchId;
|
||||||
this.status = status;
|
this.status = status;
|
||||||
this.createdAt = createdAt != null ? createdAt : LocalDateTime.now();
|
this.createdAt = createdAt != null ? createdAt : OffsetDateTime.now(ZoneOffset.UTC);
|
||||||
this.lastLogin = lastLogin;
|
this.lastLogin = lastLogin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,7 +87,7 @@ public class User {
|
||||||
roles,
|
roles,
|
||||||
branchId,
|
branchId,
|
||||||
UserStatus.ACTIVE,
|
UserStatus.ACTIVE,
|
||||||
LocalDateTime.now(),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
null
|
null
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
@ -102,15 +103,15 @@ public class User {
|
||||||
Set<Role> roles,
|
Set<Role> roles,
|
||||||
String branchId,
|
String branchId,
|
||||||
UserStatus status,
|
UserStatus status,
|
||||||
LocalDateTime createdAt,
|
OffsetDateTime createdAt,
|
||||||
LocalDateTime lastLogin
|
OffsetDateTime lastLogin
|
||||||
) {
|
) {
|
||||||
return new User(id, username, email, passwordHash, roles, branchId, status, createdAt, lastLogin);
|
return new User(id, username, email, passwordHash, roles, branchId, status, createdAt, lastLogin);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== Business Methods (Wither-Pattern) ====================
|
// ==================== Business Methods (Wither-Pattern) ====================
|
||||||
|
|
||||||
public Result<UserError, User> withLastLogin(LocalDateTime timestamp) {
|
public Result<UserError, User> withLastLogin(OffsetDateTime timestamp) {
|
||||||
return Result.success(new User(id, username, email, passwordHash, roles, branchId, status, createdAt, timestamp));
|
return Result.success(new User(id, username, email, passwordHash, roles, branchId, status, createdAt, timestamp));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -218,8 +219,8 @@ public class User {
|
||||||
public Set<Role> roles() { return Collections.unmodifiableSet(roles); }
|
public Set<Role> roles() { return Collections.unmodifiableSet(roles); }
|
||||||
public String branchId() { return branchId; }
|
public String branchId() { return branchId; }
|
||||||
public UserStatus status() { return status; }
|
public UserStatus status() { return status; }
|
||||||
public LocalDateTime createdAt() { return createdAt; }
|
public OffsetDateTime createdAt() { return createdAt; }
|
||||||
public LocalDateTime lastLogin() { return lastLogin; }
|
public OffsetDateTime lastLogin() { return lastLogin; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@ import jakarta.persistence.*;
|
||||||
import org.springframework.data.annotation.CreatedDate;
|
import org.springframework.data.annotation.CreatedDate;
|
||||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JPA Entity for Audit Logs.
|
* JPA Entity for Audit Logs.
|
||||||
|
|
@ -43,7 +44,7 @@ public class AuditLogEntity {
|
||||||
private String details;
|
private String details;
|
||||||
|
|
||||||
@Column(name = "timestamp", nullable = false)
|
@Column(name = "timestamp", nullable = false)
|
||||||
private LocalDateTime timestamp;
|
private OffsetDateTime timestamp;
|
||||||
|
|
||||||
@Column(name = "ip_address", length = 45) // IPv6 max length
|
@Column(name = "ip_address", length = 45) // IPv6 max length
|
||||||
private String ipAddress;
|
private String ipAddress;
|
||||||
|
|
@ -53,7 +54,7 @@ public class AuditLogEntity {
|
||||||
|
|
||||||
@CreatedDate
|
@CreatedDate
|
||||||
@Column(name = "created_at", nullable = false, updatable = false)
|
@Column(name = "created_at", nullable = false, updatable = false)
|
||||||
private LocalDateTime createdAt;
|
private OffsetDateTime createdAt;
|
||||||
|
|
||||||
// JPA requires no-arg constructor
|
// JPA requires no-arg constructor
|
||||||
protected AuditLogEntity() {
|
protected AuditLogEntity() {
|
||||||
|
|
@ -65,7 +66,7 @@ public class AuditLogEntity {
|
||||||
String entityId,
|
String entityId,
|
||||||
String performedBy,
|
String performedBy,
|
||||||
String details,
|
String details,
|
||||||
LocalDateTime timestamp,
|
OffsetDateTime timestamp,
|
||||||
String ipAddress,
|
String ipAddress,
|
||||||
String userAgent
|
String userAgent
|
||||||
) {
|
) {
|
||||||
|
|
@ -77,7 +78,7 @@ public class AuditLogEntity {
|
||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
this.ipAddress = ipAddress;
|
this.ipAddress = ipAddress;
|
||||||
this.userAgent = userAgent;
|
this.userAgent = userAgent;
|
||||||
this.createdAt = LocalDateTime.now();
|
this.createdAt = OffsetDateTime.now(ZoneOffset.UTC);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters only (immutable after creation)
|
// Getters only (immutable after creation)
|
||||||
|
|
@ -101,7 +102,7 @@ public class AuditLogEntity {
|
||||||
return details;
|
return details;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalDateTime getTimestamp() {
|
public OffsetDateTime getTimestamp() {
|
||||||
return timestamp;
|
return timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,7 +114,7 @@ public class AuditLogEntity {
|
||||||
return userAgent;
|
return userAgent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalDateTime getCreatedAt() {
|
public OffsetDateTime getCreatedAt() {
|
||||||
return createdAt;
|
return createdAt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import de.effigenix.application.usermanagement.AuditEvent;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -34,7 +34,7 @@ public interface AuditLogJpaRepository extends JpaRepository<AuditLogEntity, Str
|
||||||
/**
|
/**
|
||||||
* Finds all audit logs within a time range.
|
* Finds all audit logs within a time range.
|
||||||
*/
|
*/
|
||||||
List<AuditLogEntity> findByTimestampBetween(LocalDateTime start, LocalDateTime end);
|
List<AuditLogEntity> findByTimestampBetween(OffsetDateTime start, OffsetDateTime end);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds all audit logs for a specific event and actor.
|
* Finds all audit logs for a specific event and actor.
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.web.context.request.RequestContextHolder;
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -52,7 +53,7 @@ public class DatabaseAuditLogger implements AuditLogger {
|
||||||
entityId,
|
entityId,
|
||||||
performedBy.value(),
|
performedBy.value(),
|
||||||
null, // no additional details
|
null, // no additional details
|
||||||
LocalDateTime.now(),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
getClientIpAddress(),
|
getClientIpAddress(),
|
||||||
getUserAgent()
|
getUserAgent()
|
||||||
);
|
);
|
||||||
|
|
@ -75,7 +76,7 @@ public class DatabaseAuditLogger implements AuditLogger {
|
||||||
null, // no entity ID
|
null, // no entity ID
|
||||||
null, // no actor (e.g., system event)
|
null, // no actor (e.g., system event)
|
||||||
details,
|
details,
|
||||||
LocalDateTime.now(),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
getClientIpAddress(),
|
getClientIpAddress(),
|
||||||
getUserAgent()
|
getUserAgent()
|
||||||
);
|
);
|
||||||
|
|
@ -97,7 +98,7 @@ public class DatabaseAuditLogger implements AuditLogger {
|
||||||
entityId,
|
entityId,
|
||||||
performedBy.value(),
|
performedBy.value(),
|
||||||
details,
|
details,
|
||||||
LocalDateTime.now(),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
getClientIpAddress(),
|
getClientIpAddress(),
|
||||||
getUserAgent()
|
getUserAgent()
|
||||||
);
|
);
|
||||||
|
|
@ -119,7 +120,7 @@ public class DatabaseAuditLogger implements AuditLogger {
|
||||||
null, // no entity ID
|
null, // no entity ID
|
||||||
performedBy.value(),
|
performedBy.value(),
|
||||||
null, // no additional details
|
null, // no additional details
|
||||||
LocalDateTime.now(),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
getClientIpAddress(),
|
getClientIpAddress(),
|
||||||
getUserAgent()
|
getUserAgent()
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,26 @@
|
||||||
package de.effigenix.infrastructure.config;
|
package de.effigenix.infrastructure.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Profile;
|
import org.springframework.context.annotation.Profile;
|
||||||
|
import org.springframework.data.auditing.DateTimeProvider;
|
||||||
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
||||||
|
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aktiviert JPA-Auditing nur, wenn eine Datenbankverbindung vorhanden ist.
|
* Aktiviert JPA-Auditing nur, wenn eine Datenbankverbindung vorhanden ist.
|
||||||
|
* Verwendet OffsetDateTime (UTC) statt LocalDateTime für @CreatedDate/@LastModifiedDate.
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@Profile("!no-db")
|
@Profile("!no-db")
|
||||||
@EnableJpaAuditing
|
@EnableJpaAuditing(dateTimeProviderRef = "utcDateTimeProvider")
|
||||||
public class JpaAuditingConfig {
|
public class JpaAuditingConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public DateTimeProvider utcDateTimeProvider() {
|
||||||
|
return () -> Optional.of(OffsetDateTime.now(ZoneOffset.UTC));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,7 @@ public class StockMapper {
|
||||||
if (entity.getMinimumLevelAmount() != null && entity.getMinimumLevelUnit() != null) {
|
if (entity.getMinimumLevelAmount() != null && entity.getMinimumLevelUnit() != null) {
|
||||||
var quantity = Quantity.reconstitute(
|
var quantity = Quantity.reconstitute(
|
||||||
entity.getMinimumLevelAmount(),
|
entity.getMinimumLevelAmount(),
|
||||||
UnitOfMeasure.valueOf(entity.getMinimumLevelUnit()),
|
UnitOfMeasure.valueOf(entity.getMinimumLevelUnit())
|
||||||
null, null
|
|
||||||
);
|
);
|
||||||
minimumLevel = new MinimumLevel(quantity);
|
minimumLevel = new MinimumLevel(quantity);
|
||||||
}
|
}
|
||||||
|
|
@ -87,8 +86,7 @@ public class StockMapper {
|
||||||
new BatchReference(entity.getBatchId(), BatchType.valueOf(entity.getBatchType())),
|
new BatchReference(entity.getBatchId(), BatchType.valueOf(entity.getBatchType())),
|
||||||
Quantity.reconstitute(
|
Quantity.reconstitute(
|
||||||
entity.getQuantityAmount(),
|
entity.getQuantityAmount(),
|
||||||
UnitOfMeasure.valueOf(entity.getQuantityUnit()),
|
UnitOfMeasure.valueOf(entity.getQuantityUnit())
|
||||||
null, null
|
|
||||||
),
|
),
|
||||||
entity.getExpiryDate(),
|
entity.getExpiryDate(),
|
||||||
StockBatchStatus.valueOf(entity.getStatus()),
|
StockBatchStatus.valueOf(entity.getStatus()),
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ package de.effigenix.infrastructure.masterdata.persistence.entity;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -29,10 +29,10 @@ public class ArticleEntity {
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
@Column(name = "created_at", nullable = false, updatable = false)
|
@Column(name = "created_at", nullable = false, updatable = false)
|
||||||
private LocalDateTime createdAt;
|
private OffsetDateTime createdAt;
|
||||||
|
|
||||||
@Column(name = "updated_at", nullable = false)
|
@Column(name = "updated_at", nullable = false)
|
||||||
private LocalDateTime updatedAt;
|
private OffsetDateTime updatedAt;
|
||||||
|
|
||||||
@OneToMany(mappedBy = "article", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
|
@OneToMany(mappedBy = "article", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
|
||||||
private List<SalesUnitEntity> salesUnits = new ArrayList<>();
|
private List<SalesUnitEntity> salesUnits = new ArrayList<>();
|
||||||
|
|
@ -45,7 +45,7 @@ public class ArticleEntity {
|
||||||
protected ArticleEntity() {}
|
protected ArticleEntity() {}
|
||||||
|
|
||||||
public ArticleEntity(String id, String name, String articleNumber, String categoryId,
|
public ArticleEntity(String id, String name, String articleNumber, String categoryId,
|
||||||
String status, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
String status, OffsetDateTime createdAt, OffsetDateTime updatedAt) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.articleNumber = articleNumber;
|
this.articleNumber = articleNumber;
|
||||||
|
|
@ -60,8 +60,8 @@ public class ArticleEntity {
|
||||||
public String getArticleNumber() { return articleNumber; }
|
public String getArticleNumber() { return articleNumber; }
|
||||||
public String getCategoryId() { return categoryId; }
|
public String getCategoryId() { return categoryId; }
|
||||||
public String getStatus() { return status; }
|
public String getStatus() { return status; }
|
||||||
public LocalDateTime getCreatedAt() { return createdAt; }
|
public OffsetDateTime getCreatedAt() { return createdAt; }
|
||||||
public LocalDateTime getUpdatedAt() { return updatedAt; }
|
public OffsetDateTime getUpdatedAt() { return updatedAt; }
|
||||||
public List<SalesUnitEntity> getSalesUnits() { return salesUnits; }
|
public List<SalesUnitEntity> getSalesUnits() { return salesUnits; }
|
||||||
public Set<String> getSupplierIds() { return supplierIds; }
|
public Set<String> getSupplierIds() { return supplierIds; }
|
||||||
|
|
||||||
|
|
@ -70,8 +70,8 @@ public class ArticleEntity {
|
||||||
public void setArticleNumber(String articleNumber) { this.articleNumber = articleNumber; }
|
public void setArticleNumber(String articleNumber) { this.articleNumber = articleNumber; }
|
||||||
public void setCategoryId(String categoryId) { this.categoryId = categoryId; }
|
public void setCategoryId(String categoryId) { this.categoryId = categoryId; }
|
||||||
public void setStatus(String status) { this.status = status; }
|
public void setStatus(String status) { this.status = status; }
|
||||||
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
|
public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; }
|
||||||
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
|
public void setUpdatedAt(OffsetDateTime updatedAt) { this.updatedAt = updatedAt; }
|
||||||
public void setSalesUnits(List<SalesUnitEntity> salesUnits) { this.salesUnits = salesUnits; }
|
public void setSalesUnits(List<SalesUnitEntity> salesUnits) { this.salesUnits = salesUnits; }
|
||||||
public void setSupplierIds(Set<String> supplierIds) { this.supplierIds = supplierIds; }
|
public void setSupplierIds(Set<String> supplierIds) { this.supplierIds = supplierIds; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ package de.effigenix.infrastructure.masterdata.persistence.entity;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -56,10 +56,10 @@ public class CustomerEntity {
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
@Column(name = "created_at", nullable = false, updatable = false)
|
@Column(name = "created_at", nullable = false, updatable = false)
|
||||||
private LocalDateTime createdAt;
|
private OffsetDateTime createdAt;
|
||||||
|
|
||||||
@Column(name = "updated_at", nullable = false)
|
@Column(name = "updated_at", nullable = false)
|
||||||
private LocalDateTime updatedAt;
|
private OffsetDateTime updatedAt;
|
||||||
|
|
||||||
@ElementCollection(fetch = FetchType.EAGER)
|
@ElementCollection(fetch = FetchType.EAGER)
|
||||||
@CollectionTable(name = "delivery_addresses", joinColumns = @JoinColumn(name = "customer_id"))
|
@CollectionTable(name = "delivery_addresses", joinColumns = @JoinColumn(name = "customer_id"))
|
||||||
|
|
@ -91,8 +91,8 @@ public class CustomerEntity {
|
||||||
public Integer getPaymentDueDays() { return paymentDueDays; }
|
public Integer getPaymentDueDays() { return paymentDueDays; }
|
||||||
public String getPaymentDescription() { return paymentDescription; }
|
public String getPaymentDescription() { return paymentDescription; }
|
||||||
public String getStatus() { return status; }
|
public String getStatus() { return status; }
|
||||||
public LocalDateTime getCreatedAt() { return createdAt; }
|
public OffsetDateTime getCreatedAt() { return createdAt; }
|
||||||
public LocalDateTime getUpdatedAt() { return updatedAt; }
|
public OffsetDateTime getUpdatedAt() { return updatedAt; }
|
||||||
public List<DeliveryAddressEmbeddable> getDeliveryAddresses() { return deliveryAddresses; }
|
public List<DeliveryAddressEmbeddable> getDeliveryAddresses() { return deliveryAddresses; }
|
||||||
public Set<de.effigenix.domain.masterdata.CustomerPreference> getPreferences() { return preferences; }
|
public Set<de.effigenix.domain.masterdata.CustomerPreference> getPreferences() { return preferences; }
|
||||||
public FrameContractEntity getFrameContract() { return frameContract; }
|
public FrameContractEntity getFrameContract() { return frameContract; }
|
||||||
|
|
@ -111,8 +111,8 @@ public class CustomerEntity {
|
||||||
public void setPaymentDueDays(Integer paymentDueDays) { this.paymentDueDays = paymentDueDays; }
|
public void setPaymentDueDays(Integer paymentDueDays) { this.paymentDueDays = paymentDueDays; }
|
||||||
public void setPaymentDescription(String paymentDescription) { this.paymentDescription = paymentDescription; }
|
public void setPaymentDescription(String paymentDescription) { this.paymentDescription = paymentDescription; }
|
||||||
public void setStatus(String status) { this.status = status; }
|
public void setStatus(String status) { this.status = status; }
|
||||||
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
|
public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; }
|
||||||
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
|
public void setUpdatedAt(OffsetDateTime updatedAt) { this.updatedAt = updatedAt; }
|
||||||
public void setDeliveryAddresses(List<DeliveryAddressEmbeddable> deliveryAddresses) { this.deliveryAddresses = deliveryAddresses; }
|
public void setDeliveryAddresses(List<DeliveryAddressEmbeddable> deliveryAddresses) { this.deliveryAddresses = deliveryAddresses; }
|
||||||
public void setPreferences(Set<de.effigenix.domain.masterdata.CustomerPreference> preferences) { this.preferences = preferences; }
|
public void setPreferences(Set<de.effigenix.domain.masterdata.CustomerPreference> preferences) { this.preferences = preferences; }
|
||||||
public void setFrameContract(FrameContractEntity frameContract) { this.frameContract = frameContract; }
|
public void setFrameContract(FrameContractEntity frameContract) { this.frameContract = frameContract; }
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ package de.effigenix.infrastructure.masterdata.persistence.entity;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
@ -60,10 +60,10 @@ public class SupplierEntity {
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
@Column(name = "created_at", nullable = false, updatable = false)
|
@Column(name = "created_at", nullable = false, updatable = false)
|
||||||
private LocalDateTime createdAt;
|
private OffsetDateTime createdAt;
|
||||||
|
|
||||||
@Column(name = "updated_at", nullable = false)
|
@Column(name = "updated_at", nullable = false)
|
||||||
private LocalDateTime updatedAt;
|
private OffsetDateTime updatedAt;
|
||||||
|
|
||||||
@ElementCollection(fetch = FetchType.EAGER)
|
@ElementCollection(fetch = FetchType.EAGER)
|
||||||
@CollectionTable(name = "quality_certificates", joinColumns = @JoinColumn(name = "supplier_id"))
|
@CollectionTable(name = "quality_certificates", joinColumns = @JoinColumn(name = "supplier_id"))
|
||||||
|
|
@ -87,8 +87,8 @@ public class SupplierEntity {
|
||||||
public Integer getDeliveryScore() { return deliveryScore; }
|
public Integer getDeliveryScore() { return deliveryScore; }
|
||||||
public Integer getPriceScore() { return priceScore; }
|
public Integer getPriceScore() { return priceScore; }
|
||||||
public String getStatus() { return status; }
|
public String getStatus() { return status; }
|
||||||
public LocalDateTime getCreatedAt() { return createdAt; }
|
public OffsetDateTime getCreatedAt() { return createdAt; }
|
||||||
public LocalDateTime getUpdatedAt() { return updatedAt; }
|
public OffsetDateTime getUpdatedAt() { return updatedAt; }
|
||||||
public List<QualityCertificateEmbeddable> getCertificates() { return certificates; }
|
public List<QualityCertificateEmbeddable> getCertificates() { return certificates; }
|
||||||
|
|
||||||
public void setId(String id) { this.id = id; }
|
public void setId(String id) { this.id = id; }
|
||||||
|
|
@ -107,7 +107,7 @@ public class SupplierEntity {
|
||||||
public void setDeliveryScore(Integer deliveryScore) { this.deliveryScore = deliveryScore; }
|
public void setDeliveryScore(Integer deliveryScore) { this.deliveryScore = deliveryScore; }
|
||||||
public void setPriceScore(Integer priceScore) { this.priceScore = priceScore; }
|
public void setPriceScore(Integer priceScore) { this.priceScore = priceScore; }
|
||||||
public void setStatus(String status) { this.status = status; }
|
public void setStatus(String status) { this.status = status; }
|
||||||
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
|
public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; }
|
||||||
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
|
public void setUpdatedAt(OffsetDateTime updatedAt) { this.updatedAt = updatedAt; }
|
||||||
public void setCertificates(List<QualityCertificateEmbeddable> certificates) { this.certificates = certificates; }
|
public void setCertificates(List<QualityCertificateEmbeddable> certificates) { this.certificates = certificates; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import de.effigenix.domain.masterdata.Article;
|
||||||
import de.effigenix.domain.masterdata.SupplierId;
|
import de.effigenix.domain.masterdata.SupplierId;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Schema(requiredProperties = {"id", "name", "articleNumber", "categoryId", "salesUnits", "status", "supplierIds", "createdAt", "updatedAt"})
|
@Schema(requiredProperties = {"id", "name", "articleNumber", "categoryId", "salesUnits", "status", "supplierIds", "createdAt", "updatedAt"})
|
||||||
|
|
@ -16,8 +16,8 @@ public record ArticleResponse(
|
||||||
List<SalesUnitResponse> salesUnits,
|
List<SalesUnitResponse> salesUnits,
|
||||||
String status,
|
String status,
|
||||||
List<String> supplierIds,
|
List<String> supplierIds,
|
||||||
LocalDateTime createdAt,
|
OffsetDateTime createdAt,
|
||||||
LocalDateTime updatedAt
|
OffsetDateTime updatedAt
|
||||||
) {
|
) {
|
||||||
public static ArticleResponse from(Article article) {
|
public static ArticleResponse from(Article article) {
|
||||||
return new ArticleResponse(
|
return new ArticleResponse(
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import de.effigenix.domain.masterdata.Customer;
|
||||||
import de.effigenix.domain.masterdata.CustomerPreference;
|
import de.effigenix.domain.masterdata.CustomerPreference;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Schema(requiredProperties = {"id", "name", "type", "billingAddress", "contactInfo", "deliveryAddresses", "preferences", "status", "createdAt", "updatedAt"})
|
@Schema(requiredProperties = {"id", "name", "type", "billingAddress", "contactInfo", "deliveryAddresses", "preferences", "status", "createdAt", "updatedAt"})
|
||||||
|
|
@ -19,8 +19,8 @@ public record CustomerResponse(
|
||||||
@Schema(nullable = true) FrameContractResponse frameContract,
|
@Schema(nullable = true) FrameContractResponse frameContract,
|
||||||
List<String> preferences,
|
List<String> preferences,
|
||||||
String status,
|
String status,
|
||||||
LocalDateTime createdAt,
|
OffsetDateTime createdAt,
|
||||||
LocalDateTime updatedAt
|
OffsetDateTime updatedAt
|
||||||
) {
|
) {
|
||||||
public static CustomerResponse from(Customer customer) {
|
public static CustomerResponse from(Customer customer) {
|
||||||
return new CustomerResponse(
|
return new CustomerResponse(
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package de.effigenix.infrastructure.masterdata.web.dto;
|
||||||
import de.effigenix.domain.masterdata.Supplier;
|
import de.effigenix.domain.masterdata.Supplier;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Schema(requiredProperties = {"id", "name", "contactInfo", "certificates", "status", "createdAt", "updatedAt"})
|
@Schema(requiredProperties = {"id", "name", "contactInfo", "certificates", "status", "createdAt", "updatedAt"})
|
||||||
|
|
@ -16,8 +16,8 @@ public record SupplierResponse(
|
||||||
List<QualityCertificateResponse> certificates,
|
List<QualityCertificateResponse> certificates,
|
||||||
@Schema(nullable = true) SupplierRatingResponse rating,
|
@Schema(nullable = true) SupplierRatingResponse rating,
|
||||||
String status,
|
String status,
|
||||||
LocalDateTime createdAt,
|
OffsetDateTime createdAt,
|
||||||
LocalDateTime updatedAt
|
OffsetDateTime updatedAt
|
||||||
) {
|
) {
|
||||||
public static SupplierResponse from(Supplier supplier) {
|
public static SupplierResponse from(Supplier supplier) {
|
||||||
return new SupplierResponse(
|
return new SupplierResponse(
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,12 @@ package de.effigenix.infrastructure.production.persistence;
|
||||||
import de.effigenix.domain.production.BatchError;
|
import de.effigenix.domain.production.BatchError;
|
||||||
import de.effigenix.domain.production.BatchNumber;
|
import de.effigenix.domain.production.BatchNumber;
|
||||||
import de.effigenix.domain.production.BatchNumberGenerator;
|
import de.effigenix.domain.production.BatchNumberGenerator;
|
||||||
import de.effigenix.infrastructure.production.persistence.repository.BatchJpaRepository;
|
import de.effigenix.infrastructure.production.persistence.entity.BatchNumberSequenceEntity;
|
||||||
|
import de.effigenix.infrastructure.production.persistence.repository.BatchNumberSequenceJpaRepository;
|
||||||
import de.effigenix.shared.common.Result;
|
import de.effigenix.shared.common.Result;
|
||||||
import org.springframework.context.annotation.Profile;
|
import org.springframework.context.annotation.Profile;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
|
@ -14,17 +16,25 @@ import java.time.LocalDate;
|
||||||
@Profile("!no-db")
|
@Profile("!no-db")
|
||||||
public class JpaBatchNumberGenerator implements BatchNumberGenerator {
|
public class JpaBatchNumberGenerator implements BatchNumberGenerator {
|
||||||
|
|
||||||
private final BatchJpaRepository batchJpaRepository;
|
private final BatchNumberSequenceJpaRepository sequenceRepository;
|
||||||
|
|
||||||
public JpaBatchNumberGenerator(BatchJpaRepository batchJpaRepository) {
|
public JpaBatchNumberGenerator(BatchNumberSequenceJpaRepository sequenceRepository) {
|
||||||
this.batchJpaRepository = batchJpaRepository;
|
this.sequenceRepository = sequenceRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional
|
||||||
public Result<BatchError, BatchNumber> generateNext(LocalDate date) {
|
public Result<BatchError, BatchNumber> generateNext(LocalDate date) {
|
||||||
try {
|
try {
|
||||||
int count = batchJpaRepository.countByProductionDate(date);
|
var sequence = sequenceRepository.findByProductionDate(date);
|
||||||
int nextSequence = count + 1;
|
int nextSequence;
|
||||||
|
if (sequence.isPresent()) {
|
||||||
|
nextSequence = sequence.get().getLastSequence() + 1;
|
||||||
|
sequence.get().setLastSequence(nextSequence);
|
||||||
|
} else {
|
||||||
|
nextSequence = 1;
|
||||||
|
sequenceRepository.save(new BatchNumberSequenceEntity(date, 1));
|
||||||
|
}
|
||||||
if (nextSequence > 999) {
|
if (nextSequence > 999) {
|
||||||
return Result.failure(new BatchError.ValidationFailure(
|
return Result.failure(new BatchError.ValidationFailure(
|
||||||
"Maximum batch number sequence (999) reached for date " + date));
|
"Maximum batch number sequence (999) reached for date " + date));
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import jakarta.persistence.*;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "batches")
|
@Table(name = "batches")
|
||||||
|
|
@ -36,10 +36,10 @@ public class BatchEntity {
|
||||||
private LocalDate bestBeforeDate;
|
private LocalDate bestBeforeDate;
|
||||||
|
|
||||||
@Column(name = "created_at", nullable = false, updatable = false)
|
@Column(name = "created_at", nullable = false, updatable = false)
|
||||||
private LocalDateTime createdAt;
|
private OffsetDateTime createdAt;
|
||||||
|
|
||||||
@Column(name = "updated_at", nullable = false)
|
@Column(name = "updated_at", nullable = false)
|
||||||
private LocalDateTime updatedAt;
|
private OffsetDateTime updatedAt;
|
||||||
|
|
||||||
protected BatchEntity() {}
|
protected BatchEntity() {}
|
||||||
|
|
||||||
|
|
@ -52,8 +52,8 @@ public class BatchEntity {
|
||||||
String plannedQuantityUnit,
|
String plannedQuantityUnit,
|
||||||
LocalDate productionDate,
|
LocalDate productionDate,
|
||||||
LocalDate bestBeforeDate,
|
LocalDate bestBeforeDate,
|
||||||
LocalDateTime createdAt,
|
OffsetDateTime createdAt,
|
||||||
LocalDateTime updatedAt
|
OffsetDateTime updatedAt
|
||||||
) {
|
) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.batchNumber = batchNumber;
|
this.batchNumber = batchNumber;
|
||||||
|
|
@ -75,6 +75,6 @@ public class BatchEntity {
|
||||||
public String getPlannedQuantityUnit() { return plannedQuantityUnit; }
|
public String getPlannedQuantityUnit() { return plannedQuantityUnit; }
|
||||||
public LocalDate getProductionDate() { return productionDate; }
|
public LocalDate getProductionDate() { return productionDate; }
|
||||||
public LocalDate getBestBeforeDate() { return bestBeforeDate; }
|
public LocalDate getBestBeforeDate() { return bestBeforeDate; }
|
||||||
public LocalDateTime getCreatedAt() { return createdAt; }
|
public OffsetDateTime getCreatedAt() { return createdAt; }
|
||||||
public LocalDateTime getUpdatedAt() { return updatedAt; }
|
public OffsetDateTime getUpdatedAt() { return updatedAt; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
package de.effigenix.infrastructure.production.persistence.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "batch_number_sequences")
|
||||||
|
public class BatchNumberSequenceEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "production_date", nullable = false)
|
||||||
|
private LocalDate productionDate;
|
||||||
|
|
||||||
|
@Column(name = "last_sequence", nullable = false)
|
||||||
|
private int lastSequence;
|
||||||
|
|
||||||
|
protected BatchNumberSequenceEntity() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public BatchNumberSequenceEntity(LocalDate productionDate, int lastSequence) {
|
||||||
|
this.productionDate = productionDate;
|
||||||
|
this.lastSequence = lastSequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDate getProductionDate() {
|
||||||
|
return productionDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLastSequence() {
|
||||||
|
return lastSequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastSequence(int lastSequence) {
|
||||||
|
this.lastSequence = lastSequence;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,7 @@ package de.effigenix.infrastructure.production.persistence.entity;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
@ -44,10 +44,10 @@ public class RecipeEntity {
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
@Column(name = "created_at", nullable = false, updatable = false)
|
@Column(name = "created_at", nullable = false, updatable = false)
|
||||||
private LocalDateTime createdAt;
|
private OffsetDateTime createdAt;
|
||||||
|
|
||||||
@Column(name = "updated_at", nullable = false)
|
@Column(name = "updated_at", nullable = false)
|
||||||
private LocalDateTime updatedAt;
|
private OffsetDateTime updatedAt;
|
||||||
|
|
||||||
@OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
|
@OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
|
||||||
@OrderBy("position ASC")
|
@OrderBy("position ASC")
|
||||||
|
|
@ -61,7 +61,7 @@ public class RecipeEntity {
|
||||||
|
|
||||||
public RecipeEntity(String id, String name, int version, String type, String description,
|
public RecipeEntity(String id, String name, int version, String type, String description,
|
||||||
int yieldPercentage, Integer shelfLifeDays, BigDecimal outputQuantity,
|
int yieldPercentage, Integer shelfLifeDays, BigDecimal outputQuantity,
|
||||||
String outputUom, String status, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
String outputUom, String status, OffsetDateTime createdAt, OffsetDateTime updatedAt) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
|
|
@ -86,8 +86,8 @@ public class RecipeEntity {
|
||||||
public BigDecimal getOutputQuantity() { return outputQuantity; }
|
public BigDecimal getOutputQuantity() { return outputQuantity; }
|
||||||
public String getOutputUom() { return outputUom; }
|
public String getOutputUom() { return outputUom; }
|
||||||
public String getStatus() { return status; }
|
public String getStatus() { return status; }
|
||||||
public LocalDateTime getCreatedAt() { return createdAt; }
|
public OffsetDateTime getCreatedAt() { return createdAt; }
|
||||||
public LocalDateTime getUpdatedAt() { return updatedAt; }
|
public OffsetDateTime getUpdatedAt() { return updatedAt; }
|
||||||
public List<IngredientEntity> getIngredients() { return ingredients; }
|
public List<IngredientEntity> getIngredients() { return ingredients; }
|
||||||
public List<ProductionStepEntity> getProductionSteps() { return productionSteps; }
|
public List<ProductionStepEntity> getProductionSteps() { return productionSteps; }
|
||||||
|
|
||||||
|
|
@ -101,8 +101,8 @@ public class RecipeEntity {
|
||||||
public void setOutputQuantity(BigDecimal outputQuantity) { this.outputQuantity = outputQuantity; }
|
public void setOutputQuantity(BigDecimal outputQuantity) { this.outputQuantity = outputQuantity; }
|
||||||
public void setOutputUom(String outputUom) { this.outputUom = outputUom; }
|
public void setOutputUom(String outputUom) { this.outputUom = outputUom; }
|
||||||
public void setStatus(String status) { this.status = status; }
|
public void setStatus(String status) { this.status = status; }
|
||||||
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
|
public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; }
|
||||||
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
|
public void setUpdatedAt(OffsetDateTime updatedAt) { this.updatedAt = updatedAt; }
|
||||||
public void setIngredients(List<IngredientEntity> ingredients) { this.ingredients = ingredients; }
|
public void setIngredients(List<IngredientEntity> ingredients) { this.ingredients = ingredients; }
|
||||||
public void setProductionSteps(List<ProductionStepEntity> productionSteps) { this.productionSteps = productionSteps; }
|
public void setProductionSteps(List<ProductionStepEntity> productionSteps) { this.productionSteps = productionSteps; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,7 @@ public class BatchMapper {
|
||||||
BatchStatus.valueOf(entity.getStatus()),
|
BatchStatus.valueOf(entity.getStatus()),
|
||||||
Quantity.reconstitute(
|
Quantity.reconstitute(
|
||||||
entity.getPlannedQuantityAmount(),
|
entity.getPlannedQuantityAmount(),
|
||||||
UnitOfMeasure.valueOf(entity.getPlannedQuantityUnit()),
|
UnitOfMeasure.valueOf(entity.getPlannedQuantityUnit())
|
||||||
null, null
|
|
||||||
),
|
),
|
||||||
entity.getProductionDate(),
|
entity.getProductionDate(),
|
||||||
entity.getBestBeforeDate(),
|
entity.getBestBeforeDate(),
|
||||||
|
|
|
||||||
|
|
@ -62,8 +62,7 @@ public class RecipeMapper {
|
||||||
entity.getShelfLifeDays(),
|
entity.getShelfLifeDays(),
|
||||||
Quantity.reconstitute(
|
Quantity.reconstitute(
|
||||||
entity.getOutputQuantity(),
|
entity.getOutputQuantity(),
|
||||||
UnitOfMeasure.valueOf(entity.getOutputUom()),
|
UnitOfMeasure.valueOf(entity.getOutputUom())
|
||||||
null, null
|
|
||||||
),
|
),
|
||||||
RecipeStatus.valueOf(entity.getStatus()),
|
RecipeStatus.valueOf(entity.getStatus()),
|
||||||
ingredients,
|
ingredients,
|
||||||
|
|
@ -114,8 +113,7 @@ public class RecipeMapper {
|
||||||
entity.getArticleId(),
|
entity.getArticleId(),
|
||||||
Quantity.reconstitute(
|
Quantity.reconstitute(
|
||||||
entity.getQuantity(),
|
entity.getQuantity(),
|
||||||
UnitOfMeasure.valueOf(entity.getUom()),
|
UnitOfMeasure.valueOf(entity.getUom())
|
||||||
null, null
|
|
||||||
),
|
),
|
||||||
entity.getSubRecipeId(),
|
entity.getSubRecipeId(),
|
||||||
entity.isSubstitutable()
|
entity.isSubstitutable()
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,6 @@ package de.effigenix.infrastructure.production.persistence.repository;
|
||||||
|
|
||||||
import de.effigenix.infrastructure.production.persistence.entity.BatchEntity;
|
import de.effigenix.infrastructure.production.persistence.entity.BatchEntity;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
|
||||||
|
|
||||||
import java.time.LocalDate;
|
|
||||||
|
|
||||||
public interface BatchJpaRepository extends JpaRepository<BatchEntity, String> {
|
public interface BatchJpaRepository extends JpaRepository<BatchEntity, String> {
|
||||||
|
|
||||||
@Query("SELECT COUNT(b) FROM BatchEntity b WHERE b.productionDate = :date")
|
|
||||||
int countByProductionDate(LocalDate date);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package de.effigenix.infrastructure.production.persistence.repository;
|
||||||
|
|
||||||
|
import de.effigenix.infrastructure.production.persistence.entity.BatchNumberSequenceEntity;
|
||||||
|
import jakarta.persistence.LockModeType;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Lock;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public interface BatchNumberSequenceJpaRepository extends JpaRepository<BatchNumberSequenceEntity, LocalDate> {
|
||||||
|
|
||||||
|
@Lock(LockModeType.PESSIMISTIC_WRITE)
|
||||||
|
Optional<BatchNumberSequenceEntity> findByProductionDate(LocalDate productionDate);
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,7 @@ package de.effigenix.infrastructure.production.web.dto;
|
||||||
import de.effigenix.domain.production.Batch;
|
import de.effigenix.domain.production.Batch;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
|
||||||
public record BatchResponse(
|
public record BatchResponse(
|
||||||
String id,
|
String id,
|
||||||
|
|
@ -14,8 +14,8 @@ public record BatchResponse(
|
||||||
String plannedQuantityUnit,
|
String plannedQuantityUnit,
|
||||||
LocalDate productionDate,
|
LocalDate productionDate,
|
||||||
LocalDate bestBeforeDate,
|
LocalDate bestBeforeDate,
|
||||||
LocalDateTime createdAt,
|
OffsetDateTime createdAt,
|
||||||
LocalDateTime updatedAt
|
OffsetDateTime updatedAt
|
||||||
) {
|
) {
|
||||||
public static BatchResponse from(Batch batch) {
|
public static BatchResponse from(Batch batch) {
|
||||||
return new BatchResponse(
|
return new BatchResponse(
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package de.effigenix.infrastructure.production.web.dto;
|
||||||
import de.effigenix.domain.production.Recipe;
|
import de.effigenix.domain.production.Recipe;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Schema(requiredProperties = {"id", "name", "version", "type", "description", "yieldPercentage", "outputQuantity", "outputUom", "status", "ingredients", "productionSteps", "createdAt", "updatedAt"})
|
@Schema(requiredProperties = {"id", "name", "version", "type", "description", "yieldPercentage", "outputQuantity", "outputUom", "status", "ingredients", "productionSteps", "createdAt", "updatedAt"})
|
||||||
|
|
@ -20,8 +20,8 @@ public record RecipeResponse(
|
||||||
String status,
|
String status,
|
||||||
List<IngredientResponse> ingredients,
|
List<IngredientResponse> ingredients,
|
||||||
List<ProductionStepResponse> productionSteps,
|
List<ProductionStepResponse> productionSteps,
|
||||||
LocalDateTime createdAt,
|
OffsetDateTime createdAt,
|
||||||
LocalDateTime updatedAt
|
OffsetDateTime updatedAt
|
||||||
) {
|
) {
|
||||||
public static RecipeResponse from(Recipe recipe) {
|
public static RecipeResponse from(Recipe recipe) {
|
||||||
return new RecipeResponse(
|
return new RecipeResponse(
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package de.effigenix.infrastructure.production.web.dto;
|
||||||
import de.effigenix.domain.production.Recipe;
|
import de.effigenix.domain.production.Recipe;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
|
||||||
@Schema(requiredProperties = {"id", "name", "version", "type", "description", "yieldPercentage", "outputQuantity", "outputUom", "status", "ingredientCount", "stepCount", "createdAt", "updatedAt"})
|
@Schema(requiredProperties = {"id", "name", "version", "type", "description", "yieldPercentage", "outputQuantity", "outputUom", "status", "ingredientCount", "stepCount", "createdAt", "updatedAt"})
|
||||||
public record RecipeSummaryResponse(
|
public record RecipeSummaryResponse(
|
||||||
|
|
@ -19,8 +19,8 @@ public record RecipeSummaryResponse(
|
||||||
String status,
|
String status,
|
||||||
int ingredientCount,
|
int ingredientCount,
|
||||||
int stepCount,
|
int stepCount,
|
||||||
LocalDateTime createdAt,
|
OffsetDateTime createdAt,
|
||||||
LocalDateTime updatedAt
|
OffsetDateTime updatedAt
|
||||||
) {
|
) {
|
||||||
public static RecipeSummaryResponse from(Recipe recipe) {
|
public static RecipeSummaryResponse from(Recipe recipe) {
|
||||||
return new RecipeSummaryResponse(
|
return new RecipeSummaryResponse(
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import jakarta.persistence.*;
|
||||||
import org.springframework.data.annotation.CreatedDate;
|
import org.springframework.data.annotation.CreatedDate;
|
||||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
@ -48,10 +48,10 @@ public class UserEntity {
|
||||||
|
|
||||||
@CreatedDate
|
@CreatedDate
|
||||||
@Column(name = "created_at", nullable = false, updatable = false)
|
@Column(name = "created_at", nullable = false, updatable = false)
|
||||||
private LocalDateTime createdAt;
|
private OffsetDateTime createdAt;
|
||||||
|
|
||||||
@Column(name = "last_login")
|
@Column(name = "last_login")
|
||||||
private LocalDateTime lastLogin;
|
private OffsetDateTime lastLogin;
|
||||||
|
|
||||||
// JPA requires no-arg constructor
|
// JPA requires no-arg constructor
|
||||||
protected UserEntity() {
|
protected UserEntity() {
|
||||||
|
|
@ -65,8 +65,8 @@ public class UserEntity {
|
||||||
Set<RoleEntity> roles,
|
Set<RoleEntity> roles,
|
||||||
String branchId,
|
String branchId,
|
||||||
UserStatus status,
|
UserStatus status,
|
||||||
LocalDateTime createdAt,
|
OffsetDateTime createdAt,
|
||||||
LocalDateTime lastLogin
|
OffsetDateTime lastLogin
|
||||||
) {
|
) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.username = username;
|
this.username = username;
|
||||||
|
|
@ -136,19 +136,19 @@ public class UserEntity {
|
||||||
this.status = status;
|
this.status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalDateTime getCreatedAt() {
|
public OffsetDateTime getCreatedAt() {
|
||||||
return createdAt;
|
return createdAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCreatedAt(LocalDateTime createdAt) {
|
public void setCreatedAt(OffsetDateTime createdAt) {
|
||||||
this.createdAt = createdAt;
|
this.createdAt = createdAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalDateTime getLastLogin() {
|
public OffsetDateTime getLastLogin() {
|
||||||
return lastLogin;
|
return lastLogin;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLastLogin(LocalDateTime lastLogin) {
|
public void setLastLogin(OffsetDateTime lastLogin) {
|
||||||
this.lastLogin = lastLogin;
|
this.lastLogin = lastLogin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ package de.effigenix.infrastructure.usermanagement.web.dto;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -23,7 +24,7 @@ public record ErrorResponse(
|
||||||
int status,
|
int status,
|
||||||
|
|
||||||
@Schema(description = "Timestamp when error occurred")
|
@Schema(description = "Timestamp when error occurred")
|
||||||
LocalDateTime timestamp,
|
OffsetDateTime timestamp,
|
||||||
|
|
||||||
@Schema(description = "Request path where error occurred", example = "/api/users/user-123")
|
@Schema(description = "Request path where error occurred", example = "/api/users/user-123")
|
||||||
String path,
|
String path,
|
||||||
|
|
@ -44,7 +45,7 @@ public record ErrorResponse(
|
||||||
code,
|
code,
|
||||||
message,
|
message,
|
||||||
status,
|
status,
|
||||||
LocalDateTime.now(),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
path,
|
path,
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
|
@ -63,7 +64,7 @@ public record ErrorResponse(
|
||||||
"VALIDATION_ERROR",
|
"VALIDATION_ERROR",
|
||||||
message,
|
message,
|
||||||
status,
|
status,
|
||||||
LocalDateTime.now(),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
path,
|
path,
|
||||||
validationErrors
|
validationErrors
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package de.effigenix.infrastructure.usermanagement.web.dto;
|
||||||
import de.effigenix.application.usermanagement.dto.SessionToken;
|
import de.effigenix.application.usermanagement.dto.SessionToken;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Response DTO for successful login.
|
* Response DTO for successful login.
|
||||||
|
|
@ -25,7 +25,7 @@ public record LoginResponse(
|
||||||
long expiresIn,
|
long expiresIn,
|
||||||
|
|
||||||
@Schema(description = "Token expiration timestamp")
|
@Schema(description = "Token expiration timestamp")
|
||||||
LocalDateTime expiresAt,
|
OffsetDateTime expiresAt,
|
||||||
|
|
||||||
@Schema(description = "Refresh token for obtaining new access token")
|
@Schema(description = "Refresh token for obtaining new access token")
|
||||||
String refreshToken
|
String refreshToken
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,14 @@ public final class Quantity {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reconstitutes a Quantity from persistence. No validation.
|
* Reconstitutes a simple Quantity from persistence. No validation.
|
||||||
|
*/
|
||||||
|
public static Quantity reconstitute(BigDecimal amount, UnitOfMeasure uom) {
|
||||||
|
return new Quantity(amount, uom, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reconstitutes a dual Quantity (catch-weight) from persistence. No validation.
|
||||||
*/
|
*/
|
||||||
public static Quantity reconstitute(BigDecimal amount, UnitOfMeasure uom,
|
public static Quantity reconstitute(BigDecimal amount, UnitOfMeasure uom,
|
||||||
BigDecimal secondaryAmount, UnitOfMeasure secondaryUom) {
|
BigDecimal secondaryAmount, UnitOfMeasure secondaryUom) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<databaseChangeLog
|
||||||
|
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
|
||||||
|
|
||||||
|
<changeSet id="016-create-batch-number-sequences-table" author="effigenix">
|
||||||
|
<createTable tableName="batch_number_sequences">
|
||||||
|
<column name="production_date" type="DATE">
|
||||||
|
<constraints primaryKey="true" nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="last_sequence" type="INT" defaultValueNumeric="0">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
</createTable>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<databaseChangeLog
|
||||||
|
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
|
||||||
|
|
||||||
|
<changeSet id="017-timestamps-to-timestamptz" author="effigenix">
|
||||||
|
<comment>Migrate all TIMESTAMP columns to TIMESTAMP WITH TIME ZONE for consistent timezone handling</comment>
|
||||||
|
|
||||||
|
<!-- users -->
|
||||||
|
<sql>ALTER TABLE users ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE;</sql>
|
||||||
|
<sql>ALTER TABLE users ALTER COLUMN last_login TYPE TIMESTAMP WITH TIME ZONE;</sql>
|
||||||
|
|
||||||
|
<!-- audit_logs -->
|
||||||
|
<sql>ALTER TABLE audit_logs ALTER COLUMN timestamp TYPE TIMESTAMP WITH TIME ZONE;</sql>
|
||||||
|
<sql>ALTER TABLE audit_logs ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE;</sql>
|
||||||
|
|
||||||
|
<!-- articles -->
|
||||||
|
<sql>ALTER TABLE articles ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE;</sql>
|
||||||
|
<sql>ALTER TABLE articles ALTER COLUMN updated_at TYPE TIMESTAMP WITH TIME ZONE;</sql>
|
||||||
|
|
||||||
|
<!-- suppliers -->
|
||||||
|
<sql>ALTER TABLE suppliers ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE;</sql>
|
||||||
|
<sql>ALTER TABLE suppliers ALTER COLUMN updated_at TYPE TIMESTAMP WITH TIME ZONE;</sql>
|
||||||
|
|
||||||
|
<!-- customers -->
|
||||||
|
<sql>ALTER TABLE customers ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE;</sql>
|
||||||
|
<sql>ALTER TABLE customers ALTER COLUMN updated_at TYPE TIMESTAMP WITH TIME ZONE;</sql>
|
||||||
|
|
||||||
|
<!-- recipes -->
|
||||||
|
<sql>ALTER TABLE recipes ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE;</sql>
|
||||||
|
<sql>ALTER TABLE recipes ALTER COLUMN updated_at TYPE TIMESTAMP WITH TIME ZONE;</sql>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
|
|
@ -20,5 +20,7 @@
|
||||||
<include file="db/changelog/changes/013-create-stock-schema.xml"/>
|
<include file="db/changelog/changes/013-create-stock-schema.xml"/>
|
||||||
<include file="db/changelog/changes/014-create-stock-batches-table.xml"/>
|
<include file="db/changelog/changes/014-create-stock-batches-table.xml"/>
|
||||||
<include file="db/changelog/changes/015-create-batches-table.xml"/>
|
<include file="db/changelog/changes/015-create-batches-table.xml"/>
|
||||||
|
<include file="db/changelog/changes/016-create-batch-number-sequences-table.xml"/>
|
||||||
|
<include file="db/changelog/changes/017-timestamps-to-timestamptz.xml"/>
|
||||||
|
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ class AddStockBatchTest {
|
||||||
List.of(StockBatch.reconstitute(
|
List.of(StockBatch.reconstitute(
|
||||||
StockBatchId.generate(),
|
StockBatchId.generate(),
|
||||||
new BatchReference("BATCH-001", BatchType.PRODUCED),
|
new BatchReference("BATCH-001", BatchType.PRODUCED),
|
||||||
Quantity.reconstitute(new BigDecimal("5"), UnitOfMeasure.KILOGRAM, null, null),
|
Quantity.reconstitute(new BigDecimal("5"), UnitOfMeasure.KILOGRAM),
|
||||||
LocalDate.of(2026, 12, 31),
|
LocalDate.of(2026, 12, 31),
|
||||||
StockBatchStatus.AVAILABLE,
|
StockBatchStatus.AVAILABLE,
|
||||||
Instant.now()
|
Instant.now()
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ class RemoveStockBatchTest {
|
||||||
var batch = StockBatch.reconstitute(
|
var batch = StockBatch.reconstitute(
|
||||||
batchId,
|
batchId,
|
||||||
new BatchReference("BATCH-001", BatchType.PRODUCED),
|
new BatchReference("BATCH-001", BatchType.PRODUCED),
|
||||||
Quantity.reconstitute(new BigDecimal("10"), UnitOfMeasure.KILOGRAM, null, null),
|
Quantity.reconstitute(new BigDecimal("10"), UnitOfMeasure.KILOGRAM),
|
||||||
LocalDate.of(2026, 12, 31),
|
LocalDate.of(2026, 12, 31),
|
||||||
StockBatchStatus.AVAILABLE,
|
StockBatchStatus.AVAILABLE,
|
||||||
Instant.now()
|
Instant.now()
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@ import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
|
@ -61,7 +62,7 @@ class ActivateRecipeTest {
|
||||||
null, new YieldPercentage(85), 14,
|
null, new YieldPercentage(85), 14,
|
||||||
Quantity.of(new BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
Quantity.of(new BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
||||||
RecipeStatus.ACTIVE, List.of(), List.of(),
|
RecipeStatus.ACTIVE, List.of(), List.of(),
|
||||||
LocalDateTime.now(), LocalDateTime.now()
|
OffsetDateTime.now(ZoneOffset.UTC), OffsetDateTime.now(ZoneOffset.UTC)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@ import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
|
@ -45,7 +46,7 @@ class ArchiveRecipeTest {
|
||||||
null, new YieldPercentage(85), 14,
|
null, new YieldPercentage(85), 14,
|
||||||
Quantity.of(new BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
Quantity.of(new BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
||||||
RecipeStatus.ACTIVE, List.of(), List.of(),
|
RecipeStatus.ACTIVE, List.of(), List.of(),
|
||||||
LocalDateTime.now(), LocalDateTime.now()
|
OffsetDateTime.now(ZoneOffset.UTC), OffsetDateTime.now(ZoneOffset.UTC)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,7 +119,7 @@ class ArchiveRecipeTest {
|
||||||
null, new YieldPercentage(85), 14,
|
null, new YieldPercentage(85), 14,
|
||||||
Quantity.of(new BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
Quantity.of(new BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
||||||
RecipeStatus.ARCHIVED, List.of(), List.of(),
|
RecipeStatus.ARCHIVED, List.of(), List.of(),
|
||||||
LocalDateTime.now(), LocalDateTime.now()
|
OffsetDateTime.now(ZoneOffset.UTC), OffsetDateTime.now(ZoneOffset.UTC)
|
||||||
);
|
);
|
||||||
when(authPort.can(performedBy, ProductionAction.RECIPE_WRITE)).thenReturn(true);
|
when(authPort.can(performedBy, ProductionAction.RECIPE_WRITE)).thenReturn(true);
|
||||||
when(recipeRepository.findById(RecipeId.of("recipe-2"))).thenReturn(Result.success(Optional.of(recipe)));
|
when(recipeRepository.findById(RecipeId.of("recipe-2"))).thenReturn(Result.success(Optional.of(recipe)));
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@ import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
|
@ -43,7 +44,7 @@ class GetRecipeTest {
|
||||||
"Beschreibung", new YieldPercentage(85), 14,
|
"Beschreibung", new YieldPercentage(85), 14,
|
||||||
Quantity.of(new BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
Quantity.of(new BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
||||||
RecipeStatus.ACTIVE, List.of(), List.of(),
|
RecipeStatus.ACTIVE, List.of(), List.of(),
|
||||||
LocalDateTime.now(), LocalDateTime.now()
|
OffsetDateTime.now(ZoneOffset.UTC), OffsetDateTime.now(ZoneOffset.UTC)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@ import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
@ -42,7 +43,7 @@ class ListRecipesTest {
|
||||||
null, new YieldPercentage(85), 14,
|
null, new YieldPercentage(85), 14,
|
||||||
Quantity.of(new BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
Quantity.of(new BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
||||||
status, List.of(), List.of(),
|
status, List.of(), List.of(),
|
||||||
LocalDateTime.now(), LocalDateTime.now()
|
OffsetDateTime.now(ZoneOffset.UTC), OffsetDateTime.now(ZoneOffset.UTC)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,8 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
|
@ -57,7 +58,7 @@ class PlanBatchTest {
|
||||||
null, new YieldPercentage(85), 14,
|
null, new YieldPercentage(85), 14,
|
||||||
Quantity.of(new BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
Quantity.of(new BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
||||||
RecipeStatus.ACTIVE, List.of(), List.of(),
|
RecipeStatus.ACTIVE, List.of(), List.of(),
|
||||||
LocalDateTime.now(), LocalDateTime.now()
|
OffsetDateTime.now(ZoneOffset.UTC), OffsetDateTime.now(ZoneOffset.UTC)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,7 +68,7 @@ class PlanBatchTest {
|
||||||
null, new YieldPercentage(85), 14,
|
null, new YieldPercentage(85), 14,
|
||||||
Quantity.of(new BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
Quantity.of(new BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
||||||
RecipeStatus.DRAFT, List.of(), List.of(),
|
RecipeStatus.DRAFT, List.of(), List.of(),
|
||||||
LocalDateTime.now(), LocalDateTime.now()
|
OffsetDateTime.now(ZoneOffset.UTC), OffsetDateTime.now(ZoneOffset.UTC)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
|
@ -48,7 +49,7 @@ class RecipeCycleCheckerTest {
|
||||||
null, new YieldPercentage(100), 14,
|
null, new YieldPercentage(100), 14,
|
||||||
Quantity.of(new BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
Quantity.of(new BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
||||||
RecipeStatus.DRAFT, ingredients, List.of(),
|
RecipeStatus.DRAFT, ingredients, List.of(),
|
||||||
LocalDateTime.now(), LocalDateTime.now());
|
OffsetDateTime.now(ZoneOffset.UTC), OffsetDateTime.now(ZoneOffset.UTC));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Recipe recipeWithoutSubRecipes(String id) {
|
private Recipe recipeWithoutSubRecipes(String id) {
|
||||||
|
|
@ -145,7 +146,7 @@ class RecipeCycleCheckerTest {
|
||||||
null, new YieldPercentage(100), 14,
|
null, new YieldPercentage(100), 14,
|
||||||
Quantity.of(new BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
Quantity.of(new BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
||||||
RecipeStatus.DRAFT, ingredients, List.of(),
|
RecipeStatus.DRAFT, ingredients, List.of(),
|
||||||
LocalDateTime.now(), LocalDateTime.now());
|
OffsetDateTime.now(ZoneOffset.UTC), OffsetDateTime.now(ZoneOffset.UTC));
|
||||||
|
|
||||||
when(recipeRepository.findById(RecipeId.of("B"))).thenReturn(Result.success(Optional.of(recipeB)));
|
when(recipeRepository.findById(RecipeId.of("B"))).thenReturn(Result.success(Optional.of(recipeB)));
|
||||||
when(recipeRepository.findById(RecipeId.of("C"))).thenReturn(Result.success(Optional.of(recipeWithoutSubRecipes("C"))));
|
when(recipeRepository.findById(RecipeId.of("C"))).thenReturn(Result.success(Optional.of(recipeWithoutSubRecipes("C"))));
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
|
@ -42,7 +43,7 @@ class AssignRoleTest {
|
||||||
testUser = User.reconstitute(
|
testUser = User.reconstitute(
|
||||||
UserId.of("user-1"), "john.doe", "john@example.com",
|
UserId.of("user-1"), "john.doe", "john@example.com",
|
||||||
new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
|
new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
|
||||||
new HashSet<>(), "branch-1", UserStatus.ACTIVE, LocalDateTime.now(), null
|
new HashSet<>(), "branch-1", UserStatus.ACTIVE, OffsetDateTime.now(ZoneOffset.UTC), null
|
||||||
);
|
);
|
||||||
workerRole = Role.reconstitute(RoleId.generate(), RoleName.PRODUCTION_WORKER, new HashSet<>(), "Production Worker");
|
workerRole = Role.reconstitute(RoleId.generate(), RoleName.PRODUCTION_WORKER, new HashSet<>(), "Production Worker");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
|
@ -45,10 +46,10 @@ class AuthenticateUserTest {
|
||||||
|
|
||||||
testUser = User.reconstitute(
|
testUser = User.reconstitute(
|
||||||
UserId.of("user-1"), "john.doe", "john@example.com", validPasswordHash,
|
UserId.of("user-1"), "john.doe", "john@example.com", validPasswordHash,
|
||||||
new HashSet<>(), "branch-1", UserStatus.ACTIVE, LocalDateTime.now(), null
|
new HashSet<>(), "branch-1", UserStatus.ACTIVE, OffsetDateTime.now(ZoneOffset.UTC), null
|
||||||
);
|
);
|
||||||
|
|
||||||
sessionToken = new SessionToken("jwt-token", "Bearer", 3600L, LocalDateTime.now().plusSeconds(3600), "refresh-token");
|
sessionToken = new SessionToken("jwt-token", "Bearer", 3600L, OffsetDateTime.now(ZoneOffset.UTC).plusSeconds(3600), "refresh-token");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -84,7 +85,7 @@ class AuthenticateUserTest {
|
||||||
void should_FailWithLockedUser_When_UserStatusIsLocked() {
|
void should_FailWithLockedUser_When_UserStatusIsLocked() {
|
||||||
User lockedUser = User.reconstitute(
|
User lockedUser = User.reconstitute(
|
||||||
UserId.of("user-2"), "john.doe", "john@example.com", validPasswordHash,
|
UserId.of("user-2"), "john.doe", "john@example.com", validPasswordHash,
|
||||||
new HashSet<>(), "branch-1", UserStatus.LOCKED, LocalDateTime.now(), null
|
new HashSet<>(), "branch-1", UserStatus.LOCKED, OffsetDateTime.now(ZoneOffset.UTC), null
|
||||||
);
|
);
|
||||||
when(userRepository.findByUsername("john.doe")).thenReturn(Result.success(Optional.of(lockedUser)));
|
when(userRepository.findByUsername("john.doe")).thenReturn(Result.success(Optional.of(lockedUser)));
|
||||||
|
|
||||||
|
|
@ -100,7 +101,7 @@ class AuthenticateUserTest {
|
||||||
void should_FailWithInactiveUser_When_UserStatusIsInactive() {
|
void should_FailWithInactiveUser_When_UserStatusIsInactive() {
|
||||||
User inactiveUser = User.reconstitute(
|
User inactiveUser = User.reconstitute(
|
||||||
UserId.of("user-3"), "john.doe", "john@example.com", validPasswordHash,
|
UserId.of("user-3"), "john.doe", "john@example.com", validPasswordHash,
|
||||||
new HashSet<>(), "branch-1", UserStatus.INACTIVE, LocalDateTime.now(), null
|
new HashSet<>(), "branch-1", UserStatus.INACTIVE, OffsetDateTime.now(ZoneOffset.UTC), null
|
||||||
);
|
);
|
||||||
when(userRepository.findByUsername("john.doe")).thenReturn(Result.success(Optional.of(inactiveUser)));
|
when(userRepository.findByUsername("john.doe")).thenReturn(Result.success(Optional.of(inactiveUser)));
|
||||||
|
|
||||||
|
|
@ -143,7 +144,7 @@ class AuthenticateUserTest {
|
||||||
void should_NotCreateSession_When_UserLocked() {
|
void should_NotCreateSession_When_UserLocked() {
|
||||||
User lockedUser = User.reconstitute(
|
User lockedUser = User.reconstitute(
|
||||||
UserId.of("user-4"), "john.doe", "john@example.com", validPasswordHash,
|
UserId.of("user-4"), "john.doe", "john@example.com", validPasswordHash,
|
||||||
new HashSet<>(), "branch-1", UserStatus.LOCKED, LocalDateTime.now(), null
|
new HashSet<>(), "branch-1", UserStatus.LOCKED, OffsetDateTime.now(ZoneOffset.UTC), null
|
||||||
);
|
);
|
||||||
when(userRepository.findByUsername("john.doe")).thenReturn(Result.success(Optional.of(lockedUser)));
|
when(userRepository.findByUsername("john.doe")).thenReturn(Result.success(Optional.of(lockedUser)));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
|
@ -44,7 +45,7 @@ class ChangePasswordTest {
|
||||||
|
|
||||||
testUser = User.reconstitute(
|
testUser = User.reconstitute(
|
||||||
UserId.of("user-123"), "john.doe", "john@example.com", oldPasswordHash,
|
UserId.of("user-123"), "john.doe", "john@example.com", oldPasswordHash,
|
||||||
new HashSet<>(), "branch-1", UserStatus.ACTIVE, LocalDateTime.now(), null
|
new HashSet<>(), "branch-1", UserStatus.ACTIVE, OffsetDateTime.now(ZoneOffset.UTC), null
|
||||||
);
|
);
|
||||||
|
|
||||||
validCommand = new ChangePasswordCommand("user-123", "OldPassword123!", "NewPassword456!");
|
validCommand = new ChangePasswordCommand("user-123", "OldPassword123!", "NewPassword456!");
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
|
@ -37,7 +38,7 @@ class GetUserTest {
|
||||||
testUser = User.reconstitute(
|
testUser = User.reconstitute(
|
||||||
UserId.of("user-1"), "john.doe", "john@example.com",
|
UserId.of("user-1"), "john.doe", "john@example.com",
|
||||||
new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
|
new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
|
||||||
new HashSet<>(), "branch-1", UserStatus.ACTIVE, LocalDateTime.now(), null
|
new HashSet<>(), "branch-1", UserStatus.ACTIVE, OffsetDateTime.now(ZoneOffset.UTC), null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
@ -39,12 +40,12 @@ class ListUsersTest {
|
||||||
user1 = User.reconstitute(
|
user1 = User.reconstitute(
|
||||||
UserId.of("user-1"), "john.doe", "john@example.com",
|
UserId.of("user-1"), "john.doe", "john@example.com",
|
||||||
new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
|
new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
|
||||||
new HashSet<>(), "branch-1", UserStatus.ACTIVE, LocalDateTime.now(), null
|
new HashSet<>(), "branch-1", UserStatus.ACTIVE, OffsetDateTime.now(ZoneOffset.UTC), null
|
||||||
);
|
);
|
||||||
user2 = User.reconstitute(
|
user2 = User.reconstitute(
|
||||||
UserId.of("user-2"), "jane.doe", "jane@example.com",
|
UserId.of("user-2"), "jane.doe", "jane@example.com",
|
||||||
new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
|
new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
|
||||||
new HashSet<>(), "branch-1", UserStatus.ACTIVE, LocalDateTime.now(), null
|
new HashSet<>(), "branch-1", UserStatus.ACTIVE, OffsetDateTime.now(ZoneOffset.UTC), null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
|
@ -40,7 +41,7 @@ class LockUserTest {
|
||||||
activeUser = User.reconstitute(
|
activeUser = User.reconstitute(
|
||||||
UserId.of("user-1"), "john.doe", "john@example.com",
|
UserId.of("user-1"), "john.doe", "john@example.com",
|
||||||
new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
|
new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
|
||||||
new HashSet<>(), "branch-1", UserStatus.ACTIVE, LocalDateTime.now(), null
|
new HashSet<>(), "branch-1", UserStatus.ACTIVE, OffsetDateTime.now(ZoneOffset.UTC), null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -100,7 +101,7 @@ class LockUserTest {
|
||||||
User lockedUser = User.reconstitute(
|
User lockedUser = User.reconstitute(
|
||||||
UserId.of("user-2"), "jane.doe", "jane@example.com",
|
UserId.of("user-2"), "jane.doe", "jane@example.com",
|
||||||
new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
|
new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
|
||||||
new HashSet<>(), "branch-1", UserStatus.LOCKED, LocalDateTime.now(), null
|
new HashSet<>(), "branch-1", UserStatus.LOCKED, OffsetDateTime.now(ZoneOffset.UTC), null
|
||||||
);
|
);
|
||||||
when(authPort.can(performedBy, UserManagementAction.USER_LOCK)).thenReturn(true);
|
when(authPort.can(performedBy, UserManagementAction.USER_LOCK)).thenReturn(true);
|
||||||
when(userRepository.findById(UserId.of("user-2"))).thenReturn(Result.success(Optional.of(lockedUser)));
|
when(userRepository.findById(UserId.of("user-2"))).thenReturn(Result.success(Optional.of(lockedUser)));
|
||||||
|
|
@ -118,7 +119,7 @@ class LockUserTest {
|
||||||
User inactiveUser = User.reconstitute(
|
User inactiveUser = User.reconstitute(
|
||||||
UserId.of("user-3"), "bob", "bob@example.com",
|
UserId.of("user-3"), "bob", "bob@example.com",
|
||||||
new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
|
new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
|
||||||
new HashSet<>(), "branch-1", UserStatus.INACTIVE, LocalDateTime.now(), null
|
new HashSet<>(), "branch-1", UserStatus.INACTIVE, OffsetDateTime.now(ZoneOffset.UTC), null
|
||||||
);
|
);
|
||||||
when(authPort.can(performedBy, UserManagementAction.USER_LOCK)).thenReturn(true);
|
when(authPort.can(performedBy, UserManagementAction.USER_LOCK)).thenReturn(true);
|
||||||
when(userRepository.findById(UserId.of("user-3"))).thenReturn(Result.success(Optional.of(inactiveUser)));
|
when(userRepository.findById(UserId.of("user-3"))).thenReturn(Result.success(Optional.of(inactiveUser)));
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
@ -44,7 +45,7 @@ class RemoveRoleTest {
|
||||||
userWithRole = User.reconstitute(
|
userWithRole = User.reconstitute(
|
||||||
UserId.of("user-1"), "john.doe", "john@example.com",
|
UserId.of("user-1"), "john.doe", "john@example.com",
|
||||||
new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
|
new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
|
||||||
new HashSet<>(Set.of(workerRole)), "branch-1", UserStatus.ACTIVE, LocalDateTime.now(), null
|
new HashSet<>(Set.of(workerRole)), "branch-1", UserStatus.ACTIVE, OffsetDateTime.now(ZoneOffset.UTC), null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
|
@ -40,7 +41,7 @@ class UnlockUserTest {
|
||||||
lockedUser = User.reconstitute(
|
lockedUser = User.reconstitute(
|
||||||
UserId.of("user-1"), "john.doe", "john@example.com",
|
UserId.of("user-1"), "john.doe", "john@example.com",
|
||||||
new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
|
new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
|
||||||
new HashSet<>(), "branch-1", UserStatus.LOCKED, LocalDateTime.now(), null
|
new HashSet<>(), "branch-1", UserStatus.LOCKED, OffsetDateTime.now(ZoneOffset.UTC), null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -100,7 +101,7 @@ class UnlockUserTest {
|
||||||
User activeUser = User.reconstitute(
|
User activeUser = User.reconstitute(
|
||||||
UserId.of("user-2"), "jane.doe", "jane@example.com",
|
UserId.of("user-2"), "jane.doe", "jane@example.com",
|
||||||
new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
|
new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
|
||||||
new HashSet<>(), "branch-1", UserStatus.ACTIVE, LocalDateTime.now(), null
|
new HashSet<>(), "branch-1", UserStatus.ACTIVE, OffsetDateTime.now(ZoneOffset.UTC), null
|
||||||
);
|
);
|
||||||
when(authPort.can(performedBy, UserManagementAction.USER_UNLOCK)).thenReturn(true);
|
when(authPort.can(performedBy, UserManagementAction.USER_UNLOCK)).thenReturn(true);
|
||||||
when(userRepository.findById(UserId.of("user-2"))).thenReturn(Result.success(Optional.of(activeUser)));
|
when(userRepository.findById(UserId.of("user-2"))).thenReturn(Result.success(Optional.of(activeUser)));
|
||||||
|
|
@ -118,7 +119,7 @@ class UnlockUserTest {
|
||||||
User inactiveUser = User.reconstitute(
|
User inactiveUser = User.reconstitute(
|
||||||
UserId.of("user-3"), "bob", "bob@example.com",
|
UserId.of("user-3"), "bob", "bob@example.com",
|
||||||
new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
|
new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
|
||||||
new HashSet<>(), "branch-1", UserStatus.INACTIVE, LocalDateTime.now(), null
|
new HashSet<>(), "branch-1", UserStatus.INACTIVE, OffsetDateTime.now(ZoneOffset.UTC), null
|
||||||
);
|
);
|
||||||
when(authPort.can(performedBy, UserManagementAction.USER_UNLOCK)).thenReturn(true);
|
when(authPort.can(performedBy, UserManagementAction.USER_UNLOCK)).thenReturn(true);
|
||||||
when(userRepository.findById(UserId.of("user-3"))).thenReturn(Result.success(Optional.of(inactiveUser)));
|
when(userRepository.findById(UserId.of("user-3"))).thenReturn(Result.success(Optional.of(inactiveUser)));
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
|
@ -40,7 +41,7 @@ class UpdateUserTest {
|
||||||
testUser = User.reconstitute(
|
testUser = User.reconstitute(
|
||||||
UserId.of("user-1"), "john.doe", "john@example.com",
|
UserId.of("user-1"), "john.doe", "john@example.com",
|
||||||
new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
|
new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
|
||||||
new HashSet<>(), "branch-1", UserStatus.ACTIVE, LocalDateTime.now(), null
|
new HashSet<>(), "branch-1", UserStatus.ACTIVE, OffsetDateTime.now(ZoneOffset.UTC), null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -266,7 +266,7 @@ class StockTest {
|
||||||
var id = StockId.generate();
|
var id = StockId.generate();
|
||||||
var articleId = ArticleId.of("article-1");
|
var articleId = ArticleId.of("article-1");
|
||||||
var locationId = StorageLocationId.of("location-1");
|
var locationId = StorageLocationId.of("location-1");
|
||||||
var quantity = Quantity.reconstitute(new BigDecimal("10"), UnitOfMeasure.KILOGRAM, null, null);
|
var quantity = Quantity.reconstitute(new BigDecimal("10"), UnitOfMeasure.KILOGRAM);
|
||||||
var minimumLevel = new MinimumLevel(quantity);
|
var minimumLevel = new MinimumLevel(quantity);
|
||||||
var minimumShelfLife = new MinimumShelfLife(30);
|
var minimumShelfLife = new MinimumShelfLife(30);
|
||||||
|
|
||||||
|
|
@ -606,7 +606,7 @@ class StockTest {
|
||||||
var batch = StockBatch.reconstitute(
|
var batch = StockBatch.reconstitute(
|
||||||
StockBatchId.generate(),
|
StockBatchId.generate(),
|
||||||
new BatchReference("BATCH-001", BatchType.PRODUCED),
|
new BatchReference("BATCH-001", BatchType.PRODUCED),
|
||||||
Quantity.reconstitute(new BigDecimal(amount), uom, null, null),
|
Quantity.reconstitute(new BigDecimal(amount), uom),
|
||||||
LocalDate.of(2026, 12, 31),
|
LocalDate.of(2026, 12, 31),
|
||||||
status,
|
status,
|
||||||
Instant.now()
|
Instant.now()
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
|
@ -187,8 +188,8 @@ class BatchTest {
|
||||||
Quantity.of(new BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
Quantity.of(new BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
||||||
PRODUCTION_DATE,
|
PRODUCTION_DATE,
|
||||||
BEST_BEFORE_DATE,
|
BEST_BEFORE_DATE,
|
||||||
LocalDateTime.now(),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
LocalDateTime.now()
|
OffsetDateTime.now(ZoneOffset.UTC)
|
||||||
);
|
);
|
||||||
|
|
||||||
assertThat(batch.id().value()).isEqualTo("batch-1");
|
assertThat(batch.id().value()).isEqualTo("batch-1");
|
||||||
|
|
|
||||||
|
|
@ -239,7 +239,7 @@ class RecipeTest {
|
||||||
null, new YieldPercentage(85), 14,
|
null, new YieldPercentage(85), 14,
|
||||||
Quantity.of(new java.math.BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
Quantity.of(new java.math.BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
||||||
RecipeStatus.ACTIVE, List.of(), List.of(),
|
RecipeStatus.ACTIVE, List.of(), List.of(),
|
||||||
java.time.LocalDateTime.now(), java.time.LocalDateTime.now()
|
java.time.OffsetDateTime.now(java.time.ZoneOffset.UTC), java.time.OffsetDateTime.now(java.time.ZoneOffset.UTC)
|
||||||
);
|
);
|
||||||
|
|
||||||
var result = recipe.addIngredient(validIngredientDraft(1));
|
var result = recipe.addIngredient(validIngredientDraft(1));
|
||||||
|
|
@ -322,7 +322,7 @@ class RecipeTest {
|
||||||
null, new YieldPercentage(85), 14,
|
null, new YieldPercentage(85), 14,
|
||||||
Quantity.of(new java.math.BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
Quantity.of(new java.math.BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
||||||
RecipeStatus.ACTIVE, List.of(), List.of(),
|
RecipeStatus.ACTIVE, List.of(), List.of(),
|
||||||
java.time.LocalDateTime.now(), java.time.LocalDateTime.now()
|
java.time.OffsetDateTime.now(java.time.ZoneOffset.UTC), java.time.OffsetDateTime.now(java.time.ZoneOffset.UTC)
|
||||||
);
|
);
|
||||||
|
|
||||||
var result = recipe.removeIngredient(IngredientId.generate());
|
var result = recipe.removeIngredient(IngredientId.generate());
|
||||||
|
|
@ -376,7 +376,7 @@ class RecipeTest {
|
||||||
null, new YieldPercentage(85), 14,
|
null, new YieldPercentage(85), 14,
|
||||||
Quantity.of(new java.math.BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
Quantity.of(new java.math.BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
||||||
RecipeStatus.ACTIVE, List.of(), List.of(),
|
RecipeStatus.ACTIVE, List.of(), List.of(),
|
||||||
java.time.LocalDateTime.now(), java.time.LocalDateTime.now()
|
java.time.OffsetDateTime.now(java.time.ZoneOffset.UTC), java.time.OffsetDateTime.now(java.time.ZoneOffset.UTC)
|
||||||
);
|
);
|
||||||
|
|
||||||
var result = recipe.addProductionStep(new ProductionStepDraft(1, "Mischen", null, null));
|
var result = recipe.addProductionStep(new ProductionStepDraft(1, "Mischen", null, null));
|
||||||
|
|
@ -445,7 +445,7 @@ class RecipeTest {
|
||||||
null, new YieldPercentage(85), 14,
|
null, new YieldPercentage(85), 14,
|
||||||
Quantity.of(new java.math.BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
Quantity.of(new java.math.BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
||||||
RecipeStatus.ACTIVE, List.of(), List.of(),
|
RecipeStatus.ACTIVE, List.of(), List.of(),
|
||||||
java.time.LocalDateTime.now(), java.time.LocalDateTime.now()
|
java.time.OffsetDateTime.now(java.time.ZoneOffset.UTC), java.time.OffsetDateTime.now(java.time.ZoneOffset.UTC)
|
||||||
);
|
);
|
||||||
|
|
||||||
var result = recipe.removeProductionStep(1);
|
var result = recipe.removeProductionStep(1);
|
||||||
|
|
@ -507,7 +507,7 @@ class RecipeTest {
|
||||||
null, new YieldPercentage(85), 14,
|
null, new YieldPercentage(85), 14,
|
||||||
Quantity.of(new java.math.BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
Quantity.of(new java.math.BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
||||||
RecipeStatus.ACTIVE, List.of(), List.of(),
|
RecipeStatus.ACTIVE, List.of(), List.of(),
|
||||||
java.time.LocalDateTime.now(), java.time.LocalDateTime.now()
|
java.time.OffsetDateTime.now(java.time.ZoneOffset.UTC), java.time.OffsetDateTime.now(java.time.ZoneOffset.UTC)
|
||||||
);
|
);
|
||||||
|
|
||||||
var result = recipe.activate();
|
var result = recipe.activate();
|
||||||
|
|
@ -528,7 +528,7 @@ class RecipeTest {
|
||||||
null, new YieldPercentage(85), 14,
|
null, new YieldPercentage(85), 14,
|
||||||
Quantity.of(new java.math.BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
Quantity.of(new java.math.BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
||||||
RecipeStatus.ARCHIVED, List.of(), List.of(),
|
RecipeStatus.ARCHIVED, List.of(), List.of(),
|
||||||
java.time.LocalDateTime.now(), java.time.LocalDateTime.now()
|
java.time.OffsetDateTime.now(java.time.ZoneOffset.UTC), java.time.OffsetDateTime.now(java.time.ZoneOffset.UTC)
|
||||||
);
|
);
|
||||||
|
|
||||||
var result = recipe.activate();
|
var result = recipe.activate();
|
||||||
|
|
@ -554,7 +554,7 @@ class RecipeTest {
|
||||||
null, new YieldPercentage(85), 14,
|
null, new YieldPercentage(85), 14,
|
||||||
Quantity.of(new java.math.BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
Quantity.of(new java.math.BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
||||||
RecipeStatus.ACTIVE, List.of(), List.of(),
|
RecipeStatus.ACTIVE, List.of(), List.of(),
|
||||||
java.time.LocalDateTime.now(), java.time.LocalDateTime.now()
|
java.time.OffsetDateTime.now(java.time.ZoneOffset.UTC), java.time.OffsetDateTime.now(java.time.ZoneOffset.UTC)
|
||||||
);
|
);
|
||||||
var updatedBefore = recipe.updatedAt();
|
var updatedBefore = recipe.updatedAt();
|
||||||
|
|
||||||
|
|
@ -588,7 +588,7 @@ class RecipeTest {
|
||||||
null, new YieldPercentage(85), 14,
|
null, new YieldPercentage(85), 14,
|
||||||
Quantity.of(new java.math.BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
Quantity.of(new java.math.BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
||||||
RecipeStatus.ARCHIVED, List.of(), List.of(),
|
RecipeStatus.ARCHIVED, List.of(), List.of(),
|
||||||
java.time.LocalDateTime.now(), java.time.LocalDateTime.now()
|
java.time.OffsetDateTime.now(java.time.ZoneOffset.UTC), java.time.OffsetDateTime.now(java.time.ZoneOffset.UTC)
|
||||||
);
|
);
|
||||||
|
|
||||||
var result = recipe.archive();
|
var result = recipe.archive();
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.ValueSource;
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.*;
|
import static org.assertj.core.api.Assertions.*;
|
||||||
|
|
@ -25,7 +26,7 @@ class UserTest {
|
||||||
private PasswordHash passwordHash;
|
private PasswordHash passwordHash;
|
||||||
private Set<Role> roles;
|
private Set<Role> roles;
|
||||||
private String branchId;
|
private String branchId;
|
||||||
private LocalDateTime createdAt;
|
private OffsetDateTime createdAt;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
|
|
@ -35,7 +36,7 @@ class UserTest {
|
||||||
passwordHash = new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW");
|
passwordHash = new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW");
|
||||||
roles = new HashSet<>();
|
roles = new HashSet<>();
|
||||||
branchId = "branch-1";
|
branchId = "branch-1";
|
||||||
createdAt = LocalDateTime.now();
|
createdAt = OffsetDateTime.now(ZoneOffset.UTC);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -56,9 +57,9 @@ class UserTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("should_SetDefaultCreatedAtToNow_When_NullProvidedForCreatedAt")
|
@DisplayName("should_SetDefaultCreatedAtToNow_When_NullProvidedForCreatedAt")
|
||||||
void should_SetDefaultCreatedAtToNow_When_NullProvidedForCreatedAt() {
|
void should_SetDefaultCreatedAtToNow_When_NullProvidedForCreatedAt() {
|
||||||
LocalDateTime before = LocalDateTime.now();
|
OffsetDateTime before = OffsetDateTime.now(ZoneOffset.UTC);
|
||||||
User user = User.reconstitute(userId, username, email, passwordHash, roles, branchId, UserStatus.ACTIVE, null, null);
|
User user = User.reconstitute(userId, username, email, passwordHash, roles, branchId, UserStatus.ACTIVE, null, null);
|
||||||
LocalDateTime after = LocalDateTime.now();
|
OffsetDateTime after = OffsetDateTime.now(ZoneOffset.UTC);
|
||||||
|
|
||||||
assertThat(user.createdAt()).isNotNull();
|
assertThat(user.createdAt()).isNotNull();
|
||||||
assertThat(user.createdAt()).isBetween(before, after);
|
assertThat(user.createdAt()).isBetween(before, after);
|
||||||
|
|
@ -129,7 +130,7 @@ class UserTest {
|
||||||
@DisplayName("should_ReturnNewUserWithLastLogin_When_WithLastLoginCalled")
|
@DisplayName("should_ReturnNewUserWithLastLogin_When_WithLastLoginCalled")
|
||||||
void should_ReturnNewUserWithLastLogin_When_WithLastLoginCalled() {
|
void should_ReturnNewUserWithLastLogin_When_WithLastLoginCalled() {
|
||||||
User user = User.create(username, email, passwordHash, roles, branchId).unsafeGetValue();
|
User user = User.create(username, email, passwordHash, roles, branchId).unsafeGetValue();
|
||||||
LocalDateTime now = LocalDateTime.now();
|
OffsetDateTime now = OffsetDateTime.now(ZoneOffset.UTC);
|
||||||
|
|
||||||
User updated = user.withLastLogin(now).unsafeGetValue();
|
User updated = user.withLastLogin(now).unsafeGetValue();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,8 @@ import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
@ -102,7 +103,7 @@ public abstract class AbstractIntegrationTest {
|
||||||
UserEntity user = new UserEntity(
|
UserEntity user = new UserEntity(
|
||||||
UUID.randomUUID().toString(), username, email,
|
UUID.randomUUID().toString(), username, email,
|
||||||
BCRYPT_PASS123, roles,
|
BCRYPT_PASS123, roles,
|
||||||
branchId, UserStatus.ACTIVE, LocalDateTime.now(), null);
|
branchId, UserStatus.ACTIVE, OffsetDateTime.now(ZoneOffset.UTC), null);
|
||||||
return userRepository.save(user);
|
return userRepository.save(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@ import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
@ -29,11 +30,11 @@ class UserMapperTest {
|
||||||
|
|
||||||
private User domainUser;
|
private User domainUser;
|
||||||
private UserEntity jpaEntity;
|
private UserEntity jpaEntity;
|
||||||
private LocalDateTime createdAt;
|
private OffsetDateTime createdAt;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
createdAt = LocalDateTime.now();
|
createdAt = OffsetDateTime.now(ZoneOffset.UTC);
|
||||||
|
|
||||||
// Create JPA entity first
|
// Create JPA entity first
|
||||||
jpaEntity = new UserEntity(
|
jpaEntity = new UserEntity(
|
||||||
|
|
@ -112,7 +113,7 @@ class UserMapperTest {
|
||||||
@DisplayName("should_PreserveAllUserFields_When_MappingToEntity")
|
@DisplayName("should_PreserveAllUserFields_When_MappingToEntity")
|
||||||
void should_PreserveAllUserFields_When_MappingToEntity() {
|
void should_PreserveAllUserFields_When_MappingToEntity() {
|
||||||
// Arrange
|
// Arrange
|
||||||
LocalDateTime lastLogin = LocalDateTime.now();
|
OffsetDateTime lastLogin = OffsetDateTime.now(ZoneOffset.UTC);
|
||||||
UserEntity sourceEntity = new UserEntity(
|
UserEntity sourceEntity = new UserEntity(
|
||||||
"user-456",
|
"user-456",
|
||||||
"jane.smith",
|
"jane.smith",
|
||||||
|
|
@ -144,7 +145,7 @@ class UserMapperTest {
|
||||||
@DisplayName("should_PreserveAllEntityFields_When_MappingToDomain")
|
@DisplayName("should_PreserveAllEntityFields_When_MappingToDomain")
|
||||||
void should_PreserveAllEntityFields_When_MappingToDomain() {
|
void should_PreserveAllEntityFields_When_MappingToDomain() {
|
||||||
// Arrange
|
// Arrange
|
||||||
LocalDateTime lastLogin = LocalDateTime.now();
|
OffsetDateTime lastLogin = OffsetDateTime.now(ZoneOffset.UTC);
|
||||||
UserEntity entityWithLastLogin = new UserEntity(
|
UserEntity entityWithLastLogin = new UserEntity(
|
||||||
"user-789",
|
"user-789",
|
||||||
"bob.jones",
|
"bob.jones",
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.test.web.servlet.MvcResult;
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
@ -327,7 +328,7 @@ class SecurityIntegrationTest extends AbstractIntegrationTest {
|
||||||
.orElseThrow();
|
.orElseThrow();
|
||||||
|
|
||||||
assertThat(auditLog.getTimestamp()).isNotNull();
|
assertThat(auditLog.getTimestamp()).isNotNull();
|
||||||
assertThat(auditLog.getTimestamp()).isAfter(LocalDateTime.now().minusMinutes(1));
|
assertThat(auditLog.getTimestamp()).isAfter(OffsetDateTime.now(ZoneOffset.UTC).minusMinutes(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue