diff --git a/backend/src/main/java/de/effigenix/application/masterdata/ActivateArticle.java b/backend/src/main/java/de/effigenix/application/masterdata/ActivateArticle.java index a340612..43d3eb7 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/ActivateArticle.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/ActivateArticle.java @@ -2,18 +2,19 @@ package de.effigenix.application.masterdata; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; import static de.effigenix.shared.common.Result.*; -@Transactional public class ActivateArticle { private final ArticleRepository articleRepository; + private final UnitOfWork unitOfWork; - public ActivateArticle(ArticleRepository articleRepository) { + public ActivateArticle(ArticleRepository articleRepository, UnitOfWork unitOfWork) { this.articleRepository = articleRepository; + this.unitOfWork = unitOfWork; } public Result execute(ArticleId articleId, ActorId performedBy) { @@ -30,12 +31,13 @@ public class ActivateArticle { } article.activate(); - switch (articleRepository.save(article)) { - case Failure(var err) -> - { return Result.failure(new ArticleError.RepositoryFailure(err.message())); } - case Success(var ignored) -> { } - } - - return Result.success(article); + return unitOfWork.executeAtomically(() -> { + switch (articleRepository.save(article)) { + case Failure(var err) -> + { return Result.failure(new ArticleError.RepositoryFailure(err.message())); } + case Success(var ignored) -> { } + } + return Result.success(article); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/ActivateCustomer.java b/backend/src/main/java/de/effigenix/application/masterdata/ActivateCustomer.java index e014b39..ed207a7 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/ActivateCustomer.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/ActivateCustomer.java @@ -2,18 +2,19 @@ package de.effigenix.application.masterdata; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; import static de.effigenix.shared.common.Result.*; -@Transactional public class ActivateCustomer { private final CustomerRepository customerRepository; + private final UnitOfWork unitOfWork; - public ActivateCustomer(CustomerRepository customerRepository) { + public ActivateCustomer(CustomerRepository customerRepository, UnitOfWork unitOfWork) { this.customerRepository = customerRepository; + this.unitOfWork = unitOfWork; } public Result execute(CustomerId customerId, ActorId performedBy) { @@ -30,12 +31,13 @@ public class ActivateCustomer { } customer.activate(); - switch (customerRepository.save(customer)) { - case Failure(var err) -> - { return Result.failure(new CustomerError.RepositoryFailure(err.message())); } - case Success(var ignored) -> { } - } - - return Result.success(customer); + return unitOfWork.executeAtomically(() -> { + switch (customerRepository.save(customer)) { + case Failure(var err) -> + { return Result.failure(new CustomerError.RepositoryFailure(err.message())); } + case Success(var ignored) -> { } + } + return Result.success(customer); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/ActivateSupplier.java b/backend/src/main/java/de/effigenix/application/masterdata/ActivateSupplier.java index 13c5ec5..7bc92ff 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/ActivateSupplier.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/ActivateSupplier.java @@ -2,18 +2,19 @@ package de.effigenix.application.masterdata; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; import static de.effigenix.shared.common.Result.*; -@Transactional public class ActivateSupplier { private final SupplierRepository supplierRepository; + private final UnitOfWork unitOfWork; - public ActivateSupplier(SupplierRepository supplierRepository) { + public ActivateSupplier(SupplierRepository supplierRepository, UnitOfWork unitOfWork) { this.supplierRepository = supplierRepository; + this.unitOfWork = unitOfWork; } public Result execute(SupplierId supplierId, ActorId performedBy) { @@ -30,12 +31,13 @@ public class ActivateSupplier { } supplier.activate(); - switch (supplierRepository.save(supplier)) { - case Failure(var err) -> - { return Result.failure(new SupplierError.RepositoryFailure(err.message())); } - case Success(var ignored) -> { } - } - - return Result.success(supplier); + return unitOfWork.executeAtomically(() -> { + switch (supplierRepository.save(supplier)) { + case Failure(var err) -> + { return Result.failure(new SupplierError.RepositoryFailure(err.message())); } + case Success(var ignored) -> { } + } + return Result.success(supplier); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/AddCertificate.java b/backend/src/main/java/de/effigenix/application/masterdata/AddCertificate.java index dfde62a..3d56898 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/AddCertificate.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/AddCertificate.java @@ -3,18 +3,19 @@ package de.effigenix.application.masterdata; import de.effigenix.application.masterdata.command.AddCertificateCommand; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; import static de.effigenix.shared.common.Result.*; -@Transactional public class AddCertificate { private final SupplierRepository supplierRepository; + private final UnitOfWork unitOfWork; - public AddCertificate(SupplierRepository supplierRepository) { + public AddCertificate(SupplierRepository supplierRepository, UnitOfWork unitOfWork) { this.supplierRepository = supplierRepository; + this.unitOfWork = unitOfWork; } public Result execute(AddCertificateCommand cmd, ActorId performedBy) { @@ -39,12 +40,13 @@ public class AddCertificate { } supplier.addCertificate(certificate); - switch (supplierRepository.save(supplier)) { - case Failure(var err) -> - { return Result.failure(new SupplierError.RepositoryFailure(err.message())); } - case Success(var ignored) -> { } - } - - return Result.success(supplier); + return unitOfWork.executeAtomically(() -> { + switch (supplierRepository.save(supplier)) { + case Failure(var err) -> + { return Result.failure(new SupplierError.RepositoryFailure(err.message())); } + case Success(var ignored) -> { } + } + return Result.success(supplier); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/AddDeliveryAddress.java b/backend/src/main/java/de/effigenix/application/masterdata/AddDeliveryAddress.java index 7559310..138716b 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/AddDeliveryAddress.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/AddDeliveryAddress.java @@ -4,18 +4,19 @@ import de.effigenix.application.masterdata.command.AddDeliveryAddressCommand; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Address; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; import static de.effigenix.shared.common.Result.*; -@Transactional public class AddDeliveryAddress { private final CustomerRepository customerRepository; + private final UnitOfWork unitOfWork; - public AddDeliveryAddress(CustomerRepository customerRepository) { + public AddDeliveryAddress(CustomerRepository customerRepository, UnitOfWork unitOfWork) { this.customerRepository = customerRepository; + this.unitOfWork = unitOfWork; } public Result execute(AddDeliveryAddressCommand cmd, ActorId performedBy) { @@ -46,12 +47,13 @@ public class AddDeliveryAddress { } customer.addDeliveryAddress(deliveryAddress); - switch (customerRepository.save(customer)) { - case Failure(var err) -> - { return Result.failure(new CustomerError.RepositoryFailure(err.message())); } - case Success(var ignored) -> { } - } - - return Result.success(customer); + return unitOfWork.executeAtomically(() -> { + switch (customerRepository.save(customer)) { + case Failure(var err) -> + { return Result.failure(new CustomerError.RepositoryFailure(err.message())); } + case Success(var ignored) -> { } + } + return Result.success(customer); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/AddSalesUnit.java b/backend/src/main/java/de/effigenix/application/masterdata/AddSalesUnit.java index 4bf6338..b0184ea 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/AddSalesUnit.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/AddSalesUnit.java @@ -4,18 +4,19 @@ import de.effigenix.application.masterdata.command.AddSalesUnitCommand; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Money; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; import static de.effigenix.shared.common.Result.*; -@Transactional public class AddSalesUnit { private final ArticleRepository articleRepository; + private final UnitOfWork unitOfWork; - public AddSalesUnit(ArticleRepository articleRepository) { + public AddSalesUnit(ArticleRepository articleRepository, UnitOfWork unitOfWork) { this.articleRepository = articleRepository; + this.unitOfWork = unitOfWork; } public Result execute(AddSalesUnitCommand cmd, ActorId performedBy) { @@ -50,12 +51,13 @@ public class AddSalesUnit { case Success(var ignored) -> { } } - switch (articleRepository.save(article)) { - case Failure(var err) -> - { return Result.failure(new ArticleError.RepositoryFailure(err.message())); } - case Success(var ignored) -> { } - } - - return Result.success(article); + return unitOfWork.executeAtomically(() -> { + switch (articleRepository.save(article)) { + case Failure(var err) -> + { return Result.failure(new ArticleError.RepositoryFailure(err.message())); } + case Success(var ignored) -> { } + } + return Result.success(article); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/AssignSupplier.java b/backend/src/main/java/de/effigenix/application/masterdata/AssignSupplier.java index 728f318..e85e785 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/AssignSupplier.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/AssignSupplier.java @@ -3,18 +3,19 @@ package de.effigenix.application.masterdata; import de.effigenix.application.masterdata.command.AssignSupplierCommand; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; import static de.effigenix.shared.common.Result.*; -@Transactional public class AssignSupplier { private final ArticleRepository articleRepository; + private final UnitOfWork unitOfWork; - public AssignSupplier(ArticleRepository articleRepository) { + public AssignSupplier(ArticleRepository articleRepository, UnitOfWork unitOfWork) { this.articleRepository = articleRepository; + this.unitOfWork = unitOfWork; } public Result execute(AssignSupplierCommand cmd, ActorId performedBy) { @@ -33,12 +34,13 @@ public class AssignSupplier { } article.addSupplierReference(SupplierId.of(cmd.supplierId())); - switch (articleRepository.save(article)) { - case Failure(var err) -> - { return Result.failure(new ArticleError.RepositoryFailure(err.message())); } - case Success(var ignored) -> { } - } - - return Result.success(article); + return unitOfWork.executeAtomically(() -> { + switch (articleRepository.save(article)) { + case Failure(var err) -> + { return Result.failure(new ArticleError.RepositoryFailure(err.message())); } + case Success(var ignored) -> { } + } + return Result.success(article); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/CreateArticle.java b/backend/src/main/java/de/effigenix/application/masterdata/CreateArticle.java index 0db75e7..8fc9f49 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/CreateArticle.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/CreateArticle.java @@ -3,16 +3,17 @@ package de.effigenix.application.masterdata; import de.effigenix.application.masterdata.command.CreateArticleCommand; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; -@Transactional public class CreateArticle { private final ArticleRepository articleRepository; + private final UnitOfWork unitOfWork; - public CreateArticle(ArticleRepository articleRepository) { + public CreateArticle(ArticleRepository articleRepository, UnitOfWork unitOfWork) { this.articleRepository = articleRepository; + this.unitOfWork = unitOfWork; } public Result execute(CreateArticleCommand cmd, ActorId performedBy) { @@ -37,12 +38,13 @@ public class CreateArticle { } } - switch (articleRepository.save(article)) { - case Result.Failure(var err) -> - { return Result.failure(new ArticleError.RepositoryFailure(err.message())); } - case Result.Success(var ignored) -> { } - } - - return Result.success(article); + return unitOfWork.executeAtomically(() -> { + switch (articleRepository.save(article)) { + case Result.Failure(var err) -> + { return Result.failure(new ArticleError.RepositoryFailure(err.message())); } + case Result.Success(var ignored) -> { } + } + return Result.success(article); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/CreateCustomer.java b/backend/src/main/java/de/effigenix/application/masterdata/CreateCustomer.java index 213a964..2758772 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/CreateCustomer.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/CreateCustomer.java @@ -3,16 +3,17 @@ package de.effigenix.application.masterdata; import de.effigenix.application.masterdata.command.CreateCustomerCommand; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; -@Transactional public class CreateCustomer { private final CustomerRepository customerRepository; + private final UnitOfWork unitOfWork; - public CreateCustomer(CustomerRepository customerRepository) { + public CreateCustomer(CustomerRepository customerRepository, UnitOfWork unitOfWork) { this.customerRepository = customerRepository; + this.unitOfWork = unitOfWork; } public Result execute(CreateCustomerCommand cmd, ActorId performedBy) { @@ -38,12 +39,13 @@ public class CreateCustomer { } } - switch (customerRepository.save(customer)) { - case Result.Failure(var err) -> - { return Result.failure(new CustomerError.RepositoryFailure(err.message())); } - case Result.Success(var ignored) -> { } - } - - return Result.success(customer); + return unitOfWork.executeAtomically(() -> { + switch (customerRepository.save(customer)) { + case Result.Failure(var err) -> + { return Result.failure(new CustomerError.RepositoryFailure(err.message())); } + case Result.Success(var ignored) -> { } + } + return Result.success(customer); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/CreateProductCategory.java b/backend/src/main/java/de/effigenix/application/masterdata/CreateProductCategory.java index 87e8781..3736448 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/CreateProductCategory.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/CreateProductCategory.java @@ -3,16 +3,17 @@ package de.effigenix.application.masterdata; import de.effigenix.application.masterdata.command.CreateProductCategoryCommand; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; -@Transactional public class CreateProductCategory { private final ProductCategoryRepository categoryRepository; + private final UnitOfWork unitOfWork; - public CreateProductCategory(ProductCategoryRepository categoryRepository) { + public CreateProductCategory(ProductCategoryRepository categoryRepository, UnitOfWork unitOfWork) { this.categoryRepository = categoryRepository; + this.unitOfWork = unitOfWork; } public Result execute(CreateProductCategoryCommand cmd, ActorId performedBy) { @@ -34,12 +35,13 @@ public class CreateProductCategory { } } - switch (categoryRepository.save(category)) { - case Result.Failure(var err) -> - { return Result.failure(new ProductCategoryError.RepositoryFailure(err.message())); } - case Result.Success(var ignored) -> { } - } - - return Result.success(category); + return unitOfWork.executeAtomically(() -> { + switch (categoryRepository.save(category)) { + case Result.Failure(var err) -> + { return Result.failure(new ProductCategoryError.RepositoryFailure(err.message())); } + case Result.Success(var ignored) -> { } + } + return Result.success(category); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/CreateSupplier.java b/backend/src/main/java/de/effigenix/application/masterdata/CreateSupplier.java index f16ef0d..5b80f01 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/CreateSupplier.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/CreateSupplier.java @@ -3,16 +3,17 @@ package de.effigenix.application.masterdata; import de.effigenix.application.masterdata.command.CreateSupplierCommand; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; -@Transactional public class CreateSupplier { private final SupplierRepository supplierRepository; + private final UnitOfWork unitOfWork; - public CreateSupplier(SupplierRepository supplierRepository) { + public CreateSupplier(SupplierRepository supplierRepository, UnitOfWork unitOfWork) { this.supplierRepository = supplierRepository; + this.unitOfWork = unitOfWork; } public Result execute(CreateSupplierCommand cmd, ActorId performedBy) { @@ -42,12 +43,13 @@ public class CreateSupplier { } // 4. Speichern - switch (supplierRepository.save(supplier)) { - case Result.Failure(var err) -> - { return Result.failure(new SupplierError.RepositoryFailure(err.message())); } - case Result.Success(var ignored) -> { } - } - - return Result.success(supplier); + return unitOfWork.executeAtomically(() -> { + switch (supplierRepository.save(supplier)) { + case Result.Failure(var err) -> + { return Result.failure(new SupplierError.RepositoryFailure(err.message())); } + case Result.Success(var ignored) -> { } + } + return Result.success(supplier); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/DeactivateArticle.java b/backend/src/main/java/de/effigenix/application/masterdata/DeactivateArticle.java index 1a909c2..227ff10 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/DeactivateArticle.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/DeactivateArticle.java @@ -2,18 +2,19 @@ package de.effigenix.application.masterdata; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; import static de.effigenix.shared.common.Result.*; -@Transactional public class DeactivateArticle { private final ArticleRepository articleRepository; + private final UnitOfWork unitOfWork; - public DeactivateArticle(ArticleRepository articleRepository) { + public DeactivateArticle(ArticleRepository articleRepository, UnitOfWork unitOfWork) { this.articleRepository = articleRepository; + this.unitOfWork = unitOfWork; } public Result execute(ArticleId articleId, ActorId performedBy) { @@ -30,12 +31,13 @@ public class DeactivateArticle { } article.deactivate(); - switch (articleRepository.save(article)) { - case Failure(var err) -> - { return Result.failure(new ArticleError.RepositoryFailure(err.message())); } - case Success(var ignored) -> { } - } - - return Result.success(article); + return unitOfWork.executeAtomically(() -> { + switch (articleRepository.save(article)) { + case Failure(var err) -> + { return Result.failure(new ArticleError.RepositoryFailure(err.message())); } + case Success(var ignored) -> { } + } + return Result.success(article); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/DeactivateCustomer.java b/backend/src/main/java/de/effigenix/application/masterdata/DeactivateCustomer.java index 5052f00..acd5641 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/DeactivateCustomer.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/DeactivateCustomer.java @@ -2,18 +2,19 @@ package de.effigenix.application.masterdata; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; import static de.effigenix.shared.common.Result.*; -@Transactional public class DeactivateCustomer { private final CustomerRepository customerRepository; + private final UnitOfWork unitOfWork; - public DeactivateCustomer(CustomerRepository customerRepository) { + public DeactivateCustomer(CustomerRepository customerRepository, UnitOfWork unitOfWork) { this.customerRepository = customerRepository; + this.unitOfWork = unitOfWork; } public Result execute(CustomerId customerId, ActorId performedBy) { @@ -30,12 +31,13 @@ public class DeactivateCustomer { } customer.deactivate(); - switch (customerRepository.save(customer)) { - case Failure(var err) -> - { return Result.failure(new CustomerError.RepositoryFailure(err.message())); } - case Success(var ignored) -> { } - } - - return Result.success(customer); + return unitOfWork.executeAtomically(() -> { + switch (customerRepository.save(customer)) { + case Failure(var err) -> + { return Result.failure(new CustomerError.RepositoryFailure(err.message())); } + case Success(var ignored) -> { } + } + return Result.success(customer); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/DeactivateSupplier.java b/backend/src/main/java/de/effigenix/application/masterdata/DeactivateSupplier.java index 7a5d931..e2939d1 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/DeactivateSupplier.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/DeactivateSupplier.java @@ -2,18 +2,19 @@ package de.effigenix.application.masterdata; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; import static de.effigenix.shared.common.Result.*; -@Transactional public class DeactivateSupplier { private final SupplierRepository supplierRepository; + private final UnitOfWork unitOfWork; - public DeactivateSupplier(SupplierRepository supplierRepository) { + public DeactivateSupplier(SupplierRepository supplierRepository, UnitOfWork unitOfWork) { this.supplierRepository = supplierRepository; + this.unitOfWork = unitOfWork; } public Result execute(SupplierId supplierId, ActorId performedBy) { @@ -30,12 +31,13 @@ public class DeactivateSupplier { } supplier.deactivate(); - switch (supplierRepository.save(supplier)) { - case Failure(var err) -> - { return Result.failure(new SupplierError.RepositoryFailure(err.message())); } - case Success(var ignored) -> { } - } - - return Result.success(supplier); + return unitOfWork.executeAtomically(() -> { + switch (supplierRepository.save(supplier)) { + case Failure(var err) -> + { return Result.failure(new SupplierError.RepositoryFailure(err.message())); } + case Success(var ignored) -> { } + } + return Result.success(supplier); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/DeleteProductCategory.java b/backend/src/main/java/de/effigenix/application/masterdata/DeleteProductCategory.java index 88ffa91..8a4afc6 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/DeleteProductCategory.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/DeleteProductCategory.java @@ -2,20 +2,21 @@ package de.effigenix.application.masterdata; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; import static de.effigenix.shared.common.Result.*; -@Transactional public class DeleteProductCategory { private final ProductCategoryRepository categoryRepository; private final ArticleRepository articleRepository; + private final UnitOfWork unitOfWork; - public DeleteProductCategory(ProductCategoryRepository categoryRepository, ArticleRepository articleRepository) { + public DeleteProductCategory(ProductCategoryRepository categoryRepository, ArticleRepository articleRepository, UnitOfWork unitOfWork) { this.categoryRepository = categoryRepository; this.articleRepository = articleRepository; + this.unitOfWork = unitOfWork; } public Result execute(ProductCategoryId categoryId, ActorId performedBy) { @@ -31,22 +32,24 @@ public class DeleteProductCategory { } } - switch (articleRepository.findByCategory(categoryId)) { - case Failure(var err) -> - { return Result.failure(new ProductCategoryError.RepositoryFailure(err.message())); } - case Success(var articles) -> { - if (!articles.isEmpty()) { - return Result.failure(new ProductCategoryError.CategoryInUse(categoryId)); + return unitOfWork.executeAtomically(() -> { + switch (articleRepository.findByCategory(categoryId)) { + case Failure(var err) -> + { return Result.failure(new ProductCategoryError.RepositoryFailure(err.message())); } + case Success(var articles) -> { + if (!articles.isEmpty()) { + return Result.failure(new ProductCategoryError.CategoryInUse(categoryId)); + } } } - } - switch (categoryRepository.delete(category)) { - case Failure(var err) -> - { return Result.failure(new ProductCategoryError.RepositoryFailure(err.message())); } - case Success(var ignored) -> { } - } + switch (categoryRepository.delete(category)) { + case Failure(var err) -> + { return Result.failure(new ProductCategoryError.RepositoryFailure(err.message())); } + case Success(var ignored) -> { } + } - return Result.success(null); + return Result.success(null); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/GetArticle.java b/backend/src/main/java/de/effigenix/application/masterdata/GetArticle.java index abe7cd1..b350c03 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/GetArticle.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/GetArticle.java @@ -2,11 +2,9 @@ package de.effigenix.application.masterdata; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; -import org.springframework.transaction.annotation.Transactional; import static de.effigenix.shared.common.Result.*; -@Transactional(readOnly = true) public class GetArticle { private final ArticleRepository articleRepository; diff --git a/backend/src/main/java/de/effigenix/application/masterdata/GetCustomer.java b/backend/src/main/java/de/effigenix/application/masterdata/GetCustomer.java index 0ab4c64..afe2e04 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/GetCustomer.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/GetCustomer.java @@ -2,11 +2,9 @@ package de.effigenix.application.masterdata; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; -import org.springframework.transaction.annotation.Transactional; import static de.effigenix.shared.common.Result.*; -@Transactional(readOnly = true) public class GetCustomer { private final CustomerRepository customerRepository; diff --git a/backend/src/main/java/de/effigenix/application/masterdata/GetSupplier.java b/backend/src/main/java/de/effigenix/application/masterdata/GetSupplier.java index b4d4a0e..97162f6 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/GetSupplier.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/GetSupplier.java @@ -2,11 +2,9 @@ package de.effigenix.application.masterdata; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; -import org.springframework.transaction.annotation.Transactional; import static de.effigenix.shared.common.Result.*; -@Transactional(readOnly = true) public class GetSupplier { private final SupplierRepository supplierRepository; diff --git a/backend/src/main/java/de/effigenix/application/masterdata/ListArticles.java b/backend/src/main/java/de/effigenix/application/masterdata/ListArticles.java index be41048..29b3aac 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/ListArticles.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/ListArticles.java @@ -2,13 +2,11 @@ package de.effigenix.application.masterdata; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; -import org.springframework.transaction.annotation.Transactional; import java.util.List; import static de.effigenix.shared.common.Result.*; -@Transactional(readOnly = true) public class ListArticles { private final ArticleRepository articleRepository; diff --git a/backend/src/main/java/de/effigenix/application/masterdata/ListCustomers.java b/backend/src/main/java/de/effigenix/application/masterdata/ListCustomers.java index ce594ad..8ad18b3 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/ListCustomers.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/ListCustomers.java @@ -2,13 +2,11 @@ package de.effigenix.application.masterdata; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; -import org.springframework.transaction.annotation.Transactional; import java.util.List; import static de.effigenix.shared.common.Result.*; -@Transactional(readOnly = true) public class ListCustomers { private final CustomerRepository customerRepository; diff --git a/backend/src/main/java/de/effigenix/application/masterdata/ListProductCategories.java b/backend/src/main/java/de/effigenix/application/masterdata/ListProductCategories.java index e968e8e..f717935 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/ListProductCategories.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/ListProductCategories.java @@ -2,13 +2,11 @@ package de.effigenix.application.masterdata; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; -import org.springframework.transaction.annotation.Transactional; import java.util.List; import static de.effigenix.shared.common.Result.*; -@Transactional(readOnly = true) public class ListProductCategories { private final ProductCategoryRepository categoryRepository; diff --git a/backend/src/main/java/de/effigenix/application/masterdata/ListSuppliers.java b/backend/src/main/java/de/effigenix/application/masterdata/ListSuppliers.java index 7f53d3d..23db283 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/ListSuppliers.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/ListSuppliers.java @@ -2,13 +2,11 @@ package de.effigenix.application.masterdata; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; -import org.springframework.transaction.annotation.Transactional; import java.util.List; import static de.effigenix.shared.common.Result.*; -@Transactional(readOnly = true) public class ListSuppliers { private final SupplierRepository supplierRepository; diff --git a/backend/src/main/java/de/effigenix/application/masterdata/RateSupplier.java b/backend/src/main/java/de/effigenix/application/masterdata/RateSupplier.java index 7be777e..ede2742 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/RateSupplier.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/RateSupplier.java @@ -3,18 +3,19 @@ package de.effigenix.application.masterdata; import de.effigenix.application.masterdata.command.RateSupplierCommand; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; import static de.effigenix.shared.common.Result.*; -@Transactional public class RateSupplier { private final SupplierRepository supplierRepository; + private final UnitOfWork unitOfWork; - public RateSupplier(SupplierRepository supplierRepository) { + public RateSupplier(SupplierRepository supplierRepository, UnitOfWork unitOfWork) { this.supplierRepository = supplierRepository; + this.unitOfWork = unitOfWork; } public Result execute(RateSupplierCommand cmd, ActorId performedBy) { @@ -39,12 +40,13 @@ public class RateSupplier { } supplier.rate(rating); - switch (supplierRepository.save(supplier)) { - case Failure(var err) -> - { return Result.failure(new SupplierError.RepositoryFailure(err.message())); } - case Success(var ignored) -> { } - } - - return Result.success(supplier); + return unitOfWork.executeAtomically(() -> { + switch (supplierRepository.save(supplier)) { + case Failure(var err) -> + { return Result.failure(new SupplierError.RepositoryFailure(err.message())); } + case Success(var ignored) -> { } + } + return Result.success(supplier); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/RemoveCertificate.java b/backend/src/main/java/de/effigenix/application/masterdata/RemoveCertificate.java index 31ce573..e5d30fe 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/RemoveCertificate.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/RemoveCertificate.java @@ -3,20 +3,21 @@ package de.effigenix.application.masterdata; import de.effigenix.application.masterdata.command.RemoveCertificateCommand; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; import java.util.Objects; import static de.effigenix.shared.common.Result.*; -@Transactional public class RemoveCertificate { private final SupplierRepository supplierRepository; + private final UnitOfWork unitOfWork; - public RemoveCertificate(SupplierRepository supplierRepository) { + public RemoveCertificate(SupplierRepository supplierRepository, UnitOfWork unitOfWork) { this.supplierRepository = supplierRepository; + this.unitOfWork = unitOfWork; } public Result execute(RemoveCertificateCommand cmd, ActorId performedBy) { @@ -45,12 +46,13 @@ public class RemoveCertificate { } supplier.removeCertificate(certificate.get()); - switch (supplierRepository.save(supplier)) { - case Failure(var err) -> - { return Result.failure(new SupplierError.RepositoryFailure(err.message())); } - case Success(var ignored) -> { } - } - - return Result.success(supplier); + return unitOfWork.executeAtomically(() -> { + switch (supplierRepository.save(supplier)) { + case Failure(var err) -> + { return Result.failure(new SupplierError.RepositoryFailure(err.message())); } + case Success(var ignored) -> { } + } + return Result.success(supplier); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/RemoveDeliveryAddress.java b/backend/src/main/java/de/effigenix/application/masterdata/RemoveDeliveryAddress.java index be023ae..3ebe800 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/RemoveDeliveryAddress.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/RemoveDeliveryAddress.java @@ -3,18 +3,19 @@ package de.effigenix.application.masterdata; import de.effigenix.application.masterdata.command.RemoveDeliveryAddressCommand; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; import static de.effigenix.shared.common.Result.*; -@Transactional public class RemoveDeliveryAddress { private final CustomerRepository customerRepository; + private final UnitOfWork unitOfWork; - public RemoveDeliveryAddress(CustomerRepository customerRepository) { + public RemoveDeliveryAddress(CustomerRepository customerRepository, UnitOfWork unitOfWork) { this.customerRepository = customerRepository; + this.unitOfWork = unitOfWork; } public Result execute(RemoveDeliveryAddressCommand cmd, ActorId performedBy) { @@ -33,12 +34,13 @@ public class RemoveDeliveryAddress { } customer.removeDeliveryAddress(cmd.label()); - switch (customerRepository.save(customer)) { - case Failure(var err) -> - { return Result.failure(new CustomerError.RepositoryFailure(err.message())); } - case Success(var ignored) -> { } - } - - return Result.success(customer); + return unitOfWork.executeAtomically(() -> { + switch (customerRepository.save(customer)) { + case Failure(var err) -> + { return Result.failure(new CustomerError.RepositoryFailure(err.message())); } + case Success(var ignored) -> { } + } + return Result.success(customer); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/RemoveFrameContract.java b/backend/src/main/java/de/effigenix/application/masterdata/RemoveFrameContract.java index 35d1820..ba1f314 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/RemoveFrameContract.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/RemoveFrameContract.java @@ -2,18 +2,19 @@ package de.effigenix.application.masterdata; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; import static de.effigenix.shared.common.Result.*; -@Transactional public class RemoveFrameContract { private final CustomerRepository customerRepository; + private final UnitOfWork unitOfWork; - public RemoveFrameContract(CustomerRepository customerRepository) { + public RemoveFrameContract(CustomerRepository customerRepository, UnitOfWork unitOfWork) { this.customerRepository = customerRepository; + this.unitOfWork = unitOfWork; } public Result execute(CustomerId customerId, ActorId performedBy) { @@ -30,12 +31,13 @@ public class RemoveFrameContract { } customer.removeFrameContract(); - switch (customerRepository.save(customer)) { - case Failure(var err) -> - { return Result.failure(new CustomerError.RepositoryFailure(err.message())); } - case Success(var ignored) -> { } - } - - return Result.success(customer); + return unitOfWork.executeAtomically(() -> { + switch (customerRepository.save(customer)) { + case Failure(var err) -> + { return Result.failure(new CustomerError.RepositoryFailure(err.message())); } + case Success(var ignored) -> { } + } + return Result.success(customer); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/RemoveSalesUnit.java b/backend/src/main/java/de/effigenix/application/masterdata/RemoveSalesUnit.java index 08e9767..d81d88c 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/RemoveSalesUnit.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/RemoveSalesUnit.java @@ -3,18 +3,19 @@ package de.effigenix.application.masterdata; import de.effigenix.application.masterdata.command.RemoveSalesUnitCommand; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; import static de.effigenix.shared.common.Result.*; -@Transactional public class RemoveSalesUnit { private final ArticleRepository articleRepository; + private final UnitOfWork unitOfWork; - public RemoveSalesUnit(ArticleRepository articleRepository) { + public RemoveSalesUnit(ArticleRepository articleRepository, UnitOfWork unitOfWork) { this.articleRepository = articleRepository; + this.unitOfWork = unitOfWork; } public Result execute(RemoveSalesUnitCommand cmd, ActorId performedBy) { @@ -37,12 +38,13 @@ public class RemoveSalesUnit { case Success(var ignored) -> { } } - switch (articleRepository.save(article)) { - case Failure(var err) -> - { return Result.failure(new ArticleError.RepositoryFailure(err.message())); } - case Success(var ignored) -> { } - } - - return Result.success(article); + return unitOfWork.executeAtomically(() -> { + switch (articleRepository.save(article)) { + case Failure(var err) -> + { return Result.failure(new ArticleError.RepositoryFailure(err.message())); } + case Success(var ignored) -> { } + } + return Result.success(article); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/RemoveSupplier.java b/backend/src/main/java/de/effigenix/application/masterdata/RemoveSupplier.java index 9f08e4a..ce9ab20 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/RemoveSupplier.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/RemoveSupplier.java @@ -3,18 +3,19 @@ package de.effigenix.application.masterdata; import de.effigenix.application.masterdata.command.RemoveSupplierCommand; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; import static de.effigenix.shared.common.Result.*; -@Transactional public class RemoveSupplier { private final ArticleRepository articleRepository; + private final UnitOfWork unitOfWork; - public RemoveSupplier(ArticleRepository articleRepository) { + public RemoveSupplier(ArticleRepository articleRepository, UnitOfWork unitOfWork) { this.articleRepository = articleRepository; + this.unitOfWork = unitOfWork; } public Result execute(RemoveSupplierCommand cmd, ActorId performedBy) { @@ -33,12 +34,13 @@ public class RemoveSupplier { } article.removeSupplierReference(SupplierId.of(cmd.supplierId())); - switch (articleRepository.save(article)) { - case Failure(var err) -> - { return Result.failure(new ArticleError.RepositoryFailure(err.message())); } - case Success(var ignored) -> { } - } - - return Result.success(article); + return unitOfWork.executeAtomically(() -> { + switch (articleRepository.save(article)) { + case Failure(var err) -> + { return Result.failure(new ArticleError.RepositoryFailure(err.message())); } + case Success(var ignored) -> { } + } + return Result.success(article); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/SetFrameContract.java b/backend/src/main/java/de/effigenix/application/masterdata/SetFrameContract.java index 2d05ce5..f4021f0 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/SetFrameContract.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/SetFrameContract.java @@ -4,20 +4,21 @@ import de.effigenix.application.masterdata.command.SetFrameContractCommand; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Money; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import static de.effigenix.shared.common.Result.*; -@Transactional public class SetFrameContract { private final CustomerRepository customerRepository; + private final UnitOfWork unitOfWork; - public SetFrameContract(CustomerRepository customerRepository) { + public SetFrameContract(CustomerRepository customerRepository, UnitOfWork unitOfWork) { this.customerRepository = customerRepository; + this.unitOfWork = unitOfWork; } public Result execute(SetFrameContractCommand cmd, ActorId performedBy) { @@ -62,12 +63,13 @@ public class SetFrameContract { case Success(var ignored) -> { } } - switch (customerRepository.save(customer)) { - case Failure(var err) -> - { return Result.failure(new CustomerError.RepositoryFailure(err.message())); } - case Success(var ignored) -> { } - } - - return Result.success(customer); + return unitOfWork.executeAtomically(() -> { + switch (customerRepository.save(customer)) { + case Failure(var err) -> + { return Result.failure(new CustomerError.RepositoryFailure(err.message())); } + case Success(var ignored) -> { } + } + return Result.success(customer); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/SetPreferences.java b/backend/src/main/java/de/effigenix/application/masterdata/SetPreferences.java index 2d4cb4d..126895c 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/SetPreferences.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/SetPreferences.java @@ -3,18 +3,19 @@ package de.effigenix.application.masterdata; import de.effigenix.application.masterdata.command.SetPreferencesCommand; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; import static de.effigenix.shared.common.Result.*; -@Transactional public class SetPreferences { private final CustomerRepository customerRepository; + private final UnitOfWork unitOfWork; - public SetPreferences(CustomerRepository customerRepository) { + public SetPreferences(CustomerRepository customerRepository, UnitOfWork unitOfWork) { this.customerRepository = customerRepository; + this.unitOfWork = unitOfWork; } public Result execute(SetPreferencesCommand cmd, ActorId performedBy) { @@ -33,12 +34,13 @@ public class SetPreferences { } customer.setPreferences(cmd.preferences()); - switch (customerRepository.save(customer)) { - case Failure(var err) -> - { return Result.failure(new CustomerError.RepositoryFailure(err.message())); } - case Success(var ignored) -> { } - } - - return Result.success(customer); + return unitOfWork.executeAtomically(() -> { + switch (customerRepository.save(customer)) { + case Failure(var err) -> + { return Result.failure(new CustomerError.RepositoryFailure(err.message())); } + case Success(var ignored) -> { } + } + return Result.success(customer); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/UpdateArticle.java b/backend/src/main/java/de/effigenix/application/masterdata/UpdateArticle.java index 2261bf5..9bf6581 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/UpdateArticle.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/UpdateArticle.java @@ -3,16 +3,17 @@ package de.effigenix.application.masterdata; import de.effigenix.application.masterdata.command.UpdateArticleCommand; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; -@Transactional public class UpdateArticle { private final ArticleRepository articleRepository; + private final UnitOfWork unitOfWork; - public UpdateArticle(ArticleRepository articleRepository) { + public UpdateArticle(ArticleRepository articleRepository, UnitOfWork unitOfWork) { this.articleRepository = articleRepository; + this.unitOfWork = unitOfWork; } public Result execute(UpdateArticleCommand cmd, ActorId performedBy) { @@ -36,12 +37,13 @@ public class UpdateArticle { case Result.Success(var ignored) -> { } } - switch (articleRepository.save(article)) { - case Result.Failure(var err) -> - { return Result.failure(new ArticleError.RepositoryFailure(err.message())); } - case Result.Success(var ignored) -> { } - } - - return Result.success(article); + return unitOfWork.executeAtomically(() -> { + switch (articleRepository.save(article)) { + case Result.Failure(var err) -> + { return Result.failure(new ArticleError.RepositoryFailure(err.message())); } + case Result.Success(var ignored) -> { } + } + return Result.success(article); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/UpdateCustomer.java b/backend/src/main/java/de/effigenix/application/masterdata/UpdateCustomer.java index 4072352..61edca0 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/UpdateCustomer.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/UpdateCustomer.java @@ -3,16 +3,17 @@ package de.effigenix.application.masterdata; import de.effigenix.application.masterdata.command.UpdateCustomerCommand; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; -@Transactional public class UpdateCustomer { private final CustomerRepository customerRepository; + private final UnitOfWork unitOfWork; - public UpdateCustomer(CustomerRepository customerRepository) { + public UpdateCustomer(CustomerRepository customerRepository, UnitOfWork unitOfWork) { this.customerRepository = customerRepository; + this.unitOfWork = unitOfWork; } public Result execute(UpdateCustomerCommand cmd, ActorId performedBy) { @@ -40,12 +41,13 @@ public class UpdateCustomer { case Result.Success(var ignored) -> { } } - switch (customerRepository.save(customer)) { - case Result.Failure(var err) -> - { return Result.failure(new CustomerError.RepositoryFailure(err.message())); } - case Result.Success(var ignored) -> { } - } - - return Result.success(customer); + return unitOfWork.executeAtomically(() -> { + switch (customerRepository.save(customer)) { + case Result.Failure(var err) -> + { return Result.failure(new CustomerError.RepositoryFailure(err.message())); } + case Result.Success(var ignored) -> { } + } + return Result.success(customer); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/UpdateProductCategory.java b/backend/src/main/java/de/effigenix/application/masterdata/UpdateProductCategory.java index 81944a6..5b0ea39 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/UpdateProductCategory.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/UpdateProductCategory.java @@ -3,16 +3,17 @@ package de.effigenix.application.masterdata; import de.effigenix.application.masterdata.command.UpdateProductCategoryCommand; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; -@Transactional public class UpdateProductCategory { private final ProductCategoryRepository categoryRepository; + private final UnitOfWork unitOfWork; - public UpdateProductCategory(ProductCategoryRepository categoryRepository) { + public UpdateProductCategory(ProductCategoryRepository categoryRepository, UnitOfWork unitOfWork) { this.categoryRepository = categoryRepository; + this.unitOfWork = unitOfWork; } public Result execute(UpdateProductCategoryCommand cmd, ActorId performedBy) { @@ -36,12 +37,13 @@ public class UpdateProductCategory { case Result.Success(var ignored) -> { } } - switch (categoryRepository.save(category)) { - case Result.Failure(var err) -> - { return Result.failure(new ProductCategoryError.RepositoryFailure(err.message())); } - case Result.Success(var ignored) -> { } - } - - return Result.success(category); + return unitOfWork.executeAtomically(() -> { + switch (categoryRepository.save(category)) { + case Result.Failure(var err) -> + { return Result.failure(new ProductCategoryError.RepositoryFailure(err.message())); } + case Result.Success(var ignored) -> { } + } + return Result.success(category); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/UpdateSalesUnitPrice.java b/backend/src/main/java/de/effigenix/application/masterdata/UpdateSalesUnitPrice.java index 763f2ce..2c31368 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/UpdateSalesUnitPrice.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/UpdateSalesUnitPrice.java @@ -4,18 +4,19 @@ import de.effigenix.application.masterdata.command.UpdateSalesUnitPriceCommand; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Money; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; import static de.effigenix.shared.common.Result.*; -@Transactional public class UpdateSalesUnitPrice { private final ArticleRepository articleRepository; + private final UnitOfWork unitOfWork; - public UpdateSalesUnitPrice(ArticleRepository articleRepository) { + public UpdateSalesUnitPrice(ArticleRepository articleRepository, UnitOfWork unitOfWork) { this.articleRepository = articleRepository; + this.unitOfWork = unitOfWork; } public Result execute(UpdateSalesUnitPriceCommand cmd, ActorId performedBy) { @@ -44,12 +45,13 @@ public class UpdateSalesUnitPrice { case Success(var ignored) -> { } } - switch (articleRepository.save(article)) { - case Failure(var err) -> - { return Result.failure(new ArticleError.RepositoryFailure(err.message())); } - case Success(var ignored) -> { } - } - - return Result.success(article); + return unitOfWork.executeAtomically(() -> { + switch (articleRepository.save(article)) { + case Failure(var err) -> + { return Result.failure(new ArticleError.RepositoryFailure(err.message())); } + case Success(var ignored) -> { } + } + return Result.success(article); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/masterdata/UpdateSupplier.java b/backend/src/main/java/de/effigenix/application/masterdata/UpdateSupplier.java index 8df3c22..6c24d54 100644 --- a/backend/src/main/java/de/effigenix/application/masterdata/UpdateSupplier.java +++ b/backend/src/main/java/de/effigenix/application/masterdata/UpdateSupplier.java @@ -3,16 +3,17 @@ package de.effigenix.application.masterdata; import de.effigenix.application.masterdata.command.UpdateSupplierCommand; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; -@Transactional public class UpdateSupplier { private final SupplierRepository supplierRepository; + private final UnitOfWork unitOfWork; - public UpdateSupplier(SupplierRepository supplierRepository) { + public UpdateSupplier(SupplierRepository supplierRepository, UnitOfWork unitOfWork) { this.supplierRepository = supplierRepository; + this.unitOfWork = unitOfWork; } public Result execute(UpdateSupplierCommand cmd, ActorId performedBy) { @@ -43,12 +44,13 @@ public class UpdateSupplier { } // 3. Speichern - switch (supplierRepository.save(supplier)) { - case Result.Failure(var err) -> - { return Result.failure(new SupplierError.RepositoryFailure(err.message())); } - case Result.Success(var ignored) -> { } - } - - return Result.success(supplier); + return unitOfWork.executeAtomically(() -> { + switch (supplierRepository.save(supplier)) { + case Result.Failure(var err) -> + { return Result.failure(new SupplierError.RepositoryFailure(err.message())); } + case Result.Success(var ignored) -> { } + } + return Result.success(supplier); + }); } } diff --git a/backend/src/main/java/de/effigenix/application/usermanagement/AssignRole.java b/backend/src/main/java/de/effigenix/application/usermanagement/AssignRole.java index 494bfde..45c0a76 100644 --- a/backend/src/main/java/de/effigenix/application/usermanagement/AssignRole.java +++ b/backend/src/main/java/de/effigenix/application/usermanagement/AssignRole.java @@ -6,32 +6,34 @@ import de.effigenix.domain.usermanagement.*; import de.effigenix.shared.common.RepositoryError; import de.effigenix.shared.common.Result; import de.effigenix.shared.security.ActorId; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.AuthorizationPort; -import org.springframework.transaction.annotation.Transactional; import java.util.Optional; /** * Use Case: Assign a role to a user. */ -@Transactional public class AssignRole { private final UserRepository userRepository; private final RoleRepository roleRepository; private final AuditLogger auditLogger; private final AuthorizationPort authPort; + private final UnitOfWork unitOfWork; public AssignRole( UserRepository userRepository, RoleRepository roleRepository, AuditLogger auditLogger, - AuthorizationPort authPort + AuthorizationPort authPort, + UnitOfWork unitOfWork ) { this.userRepository = userRepository; this.roleRepository = roleRepository; this.auditLogger = auditLogger; this.authPort = authPort; + this.unitOfWork = unitOfWork; } public Result execute(AssignRoleCommand cmd, ActorId performedBy) { @@ -61,12 +63,13 @@ public class AssignRole { } Role role = optRole.get(); return user.assignRole(role) - .flatMap(updated -> userRepository.save(updated) - .mapError(err -> (UserError) new UserError.RepositoryFailure(err.message())) - .map(ignored -> { - auditLogger.log(AuditEvent.ROLE_ASSIGNED, updated.id().value(), "Role: " + role.name(), performedBy); - return UserDTO.from(updated); - })); + .flatMap(updated -> unitOfWork.executeAtomically(() -> + userRepository.save(updated) + .mapError(err -> (UserError) new UserError.RepositoryFailure(err.message())) + .map(ignored -> { + auditLogger.log(AuditEvent.ROLE_ASSIGNED, updated.id().value(), "Role: " + role.name(), performedBy); + return UserDTO.from(updated); + }))); }); } diff --git a/backend/src/main/java/de/effigenix/application/usermanagement/AuthenticateUser.java b/backend/src/main/java/de/effigenix/application/usermanagement/AuthenticateUser.java index 5cf48a6..cdfb34f 100644 --- a/backend/src/main/java/de/effigenix/application/usermanagement/AuthenticateUser.java +++ b/backend/src/main/java/de/effigenix/application/usermanagement/AuthenticateUser.java @@ -4,8 +4,8 @@ import de.effigenix.application.usermanagement.command.AuthenticateCommand; import de.effigenix.application.usermanagement.dto.SessionToken; import de.effigenix.domain.usermanagement.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; -import org.springframework.transaction.annotation.Transactional; import java.time.OffsetDateTime; import java.time.ZoneOffset; @@ -17,24 +17,26 @@ import java.util.Optional; * Returns a JWT session token on success. * Logs all authentication attempts for security auditing. */ -@Transactional public class AuthenticateUser { private final UserRepository userRepository; private final PasswordHasher passwordHasher; private final SessionManager sessionManager; private final AuditLogger auditLogger; + private final UnitOfWork unitOfWork; public AuthenticateUser( UserRepository userRepository, PasswordHasher passwordHasher, SessionManager sessionManager, - AuditLogger auditLogger + AuditLogger auditLogger, + UnitOfWork unitOfWork ) { this.userRepository = userRepository; this.passwordHasher = passwordHasher; this.sessionManager = sessionManager; this.auditLogger = auditLogger; + this.unitOfWork = unitOfWork; } public Result execute(AuthenticateCommand cmd) { @@ -73,11 +75,12 @@ public class AuthenticateUser { // 5. Update last login timestamp (immutable) return user.withLastLogin(OffsetDateTime.now(ZoneOffset.UTC)) - .flatMap(updated -> userRepository.save(updated) - .mapError(err -> (UserError) new UserError.RepositoryFailure(err.message())) - .map(ignored -> { - auditLogger.log(AuditEvent.LOGIN_SUCCESS, updated.id().value(), ActorId.of(updated.id().value())); - return token; - })); + .flatMap(updated -> unitOfWork.executeAtomically(() -> + userRepository.save(updated) + .mapError(err -> (UserError) new UserError.RepositoryFailure(err.message())) + .map(ignored -> { + auditLogger.log(AuditEvent.LOGIN_SUCCESS, updated.id().value(), ActorId.of(updated.id().value())); + return token; + }))); } } diff --git a/backend/src/main/java/de/effigenix/application/usermanagement/ChangePassword.java b/backend/src/main/java/de/effigenix/application/usermanagement/ChangePassword.java index a701a32..60e2f61 100644 --- a/backend/src/main/java/de/effigenix/application/usermanagement/ChangePassword.java +++ b/backend/src/main/java/de/effigenix/application/usermanagement/ChangePassword.java @@ -4,8 +4,8 @@ import de.effigenix.application.usermanagement.command.ChangePasswordCommand; import de.effigenix.domain.usermanagement.*; import de.effigenix.shared.common.Result; import de.effigenix.shared.security.ActorId; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.AuthorizationPort; -import org.springframework.transaction.annotation.Transactional; /** * Use Case: Change user password. @@ -13,24 +13,26 @@ import org.springframework.transaction.annotation.Transactional; * Requires current password verification for security. * Self-service: users can change their own password without PASSWORD_CHANGE permission. */ -@Transactional public class ChangePassword { private final UserRepository userRepository; private final PasswordHasher passwordHasher; private final AuditLogger auditLogger; private final AuthorizationPort authPort; + private final UnitOfWork unitOfWork; public ChangePassword( UserRepository userRepository, PasswordHasher passwordHasher, AuditLogger auditLogger, - AuthorizationPort authPort + AuthorizationPort authPort, + UnitOfWork unitOfWork ) { this.userRepository = userRepository; this.passwordHasher = passwordHasher; this.auditLogger = auditLogger; this.authPort = authPort; + this.unitOfWork = unitOfWork; } public Result execute(ChangePasswordCommand cmd, ActorId performedBy) { @@ -71,12 +73,13 @@ public class ChangePassword { // 5. Hash and update (immutable) PasswordHash newPasswordHash = passwordHasher.hash(cmd.newPassword()); return user.changePassword(newPasswordHash) - .flatMap(updated -> userRepository.save(updated) - .mapError(err -> (UserError) new UserError.RepositoryFailure(err.message())) - .map(ignored -> { - auditLogger.log(AuditEvent.PASSWORD_CHANGED, updated.id().value(), performedBy); - return null; - })); + .flatMap(updated -> unitOfWork.executeAtomically(() -> + userRepository.save(updated) + .mapError(err -> (UserError) new UserError.RepositoryFailure(err.message())) + .map(ignored -> { + auditLogger.log(AuditEvent.PASSWORD_CHANGED, updated.id().value(), performedBy); + return null; + }))); } private Result findUser(UserId userId) { diff --git a/backend/src/main/java/de/effigenix/application/usermanagement/CreateUser.java b/backend/src/main/java/de/effigenix/application/usermanagement/CreateUser.java index 9b238c5..f3ab41a 100644 --- a/backend/src/main/java/de/effigenix/application/usermanagement/CreateUser.java +++ b/backend/src/main/java/de/effigenix/application/usermanagement/CreateUser.java @@ -6,8 +6,8 @@ import de.effigenix.domain.usermanagement.*; import de.effigenix.shared.common.RepositoryError; import de.effigenix.shared.common.Result; import de.effigenix.shared.security.ActorId; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.AuthorizationPort; -import org.springframework.transaction.annotation.Transactional; import java.util.HashSet; import java.util.Set; @@ -15,7 +15,6 @@ import java.util.Set; /** * Use Case: Create a new user account. */ -@Transactional public class CreateUser { private final UserRepository userRepository; @@ -23,19 +22,22 @@ public class CreateUser { private final PasswordHasher passwordHasher; private final AuditLogger auditLogger; private final AuthorizationPort authPort; + private final UnitOfWork unitOfWork; public CreateUser( UserRepository userRepository, RoleRepository roleRepository, PasswordHasher passwordHasher, AuditLogger auditLogger, - AuthorizationPort authPort + AuthorizationPort authPort, + UnitOfWork unitOfWork ) { this.userRepository = userRepository; this.roleRepository = roleRepository; this.passwordHasher = passwordHasher; this.auditLogger = auditLogger; this.authPort = authPort; + this.unitOfWork = unitOfWork; } public Result execute(CreateUserCommand cmd, ActorId performedBy) { @@ -99,11 +101,12 @@ public class CreateUser { } return User.create(cmd.username(), cmd.email(), passwordHash, roles, cmd.branchId()) - .flatMap(user -> userRepository.save(user) - .mapError(err -> (UserError) new UserError.RepositoryFailure(err.message())) - .map(ignored -> { - auditLogger.log(AuditEvent.USER_CREATED, user.id().value(), performedBy); - return UserDTO.from(user); - })); + .flatMap(user -> unitOfWork.executeAtomically(() -> + userRepository.save(user) + .mapError(err -> (UserError) new UserError.RepositoryFailure(err.message())) + .map(ignored -> { + auditLogger.log(AuditEvent.USER_CREATED, user.id().value(), performedBy); + return UserDTO.from(user); + }))); } } diff --git a/backend/src/main/java/de/effigenix/application/usermanagement/GetUser.java b/backend/src/main/java/de/effigenix/application/usermanagement/GetUser.java index 740d95e..6250683 100644 --- a/backend/src/main/java/de/effigenix/application/usermanagement/GetUser.java +++ b/backend/src/main/java/de/effigenix/application/usermanagement/GetUser.java @@ -5,12 +5,10 @@ import de.effigenix.domain.usermanagement.*; import de.effigenix.shared.common.Result; import de.effigenix.shared.security.ActorId; import de.effigenix.shared.security.AuthorizationPort; -import org.springframework.transaction.annotation.Transactional; /** * Use Case: Get a single user by ID. */ -@Transactional(readOnly = true) public class GetUser { private final UserRepository userRepository; diff --git a/backend/src/main/java/de/effigenix/application/usermanagement/ListUsers.java b/backend/src/main/java/de/effigenix/application/usermanagement/ListUsers.java index c7d86aa..bfd7345 100644 --- a/backend/src/main/java/de/effigenix/application/usermanagement/ListUsers.java +++ b/backend/src/main/java/de/effigenix/application/usermanagement/ListUsers.java @@ -8,7 +8,6 @@ import de.effigenix.shared.common.Result; import de.effigenix.shared.security.ActorId; import de.effigenix.shared.security.AuthorizationPort; import de.effigenix.shared.security.BranchId; -import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.stream.Collectors; @@ -16,7 +15,6 @@ import java.util.stream.Collectors; /** * Use Case: List all users (with optional branch filtering). */ -@Transactional(readOnly = true) public class ListUsers { private final UserRepository userRepository; diff --git a/backend/src/main/java/de/effigenix/application/usermanagement/LockUser.java b/backend/src/main/java/de/effigenix/application/usermanagement/LockUser.java index bd0ace2..60c46fd 100644 --- a/backend/src/main/java/de/effigenix/application/usermanagement/LockUser.java +++ b/backend/src/main/java/de/effigenix/application/usermanagement/LockUser.java @@ -5,23 +5,24 @@ import de.effigenix.application.usermanagement.dto.UserDTO; import de.effigenix.domain.usermanagement.*; import de.effigenix.shared.common.Result; import de.effigenix.shared.security.ActorId; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.AuthorizationPort; -import org.springframework.transaction.annotation.Transactional; /** * Use Case: Lock a user account (prevent login). */ -@Transactional public class LockUser { private final UserRepository userRepository; private final AuditLogger auditLogger; private final AuthorizationPort authPort; + private final UnitOfWork unitOfWork; - public LockUser(UserRepository userRepository, AuditLogger auditLogger, AuthorizationPort authPort) { + public LockUser(UserRepository userRepository, AuditLogger auditLogger, AuthorizationPort authPort, UnitOfWork unitOfWork) { this.userRepository = userRepository; this.auditLogger = auditLogger; this.authPort = authPort; + this.unitOfWork = unitOfWork; } public Result execute(LockUserCommand cmd, ActorId performedBy) { @@ -38,12 +39,13 @@ public class LockUser { UserId userId = UserId.of(cmd.userId()); return findUser(userId) .flatMap(User::lock) - .flatMap(updated -> userRepository.save(updated) - .mapError(err -> (UserError) new UserError.RepositoryFailure(err.message())) - .map(ignored -> { - auditLogger.log(AuditEvent.USER_LOCKED, updated.id().value(), performedBy); - return UserDTO.from(updated); - })); + .flatMap(updated -> unitOfWork.executeAtomically(() -> + userRepository.save(updated) + .mapError(err -> (UserError) new UserError.RepositoryFailure(err.message())) + .map(ignored -> { + auditLogger.log(AuditEvent.USER_LOCKED, updated.id().value(), performedBy); + return UserDTO.from(updated); + }))); } private Result findUser(UserId userId) { diff --git a/backend/src/main/java/de/effigenix/application/usermanagement/RemoveRole.java b/backend/src/main/java/de/effigenix/application/usermanagement/RemoveRole.java index bd77cfd..2080b5e 100644 --- a/backend/src/main/java/de/effigenix/application/usermanagement/RemoveRole.java +++ b/backend/src/main/java/de/effigenix/application/usermanagement/RemoveRole.java @@ -5,30 +5,32 @@ import de.effigenix.application.usermanagement.dto.UserDTO; import de.effigenix.domain.usermanagement.*; import de.effigenix.shared.common.Result; import de.effigenix.shared.security.ActorId; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.AuthorizationPort; -import org.springframework.transaction.annotation.Transactional; /** * Use Case: Remove a role from a user. */ -@Transactional public class RemoveRole { private final UserRepository userRepository; private final RoleRepository roleRepository; private final AuditLogger auditLogger; private final AuthorizationPort authPort; + private final UnitOfWork unitOfWork; public RemoveRole( UserRepository userRepository, RoleRepository roleRepository, AuditLogger auditLogger, - AuthorizationPort authPort + AuthorizationPort authPort, + UnitOfWork unitOfWork ) { this.userRepository = userRepository; this.roleRepository = roleRepository; this.auditLogger = auditLogger; this.authPort = authPort; + this.unitOfWork = unitOfWork; } public Result execute(RemoveRoleCommand cmd, ActorId performedBy) { @@ -58,12 +60,13 @@ public class RemoveRole { } Role role = optRole.get(); return user.removeRole(role) - .flatMap(updated -> userRepository.save(updated) - .mapError(err -> (UserError) new UserError.RepositoryFailure(err.message())) - .map(ignored -> { - auditLogger.log(AuditEvent.ROLE_REMOVED, updated.id().value(), "Role: " + role.name(), performedBy); - return UserDTO.from(updated); - })); + .flatMap(updated -> unitOfWork.executeAtomically(() -> + userRepository.save(updated) + .mapError(err -> (UserError) new UserError.RepositoryFailure(err.message())) + .map(ignored -> { + auditLogger.log(AuditEvent.ROLE_REMOVED, updated.id().value(), "Role: " + role.name(), performedBy); + return UserDTO.from(updated); + }))); }); } diff --git a/backend/src/main/java/de/effigenix/application/usermanagement/UnlockUser.java b/backend/src/main/java/de/effigenix/application/usermanagement/UnlockUser.java index a68661c..f0817ce 100644 --- a/backend/src/main/java/de/effigenix/application/usermanagement/UnlockUser.java +++ b/backend/src/main/java/de/effigenix/application/usermanagement/UnlockUser.java @@ -5,23 +5,24 @@ import de.effigenix.application.usermanagement.dto.UserDTO; import de.effigenix.domain.usermanagement.*; import de.effigenix.shared.common.Result; import de.effigenix.shared.security.ActorId; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.AuthorizationPort; -import org.springframework.transaction.annotation.Transactional; /** * Use Case: Unlock a user account (allow login). */ -@Transactional public class UnlockUser { private final UserRepository userRepository; private final AuditLogger auditLogger; private final AuthorizationPort authPort; + private final UnitOfWork unitOfWork; - public UnlockUser(UserRepository userRepository, AuditLogger auditLogger, AuthorizationPort authPort) { + public UnlockUser(UserRepository userRepository, AuditLogger auditLogger, AuthorizationPort authPort, UnitOfWork unitOfWork) { this.userRepository = userRepository; this.auditLogger = auditLogger; this.authPort = authPort; + this.unitOfWork = unitOfWork; } public Result execute(UnlockUserCommand cmd, ActorId performedBy) { @@ -38,12 +39,13 @@ public class UnlockUser { UserId userId = UserId.of(cmd.userId()); return findUser(userId) .flatMap(User::unlock) - .flatMap(updated -> userRepository.save(updated) - .mapError(err -> (UserError) new UserError.RepositoryFailure(err.message())) - .map(ignored -> { - auditLogger.log(AuditEvent.USER_UNLOCKED, updated.id().value(), performedBy); - return UserDTO.from(updated); - })); + .flatMap(updated -> unitOfWork.executeAtomically(() -> + userRepository.save(updated) + .mapError(err -> (UserError) new UserError.RepositoryFailure(err.message())) + .map(ignored -> { + auditLogger.log(AuditEvent.USER_UNLOCKED, updated.id().value(), performedBy); + return UserDTO.from(updated); + }))); } private Result findUser(UserId userId) { diff --git a/backend/src/main/java/de/effigenix/application/usermanagement/UpdateUser.java b/backend/src/main/java/de/effigenix/application/usermanagement/UpdateUser.java index 8a5baa0..dd6eb55 100644 --- a/backend/src/main/java/de/effigenix/application/usermanagement/UpdateUser.java +++ b/backend/src/main/java/de/effigenix/application/usermanagement/UpdateUser.java @@ -5,23 +5,24 @@ import de.effigenix.application.usermanagement.dto.UserDTO; import de.effigenix.domain.usermanagement.*; import de.effigenix.shared.common.Result; import de.effigenix.shared.security.ActorId; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.AuthorizationPort; -import org.springframework.transaction.annotation.Transactional; /** * Use Case: Update user details (email, branch). */ -@Transactional public class UpdateUser { private final UserRepository userRepository; private final AuditLogger auditLogger; private final AuthorizationPort authPort; + private final UnitOfWork unitOfWork; - public UpdateUser(UserRepository userRepository, AuditLogger auditLogger, AuthorizationPort authPort) { + public UpdateUser(UserRepository userRepository, AuditLogger auditLogger, AuthorizationPort authPort, UnitOfWork unitOfWork) { this.userRepository = userRepository; this.auditLogger = auditLogger; this.authPort = authPort; + this.unitOfWork = unitOfWork; } public Result execute(UpdateUserCommand cmd, ActorId performedBy) { @@ -54,12 +55,13 @@ public class UpdateUser { current = current.flatMap(u -> u.updateBranch(cmd.branchId())); } - return current.flatMap(updated -> userRepository.save(updated) - .mapError(err -> (UserError) new UserError.RepositoryFailure(err.message())) - .map(ignored -> { - auditLogger.log(AuditEvent.USER_UPDATED, updated.id().value(), performedBy); - return UserDTO.from(updated); - })); + return current.flatMap(updated -> unitOfWork.executeAtomically(() -> + userRepository.save(updated) + .mapError(err -> (UserError) new UserError.RepositoryFailure(err.message())) + .map(ignored -> { + auditLogger.log(AuditEvent.USER_UPDATED, updated.id().value(), performedBy); + return UserDTO.from(updated); + }))); } private Result findUser(UserId userId) { diff --git a/backend/src/main/java/de/effigenix/infrastructure/config/MasterDataUseCaseConfiguration.java b/backend/src/main/java/de/effigenix/infrastructure/config/MasterDataUseCaseConfiguration.java index 96e7a0a..5f4d425 100644 --- a/backend/src/main/java/de/effigenix/infrastructure/config/MasterDataUseCaseConfiguration.java +++ b/backend/src/main/java/de/effigenix/infrastructure/config/MasterDataUseCaseConfiguration.java @@ -5,6 +5,7 @@ import de.effigenix.domain.masterdata.ArticleRepository; import de.effigenix.domain.masterdata.CustomerRepository; import de.effigenix.domain.masterdata.ProductCategoryRepository; import de.effigenix.domain.masterdata.SupplierRepository; +import de.effigenix.shared.persistence.UnitOfWork; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -14,13 +15,13 @@ public class MasterDataUseCaseConfiguration { // ==================== Article Use Cases ==================== @Bean - public CreateArticle createArticle(ArticleRepository articleRepository) { - return new CreateArticle(articleRepository); + public CreateArticle createArticle(ArticleRepository articleRepository, UnitOfWork unitOfWork) { + return new CreateArticle(articleRepository, unitOfWork); } @Bean - public UpdateArticle updateArticle(ArticleRepository articleRepository) { - return new UpdateArticle(articleRepository); + public UpdateArticle updateArticle(ArticleRepository articleRepository, UnitOfWork unitOfWork) { + return new UpdateArticle(articleRepository, unitOfWork); } @Bean @@ -34,50 +35,50 @@ public class MasterDataUseCaseConfiguration { } @Bean - public ActivateArticle activateArticle(ArticleRepository articleRepository) { - return new ActivateArticle(articleRepository); + public ActivateArticle activateArticle(ArticleRepository articleRepository, UnitOfWork unitOfWork) { + return new ActivateArticle(articleRepository, unitOfWork); } @Bean - public DeactivateArticle deactivateArticle(ArticleRepository articleRepository) { - return new DeactivateArticle(articleRepository); + public DeactivateArticle deactivateArticle(ArticleRepository articleRepository, UnitOfWork unitOfWork) { + return new DeactivateArticle(articleRepository, unitOfWork); } @Bean - public AddSalesUnit addSalesUnit(ArticleRepository articleRepository) { - return new AddSalesUnit(articleRepository); + public AddSalesUnit addSalesUnit(ArticleRepository articleRepository, UnitOfWork unitOfWork) { + return new AddSalesUnit(articleRepository, unitOfWork); } @Bean - public RemoveSalesUnit removeSalesUnit(ArticleRepository articleRepository) { - return new RemoveSalesUnit(articleRepository); + public RemoveSalesUnit removeSalesUnit(ArticleRepository articleRepository, UnitOfWork unitOfWork) { + return new RemoveSalesUnit(articleRepository, unitOfWork); } @Bean - public UpdateSalesUnitPrice updateSalesUnitPrice(ArticleRepository articleRepository) { - return new UpdateSalesUnitPrice(articleRepository); + public UpdateSalesUnitPrice updateSalesUnitPrice(ArticleRepository articleRepository, UnitOfWork unitOfWork) { + return new UpdateSalesUnitPrice(articleRepository, unitOfWork); } @Bean - public AssignSupplier assignSupplier(ArticleRepository articleRepository) { - return new AssignSupplier(articleRepository); + public AssignSupplier assignSupplier(ArticleRepository articleRepository, UnitOfWork unitOfWork) { + return new AssignSupplier(articleRepository, unitOfWork); } @Bean - public RemoveSupplier removeSupplier(ArticleRepository articleRepository) { - return new RemoveSupplier(articleRepository); + public RemoveSupplier removeSupplier(ArticleRepository articleRepository, UnitOfWork unitOfWork) { + return new RemoveSupplier(articleRepository, unitOfWork); } // ==================== ProductCategory Use Cases ==================== @Bean - public CreateProductCategory createProductCategory(ProductCategoryRepository categoryRepository) { - return new CreateProductCategory(categoryRepository); + public CreateProductCategory createProductCategory(ProductCategoryRepository categoryRepository, UnitOfWork unitOfWork) { + return new CreateProductCategory(categoryRepository, unitOfWork); } @Bean - public UpdateProductCategory updateProductCategory(ProductCategoryRepository categoryRepository) { - return new UpdateProductCategory(categoryRepository); + public UpdateProductCategory updateProductCategory(ProductCategoryRepository categoryRepository, UnitOfWork unitOfWork) { + return new UpdateProductCategory(categoryRepository, unitOfWork); } @Bean @@ -88,21 +89,22 @@ public class MasterDataUseCaseConfiguration { @Bean public DeleteProductCategory deleteProductCategory( ProductCategoryRepository categoryRepository, - ArticleRepository articleRepository + ArticleRepository articleRepository, + UnitOfWork unitOfWork ) { - return new DeleteProductCategory(categoryRepository, articleRepository); + return new DeleteProductCategory(categoryRepository, articleRepository, unitOfWork); } // ==================== Supplier Use Cases ==================== @Bean - public CreateSupplier createSupplier(SupplierRepository supplierRepository) { - return new CreateSupplier(supplierRepository); + public CreateSupplier createSupplier(SupplierRepository supplierRepository, UnitOfWork unitOfWork) { + return new CreateSupplier(supplierRepository, unitOfWork); } @Bean - public UpdateSupplier updateSupplier(SupplierRepository supplierRepository) { - return new UpdateSupplier(supplierRepository); + public UpdateSupplier updateSupplier(SupplierRepository supplierRepository, UnitOfWork unitOfWork) { + return new UpdateSupplier(supplierRepository, unitOfWork); } @Bean @@ -116,40 +118,40 @@ public class MasterDataUseCaseConfiguration { } @Bean - public ActivateSupplier activateSupplier(SupplierRepository supplierRepository) { - return new ActivateSupplier(supplierRepository); + public ActivateSupplier activateSupplier(SupplierRepository supplierRepository, UnitOfWork unitOfWork) { + return new ActivateSupplier(supplierRepository, unitOfWork); } @Bean - public DeactivateSupplier deactivateSupplier(SupplierRepository supplierRepository) { - return new DeactivateSupplier(supplierRepository); + public DeactivateSupplier deactivateSupplier(SupplierRepository supplierRepository, UnitOfWork unitOfWork) { + return new DeactivateSupplier(supplierRepository, unitOfWork); } @Bean - public RateSupplier rateSupplier(SupplierRepository supplierRepository) { - return new RateSupplier(supplierRepository); + public RateSupplier rateSupplier(SupplierRepository supplierRepository, UnitOfWork unitOfWork) { + return new RateSupplier(supplierRepository, unitOfWork); } @Bean - public AddCertificate addCertificate(SupplierRepository supplierRepository) { - return new AddCertificate(supplierRepository); + public AddCertificate addCertificate(SupplierRepository supplierRepository, UnitOfWork unitOfWork) { + return new AddCertificate(supplierRepository, unitOfWork); } @Bean - public RemoveCertificate removeCertificate(SupplierRepository supplierRepository) { - return new RemoveCertificate(supplierRepository); + public RemoveCertificate removeCertificate(SupplierRepository supplierRepository, UnitOfWork unitOfWork) { + return new RemoveCertificate(supplierRepository, unitOfWork); } // ==================== Customer Use Cases ==================== @Bean - public CreateCustomer createCustomer(CustomerRepository customerRepository) { - return new CreateCustomer(customerRepository); + public CreateCustomer createCustomer(CustomerRepository customerRepository, UnitOfWork unitOfWork) { + return new CreateCustomer(customerRepository, unitOfWork); } @Bean - public UpdateCustomer updateCustomer(CustomerRepository customerRepository) { - return new UpdateCustomer(customerRepository); + public UpdateCustomer updateCustomer(CustomerRepository customerRepository, UnitOfWork unitOfWork) { + return new UpdateCustomer(customerRepository, unitOfWork); } @Bean @@ -163,37 +165,37 @@ public class MasterDataUseCaseConfiguration { } @Bean - public ActivateCustomer activateCustomer(CustomerRepository customerRepository) { - return new ActivateCustomer(customerRepository); + public ActivateCustomer activateCustomer(CustomerRepository customerRepository, UnitOfWork unitOfWork) { + return new ActivateCustomer(customerRepository, unitOfWork); } @Bean - public DeactivateCustomer deactivateCustomer(CustomerRepository customerRepository) { - return new DeactivateCustomer(customerRepository); + public DeactivateCustomer deactivateCustomer(CustomerRepository customerRepository, UnitOfWork unitOfWork) { + return new DeactivateCustomer(customerRepository, unitOfWork); } @Bean - public AddDeliveryAddress addDeliveryAddress(CustomerRepository customerRepository) { - return new AddDeliveryAddress(customerRepository); + public AddDeliveryAddress addDeliveryAddress(CustomerRepository customerRepository, UnitOfWork unitOfWork) { + return new AddDeliveryAddress(customerRepository, unitOfWork); } @Bean - public RemoveDeliveryAddress removeDeliveryAddress(CustomerRepository customerRepository) { - return new RemoveDeliveryAddress(customerRepository); + public RemoveDeliveryAddress removeDeliveryAddress(CustomerRepository customerRepository, UnitOfWork unitOfWork) { + return new RemoveDeliveryAddress(customerRepository, unitOfWork); } @Bean - public SetFrameContract setFrameContract(CustomerRepository customerRepository) { - return new SetFrameContract(customerRepository); + public SetFrameContract setFrameContract(CustomerRepository customerRepository, UnitOfWork unitOfWork) { + return new SetFrameContract(customerRepository, unitOfWork); } @Bean - public RemoveFrameContract removeFrameContract(CustomerRepository customerRepository) { - return new RemoveFrameContract(customerRepository); + public RemoveFrameContract removeFrameContract(CustomerRepository customerRepository, UnitOfWork unitOfWork) { + return new RemoveFrameContract(customerRepository, unitOfWork); } @Bean - public SetPreferences setPreferences(CustomerRepository customerRepository) { - return new SetPreferences(customerRepository); + public SetPreferences setPreferences(CustomerRepository customerRepository, UnitOfWork unitOfWork) { + return new SetPreferences(customerRepository, unitOfWork); } } diff --git a/backend/src/main/java/de/effigenix/infrastructure/config/UseCaseConfiguration.java b/backend/src/main/java/de/effigenix/infrastructure/config/UseCaseConfiguration.java index 973c0a0..49d3114 100644 --- a/backend/src/main/java/de/effigenix/infrastructure/config/UseCaseConfiguration.java +++ b/backend/src/main/java/de/effigenix/infrastructure/config/UseCaseConfiguration.java @@ -3,6 +3,7 @@ package de.effigenix.infrastructure.config; import de.effigenix.application.usermanagement.*; import de.effigenix.domain.usermanagement.RoleRepository; import de.effigenix.domain.usermanagement.UserRepository; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.AuthorizationPort; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -22,9 +23,10 @@ public class UseCaseConfiguration { RoleRepository roleRepository, PasswordHasher passwordHasher, AuditLogger auditLogger, - AuthorizationPort authPort + AuthorizationPort authPort, + UnitOfWork unitOfWork ) { - return new CreateUser(userRepository, roleRepository, passwordHasher, auditLogger, authPort); + return new CreateUser(userRepository, roleRepository, passwordHasher, auditLogger, authPort, unitOfWork); } @Bean @@ -32,9 +34,10 @@ public class UseCaseConfiguration { UserRepository userRepository, PasswordHasher passwordHasher, SessionManager sessionManager, - AuditLogger auditLogger + AuditLogger auditLogger, + UnitOfWork unitOfWork ) { - return new AuthenticateUser(userRepository, passwordHasher, sessionManager, auditLogger); + return new AuthenticateUser(userRepository, passwordHasher, sessionManager, auditLogger, unitOfWork); } @Bean @@ -42,18 +45,20 @@ public class UseCaseConfiguration { UserRepository userRepository, PasswordHasher passwordHasher, AuditLogger auditLogger, - AuthorizationPort authPort + AuthorizationPort authPort, + UnitOfWork unitOfWork ) { - return new ChangePassword(userRepository, passwordHasher, auditLogger, authPort); + return new ChangePassword(userRepository, passwordHasher, auditLogger, authPort, unitOfWork); } @Bean public UpdateUser updateUser( UserRepository userRepository, AuditLogger auditLogger, - AuthorizationPort authPort + AuthorizationPort authPort, + UnitOfWork unitOfWork ) { - return new UpdateUser(userRepository, auditLogger, authPort); + return new UpdateUser(userRepository, auditLogger, authPort, unitOfWork); } @Bean @@ -61,9 +66,10 @@ public class UseCaseConfiguration { UserRepository userRepository, RoleRepository roleRepository, AuditLogger auditLogger, - AuthorizationPort authPort + AuthorizationPort authPort, + UnitOfWork unitOfWork ) { - return new AssignRole(userRepository, roleRepository, auditLogger, authPort); + return new AssignRole(userRepository, roleRepository, auditLogger, authPort, unitOfWork); } @Bean @@ -71,27 +77,30 @@ public class UseCaseConfiguration { UserRepository userRepository, RoleRepository roleRepository, AuditLogger auditLogger, - AuthorizationPort authPort + AuthorizationPort authPort, + UnitOfWork unitOfWork ) { - return new RemoveRole(userRepository, roleRepository, auditLogger, authPort); + return new RemoveRole(userRepository, roleRepository, auditLogger, authPort, unitOfWork); } @Bean public LockUser lockUser( UserRepository userRepository, AuditLogger auditLogger, - AuthorizationPort authPort + AuthorizationPort authPort, + UnitOfWork unitOfWork ) { - return new LockUser(userRepository, auditLogger, authPort); + return new LockUser(userRepository, auditLogger, authPort, unitOfWork); } @Bean public UnlockUser unlockUser( UserRepository userRepository, AuditLogger auditLogger, - AuthorizationPort authPort + AuthorizationPort authPort, + UnitOfWork unitOfWork ) { - return new UnlockUser(userRepository, auditLogger, authPort); + return new UnlockUser(userRepository, auditLogger, authPort, unitOfWork); } @Bean diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/JdbcArticleRepository.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/JdbcArticleRepository.java new file mode 100644 index 0000000..362904b --- /dev/null +++ b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/JdbcArticleRepository.java @@ -0,0 +1,239 @@ +package de.effigenix.infrastructure.masterdata.persistence; + +import de.effigenix.domain.masterdata.*; +import de.effigenix.shared.common.Money; +import de.effigenix.shared.common.RepositoryError; +import de.effigenix.shared.common.Result; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Profile; +import org.springframework.jdbc.core.simple.JdbcClient; +import org.springframework.stereotype.Repository; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.OffsetDateTime; +import java.util.*; + +@Repository +@Profile("!no-db") +public class JdbcArticleRepository implements ArticleRepository { + + private static final Logger logger = LoggerFactory.getLogger(JdbcArticleRepository.class); + + private final JdbcClient jdbc; + + public JdbcArticleRepository(JdbcClient jdbc) { + this.jdbc = jdbc; + } + + @Override + public Result> findById(ArticleId id) { + try { + var articleOpt = jdbc.sql("SELECT * FROM articles WHERE id = :id") + .param("id", id.value()) + .query(this::mapArticleRow) + .optional(); + if (articleOpt.isEmpty()) { + return Result.success(Optional.empty()); + } + return Result.success(Optional.of(loadChildren(articleOpt.get()))); + } catch (Exception e) { + logger.trace("Database error in findById", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result> findAll() { + try { + var articles = jdbc.sql("SELECT * FROM articles ORDER BY name") + .query(this::mapArticleRow) + .list() + .stream() + .map(this::loadChildren) + .toList(); + return Result.success(articles); + } catch (Exception e) { + logger.trace("Database error in findAll", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result> findByCategory(ProductCategoryId categoryId) { + try { + var articles = jdbc.sql("SELECT * FROM articles WHERE category_id = :categoryId ORDER BY name") + .param("categoryId", categoryId.value()) + .query(this::mapArticleRow) + .list() + .stream() + .map(this::loadChildren) + .toList(); + return Result.success(articles); + } catch (Exception e) { + logger.trace("Database error in findByCategory", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result> findByStatus(ArticleStatus status) { + try { + var articles = jdbc.sql("SELECT * FROM articles WHERE status = :status ORDER BY name") + .param("status", status.name()) + .query(this::mapArticleRow) + .list() + .stream() + .map(this::loadChildren) + .toList(); + return Result.success(articles); + } catch (Exception e) { + logger.trace("Database error in findByStatus", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result save(Article article) { + try { + int rows = jdbc.sql(""" + UPDATE articles + SET name = :name, article_number = :articleNumber, category_id = :categoryId, + status = :status, updated_at = :updatedAt + WHERE id = :id + """) + .param("id", article.id().value()) + .param("name", article.name().value()) + .param("articleNumber", article.articleNumber().value()) + .param("categoryId", article.categoryId().value()) + .param("status", article.status().name()) + .param("updatedAt", article.updatedAt()) + .update(); + + if (rows == 0) { + jdbc.sql(""" + INSERT INTO articles (id, name, article_number, category_id, status, created_at, updated_at) + VALUES (:id, :name, :articleNumber, :categoryId, :status, :createdAt, :updatedAt) + """) + .param("id", article.id().value()) + .param("name", article.name().value()) + .param("articleNumber", article.articleNumber().value()) + .param("categoryId", article.categoryId().value()) + .param("status", article.status().name()) + .param("createdAt", article.createdAt()) + .param("updatedAt", article.updatedAt()) + .update(); + } + + saveSalesUnits(article); + saveSupplierReferences(article); + + return Result.success(null); + } catch (Exception e) { + logger.trace("Database error in save", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result delete(Article article) { + try { + jdbc.sql("DELETE FROM articles WHERE id = :id") + .param("id", article.id().value()) + .update(); + return Result.success(null); + } catch (Exception e) { + logger.trace("Database error in delete", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result existsByArticleNumber(ArticleNumber articleNumber) { + try { + int count = jdbc.sql("SELECT COUNT(*) FROM articles WHERE article_number = :articleNumber") + .param("articleNumber", articleNumber.value()) + .query(Integer.class) + .single(); + return Result.success(count > 0); + } catch (Exception e) { + logger.trace("Database error in existsByArticleNumber", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + private void saveSalesUnits(Article article) { + jdbc.sql("DELETE FROM sales_units WHERE article_id = :articleId") + .param("articleId", article.id().value()) + .update(); + + for (SalesUnit su : article.salesUnits()) { + jdbc.sql(""" + INSERT INTO sales_units (id, article_id, unit, price_model, price_amount, price_currency) + VALUES (:id, :articleId, :unit, :priceModel, :priceAmount, :priceCurrency) + """) + .param("id", su.id().value()) + .param("articleId", article.id().value()) + .param("unit", su.unit().name()) + .param("priceModel", su.priceModel().name()) + .param("priceAmount", su.price().amount()) + .param("priceCurrency", su.price().currency().getCurrencyCode()) + .update(); + } + } + + private void saveSupplierReferences(Article article) { + jdbc.sql("DELETE FROM article_suppliers WHERE article_id = :articleId") + .param("articleId", article.id().value()) + .update(); + + for (SupplierId supplierId : article.supplierReferences()) { + jdbc.sql("INSERT INTO article_suppliers (article_id, supplier_id) VALUES (:articleId, :supplierId)") + .param("articleId", article.id().value()) + .param("supplierId", supplierId.value()) + .update(); + } + } + + private Article loadChildren(Article article) { + var salesUnits = jdbc.sql("SELECT * FROM sales_units WHERE article_id = :articleId") + .param("articleId", article.id().value()) + .query(this::mapSalesUnitRow) + .list(); + + var supplierIds = jdbc.sql("SELECT supplier_id FROM article_suppliers WHERE article_id = :articleId") + .param("articleId", article.id().value()) + .query((rs, rowNum) -> SupplierId.of(rs.getString("supplier_id"))) + .set(); + + return Article.reconstitute( + article.id(), article.name(), article.articleNumber(), article.categoryId(), + salesUnits, article.status(), supplierIds, article.createdAt(), article.updatedAt() + ); + } + + private Article mapArticleRow(ResultSet rs, int rowNum) throws SQLException { + return Article.reconstitute( + ArticleId.of(rs.getString("id")), + new ArticleName(rs.getString("name")), + new ArticleNumber(rs.getString("article_number")), + ProductCategoryId.of(rs.getString("category_id")), + List.of(), + ArticleStatus.valueOf(rs.getString("status")), + Set.of(), + rs.getObject("created_at", OffsetDateTime.class), + rs.getObject("updated_at", OffsetDateTime.class) + ); + } + + private SalesUnit mapSalesUnitRow(ResultSet rs, int rowNum) throws SQLException { + return SalesUnit.reconstitute( + SalesUnitId.of(rs.getString("id")), + Unit.valueOf(rs.getString("unit")), + PriceModel.valueOf(rs.getString("price_model")), + new Money(rs.getBigDecimal("price_amount"), + java.util.Currency.getInstance(rs.getString("price_currency"))) + ); + } +} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/JdbcCustomerRepository.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/JdbcCustomerRepository.java new file mode 100644 index 0000000..2ad63ce --- /dev/null +++ b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/JdbcCustomerRepository.java @@ -0,0 +1,382 @@ +package de.effigenix.infrastructure.masterdata.persistence; + +import de.effigenix.domain.masterdata.*; +import de.effigenix.shared.common.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Profile; +import org.springframework.jdbc.core.simple.JdbcClient; +import org.springframework.stereotype.Repository; + +import java.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.util.*; + +@Repository +@Profile("!no-db") +public class JdbcCustomerRepository implements CustomerRepository { + + private static final Logger logger = LoggerFactory.getLogger(JdbcCustomerRepository.class); + + private final JdbcClient jdbc; + + public JdbcCustomerRepository(JdbcClient jdbc) { + this.jdbc = jdbc; + } + + @Override + public Result> findById(CustomerId id) { + try { + var customerOpt = jdbc.sql("SELECT * FROM customers WHERE id = :id") + .param("id", id.value()) + .query(this::mapCustomerRow) + .optional(); + if (customerOpt.isEmpty()) { + return Result.success(Optional.empty()); + } + return Result.success(Optional.of(loadChildren(customerOpt.get()))); + } catch (Exception e) { + logger.trace("Database error in findById", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result> findAll() { + try { + var customers = jdbc.sql("SELECT * FROM customers ORDER BY name") + .query(this::mapCustomerRow) + .list() + .stream() + .map(this::loadChildren) + .toList(); + return Result.success(customers); + } catch (Exception e) { + logger.trace("Database error in findAll", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result> findByType(CustomerType type) { + try { + var customers = jdbc.sql("SELECT * FROM customers WHERE type = :type ORDER BY name") + .param("type", type.name()) + .query(this::mapCustomerRow) + .list() + .stream() + .map(this::loadChildren) + .toList(); + return Result.success(customers); + } catch (Exception e) { + logger.trace("Database error in findByType", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result> findByStatus(CustomerStatus status) { + try { + var customers = jdbc.sql("SELECT * FROM customers WHERE status = :status ORDER BY name") + .param("status", status.name()) + .query(this::mapCustomerRow) + .list() + .stream() + .map(this::loadChildren) + .toList(); + return Result.success(customers); + } catch (Exception e) { + logger.trace("Database error in findByStatus", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result save(Customer customer) { + try { + int rows = jdbc.sql(""" + UPDATE customers + SET name = :name, type = :type, phone = :phone, email = :email, + contact_person = :contactPerson, + billing_street = :billingStreet, billing_house_number = :billingHouseNumber, + billing_postal_code = :billingPostalCode, billing_city = :billingCity, + billing_country = :billingCountry, + payment_due_days = :paymentDueDays, payment_description = :paymentDescription, + status = :status, updated_at = :updatedAt + WHERE id = :id + """) + .param("id", customer.id().value()) + .params(customerParams(customer)) + .update(); + + if (rows == 0) { + jdbc.sql(""" + INSERT INTO customers + (id, name, type, phone, email, contact_person, + billing_street, billing_house_number, billing_postal_code, billing_city, billing_country, + payment_due_days, payment_description, status, created_at, updated_at) + VALUES (:id, :name, :type, :phone, :email, :contactPerson, + :billingStreet, :billingHouseNumber, :billingPostalCode, :billingCity, :billingCountry, + :paymentDueDays, :paymentDescription, :status, :createdAt, :updatedAt) + """) + .param("id", customer.id().value()) + .param("createdAt", customer.createdAt()) + .params(customerParams(customer)) + .update(); + } + + saveDeliveryAddresses(customer); + savePreferences(customer); + saveFrameContract(customer); + + return Result.success(null); + } catch (Exception e) { + logger.trace("Database error in save", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result delete(Customer customer) { + try { + jdbc.sql("DELETE FROM frame_contracts WHERE customer_id = :customerId") + .param("customerId", customer.id().value()) + .update(); + jdbc.sql("DELETE FROM customers WHERE id = :id") + .param("id", customer.id().value()) + .update(); + return Result.success(null); + } catch (Exception e) { + logger.trace("Database error in delete", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result existsByName(CustomerName name) { + try { + int count = jdbc.sql("SELECT COUNT(*) FROM customers WHERE name = :name") + .param("name", name.value()) + .query(Integer.class) + .single(); + return Result.success(count > 0); + } catch (Exception e) { + logger.trace("Database error in existsByName", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + private Map customerParams(Customer customer) { + var params = new LinkedHashMap(); + params.put("name", customer.name().value()); + params.put("type", customer.type().name()); + params.put("phone", customer.contactInfo() != null ? customer.contactInfo().phone() : null); + params.put("email", customer.contactInfo() != null ? customer.contactInfo().email() : null); + params.put("contactPerson", customer.contactInfo() != null ? customer.contactInfo().contactPerson() : null); + params.put("billingStreet", customer.billingAddress().street()); + params.put("billingHouseNumber", customer.billingAddress().houseNumber()); + params.put("billingPostalCode", customer.billingAddress().postalCode()); + params.put("billingCity", customer.billingAddress().city()); + params.put("billingCountry", customer.billingAddress().country()); + params.put("paymentDueDays", customer.paymentTerms() != null ? customer.paymentTerms().paymentDueDays() : null); + params.put("paymentDescription", customer.paymentTerms() != null ? customer.paymentTerms().description() : null); + params.put("status", customer.status().name()); + params.put("updatedAt", customer.updatedAt()); + return params; + } + + private void saveDeliveryAddresses(Customer customer) { + jdbc.sql("DELETE FROM delivery_addresses WHERE customer_id = :customerId") + .param("customerId", customer.id().value()) + .update(); + + for (DeliveryAddress da : customer.deliveryAddresses()) { + jdbc.sql(""" + INSERT INTO delivery_addresses + (customer_id, label, street, house_number, postal_code, city, country, contact_person, delivery_notes) + VALUES (:customerId, :label, :street, :houseNumber, :postalCode, :city, :country, :contactPerson, :deliveryNotes) + """) + .param("customerId", customer.id().value()) + .param("label", da.label()) + .param("street", da.address().street()) + .param("houseNumber", da.address().houseNumber()) + .param("postalCode", da.address().postalCode()) + .param("city", da.address().city()) + .param("country", da.address().country()) + .param("contactPerson", da.contactPerson()) + .param("deliveryNotes", da.deliveryNotes()) + .update(); + } + } + + private void savePreferences(Customer customer) { + jdbc.sql("DELETE FROM customer_preferences WHERE customer_id = :customerId") + .param("customerId", customer.id().value()) + .update(); + + for (CustomerPreference pref : customer.preferences()) { + jdbc.sql("INSERT INTO customer_preferences (customer_id, preference) VALUES (:customerId, :preference)") + .param("customerId", customer.id().value()) + .param("preference", pref.name()) + .update(); + } + } + + private void saveFrameContract(Customer customer) { + jdbc.sql("DELETE FROM frame_contracts WHERE customer_id = :customerId") + .param("customerId", customer.id().value()) + .update(); + + if (customer.frameContract() != null) { + var fc = customer.frameContract(); + jdbc.sql(""" + INSERT INTO frame_contracts (id, customer_id, valid_from, valid_until, delivery_rhythm) + VALUES (:id, :customerId, :validFrom, :validUntil, :deliveryRhythm) + """) + .param("id", fc.id().value()) + .param("customerId", customer.id().value()) + .param("validFrom", fc.validFrom()) + .param("validUntil", fc.validUntil()) + .param("deliveryRhythm", fc.deliveryRhythm().name()) + .update(); + + saveContractLineItems(fc); + } + } + + private void saveContractLineItems(FrameContract fc) { + jdbc.sql("DELETE FROM contract_line_items WHERE frame_contract_id = :fcId") + .param("fcId", fc.id().value()) + .update(); + + for (ContractLineItem item : fc.lineItems()) { + jdbc.sql(""" + INSERT INTO contract_line_items + (frame_contract_id, article_id, agreed_price_amount, agreed_price_currency, agreed_quantity, unit) + VALUES (:fcId, :articleId, :priceAmount, :priceCurrency, :quantity, :unit) + """) + .param("fcId", fc.id().value()) + .param("articleId", item.articleId().value()) + .param("priceAmount", item.agreedPrice().amount()) + .param("priceCurrency", item.agreedPrice().currency().getCurrencyCode()) + .param("quantity", item.agreedQuantity()) + .param("unit", item.unit() != null ? item.unit().name() : null) + .update(); + } + } + + private Customer loadChildren(Customer customer) { + var deliveryAddresses = jdbc.sql( + "SELECT * FROM delivery_addresses WHERE customer_id = :customerId") + .param("customerId", customer.id().value()) + .query(this::mapDeliveryAddressRow) + .list(); + + var preferences = jdbc.sql( + "SELECT preference FROM customer_preferences WHERE customer_id = :customerId") + .param("customerId", customer.id().value()) + .query((rs, rowNum) -> CustomerPreference.valueOf(rs.getString("preference"))) + .set(); + + FrameContract frameContract = loadFrameContract(customer.id().value()); + + return Customer.reconstitute( + customer.id(), customer.name(), customer.type(), customer.billingAddress(), + customer.contactInfo(), customer.paymentTerms(), deliveryAddresses, + frameContract, preferences, customer.status(), customer.createdAt(), customer.updatedAt() + ); + } + + private FrameContract loadFrameContract(String customerId) { + var fcOpt = jdbc.sql("SELECT * FROM frame_contracts WHERE customer_id = :customerId") + .param("customerId", customerId) + .query(this::mapFrameContractRow) + .optional(); + + if (fcOpt.isEmpty()) { + return null; + } + + var fc = fcOpt.get(); + var lineItems = jdbc.sql( + "SELECT * FROM contract_line_items WHERE frame_contract_id = :fcId") + .param("fcId", fc.id().value()) + .query(this::mapContractLineItemRow) + .list(); + + return FrameContract.reconstitute(fc.id(), fc.validFrom(), fc.validUntil(), + fc.deliveryRhythm(), lineItems); + } + + private Customer mapCustomerRow(ResultSet rs, int rowNum) throws SQLException { + Address billingAddress = new Address( + rs.getString("billing_street"), + rs.getString("billing_house_number"), + rs.getString("billing_postal_code"), + rs.getString("billing_city"), + rs.getString("billing_country") + ); + + ContactInfo contactInfo = new ContactInfo( + rs.getString("phone"), rs.getString("email"), rs.getString("contact_person")); + + PaymentTerms paymentTerms = null; + Integer dueDays = rs.getObject("payment_due_days", Integer.class); + if (dueDays != null) { + paymentTerms = new PaymentTerms(dueDays, rs.getString("payment_description")); + } + + return Customer.reconstitute( + CustomerId.of(rs.getString("id")), + new CustomerName(rs.getString("name")), + CustomerType.valueOf(rs.getString("type")), + billingAddress, + contactInfo, + paymentTerms, + List.of(), + null, + Set.of(), + CustomerStatus.valueOf(rs.getString("status")), + rs.getObject("created_at", OffsetDateTime.class), + rs.getObject("updated_at", OffsetDateTime.class) + ); + } + + private DeliveryAddress mapDeliveryAddressRow(ResultSet rs, int rowNum) throws SQLException { + Address address = new Address( + rs.getString("street"), + rs.getString("house_number"), + rs.getString("postal_code"), + rs.getString("city"), + rs.getString("country") + ); + return new DeliveryAddress( + rs.getString("label"), address, + rs.getString("contact_person"), rs.getString("delivery_notes") + ); + } + + private FrameContract mapFrameContractRow(ResultSet rs, int rowNum) throws SQLException { + return FrameContract.reconstitute( + FrameContractId.of(rs.getString("id")), + rs.getObject("valid_from", LocalDate.class), + rs.getObject("valid_until", LocalDate.class), + DeliveryRhythm.valueOf(rs.getString("delivery_rhythm")), + List.of() + ); + } + + private ContractLineItem mapContractLineItemRow(ResultSet rs, int rowNum) throws SQLException { + String unitStr = rs.getString("unit"); + return new ContractLineItem( + ArticleId.of(rs.getString("article_id")), + new Money(rs.getBigDecimal("agreed_price_amount"), + Currency.getInstance(rs.getString("agreed_price_currency"))), + rs.getObject("agreed_quantity", BigDecimal.class), + unitStr != null ? Unit.valueOf(unitStr) : null + ); + } +} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/JdbcProductCategoryRepository.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/JdbcProductCategoryRepository.java new file mode 100644 index 0000000..860b97e --- /dev/null +++ b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/JdbcProductCategoryRepository.java @@ -0,0 +1,120 @@ +package de.effigenix.infrastructure.masterdata.persistence; + +import de.effigenix.domain.masterdata.*; +import de.effigenix.shared.common.RepositoryError; +import de.effigenix.shared.common.Result; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Profile; +import org.springframework.jdbc.core.simple.JdbcClient; +import org.springframework.stereotype.Repository; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; +import java.util.Optional; + +@Repository +@Profile("!no-db") +public class JdbcProductCategoryRepository implements ProductCategoryRepository { + + private static final Logger logger = LoggerFactory.getLogger(JdbcProductCategoryRepository.class); + + private final JdbcClient jdbc; + + public JdbcProductCategoryRepository(JdbcClient jdbc) { + this.jdbc = jdbc; + } + + @Override + public Result> findById(ProductCategoryId id) { + try { + var result = jdbc.sql("SELECT * FROM product_categories WHERE id = :id") + .param("id", id.value()) + .query(this::mapRow) + .optional(); + return Result.success(result); + } catch (Exception e) { + logger.trace("Database error in findById", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result> findAll() { + try { + var result = jdbc.sql("SELECT * FROM product_categories ORDER BY name") + .query(this::mapRow) + .list(); + return Result.success(result); + } catch (Exception e) { + logger.trace("Database error in findAll", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result save(ProductCategory category) { + try { + int rows = jdbc.sql(""" + UPDATE product_categories SET name = :name, description = :description + WHERE id = :id + """) + .param("id", category.id().value()) + .param("name", category.name().value()) + .param("description", category.description()) + .update(); + + if (rows == 0) { + jdbc.sql(""" + INSERT INTO product_categories (id, name, description) + VALUES (:id, :name, :description) + """) + .param("id", category.id().value()) + .param("name", category.name().value()) + .param("description", category.description()) + .update(); + } + + return Result.success(null); + } catch (Exception e) { + logger.trace("Database error in save", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result delete(ProductCategory category) { + try { + jdbc.sql("DELETE FROM product_categories WHERE id = :id") + .param("id", category.id().value()) + .update(); + return Result.success(null); + } catch (Exception e) { + logger.trace("Database error in delete", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result existsByName(CategoryName name) { + try { + int count = jdbc.sql("SELECT COUNT(*) FROM product_categories WHERE name = :name") + .param("name", name.value()) + .query(Integer.class) + .single(); + return Result.success(count > 0); + } catch (Exception e) { + logger.trace("Database error in existsByName", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + private ProductCategory mapRow(ResultSet rs, int rowNum) throws SQLException { + return ProductCategory.reconstitute( + ProductCategoryId.of(rs.getString("id")), + new CategoryName(rs.getString("name")), + rs.getString("description") + ); + } +} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/JdbcSupplierRepository.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/JdbcSupplierRepository.java new file mode 100644 index 0000000..de7f722 --- /dev/null +++ b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/JdbcSupplierRepository.java @@ -0,0 +1,253 @@ +package de.effigenix.infrastructure.masterdata.persistence; + +import de.effigenix.domain.masterdata.*; +import de.effigenix.shared.common.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Profile; +import org.springframework.jdbc.core.simple.JdbcClient; +import org.springframework.stereotype.Repository; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Optional; + +@Repository +@Profile("!no-db") +public class JdbcSupplierRepository implements SupplierRepository { + + private static final Logger logger = LoggerFactory.getLogger(JdbcSupplierRepository.class); + + private final JdbcClient jdbc; + + public JdbcSupplierRepository(JdbcClient jdbc) { + this.jdbc = jdbc; + } + + @Override + public Result> findById(SupplierId id) { + try { + var supplierOpt = jdbc.sql("SELECT * FROM suppliers WHERE id = :id") + .param("id", id.value()) + .query(this::mapSupplierRow) + .optional(); + if (supplierOpt.isEmpty()) { + return Result.success(Optional.empty()); + } + return Result.success(Optional.of(loadCertificates(supplierOpt.get()))); + } catch (Exception e) { + logger.trace("Database error in findById", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result> findAll() { + try { + var suppliers = jdbc.sql("SELECT * FROM suppliers ORDER BY name") + .query(this::mapSupplierRow) + .list() + .stream() + .map(this::loadCertificates) + .toList(); + return Result.success(suppliers); + } catch (Exception e) { + logger.trace("Database error in findAll", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result> findByStatus(SupplierStatus status) { + try { + var suppliers = jdbc.sql("SELECT * FROM suppliers WHERE status = :status ORDER BY name") + .param("status", status.name()) + .query(this::mapSupplierRow) + .list() + .stream() + .map(this::loadCertificates) + .toList(); + return Result.success(suppliers); + } catch (Exception e) { + logger.trace("Database error in findByStatus", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result save(Supplier supplier) { + try { + int rows = jdbc.sql(""" + UPDATE suppliers + SET name = :name, phone = :phone, email = :email, contact_person = :contactPerson, + street = :street, house_number = :houseNumber, postal_code = :postalCode, + city = :city, country = :country, + payment_due_days = :paymentDueDays, payment_description = :paymentDescription, + quality_score = :qualityScore, delivery_score = :deliveryScore, price_score = :priceScore, + status = :status, updated_at = :updatedAt + WHERE id = :id + """) + .param("id", supplier.id().value()) + .params(supplierParams(supplier)) + .update(); + + if (rows == 0) { + jdbc.sql(""" + INSERT INTO suppliers + (id, name, phone, email, contact_person, street, house_number, postal_code, city, country, + payment_due_days, payment_description, quality_score, delivery_score, price_score, + status, created_at, updated_at) + VALUES (:id, :name, :phone, :email, :contactPerson, :street, :houseNumber, :postalCode, + :city, :country, :paymentDueDays, :paymentDescription, + :qualityScore, :deliveryScore, :priceScore, :status, :createdAt, :updatedAt) + """) + .param("id", supplier.id().value()) + .param("createdAt", supplier.createdAt()) + .params(supplierParams(supplier)) + .update(); + } + + saveCertificates(supplier); + + return Result.success(null); + } catch (Exception e) { + logger.trace("Database error in save", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result delete(Supplier supplier) { + try { + jdbc.sql("DELETE FROM suppliers WHERE id = :id") + .param("id", supplier.id().value()) + .update(); + return Result.success(null); + } catch (Exception e) { + logger.trace("Database error in delete", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result existsByName(SupplierName name) { + try { + int count = jdbc.sql("SELECT COUNT(*) FROM suppliers WHERE name = :name") + .param("name", name.value()) + .query(Integer.class) + .single(); + return Result.success(count > 0); + } catch (Exception e) { + logger.trace("Database error in existsByName", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + private java.util.Map supplierParams(Supplier supplier) { + var params = new java.util.LinkedHashMap(); + params.put("name", supplier.name().value()); + params.put("phone", supplier.contactInfo() != null ? supplier.contactInfo().phone() : null); + params.put("email", supplier.contactInfo() != null ? supplier.contactInfo().email() : null); + params.put("contactPerson", supplier.contactInfo() != null ? supplier.contactInfo().contactPerson() : null); + params.put("street", supplier.address() != null ? supplier.address().street() : null); + params.put("houseNumber", supplier.address() != null ? supplier.address().houseNumber() : null); + params.put("postalCode", supplier.address() != null ? supplier.address().postalCode() : null); + params.put("city", supplier.address() != null ? supplier.address().city() : null); + params.put("country", supplier.address() != null ? supplier.address().country() : null); + params.put("paymentDueDays", supplier.paymentTerms() != null ? supplier.paymentTerms().paymentDueDays() : null); + params.put("paymentDescription", supplier.paymentTerms() != null ? supplier.paymentTerms().description() : null); + params.put("qualityScore", supplier.rating() != null ? supplier.rating().qualityScore() : null); + params.put("deliveryScore", supplier.rating() != null ? supplier.rating().deliveryScore() : null); + params.put("priceScore", supplier.rating() != null ? supplier.rating().priceScore() : null); + params.put("status", supplier.status().name()); + params.put("updatedAt", supplier.updatedAt()); + return params; + } + + private void saveCertificates(Supplier supplier) { + jdbc.sql("DELETE FROM quality_certificates WHERE supplier_id = :supplierId") + .param("supplierId", supplier.id().value()) + .update(); + + for (QualityCertificate cert : supplier.certificates()) { + jdbc.sql(""" + INSERT INTO quality_certificates (supplier_id, certificate_type, issuer, valid_from, valid_until) + VALUES (:supplierId, :certificateType, :issuer, :validFrom, :validUntil) + """) + .param("supplierId", supplier.id().value()) + .param("certificateType", cert.certificateType()) + .param("issuer", cert.issuer()) + .param("validFrom", cert.validFrom()) + .param("validUntil", cert.validUntil()) + .update(); + } + } + + private Supplier loadCertificates(Supplier supplier) { + var certificates = jdbc.sql( + "SELECT * FROM quality_certificates WHERE supplier_id = :supplierId") + .param("supplierId", supplier.id().value()) + .query(this::mapCertificateRow) + .list(); + + return Supplier.reconstitute( + supplier.id(), supplier.name(), supplier.address(), supplier.contactInfo(), + supplier.paymentTerms(), certificates, supplier.rating(), supplier.status(), + supplier.createdAt(), supplier.updatedAt() + ); + } + + private Supplier mapSupplierRow(ResultSet rs, int rowNum) throws SQLException { + // Address (optional) + Address address = null; + String street = rs.getString("street"); + if (street != null) { + address = new Address(street, rs.getString("house_number"), + rs.getString("postal_code"), rs.getString("city"), rs.getString("country")); + } + + // ContactInfo + ContactInfo contactInfo = new ContactInfo( + rs.getString("phone"), rs.getString("email"), rs.getString("contact_person")); + + // PaymentTerms (optional) + PaymentTerms paymentTerms = null; + Integer dueDays = rs.getObject("payment_due_days", Integer.class); + if (dueDays != null) { + paymentTerms = new PaymentTerms(dueDays, rs.getString("payment_description")); + } + + // SupplierRating (optional) + SupplierRating rating = null; + Integer qualityScore = rs.getObject("quality_score", Integer.class); + if (qualityScore != null) { + rating = new SupplierRating(qualityScore, + rs.getInt("delivery_score"), rs.getInt("price_score")); + } + + return Supplier.reconstitute( + SupplierId.of(rs.getString("id")), + new SupplierName(rs.getString("name")), + address, + contactInfo, + paymentTerms, + List.of(), + rating, + SupplierStatus.valueOf(rs.getString("status")), + rs.getObject("created_at", OffsetDateTime.class), + rs.getObject("updated_at", OffsetDateTime.class) + ); + } + + private QualityCertificate mapCertificateRow(ResultSet rs, int rowNum) throws SQLException { + return new QualityCertificate( + rs.getString("certificate_type"), + rs.getString("issuer"), + rs.getObject("valid_from", LocalDate.class), + rs.getObject("valid_until", LocalDate.class) + ); + } +} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/ArticleEntity.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/ArticleEntity.java deleted file mode 100644 index 39219e3..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/ArticleEntity.java +++ /dev/null @@ -1,77 +0,0 @@ -package de.effigenix.infrastructure.masterdata.persistence.entity; - -import jakarta.persistence.*; - -import java.time.OffsetDateTime; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -@Entity -@Table(name = "articles") -public class ArticleEntity { - - @Id - @Column(name = "id", nullable = false, length = 36) - private String id; - - @Column(name = "name", nullable = false, length = 200) - private String name; - - @Column(name = "article_number", nullable = false, unique = true, length = 50) - private String articleNumber; - - @Column(name = "category_id", nullable = false, length = 36) - private String categoryId; - - @Column(name = "status", nullable = false, length = 20) - private String status; - - @Column(name = "created_at", nullable = false, updatable = false) - private OffsetDateTime createdAt; - - @Column(name = "updated_at", nullable = false) - private OffsetDateTime updatedAt; - - @OneToMany(mappedBy = "article", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) - private List salesUnits = new ArrayList<>(); - - @ElementCollection(fetch = FetchType.EAGER) - @CollectionTable(name = "article_suppliers", joinColumns = @JoinColumn(name = "article_id")) - @Column(name = "supplier_id") - private Set supplierIds = new HashSet<>(); - - protected ArticleEntity() {} - - public ArticleEntity(String id, String name, String articleNumber, String categoryId, - String status, OffsetDateTime createdAt, OffsetDateTime updatedAt) { - this.id = id; - this.name = name; - this.articleNumber = articleNumber; - this.categoryId = categoryId; - this.status = status; - this.createdAt = createdAt; - this.updatedAt = updatedAt; - } - - public String getId() { return id; } - public String getName() { return name; } - public String getArticleNumber() { return articleNumber; } - public String getCategoryId() { return categoryId; } - public String getStatus() { return status; } - public OffsetDateTime getCreatedAt() { return createdAt; } - public OffsetDateTime getUpdatedAt() { return updatedAt; } - public List getSalesUnits() { return salesUnits; } - public Set getSupplierIds() { return supplierIds; } - - public void setId(String id) { this.id = id; } - public void setName(String name) { this.name = name; } - public void setArticleNumber(String articleNumber) { this.articleNumber = articleNumber; } - public void setCategoryId(String categoryId) { this.categoryId = categoryId; } - public void setStatus(String status) { this.status = status; } - public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; } - public void setUpdatedAt(OffsetDateTime updatedAt) { this.updatedAt = updatedAt; } - public void setSalesUnits(List salesUnits) { this.salesUnits = salesUnits; } - public void setSupplierIds(Set supplierIds) { this.supplierIds = supplierIds; } -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/ContractLineItemEmbeddable.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/ContractLineItemEmbeddable.java deleted file mode 100644 index e495f62..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/ContractLineItemEmbeddable.java +++ /dev/null @@ -1,42 +0,0 @@ -package de.effigenix.infrastructure.masterdata.persistence.entity; - -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; - -import java.math.BigDecimal; - -@Embeddable -public class ContractLineItemEmbeddable { - - @Column(name = "article_id", nullable = false, length = 36) - private String articleId; - - @Column(name = "agreed_price_amount", nullable = false, precision = 19, scale = 2) - private BigDecimal agreedPriceAmount; - - @Column(name = "agreed_price_currency", nullable = false, length = 3) - private String agreedPriceCurrency; - - @Column(name = "agreed_quantity", precision = 19, scale = 4) - private BigDecimal agreedQuantity; - - @Column(name = "unit", length = 30) - private String unit; - - protected ContractLineItemEmbeddable() {} - - public ContractLineItemEmbeddable(String articleId, BigDecimal agreedPriceAmount, - String agreedPriceCurrency, BigDecimal agreedQuantity, String unit) { - this.articleId = articleId; - this.agreedPriceAmount = agreedPriceAmount; - this.agreedPriceCurrency = agreedPriceCurrency; - this.agreedQuantity = agreedQuantity; - this.unit = unit; - } - - public String getArticleId() { return articleId; } - public BigDecimal getAgreedPriceAmount() { return agreedPriceAmount; } - public String getAgreedPriceCurrency() { return agreedPriceCurrency; } - public BigDecimal getAgreedQuantity() { return agreedQuantity; } - public String getUnit() { return unit; } -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/CustomerEntity.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/CustomerEntity.java deleted file mode 100644 index ff67acf..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/CustomerEntity.java +++ /dev/null @@ -1,119 +0,0 @@ -package de.effigenix.infrastructure.masterdata.persistence.entity; - -import jakarta.persistence.*; - -import java.time.OffsetDateTime; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -@Entity -@Table(name = "customers") -public class CustomerEntity { - - @Id - @Column(name = "id", nullable = false, length = 36) - private String id; - - @Column(name = "name", nullable = false, unique = true, length = 200) - private String name; - - @Column(name = "type", nullable = false, length = 10) - private String type; - - @Column(name = "phone", length = 50) - private String phone; - - @Column(name = "email", length = 255) - private String email; - - @Column(name = "contact_person", length = 200) - private String contactPerson; - - @Column(name = "billing_street", nullable = false, length = 200) - private String billingStreet; - - @Column(name = "billing_house_number", length = 20) - private String billingHouseNumber; - - @Column(name = "billing_postal_code", nullable = false, length = 20) - private String billingPostalCode; - - @Column(name = "billing_city", nullable = false, length = 100) - private String billingCity; - - @Column(name = "billing_country", nullable = false, length = 2) - private String billingCountry; - - @Column(name = "payment_due_days") - private Integer paymentDueDays; - - @Column(name = "payment_description", length = 500) - private String paymentDescription; - - @Column(name = "status", nullable = false, length = 20) - private String status; - - @Column(name = "created_at", nullable = false, updatable = false) - private OffsetDateTime createdAt; - - @Column(name = "updated_at", nullable = false) - private OffsetDateTime updatedAt; - - @ElementCollection(fetch = FetchType.EAGER) - @CollectionTable(name = "delivery_addresses", joinColumns = @JoinColumn(name = "customer_id")) - private List deliveryAddresses = new ArrayList<>(); - - @ElementCollection(fetch = FetchType.EAGER) - @CollectionTable(name = "customer_preferences", joinColumns = @JoinColumn(name = "customer_id")) - @Column(name = "preference") - @Enumerated(EnumType.STRING) - private Set preferences = new HashSet<>(); - - @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) - @JoinColumn(name = "id", referencedColumnName = "customer_id", insertable = false, updatable = false) - private FrameContractEntity frameContract; - - public CustomerEntity() {} - - public String getId() { return id; } - public String getName() { return name; } - public String getType() { return type; } - public String getPhone() { return phone; } - public String getEmail() { return email; } - public String getContactPerson() { return contactPerson; } - public String getBillingStreet() { return billingStreet; } - public String getBillingHouseNumber() { return billingHouseNumber; } - public String getBillingPostalCode() { return billingPostalCode; } - public String getBillingCity() { return billingCity; } - public String getBillingCountry() { return billingCountry; } - public Integer getPaymentDueDays() { return paymentDueDays; } - public String getPaymentDescription() { return paymentDescription; } - public String getStatus() { return status; } - public OffsetDateTime getCreatedAt() { return createdAt; } - public OffsetDateTime getUpdatedAt() { return updatedAt; } - public List getDeliveryAddresses() { return deliveryAddresses; } - public Set getPreferences() { return preferences; } - public FrameContractEntity getFrameContract() { return frameContract; } - - public void setId(String id) { this.id = id; } - public void setName(String name) { this.name = name; } - public void setType(String type) { this.type = type; } - public void setPhone(String phone) { this.phone = phone; } - public void setEmail(String email) { this.email = email; } - public void setContactPerson(String contactPerson) { this.contactPerson = contactPerson; } - public void setBillingStreet(String billingStreet) { this.billingStreet = billingStreet; } - public void setBillingHouseNumber(String billingHouseNumber) { this.billingHouseNumber = billingHouseNumber; } - public void setBillingPostalCode(String billingPostalCode) { this.billingPostalCode = billingPostalCode; } - public void setBillingCity(String billingCity) { this.billingCity = billingCity; } - public void setBillingCountry(String billingCountry) { this.billingCountry = billingCountry; } - public void setPaymentDueDays(Integer paymentDueDays) { this.paymentDueDays = paymentDueDays; } - public void setPaymentDescription(String paymentDescription) { this.paymentDescription = paymentDescription; } - public void setStatus(String status) { this.status = status; } - public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; } - public void setUpdatedAt(OffsetDateTime updatedAt) { this.updatedAt = updatedAt; } - public void setDeliveryAddresses(List deliveryAddresses) { this.deliveryAddresses = deliveryAddresses; } - public void setPreferences(Set preferences) { this.preferences = preferences; } - public void setFrameContract(FrameContractEntity frameContract) { this.frameContract = frameContract; } -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/DeliveryAddressEmbeddable.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/DeliveryAddressEmbeddable.java deleted file mode 100644 index 12926c8..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/DeliveryAddressEmbeddable.java +++ /dev/null @@ -1,56 +0,0 @@ -package de.effigenix.infrastructure.masterdata.persistence.entity; - -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; - -@Embeddable -public class DeliveryAddressEmbeddable { - - @Column(name = "label", length = 100) - private String label; - - @Column(name = "street", nullable = false, length = 200) - private String street; - - @Column(name = "house_number", length = 20) - private String houseNumber; - - @Column(name = "postal_code", nullable = false, length = 20) - private String postalCode; - - @Column(name = "city", nullable = false, length = 100) - private String city; - - @Column(name = "country", nullable = false, length = 2) - private String country; - - @Column(name = "contact_person", length = 200) - private String contactPerson; - - @Column(name = "delivery_notes", length = 500) - private String deliveryNotes; - - protected DeliveryAddressEmbeddable() {} - - public DeliveryAddressEmbeddable(String label, String street, String houseNumber, - String postalCode, String city, String country, - String contactPerson, String deliveryNotes) { - this.label = label; - this.street = street; - this.houseNumber = houseNumber; - this.postalCode = postalCode; - this.city = city; - this.country = country; - this.contactPerson = contactPerson; - this.deliveryNotes = deliveryNotes; - } - - public String getLabel() { return label; } - public String getStreet() { return street; } - public String getHouseNumber() { return houseNumber; } - public String getPostalCode() { return postalCode; } - public String getCity() { return city; } - public String getCountry() { return country; } - public String getContactPerson() { return contactPerson; } - public String getDeliveryNotes() { return deliveryNotes; } -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/FrameContractEntity.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/FrameContractEntity.java deleted file mode 100644 index 92a37cc..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/FrameContractEntity.java +++ /dev/null @@ -1,57 +0,0 @@ -package de.effigenix.infrastructure.masterdata.persistence.entity; - -import jakarta.persistence.*; - -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.List; - -@Entity -@Table(name = "frame_contracts") -public class FrameContractEntity { - - @Id - @Column(name = "id", nullable = false, length = 36) - private String id; - - @Column(name = "customer_id", nullable = false, unique = true, length = 36) - private String customerId; - - @Column(name = "valid_from") - private LocalDate validFrom; - - @Column(name = "valid_until") - private LocalDate validUntil; - - @Column(name = "delivery_rhythm", nullable = false, length = 20) - private String deliveryRhythm; - - @ElementCollection(fetch = FetchType.EAGER) - @CollectionTable(name = "contract_line_items", joinColumns = @JoinColumn(name = "frame_contract_id")) - private List lineItems = new ArrayList<>(); - - protected FrameContractEntity() {} - - public FrameContractEntity(String id, String customerId, LocalDate validFrom, - LocalDate validUntil, String deliveryRhythm) { - this.id = id; - this.customerId = customerId; - this.validFrom = validFrom; - this.validUntil = validUntil; - this.deliveryRhythm = deliveryRhythm; - } - - public String getId() { return id; } - public String getCustomerId() { return customerId; } - public LocalDate getValidFrom() { return validFrom; } - public LocalDate getValidUntil() { return validUntil; } - public String getDeliveryRhythm() { return deliveryRhythm; } - public List getLineItems() { return lineItems; } - - public void setId(String id) { this.id = id; } - public void setCustomerId(String customerId) { this.customerId = customerId; } - public void setValidFrom(LocalDate validFrom) { this.validFrom = validFrom; } - public void setValidUntil(LocalDate validUntil) { this.validUntil = validUntil; } - public void setDeliveryRhythm(String deliveryRhythm) { this.deliveryRhythm = deliveryRhythm; } - public void setLineItems(List lineItems) { this.lineItems = lineItems; } -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/ProductCategoryEntity.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/ProductCategoryEntity.java deleted file mode 100644 index 287cbbf..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/ProductCategoryEntity.java +++ /dev/null @@ -1,37 +0,0 @@ -package de.effigenix.infrastructure.masterdata.persistence.entity; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Table; - -@Entity -@Table(name = "product_categories") -public class ProductCategoryEntity { - - @Id - @Column(name = "id", nullable = false, length = 36) - private String id; - - @Column(name = "name", nullable = false, unique = true, length = 100) - private String name; - - @Column(name = "description", columnDefinition = "TEXT") - private String description; - - protected ProductCategoryEntity() {} - - public ProductCategoryEntity(String id, String name, String description) { - this.id = id; - this.name = name; - this.description = description; - } - - public String getId() { return id; } - public String getName() { return name; } - public String getDescription() { return description; } - - public void setId(String id) { this.id = id; } - public void setName(String name) { this.name = name; } - public void setDescription(String description) { this.description = description; } -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/QualityCertificateEmbeddable.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/QualityCertificateEmbeddable.java deleted file mode 100644 index a6f48a8..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/QualityCertificateEmbeddable.java +++ /dev/null @@ -1,41 +0,0 @@ -package de.effigenix.infrastructure.masterdata.persistence.entity; - -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; - -import java.time.LocalDate; - -@Embeddable -public class QualityCertificateEmbeddable { - - @Column(name = "certificate_type", nullable = false, length = 100) - private String certificateType; - - @Column(name = "issuer", length = 200) - private String issuer; - - @Column(name = "valid_from") - private LocalDate validFrom; - - @Column(name = "valid_until") - private LocalDate validUntil; - - protected QualityCertificateEmbeddable() {} - - public QualityCertificateEmbeddable(String certificateType, String issuer, LocalDate validFrom, LocalDate validUntil) { - this.certificateType = certificateType; - this.issuer = issuer; - this.validFrom = validFrom; - this.validUntil = validUntil; - } - - public String getCertificateType() { return certificateType; } - public String getIssuer() { return issuer; } - public LocalDate getValidFrom() { return validFrom; } - public LocalDate getValidUntil() { return validUntil; } - - public void setCertificateType(String certificateType) { this.certificateType = certificateType; } - public void setIssuer(String issuer) { this.issuer = issuer; } - public void setValidFrom(LocalDate validFrom) { this.validFrom = validFrom; } - public void setValidUntil(LocalDate validUntil) { this.validUntil = validUntil; } -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/SalesUnitEntity.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/SalesUnitEntity.java deleted file mode 100644 index 590b802..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/SalesUnitEntity.java +++ /dev/null @@ -1,56 +0,0 @@ -package de.effigenix.infrastructure.masterdata.persistence.entity; - -import jakarta.persistence.*; - -import java.math.BigDecimal; - -@Entity -@Table(name = "sales_units") -public class SalesUnitEntity { - - @Id - @Column(name = "id", nullable = false, length = 36) - private String id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "article_id", nullable = false) - private ArticleEntity article; - - @Column(name = "unit", nullable = false, length = 30) - private String unit; - - @Column(name = "price_model", nullable = false, length = 30) - private String priceModel; - - @Column(name = "price_amount", nullable = false, precision = 19, scale = 2) - private BigDecimal priceAmount; - - @Column(name = "price_currency", nullable = false, length = 3) - private String priceCurrency; - - protected SalesUnitEntity() {} - - public SalesUnitEntity(String id, ArticleEntity article, String unit, String priceModel, - BigDecimal priceAmount, String priceCurrency) { - this.id = id; - this.article = article; - this.unit = unit; - this.priceModel = priceModel; - this.priceAmount = priceAmount; - this.priceCurrency = priceCurrency; - } - - public String getId() { return id; } - public ArticleEntity getArticle() { return article; } - public String getUnit() { return unit; } - public String getPriceModel() { return priceModel; } - public BigDecimal getPriceAmount() { return priceAmount; } - public String getPriceCurrency() { return priceCurrency; } - - public void setId(String id) { this.id = id; } - public void setArticle(ArticleEntity article) { this.article = article; } - public void setUnit(String unit) { this.unit = unit; } - public void setPriceModel(String priceModel) { this.priceModel = priceModel; } - public void setPriceAmount(BigDecimal priceAmount) { this.priceAmount = priceAmount; } - public void setPriceCurrency(String priceCurrency) { this.priceCurrency = priceCurrency; } -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/SupplierEntity.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/SupplierEntity.java deleted file mode 100644 index 3fcc73a..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/entity/SupplierEntity.java +++ /dev/null @@ -1,113 +0,0 @@ -package de.effigenix.infrastructure.masterdata.persistence.entity; - -import jakarta.persistence.*; - -import java.time.OffsetDateTime; -import java.util.ArrayList; -import java.util.List; - -@Entity -@Table(name = "suppliers") -public class SupplierEntity { - - @Id - @Column(name = "id", nullable = false, length = 36) - private String id; - - @Column(name = "name", nullable = false, unique = true, length = 200) - private String name; - - @Column(name = "phone", length = 50) - private String phone; - - @Column(name = "email", length = 255) - private String email; - - @Column(name = "contact_person", length = 200) - private String contactPerson; - - @Column(name = "street", length = 200) - private String street; - - @Column(name = "house_number", length = 20) - private String houseNumber; - - @Column(name = "postal_code", length = 20) - private String postalCode; - - @Column(name = "city", length = 100) - private String city; - - @Column(name = "country", length = 2) - private String country; - - @Column(name = "payment_due_days") - private Integer paymentDueDays; - - @Column(name = "payment_description", length = 500) - private String paymentDescription; - - @Column(name = "quality_score") - private Integer qualityScore; - - @Column(name = "delivery_score") - private Integer deliveryScore; - - @Column(name = "price_score") - private Integer priceScore; - - @Column(name = "status", nullable = false, length = 20) - private String status; - - @Column(name = "created_at", nullable = false, updatable = false) - private OffsetDateTime createdAt; - - @Column(name = "updated_at", nullable = false) - private OffsetDateTime updatedAt; - - @ElementCollection(fetch = FetchType.EAGER) - @CollectionTable(name = "quality_certificates", joinColumns = @JoinColumn(name = "supplier_id")) - private List certificates = new ArrayList<>(); - - public SupplierEntity() {} - - public String getId() { return id; } - public String getName() { return name; } - public String getPhone() { return phone; } - public String getEmail() { return email; } - public String getContactPerson() { return contactPerson; } - public String getStreet() { return street; } - public String getHouseNumber() { return houseNumber; } - public String getPostalCode() { return postalCode; } - public String getCity() { return city; } - public String getCountry() { return country; } - public Integer getPaymentDueDays() { return paymentDueDays; } - public String getPaymentDescription() { return paymentDescription; } - public Integer getQualityScore() { return qualityScore; } - public Integer getDeliveryScore() { return deliveryScore; } - public Integer getPriceScore() { return priceScore; } - public String getStatus() { return status; } - public OffsetDateTime getCreatedAt() { return createdAt; } - public OffsetDateTime getUpdatedAt() { return updatedAt; } - public List getCertificates() { return certificates; } - - public void setId(String id) { this.id = id; } - public void setName(String name) { this.name = name; } - public void setPhone(String phone) { this.phone = phone; } - public void setEmail(String email) { this.email = email; } - public void setContactPerson(String contactPerson) { this.contactPerson = contactPerson; } - public void setStreet(String street) { this.street = street; } - public void setHouseNumber(String houseNumber) { this.houseNumber = houseNumber; } - public void setPostalCode(String postalCode) { this.postalCode = postalCode; } - public void setCity(String city) { this.city = city; } - public void setCountry(String country) { this.country = country; } - public void setPaymentDueDays(Integer paymentDueDays) { this.paymentDueDays = paymentDueDays; } - public void setPaymentDescription(String paymentDescription) { this.paymentDescription = paymentDescription; } - public void setQualityScore(Integer qualityScore) { this.qualityScore = qualityScore; } - public void setDeliveryScore(Integer deliveryScore) { this.deliveryScore = deliveryScore; } - public void setPriceScore(Integer priceScore) { this.priceScore = priceScore; } - public void setStatus(String status) { this.status = status; } - public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; } - public void setUpdatedAt(OffsetDateTime updatedAt) { this.updatedAt = updatedAt; } - public void setCertificates(List certificates) { this.certificates = certificates; } -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/mapper/ArticleMapper.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/mapper/ArticleMapper.java deleted file mode 100644 index 34955d5..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/mapper/ArticleMapper.java +++ /dev/null @@ -1,83 +0,0 @@ -package de.effigenix.infrastructure.masterdata.persistence.mapper; - -import de.effigenix.domain.masterdata.*; -import de.effigenix.infrastructure.masterdata.persistence.entity.ArticleEntity; -import de.effigenix.infrastructure.masterdata.persistence.entity.SalesUnitEntity; -import de.effigenix.shared.common.Money; -import org.springframework.stereotype.Component; - -import java.util.Currency; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -@Component -public class ArticleMapper { - - public ArticleEntity toEntity(Article article) { - var entity = new ArticleEntity( - article.id().value(), - article.name().value(), - article.articleNumber().value(), - article.categoryId().value(), - article.status().name(), - article.createdAt(), - article.updatedAt() - ); - - List salesUnitEntities = article.salesUnits().stream() - .map(su -> toSalesUnitEntity(su, entity)) - .collect(Collectors.toList()); - entity.setSalesUnits(salesUnitEntities); - - Set supplierIds = article.supplierReferences().stream() - .map(SupplierId::value) - .collect(Collectors.toSet()); - entity.setSupplierIds(supplierIds); - - return entity; - } - - public Article toDomain(ArticleEntity entity) { - List salesUnits = entity.getSalesUnits().stream() - .map(this::toDomainSalesUnit) - .collect(Collectors.toList()); - - Set supplierRefs = entity.getSupplierIds().stream() - .map(SupplierId::of) - .collect(Collectors.toSet()); - - return Article.reconstitute( - ArticleId.of(entity.getId()), - new ArticleName(entity.getName()), - new ArticleNumber(entity.getArticleNumber()), - ProductCategoryId.of(entity.getCategoryId()), - salesUnits, - ArticleStatus.valueOf(entity.getStatus()), - supplierRefs, - entity.getCreatedAt(), - entity.getUpdatedAt() - ); - } - - private SalesUnitEntity toSalesUnitEntity(SalesUnit su, ArticleEntity article) { - return new SalesUnitEntity( - su.id().value(), - article, - su.unit().name(), - su.priceModel().name(), - su.price().amount(), - su.price().currency().getCurrencyCode() - ); - } - - private SalesUnit toDomainSalesUnit(SalesUnitEntity entity) { - return SalesUnit.reconstitute( - SalesUnitId.of(entity.getId()), - Unit.valueOf(entity.getUnit()), - PriceModel.valueOf(entity.getPriceModel()), - new Money(entity.getPriceAmount(), Currency.getInstance(entity.getPriceCurrency())) - ); - } -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/mapper/CustomerMapper.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/mapper/CustomerMapper.java deleted file mode 100644 index 8b72693..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/mapper/CustomerMapper.java +++ /dev/null @@ -1,138 +0,0 @@ -package de.effigenix.infrastructure.masterdata.persistence.mapper; - -import de.effigenix.domain.masterdata.*; -import de.effigenix.infrastructure.masterdata.persistence.entity.*; -import de.effigenix.shared.common.Address; -import de.effigenix.shared.common.ContactInfo; -import de.effigenix.shared.common.Money; -import de.effigenix.shared.common.PaymentTerms; -import org.springframework.stereotype.Component; - -import java.util.Currency; -import java.util.List; -import java.util.stream.Collectors; - -@Component -public class CustomerMapper { - - public CustomerEntity toEntity(Customer customer) { - var entity = new CustomerEntity(); - entity.setId(customer.id().value()); - entity.setName(customer.name().value()); - entity.setType(customer.type().name()); - - var contact = customer.contactInfo(); - if (contact != null) { - entity.setPhone(contact.phone()); - entity.setEmail(contact.email()); - entity.setContactPerson(contact.contactPerson()); - } - - var billing = customer.billingAddress(); - entity.setBillingStreet(billing.street()); - entity.setBillingHouseNumber(billing.houseNumber()); - entity.setBillingPostalCode(billing.postalCode()); - entity.setBillingCity(billing.city()); - entity.setBillingCountry(billing.country()); - - var terms = customer.paymentTerms(); - if (terms != null) { - entity.setPaymentDueDays(terms.paymentDueDays()); - entity.setPaymentDescription(terms.description()); - } - - entity.setStatus(customer.status().name()); - entity.setCreatedAt(customer.createdAt()); - entity.setUpdatedAt(customer.updatedAt()); - - entity.setDeliveryAddresses(customer.deliveryAddresses().stream() - .map(da -> new DeliveryAddressEmbeddable( - da.label(), - da.address().street(), da.address().houseNumber(), - da.address().postalCode(), da.address().city(), da.address().country(), - da.contactPerson(), da.deliveryNotes())) - .collect(Collectors.toList())); - - entity.setPreferences(customer.preferences()); - - // FrameContract is saved separately — not via the @OneToOne mapping - // (handled in JpaCustomerRepository) - - return entity; - } - - public Customer toDomain(CustomerEntity entity, FrameContractEntity fcEntity) { - Address billingAddress = new Address( - entity.getBillingStreet(), entity.getBillingHouseNumber(), - entity.getBillingPostalCode(), entity.getBillingCity(), entity.getBillingCountry() - ); - - ContactInfo contactInfo = new ContactInfo( - entity.getPhone(), entity.getEmail(), entity.getContactPerson() - ); - - PaymentTerms paymentTerms = null; - if (entity.getPaymentDueDays() != null) { - paymentTerms = new PaymentTerms(entity.getPaymentDueDays(), entity.getPaymentDescription()); - } - - List deliveryAddresses = entity.getDeliveryAddresses().stream() - .map(da -> new DeliveryAddress( - da.getLabel(), - new Address(da.getStreet(), da.getHouseNumber(), - da.getPostalCode(), da.getCity(), da.getCountry()), - da.getContactPerson(), da.getDeliveryNotes())) - .collect(Collectors.toList()); - - FrameContract frameContract = null; - if (fcEntity != null) { - List lineItems = fcEntity.getLineItems().stream() - .map(li -> new ContractLineItem( - ArticleId.of(li.getArticleId()), - new Money(li.getAgreedPriceAmount(), Currency.getInstance(li.getAgreedPriceCurrency())), - li.getAgreedQuantity(), - li.getUnit() != null ? Unit.valueOf(li.getUnit()) : null)) - .collect(Collectors.toList()); - - frameContract = FrameContract.reconstitute( - FrameContractId.of(fcEntity.getId()), - fcEntity.getValidFrom(), - fcEntity.getValidUntil(), - DeliveryRhythm.valueOf(fcEntity.getDeliveryRhythm()), - lineItems - ); - } - - return Customer.reconstitute( - CustomerId.of(entity.getId()), - new CustomerName(entity.getName()), - CustomerType.valueOf(entity.getType()), - billingAddress, - contactInfo, - paymentTerms, - deliveryAddresses, - frameContract, - entity.getPreferences(), - CustomerStatus.valueOf(entity.getStatus()), - entity.getCreatedAt(), - entity.getUpdatedAt() - ); - } - - public FrameContractEntity toFrameContractEntity(FrameContract fc, String customerId) { - var entity = new FrameContractEntity( - fc.id().value(), customerId, - fc.validFrom(), fc.validUntil(), - fc.deliveryRhythm().name() - ); - entity.setLineItems(fc.lineItems().stream() - .map(li -> new ContractLineItemEmbeddable( - li.articleId().value(), - li.agreedPrice().amount(), - li.agreedPrice().currency().getCurrencyCode(), - li.agreedQuantity(), - li.unit() != null ? li.unit().name() : null)) - .collect(Collectors.toList())); - return entity; - } -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/mapper/ProductCategoryMapper.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/mapper/ProductCategoryMapper.java deleted file mode 100644 index ff45b05..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/mapper/ProductCategoryMapper.java +++ /dev/null @@ -1,27 +0,0 @@ -package de.effigenix.infrastructure.masterdata.persistence.mapper; - -import de.effigenix.domain.masterdata.CategoryName; -import de.effigenix.domain.masterdata.ProductCategory; -import de.effigenix.domain.masterdata.ProductCategoryId; -import de.effigenix.infrastructure.masterdata.persistence.entity.ProductCategoryEntity; -import org.springframework.stereotype.Component; - -@Component -public class ProductCategoryMapper { - - public ProductCategoryEntity toEntity(ProductCategory category) { - return new ProductCategoryEntity( - category.id().value(), - category.name().value(), - category.description() - ); - } - - public ProductCategory toDomain(ProductCategoryEntity entity) { - return ProductCategory.reconstitute( - ProductCategoryId.of(entity.getId()), - new CategoryName(entity.getName()), - entity.getDescription() - ); - } -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/mapper/SupplierMapper.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/mapper/SupplierMapper.java deleted file mode 100644 index 5d0e3f8..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/mapper/SupplierMapper.java +++ /dev/null @@ -1,107 +0,0 @@ -package de.effigenix.infrastructure.masterdata.persistence.mapper; - -import de.effigenix.domain.masterdata.*; -import de.effigenix.infrastructure.masterdata.persistence.entity.QualityCertificateEmbeddable; -import de.effigenix.infrastructure.masterdata.persistence.entity.SupplierEntity; -import de.effigenix.shared.common.Address; -import de.effigenix.shared.common.ContactInfo; -import de.effigenix.shared.common.PaymentTerms; -import org.springframework.stereotype.Component; - -import java.util.List; -import java.util.stream.Collectors; - -@Component -public class SupplierMapper { - - public SupplierEntity toEntity(Supplier supplier) { - var entity = new SupplierEntity(); - entity.setId(supplier.id().value()); - entity.setName(supplier.name().value()); - - var contact = supplier.contactInfo(); - if (contact != null) { - entity.setPhone(contact.phone()); - entity.setEmail(contact.email()); - entity.setContactPerson(contact.contactPerson()); - } - - var address = supplier.address(); - if (address != null) { - entity.setStreet(address.street()); - entity.setHouseNumber(address.houseNumber()); - entity.setPostalCode(address.postalCode()); - entity.setCity(address.city()); - entity.setCountry(address.country()); - } - - var terms = supplier.paymentTerms(); - if (terms != null) { - entity.setPaymentDueDays(terms.paymentDueDays()); - entity.setPaymentDescription(terms.description()); - } - - var rating = supplier.rating(); - if (rating != null) { - entity.setQualityScore(rating.qualityScore()); - entity.setDeliveryScore(rating.deliveryScore()); - entity.setPriceScore(rating.priceScore()); - } - - entity.setStatus(supplier.status().name()); - entity.setCreatedAt(supplier.createdAt()); - entity.setUpdatedAt(supplier.updatedAt()); - - List certs = supplier.certificates().stream() - .map(c -> new QualityCertificateEmbeddable( - c.certificateType(), c.issuer(), c.validFrom(), c.validUntil())) - .collect(Collectors.toList()); - entity.setCertificates(certs); - - return entity; - } - - public Supplier toDomain(SupplierEntity entity) { - Address address = null; - if (entity.getStreet() != null) { - address = new Address( - entity.getStreet(), entity.getHouseNumber(), - entity.getPostalCode(), entity.getCity(), entity.getCountry() - ); - } - - ContactInfo contactInfo = new ContactInfo( - entity.getPhone(), entity.getEmail(), entity.getContactPerson() - ); - - PaymentTerms paymentTerms = null; - if (entity.getPaymentDueDays() != null) { - paymentTerms = new PaymentTerms(entity.getPaymentDueDays(), entity.getPaymentDescription()); - } - - SupplierRating rating = null; - if (entity.getQualityScore() != null) { - rating = new SupplierRating( - entity.getQualityScore(), entity.getDeliveryScore(), entity.getPriceScore() - ); - } - - List certificates = entity.getCertificates().stream() - .map(c -> new QualityCertificate( - c.getCertificateType(), c.getIssuer(), c.getValidFrom(), c.getValidUntil())) - .collect(Collectors.toList()); - - return Supplier.reconstitute( - SupplierId.of(entity.getId()), - new SupplierName(entity.getName()), - address, - contactInfo, - paymentTerms, - certificates, - rating, - SupplierStatus.valueOf(entity.getStatus()), - entity.getCreatedAt(), - entity.getUpdatedAt() - ); - } -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/ArticleJpaRepository.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/ArticleJpaRepository.java deleted file mode 100644 index e532df8..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/ArticleJpaRepository.java +++ /dev/null @@ -1,15 +0,0 @@ -package de.effigenix.infrastructure.masterdata.persistence.repository; - -import de.effigenix.infrastructure.masterdata.persistence.entity.ArticleEntity; -import org.springframework.data.jpa.repository.JpaRepository; - -import java.util.List; - -public interface ArticleJpaRepository extends JpaRepository { - - List findByCategoryId(String categoryId); - - List findByStatus(String status); - - boolean existsByArticleNumber(String articleNumber); -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/CustomerJpaRepository.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/CustomerJpaRepository.java deleted file mode 100644 index 0827317..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/CustomerJpaRepository.java +++ /dev/null @@ -1,15 +0,0 @@ -package de.effigenix.infrastructure.masterdata.persistence.repository; - -import de.effigenix.infrastructure.masterdata.persistence.entity.CustomerEntity; -import org.springframework.data.jpa.repository.JpaRepository; - -import java.util.List; - -public interface CustomerJpaRepository extends JpaRepository { - - List findByType(String type); - - List findByStatus(String status); - - boolean existsByName(String name); -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/FrameContractJpaRepository.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/FrameContractJpaRepository.java deleted file mode 100644 index 4f93e19..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/FrameContractJpaRepository.java +++ /dev/null @@ -1,16 +0,0 @@ -package de.effigenix.infrastructure.masterdata.persistence.repository; - -import de.effigenix.infrastructure.masterdata.persistence.entity.FrameContractEntity; -import org.springframework.data.jpa.repository.JpaRepository; - -import java.util.List; -import java.util.Optional; - -public interface FrameContractJpaRepository extends JpaRepository { - - Optional findByCustomerId(String customerId); - - List findByCustomerIdIn(List customerIds); - - void deleteByCustomerId(String customerId); -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaArticleRepository.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaArticleRepository.java deleted file mode 100644 index 91d780d..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaArticleRepository.java +++ /dev/null @@ -1,116 +0,0 @@ -package de.effigenix.infrastructure.masterdata.persistence.repository; - -import de.effigenix.domain.masterdata.*; -import de.effigenix.infrastructure.masterdata.persistence.mapper.ArticleMapper; -import de.effigenix.shared.common.RepositoryError; -import de.effigenix.shared.common.Result; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -@Repository -@Profile("!no-db") -@Transactional(readOnly = true) -public class JpaArticleRepository implements ArticleRepository { - - private static final Logger logger = LoggerFactory.getLogger(JpaArticleRepository.class); - - private final ArticleJpaRepository jpaRepository; - private final ArticleMapper mapper; - - public JpaArticleRepository(ArticleJpaRepository jpaRepository, ArticleMapper mapper) { - this.jpaRepository = jpaRepository; - this.mapper = mapper; - } - - @Override - public Result> findById(ArticleId id) { - try { - Optional
result = jpaRepository.findById(id.value()) - .map(mapper::toDomain); - return Result.success(result); - } catch (Exception e) { - logger.trace("Database error in findById", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - public Result> findAll() { - try { - List
result = jpaRepository.findAll().stream() - .map(mapper::toDomain) - .collect(Collectors.toList()); - return Result.success(result); - } catch (Exception e) { - logger.trace("Database error in findAll", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - public Result> findByCategory(ProductCategoryId categoryId) { - try { - List
result = jpaRepository.findByCategoryId(categoryId.value()).stream() - .map(mapper::toDomain) - .collect(Collectors.toList()); - return Result.success(result); - } catch (Exception e) { - logger.trace("Database error in findByCategory", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - public Result> findByStatus(ArticleStatus status) { - try { - List
result = jpaRepository.findByStatus(status.name()).stream() - .map(mapper::toDomain) - .collect(Collectors.toList()); - return Result.success(result); - } catch (Exception e) { - logger.trace("Database error in findByStatus", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - @Transactional - public Result save(Article article) { - try { - jpaRepository.save(mapper.toEntity(article)); - return Result.success(null); - } catch (Exception e) { - logger.trace("Database error in save", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - @Transactional - public Result delete(Article article) { - try { - jpaRepository.deleteById(article.id().value()); - return Result.success(null); - } catch (Exception e) { - logger.trace("Database error in delete", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - public Result existsByArticleNumber(ArticleNumber articleNumber) { - try { - return Result.success(jpaRepository.existsByArticleNumber(articleNumber.value())); - } catch (Exception e) { - logger.trace("Database error in existsByArticleNumber", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaCustomerRepository.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaCustomerRepository.java deleted file mode 100644 index 701dbe3..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaCustomerRepository.java +++ /dev/null @@ -1,138 +0,0 @@ -package de.effigenix.infrastructure.masterdata.persistence.repository; - -import de.effigenix.domain.masterdata.*; -import de.effigenix.infrastructure.masterdata.persistence.entity.CustomerEntity; -import de.effigenix.infrastructure.masterdata.persistence.entity.FrameContractEntity; -import de.effigenix.infrastructure.masterdata.persistence.mapper.CustomerMapper; -import de.effigenix.shared.common.RepositoryError; -import de.effigenix.shared.common.Result; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Collectors; - -@Repository -@Profile("!no-db") -@Transactional(readOnly = true) -public class JpaCustomerRepository implements CustomerRepository { - - private static final Logger logger = LoggerFactory.getLogger(JpaCustomerRepository.class); - - private final CustomerJpaRepository jpaRepository; - private final FrameContractJpaRepository frameContractJpaRepository; - private final CustomerMapper mapper; - - public JpaCustomerRepository(CustomerJpaRepository jpaRepository, - FrameContractJpaRepository frameContractJpaRepository, - CustomerMapper mapper) { - this.jpaRepository = jpaRepository; - this.frameContractJpaRepository = frameContractJpaRepository; - this.mapper = mapper; - } - - @Override - public Result> findById(CustomerId id) { - try { - var entityOpt = jpaRepository.findById(id.value()); - if (entityOpt.isEmpty()) { - return Result.success(Optional.empty()); - } - var fcEntity = frameContractJpaRepository.findByCustomerId(id.value()).orElse(null); - return Result.success(Optional.of(mapper.toDomain(entityOpt.get(), fcEntity))); - } catch (Exception e) { - logger.trace("Database error in findById", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - public Result> findAll() { - try { - return Result.success(mapWithFrameContracts(jpaRepository.findAll())); - } catch (Exception e) { - logger.trace("Database error in findAll", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - public Result> findByType(CustomerType type) { - try { - return Result.success(mapWithFrameContracts(jpaRepository.findByType(type.name()))); - } catch (Exception e) { - logger.trace("Database error in findByType", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - public Result> findByStatus(CustomerStatus status) { - try { - return Result.success(mapWithFrameContracts(jpaRepository.findByStatus(status.name()))); - } catch (Exception e) { - logger.trace("Database error in findByStatus", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - private List mapWithFrameContracts(List entities) { - List customerIds = entities.stream() - .map(CustomerEntity::getId) - .toList(); - Map fcMap = frameContractJpaRepository.findByCustomerIdIn(customerIds).stream() - .collect(Collectors.toMap(FrameContractEntity::getCustomerId, Function.identity())); - return entities.stream() - .map(entity -> mapper.toDomain(entity, fcMap.get(entity.getId()))) - .collect(Collectors.toList()); - } - - @Override - @Transactional - public Result save(Customer customer) { - try { - jpaRepository.save(mapper.toEntity(customer)); - - // Handle FrameContract separately - frameContractJpaRepository.deleteByCustomerId(customer.id().value()); - if (customer.frameContract() != null) { - var fcEntity = mapper.toFrameContractEntity(customer.frameContract(), customer.id().value()); - frameContractJpaRepository.save(fcEntity); - } - - return Result.success(null); - } catch (Exception e) { - logger.trace("Database error in save", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - @Transactional - public Result delete(Customer customer) { - try { - frameContractJpaRepository.deleteByCustomerId(customer.id().value()); - jpaRepository.deleteById(customer.id().value()); - return Result.success(null); - } catch (Exception e) { - logger.trace("Database error in delete", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - public Result existsByName(CustomerName name) { - try { - return Result.success(jpaRepository.existsByName(name.value())); - } catch (Exception e) { - logger.trace("Database error in existsByName", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaProductCategoryRepository.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaProductCategoryRepository.java deleted file mode 100644 index 654f772..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaProductCategoryRepository.java +++ /dev/null @@ -1,93 +0,0 @@ -package de.effigenix.infrastructure.masterdata.persistence.repository; - -import de.effigenix.domain.masterdata.CategoryName; -import de.effigenix.domain.masterdata.ProductCategory; -import de.effigenix.domain.masterdata.ProductCategoryId; -import de.effigenix.domain.masterdata.ProductCategoryRepository; -import de.effigenix.infrastructure.masterdata.persistence.mapper.ProductCategoryMapper; -import de.effigenix.shared.common.RepositoryError; -import de.effigenix.shared.common.Result; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -@Repository -@Profile("!no-db") -@Transactional(readOnly = true) -public class JpaProductCategoryRepository implements ProductCategoryRepository { - - private static final Logger logger = LoggerFactory.getLogger(JpaProductCategoryRepository.class); - - private final ProductCategoryJpaRepository jpaRepository; - private final ProductCategoryMapper mapper; - - public JpaProductCategoryRepository(ProductCategoryJpaRepository jpaRepository, ProductCategoryMapper mapper) { - this.jpaRepository = jpaRepository; - this.mapper = mapper; - } - - @Override - public Result> findById(ProductCategoryId id) { - try { - Optional result = jpaRepository.findById(id.value()) - .map(mapper::toDomain); - return Result.success(result); - } catch (Exception e) { - logger.trace("Database error in findById", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - public Result> findAll() { - try { - List result = jpaRepository.findAll().stream() - .map(mapper::toDomain) - .collect(Collectors.toList()); - return Result.success(result); - } catch (Exception e) { - logger.trace("Database error in findAll", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - @Transactional - public Result save(ProductCategory category) { - try { - jpaRepository.save(mapper.toEntity(category)); - return Result.success(null); - } catch (Exception e) { - logger.trace("Database error in save", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - @Transactional - public Result delete(ProductCategory category) { - try { - jpaRepository.deleteById(category.id().value()); - return Result.success(null); - } catch (Exception e) { - logger.trace("Database error in delete", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - public Result existsByName(CategoryName name) { - try { - return Result.success(jpaRepository.existsByName(name.value())); - } catch (Exception e) { - logger.trace("Database error in existsByName", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaSupplierRepository.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaSupplierRepository.java deleted file mode 100644 index 072eb83..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaSupplierRepository.java +++ /dev/null @@ -1,103 +0,0 @@ -package de.effigenix.infrastructure.masterdata.persistence.repository; - -import de.effigenix.domain.masterdata.*; -import de.effigenix.infrastructure.masterdata.persistence.mapper.SupplierMapper; -import de.effigenix.shared.common.RepositoryError; -import de.effigenix.shared.common.Result; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -@Repository -@Profile("!no-db") -@Transactional(readOnly = true) -public class JpaSupplierRepository implements SupplierRepository { - - private static final Logger logger = LoggerFactory.getLogger(JpaSupplierRepository.class); - - private final SupplierJpaRepository jpaRepository; - private final SupplierMapper mapper; - - public JpaSupplierRepository(SupplierJpaRepository jpaRepository, SupplierMapper mapper) { - this.jpaRepository = jpaRepository; - this.mapper = mapper; - } - - @Override - public Result> findById(SupplierId id) { - try { - Optional result = jpaRepository.findById(id.value()) - .map(mapper::toDomain); - return Result.success(result); - } catch (Exception e) { - logger.trace("Database error in findById", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - public Result> findAll() { - try { - List result = jpaRepository.findAll().stream() - .map(mapper::toDomain) - .collect(Collectors.toList()); - return Result.success(result); - } catch (Exception e) { - logger.trace("Database error in findAll", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - public Result> findByStatus(SupplierStatus status) { - try { - List result = jpaRepository.findByStatus(status.name()).stream() - .map(mapper::toDomain) - .collect(Collectors.toList()); - return Result.success(result); - } catch (Exception e) { - logger.trace("Database error in findByStatus", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - @Transactional - public Result save(Supplier supplier) { - try { - jpaRepository.save(mapper.toEntity(supplier)); - return Result.success(null); - } catch (Exception e) { - logger.trace("Database error in save", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - @Transactional - public Result delete(Supplier supplier) { - try { - jpaRepository.deleteById(supplier.id().value()); - return Result.success(null); - } catch (Exception e) { - logger.trace("Database error in delete", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - public Result existsByName(SupplierName name) { - try { - return Result.success(jpaRepository.existsByName(name.value())); - } catch (Exception e) { - logger.trace("Database error in existsByName", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/ProductCategoryJpaRepository.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/ProductCategoryJpaRepository.java deleted file mode 100644 index 7d78faa..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/ProductCategoryJpaRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package de.effigenix.infrastructure.masterdata.persistence.repository; - -import de.effigenix.infrastructure.masterdata.persistence.entity.ProductCategoryEntity; -import org.springframework.data.jpa.repository.JpaRepository; - -import java.util.Optional; - -public interface ProductCategoryJpaRepository extends JpaRepository { - - boolean existsByName(String name); - - Optional findByName(String name); -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/SupplierJpaRepository.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/SupplierJpaRepository.java deleted file mode 100644 index 7af358b..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/SupplierJpaRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package de.effigenix.infrastructure.masterdata.persistence.repository; - -import de.effigenix.infrastructure.masterdata.persistence.entity.SupplierEntity; -import org.springframework.data.jpa.repository.JpaRepository; - -import java.util.List; - -public interface SupplierJpaRepository extends JpaRepository { - - List findByStatus(String status); - - boolean existsByName(String name); -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/JdbcRoleRepository.java b/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/JdbcRoleRepository.java new file mode 100644 index 0000000..79f568a --- /dev/null +++ b/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/JdbcRoleRepository.java @@ -0,0 +1,166 @@ +package de.effigenix.infrastructure.usermanagement.persistence; + +import de.effigenix.domain.usermanagement.*; +import de.effigenix.shared.common.RepositoryError; +import de.effigenix.shared.common.Result; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Profile; +import org.springframework.jdbc.core.simple.JdbcClient; +import org.springframework.stereotype.Repository; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; + +@Repository +@Profile("!no-db") +public class JdbcRoleRepository implements RoleRepository { + + private static final Logger logger = LoggerFactory.getLogger(JdbcRoleRepository.class); + + private final JdbcClient jdbc; + + public JdbcRoleRepository(JdbcClient jdbc) { + this.jdbc = jdbc; + } + + @Override + public Result> findById(RoleId id) { + try { + var roleOpt = jdbc.sql("SELECT * FROM roles WHERE id = :id") + .param("id", id.value()) + .query(this::mapRoleRow) + .optional(); + if (roleOpt.isEmpty()) { + return Result.success(Optional.empty()); + } + return Result.success(Optional.of(loadPermissions(roleOpt.get()))); + } catch (Exception e) { + logger.trace("Database error in findById", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result> findByName(RoleName name) { + try { + var roleOpt = jdbc.sql("SELECT * FROM roles WHERE name = :name") + .param("name", name.name()) + .query(this::mapRoleRow) + .optional(); + if (roleOpt.isEmpty()) { + return Result.success(Optional.empty()); + } + return Result.success(Optional.of(loadPermissions(roleOpt.get()))); + } catch (Exception e) { + logger.trace("Database error in findByName", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result> findAll() { + try { + var roles = jdbc.sql("SELECT * FROM roles ORDER BY name") + .query(this::mapRoleRow) + .list() + .stream() + .map(this::loadPermissions) + .toList(); + return Result.success(roles); + } catch (Exception e) { + logger.trace("Database error in findAll", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result save(Role role) { + try { + int rows = jdbc.sql(""" + UPDATE roles SET name = :name, description = :description + WHERE id = :id + """) + .param("id", role.id().value()) + .param("name", role.name().name()) + .param("description", role.description()) + .update(); + + if (rows == 0) { + jdbc.sql(""" + INSERT INTO roles (id, name, description) + VALUES (:id, :name, :description) + """) + .param("id", role.id().value()) + .param("name", role.name().name()) + .param("description", role.description()) + .update(); + } + + savePermissions(role); + + return Result.success(null); + } catch (Exception e) { + logger.trace("Database error in save", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result delete(Role role) { + try { + jdbc.sql("DELETE FROM roles WHERE id = :id") + .param("id", role.id().value()) + .update(); + return Result.success(null); + } catch (Exception e) { + logger.trace("Database error in delete", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result existsByName(RoleName name) { + try { + int count = jdbc.sql("SELECT COUNT(*) FROM roles WHERE name = :name") + .param("name", name.name()) + .query(Integer.class) + .single(); + return Result.success(count > 0); + } catch (Exception e) { + logger.trace("Database error in existsByName", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + private void savePermissions(Role role) { + jdbc.sql("DELETE FROM role_permissions WHERE role_id = :roleId") + .param("roleId", role.id().value()) + .update(); + + for (Permission permission : role.permissions()) { + jdbc.sql("INSERT INTO role_permissions (role_id, permission) VALUES (:roleId, :permission)") + .param("roleId", role.id().value()) + .param("permission", permission.name()) + .update(); + } + } + + private Role loadPermissions(Role role) { + var permissions = jdbc.sql("SELECT permission FROM role_permissions WHERE role_id = :roleId") + .param("roleId", role.id().value()) + .query((rs, rowNum) -> Permission.valueOf(rs.getString("permission"))) + .set(); + return Role.reconstitute(role.id(), role.name(), permissions, role.description()); + } + + private Role mapRoleRow(ResultSet rs, int rowNum) throws SQLException { + return Role.reconstitute( + RoleId.of(rs.getString("id")), + RoleName.valueOf(rs.getString("name")), + Set.of(), + rs.getString("description") + ); + } +} diff --git a/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/JdbcUserRepository.java b/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/JdbcUserRepository.java new file mode 100644 index 0000000..734b444 --- /dev/null +++ b/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/JdbcUserRepository.java @@ -0,0 +1,263 @@ +package de.effigenix.infrastructure.usermanagement.persistence; + +import de.effigenix.domain.usermanagement.*; +import de.effigenix.shared.common.RepositoryError; +import de.effigenix.shared.common.Result; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Profile; +import org.springframework.jdbc.core.simple.JdbcClient; +import org.springframework.stereotype.Repository; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.OffsetDateTime; +import java.util.*; + +@Repository +@Profile("!no-db") +public class JdbcUserRepository implements UserRepository { + + private static final Logger logger = LoggerFactory.getLogger(JdbcUserRepository.class); + + private final JdbcClient jdbc; + private final RoleRepository roleRepository; + + public JdbcUserRepository(JdbcClient jdbc, RoleRepository roleRepository) { + this.jdbc = jdbc; + this.roleRepository = roleRepository; + } + + @Override + public Result> findById(UserId id) { + try { + var userOpt = jdbc.sql("SELECT * FROM users WHERE id = :id") + .param("id", id.value()) + .query(this::mapUserRow) + .optional(); + if (userOpt.isEmpty()) { + return Result.success(Optional.empty()); + } + return Result.success(Optional.of(loadRoles(userOpt.get()))); + } catch (Exception e) { + logger.trace("Database error in findById", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result> findByUsername(String username) { + try { + var userOpt = jdbc.sql("SELECT * FROM users WHERE username = :username") + .param("username", username) + .query(this::mapUserRow) + .optional(); + if (userOpt.isEmpty()) { + return Result.success(Optional.empty()); + } + return Result.success(Optional.of(loadRoles(userOpt.get()))); + } catch (Exception e) { + logger.trace("Database error in findByUsername", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result> findByEmail(String email) { + try { + var userOpt = jdbc.sql("SELECT * FROM users WHERE email = :email") + .param("email", email) + .query(this::mapUserRow) + .optional(); + if (userOpt.isEmpty()) { + return Result.success(Optional.empty()); + } + return Result.success(Optional.of(loadRoles(userOpt.get()))); + } catch (Exception e) { + logger.trace("Database error in findByEmail", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result> findByBranchId(String branchId) { + try { + var users = jdbc.sql("SELECT * FROM users WHERE branch_id = :branchId") + .param("branchId", branchId) + .query(this::mapUserRow) + .list() + .stream() + .map(this::loadRoles) + .toList(); + return Result.success(users); + } catch (Exception e) { + logger.trace("Database error in findByBranchId", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result> findByStatus(UserStatus status) { + try { + var users = jdbc.sql("SELECT * FROM users WHERE status = :status") + .param("status", status.name()) + .query(this::mapUserRow) + .list() + .stream() + .map(this::loadRoles) + .toList(); + return Result.success(users); + } catch (Exception e) { + logger.trace("Database error in findByStatus", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result> findAll() { + try { + var users = jdbc.sql("SELECT * FROM users ORDER BY username") + .query(this::mapUserRow) + .list() + .stream() + .map(this::loadRoles) + .toList(); + return Result.success(users); + } catch (Exception e) { + logger.trace("Database error in findAll", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result save(User user) { + try { + int rows = jdbc.sql(""" + UPDATE users + SET username = :username, email = :email, password_hash = :passwordHash, + branch_id = :branchId, status = :status, last_login = :lastLogin + WHERE id = :id + """) + .param("id", user.id().value()) + .param("username", user.username()) + .param("email", user.email()) + .param("passwordHash", user.passwordHash().value()) + .param("branchId", user.branchId()) + .param("status", user.status().name()) + .param("lastLogin", user.lastLogin()) + .update(); + + if (rows == 0) { + jdbc.sql(""" + INSERT INTO users (id, username, email, password_hash, branch_id, status, created_at, last_login) + VALUES (:id, :username, :email, :passwordHash, :branchId, :status, :createdAt, :lastLogin) + """) + .param("id", user.id().value()) + .param("username", user.username()) + .param("email", user.email()) + .param("passwordHash", user.passwordHash().value()) + .param("branchId", user.branchId()) + .param("status", user.status().name()) + .param("createdAt", user.createdAt()) + .param("lastLogin", user.lastLogin()) + .update(); + } + + saveUserRoles(user); + + return Result.success(null); + } catch (Exception e) { + logger.trace("Database error in save", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result delete(User user) { + try { + jdbc.sql("DELETE FROM users WHERE id = :id") + .param("id", user.id().value()) + .update(); + return Result.success(null); + } catch (Exception e) { + logger.trace("Database error in delete", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result existsByUsername(String username) { + try { + int count = jdbc.sql("SELECT COUNT(*) FROM users WHERE username = :username") + .param("username", username) + .query(Integer.class) + .single(); + return Result.success(count > 0); + } catch (Exception e) { + logger.trace("Database error in existsByUsername", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + @Override + public Result existsByEmail(String email) { + try { + int count = jdbc.sql("SELECT COUNT(*) FROM users WHERE email = :email") + .param("email", email) + .query(Integer.class) + .single(); + return Result.success(count > 0); + } catch (Exception e) { + logger.trace("Database error in existsByEmail", e); + return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); + } + } + + private void saveUserRoles(User user) { + jdbc.sql("DELETE FROM user_roles WHERE user_id = :userId") + .param("userId", user.id().value()) + .update(); + + for (Role role : user.roles()) { + jdbc.sql("INSERT INTO user_roles (user_id, role_id) VALUES (:userId, :roleId)") + .param("userId", user.id().value()) + .param("roleId", role.id().value()) + .update(); + } + } + + private User loadRoles(User user) { + var roleIds = jdbc.sql("SELECT role_id FROM user_roles WHERE user_id = :userId") + .param("userId", user.id().value()) + .query((rs, rowNum) -> rs.getString("role_id")) + .list(); + + Set roles = new HashSet<>(); + for (String roleId : roleIds) { + switch (roleRepository.findById(RoleId.of(roleId))) { + case Result.Success(var opt) -> opt.ifPresent(roles::add); + case Result.Failure(var err) -> + logger.trace("Failed to load role {}: {}", roleId, err.message()); + } + } + + return User.reconstitute( + user.id(), user.username(), user.email(), user.passwordHash(), + roles, user.branchId(), user.status(), user.createdAt(), user.lastLogin() + ); + } + + private User mapUserRow(ResultSet rs, int rowNum) throws SQLException { + return User.reconstitute( + UserId.of(rs.getString("id")), + rs.getString("username"), + rs.getString("email"), + PasswordHash.of(rs.getString("password_hash")), + Set.of(), + rs.getString("branch_id"), + UserStatus.valueOf(rs.getString("status")), + rs.getObject("created_at", OffsetDateTime.class), + rs.getObject("last_login", OffsetDateTime.class) + ); + } +} diff --git a/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/entity/RoleEntity.java b/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/entity/RoleEntity.java deleted file mode 100644 index 4635fab..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/entity/RoleEntity.java +++ /dev/null @@ -1,83 +0,0 @@ -package de.effigenix.infrastructure.usermanagement.persistence.entity; - -import de.effigenix.domain.usermanagement.Permission; -import de.effigenix.domain.usermanagement.RoleName; -import jakarta.persistence.*; - -import java.util.HashSet; -import java.util.Set; - -/** - * JPA Entity for Role. - * Infrastructure layer - NOT part of domain model! - */ -@Entity -@Table(name = "roles") -public class RoleEntity { - - @Id - @Column(name = "id", nullable = false, length = 36) - private String id; - - @Enumerated(EnumType.STRING) - @Column(name = "name", nullable = false, unique = true, length = 50) - private RoleName name; - - @ElementCollection(fetch = FetchType.EAGER) - @CollectionTable(name = "role_permissions", joinColumns = @JoinColumn(name = "role_id")) - @Enumerated(EnumType.STRING) - @Column(name = "permission", nullable = false, length = 100) - private Set permissions = new HashSet<>(); - - @Column(name = "description", length = 500) - private String description; - - // JPA requires no-arg constructor - protected RoleEntity() { - } - - public RoleEntity( - String id, - RoleName name, - Set permissions, - String description - ) { - this.id = id; - this.name = name; - this.permissions = permissions != null ? permissions : new HashSet<>(); - this.description = description; - } - - // Getters and Setters - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public RoleName getName() { - return name; - } - - public void setName(RoleName name) { - this.name = name; - } - - public Set getPermissions() { - return permissions; - } - - public void setPermissions(Set permissions) { - this.permissions = permissions; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/entity/UserEntity.java b/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/entity/UserEntity.java deleted file mode 100644 index 2877817..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/entity/UserEntity.java +++ /dev/null @@ -1,154 +0,0 @@ -package de.effigenix.infrastructure.usermanagement.persistence.entity; - -import de.effigenix.domain.usermanagement.UserStatus; -import jakarta.persistence.*; -import org.springframework.data.annotation.CreatedDate; -import org.springframework.data.jpa.domain.support.AuditingEntityListener; - -import java.time.OffsetDateTime; -import java.util.HashSet; -import java.util.Set; - -/** - * JPA Entity for User. - * Infrastructure layer - NOT part of domain model! - */ -@Entity -@Table(name = "users") -@EntityListeners(AuditingEntityListener.class) -public class UserEntity { - - @Id - @Column(name = "id", nullable = false, length = 36) - private String id; - - @Column(name = "username", nullable = false, unique = true, length = 100) - private String username; - - @Column(name = "email", nullable = false, unique = true, length = 255) - private String email; - - @Column(name = "password_hash", nullable = false, length = 60) - private String passwordHash; - - @ManyToMany(fetch = FetchType.EAGER) - @JoinTable( - name = "user_roles", - joinColumns = @JoinColumn(name = "user_id"), - inverseJoinColumns = @JoinColumn(name = "role_id") - ) - private Set roles = new HashSet<>(); - - @Column(name = "branch_id", length = 36) - private String branchId; - - @Enumerated(EnumType.STRING) - @Column(name = "status", nullable = false, length = 20) - private UserStatus status; - - @CreatedDate - @Column(name = "created_at", nullable = false, updatable = false) - private OffsetDateTime createdAt; - - @Column(name = "last_login") - private OffsetDateTime lastLogin; - - // JPA requires no-arg constructor - protected UserEntity() { - } - - public UserEntity( - String id, - String username, - String email, - String passwordHash, - Set roles, - String branchId, - UserStatus status, - OffsetDateTime createdAt, - OffsetDateTime lastLogin - ) { - this.id = id; - this.username = username; - this.email = email; - this.passwordHash = passwordHash; - this.roles = roles != null ? roles : new HashSet<>(); - this.branchId = branchId; - this.status = status; - this.createdAt = createdAt; - this.lastLogin = lastLogin; - } - - // Getters and Setters - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getPasswordHash() { - return passwordHash; - } - - public void setPasswordHash(String passwordHash) { - this.passwordHash = passwordHash; - } - - public Set getRoles() { - return roles; - } - - public void setRoles(Set roles) { - this.roles = roles; - } - - public String getBranchId() { - return branchId; - } - - public void setBranchId(String branchId) { - this.branchId = branchId; - } - - public UserStatus getStatus() { - return status; - } - - public void setStatus(UserStatus status) { - this.status = status; - } - - public OffsetDateTime getCreatedAt() { - return createdAt; - } - - public void setCreatedAt(OffsetDateTime createdAt) { - this.createdAt = createdAt; - } - - public OffsetDateTime getLastLogin() { - return lastLogin; - } - - public void setLastLogin(OffsetDateTime lastLogin) { - this.lastLogin = lastLogin; - } -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/mapper/RoleMapper.java b/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/mapper/RoleMapper.java deleted file mode 100644 index 39aff56..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/mapper/RoleMapper.java +++ /dev/null @@ -1,55 +0,0 @@ -package de.effigenix.infrastructure.usermanagement.persistence.mapper; - -import de.effigenix.domain.usermanagement.Role; -import de.effigenix.domain.usermanagement.RoleId; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import org.springframework.stereotype.Component; - -import java.util.HashSet; - -/** - * Maps between Role domain entity and RoleEntity JPA entity. - * Infrastructure Layer - translates between Domain and Persistence layers. - * - * This is a crucial part of Hexagonal Architecture: - * - Domain layer defines pure business logic (Role) - * - Infrastructure layer handles persistence (RoleEntity) - * - Mapper translates between the two layers - */ -@Component -public class RoleMapper { - - /** - * Converts a Role domain entity to a RoleEntity JPA entity. - * Used when saving to the database. - */ - public RoleEntity toEntity(Role role) { - if (role == null) { - return null; - } - - return new RoleEntity( - role.id().value(), - role.name(), - new HashSet<>(role.permissions()), - role.description() - ); - } - - /** - * Converts a RoleEntity JPA entity to a Role domain entity. - * Used when loading from the database. - */ - public Role toDomain(RoleEntity entity) { - if (entity == null) { - return null; - } - - return Role.reconstitute( - RoleId.of(entity.getId()), - entity.getName(), - new HashSet<>(entity.getPermissions()), - entity.getDescription() - ); - } -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/mapper/UserMapper.java b/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/mapper/UserMapper.java deleted file mode 100644 index 25ec114..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/mapper/UserMapper.java +++ /dev/null @@ -1,83 +0,0 @@ -package de.effigenix.infrastructure.usermanagement.persistence.mapper; - -import de.effigenix.domain.usermanagement.*; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity; -import org.springframework.stereotype.Component; - -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * Maps between User domain entity and UserEntity JPA entity. - * Infrastructure Layer - translates between Domain and Persistence layers. - * - * This is a crucial part of Hexagonal Architecture: - * - Domain layer defines pure business logic (User) - * - Infrastructure layer handles persistence (UserEntity) - * - Mapper translates between the two layers - */ -@Component -public class UserMapper { - - private final RoleMapper roleMapper; - - public UserMapper(RoleMapper roleMapper) { - this.roleMapper = roleMapper; - } - - /** - * Converts a User domain entity to a UserEntity JPA entity. - * Used when saving to the database. - */ - public UserEntity toEntity(User user) { - if (user == null) { - return null; - } - - Set roleEntities = user.roles().stream() - .map(roleMapper::toEntity) - .collect(Collectors.toSet()); - - return new UserEntity( - user.id().value(), - user.username(), - user.email(), - user.passwordHash().value(), - roleEntities, - user.branchId(), - user.status(), - user.createdAt(), - user.lastLogin() - ); - } - - /** - * Converts a UserEntity JPA entity to a User domain entity. - * Used when loading from the database. - */ - public User toDomain(UserEntity entity) { - if (entity == null) { - return null; - } - - Set roles = entity.getRoles() != null - ? entity.getRoles().stream() - .map(roleMapper::toDomain) - .collect(Collectors.toSet()) - : new HashSet<>(); - - return User.reconstitute( - UserId.of(entity.getId()), - entity.getUsername(), - entity.getEmail(), - PasswordHash.of(entity.getPasswordHash()), - roles, - entity.getBranchId(), - entity.getStatus(), - entity.getCreatedAt(), - entity.getLastLogin() - ); - } -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/repository/JpaRoleRepository.java b/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/repository/JpaRoleRepository.java deleted file mode 100644 index c71785a..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/repository/JpaRoleRepository.java +++ /dev/null @@ -1,117 +0,0 @@ -package de.effigenix.infrastructure.usermanagement.persistence.repository; - -import de.effigenix.shared.common.RepositoryError; -import de.effigenix.domain.usermanagement.Role; -import de.effigenix.domain.usermanagement.RoleId; -import de.effigenix.domain.usermanagement.RoleName; -import de.effigenix.domain.usermanagement.RoleRepository; -import de.effigenix.infrastructure.usermanagement.persistence.mapper.RoleMapper; -import de.effigenix.shared.common.Result; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -/** - * JPA Adapter for RoleRepository (Domain Interface). - * Infrastructure Layer - implements the Domain's RoleRepository interface. - * - * This is the Adapter pattern in Hexagonal Architecture: - * - Domain defines the interface (RoleRepository) - * - Infrastructure implements it (JpaRoleRepository) - * - Uses Spring Data JPA (RoleJpaRepository) internally - * - Translates between Domain and JPA entities using RoleMapper - * - * @Transactional ensures database consistency. - */ -@Repository -@Profile("!no-db") -@Transactional(readOnly = true) -public class JpaRoleRepository implements RoleRepository { - - private static final Logger logger = LoggerFactory.getLogger(JpaRoleRepository.class); - - private final RoleJpaRepository jpaRepository; - private final RoleMapper roleMapper; - - public JpaRoleRepository(RoleJpaRepository jpaRepository, RoleMapper roleMapper) { - this.jpaRepository = jpaRepository; - this.roleMapper = roleMapper; - } - - @Override - public Result> findById(RoleId id) { - try { - Optional result = jpaRepository.findById(id.value()) - .map(roleMapper::toDomain); - return Result.success(result); - } catch (Exception e) { - logger.trace("Database error in findById", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - public Result> findByName(RoleName name) { - try { - Optional result = jpaRepository.findByName(name) - .map(roleMapper::toDomain); - return Result.success(result); - } catch (Exception e) { - logger.trace("Database error in findByName", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - public Result> findAll() { - try { - List result = jpaRepository.findAll().stream() - .map(roleMapper::toDomain) - .collect(Collectors.toList()); - return Result.success(result); - } catch (Exception e) { - logger.trace("Database error in findAll", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - @Transactional - public Result save(Role role) { - try { - jpaRepository.save(roleMapper.toEntity(role)); - return Result.success(null); - } catch (Exception e) { - logger.trace("Database error in save", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - @Transactional - public Result delete(Role role) { - try { - jpaRepository.deleteById(role.id().value()); - return Result.success(null); - } catch (Exception e) { - logger.trace("Database error in delete", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - public Result existsByName(RoleName name) { - try { - return Result.success(jpaRepository.existsByName(name)); - } catch (Exception e) { - logger.trace("Database error in existsByName", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/repository/JpaUserRepository.java b/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/repository/JpaUserRepository.java deleted file mode 100644 index 64ed314..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/repository/JpaUserRepository.java +++ /dev/null @@ -1,164 +0,0 @@ -package de.effigenix.infrastructure.usermanagement.persistence.repository; - -import de.effigenix.shared.common.RepositoryError; -import de.effigenix.domain.usermanagement.User; -import de.effigenix.domain.usermanagement.UserId; -import de.effigenix.domain.usermanagement.UserRepository; -import de.effigenix.domain.usermanagement.UserStatus; -import de.effigenix.infrastructure.usermanagement.persistence.mapper.UserMapper; -import de.effigenix.shared.common.Result; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -/** - * JPA Adapter for UserRepository (Domain Interface). - * Infrastructure Layer - implements the Domain's UserRepository interface. - * - * This is the Adapter pattern in Hexagonal Architecture: - * - Domain defines the interface (UserRepository) - * - Infrastructure implements it (JpaUserRepository) - * - Uses Spring Data JPA (UserJpaRepository) internally - * - Translates between Domain and JPA entities using UserMapper - * - * @Transactional ensures database consistency. - */ -@Repository -@Profile("!no-db") -@Transactional(readOnly = true) -public class JpaUserRepository implements UserRepository { - - private static final Logger logger = LoggerFactory.getLogger(JpaUserRepository.class); - private final UserJpaRepository jpaRepository; - private final UserMapper userMapper; - - public JpaUserRepository(UserJpaRepository jpaRepository, UserMapper userMapper) { - this.jpaRepository = jpaRepository; - this.userMapper = userMapper; - } - - @Override - public Result> findById(UserId id) { - try { - Optional result = jpaRepository.findById(id.value()) - .map(userMapper::toDomain); - return Result.success(result); - } catch (Exception e) { - logger.trace("Database error in findById", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - public Result> findByUsername(String username) { - try { - Optional result = jpaRepository.findByUsername(username) - .map(userMapper::toDomain); - return Result.success(result); - } catch (Exception e) { - logger.trace("Database error in findByUsername", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - public Result> findByEmail(String email) { - try { - Optional result = jpaRepository.findByEmail(email) - .map(userMapper::toDomain); - return Result.success(result); - } catch (Exception e) { - logger.trace("Database error in findByEmail", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - public Result> findByBranchId(String branchId) { - try { - List result = jpaRepository.findByBranchId(branchId).stream() - .map(userMapper::toDomain) - .collect(Collectors.toList()); - return Result.success(result); - } catch (Exception e) { - logger.trace("Database error in findByBranchId", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - public Result> findByStatus(UserStatus status) { - try { - List result = jpaRepository.findByStatus(status).stream() - .map(userMapper::toDomain) - .collect(Collectors.toList()); - return Result.success(result); - } catch (Exception e) { - logger.trace("Database error in findByStatus", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - public Result> findAll() { - try { - List result = jpaRepository.findAll().stream() - .map(userMapper::toDomain) - .collect(Collectors.toList()); - return Result.success(result); - } catch (Exception e) { - logger.trace("Database error in findAll", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - @Transactional - public Result save(User user) { - try { - jpaRepository.save(userMapper.toEntity(user)); - return Result.success(null); - } catch (Exception e) { - logger.trace("Database error in save", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - @Transactional - public Result delete(User user) { - try { - jpaRepository.deleteById(user.id().value()); - return Result.success(null); - } catch (Exception e) { - logger.trace("Database error in delete", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - public Result existsByUsername(String username) { - try { - return Result.success(jpaRepository.existsByUsername(username)); - } catch (Exception e) { - logger.trace("Database error in existsByUsername", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } - - @Override - public Result existsByEmail(String email) { - try { - return Result.success(jpaRepository.existsByEmail(email)); - } catch (Exception e) { - logger.trace("Database error in existsByEmail", e); - return Result.failure(new RepositoryError.DatabaseError(e.getMessage())); - } - } -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/repository/RoleJpaRepository.java b/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/repository/RoleJpaRepository.java deleted file mode 100644 index ad63e89..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/repository/RoleJpaRepository.java +++ /dev/null @@ -1,28 +0,0 @@ -package de.effigenix.infrastructure.usermanagement.persistence.repository; - -import de.effigenix.domain.usermanagement.RoleName; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -import java.util.Optional; - -/** - * Spring Data JPA Repository for RoleEntity. - * Infrastructure Layer - automatically provides CRUD operations. - * - * Spring Data generates implementations at runtime based on method names. - */ -@Repository -public interface RoleJpaRepository extends JpaRepository { - - /** - * Finds a role by its name. - */ - Optional findByName(RoleName name); - - /** - * Checks if a role with the given name exists. - */ - boolean existsByName(RoleName name); -} diff --git a/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/repository/UserJpaRepository.java b/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/repository/UserJpaRepository.java deleted file mode 100644 index ad0617d..0000000 --- a/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/repository/UserJpaRepository.java +++ /dev/null @@ -1,49 +0,0 @@ -package de.effigenix.infrastructure.usermanagement.persistence.repository; - -import de.effigenix.domain.usermanagement.UserStatus; -import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -import java.util.List; -import java.util.Optional; - -/** - * Spring Data JPA Repository for UserEntity. - * Infrastructure Layer - automatically provides CRUD operations. - * - * Spring Data generates implementations at runtime based on method names. - */ -@Repository -public interface UserJpaRepository extends JpaRepository { - - /** - * Finds a user by their username. - */ - Optional findByUsername(String username); - - /** - * Finds a user by their email. - */ - Optional findByEmail(String email); - - /** - * Finds all users assigned to a specific branch. - */ - List findByBranchId(String branchId); - - /** - * Finds all users with a specific status. - */ - List findByStatus(UserStatus status); - - /** - * Checks if a username already exists. - */ - boolean existsByUsername(String username); - - /** - * Checks if an email already exists. - */ - boolean existsByEmail(String email); -} diff --git a/backend/src/test/java/de/effigenix/application/masterdata/ArticleUseCaseTest.java b/backend/src/test/java/de/effigenix/application/masterdata/ArticleUseCaseTest.java index 96c8d58..16a7524 100644 --- a/backend/src/test/java/de/effigenix/application/masterdata/ArticleUseCaseTest.java +++ b/backend/src/test/java/de/effigenix/application/masterdata/ArticleUseCaseTest.java @@ -5,6 +5,7 @@ import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.Money; import de.effigenix.shared.common.RepositoryError; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -18,6 +19,7 @@ import java.math.BigDecimal; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.*; +import java.util.function.Supplier; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -28,6 +30,7 @@ import static org.mockito.Mockito.*; class ArticleUseCaseTest { @Mock private ArticleRepository articleRepository; + @Mock private UnitOfWork unitOfWork; private final ActorId performedBy = ActorId.of("admin-user"); private final String CATEGORY_ID = UUID.randomUUID().toString(); @@ -99,7 +102,9 @@ class ArticleUseCaseTest { @BeforeEach void setUp() { - createArticle = new CreateArticle(articleRepository); + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((Supplier) inv.getArgument(0)).get()); + createArticle = new CreateArticle(articleRepository, unitOfWork); } @Test @@ -236,7 +241,9 @@ class ArticleUseCaseTest { @BeforeEach void setUp() { - updateArticle = new UpdateArticle(articleRepository); + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((Supplier) inv.getArgument(0)).get()); + updateArticle = new UpdateArticle(articleRepository, unitOfWork); } @Test @@ -497,7 +504,9 @@ class ArticleUseCaseTest { @BeforeEach void setUp() { - activateArticle = new ActivateArticle(articleRepository); + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((Supplier) inv.getArgument(0)).get()); + activateArticle = new ActivateArticle(articleRepository, unitOfWork); } @Test @@ -576,7 +585,9 @@ class ArticleUseCaseTest { @BeforeEach void setUp() { - deactivateArticle = new DeactivateArticle(articleRepository); + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((Supplier) inv.getArgument(0)).get()); + deactivateArticle = new DeactivateArticle(articleRepository, unitOfWork); } @Test @@ -648,7 +659,9 @@ class ArticleUseCaseTest { @BeforeEach void setUp() { - addSalesUnit = new AddSalesUnit(articleRepository); + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((Supplier) inv.getArgument(0)).get()); + addSalesUnit = new AddSalesUnit(articleRepository, unitOfWork); } @Test @@ -776,7 +789,9 @@ class ArticleUseCaseTest { @BeforeEach void setUp() { - removeSalesUnit = new RemoveSalesUnit(articleRepository); + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((Supplier) inv.getArgument(0)).get()); + removeSalesUnit = new RemoveSalesUnit(articleRepository, unitOfWork); } @Test @@ -881,7 +896,9 @@ class ArticleUseCaseTest { @BeforeEach void setUp() { - updateSalesUnitPrice = new UpdateSalesUnitPrice(articleRepository); + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((Supplier) inv.getArgument(0)).get()); + updateSalesUnitPrice = new UpdateSalesUnitPrice(articleRepository, unitOfWork); } @Test @@ -1004,7 +1021,9 @@ class ArticleUseCaseTest { @BeforeEach void setUp() { - assignSupplier = new AssignSupplier(articleRepository); + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((Supplier) inv.getArgument(0)).get()); + assignSupplier = new AssignSupplier(articleRepository, unitOfWork); } @Test @@ -1082,7 +1101,9 @@ class ArticleUseCaseTest { @BeforeEach void setUp() { - removeSupplier = new RemoveSupplier(articleRepository); + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((Supplier) inv.getArgument(0)).get()); + removeSupplier = new RemoveSupplier(articleRepository, unitOfWork); } @Test diff --git a/backend/src/test/java/de/effigenix/application/masterdata/CustomerUseCaseTest.java b/backend/src/test/java/de/effigenix/application/masterdata/CustomerUseCaseTest.java index 347da5a..da3a6b5 100644 --- a/backend/src/test/java/de/effigenix/application/masterdata/CustomerUseCaseTest.java +++ b/backend/src/test/java/de/effigenix/application/masterdata/CustomerUseCaseTest.java @@ -3,6 +3,7 @@ package de.effigenix.application.masterdata; import de.effigenix.application.masterdata.command.*; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.*; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -19,6 +20,7 @@ import java.time.ZoneOffset; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.function.Supplier; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -29,11 +31,14 @@ import static org.mockito.Mockito.*; class CustomerUseCaseTest { @Mock private CustomerRepository customerRepository; + @Mock private UnitOfWork unitOfWork; private ActorId performedBy; @BeforeEach void setUp() { + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((Supplier)inv.getArgument(0)).get()); performedBy = ActorId.of("admin-user"); } @@ -109,7 +114,7 @@ class CustomerUseCaseTest { @BeforeEach void setUp() { - createCustomer = new CreateCustomer(customerRepository); + createCustomer = new CreateCustomer(customerRepository, unitOfWork); } @Test @@ -215,7 +220,7 @@ class CustomerUseCaseTest { @BeforeEach void setUp() { - updateCustomer = new UpdateCustomer(customerRepository); + updateCustomer = new UpdateCustomer(customerRepository, unitOfWork); } @Test @@ -469,7 +474,7 @@ class CustomerUseCaseTest { @BeforeEach void setUp() { - activateCustomer = new ActivateCustomer(customerRepository); + activateCustomer = new ActivateCustomer(customerRepository, unitOfWork); } @Test @@ -540,7 +545,7 @@ class CustomerUseCaseTest { @BeforeEach void setUp() { - deactivateCustomer = new DeactivateCustomer(customerRepository); + deactivateCustomer = new DeactivateCustomer(customerRepository, unitOfWork); } @Test @@ -611,7 +616,7 @@ class CustomerUseCaseTest { @BeforeEach void setUp() { - addDeliveryAddress = new AddDeliveryAddress(customerRepository); + addDeliveryAddress = new AddDeliveryAddress(customerRepository, unitOfWork); } @Test @@ -712,7 +717,7 @@ class CustomerUseCaseTest { @BeforeEach void setUp() { - removeDeliveryAddress = new RemoveDeliveryAddress(customerRepository); + removeDeliveryAddress = new RemoveDeliveryAddress(customerRepository, unitOfWork); } @Test @@ -797,7 +802,7 @@ class CustomerUseCaseTest { @BeforeEach void setUp() { - setPreferences = new SetPreferences(customerRepository); + setPreferences = new SetPreferences(customerRepository, unitOfWork); } @Test @@ -887,7 +892,7 @@ class CustomerUseCaseTest { @BeforeEach void setUp() { - setFrameContract = new SetFrameContract(customerRepository); + setFrameContract = new SetFrameContract(customerRepository, unitOfWork); } private SetFrameContractCommand validFrameContractCommand(String customerId) { @@ -1049,7 +1054,7 @@ class CustomerUseCaseTest { @BeforeEach void setUp() { - removeFrameContract = new RemoveFrameContract(customerRepository); + removeFrameContract = new RemoveFrameContract(customerRepository, unitOfWork); } @Test diff --git a/backend/src/test/java/de/effigenix/application/masterdata/ProductCategoryUseCaseTest.java b/backend/src/test/java/de/effigenix/application/masterdata/ProductCategoryUseCaseTest.java index 1edcdbb..b073e77 100644 --- a/backend/src/test/java/de/effigenix/application/masterdata/ProductCategoryUseCaseTest.java +++ b/backend/src/test/java/de/effigenix/application/masterdata/ProductCategoryUseCaseTest.java @@ -5,6 +5,7 @@ import de.effigenix.application.masterdata.command.UpdateProductCategoryCommand; import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.RepositoryError; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -27,11 +28,14 @@ class ProductCategoryUseCaseTest { @Mock private ProductCategoryRepository categoryRepository; @Mock private ArticleRepository articleRepository; + @Mock private UnitOfWork unitOfWork; private ActorId performedBy; @BeforeEach void setUp() { + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((java.util.function.Supplier) inv.getArgument(0)).get()); performedBy = ActorId.of("admin-user"); } @@ -59,7 +63,7 @@ class ProductCategoryUseCaseTest { @BeforeEach void setUp() { - useCase = new CreateProductCategory(categoryRepository); + useCase = new CreateProductCategory(categoryRepository, unitOfWork); } @Test @@ -186,7 +190,7 @@ class ProductCategoryUseCaseTest { @BeforeEach void setUp() { - useCase = new UpdateProductCategory(categoryRepository); + useCase = new UpdateProductCategory(categoryRepository, unitOfWork); } @Test @@ -378,7 +382,7 @@ class ProductCategoryUseCaseTest { @BeforeEach void setUp() { - useCase = new DeleteProductCategory(categoryRepository, articleRepository); + useCase = new DeleteProductCategory(categoryRepository, articleRepository, unitOfWork); } @Test diff --git a/backend/src/test/java/de/effigenix/application/masterdata/SupplierUseCaseTest.java b/backend/src/test/java/de/effigenix/application/masterdata/SupplierUseCaseTest.java index b28772d..27c28f5 100644 --- a/backend/src/test/java/de/effigenix/application/masterdata/SupplierUseCaseTest.java +++ b/backend/src/test/java/de/effigenix/application/masterdata/SupplierUseCaseTest.java @@ -5,6 +5,7 @@ import de.effigenix.domain.masterdata.*; import de.effigenix.shared.common.ContactInfo; import de.effigenix.shared.common.RepositoryError; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -29,6 +30,7 @@ import static org.mockito.Mockito.*; class SupplierUseCaseTest { @Mock private SupplierRepository supplierRepository; + @Mock private UnitOfWork unitOfWork; private final ActorId performedBy = ActorId.of("admin-user"); @@ -105,7 +107,9 @@ class SupplierUseCaseTest { @BeforeEach void setUp() { - createSupplier = new CreateSupplier(supplierRepository); + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((java.util.function.Supplier) inv.getArgument(0)).get()); + createSupplier = new CreateSupplier(supplierRepository, unitOfWork); } @Test @@ -214,7 +218,9 @@ class SupplierUseCaseTest { @BeforeEach void setUp() { - updateSupplier = new UpdateSupplier(supplierRepository); + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((java.util.function.Supplier) inv.getArgument(0)).get()); + updateSupplier = new UpdateSupplier(supplierRepository, unitOfWork); } @Test @@ -448,7 +454,9 @@ class SupplierUseCaseTest { @BeforeEach void setUp() { - activateSupplier = new ActivateSupplier(supplierRepository); + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((java.util.function.Supplier) inv.getArgument(0)).get()); + activateSupplier = new ActivateSupplier(supplierRepository, unitOfWork); } @Test @@ -519,7 +527,9 @@ class SupplierUseCaseTest { @BeforeEach void setUp() { - deactivateSupplier = new DeactivateSupplier(supplierRepository); + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((java.util.function.Supplier) inv.getArgument(0)).get()); + deactivateSupplier = new DeactivateSupplier(supplierRepository, unitOfWork); } @Test @@ -590,7 +600,9 @@ class SupplierUseCaseTest { @BeforeEach void setUp() { - rateSupplier = new RateSupplier(supplierRepository); + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((java.util.function.Supplier) inv.getArgument(0)).get()); + rateSupplier = new RateSupplier(supplierRepository, unitOfWork); } @Test @@ -699,7 +711,9 @@ class SupplierUseCaseTest { @BeforeEach void setUp() { - addCertificate = new AddCertificate(supplierRepository); + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((java.util.function.Supplier) inv.getArgument(0)).get()); + addCertificate = new AddCertificate(supplierRepository, unitOfWork); } @Test @@ -817,7 +831,9 @@ class SupplierUseCaseTest { @BeforeEach void setUp() { - removeCertificate = new RemoveCertificate(supplierRepository); + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((java.util.function.Supplier) inv.getArgument(0)).get()); + removeCertificate = new RemoveCertificate(supplierRepository, unitOfWork); } @Test diff --git a/backend/src/test/java/de/effigenix/application/usermanagement/AssignRoleTest.java b/backend/src/test/java/de/effigenix/application/usermanagement/AssignRoleTest.java index 5d0bd6f..0c72be4 100644 --- a/backend/src/test/java/de/effigenix/application/usermanagement/AssignRoleTest.java +++ b/backend/src/test/java/de/effigenix/application/usermanagement/AssignRoleTest.java @@ -5,6 +5,7 @@ import de.effigenix.application.usermanagement.dto.UserDTO; import de.effigenix.domain.usermanagement.*; import de.effigenix.shared.common.Result; import de.effigenix.shared.security.ActorId; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.AuthorizationPort; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -17,6 +18,7 @@ import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.HashSet; import java.util.Optional; +import java.util.function.Supplier; import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; @@ -30,6 +32,7 @@ class AssignRoleTest { @Mock private RoleRepository roleRepository; @Mock private AuditLogger auditLogger; @Mock private AuthorizationPort authPort; + @Mock private UnitOfWork unitOfWork; private AssignRole assignRole; private ActorId performedBy; @@ -38,7 +41,9 @@ class AssignRoleTest { @BeforeEach void setUp() { - assignRole = new AssignRole(userRepository, roleRepository, auditLogger, authPort); + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((Supplier) inv.getArgument(0)).get()); + assignRole = new AssignRole(userRepository, roleRepository, auditLogger, authPort, unitOfWork); performedBy = ActorId.of("admin-user"); testUser = User.reconstitute( UserId.of("user-1"), "john.doe", "john@example.com", diff --git a/backend/src/test/java/de/effigenix/application/usermanagement/AuthenticateUserTest.java b/backend/src/test/java/de/effigenix/application/usermanagement/AuthenticateUserTest.java index b767e9f..37aa34f 100644 --- a/backend/src/test/java/de/effigenix/application/usermanagement/AuthenticateUserTest.java +++ b/backend/src/test/java/de/effigenix/application/usermanagement/AuthenticateUserTest.java @@ -4,6 +4,7 @@ import de.effigenix.application.usermanagement.command.AuthenticateCommand; import de.effigenix.application.usermanagement.dto.SessionToken; import de.effigenix.domain.usermanagement.*; import de.effigenix.shared.common.Result; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.ActorId; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -17,6 +18,7 @@ import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.HashSet; import java.util.Optional; +import java.util.function.Supplier; import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; @@ -30,6 +32,7 @@ class AuthenticateUserTest { @Mock private PasswordHasher passwordHasher; @Mock private SessionManager sessionManager; @Mock private AuditLogger auditLogger; + @Mock private UnitOfWork unitOfWork; @InjectMocks private AuthenticateUser authenticateUser; @@ -41,6 +44,8 @@ class AuthenticateUserTest { @BeforeEach void setUp() { + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((Supplier) inv.getArgument(0)).get()); validCommand = new AuthenticateCommand("john.doe", "Password123!"); validPasswordHash = new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"); diff --git a/backend/src/test/java/de/effigenix/application/usermanagement/ChangePasswordTest.java b/backend/src/test/java/de/effigenix/application/usermanagement/ChangePasswordTest.java index 2b8be45..b71748b 100644 --- a/backend/src/test/java/de/effigenix/application/usermanagement/ChangePasswordTest.java +++ b/backend/src/test/java/de/effigenix/application/usermanagement/ChangePasswordTest.java @@ -4,6 +4,7 @@ import de.effigenix.application.usermanagement.command.ChangePasswordCommand; import de.effigenix.domain.usermanagement.*; import de.effigenix.shared.common.Result; import de.effigenix.shared.security.ActorId; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.AuthorizationPort; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -16,6 +17,7 @@ import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.HashSet; import java.util.Optional; +import java.util.function.Supplier; import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; @@ -29,6 +31,7 @@ class ChangePasswordTest { @Mock private PasswordHasher passwordHasher; @Mock private AuditLogger auditLogger; @Mock private AuthorizationPort authPort; + @Mock private UnitOfWork unitOfWork; private ChangePassword changePassword; private User testUser; @@ -39,7 +42,9 @@ class ChangePasswordTest { @BeforeEach void setUp() { - changePassword = new ChangePassword(userRepository, passwordHasher, auditLogger, authPort); + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((Supplier) inv.getArgument(0)).get()); + changePassword = new ChangePassword(userRepository, passwordHasher, auditLogger, authPort, unitOfWork); oldPasswordHash = new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"); newPasswordHash = new PasswordHash("$2b$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"); diff --git a/backend/src/test/java/de/effigenix/application/usermanagement/CreateUserTest.java b/backend/src/test/java/de/effigenix/application/usermanagement/CreateUserTest.java index 5c186a2..ea6e0d7 100644 --- a/backend/src/test/java/de/effigenix/application/usermanagement/CreateUserTest.java +++ b/backend/src/test/java/de/effigenix/application/usermanagement/CreateUserTest.java @@ -4,6 +4,7 @@ import de.effigenix.application.usermanagement.command.CreateUserCommand; import de.effigenix.domain.usermanagement.*; import de.effigenix.shared.common.Result; import de.effigenix.shared.security.ActorId; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.AuthorizationPort; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -15,6 +16,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import java.util.HashSet; import java.util.Optional; import java.util.Set; +import java.util.function.Supplier; import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; @@ -30,6 +32,7 @@ class CreateUserTest { @Mock private PasswordHasher passwordHasher; @Mock private AuditLogger auditLogger; @Mock private AuthorizationPort authPort; + @Mock private UnitOfWork unitOfWork; private CreateUser createUser; private CreateUserCommand validCommand; @@ -38,7 +41,9 @@ class CreateUserTest { @BeforeEach void setUp() { - createUser = new CreateUser(userRepository, roleRepository, passwordHasher, auditLogger, authPort); + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((Supplier) inv.getArgument(0)).get()); + createUser = new CreateUser(userRepository, roleRepository, passwordHasher, auditLogger, authPort, unitOfWork); performedBy = ActorId.of("admin-user"); validPasswordHash = new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"); validCommand = new CreateUserCommand("john.doe", "john@example.com", "Password123!", Set.of(RoleName.PRODUCTION_WORKER), "branch-1"); diff --git a/backend/src/test/java/de/effigenix/application/usermanagement/LockUserTest.java b/backend/src/test/java/de/effigenix/application/usermanagement/LockUserTest.java index 007cefc..1f910dd 100644 --- a/backend/src/test/java/de/effigenix/application/usermanagement/LockUserTest.java +++ b/backend/src/test/java/de/effigenix/application/usermanagement/LockUserTest.java @@ -5,6 +5,7 @@ import de.effigenix.application.usermanagement.dto.UserDTO; import de.effigenix.domain.usermanagement.*; import de.effigenix.shared.common.Result; import de.effigenix.shared.security.ActorId; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.AuthorizationPort; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -17,6 +18,7 @@ import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.HashSet; import java.util.Optional; +import java.util.function.Supplier; import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; @@ -29,6 +31,7 @@ class LockUserTest { @Mock private UserRepository userRepository; @Mock private AuditLogger auditLogger; @Mock private AuthorizationPort authPort; + @Mock private UnitOfWork unitOfWork; private LockUser lockUser; private ActorId performedBy; @@ -36,7 +39,9 @@ class LockUserTest { @BeforeEach void setUp() { - lockUser = new LockUser(userRepository, auditLogger, authPort); + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((Supplier) inv.getArgument(0)).get()); + lockUser = new LockUser(userRepository, auditLogger, authPort, unitOfWork); performedBy = ActorId.of("admin-user"); activeUser = User.reconstitute( UserId.of("user-1"), "john.doe", "john@example.com", diff --git a/backend/src/test/java/de/effigenix/application/usermanagement/RemoveRoleTest.java b/backend/src/test/java/de/effigenix/application/usermanagement/RemoveRoleTest.java index a1cfde4..87d3eb3 100644 --- a/backend/src/test/java/de/effigenix/application/usermanagement/RemoveRoleTest.java +++ b/backend/src/test/java/de/effigenix/application/usermanagement/RemoveRoleTest.java @@ -5,6 +5,7 @@ import de.effigenix.application.usermanagement.dto.UserDTO; import de.effigenix.domain.usermanagement.*; import de.effigenix.shared.common.Result; import de.effigenix.shared.security.ActorId; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.AuthorizationPort; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -18,6 +19,7 @@ import java.time.ZoneOffset; import java.util.HashSet; import java.util.Optional; import java.util.Set; +import java.util.function.Supplier; import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; @@ -31,6 +33,7 @@ class RemoveRoleTest { @Mock private RoleRepository roleRepository; @Mock private AuditLogger auditLogger; @Mock private AuthorizationPort authPort; + @Mock private UnitOfWork unitOfWork; private RemoveRole removeRole; private ActorId performedBy; @@ -39,7 +42,9 @@ class RemoveRoleTest { @BeforeEach void setUp() { - removeRole = new RemoveRole(userRepository, roleRepository, auditLogger, authPort); + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((Supplier) inv.getArgument(0)).get()); + removeRole = new RemoveRole(userRepository, roleRepository, auditLogger, authPort, unitOfWork); performedBy = ActorId.of("admin-user"); workerRole = Role.reconstitute(RoleId.generate(), RoleName.PRODUCTION_WORKER, new HashSet<>(), "Production Worker"); userWithRole = User.reconstitute( diff --git a/backend/src/test/java/de/effigenix/application/usermanagement/UnlockUserTest.java b/backend/src/test/java/de/effigenix/application/usermanagement/UnlockUserTest.java index 169f479..151ad6e 100644 --- a/backend/src/test/java/de/effigenix/application/usermanagement/UnlockUserTest.java +++ b/backend/src/test/java/de/effigenix/application/usermanagement/UnlockUserTest.java @@ -5,6 +5,7 @@ import de.effigenix.application.usermanagement.dto.UserDTO; import de.effigenix.domain.usermanagement.*; import de.effigenix.shared.common.Result; import de.effigenix.shared.security.ActorId; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.AuthorizationPort; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -17,6 +18,7 @@ import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.HashSet; import java.util.Optional; +import java.util.function.Supplier; import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; @@ -29,6 +31,7 @@ class UnlockUserTest { @Mock private UserRepository userRepository; @Mock private AuditLogger auditLogger; @Mock private AuthorizationPort authPort; + @Mock private UnitOfWork unitOfWork; private UnlockUser unlockUser; private ActorId performedBy; @@ -36,7 +39,9 @@ class UnlockUserTest { @BeforeEach void setUp() { - unlockUser = new UnlockUser(userRepository, auditLogger, authPort); + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((Supplier) inv.getArgument(0)).get()); + unlockUser = new UnlockUser(userRepository, auditLogger, authPort, unitOfWork); performedBy = ActorId.of("admin-user"); lockedUser = User.reconstitute( UserId.of("user-1"), "john.doe", "john@example.com", diff --git a/backend/src/test/java/de/effigenix/application/usermanagement/UpdateUserTest.java b/backend/src/test/java/de/effigenix/application/usermanagement/UpdateUserTest.java index c985760..b075bd1 100644 --- a/backend/src/test/java/de/effigenix/application/usermanagement/UpdateUserTest.java +++ b/backend/src/test/java/de/effigenix/application/usermanagement/UpdateUserTest.java @@ -5,6 +5,7 @@ import de.effigenix.application.usermanagement.dto.UserDTO; import de.effigenix.domain.usermanagement.*; import de.effigenix.shared.common.Result; import de.effigenix.shared.security.ActorId; +import de.effigenix.shared.persistence.UnitOfWork; import de.effigenix.shared.security.AuthorizationPort; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -17,6 +18,7 @@ import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.HashSet; import java.util.Optional; +import java.util.function.Supplier; import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; @@ -29,6 +31,7 @@ class UpdateUserTest { @Mock private UserRepository userRepository; @Mock private AuditLogger auditLogger; @Mock private AuthorizationPort authPort; + @Mock private UnitOfWork unitOfWork; private UpdateUser updateUser; private ActorId performedBy; @@ -36,7 +39,9 @@ class UpdateUserTest { @BeforeEach void setUp() { - updateUser = new UpdateUser(userRepository, auditLogger, authPort); + lenient().when(unitOfWork.executeAtomically(any())) + .thenAnswer(inv -> ((Supplier) inv.getArgument(0)).get()); + updateUser = new UpdateUser(userRepository, auditLogger, authPort, unitOfWork); performedBy = ActorId.of("admin-user"); testUser = User.reconstitute( UserId.of("user-1"), "john.doe", "john@example.com", diff --git a/backend/src/test/java/de/effigenix/infrastructure/AbstractIntegrationTest.java b/backend/src/test/java/de/effigenix/infrastructure/AbstractIntegrationTest.java index f2a1f37..7d2076b 100644 --- a/backend/src/test/java/de/effigenix/infrastructure/AbstractIntegrationTest.java +++ b/backend/src/test/java/de/effigenix/infrastructure/AbstractIntegrationTest.java @@ -3,19 +3,12 @@ package de.effigenix.infrastructure; import com.fasterxml.jackson.databind.ObjectMapper; import de.effigenix.domain.usermanagement.RoleName; import de.effigenix.domain.usermanagement.UserStatus; -import de.effigenix.infrastructure.masterdata.persistence.entity.ArticleEntity; -import de.effigenix.infrastructure.masterdata.persistence.entity.ProductCategoryEntity; -import de.effigenix.infrastructure.masterdata.persistence.repository.ArticleJpaRepository; -import de.effigenix.infrastructure.masterdata.persistence.repository.ProductCategoryJpaRepository; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity; -import de.effigenix.infrastructure.usermanagement.persistence.repository.RoleJpaRepository; -import de.effigenix.infrastructure.usermanagement.persistence.repository.UserJpaRepository; import io.jsonwebtoken.Jwts; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.jdbc.core.simple.JdbcClient; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.servlet.MockMvc; import org.springframework.transaction.annotation.Transactional; @@ -44,16 +37,7 @@ public abstract class AbstractIntegrationTest { protected ObjectMapper objectMapper; @Autowired - protected UserJpaRepository userRepository; - - @Autowired - protected RoleJpaRepository roleRepository; - - @Autowired - protected ArticleJpaRepository articleRepository; - - @Autowired - protected ProductCategoryJpaRepository productCategoryRepository; + protected JdbcClient jdbc; @Value("${jwt.secret}") protected String jwtSecret; @@ -103,29 +87,70 @@ public abstract class AbstractIntegrationTest { .compact(); } - protected RoleEntity createRole(RoleName roleName, String description) { - return roleRepository.findByName(roleName).orElseGet(() -> { - RoleEntity role = new RoleEntity( - UUID.randomUUID().toString(), roleName, Set.of(), description); - return roleRepository.save(role); - }); + protected String createRole(RoleName roleName, String description) { + var existing = jdbc.sql("SELECT id FROM roles WHERE name = :name") + .param("name", roleName.name()) + .query(String.class) + .optional(); + if (existing.isPresent()) { + return existing.get(); + } + String id = UUID.randomUUID().toString(); + jdbc.sql("INSERT INTO roles (id, name, description) VALUES (:id, :name, :description)") + .param("id", id) + .param("name", roleName.name()) + .param("description", description) + .update(); + return id; } - protected UserEntity createUser(String username, String email, Set roles, String branchId) { - UserEntity user = new UserEntity( - UUID.randomUUID().toString(), username, email, - BCRYPT_PASS123, roles, - branchId, UserStatus.ACTIVE, OffsetDateTime.now(ZoneOffset.UTC), null); - return userRepository.save(user); + protected String createUser(String username, String email, Set roleIds, String branchId) { + String id = UUID.randomUUID().toString(); + jdbc.sql(""" + INSERT INTO users (id, username, email, password_hash, branch_id, status, created_at) + VALUES (:id, :username, :email, :passwordHash, :branchId, :status, :createdAt) + """) + .param("id", id) + .param("username", username) + .param("email", email) + .param("passwordHash", BCRYPT_PASS123) + .param("branchId", branchId) + .param("status", UserStatus.ACTIVE.name()) + .param("createdAt", OffsetDateTime.now(ZoneOffset.UTC)) + .update(); + + for (String roleId : roleIds) { + jdbc.sql("INSERT INTO user_roles (user_id, role_id) VALUES (:userId, :roleId)") + .param("userId", id) + .param("roleId", roleId) + .update(); + } + + return id; } protected String createArticleId() { String categoryId = UUID.randomUUID().toString(); - productCategoryRepository.save(new ProductCategoryEntity(categoryId, "TestCat-" + categoryId.substring(0, 8), null)); + jdbc.sql("INSERT INTO product_categories (id, name) VALUES (:id, :name)") + .param("id", categoryId) + .param("name", "TestCat-" + categoryId.substring(0, 8)) + .update(); + + String articleId = UUID.randomUUID().toString(); var now = OffsetDateTime.now(ZoneOffset.UTC); - var article = new ArticleEntity( - UUID.randomUUID().toString(), "TestArticle-" + UUID.randomUUID().toString().substring(0, 8), - "ART-" + UUID.randomUUID().toString().substring(0, 8), categoryId, "ACTIVE", now, now); - return articleRepository.save(article).getId(); + jdbc.sql(""" + INSERT INTO articles (id, name, article_number, category_id, status, created_at, updated_at) + VALUES (:id, :name, :articleNumber, :categoryId, :status, :createdAt, :updatedAt) + """) + .param("id", articleId) + .param("name", "TestArticle-" + UUID.randomUUID().toString().substring(0, 8)) + .param("articleNumber", "ART-" + UUID.randomUUID().toString().substring(0, 8)) + .param("categoryId", categoryId) + .param("status", "ACTIVE") + .param("createdAt", now) + .param("updatedAt", now) + .update(); + + return articleId; } } diff --git a/backend/src/test/java/de/effigenix/infrastructure/inventory/web/StockControllerIntegrationTest.java b/backend/src/test/java/de/effigenix/infrastructure/inventory/web/StockControllerIntegrationTest.java index 70703f7..49411aa 100644 --- a/backend/src/test/java/de/effigenix/infrastructure/inventory/web/StockControllerIntegrationTest.java +++ b/backend/src/test/java/de/effigenix/infrastructure/inventory/web/StockControllerIntegrationTest.java @@ -5,8 +5,6 @@ import de.effigenix.infrastructure.AbstractIntegrationTest; import de.effigenix.infrastructure.inventory.web.dto.AddStockBatchRequest; import de.effigenix.infrastructure.inventory.web.dto.CreateStockRequest; import de.effigenix.infrastructure.inventory.web.dto.ReserveStockRequest; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -40,14 +38,14 @@ class StockControllerIntegrationTest extends AbstractIntegrationTest { @BeforeEach void setUp() throws Exception { - RoleEntity adminRole = createRole(RoleName.ADMIN, "Admin"); - RoleEntity viewerRole = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); + String adminRoleId = createRole(RoleName.ADMIN, "Admin"); + String viewerRoleId = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); - UserEntity admin = createUser("stock.admin", "stock.admin@test.com", Set.of(adminRole), "BRANCH-01"); - UserEntity viewer = createUser("stock.viewer", "stock.viewer@test.com", Set.of(viewerRole), "BRANCH-01"); + String adminId = createUser("stock.admin", "stock.admin@test.com", Set.of(adminRoleId), "BRANCH-01"); + String viewerId = createUser("stock.viewer", "stock.viewer@test.com", Set.of(viewerRoleId), "BRANCH-01"); - adminToken = generateToken(admin.getId(), "stock.admin", "STOCK_WRITE,STOCK_READ"); - viewerToken = generateToken(viewer.getId(), "stock.viewer", "USER_READ"); + adminToken = generateToken(adminId, "stock.admin", "STOCK_WRITE,STOCK_READ"); + viewerToken = generateToken(viewerId, "stock.viewer", "USER_READ"); storageLocationId = createStorageLocation(); } diff --git a/backend/src/test/java/de/effigenix/infrastructure/inventory/web/StorageLocationControllerIntegrationTest.java b/backend/src/test/java/de/effigenix/infrastructure/inventory/web/StorageLocationControllerIntegrationTest.java index 608081a..ac16cf6 100644 --- a/backend/src/test/java/de/effigenix/infrastructure/inventory/web/StorageLocationControllerIntegrationTest.java +++ b/backend/src/test/java/de/effigenix/infrastructure/inventory/web/StorageLocationControllerIntegrationTest.java @@ -5,8 +5,6 @@ import de.effigenix.infrastructure.AbstractIntegrationTest; import de.effigenix.infrastructure.inventory.web.dto.CreateStorageLocationRequest; import de.effigenix.infrastructure.inventory.web.dto.CreateStockRequest; import de.effigenix.infrastructure.inventory.web.dto.UpdateStorageLocationRequest; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -35,16 +33,16 @@ class StorageLocationControllerIntegrationTest extends AbstractIntegrationTest { @BeforeEach void setUp() { - RoleEntity adminRole = createRole(RoleName.ADMIN, "Admin"); - RoleEntity viewerRole = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); + String adminRoleId = createRole(RoleName.ADMIN, "Admin"); + String viewerRoleId = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); - UserEntity admin = createUser("inv.admin", "inv.admin@test.com", Set.of(adminRole), "BRANCH-01"); - UserEntity reader = createUser("inv.reader", "inv.reader@test.com", Set.of(viewerRole), "BRANCH-01"); - UserEntity viewer = createUser("inv.viewer", "inv.viewer@test.com", Set.of(viewerRole), "BRANCH-01"); + String adminId = createUser("inv.admin", "inv.admin@test.com", Set.of(adminRoleId), "BRANCH-01"); + String readerId = createUser("inv.reader", "inv.reader@test.com", Set.of(viewerRoleId), "BRANCH-01"); + String viewerId = createUser("inv.viewer", "inv.viewer@test.com", Set.of(viewerRoleId), "BRANCH-01"); - adminToken = generateToken(admin.getId(), "inv.admin", "STOCK_WRITE,STOCK_READ"); - readerToken = generateToken(reader.getId(), "inv.reader", "STOCK_READ"); - viewerToken = generateToken(viewer.getId(), "inv.viewer", "USER_READ"); + adminToken = generateToken(adminId, "inv.admin", "STOCK_WRITE,STOCK_READ"); + readerToken = generateToken(readerId, "inv.reader", "STOCK_READ"); + viewerToken = generateToken(viewerId, "inv.viewer", "USER_READ"); } // ==================== Lagerort anlegen – Pflichtfelder ==================== diff --git a/backend/src/test/java/de/effigenix/infrastructure/masterdata/web/ArticleControllerIntegrationTest.java b/backend/src/test/java/de/effigenix/infrastructure/masterdata/web/ArticleControllerIntegrationTest.java index 4fedbbb..d36b862 100644 --- a/backend/src/test/java/de/effigenix/infrastructure/masterdata/web/ArticleControllerIntegrationTest.java +++ b/backend/src/test/java/de/effigenix/infrastructure/masterdata/web/ArticleControllerIntegrationTest.java @@ -5,8 +5,6 @@ import de.effigenix.domain.masterdata.Unit; import de.effigenix.domain.usermanagement.RoleName; import de.effigenix.infrastructure.AbstractIntegrationTest; import de.effigenix.infrastructure.masterdata.web.dto.*; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -35,14 +33,14 @@ class ArticleControllerIntegrationTest extends AbstractIntegrationTest { @BeforeEach void setUp() throws Exception { - RoleEntity adminRole = createRole(RoleName.ADMIN, "Admin"); - RoleEntity viewerRole = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); + String adminRoleId = createRole(RoleName.ADMIN, "Admin"); + String viewerRoleId = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); - UserEntity admin = createUser("art.admin", "art.admin@test.com", Set.of(adminRole), "BRANCH-01"); - UserEntity viewer = createUser("art.viewer", "art.viewer@test.com", Set.of(viewerRole), "BRANCH-01"); + String adminId = createUser("art.admin", "art.admin@test.com", Set.of(adminRoleId), "BRANCH-01"); + String viewerId = createUser("art.viewer", "art.viewer@test.com", Set.of(viewerRoleId), "BRANCH-01"); - adminToken = generateToken(admin.getId(), "art.admin", "MASTERDATA_WRITE"); - viewerToken = generateToken(viewer.getId(), "art.viewer", "USER_READ"); + adminToken = generateToken(adminId, "art.admin", "MASTERDATA_WRITE"); + viewerToken = generateToken(viewerId, "art.viewer", "USER_READ"); // Vorbedingung: Kategorie erstellen categoryId = createCategory("Obst & Gemüse"); diff --git a/backend/src/test/java/de/effigenix/infrastructure/masterdata/web/CustomerControllerIntegrationTest.java b/backend/src/test/java/de/effigenix/infrastructure/masterdata/web/CustomerControllerIntegrationTest.java index 7638871..271810d 100644 --- a/backend/src/test/java/de/effigenix/infrastructure/masterdata/web/CustomerControllerIntegrationTest.java +++ b/backend/src/test/java/de/effigenix/infrastructure/masterdata/web/CustomerControllerIntegrationTest.java @@ -4,8 +4,6 @@ import de.effigenix.domain.masterdata.*; import de.effigenix.domain.usermanagement.RoleName; import de.effigenix.infrastructure.AbstractIntegrationTest; import de.effigenix.infrastructure.masterdata.web.dto.*; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -35,14 +33,14 @@ class CustomerControllerIntegrationTest extends AbstractIntegrationTest { @BeforeEach void setUp() { - RoleEntity adminRole = createRole(RoleName.ADMIN, "Admin"); - RoleEntity viewerRole = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); + String adminRoleId = createRole(RoleName.ADMIN, "Admin"); + String viewerRoleId = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); - UserEntity admin = createUser("cus.admin", "cus.admin@test.com", Set.of(adminRole), "BRANCH-01"); - UserEntity viewer = createUser("cus.viewer", "cus.viewer@test.com", Set.of(viewerRole), "BRANCH-01"); + String adminId = createUser("cus.admin", "cus.admin@test.com", Set.of(adminRoleId), "BRANCH-01"); + String viewerId = createUser("cus.viewer", "cus.viewer@test.com", Set.of(viewerRoleId), "BRANCH-01"); - adminToken = generateToken(admin.getId(), "cus.admin", "MASTERDATA_WRITE"); - viewerToken = generateToken(viewer.getId(), "cus.viewer", "USER_READ"); + adminToken = generateToken(adminId, "cus.admin", "MASTERDATA_WRITE"); + viewerToken = generateToken(viewerId, "cus.viewer", "USER_READ"); } // ==================== TC-CUS-01: B2C-Kunde erstellen ==================== diff --git a/backend/src/test/java/de/effigenix/infrastructure/masterdata/web/ProductCategoryControllerIntegrationTest.java b/backend/src/test/java/de/effigenix/infrastructure/masterdata/web/ProductCategoryControllerIntegrationTest.java index 837f4c1..be9bb6e 100644 --- a/backend/src/test/java/de/effigenix/infrastructure/masterdata/web/ProductCategoryControllerIntegrationTest.java +++ b/backend/src/test/java/de/effigenix/infrastructure/masterdata/web/ProductCategoryControllerIntegrationTest.java @@ -4,8 +4,6 @@ import de.effigenix.domain.usermanagement.RoleName; import de.effigenix.infrastructure.AbstractIntegrationTest; import de.effigenix.infrastructure.masterdata.web.dto.CreateProductCategoryRequest; import de.effigenix.infrastructure.masterdata.web.dto.UpdateProductCategoryRequest; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -32,14 +30,14 @@ class ProductCategoryControllerIntegrationTest extends AbstractIntegrationTest { @BeforeEach void setUp() { - RoleEntity adminRole = createRole(RoleName.ADMIN, "Admin"); - RoleEntity viewerRole = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); + String adminRoleId = createRole(RoleName.ADMIN, "Admin"); + String viewerRoleId = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); - UserEntity admin = createUser("cat.admin", "cat.admin@test.com", Set.of(adminRole), "BRANCH-01"); - UserEntity viewer = createUser("cat.viewer", "cat.viewer@test.com", Set.of(viewerRole), "BRANCH-01"); + String adminId = createUser("cat.admin", "cat.admin@test.com", Set.of(adminRoleId), "BRANCH-01"); + String viewerId = createUser("cat.viewer", "cat.viewer@test.com", Set.of(viewerRoleId), "BRANCH-01"); - adminToken = generateToken(admin.getId(), "cat.admin", "MASTERDATA_WRITE"); - viewerToken = generateToken(viewer.getId(), "cat.viewer", "USER_READ"); + adminToken = generateToken(adminId, "cat.admin", "MASTERDATA_WRITE"); + viewerToken = generateToken(viewerId, "cat.viewer", "USER_READ"); } // ==================== TC-CAT-01: Kategorie erstellen (Happy Path) ==================== diff --git a/backend/src/test/java/de/effigenix/infrastructure/masterdata/web/SupplierControllerIntegrationTest.java b/backend/src/test/java/de/effigenix/infrastructure/masterdata/web/SupplierControllerIntegrationTest.java index 47156e7..9529573 100644 --- a/backend/src/test/java/de/effigenix/infrastructure/masterdata/web/SupplierControllerIntegrationTest.java +++ b/backend/src/test/java/de/effigenix/infrastructure/masterdata/web/SupplierControllerIntegrationTest.java @@ -3,8 +3,6 @@ package de.effigenix.infrastructure.masterdata.web; import de.effigenix.domain.usermanagement.RoleName; import de.effigenix.infrastructure.AbstractIntegrationTest; import de.effigenix.infrastructure.masterdata.web.dto.*; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -32,14 +30,14 @@ class SupplierControllerIntegrationTest extends AbstractIntegrationTest { @BeforeEach void setUp() { - RoleEntity adminRole = createRole(RoleName.ADMIN, "Admin"); - RoleEntity viewerRole = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); + String adminRoleId = createRole(RoleName.ADMIN, "Admin"); + String viewerRoleId = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); - UserEntity admin = createUser("sup.admin", "sup.admin@test.com", Set.of(adminRole), "BRANCH-01"); - UserEntity viewer = createUser("sup.viewer", "sup.viewer@test.com", Set.of(viewerRole), "BRANCH-01"); + String adminId = createUser("sup.admin", "sup.admin@test.com", Set.of(adminRoleId), "BRANCH-01"); + String viewerId = createUser("sup.viewer", "sup.viewer@test.com", Set.of(viewerRoleId), "BRANCH-01"); - adminToken = generateToken(admin.getId(), "sup.admin", "MASTERDATA_WRITE"); - viewerToken = generateToken(viewer.getId(), "sup.viewer", "USER_READ"); + adminToken = generateToken(adminId, "sup.admin", "MASTERDATA_WRITE"); + viewerToken = generateToken(viewerId, "sup.viewer", "USER_READ"); } // ==================== TC-SUP-01: Lieferant erstellen – Pflichtfelder ==================== diff --git a/backend/src/test/java/de/effigenix/infrastructure/production/web/BatchControllerIntegrationTest.java b/backend/src/test/java/de/effigenix/infrastructure/production/web/BatchControllerIntegrationTest.java index 3541dfc..f3fc4b3 100644 --- a/backend/src/test/java/de/effigenix/infrastructure/production/web/BatchControllerIntegrationTest.java +++ b/backend/src/test/java/de/effigenix/infrastructure/production/web/BatchControllerIntegrationTest.java @@ -3,13 +3,10 @@ package de.effigenix.infrastructure.production.web; import de.effigenix.domain.usermanagement.RoleName; import de.effigenix.infrastructure.AbstractIntegrationTest; import de.effigenix.infrastructure.production.web.dto.PlanBatchRequest; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import java.time.LocalDate; @@ -30,14 +27,14 @@ class BatchControllerIntegrationTest extends AbstractIntegrationTest { @BeforeEach void setUp() throws Exception { - RoleEntity adminRole = createRole(RoleName.ADMIN, "Admin"); - RoleEntity viewerRole = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); + String adminRoleId = createRole(RoleName.ADMIN, "Admin"); + String viewerRoleId = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); - UserEntity admin = createUser("batch.admin", "batch.admin@test.com", Set.of(adminRole), "BRANCH-01"); - UserEntity viewer = createUser("batch.viewer", "batch.viewer@test.com", Set.of(viewerRole), "BRANCH-01"); + String adminId = createUser("batch.admin", "batch.admin@test.com", Set.of(adminRoleId), "BRANCH-01"); + String viewerId = createUser("batch.viewer", "batch.viewer@test.com", Set.of(viewerRoleId), "BRANCH-01"); - adminToken = generateToken(admin.getId(), "batch.admin", "BATCH_WRITE,BATCH_READ,RECIPE_WRITE,RECIPE_READ"); - viewerToken = generateToken(viewer.getId(), "batch.viewer", "USER_READ"); + adminToken = generateToken(adminId, "batch.admin", "BATCH_WRITE,BATCH_READ,RECIPE_WRITE,RECIPE_READ"); + viewerToken = generateToken(viewerId, "batch.viewer", "USER_READ"); } @Nested diff --git a/backend/src/test/java/de/effigenix/infrastructure/production/web/GetBatchIntegrationTest.java b/backend/src/test/java/de/effigenix/infrastructure/production/web/GetBatchIntegrationTest.java index 26b4334..59d4a0c 100644 --- a/backend/src/test/java/de/effigenix/infrastructure/production/web/GetBatchIntegrationTest.java +++ b/backend/src/test/java/de/effigenix/infrastructure/production/web/GetBatchIntegrationTest.java @@ -2,8 +2,6 @@ package de.effigenix.infrastructure.production.web; import de.effigenix.domain.usermanagement.RoleName; import de.effigenix.infrastructure.AbstractIntegrationTest; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -24,14 +22,14 @@ class GetBatchIntegrationTest extends AbstractIntegrationTest { @BeforeEach void setUp() { - RoleEntity adminRole = createRole(RoleName.ADMIN, "Admin"); - RoleEntity viewerRole = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); + String adminRoleId = createRole(RoleName.ADMIN, "Admin"); + String viewerRoleId = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); - UserEntity admin = createUser("batch.admin", "batch.admin@test.com", Set.of(adminRole), "BRANCH-01"); - UserEntity viewer = createUser("batch.viewer", "batch.viewer@test.com", Set.of(viewerRole), "BRANCH-01"); + String adminId = createUser("batch.admin", "batch.admin@test.com", Set.of(adminRoleId), "BRANCH-01"); + String viewerId = createUser("batch.viewer", "batch.viewer@test.com", Set.of(viewerRoleId), "BRANCH-01"); - adminToken = generateToken(admin.getId(), "batch.admin", "BATCH_WRITE,BATCH_READ,RECIPE_WRITE,RECIPE_READ"); - viewerToken = generateToken(viewer.getId(), "batch.viewer", "USER_READ"); + adminToken = generateToken(adminId, "batch.admin", "BATCH_WRITE,BATCH_READ,RECIPE_WRITE,RECIPE_READ"); + viewerToken = generateToken(viewerId, "batch.viewer", "USER_READ"); } @Test diff --git a/backend/src/test/java/de/effigenix/infrastructure/production/web/GetRecipeIntegrationTest.java b/backend/src/test/java/de/effigenix/infrastructure/production/web/GetRecipeIntegrationTest.java index 01d1184..b75e2f0 100644 --- a/backend/src/test/java/de/effigenix/infrastructure/production/web/GetRecipeIntegrationTest.java +++ b/backend/src/test/java/de/effigenix/infrastructure/production/web/GetRecipeIntegrationTest.java @@ -2,8 +2,6 @@ package de.effigenix.infrastructure.production.web; import de.effigenix.domain.usermanagement.RoleName; import de.effigenix.infrastructure.AbstractIntegrationTest; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -23,14 +21,14 @@ class GetRecipeIntegrationTest extends AbstractIntegrationTest { @BeforeEach void setUp() { - RoleEntity adminRole = createRole(RoleName.ADMIN, "Admin"); - RoleEntity viewerRole = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); + String adminRoleId = createRole(RoleName.ADMIN, "Admin"); + String viewerRoleId = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); - UserEntity admin = createUser("recipe.admin", "recipe.admin@test.com", Set.of(adminRole), "BRANCH-01"); - UserEntity viewer = createUser("recipe.viewer", "recipe.viewer@test.com", Set.of(viewerRole), "BRANCH-01"); + String adminId = createUser("recipe.admin", "recipe.admin@test.com", Set.of(adminRoleId), "BRANCH-01"); + String viewerId = createUser("recipe.viewer", "recipe.viewer@test.com", Set.of(viewerRoleId), "BRANCH-01"); - adminToken = generateToken(admin.getId(), "recipe.admin", "RECIPE_WRITE,RECIPE_READ"); - viewerToken = generateToken(viewer.getId(), "recipe.viewer", "USER_READ"); + adminToken = generateToken(adminId, "recipe.admin", "RECIPE_WRITE,RECIPE_READ"); + viewerToken = generateToken(viewerId, "recipe.viewer", "USER_READ"); } @Test diff --git a/backend/src/test/java/de/effigenix/infrastructure/production/web/ListBatchesIntegrationTest.java b/backend/src/test/java/de/effigenix/infrastructure/production/web/ListBatchesIntegrationTest.java index b08ddfe..025e776 100644 --- a/backend/src/test/java/de/effigenix/infrastructure/production/web/ListBatchesIntegrationTest.java +++ b/backend/src/test/java/de/effigenix/infrastructure/production/web/ListBatchesIntegrationTest.java @@ -2,8 +2,6 @@ package de.effigenix.infrastructure.production.web; import de.effigenix.domain.usermanagement.RoleName; import de.effigenix.infrastructure.AbstractIntegrationTest; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -25,14 +23,14 @@ class ListBatchesIntegrationTest extends AbstractIntegrationTest { @BeforeEach void setUp() { - RoleEntity adminRole = createRole(RoleName.ADMIN, "Admin"); - RoleEntity viewerRole = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); + String adminRoleId = createRole(RoleName.ADMIN, "Admin"); + String viewerRoleId = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); - UserEntity admin = createUser("batch.admin", "batch.admin@test.com", Set.of(adminRole), "BRANCH-01"); - UserEntity viewer = createUser("batch.viewer", "batch.viewer@test.com", Set.of(viewerRole), "BRANCH-01"); + String adminId = createUser("batch.admin", "batch.admin@test.com", Set.of(adminRoleId), "BRANCH-01"); + String viewerId = createUser("batch.viewer", "batch.viewer@test.com", Set.of(viewerRoleId), "BRANCH-01"); - adminToken = generateToken(admin.getId(), "batch.admin", "BATCH_WRITE,BATCH_READ,RECIPE_WRITE,RECIPE_READ"); - viewerToken = generateToken(viewer.getId(), "batch.viewer", "USER_READ"); + adminToken = generateToken(adminId, "batch.admin", "BATCH_WRITE,BATCH_READ,RECIPE_WRITE,RECIPE_READ"); + viewerToken = generateToken(viewerId, "batch.viewer", "USER_READ"); } @Test diff --git a/backend/src/test/java/de/effigenix/infrastructure/production/web/ListRecipesIntegrationTest.java b/backend/src/test/java/de/effigenix/infrastructure/production/web/ListRecipesIntegrationTest.java index 795c79f..9806256 100644 --- a/backend/src/test/java/de/effigenix/infrastructure/production/web/ListRecipesIntegrationTest.java +++ b/backend/src/test/java/de/effigenix/infrastructure/production/web/ListRecipesIntegrationTest.java @@ -2,8 +2,6 @@ package de.effigenix.infrastructure.production.web; import de.effigenix.domain.usermanagement.RoleName; import de.effigenix.infrastructure.AbstractIntegrationTest; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -24,14 +22,14 @@ class ListRecipesIntegrationTest extends AbstractIntegrationTest { @BeforeEach void setUp() { - RoleEntity adminRole = createRole(RoleName.ADMIN, "Admin"); - RoleEntity viewerRole = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); + String adminRoleId = createRole(RoleName.ADMIN, "Admin"); + String viewerRoleId = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); - UserEntity admin = createUser("recipe.admin", "recipe.admin@test.com", Set.of(adminRole), "BRANCH-01"); - UserEntity viewer = createUser("recipe.viewer", "recipe.viewer@test.com", Set.of(viewerRole), "BRANCH-01"); + String adminId = createUser("recipe.admin", "recipe.admin@test.com", Set.of(adminRoleId), "BRANCH-01"); + String viewerId = createUser("recipe.viewer", "recipe.viewer@test.com", Set.of(viewerRoleId), "BRANCH-01"); - adminToken = generateToken(admin.getId(), "recipe.admin", "RECIPE_WRITE,RECIPE_READ"); - viewerToken = generateToken(viewer.getId(), "recipe.viewer", "USER_READ"); + adminToken = generateToken(adminId, "recipe.admin", "RECIPE_WRITE,RECIPE_READ"); + viewerToken = generateToken(viewerId, "recipe.viewer", "USER_READ"); } @Test diff --git a/backend/src/test/java/de/effigenix/infrastructure/production/web/ProductionOrderControllerIntegrationTest.java b/backend/src/test/java/de/effigenix/infrastructure/production/web/ProductionOrderControllerIntegrationTest.java index ff8467d..9f91cb6 100644 --- a/backend/src/test/java/de/effigenix/infrastructure/production/web/ProductionOrderControllerIntegrationTest.java +++ b/backend/src/test/java/de/effigenix/infrastructure/production/web/ProductionOrderControllerIntegrationTest.java @@ -4,8 +4,6 @@ import de.effigenix.domain.usermanagement.RoleName; import de.effigenix.infrastructure.AbstractIntegrationTest; import de.effigenix.infrastructure.production.web.dto.CreateProductionOrderRequest; import de.effigenix.infrastructure.production.web.dto.PlanBatchRequest; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -29,15 +27,15 @@ class ProductionOrderControllerIntegrationTest extends AbstractIntegrationTest { @BeforeEach void setUp() throws Exception { - RoleEntity adminRole = createRole(RoleName.ADMIN, "Admin"); - RoleEntity viewerRole = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); + String adminRoleId = createRole(RoleName.ADMIN, "Admin"); + String viewerRoleId = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); - UserEntity admin = createUser("po.admin", "po.admin@test.com", Set.of(adminRole), "BRANCH-01"); - UserEntity viewer = createUser("po.viewer", "po.viewer@test.com", Set.of(viewerRole), "BRANCH-01"); + String adminId = createUser("po.admin", "po.admin@test.com", Set.of(adminRoleId), "BRANCH-01"); + String viewerId = createUser("po.viewer", "po.viewer@test.com", Set.of(viewerRoleId), "BRANCH-01"); - adminToken = generateToken(admin.getId(), "po.admin", + adminToken = generateToken(adminId, "po.admin", "PRODUCTION_ORDER_WRITE,PRODUCTION_ORDER_READ,RECIPE_WRITE,RECIPE_READ,BATCH_WRITE,BATCH_READ"); - viewerToken = generateToken(viewer.getId(), "po.viewer", "USER_READ"); + viewerToken = generateToken(viewerId, "po.viewer", "USER_READ"); } @Nested diff --git a/backend/src/test/java/de/effigenix/infrastructure/production/web/RecipeLifecycleIntegrationTest.java b/backend/src/test/java/de/effigenix/infrastructure/production/web/RecipeLifecycleIntegrationTest.java index b91df08..bfc88a9 100644 --- a/backend/src/test/java/de/effigenix/infrastructure/production/web/RecipeLifecycleIntegrationTest.java +++ b/backend/src/test/java/de/effigenix/infrastructure/production/web/RecipeLifecycleIntegrationTest.java @@ -2,8 +2,6 @@ package de.effigenix.infrastructure.production.web; import de.effigenix.domain.usermanagement.RoleName; import de.effigenix.infrastructure.AbstractIntegrationTest; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -23,9 +21,9 @@ class RecipeLifecycleIntegrationTest extends AbstractIntegrationTest { @BeforeEach void setUp() { - RoleEntity adminRole = createRole(RoleName.ADMIN, "Admin"); - UserEntity admin = createUser("recipe.admin", "recipe.admin@test.com", Set.of(adminRole), "BRANCH-01"); - adminToken = generateToken(admin.getId(), "recipe.admin", "RECIPE_WRITE,RECIPE_READ"); + String adminRoleId = createRole(RoleName.ADMIN, "Admin"); + String adminId = createUser("recipe.admin", "recipe.admin@test.com", Set.of(adminRoleId), "BRANCH-01"); + adminToken = generateToken(adminId, "recipe.admin", "RECIPE_WRITE,RECIPE_READ"); } // ==================== Aktivierung ==================== diff --git a/backend/src/test/java/de/effigenix/infrastructure/production/web/RecordConsumptionIntegrationTest.java b/backend/src/test/java/de/effigenix/infrastructure/production/web/RecordConsumptionIntegrationTest.java index 938ec59..80237e0 100644 --- a/backend/src/test/java/de/effigenix/infrastructure/production/web/RecordConsumptionIntegrationTest.java +++ b/backend/src/test/java/de/effigenix/infrastructure/production/web/RecordConsumptionIntegrationTest.java @@ -4,8 +4,6 @@ import de.effigenix.domain.usermanagement.RoleName; import de.effigenix.infrastructure.AbstractIntegrationTest; import de.effigenix.infrastructure.production.web.dto.PlanBatchRequest; import de.effigenix.infrastructure.production.web.dto.RecordConsumptionRequest; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -31,14 +29,14 @@ class RecordConsumptionIntegrationTest extends AbstractIntegrationTest { @BeforeEach void setUp() throws Exception { - RoleEntity adminRole = createRole(RoleName.ADMIN, "Admin"); - RoleEntity viewerRole = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); + String adminRoleId = createRole(RoleName.ADMIN, "Admin"); + String viewerRoleId = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); - UserEntity admin = createUser("cons.admin", "cons.admin@test.com", Set.of(adminRole), "BRANCH-01"); - UserEntity viewer = createUser("cons.viewer", "cons.viewer@test.com", Set.of(viewerRole), "BRANCH-01"); + String adminId = createUser("cons.admin", "cons.admin@test.com", Set.of(adminRoleId), "BRANCH-01"); + String viewerId = createUser("cons.viewer", "cons.viewer@test.com", Set.of(viewerRoleId), "BRANCH-01"); - adminToken = generateToken(admin.getId(), "cons.admin", "BATCH_WRITE,BATCH_READ,RECIPE_WRITE,RECIPE_READ"); - viewerToken = generateToken(viewer.getId(), "cons.viewer", "USER_READ"); + adminToken = generateToken(adminId, "cons.admin", "BATCH_WRITE,BATCH_READ,RECIPE_WRITE,RECIPE_READ"); + viewerToken = generateToken(viewerId, "cons.viewer", "USER_READ"); } @Nested diff --git a/backend/src/test/java/de/effigenix/infrastructure/production/web/StartBatchIntegrationTest.java b/backend/src/test/java/de/effigenix/infrastructure/production/web/StartBatchIntegrationTest.java index 6b72449..800cc93 100644 --- a/backend/src/test/java/de/effigenix/infrastructure/production/web/StartBatchIntegrationTest.java +++ b/backend/src/test/java/de/effigenix/infrastructure/production/web/StartBatchIntegrationTest.java @@ -3,8 +3,6 @@ package de.effigenix.infrastructure.production.web; import de.effigenix.domain.usermanagement.RoleName; import de.effigenix.infrastructure.AbstractIntegrationTest; import de.effigenix.infrastructure.production.web.dto.PlanBatchRequest; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -29,14 +27,14 @@ class StartBatchIntegrationTest extends AbstractIntegrationTest { @BeforeEach void setUp() throws Exception { - RoleEntity adminRole = createRole(RoleName.ADMIN, "Admin"); - RoleEntity viewerRole = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); + String adminRoleId = createRole(RoleName.ADMIN, "Admin"); + String viewerRoleId = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); - UserEntity admin = createUser("start.admin", "start.admin@test.com", Set.of(adminRole), "BRANCH-01"); - UserEntity viewer = createUser("start.viewer", "start.viewer@test.com", Set.of(viewerRole), "BRANCH-01"); + String adminId = createUser("start.admin", "start.admin@test.com", Set.of(adminRoleId), "BRANCH-01"); + String viewerId = createUser("start.viewer", "start.viewer@test.com", Set.of(viewerRoleId), "BRANCH-01"); - adminToken = generateToken(admin.getId(), "start.admin", "BATCH_WRITE,BATCH_READ,RECIPE_WRITE,RECIPE_READ"); - viewerToken = generateToken(viewer.getId(), "start.viewer", "USER_READ"); + adminToken = generateToken(adminId, "start.admin", "BATCH_WRITE,BATCH_READ,RECIPE_WRITE,RECIPE_READ"); + viewerToken = generateToken(viewerId, "start.viewer", "USER_READ"); } @Nested diff --git a/backend/src/test/java/de/effigenix/infrastructure/usermanagement/persistence/mapper/RoleMapperTest.java b/backend/src/test/java/de/effigenix/infrastructure/usermanagement/persistence/mapper/RoleMapperTest.java deleted file mode 100644 index 6567444..0000000 --- a/backend/src/test/java/de/effigenix/infrastructure/usermanagement/persistence/mapper/RoleMapperTest.java +++ /dev/null @@ -1,350 +0,0 @@ -package de.effigenix.infrastructure.usermanagement.persistence.mapper; - -import de.effigenix.domain.usermanagement.*; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.HashSet; -import java.util.Set; - -import static org.assertj.core.api.Assertions.*; - -/** - * Unit tests for RoleMapper. - * Tests bidirectional mapping between Role domain entity and RoleEntity JPA entity. - */ -@DisplayName("RoleMapper") -class RoleMapperTest { - - private final RoleMapper roleMapper = new RoleMapper(); - - private Role domainRole; - private RoleEntity jpaEntity; - private Set permissions; - - @BeforeEach - void setUp() { - permissions = new HashSet<>(Set.of( - Permission.USER_READ, - Permission.USER_WRITE, - Permission.ROLE_READ - )); - - // Create JPA entity first - jpaEntity = new RoleEntity( - "role-123", - RoleName.ADMIN, - new HashSet<>(permissions), - "Administrator role with full access" - ); - - // Create domain role via mapper (which internally uses reconstitute) - domainRole = roleMapper.toDomain(jpaEntity); - } - - @Test - @DisplayName("should_MapRoleToEntity_When_DomainRoleProvided") - void should_MapRoleToEntity_When_DomainRoleProvided() { - // Act - RoleEntity entity = roleMapper.toEntity(domainRole); - - // Assert - assertThat(entity).isNotNull(); - assertThat(entity.getId()).isEqualTo("role-123"); - assertThat(entity.getName()).isEqualTo(RoleName.ADMIN); - assertThat(entity.getDescription()).isEqualTo("Administrator role with full access"); - assertThat(entity.getPermissions()).containsAll(permissions); - } - - @Test - @DisplayName("should_MapRoleToDomain_When_JpaEntityProvided") - void should_MapRoleToDomain_When_JpaEntityProvided() { - // Act - Role role = roleMapper.toDomain(jpaEntity); - - // Assert - assertThat(role).isNotNull(); - assertThat(role.id().value()).isEqualTo("role-123"); - assertThat(role.name()).isEqualTo(RoleName.ADMIN); - assertThat(role.description()).isEqualTo("Administrator role with full access"); - assertThat(role.permissions()).containsAll(permissions); - } - - @Test - @DisplayName("should_ReturnNull_When_NullRoleProvidedToToEntity") - void should_ReturnNull_When_NullRoleProvidedToToEntity() { - // Act - RoleEntity entity = roleMapper.toEntity(null); - - // Assert - assertThat(entity).isNull(); - } - - @Test - @DisplayName("should_ReturnNull_When_NullEntityProvidedToToDomain") - void should_ReturnNull_When_NullEntityProvidedToToDomain() { - // Act - Role role = roleMapper.toDomain(null); - - // Assert - assertThat(role).isNull(); - } - - @Test - @DisplayName("should_PreserveAllRoleFields_When_MappingToEntity") - void should_PreserveAllRoleFields_When_MappingToEntity() { - // Arrange - Set perms = new HashSet<>(Set.of( - Permission.BATCH_READ, - Permission.BATCH_WRITE, - Permission.BATCH_COMPLETE - )); - Role role = roleMapper.toDomain(new RoleEntity( - "role-prod-manager", - RoleName.PRODUCTION_MANAGER, - new HashSet<>(perms), - "Production Manager role" - )); - - // Act - RoleEntity entity = roleMapper.toEntity(role); - - // Assert - assertThat(entity.getId()).isEqualTo("role-prod-manager"); - assertThat(entity.getName()).isEqualTo(RoleName.PRODUCTION_MANAGER); - assertThat(entity.getDescription()).isEqualTo("Production Manager role"); - assertThat(entity.getPermissions()).containsAll(perms); - } - - @Test - @DisplayName("should_PreserveAllEntityFields_When_MappingToDomain") - void should_PreserveAllEntityFields_When_MappingToDomain() { - // Arrange - Set perms = new HashSet<>(Set.of( - Permission.STOCK_READ, - Permission.STOCK_WRITE - )); - RoleEntity entity = new RoleEntity( - "role-warehouse", - RoleName.WAREHOUSE_WORKER, - perms, - "Warehouse Worker role" - ); - - // Act - Role role = roleMapper.toDomain(entity); - - // Assert - assertThat(role.id().value()).isEqualTo("role-warehouse"); - assertThat(role.name()).isEqualTo(RoleName.WAREHOUSE_WORKER); - assertThat(role.description()).isEqualTo("Warehouse Worker role"); - assertThat(role.permissions()).containsAll(perms); - } - - @Test - @DisplayName("should_HandleEmptyPermissions_When_RoleHasNoPermissions") - void should_HandleEmptyPermissions_When_RoleHasNoPermissions() { - // Arrange - Role roleNoPerms = roleMapper.toDomain(new RoleEntity( - "role-empty", - RoleName.ADMIN, - new HashSet<>(), - "Empty role" - )); - - // Act - RoleEntity entity = roleMapper.toEntity(roleNoPerms); - - // Assert - assertThat(entity.getPermissions()).isEmpty(); - } - - @Test - @DisplayName("should_HandleNullPermissions_When_MappingToEntity") - void should_HandleNullPermissions_When_MappingToEntity() { - // Arrange - Role roleNullPerms = roleMapper.toDomain(new RoleEntity( - "role-null", - RoleName.ADMIN, - null, - "Role with null permissions" - )); - - // Act - RoleEntity entity = roleMapper.toEntity(roleNullPerms); - - // Assert - assertThat(entity.getPermissions()).isEmpty(); - } - - @Test - @DisplayName("should_HandleNullPermissions_When_MappingToDomain") - void should_HandleNullPermissions_When_MappingToDomain() { - // Arrange - RoleEntity entityNullPerms = new RoleEntity( - "role-null", - RoleName.ADMIN, - null, - "Entity with null permissions" - ); - - // Act - Role role = roleMapper.toDomain(entityNullPerms); - - // Assert - assertThat(role.permissions()).isEmpty(); - } - - @Test - @DisplayName("should_BidirectionalMapping_When_MappingRoleBackAndForth") - void should_BidirectionalMapping_When_MappingRoleBackAndForth() { - // Act - RoleEntity entity = roleMapper.toEntity(domainRole); - Role mappedBackRole = roleMapper.toDomain(entity); - - // Assert - assertThat(mappedBackRole.id().value()).isEqualTo(domainRole.id().value()); - assertThat(mappedBackRole.name()).isEqualTo(domainRole.name()); - assertThat(mappedBackRole.description()).isEqualTo(domainRole.description()); - assertThat(mappedBackRole.permissions()).containsAll(domainRole.permissions()); - } - - @Test - @DisplayName("should_CreateNewSetForPermissions_When_MappingToEntity") - void should_CreateNewSetForPermissions_When_MappingToEntity() { - // Act - RoleEntity entity = roleMapper.toEntity(domainRole); - - // Assert - assertThat(entity.getPermissions()).isNotNull(); - assertThat(entity.getPermissions()).isInstanceOf(Set.class); - } - - @Test - @DisplayName("should_CreateNewSetForPermissions_When_MappingToDomain") - void should_CreateNewSetForPermissions_When_MappingToDomain() { - // Act - Role role = roleMapper.toDomain(jpaEntity); - - // Assert - assertThat(role.permissions()).isNotNull(); - assertThat(role.permissions()).isInstanceOf(Set.class); - } - - @Test - @DisplayName("should_MapAllRoleNames_When_DifferentRoleNamesUsed") - void should_MapAllRoleNames_When_DifferentRoleNamesUsed() { - // Act - Role adminRole = roleMapper.toDomain( - new RoleEntity("id-1", RoleName.ADMIN, new HashSet<>(), "Admin") - ); - Role prodWorkerRole = roleMapper.toDomain( - new RoleEntity("id-2", RoleName.PRODUCTION_WORKER, new HashSet<>(), "Worker") - ); - Role warehouseRole = roleMapper.toDomain( - new RoleEntity("id-3", RoleName.WAREHOUSE_WORKER, new HashSet<>(), "Warehouse") - ); - - // Assert - assertThat(adminRole.name()).isEqualTo(RoleName.ADMIN); - assertThat(prodWorkerRole.name()).isEqualTo(RoleName.PRODUCTION_WORKER); - assertThat(warehouseRole.name()).isEqualTo(RoleName.WAREHOUSE_WORKER); - } - - @Test - @DisplayName("should_MapAllPermissionTypes_When_DifferentPermissionsUsed") - void should_MapAllPermissionTypes_When_DifferentPermissionsUsed() { - // Arrange - Set allPermissions = new HashSet<>(Set.of( - Permission.USER_READ, - Permission.BATCH_WRITE, - Permission.STOCK_READ, - Permission.ORDER_DELETE, - Permission.REPORT_GENERATE - )); - Role roleWithVariousPerms = roleMapper.toDomain(new RoleEntity( - "role-various", - RoleName.ADMIN, - new HashSet<>(allPermissions), - "Role with various permissions" - )); - - // Act - RoleEntity entity = roleMapper.toEntity(roleWithVariousPerms); - - // Assert - assertThat(entity.getPermissions()).containsAll(allPermissions); - assertThat(entity.getPermissions()).hasSize(5); - } - - @Test - @DisplayName("should_HandleNullDescription_When_MappingToEntity") - void should_HandleNullDescription_When_MappingToEntity() { - // Arrange - Role roleNullDesc = roleMapper.toDomain(new RoleEntity( - "role-no-desc", - RoleName.ADMIN, - new HashSet<>(permissions), - null - )); - - // Act - RoleEntity entity = roleMapper.toEntity(roleNullDesc); - - // Assert - assertThat(entity.getDescription()).isNull(); - } - - @Test - @DisplayName("should_HandleNullDescription_When_MappingToDomain") - void should_HandleNullDescription_When_MappingToDomain() { - // Arrange - RoleEntity entityNullDesc = new RoleEntity( - "role-no-desc", - RoleName.ADMIN, - permissions, - null - ); - - // Act - Role role = roleMapper.toDomain(entityNullDesc); - - // Assert - assertThat(role.description()).isNull(); - } - - @Test - @DisplayName("should_MapLargePermissionSet_When_RoleHasManyPermissions") - void should_MapLargePermissionSet_When_RoleHasManyPermissions() { - // Arrange - Set largePermSet = new HashSet<>(Set.of( - Permission.values() // All permissions - )); - Role roleWithManyPerms = roleMapper.toDomain(new RoleEntity( - "role-admin-full", - RoleName.ADMIN, - new HashSet<>(largePermSet), - "Super admin with all permissions" - )); - - // Act - RoleEntity entity = roleMapper.toEntity(roleWithManyPerms); - Role mappedBack = roleMapper.toDomain(entity); - - // Assert - assertThat(entity.getPermissions()).hasSize(largePermSet.size()); - assertThat(mappedBack.permissions()).hasSize(largePermSet.size()); - } - - @Test - @DisplayName("should_PreservePermissionImmutability_When_MappingToEntity") - void should_PreservePermissionImmutability_When_MappingToEntity() { - // Act - RoleEntity entity = roleMapper.toEntity(domainRole); - - // Assert - mapped entity should have same permissions - assertThat(entity.getPermissions()).containsAll(domainRole.permissions()); - } -} diff --git a/backend/src/test/java/de/effigenix/infrastructure/usermanagement/persistence/mapper/UserMapperTest.java b/backend/src/test/java/de/effigenix/infrastructure/usermanagement/persistence/mapper/UserMapperTest.java deleted file mode 100644 index f284837..0000000 --- a/backend/src/test/java/de/effigenix/infrastructure/usermanagement/persistence/mapper/UserMapperTest.java +++ /dev/null @@ -1,294 +0,0 @@ -package de.effigenix.infrastructure.usermanagement.persistence.mapper; - -import de.effigenix.domain.usermanagement.*; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.time.OffsetDateTime; -import java.time.ZoneOffset; -import java.util.HashSet; -import java.util.Set; - -import static org.assertj.core.api.Assertions.*; - -/** - * Unit tests for UserMapper. - * Tests bidirectional mapping between User domain entity and UserEntity JPA entity. - * - * Uses real RoleMapper (no mocks) because both mappers are simple stateless components. - * Domain User/Role objects are created via the mapper's toDomain() method which internally - * calls the package-private reconstitute() factory method. - */ -@DisplayName("UserMapper") -class UserMapperTest { - - private final RoleMapper roleMapper = new RoleMapper(); - private final UserMapper userMapper = new UserMapper(roleMapper); - - private User domainUser; - private UserEntity jpaEntity; - private OffsetDateTime createdAt; - - @BeforeEach - void setUp() { - createdAt = OffsetDateTime.now(ZoneOffset.UTC); - - // Create JPA entity first - jpaEntity = new UserEntity( - "user-123", - "john.doe", - "john@example.com", - "$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW", - new HashSet<>(), - "branch-1", - UserStatus.ACTIVE, - createdAt, - null - ); - - // Create domain user via mapper (which internally uses reconstitute) - domainUser = userMapper.toDomain(jpaEntity); - } - - @Test - @DisplayName("should_MapUserToEntity_When_DomainUserProvided") - void should_MapUserToEntity_When_DomainUserProvided() { - // Act - UserEntity entity = userMapper.toEntity(domainUser); - - // Assert - assertThat(entity).isNotNull(); - assertThat(entity.getId()).isEqualTo("user-123"); - assertThat(entity.getUsername()).isEqualTo("john.doe"); - assertThat(entity.getEmail()).isEqualTo("john@example.com"); - assertThat(entity.getPasswordHash()).isEqualTo("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"); - assertThat(entity.getBranchId()).isEqualTo("branch-1"); - assertThat(entity.getStatus()).isEqualTo(UserStatus.ACTIVE); - assertThat(entity.getCreatedAt()).isEqualTo(createdAt); - assertThat(entity.getLastLogin()).isNull(); - } - - @Test - @DisplayName("should_MapUserToDomain_When_JpaEntityProvided") - void should_MapUserToDomain_When_JpaEntityProvided() { - // Act - User user = userMapper.toDomain(jpaEntity); - - // Assert - assertThat(user).isNotNull(); - assertThat(user.id().value()).isEqualTo("user-123"); - assertThat(user.username()).isEqualTo("john.doe"); - assertThat(user.email()).isEqualTo("john@example.com"); - assertThat(user.passwordHash().value()).isEqualTo("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"); - assertThat(user.branchId()).isEqualTo("branch-1"); - assertThat(user.status()).isEqualTo(UserStatus.ACTIVE); - assertThat(user.createdAt()).isEqualTo(createdAt); - assertThat(user.lastLogin()).isNull(); - } - - @Test - @DisplayName("should_ReturnNull_When_NullUserProvidedToToEntity") - void should_ReturnNull_When_NullUserProvidedToToEntity() { - // Act - UserEntity entity = userMapper.toEntity(null); - - // Assert - assertThat(entity).isNull(); - } - - @Test - @DisplayName("should_ReturnNull_When_NullEntityProvidedToToDomain") - void should_ReturnNull_When_NullEntityProvidedToToDomain() { - // Act - User user = userMapper.toDomain(null); - - // Assert - assertThat(user).isNull(); - } - - @Test - @DisplayName("should_PreserveAllUserFields_When_MappingToEntity") - void should_PreserveAllUserFields_When_MappingToEntity() { - // Arrange - OffsetDateTime lastLogin = OffsetDateTime.now(ZoneOffset.UTC); - UserEntity sourceEntity = new UserEntity( - "user-456", - "jane.smith", - "jane@example.com", - "$2b$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW", - new HashSet<>(), - "branch-2", - UserStatus.LOCKED, - createdAt, - lastLogin - ); - User userWithLastLogin = userMapper.toDomain(sourceEntity); - - // Act - UserEntity entity = userMapper.toEntity(userWithLastLogin); - - // Assert - assertThat(entity.getId()).isEqualTo("user-456"); - assertThat(entity.getUsername()).isEqualTo("jane.smith"); - assertThat(entity.getEmail()).isEqualTo("jane@example.com"); - assertThat(entity.getPasswordHash()).isEqualTo("$2b$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"); - assertThat(entity.getBranchId()).isEqualTo("branch-2"); - assertThat(entity.getStatus()).isEqualTo(UserStatus.LOCKED); - assertThat(entity.getCreatedAt()).isEqualTo(createdAt); - assertThat(entity.getLastLogin()).isEqualTo(lastLogin); - } - - @Test - @DisplayName("should_PreserveAllEntityFields_When_MappingToDomain") - void should_PreserveAllEntityFields_When_MappingToDomain() { - // Arrange - OffsetDateTime lastLogin = OffsetDateTime.now(ZoneOffset.UTC); - UserEntity entityWithLastLogin = new UserEntity( - "user-789", - "bob.jones", - "bob@example.com", - "$2y$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW", - new HashSet<>(), - "branch-3", - UserStatus.INACTIVE, - createdAt, - lastLogin - ); - - // Act - User user = userMapper.toDomain(entityWithLastLogin); - - // Assert - assertThat(user.id().value()).isEqualTo("user-789"); - assertThat(user.username()).isEqualTo("bob.jones"); - assertThat(user.email()).isEqualTo("bob@example.com"); - assertThat(user.passwordHash().value()).isEqualTo("$2y$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"); - assertThat(user.branchId()).isEqualTo("branch-3"); - assertThat(user.status()).isEqualTo(UserStatus.INACTIVE); - assertThat(user.createdAt()).isEqualTo(createdAt); - assertThat(user.lastLogin()).isEqualTo(lastLogin); - } - - @Test - @DisplayName("should_MapRoleEntitiesCorrectly_When_UserHasRoles") - void should_MapRoleEntitiesCorrectly_When_UserHasRoles() { - // Arrange - RoleEntity roleEntity1 = new RoleEntity("role-1", RoleName.ADMIN, new HashSet<>(), "Admin"); - RoleEntity roleEntity2 = new RoleEntity("role-2", RoleName.PRODUCTION_WORKER, new HashSet<>(), "Worker"); - - UserEntity userEntityWithRoles = new UserEntity( - "user-999", - "john.doe", - "john@example.com", - "$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW", - new HashSet<>(Set.of(roleEntity1, roleEntity2)), - "branch-1", - UserStatus.ACTIVE, - createdAt, - null - ); - - // Map entity to domain (toDomain maps roles via real roleMapper) - User userWithRoles = userMapper.toDomain(userEntityWithRoles); - - // Act - UserEntity entity = userMapper.toEntity(userWithRoles); - - // Assert - assertThat(entity.getRoles()).hasSize(2); - } - - @Test - @DisplayName("should_HandleEmptyRoleSet_When_UserHasNoRoles") - void should_HandleEmptyRoleSet_When_UserHasNoRoles() { - // Act - UserEntity entity = userMapper.toEntity(domainUser); - - // Assert - assertThat(entity.getRoles()).isEmpty(); - } - - @Test - @DisplayName("should_HandleNullRolesInEntity_When_MappingToDomain") - void should_HandleNullRolesInEntity_When_MappingToDomain() { - // Arrange - jpaEntity.setRoles(null); - - // Act - User user = userMapper.toDomain(jpaEntity); - - // Assert - assertThat(user.roles()).isEmpty(); - } - - @Test - @DisplayName("should_BidirectionalMapping_When_MappingUserBackAndForth") - void should_BidirectionalMapping_When_MappingUserBackAndForth() { - // Act - UserEntity entity = userMapper.toEntity(domainUser); - User mappedBackUser = userMapper.toDomain(entity); - - // Assert - assertThat(mappedBackUser.id().value()).isEqualTo(domainUser.id().value()); - assertThat(mappedBackUser.username()).isEqualTo(domainUser.username()); - assertThat(mappedBackUser.email()).isEqualTo(domainUser.email()); - assertThat(mappedBackUser.passwordHash().value()).isEqualTo(domainUser.passwordHash().value()); - assertThat(mappedBackUser.branchId()).isEqualTo(domainUser.branchId()); - assertThat(mappedBackUser.status()).isEqualTo(domainUser.status()); - } - - @Test - @DisplayName("should_CreateNewSetForRoles_When_MappingToEntity") - void should_CreateNewSetForRoles_When_MappingToEntity() { - // Act - UserEntity entity = userMapper.toEntity(domainUser); - - // Assert - assertThat(entity.getRoles()).isNotNull(); - assertThat(entity.getRoles()).isInstanceOf(Set.class); - } - - @Test - @DisplayName("should_CreateNewSetForRoles_When_MappingToDomain") - void should_CreateNewSetForRoles_When_MappingToDomain() { - // Act - User user = userMapper.toDomain(jpaEntity); - - // Assert - assertThat(user.roles()).isNotNull(); - assertThat(user.roles()).isInstanceOf(Set.class); - } - - @Test - @DisplayName("should_MapAllUserStatuses_When_DifferentStatusesUsed") - void should_MapAllUserStatuses_When_DifferentStatusesUsed() { - // Arrange - create users with different statuses via entity -> domain mapping - User activeUser = userMapper.toDomain(new UserEntity( - "id-1", "user1", "u1@test.com", - "$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW", - new HashSet<>(), "b1", UserStatus.ACTIVE, createdAt, null)); - - User inactiveUser = userMapper.toDomain(new UserEntity( - "id-2", "user2", "u2@test.com", - "$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW", - new HashSet<>(), "b1", UserStatus.INACTIVE, createdAt, null)); - - User lockedUser = userMapper.toDomain(new UserEntity( - "id-3", "user3", "u3@test.com", - "$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW", - new HashSet<>(), "b1", UserStatus.LOCKED, createdAt, null)); - - // Act - UserEntity activeEntity = userMapper.toEntity(activeUser); - UserEntity inactiveEntity = userMapper.toEntity(inactiveUser); - UserEntity lockedEntity = userMapper.toEntity(lockedUser); - - // Assert - assertThat(activeEntity.getStatus()).isEqualTo(UserStatus.ACTIVE); - assertThat(inactiveEntity.getStatus()).isEqualTo(UserStatus.INACTIVE); - assertThat(lockedEntity.getStatus()).isEqualTo(UserStatus.LOCKED); - } -} diff --git a/backend/src/test/java/de/effigenix/infrastructure/usermanagement/web/AuthControllerIntegrationTest.java b/backend/src/test/java/de/effigenix/infrastructure/usermanagement/web/AuthControllerIntegrationTest.java index 57bb44b..8515a56 100644 --- a/backend/src/test/java/de/effigenix/infrastructure/usermanagement/web/AuthControllerIntegrationTest.java +++ b/backend/src/test/java/de/effigenix/infrastructure/usermanagement/web/AuthControllerIntegrationTest.java @@ -1,10 +1,7 @@ package de.effigenix.infrastructure.usermanagement.web; import de.effigenix.domain.usermanagement.RoleName; -import de.effigenix.domain.usermanagement.UserStatus; import de.effigenix.infrastructure.AbstractIntegrationTest; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity; import de.effigenix.infrastructure.usermanagement.web.dto.LoginRequest; import de.effigenix.infrastructure.usermanagement.web.dto.LoginResponse; import de.effigenix.infrastructure.usermanagement.web.dto.RefreshTokenRequest; @@ -35,11 +32,10 @@ class AuthControllerIntegrationTest extends AbstractIntegrationTest { void setUp() { validUsername = "auth.test.user"; - RoleEntity adminRole = createRole(RoleName.ADMIN, "Admin role"); + String adminRoleId = createRole(RoleName.ADMIN, "Admin role"); - UserEntity testUser = createUser(validUsername, "auth.test@example.com", - Set.of(adminRole), "BRANCH-TEST-001"); - validUserId = testUser.getId(); + validUserId = createUser(validUsername, "auth.test@example.com", + Set.of(adminRoleId), "BRANCH-TEST-001"); validRefreshToken = generateRefreshToken(validUserId, validUsername); } @@ -99,9 +95,8 @@ class AuthControllerIntegrationTest extends AbstractIntegrationTest { @Test @DisplayName("Login with locked user should return 401 Unauthorized") void testLoginWithLockedUser() throws Exception { - UserEntity user = userRepository.findByUsername(validUsername).orElseThrow(); - user.setStatus(UserStatus.LOCKED); - userRepository.save(user); + jdbc.sql("UPDATE users SET status = 'LOCKED' WHERE id = ?") + .param(validUserId).update(); LoginRequest request = new LoginRequest(validUsername, TEST_PASSWORD); @@ -115,9 +110,8 @@ class AuthControllerIntegrationTest extends AbstractIntegrationTest { @Test @DisplayName("Login with inactive user should return 401 Unauthorized") void testLoginWithInactiveUser() throws Exception { - UserEntity user = userRepository.findByUsername(validUsername).orElseThrow(); - user.setStatus(UserStatus.INACTIVE); - userRepository.save(user); + jdbc.sql("UPDATE users SET status = 'INACTIVE' WHERE id = ?") + .param(validUserId).update(); LoginRequest request = new LoginRequest(validUsername, TEST_PASSWORD); diff --git a/backend/src/test/java/de/effigenix/infrastructure/usermanagement/web/RoleControllerIntegrationTest.java b/backend/src/test/java/de/effigenix/infrastructure/usermanagement/web/RoleControllerIntegrationTest.java index 310a695..42c9fcb 100644 --- a/backend/src/test/java/de/effigenix/infrastructure/usermanagement/web/RoleControllerIntegrationTest.java +++ b/backend/src/test/java/de/effigenix/infrastructure/usermanagement/web/RoleControllerIntegrationTest.java @@ -2,8 +2,6 @@ package de.effigenix.infrastructure.usermanagement.web; import de.effigenix.domain.usermanagement.RoleName; import de.effigenix.infrastructure.AbstractIntegrationTest; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -23,14 +21,14 @@ class RoleControllerIntegrationTest extends AbstractIntegrationTest { @BeforeEach void setUp() { - RoleEntity adminRole = createRole(RoleName.ADMIN, "Admin"); - RoleEntity viewerRole = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); + String adminRoleId = createRole(RoleName.ADMIN, "Admin"); + String viewerRoleId = createRole(RoleName.PRODUCTION_WORKER, "Viewer"); - UserEntity admin = createUser("role.admin", "role.admin@test.com", Set.of(adminRole), "BRANCH-01"); - UserEntity viewer = createUser("role.viewer", "role.viewer@test.com", Set.of(viewerRole), "BRANCH-01"); + String adminId = createUser("role.admin", "role.admin@test.com", Set.of(adminRoleId), "BRANCH-01"); + String viewerId = createUser("role.viewer", "role.viewer@test.com", Set.of(viewerRoleId), "BRANCH-01"); - adminToken = generateToken(admin.getId(), "role.admin", "ROLE_READ"); - viewerToken = generateToken(viewer.getId(), "role.viewer", "USER_READ"); + adminToken = generateToken(adminId, "role.admin", "ROLE_READ"); + viewerToken = generateToken(viewerId, "role.viewer", "USER_READ"); } @Test diff --git a/backend/src/test/java/de/effigenix/infrastructure/usermanagement/web/SecurityIntegrationTest.java b/backend/src/test/java/de/effigenix/infrastructure/usermanagement/web/SecurityIntegrationTest.java index abe4e2f..374ce38 100644 --- a/backend/src/test/java/de/effigenix/infrastructure/usermanagement/web/SecurityIntegrationTest.java +++ b/backend/src/test/java/de/effigenix/infrastructure/usermanagement/web/SecurityIntegrationTest.java @@ -1,12 +1,9 @@ package de.effigenix.infrastructure.usermanagement.web; import de.effigenix.domain.usermanagement.RoleName; -import de.effigenix.domain.usermanagement.UserStatus; import de.effigenix.infrastructure.AbstractIntegrationTest; import de.effigenix.infrastructure.audit.AuditLogEntity; import de.effigenix.infrastructure.audit.AuditLogJpaRepository; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity; import de.effigenix.infrastructure.usermanagement.web.dto.CreateUserRequest; import de.effigenix.infrastructure.usermanagement.web.dto.LoginRequest; import de.effigenix.infrastructure.usermanagement.web.dto.UpdateUserRequest; @@ -45,21 +42,19 @@ class SecurityIntegrationTest extends AbstractIntegrationTest { private String adminUserId; private String regularUserId; - private RoleEntity adminRole; - private RoleEntity userRole; + private String adminRoleId; + private String userRoleId; @BeforeEach void setUp() { - adminRole = createRole(RoleName.ADMIN, "Admin role"); - userRole = createRole(RoleName.PRODUCTION_WORKER, "Production worker role"); + adminRoleId = createRole(RoleName.ADMIN, "Admin role"); + userRoleId = createRole(RoleName.PRODUCTION_WORKER, "Production worker role"); - UserEntity adminUser = createUser("security.admin", "admin@security.test", - Set.of(adminRole), "BRANCH-ADMIN"); - adminUserId = adminUser.getId(); + adminUserId = createUser("security.admin", "admin@security.test", + Set.of(adminRoleId), "BRANCH-ADMIN"); - UserEntity regularUser = createUser("security.user", "user@security.test", - Set.of(userRole), "BRANCH-USER"); - regularUserId = regularUser.getId(); + regularUserId = createUser("security.user", "user@security.test", + Set.of(userRoleId), "BRANCH-USER"); adminToken = generateTestJWT(adminUserId, "security.admin", true); regularUserToken = generateTestJWT(regularUserId, "security.user", false); @@ -222,8 +217,8 @@ class SecurityIntegrationTest extends AbstractIntegrationTest { @Test @DisplayName("Users should see data filtered by their branch (if filtering is implemented)") void testBranchBasedDataVisibility() throws Exception { - UserEntity otherBranchUser = createUser("other.branch.user", "other@branch.test", - Set.of(userRole), "BRANCH-OTHER"); + String otherBranchUserId = createUser("other.branch.user", "other@branch.test", + Set.of(userRoleId), "BRANCH-OTHER"); mockMvc.perform(get("/api/users") .header("Authorization", "Bearer " + regularUserToken)) @@ -360,9 +355,12 @@ class SecurityIntegrationTest extends AbstractIntegrationTest { void testUnlockUserAuditLogging() throws Exception { auditLogRepository.deleteAll(); - UserEntity user = userRepository.findById(regularUserId).orElseThrow(); - user.setStatus(UserStatus.LOCKED); - userRepository.save(user); + // Lock the user first via API + mockMvc.perform(post("/api/users/{id}/lock", regularUserId) + .header("Authorization", "Bearer " + adminToken)) + .andExpect(status().isOk()); + + auditLogRepository.deleteAll(); mockMvc.perform(post("/api/users/{id}/unlock", regularUserId) .header("Authorization", "Bearer " + adminToken)) diff --git a/backend/src/test/java/de/effigenix/infrastructure/usermanagement/web/UserControllerIntegrationTest.java b/backend/src/test/java/de/effigenix/infrastructure/usermanagement/web/UserControllerIntegrationTest.java index d8a4164..0e72628 100644 --- a/backend/src/test/java/de/effigenix/infrastructure/usermanagement/web/UserControllerIntegrationTest.java +++ b/backend/src/test/java/de/effigenix/infrastructure/usermanagement/web/UserControllerIntegrationTest.java @@ -1,10 +1,7 @@ package de.effigenix.infrastructure.usermanagement.web; import de.effigenix.domain.usermanagement.RoleName; -import de.effigenix.domain.usermanagement.UserStatus; import de.effigenix.infrastructure.AbstractIntegrationTest; -import de.effigenix.infrastructure.usermanagement.persistence.entity.RoleEntity; -import de.effigenix.infrastructure.usermanagement.persistence.entity.UserEntity; import de.effigenix.infrastructure.usermanagement.web.dto.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -35,19 +32,17 @@ class UserControllerIntegrationTest extends AbstractIntegrationTest { private String adminUserId; private String regularUserId; - private RoleEntity adminRole; - private RoleEntity userRole; + private String adminRoleId; + private String userRoleId; @BeforeEach void setUp() { - adminRole = createRole(RoleName.ADMIN, "Admin role"); - userRole = createRole(RoleName.PRODUCTION_WORKER, "Production worker role"); + adminRoleId = createRole(RoleName.ADMIN, "Admin role"); + userRoleId = createRole(RoleName.PRODUCTION_WORKER, "Production worker role"); - UserEntity adminUser = createUser("admin.user", "admin@example.com", Set.of(adminRole), "BRANCH-001"); - adminUserId = adminUser.getId(); + adminUserId = createUser("admin.user", "admin@example.com", Set.of(adminRoleId), "BRANCH-001"); - UserEntity regularUser = createUser("regular.user", "regular@example.com", Set.of(userRole), "BRANCH-001"); - regularUserId = regularUser.getId(); + regularUserId = createUser("regular.user", "regular@example.com", Set.of(userRoleId), "BRANCH-001"); adminToken = generateTestJWT(adminUserId, "admin.user", true); regularUserToken = generateTestJWT(regularUserId, "regular.user", false); @@ -353,8 +348,9 @@ class UserControllerIntegrationTest extends AbstractIntegrationTest { .andReturn(); // Verify user is actually locked in database - UserEntity lockedUser = userRepository.findById(regularUserId).orElseThrow(); - assertThat(lockedUser.getStatus()).isEqualTo(UserStatus.LOCKED); + String lockedStatus = jdbc.sql("SELECT status FROM users WHERE id = ?") + .param(regularUserId).query(String.class).single(); + assertThat(lockedStatus).isEqualTo("LOCKED"); } @Test @@ -383,9 +379,8 @@ class UserControllerIntegrationTest extends AbstractIntegrationTest { @DisplayName("Unlock locked user should return 200 with ACTIVE status") void testUnlockUser() throws Exception { // First lock the user - UserEntity user = userRepository.findById(regularUserId).orElseThrow(); - user.setStatus(UserStatus.LOCKED); - userRepository.save(user); + jdbc.sql("UPDATE users SET status = 'LOCKED' WHERE id = ?") + .param(regularUserId).update(); // Now unlock mockMvc.perform(post("/api/users/{id}/unlock", regularUserId) @@ -395,8 +390,9 @@ class UserControllerIntegrationTest extends AbstractIntegrationTest { .andReturn(); // Verify user is actually unlocked in database - UserEntity unlockedUser = userRepository.findById(regularUserId).orElseThrow(); - assertThat(unlockedUser.getStatus()).isEqualTo(UserStatus.ACTIVE); + String unlockedStatus = jdbc.sql("SELECT status FROM users WHERE id = ?") + .param(regularUserId).query(String.class).single(); + assertThat(unlockedStatus).isEqualTo("ACTIVE"); } @Test @@ -437,8 +433,9 @@ class UserControllerIntegrationTest extends AbstractIntegrationTest { .andReturn(); // Verify password was actually changed - UserEntity updatedUser = userRepository.findById(regularUserId).orElseThrow(); - assertThat(passwordEncoder.matches("NewSecurePass456!", updatedUser.getPasswordHash())).isTrue(); + String passwordHash = jdbc.sql("SELECT password_hash FROM users WHERE id = ?") + .param(regularUserId).query(String.class).single(); + assertThat(passwordEncoder.matches("NewSecurePass456!", passwordHash)).isTrue(); } @Test