mirror of
https://github.com/s-frick/effigenix.git
synced 2026-03-28 10:09:35 +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:
parent
42c9ca9d19
commit
792d5f0d97
2 changed files with 153 additions and 5 deletions
|
|
@ -9,9 +9,11 @@ import org.springframework.transaction.annotation.Transactional;
|
||||||
public class DeactivateStorageLocation {
|
public class DeactivateStorageLocation {
|
||||||
|
|
||||||
private final StorageLocationRepository storageLocationRepository;
|
private final StorageLocationRepository storageLocationRepository;
|
||||||
|
private final StockRepository stockRepository;
|
||||||
|
|
||||||
public DeactivateStorageLocation(StorageLocationRepository storageLocationRepository) {
|
public DeactivateStorageLocation(StorageLocationRepository storageLocationRepository, StockRepository stockRepository) {
|
||||||
this.storageLocationRepository = storageLocationRepository;
|
this.storageLocationRepository = storageLocationRepository;
|
||||||
|
this.stockRepository = stockRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result<StorageLocationError, StorageLocation> execute(String storageLocationId, ActorId performedBy) {
|
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
|
// 2. Stock-Existenz prüfen
|
||||||
// Akzeptanzkriterium: Deaktivierung schlägt fehl, wenn Stock am Lagerort existiert
|
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()) {
|
switch (location.deactivate()) {
|
||||||
case Result.Failure(var err) -> { return Result.failure(err); }
|
case Result.Failure(var err) -> { return Result.failure(err); }
|
||||||
case Result.Success(var ignored) -> { }
|
case Result.Success(var ignored) -> { }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Speichern
|
// 4. Speichern
|
||||||
switch (storageLocationRepository.save(location)) {
|
switch (storageLocationRepository.save(location)) {
|
||||||
case Result.Failure(var err) ->
|
case Result.Failure(var err) ->
|
||||||
{ return Result.failure(new StorageLocationError.RepositoryFailure(err.message())); }
|
{ return Result.failure(new StorageLocationError.RepositoryFailure(err.message())); }
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue