1
0
Fork 0
mirror of https://github.com/s-frick/effigenix.git synced 2026-03-28 10:09:35 +01:00

refactor(inventory): InvalidFilterCombination Error und CreateStockResponse trennen

- 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
This commit is contained in:
Sebastian Frick 2026-02-20 09:08:50 +01:00
parent 1ef37497c3
commit fef3baa0ae
8 changed files with 55 additions and 7 deletions

View file

@ -22,7 +22,7 @@ public class ListStocks {
public Result<StockError, List<Stock>> 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"));
}

View file

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

View file

@ -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<StockResponse> createStock(
public ResponseEntity<CreateStockResponse> 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")

View file

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

View file

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

View file

@ -63,7 +63,4 @@ public record StockResponse(
availableQuantity
);
}
@Schema(requiredProperties = {"amount", "unit"})
public record MinimumLevelResponse(BigDecimal amount, String unit) {}
}

View file

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

View file

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