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

fix(production): cancelledReason im ProductionOrder-Aggregat persistieren

cancel() nimmt jetzt einen reason-Parameter entgegen und speichert ihn
im Aggregat, wie im DDD-Modell (04-production-bc.md) spezifiziert.
Liquibase-Migration für cancelled_reason-Spalte ergänzt.
This commit is contained in:
Sebastian Frick 2026-02-25 22:27:51 +01:00
parent 0e58cbfacf
commit 6504d3a54e
12 changed files with 95 additions and 18 deletions

View file

@ -61,6 +61,7 @@ class CancelProductionOrderTest {
PLANNED_DATE,
Priority.NORMAL,
null,
null,
OffsetDateTime.now(ZoneOffset.UTC),
OffsetDateTime.now(ZoneOffset.UTC),
1L
@ -79,6 +80,7 @@ class CancelProductionOrderTest {
assertThat(result.isSuccess()).isTrue();
assertThat(result.unsafeGetValue().status()).isEqualTo(ProductionOrderStatus.CANCELLED);
assertThat(result.unsafeGetValue().cancelledReason()).isEqualTo("Kunde hat storniert");
verify(productionOrderRepository).save(any(ProductionOrder.class));
}
@ -94,6 +96,7 @@ class CancelProductionOrderTest {
assertThat(result.isSuccess()).isTrue();
assertThat(result.unsafeGetValue().status()).isEqualTo(ProductionOrderStatus.CANCELLED);
assertThat(result.unsafeGetValue().cancelledReason()).isEqualTo("Kunde hat storniert");
verify(productionOrderRepository).save(any(ProductionOrder.class));
}

View file

@ -63,6 +63,7 @@ class CompleteProductionOrderTest {
PLANNED_DATE,
Priority.NORMAL,
null,
null,
OffsetDateTime.now(ZoneOffset.UTC),
OffsetDateTime.now(ZoneOffset.UTC),
1L
@ -79,6 +80,7 @@ class CompleteProductionOrderTest {
PLANNED_DATE,
Priority.NORMAL,
null,
null,
OffsetDateTime.now(ZoneOffset.UTC),
OffsetDateTime.now(ZoneOffset.UTC),
1L
@ -176,7 +178,7 @@ class CompleteProductionOrderTest {
ProductionOrderId.of("order-1"), RecipeId.of("recipe-1"),
ProductionOrderStatus.COMPLETED, BatchId.of("batch-1"),
Quantity.of(new BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
PLANNED_DATE, Priority.NORMAL, null,
PLANNED_DATE, Priority.NORMAL, null, null,
OffsetDateTime.now(ZoneOffset.UTC), OffsetDateTime.now(ZoneOffset.UTC), 1L);
when(authPort.can(performedBy, ProductionAction.PRODUCTION_ORDER_WRITE)).thenReturn(true);
@ -199,7 +201,7 @@ class CompleteProductionOrderTest {
ProductionOrderId.of("order-1"), RecipeId.of("recipe-1"),
ProductionOrderStatus.CANCELLED, null,
Quantity.of(new BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
PLANNED_DATE, Priority.NORMAL, null,
PLANNED_DATE, Priority.NORMAL, null, "Storniert",
OffsetDateTime.now(ZoneOffset.UTC), OffsetDateTime.now(ZoneOffset.UTC), 1L);
when(authPort.can(performedBy, ProductionAction.PRODUCTION_ORDER_WRITE)).thenReturn(true);

View file

@ -63,6 +63,7 @@ class ReleaseProductionOrderTest {
PLANNED_DATE,
Priority.NORMAL,
null,
null,
OffsetDateTime.now(ZoneOffset.UTC),
OffsetDateTime.now(ZoneOffset.UTC),
1L
@ -79,6 +80,7 @@ class ReleaseProductionOrderTest {
PLANNED_DATE,
Priority.NORMAL,
null,
null,
OffsetDateTime.now(ZoneOffset.UTC),
OffsetDateTime.now(ZoneOffset.UTC),
1L

View file

@ -63,6 +63,7 @@ class StartProductionOrderTest {
PLANNED_DATE,
Priority.NORMAL,
null,
null,
OffsetDateTime.now(ZoneOffset.UTC),
OffsetDateTime.now(ZoneOffset.UTC),
1L
@ -79,6 +80,7 @@ class StartProductionOrderTest {
PLANNED_DATE,
Priority.NORMAL,
null,
null,
OffsetDateTime.now(ZoneOffset.UTC),
OffsetDateTime.now(ZoneOffset.UTC),
1L

View file

@ -310,6 +310,7 @@ class ProductionOrderTest {
FUTURE_DATE,
Priority.NORMAL,
null,
null,
OffsetDateTime.now(ZoneOffset.UTC).minusHours(1),
OffsetDateTime.now(ZoneOffset.UTC).minusHours(1),
1L
@ -394,6 +395,7 @@ class ProductionOrderTest {
FUTURE_DATE,
Priority.NORMAL,
null,
null,
OffsetDateTime.now(ZoneOffset.UTC).minusHours(1),
OffsetDateTime.now(ZoneOffset.UTC).minusHours(1),
1L
@ -481,6 +483,7 @@ class ProductionOrderTest {
FUTURE_DATE,
Priority.NORMAL,
null,
null,
OffsetDateTime.now(ZoneOffset.UTC),
OffsetDateTime.now(ZoneOffset.UTC),
1L
@ -518,6 +521,7 @@ class ProductionOrderTest {
FUTURE_DATE,
Priority.NORMAL,
null,
null,
OffsetDateTime.now(ZoneOffset.UTC).minusHours(1),
OffsetDateTime.now(ZoneOffset.UTC).minusHours(1),
1L
@ -605,6 +609,7 @@ class ProductionOrderTest {
FUTURE_DATE,
Priority.NORMAL,
null,
null,
OffsetDateTime.now(ZoneOffset.UTC).minusHours(1),
OffsetDateTime.now(ZoneOffset.UTC).minusHours(1),
1L
@ -612,37 +617,61 @@ class ProductionOrderTest {
}
@Test
@DisplayName("should cancel PLANNED order")
@DisplayName("should cancel PLANNED order with reason")
void should_Cancel_When_Planned() {
var order = orderWithStatus(ProductionOrderStatus.PLANNED);
var beforeUpdate = order.updatedAt();
var result = order.cancel();
var result = order.cancel("Kunde hat storniert");
assertThat(result.isSuccess()).isTrue();
assertThat(order.status()).isEqualTo(ProductionOrderStatus.CANCELLED);
assertThat(order.cancelledReason()).isEqualTo("Kunde hat storniert");
assertThat(order.updatedAt()).isAfter(beforeUpdate);
}
@Test
@DisplayName("should cancel RELEASED order")
@DisplayName("should cancel RELEASED order with reason")
void should_Cancel_When_Released() {
var order = orderWithStatus(ProductionOrderStatus.RELEASED);
var beforeUpdate = order.updatedAt();
var result = order.cancel();
var result = order.cancel("Material nicht verfügbar");
assertThat(result.isSuccess()).isTrue();
assertThat(order.status()).isEqualTo(ProductionOrderStatus.CANCELLED);
assertThat(order.cancelledReason()).isEqualTo("Material nicht verfügbar");
assertThat(order.updatedAt()).isAfter(beforeUpdate);
}
@Test
@DisplayName("should fail when reason is blank")
void should_Fail_When_ReasonBlank() {
var order = orderWithStatus(ProductionOrderStatus.PLANNED);
var result = order.cancel("");
assertThat(result.isFailure()).isTrue();
assertThat(result.unsafeGetError()).isInstanceOf(ProductionOrderError.ValidationFailure.class);
}
@Test
@DisplayName("should fail when reason is null")
void should_Fail_When_ReasonNull() {
var order = orderWithStatus(ProductionOrderStatus.PLANNED);
var result = order.cancel(null);
assertThat(result.isFailure()).isTrue();
assertThat(result.unsafeGetError()).isInstanceOf(ProductionOrderError.ValidationFailure.class);
}
@Test
@DisplayName("should fail when cancelling IN_PROGRESS order")
void should_Fail_When_InProgress() {
var order = orderWithStatus(ProductionOrderStatus.IN_PROGRESS);
var result = order.cancel();
var result = order.cancel("Storniergrund");
assertThat(result.isFailure()).isTrue();
var err = (ProductionOrderError.InvalidStatusTransition) result.unsafeGetError();
@ -655,7 +684,7 @@ class ProductionOrderTest {
void should_Fail_When_Completed() {
var order = orderWithStatus(ProductionOrderStatus.COMPLETED);
var result = order.cancel();
var result = order.cancel("Storniergrund");
assertThat(result.isFailure()).isTrue();
var err = (ProductionOrderError.InvalidStatusTransition) result.unsafeGetError();
@ -668,7 +697,7 @@ class ProductionOrderTest {
void should_Fail_When_AlreadyCancelled() {
var order = orderWithStatus(ProductionOrderStatus.CANCELLED);
var result = order.cancel();
var result = order.cancel("Storniergrund");
assertThat(result.isFailure()).isTrue();
var err = (ProductionOrderError.InvalidStatusTransition) result.unsafeGetError();
@ -693,6 +722,7 @@ class ProductionOrderTest {
FUTURE_DATE,
Priority.HIGH,
"Wichtiger Auftrag",
null,
OffsetDateTime.now(ZoneOffset.UTC),
OffsetDateTime.now(ZoneOffset.UTC),
5L

View file

@ -684,7 +684,8 @@ class ProductionOrderControllerIntegrationTest extends AbstractIntegrationTest {
.content(json))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(orderId))
.andExpect(jsonPath("$.status").value("CANCELLED"));
.andExpect(jsonPath("$.status").value("CANCELLED"))
.andExpect(jsonPath("$.cancelledReason").value("Kunde hat storniert"));
}
@Test
@ -702,7 +703,8 @@ class ProductionOrderControllerIntegrationTest extends AbstractIntegrationTest {
.content(json))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(orderId))
.andExpect(jsonPath("$.status").value("CANCELLED"));
.andExpect(jsonPath("$.status").value("CANCELLED"))
.andExpect(jsonPath("$.cancelledReason").value("Material nicht verfügbar"));
}
@Test