diff --git a/backend/src/main/java/de/effigenix/application/inventory/DeactivateStorageLocation.java b/backend/src/main/java/de/effigenix/application/inventory/DeactivateStorageLocation.java index a11e1bb..083abfb 100644 --- a/backend/src/main/java/de/effigenix/application/inventory/DeactivateStorageLocation.java +++ b/backend/src/main/java/de/effigenix/application/inventory/DeactivateStorageLocation.java @@ -9,9 +9,11 @@ import org.springframework.transaction.annotation.Transactional; public class DeactivateStorageLocation { private final StorageLocationRepository storageLocationRepository; + private final StockRepository stockRepository; - public DeactivateStorageLocation(StorageLocationRepository storageLocationRepository) { + public DeactivateStorageLocation(StorageLocationRepository storageLocationRepository, StockRepository stockRepository) { this.storageLocationRepository = storageLocationRepository; + this.stockRepository = stockRepository; } public Result execute(String storageLocationId, ActorId performedBy) { @@ -29,16 +31,24 @@ public class DeactivateStorageLocation { } } - // TODO: Stock-Existenz prüfen, wenn Stock BC implementiert ist - // Akzeptanzkriterium: Deaktivierung schlägt fehl, wenn Stock am Lagerort existiert + // 2. Stock-Existenz prüfen + switch (stockRepository.findAllByStorageLocationId(locationId)) { + case Result.Failure(var err) -> + { return Result.failure(new StorageLocationError.RepositoryFailure(err.message())); } + case Result.Success(var stocks) -> { + if (!stocks.isEmpty()) { + return Result.failure(new StorageLocationError.StockExistsAtLocation(storageLocationId)); + } + } + } - // 2. Deaktivieren + // 3. Deaktivieren switch (location.deactivate()) { case Result.Failure(var err) -> { return Result.failure(err); } case Result.Success(var ignored) -> { } } - // 3. Speichern + // 4. Speichern switch (storageLocationRepository.save(location)) { case Result.Failure(var err) -> { return Result.failure(new StorageLocationError.RepositoryFailure(err.message())); } diff --git a/backend/src/test/java/de/effigenix/application/inventory/DeactivateStorageLocationTest.java b/backend/src/test/java/de/effigenix/application/inventory/DeactivateStorageLocationTest.java new file mode 100644 index 0000000..d46e2fa --- /dev/null +++ b/backend/src/test/java/de/effigenix/application/inventory/DeactivateStorageLocationTest.java @@ -0,0 +1,138 @@ +package de.effigenix.application.inventory; + +import de.effigenix.domain.inventory.*; +import de.effigenix.domain.masterdata.ArticleId; +import de.effigenix.shared.common.RepositoryError; +import de.effigenix.shared.common.Result; +import de.effigenix.shared.security.ActorId; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +@DisplayName("DeactivateStorageLocation Use Case") +class DeactivateStorageLocationTest { + + @Mock private StorageLocationRepository storageLocationRepository; + @Mock private StockRepository stockRepository; + + private DeactivateStorageLocation deactivateStorageLocation; + private ActorId performedBy; + + @BeforeEach + void setUp() { + deactivateStorageLocation = new DeactivateStorageLocation(storageLocationRepository, stockRepository); + performedBy = ActorId.of("admin-user"); + } + + private StorageLocation activeLocation(String id) { + return StorageLocation.reconstitute( + StorageLocationId.of(id), + new StorageLocationName("Kühlraum 1"), + StorageType.COLD_ROOM, + null, + true + ); + } + + private StorageLocation inactiveLocation(String id) { + return StorageLocation.reconstitute( + StorageLocationId.of(id), + new StorageLocationName("Kühlraum 1"), + StorageType.COLD_ROOM, + null, + false + ); + } + + @Test + @DisplayName("should deactivate active location without stock") + void shouldDeactivateActiveLocationWithoutStock() { + var locationId = StorageLocationId.of("loc-1"); + when(storageLocationRepository.findById(locationId)) + .thenReturn(Result.success(Optional.of(activeLocation("loc-1")))); + when(stockRepository.findAllByStorageLocationId(locationId)) + .thenReturn(Result.success(List.of())); + when(storageLocationRepository.save(any())) + .thenReturn(Result.success(null)); + + var result = deactivateStorageLocation.execute("loc-1", performedBy); + + assertThat(result.isSuccess()).isTrue(); + assertThat(result.unsafeGetValue().active()).isFalse(); + } + + @Test + @DisplayName("should fail with StockExistsAtLocation when stock exists") + void shouldFailWhenStockExistsAtLocation() { + var locationId = StorageLocationId.of("loc-1"); + var stock = Stock.reconstitute( + StockId.of("stock-1"), ArticleId.of("article-1"), + locationId, null, null, List.of() + ); + when(storageLocationRepository.findById(locationId)) + .thenReturn(Result.success(Optional.of(activeLocation("loc-1")))); + when(stockRepository.findAllByStorageLocationId(locationId)) + .thenReturn(Result.success(List.of(stock))); + + var result = deactivateStorageLocation.execute("loc-1", performedBy); + + assertThat(result.isFailure()).isTrue(); + assertThat(result.unsafeGetError()).isInstanceOf(StorageLocationError.StockExistsAtLocation.class); + verify(storageLocationRepository, never()).save(any()); + } + + @Test + @DisplayName("should fail with StorageLocationNotFound when location does not exist") + void shouldFailWhenLocationNotFound() { + when(storageLocationRepository.findById(StorageLocationId.of("nonexistent"))) + .thenReturn(Result.success(Optional.empty())); + + var result = deactivateStorageLocation.execute("nonexistent", performedBy); + + assertThat(result.isFailure()).isTrue(); + assertThat(result.unsafeGetError()).isInstanceOf(StorageLocationError.StorageLocationNotFound.class); + } + + @Test + @DisplayName("should fail with AlreadyInactive when location is already inactive") + void shouldFailWhenAlreadyInactive() { + var locationId = StorageLocationId.of("loc-1"); + when(storageLocationRepository.findById(locationId)) + .thenReturn(Result.success(Optional.of(inactiveLocation("loc-1")))); + when(stockRepository.findAllByStorageLocationId(locationId)) + .thenReturn(Result.success(List.of())); + + var result = deactivateStorageLocation.execute("loc-1", performedBy); + + assertThat(result.isFailure()).isTrue(); + assertThat(result.unsafeGetError()).isInstanceOf(StorageLocationError.AlreadyInactive.class); + } + + @Test + @DisplayName("should fail with RepositoryFailure when stock repository fails") + void shouldFailWhenStockRepositoryFails() { + var locationId = StorageLocationId.of("loc-1"); + when(storageLocationRepository.findById(locationId)) + .thenReturn(Result.success(Optional.of(activeLocation("loc-1")))); + when(stockRepository.findAllByStorageLocationId(locationId)) + .thenReturn(Result.failure(new RepositoryError.DatabaseError("connection lost"))); + + var result = deactivateStorageLocation.execute("loc-1", performedBy); + + assertThat(result.isFailure()).isTrue(); + assertThat(result.unsafeGetError()).isInstanceOf(StorageLocationError.RepositoryFailure.class); + } +}