mirror of
https://github.com/s-frick/effigenix.git
synced 2026-03-28 12:09:35 +01:00
fix(inventory): confirmReservation – Layering, Error-Semantik und FK-Bug
- MovementType-Ableitung via ReferenceType.toMovementType() in Domain Layer - Doppelten Batch-Lookup in Stock.confirmReservation() eliminiert - StockError.MovementCreationFailed statt RepositoryFailure für Domain-Fehler - 204 No Content statt 200 OK (konsistent mit releaseReservation) - Batches mit Menge 0 nicht mehr entfernen (FK stock_movements → stock_batches)
This commit is contained in:
parent
0b6028b967
commit
74dc9a6981
9 changed files with 43 additions and 44 deletions
|
|
@ -1853,15 +1853,17 @@ class StockTest {
|
|||
assertThat(confirmed.referenceType()).isEqualTo(ReferenceType.SALE_ORDER);
|
||||
assertThat(confirmed.allocations()).hasSize(2);
|
||||
assertThat(stock.reservations()).isEmpty();
|
||||
// batch1 fully consumed → removed, batch2 has 4kg remaining
|
||||
assertThat(stock.batches()).hasSize(1);
|
||||
assertThat(stock.batches().getFirst().quantity().amount())
|
||||
// batch1 fully consumed (0 kg), batch2 has 4kg remaining
|
||||
assertThat(stock.batches()).hasSize(2);
|
||||
assertThat(stock.batches().get(0).quantity().amount())
|
||||
.isEqualByComparingTo(BigDecimal.ZERO);
|
||||
assertThat(stock.batches().get(1).quantity().amount())
|
||||
.isEqualByComparingTo(new BigDecimal("4"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should remove batch when quantity reaches zero")
|
||||
void shouldRemoveBatchWhenQuantityZero() {
|
||||
@DisplayName("should keep batch with zero quantity after full deduction")
|
||||
void shouldKeepBatchWithZeroQuantity() {
|
||||
var stock = createStockWithBatchAndExpiry("10", UnitOfMeasure.KILOGRAM,
|
||||
StockBatchStatus.AVAILABLE, LocalDate.of(2026, 12, 31));
|
||||
|
||||
|
|
@ -1871,7 +1873,8 @@ class StockTest {
|
|||
var result = stock.confirmReservation(reserveResult.unsafeGetValue().id());
|
||||
|
||||
assertThat(result.isSuccess()).isTrue();
|
||||
assertThat(stock.batches()).isEmpty();
|
||||
assertThat(stock.batches()).hasSize(1);
|
||||
assertThat(stock.batches().getFirst().quantity().amount()).isEqualByComparingTo(BigDecimal.ZERO);
|
||||
assertThat(stock.reservations()).isEmpty();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1506,7 +1506,7 @@ class StockControllerIntegrationTest extends AbstractIntegrationTest {
|
|||
// Bestätigen
|
||||
mockMvc.perform(post("/api/inventory/stocks/{stockId}/reservations/{reservationId}/confirm", stockId, reservationId)
|
||||
.header("Authorization", "Bearer " + adminToken))
|
||||
.andExpect(status().isOk());
|
||||
.andExpect(status().isNoContent());
|
||||
|
||||
// Prüfen: Reservation entfernt, Menge physisch abgezogen
|
||||
mockMvc.perform(get("/api/inventory/stocks/{id}", stockId)
|
||||
|
|
@ -1517,8 +1517,8 @@ class StockControllerIntegrationTest extends AbstractIntegrationTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Bestätigung der gesamten Charge → Batch wird entfernt")
|
||||
void confirmReservation_fullBatch_removesBatch() throws Exception {
|
||||
@DisplayName("Bestätigung der gesamten Charge → Batch bleibt mit Menge 0")
|
||||
void confirmReservation_fullBatch_keepsBatchWithZeroQuantity() throws Exception {
|
||||
String stockId = createStock();
|
||||
addBatchToStock(stockId); // 10 KILOGRAM
|
||||
|
||||
|
|
@ -1535,13 +1535,14 @@ class StockControllerIntegrationTest extends AbstractIntegrationTest {
|
|||
// Bestätigen
|
||||
mockMvc.perform(post("/api/inventory/stocks/{stockId}/reservations/{reservationId}/confirm", stockId, reservationId)
|
||||
.header("Authorization", "Bearer " + adminToken))
|
||||
.andExpect(status().isOk());
|
||||
.andExpect(status().isNoContent());
|
||||
|
||||
// Batch entfernt
|
||||
// Batch bleibt mit Menge 0 (FK von stock_movements erfordert Persistenz)
|
||||
mockMvc.perform(get("/api/inventory/stocks/{id}", stockId)
|
||||
.header("Authorization", "Bearer " + adminToken))
|
||||
.andExpect(jsonPath("$.reservations.length()").value(0))
|
||||
.andExpect(jsonPath("$.batches.length()").value(0))
|
||||
.andExpect(jsonPath("$.batches.length()").value(1))
|
||||
.andExpect(jsonPath("$.batches[0].quantityAmount").value(0))
|
||||
.andExpect(jsonPath("$.availableQuantity").value(0));
|
||||
}
|
||||
|
||||
|
|
@ -1599,10 +1600,10 @@ class StockControllerIntegrationTest extends AbstractIntegrationTest {
|
|||
.andReturn();
|
||||
String reservationId = objectMapper.readTree(reserveResult.getResponse().getContentAsString()).get("id").asText();
|
||||
|
||||
// Erstes Confirm → 200
|
||||
// Erstes Confirm → 204
|
||||
mockMvc.perform(post("/api/inventory/stocks/{stockId}/reservations/{reservationId}/confirm", stockId, reservationId)
|
||||
.header("Authorization", "Bearer " + adminToken))
|
||||
.andExpect(status().isOk());
|
||||
.andExpect(status().isNoContent());
|
||||
|
||||
// Zweites Confirm → 404
|
||||
mockMvc.perform(post("/api/inventory/stocks/{stockId}/reservations/{reservationId}/confirm", stockId, reservationId)
|
||||
|
|
@ -1638,7 +1639,7 @@ class StockControllerIntegrationTest extends AbstractIntegrationTest {
|
|||
// Erste Reservierung bestätigen
|
||||
mockMvc.perform(post("/api/inventory/stocks/{stockId}/reservations/{reservationId}/confirm", stockId, reservationId1)
|
||||
.header("Authorization", "Bearer " + adminToken))
|
||||
.andExpect(status().isOk());
|
||||
.andExpect(status().isNoContent());
|
||||
|
||||
// Batch: 10 - 4 = 6 kg, SO-001 bleibt
|
||||
mockMvc.perform(get("/api/inventory/stocks/{id}", stockId)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue