From fef3baa0ae4bc6b0e7fcc4a4e1df671ae8c123ee Mon Sep 17 00:00:00 2001 From: Sebastian Frick Date: Fri, 20 Feb 2026 09:08:50 +0100 Subject: [PATCH] refactor(inventory): InvalidFilterCombination Error und CreateStockResponse trennen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - StockError.InvalidFilterCombination statt InvalidArticleId für ungültige Filter-Kombination in ListStocks - CreateStockResponse als eigenständiges DTO für POST createStock (ohne batches/quantities), StockResponse bleibt für GET-Endpoints - MinimumLevelResponse als shared Top-Level-Record extrahiert --- .../application/inventory/ListStocks.java | 2 +- .../domain/inventory/StockError.java | 4 ++ .../web/controller/StockController.java | 5 ++- .../web/dto/CreateStockResponse.java | 37 +++++++++++++++++++ .../web/dto/MinimumLevelResponse.java | 8 ++++ .../inventory/web/dto/StockResponse.java | 3 -- .../InventoryErrorHttpStatusMapper.java | 1 + .../application/inventory/ListStocksTest.java | 2 +- 8 files changed, 55 insertions(+), 7 deletions(-) create mode 100644 backend/src/main/java/de/effigenix/infrastructure/inventory/web/dto/CreateStockResponse.java create mode 100644 backend/src/main/java/de/effigenix/infrastructure/inventory/web/dto/MinimumLevelResponse.java diff --git a/backend/src/main/java/de/effigenix/application/inventory/ListStocks.java b/backend/src/main/java/de/effigenix/application/inventory/ListStocks.java index 749764a..8dbd9a5 100644 --- a/backend/src/main/java/de/effigenix/application/inventory/ListStocks.java +++ b/backend/src/main/java/de/effigenix/application/inventory/ListStocks.java @@ -22,7 +22,7 @@ public class ListStocks { public Result> execute(String storageLocationId, String articleId) { if (storageLocationId != null && articleId != null) { - return Result.failure(new StockError.InvalidArticleId( + return Result.failure(new StockError.InvalidFilterCombination( "Only one filter parameter allowed: storageLocationId or articleId")); } diff --git a/backend/src/main/java/de/effigenix/domain/inventory/StockError.java b/backend/src/main/java/de/effigenix/domain/inventory/StockError.java index f94d564..d7e4328 100644 --- a/backend/src/main/java/de/effigenix/domain/inventory/StockError.java +++ b/backend/src/main/java/de/effigenix/domain/inventory/StockError.java @@ -80,6 +80,10 @@ public sealed interface StockError { @Override public String message() { return "Batch " + id + " is not blocked"; } } + record InvalidFilterCombination(String message) implements StockError { + @Override public String code() { return "INVALID_FILTER_COMBINATION"; } + } + record Unauthorized(String message) implements StockError { @Override public String code() { return "UNAUTHORIZED"; } } diff --git a/backend/src/main/java/de/effigenix/infrastructure/inventory/web/controller/StockController.java b/backend/src/main/java/de/effigenix/infrastructure/inventory/web/controller/StockController.java index 981f2e0..7bba592 100644 --- a/backend/src/main/java/de/effigenix/infrastructure/inventory/web/controller/StockController.java +++ b/backend/src/main/java/de/effigenix/infrastructure/inventory/web/controller/StockController.java @@ -17,6 +17,7 @@ import de.effigenix.shared.security.ActorId; import de.effigenix.infrastructure.inventory.web.dto.AddStockBatchRequest; import de.effigenix.infrastructure.inventory.web.dto.BlockStockBatchRequest; import de.effigenix.infrastructure.inventory.web.dto.CreateStockRequest; +import de.effigenix.infrastructure.inventory.web.dto.CreateStockResponse; import de.effigenix.infrastructure.inventory.web.dto.RemoveStockBatchRequest; import de.effigenix.infrastructure.inventory.web.dto.StockBatchResponse; import de.effigenix.infrastructure.inventory.web.dto.StockResponse; @@ -93,7 +94,7 @@ public class StockController { @PostMapping @PreAuthorize("hasAuthority('STOCK_WRITE')") - public ResponseEntity createStock( + public ResponseEntity createStock( @Valid @RequestBody CreateStockRequest request, Authentication authentication ) { @@ -113,7 +114,7 @@ public class StockController { logger.info("Stock created: {}", result.unsafeGetValue().id().value()); return ResponseEntity.status(HttpStatus.CREATED) - .body(StockResponse.from(result.unsafeGetValue())); + .body(CreateStockResponse.from(result.unsafeGetValue())); } @PostMapping("/{stockId}/batches") diff --git a/backend/src/main/java/de/effigenix/infrastructure/inventory/web/dto/CreateStockResponse.java b/backend/src/main/java/de/effigenix/infrastructure/inventory/web/dto/CreateStockResponse.java new file mode 100644 index 0000000..7ed3baf --- /dev/null +++ b/backend/src/main/java/de/effigenix/infrastructure/inventory/web/dto/CreateStockResponse.java @@ -0,0 +1,37 @@ +package de.effigenix.infrastructure.inventory.web.dto; + +import de.effigenix.domain.inventory.Stock; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(requiredProperties = {"id", "articleId", "storageLocationId"}) +public record CreateStockResponse( + String id, + String articleId, + String storageLocationId, + @Schema(nullable = true) MinimumLevelResponse minimumLevel, + @Schema(nullable = true) Integer minimumShelfLifeDays +) { + + public static CreateStockResponse from(Stock stock) { + MinimumLevelResponse minimumLevel = null; + if (stock.minimumLevel() != null) { + minimumLevel = new MinimumLevelResponse( + stock.minimumLevel().quantity().amount(), + stock.minimumLevel().quantity().uom().name() + ); + } + + Integer shelfLifeDays = null; + if (stock.minimumShelfLife() != null) { + shelfLifeDays = stock.minimumShelfLife().days(); + } + + return new CreateStockResponse( + stock.id().value(), + stock.articleId().value(), + stock.storageLocationId().value(), + minimumLevel, + shelfLifeDays + ); + } +} diff --git a/backend/src/main/java/de/effigenix/infrastructure/inventory/web/dto/MinimumLevelResponse.java b/backend/src/main/java/de/effigenix/infrastructure/inventory/web/dto/MinimumLevelResponse.java new file mode 100644 index 0000000..3a31efa --- /dev/null +++ b/backend/src/main/java/de/effigenix/infrastructure/inventory/web/dto/MinimumLevelResponse.java @@ -0,0 +1,8 @@ +package de.effigenix.infrastructure.inventory.web.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import java.math.BigDecimal; + +@Schema(requiredProperties = {"amount", "unit"}) +public record MinimumLevelResponse(BigDecimal amount, String unit) {} diff --git a/backend/src/main/java/de/effigenix/infrastructure/inventory/web/dto/StockResponse.java b/backend/src/main/java/de/effigenix/infrastructure/inventory/web/dto/StockResponse.java index a7c0d10..3ba799a 100644 --- a/backend/src/main/java/de/effigenix/infrastructure/inventory/web/dto/StockResponse.java +++ b/backend/src/main/java/de/effigenix/infrastructure/inventory/web/dto/StockResponse.java @@ -63,7 +63,4 @@ public record StockResponse( availableQuantity ); } - - @Schema(requiredProperties = {"amount", "unit"}) - public record MinimumLevelResponse(BigDecimal amount, String unit) {} } diff --git a/backend/src/main/java/de/effigenix/infrastructure/inventory/web/exception/InventoryErrorHttpStatusMapper.java b/backend/src/main/java/de/effigenix/infrastructure/inventory/web/exception/InventoryErrorHttpStatusMapper.java index 2227e91..46ca089 100644 --- a/backend/src/main/java/de/effigenix/infrastructure/inventory/web/exception/InventoryErrorHttpStatusMapper.java +++ b/backend/src/main/java/de/effigenix/infrastructure/inventory/web/exception/InventoryErrorHttpStatusMapper.java @@ -39,6 +39,7 @@ public final class InventoryErrorHttpStatusMapper { case StockError.InvalidBatchReference e -> 400; case StockError.InvalidQuantity e -> 400; case StockError.InvalidExpiryDate e -> 400; + case StockError.InvalidFilterCombination e -> 400; case StockError.Unauthorized e -> 403; case StockError.RepositoryFailure e -> 500; }; diff --git a/backend/src/test/java/de/effigenix/application/inventory/ListStocksTest.java b/backend/src/test/java/de/effigenix/application/inventory/ListStocksTest.java index 3c918b2..295b2ae 100644 --- a/backend/src/test/java/de/effigenix/application/inventory/ListStocksTest.java +++ b/backend/src/test/java/de/effigenix/application/inventory/ListStocksTest.java @@ -91,7 +91,7 @@ class ListStocksTest { var result = listStocks.execute("location-1", "article-1"); assertThat(result.isFailure()).isTrue(); - assertThat(result.unsafeGetError()).isInstanceOf(StockError.InvalidArticleId.class); + assertThat(result.unsafeGetError()).isInstanceOf(StockError.InvalidFilterCombination.class); verifyNoInteractions(stockRepository); }