1
0
Fork 0
mirror of https://github.com/s-frick/effigenix.git synced 2026-03-28 13:49:36 +01:00

fix(inventory): Stock-Existenz-Check bei Lagerort-Deaktivierung

DeactivateStorageLocation prüft jetzt via StockRepository ob Bestände am
Lagerort existieren bevor deaktiviert wird (Story 1.2 Akzeptanzkriterium).
5 Unit Tests und 1 Integrationstest für den neuen Check.
This commit is contained in:
Sebastian Frick 2026-02-23 22:40:39 +01:00
parent 42c9ca9d19
commit 792d5f0d97
2 changed files with 153 additions and 5 deletions

View file

@ -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<StorageLocationError, StorageLocation> 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())); }

View file

@ -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);
}
}