mirror of
https://github.com/s-frick/effigenix.git
synced 2026-03-28 10:39: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:
parent
0e58cbfacf
commit
6504d3a54e
12 changed files with 95 additions and 18 deletions
|
|
@ -42,7 +42,7 @@ public class CancelProductionOrder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (order.cancel()) {
|
switch (order.cancel(cmd.reason())) {
|
||||||
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) -> { }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,8 @@ import java.time.ZoneOffset;
|
||||||
* 8. BatchId is set exactly once (null → non-null) during startProduction()
|
* 8. BatchId is set exactly once (null → non-null) during startProduction()
|
||||||
* 9. BatchId must not already be assigned (BatchAlreadyAssigned)
|
* 9. BatchId must not already be assigned (BatchAlreadyAssigned)
|
||||||
* 10. Only IN_PROGRESS → COMPLETED transition allowed via complete() (Batch must be COMPLETED – enforced by Use Case)
|
* 10. Only IN_PROGRESS → COMPLETED transition allowed via complete() (Batch must be COMPLETED – enforced by Use Case)
|
||||||
* 11. Only PLANNED or RELEASED → CANCELLED transition allowed via cancel()
|
* 11. Only PLANNED or RELEASED → CANCELLED transition allowed via cancel(reason)
|
||||||
|
* 12. CancelledReason is set exactly once during cancel() and must not be blank
|
||||||
*/
|
*/
|
||||||
public class ProductionOrder {
|
public class ProductionOrder {
|
||||||
|
|
||||||
|
|
@ -35,6 +36,7 @@ public class ProductionOrder {
|
||||||
private final LocalDate plannedDate;
|
private final LocalDate plannedDate;
|
||||||
private final Priority priority;
|
private final Priority priority;
|
||||||
private final String notes;
|
private final String notes;
|
||||||
|
private String cancelledReason;
|
||||||
private final OffsetDateTime createdAt;
|
private final OffsetDateTime createdAt;
|
||||||
private OffsetDateTime updatedAt;
|
private OffsetDateTime updatedAt;
|
||||||
private final long version;
|
private final long version;
|
||||||
|
|
@ -48,6 +50,7 @@ public class ProductionOrder {
|
||||||
LocalDate plannedDate,
|
LocalDate plannedDate,
|
||||||
Priority priority,
|
Priority priority,
|
||||||
String notes,
|
String notes,
|
||||||
|
String cancelledReason,
|
||||||
OffsetDateTime createdAt,
|
OffsetDateTime createdAt,
|
||||||
OffsetDateTime updatedAt,
|
OffsetDateTime updatedAt,
|
||||||
long version
|
long version
|
||||||
|
|
@ -60,6 +63,7 @@ public class ProductionOrder {
|
||||||
this.plannedDate = plannedDate;
|
this.plannedDate = plannedDate;
|
||||||
this.priority = priority;
|
this.priority = priority;
|
||||||
this.notes = notes;
|
this.notes = notes;
|
||||||
|
this.cancelledReason = cancelledReason;
|
||||||
this.createdAt = createdAt;
|
this.createdAt = createdAt;
|
||||||
this.updatedAt = updatedAt;
|
this.updatedAt = updatedAt;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
|
|
@ -120,6 +124,7 @@ public class ProductionOrder {
|
||||||
draft.plannedDate(),
|
draft.plannedDate(),
|
||||||
priority,
|
priority,
|
||||||
draft.notes(),
|
draft.notes(),
|
||||||
|
null,
|
||||||
now,
|
now,
|
||||||
now,
|
now,
|
||||||
0L
|
0L
|
||||||
|
|
@ -135,12 +140,13 @@ public class ProductionOrder {
|
||||||
LocalDate plannedDate,
|
LocalDate plannedDate,
|
||||||
Priority priority,
|
Priority priority,
|
||||||
String notes,
|
String notes,
|
||||||
|
String cancelledReason,
|
||||||
OffsetDateTime createdAt,
|
OffsetDateTime createdAt,
|
||||||
OffsetDateTime updatedAt,
|
OffsetDateTime updatedAt,
|
||||||
long version
|
long version
|
||||||
) {
|
) {
|
||||||
return new ProductionOrder(id, recipeId, status, batchId, plannedQuantity, plannedDate,
|
return new ProductionOrder(id, recipeId, status, batchId, plannedQuantity, plannedDate,
|
||||||
priority, notes, createdAt, updatedAt, version);
|
priority, notes, cancelledReason, createdAt, updatedAt, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProductionOrderId id() { return id; }
|
public ProductionOrderId id() { return id; }
|
||||||
|
|
@ -151,6 +157,7 @@ public class ProductionOrder {
|
||||||
public LocalDate plannedDate() { return plannedDate; }
|
public LocalDate plannedDate() { return plannedDate; }
|
||||||
public Priority priority() { return priority; }
|
public Priority priority() { return priority; }
|
||||||
public String notes() { return notes; }
|
public String notes() { return notes; }
|
||||||
|
public String cancelledReason() { return cancelledReason; }
|
||||||
public OffsetDateTime createdAt() { return createdAt; }
|
public OffsetDateTime createdAt() { return createdAt; }
|
||||||
public OffsetDateTime updatedAt() { return updatedAt; }
|
public OffsetDateTime updatedAt() { return updatedAt; }
|
||||||
public long version() { return version; }
|
public long version() { return version; }
|
||||||
|
|
@ -186,11 +193,15 @@ public class ProductionOrder {
|
||||||
return Result.success(null);
|
return Result.success(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result<ProductionOrderError, Void> cancel() {
|
public Result<ProductionOrderError, Void> cancel(String reason) {
|
||||||
|
if (reason == null || reason.isBlank()) {
|
||||||
|
return Result.failure(new ProductionOrderError.ValidationFailure("Cancellation reason must not be blank"));
|
||||||
|
}
|
||||||
if (status != ProductionOrderStatus.PLANNED && status != ProductionOrderStatus.RELEASED) {
|
if (status != ProductionOrderStatus.PLANNED && status != ProductionOrderStatus.RELEASED) {
|
||||||
return Result.failure(new ProductionOrderError.InvalidStatusTransition(status, ProductionOrderStatus.CANCELLED));
|
return Result.failure(new ProductionOrderError.InvalidStatusTransition(status, ProductionOrderStatus.CANCELLED));
|
||||||
}
|
}
|
||||||
this.status = ProductionOrderStatus.CANCELLED;
|
this.status = ProductionOrderStatus.CANCELLED;
|
||||||
|
this.cancelledReason = reason;
|
||||||
this.updatedAt = OffsetDateTime.now(ZoneOffset.UTC);
|
this.updatedAt = OffsetDateTime.now(ZoneOffset.UTC);
|
||||||
return Result.success(null);
|
return Result.success(null);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,8 @@ public class JdbcProductionOrderRepository implements ProductionOrderRepository
|
||||||
int rows = jdbc.sql("""
|
int rows = jdbc.sql("""
|
||||||
UPDATE production_orders
|
UPDATE production_orders
|
||||||
SET status = :status, batch_id = :batchId, priority = :priority,
|
SET status = :status, batch_id = :batchId, priority = :priority,
|
||||||
notes = :notes, updated_at = :updatedAt, version = version + 1
|
notes = :notes, cancelled_reason = :cancelledReason,
|
||||||
|
updated_at = :updatedAt, version = version + 1
|
||||||
WHERE id = :id AND version = :version
|
WHERE id = :id AND version = :version
|
||||||
""")
|
""")
|
||||||
.param("id", order.id().value())
|
.param("id", order.id().value())
|
||||||
|
|
@ -70,6 +71,7 @@ public class JdbcProductionOrderRepository implements ProductionOrderRepository
|
||||||
.param("batchId", order.batchId() != null ? order.batchId().value() : null)
|
.param("batchId", order.batchId() != null ? order.batchId().value() : null)
|
||||||
.param("priority", order.priority().name())
|
.param("priority", order.priority().name())
|
||||||
.param("notes", order.notes())
|
.param("notes", order.notes())
|
||||||
|
.param("cancelledReason", order.cancelledReason())
|
||||||
.param("updatedAt", order.updatedAt())
|
.param("updatedAt", order.updatedAt())
|
||||||
.param("version", order.version())
|
.param("version", order.version())
|
||||||
.update();
|
.update();
|
||||||
|
|
@ -88,9 +90,11 @@ public class JdbcProductionOrderRepository implements ProductionOrderRepository
|
||||||
jdbc.sql("""
|
jdbc.sql("""
|
||||||
INSERT INTO production_orders
|
INSERT INTO production_orders
|
||||||
(id, recipe_id, status, planned_quantity_amount, planned_quantity_unit,
|
(id, recipe_id, status, planned_quantity_amount, planned_quantity_unit,
|
||||||
planned_date, priority, batch_id, notes, created_at, updated_at, version)
|
planned_date, priority, batch_id, notes, cancelled_reason,
|
||||||
|
created_at, updated_at, version)
|
||||||
VALUES (:id, :recipeId, :status, :plannedQuantityAmount, :plannedQuantityUnit,
|
VALUES (:id, :recipeId, :status, :plannedQuantityAmount, :plannedQuantityUnit,
|
||||||
:plannedDate, :priority, :batchId, :notes, :createdAt, :updatedAt, 0)
|
:plannedDate, :priority, :batchId, :notes, :cancelledReason,
|
||||||
|
:createdAt, :updatedAt, 0)
|
||||||
""")
|
""")
|
||||||
.param("id", order.id().value())
|
.param("id", order.id().value())
|
||||||
.param("recipeId", order.recipeId().value())
|
.param("recipeId", order.recipeId().value())
|
||||||
|
|
@ -101,6 +105,7 @@ public class JdbcProductionOrderRepository implements ProductionOrderRepository
|
||||||
.param("priority", order.priority().name())
|
.param("priority", order.priority().name())
|
||||||
.param("batchId", order.batchId() != null ? order.batchId().value() : null)
|
.param("batchId", order.batchId() != null ? order.batchId().value() : null)
|
||||||
.param("notes", order.notes())
|
.param("notes", order.notes())
|
||||||
|
.param("cancelledReason", order.cancelledReason())
|
||||||
.param("createdAt", order.createdAt())
|
.param("createdAt", order.createdAt())
|
||||||
.param("updatedAt", order.updatedAt())
|
.param("updatedAt", order.updatedAt())
|
||||||
.update();
|
.update();
|
||||||
|
|
@ -126,6 +131,7 @@ public class JdbcProductionOrderRepository implements ProductionOrderRepository
|
||||||
rs.getObject("planned_date", java.time.LocalDate.class),
|
rs.getObject("planned_date", java.time.LocalDate.class),
|
||||||
Priority.valueOf(rs.getString("priority")),
|
Priority.valueOf(rs.getString("priority")),
|
||||||
rs.getString("notes"),
|
rs.getString("notes"),
|
||||||
|
rs.getString("cancelled_reason"),
|
||||||
rs.getObject("created_at", OffsetDateTime.class),
|
rs.getObject("created_at", OffsetDateTime.class),
|
||||||
rs.getObject("updated_at", OffsetDateTime.class),
|
rs.getObject("updated_at", OffsetDateTime.class),
|
||||||
rs.getLong("version")
|
rs.getLong("version")
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ public record ProductionOrderResponse(
|
||||||
LocalDate plannedDate,
|
LocalDate plannedDate,
|
||||||
String priority,
|
String priority,
|
||||||
String notes,
|
String notes,
|
||||||
|
String cancelledReason,
|
||||||
OffsetDateTime createdAt,
|
OffsetDateTime createdAt,
|
||||||
OffsetDateTime updatedAt
|
OffsetDateTime updatedAt
|
||||||
) {
|
) {
|
||||||
|
|
@ -29,6 +30,7 @@ public record ProductionOrderResponse(
|
||||||
order.plannedDate(),
|
order.plannedDate(),
|
||||||
order.priority().name(),
|
order.priority().name(),
|
||||||
order.notes(),
|
order.notes(),
|
||||||
|
order.cancelledReason(),
|
||||||
order.createdAt(),
|
order.createdAt(),
|
||||||
order.updatedAt()
|
order.updatedAt()
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<databaseChangeLog
|
||||||
|
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
|
||||||
|
|
||||||
|
<changeSet id="033-add-cancelled-reason-to-production-orders" author="effigenix">
|
||||||
|
<addColumn tableName="production_orders">
|
||||||
|
<column name="cancelled_reason" type="VARCHAR(1000)">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
|
|
@ -37,5 +37,6 @@
|
||||||
<include file="db/changelog/changes/030-add-batch-id-index-to-stock-movements.xml"/>
|
<include file="db/changelog/changes/030-add-batch-id-index-to-stock-movements.xml"/>
|
||||||
|
|
||||||
<include file="db/changelog/changes/032-add-batch-id-to-production-orders.xml"/>
|
<include file="db/changelog/changes/032-add-batch-id-to-production-orders.xml"/>
|
||||||
|
<include file="db/changelog/changes/033-add-cancelled-reason-to-production-orders.xml"/>
|
||||||
|
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ class CancelProductionOrderTest {
|
||||||
PLANNED_DATE,
|
PLANNED_DATE,
|
||||||
Priority.NORMAL,
|
Priority.NORMAL,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
OffsetDateTime.now(ZoneOffset.UTC),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
OffsetDateTime.now(ZoneOffset.UTC),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
1L
|
1L
|
||||||
|
|
@ -79,6 +80,7 @@ class CancelProductionOrderTest {
|
||||||
|
|
||||||
assertThat(result.isSuccess()).isTrue();
|
assertThat(result.isSuccess()).isTrue();
|
||||||
assertThat(result.unsafeGetValue().status()).isEqualTo(ProductionOrderStatus.CANCELLED);
|
assertThat(result.unsafeGetValue().status()).isEqualTo(ProductionOrderStatus.CANCELLED);
|
||||||
|
assertThat(result.unsafeGetValue().cancelledReason()).isEqualTo("Kunde hat storniert");
|
||||||
verify(productionOrderRepository).save(any(ProductionOrder.class));
|
verify(productionOrderRepository).save(any(ProductionOrder.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,6 +96,7 @@ class CancelProductionOrderTest {
|
||||||
|
|
||||||
assertThat(result.isSuccess()).isTrue();
|
assertThat(result.isSuccess()).isTrue();
|
||||||
assertThat(result.unsafeGetValue().status()).isEqualTo(ProductionOrderStatus.CANCELLED);
|
assertThat(result.unsafeGetValue().status()).isEqualTo(ProductionOrderStatus.CANCELLED);
|
||||||
|
assertThat(result.unsafeGetValue().cancelledReason()).isEqualTo("Kunde hat storniert");
|
||||||
verify(productionOrderRepository).save(any(ProductionOrder.class));
|
verify(productionOrderRepository).save(any(ProductionOrder.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ class CompleteProductionOrderTest {
|
||||||
PLANNED_DATE,
|
PLANNED_DATE,
|
||||||
Priority.NORMAL,
|
Priority.NORMAL,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
OffsetDateTime.now(ZoneOffset.UTC),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
OffsetDateTime.now(ZoneOffset.UTC),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
1L
|
1L
|
||||||
|
|
@ -79,6 +80,7 @@ class CompleteProductionOrderTest {
|
||||||
PLANNED_DATE,
|
PLANNED_DATE,
|
||||||
Priority.NORMAL,
|
Priority.NORMAL,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
OffsetDateTime.now(ZoneOffset.UTC),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
OffsetDateTime.now(ZoneOffset.UTC),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
1L
|
1L
|
||||||
|
|
@ -176,7 +178,7 @@ class CompleteProductionOrderTest {
|
||||||
ProductionOrderId.of("order-1"), RecipeId.of("recipe-1"),
|
ProductionOrderId.of("order-1"), RecipeId.of("recipe-1"),
|
||||||
ProductionOrderStatus.COMPLETED, BatchId.of("batch-1"),
|
ProductionOrderStatus.COMPLETED, BatchId.of("batch-1"),
|
||||||
Quantity.of(new BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
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);
|
OffsetDateTime.now(ZoneOffset.UTC), OffsetDateTime.now(ZoneOffset.UTC), 1L);
|
||||||
|
|
||||||
when(authPort.can(performedBy, ProductionAction.PRODUCTION_ORDER_WRITE)).thenReturn(true);
|
when(authPort.can(performedBy, ProductionAction.PRODUCTION_ORDER_WRITE)).thenReturn(true);
|
||||||
|
|
@ -199,7 +201,7 @@ class CompleteProductionOrderTest {
|
||||||
ProductionOrderId.of("order-1"), RecipeId.of("recipe-1"),
|
ProductionOrderId.of("order-1"), RecipeId.of("recipe-1"),
|
||||||
ProductionOrderStatus.CANCELLED, null,
|
ProductionOrderStatus.CANCELLED, null,
|
||||||
Quantity.of(new BigDecimal("100"), UnitOfMeasure.KILOGRAM).unsafeGetValue(),
|
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);
|
OffsetDateTime.now(ZoneOffset.UTC), OffsetDateTime.now(ZoneOffset.UTC), 1L);
|
||||||
|
|
||||||
when(authPort.can(performedBy, ProductionAction.PRODUCTION_ORDER_WRITE)).thenReturn(true);
|
when(authPort.can(performedBy, ProductionAction.PRODUCTION_ORDER_WRITE)).thenReturn(true);
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ class ReleaseProductionOrderTest {
|
||||||
PLANNED_DATE,
|
PLANNED_DATE,
|
||||||
Priority.NORMAL,
|
Priority.NORMAL,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
OffsetDateTime.now(ZoneOffset.UTC),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
OffsetDateTime.now(ZoneOffset.UTC),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
1L
|
1L
|
||||||
|
|
@ -79,6 +80,7 @@ class ReleaseProductionOrderTest {
|
||||||
PLANNED_DATE,
|
PLANNED_DATE,
|
||||||
Priority.NORMAL,
|
Priority.NORMAL,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
OffsetDateTime.now(ZoneOffset.UTC),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
OffsetDateTime.now(ZoneOffset.UTC),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
1L
|
1L
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ class StartProductionOrderTest {
|
||||||
PLANNED_DATE,
|
PLANNED_DATE,
|
||||||
Priority.NORMAL,
|
Priority.NORMAL,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
OffsetDateTime.now(ZoneOffset.UTC),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
OffsetDateTime.now(ZoneOffset.UTC),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
1L
|
1L
|
||||||
|
|
@ -79,6 +80,7 @@ class StartProductionOrderTest {
|
||||||
PLANNED_DATE,
|
PLANNED_DATE,
|
||||||
Priority.NORMAL,
|
Priority.NORMAL,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
OffsetDateTime.now(ZoneOffset.UTC),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
OffsetDateTime.now(ZoneOffset.UTC),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
1L
|
1L
|
||||||
|
|
|
||||||
|
|
@ -310,6 +310,7 @@ class ProductionOrderTest {
|
||||||
FUTURE_DATE,
|
FUTURE_DATE,
|
||||||
Priority.NORMAL,
|
Priority.NORMAL,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
OffsetDateTime.now(ZoneOffset.UTC).minusHours(1),
|
OffsetDateTime.now(ZoneOffset.UTC).minusHours(1),
|
||||||
OffsetDateTime.now(ZoneOffset.UTC).minusHours(1),
|
OffsetDateTime.now(ZoneOffset.UTC).minusHours(1),
|
||||||
1L
|
1L
|
||||||
|
|
@ -394,6 +395,7 @@ class ProductionOrderTest {
|
||||||
FUTURE_DATE,
|
FUTURE_DATE,
|
||||||
Priority.NORMAL,
|
Priority.NORMAL,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
OffsetDateTime.now(ZoneOffset.UTC).minusHours(1),
|
OffsetDateTime.now(ZoneOffset.UTC).minusHours(1),
|
||||||
OffsetDateTime.now(ZoneOffset.UTC).minusHours(1),
|
OffsetDateTime.now(ZoneOffset.UTC).minusHours(1),
|
||||||
1L
|
1L
|
||||||
|
|
@ -481,6 +483,7 @@ class ProductionOrderTest {
|
||||||
FUTURE_DATE,
|
FUTURE_DATE,
|
||||||
Priority.NORMAL,
|
Priority.NORMAL,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
OffsetDateTime.now(ZoneOffset.UTC),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
OffsetDateTime.now(ZoneOffset.UTC),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
1L
|
1L
|
||||||
|
|
@ -518,6 +521,7 @@ class ProductionOrderTest {
|
||||||
FUTURE_DATE,
|
FUTURE_DATE,
|
||||||
Priority.NORMAL,
|
Priority.NORMAL,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
OffsetDateTime.now(ZoneOffset.UTC).minusHours(1),
|
OffsetDateTime.now(ZoneOffset.UTC).minusHours(1),
|
||||||
OffsetDateTime.now(ZoneOffset.UTC).minusHours(1),
|
OffsetDateTime.now(ZoneOffset.UTC).minusHours(1),
|
||||||
1L
|
1L
|
||||||
|
|
@ -605,6 +609,7 @@ class ProductionOrderTest {
|
||||||
FUTURE_DATE,
|
FUTURE_DATE,
|
||||||
Priority.NORMAL,
|
Priority.NORMAL,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
OffsetDateTime.now(ZoneOffset.UTC).minusHours(1),
|
OffsetDateTime.now(ZoneOffset.UTC).minusHours(1),
|
||||||
OffsetDateTime.now(ZoneOffset.UTC).minusHours(1),
|
OffsetDateTime.now(ZoneOffset.UTC).minusHours(1),
|
||||||
1L
|
1L
|
||||||
|
|
@ -612,37 +617,61 @@ class ProductionOrderTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("should cancel PLANNED order")
|
@DisplayName("should cancel PLANNED order with reason")
|
||||||
void should_Cancel_When_Planned() {
|
void should_Cancel_When_Planned() {
|
||||||
var order = orderWithStatus(ProductionOrderStatus.PLANNED);
|
var order = orderWithStatus(ProductionOrderStatus.PLANNED);
|
||||||
var beforeUpdate = order.updatedAt();
|
var beforeUpdate = order.updatedAt();
|
||||||
|
|
||||||
var result = order.cancel();
|
var result = order.cancel("Kunde hat storniert");
|
||||||
|
|
||||||
assertThat(result.isSuccess()).isTrue();
|
assertThat(result.isSuccess()).isTrue();
|
||||||
assertThat(order.status()).isEqualTo(ProductionOrderStatus.CANCELLED);
|
assertThat(order.status()).isEqualTo(ProductionOrderStatus.CANCELLED);
|
||||||
|
assertThat(order.cancelledReason()).isEqualTo("Kunde hat storniert");
|
||||||
assertThat(order.updatedAt()).isAfter(beforeUpdate);
|
assertThat(order.updatedAt()).isAfter(beforeUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("should cancel RELEASED order")
|
@DisplayName("should cancel RELEASED order with reason")
|
||||||
void should_Cancel_When_Released() {
|
void should_Cancel_When_Released() {
|
||||||
var order = orderWithStatus(ProductionOrderStatus.RELEASED);
|
var order = orderWithStatus(ProductionOrderStatus.RELEASED);
|
||||||
var beforeUpdate = order.updatedAt();
|
var beforeUpdate = order.updatedAt();
|
||||||
|
|
||||||
var result = order.cancel();
|
var result = order.cancel("Material nicht verfügbar");
|
||||||
|
|
||||||
assertThat(result.isSuccess()).isTrue();
|
assertThat(result.isSuccess()).isTrue();
|
||||||
assertThat(order.status()).isEqualTo(ProductionOrderStatus.CANCELLED);
|
assertThat(order.status()).isEqualTo(ProductionOrderStatus.CANCELLED);
|
||||||
|
assertThat(order.cancelledReason()).isEqualTo("Material nicht verfügbar");
|
||||||
assertThat(order.updatedAt()).isAfter(beforeUpdate);
|
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
|
@Test
|
||||||
@DisplayName("should fail when cancelling IN_PROGRESS order")
|
@DisplayName("should fail when cancelling IN_PROGRESS order")
|
||||||
void should_Fail_When_InProgress() {
|
void should_Fail_When_InProgress() {
|
||||||
var order = orderWithStatus(ProductionOrderStatus.IN_PROGRESS);
|
var order = orderWithStatus(ProductionOrderStatus.IN_PROGRESS);
|
||||||
|
|
||||||
var result = order.cancel();
|
var result = order.cancel("Storniergrund");
|
||||||
|
|
||||||
assertThat(result.isFailure()).isTrue();
|
assertThat(result.isFailure()).isTrue();
|
||||||
var err = (ProductionOrderError.InvalidStatusTransition) result.unsafeGetError();
|
var err = (ProductionOrderError.InvalidStatusTransition) result.unsafeGetError();
|
||||||
|
|
@ -655,7 +684,7 @@ class ProductionOrderTest {
|
||||||
void should_Fail_When_Completed() {
|
void should_Fail_When_Completed() {
|
||||||
var order = orderWithStatus(ProductionOrderStatus.COMPLETED);
|
var order = orderWithStatus(ProductionOrderStatus.COMPLETED);
|
||||||
|
|
||||||
var result = order.cancel();
|
var result = order.cancel("Storniergrund");
|
||||||
|
|
||||||
assertThat(result.isFailure()).isTrue();
|
assertThat(result.isFailure()).isTrue();
|
||||||
var err = (ProductionOrderError.InvalidStatusTransition) result.unsafeGetError();
|
var err = (ProductionOrderError.InvalidStatusTransition) result.unsafeGetError();
|
||||||
|
|
@ -668,7 +697,7 @@ class ProductionOrderTest {
|
||||||
void should_Fail_When_AlreadyCancelled() {
|
void should_Fail_When_AlreadyCancelled() {
|
||||||
var order = orderWithStatus(ProductionOrderStatus.CANCELLED);
|
var order = orderWithStatus(ProductionOrderStatus.CANCELLED);
|
||||||
|
|
||||||
var result = order.cancel();
|
var result = order.cancel("Storniergrund");
|
||||||
|
|
||||||
assertThat(result.isFailure()).isTrue();
|
assertThat(result.isFailure()).isTrue();
|
||||||
var err = (ProductionOrderError.InvalidStatusTransition) result.unsafeGetError();
|
var err = (ProductionOrderError.InvalidStatusTransition) result.unsafeGetError();
|
||||||
|
|
@ -693,6 +722,7 @@ class ProductionOrderTest {
|
||||||
FUTURE_DATE,
|
FUTURE_DATE,
|
||||||
Priority.HIGH,
|
Priority.HIGH,
|
||||||
"Wichtiger Auftrag",
|
"Wichtiger Auftrag",
|
||||||
|
null,
|
||||||
OffsetDateTime.now(ZoneOffset.UTC),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
OffsetDateTime.now(ZoneOffset.UTC),
|
OffsetDateTime.now(ZoneOffset.UTC),
|
||||||
5L
|
5L
|
||||||
|
|
|
||||||
|
|
@ -684,7 +684,8 @@ class ProductionOrderControllerIntegrationTest extends AbstractIntegrationTest {
|
||||||
.content(json))
|
.content(json))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.id").value(orderId))
|
.andExpect(jsonPath("$.id").value(orderId))
|
||||||
.andExpect(jsonPath("$.status").value("CANCELLED"));
|
.andExpect(jsonPath("$.status").value("CANCELLED"))
|
||||||
|
.andExpect(jsonPath("$.cancelledReason").value("Kunde hat storniert"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -702,7 +703,8 @@ class ProductionOrderControllerIntegrationTest extends AbstractIntegrationTest {
|
||||||
.content(json))
|
.content(json))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.id").value(orderId))
|
.andExpect(jsonPath("$.id").value(orderId))
|
||||||
.andExpect(jsonPath("$.status").value("CANCELLED"));
|
.andExpect(jsonPath("$.status").value("CANCELLED"))
|
||||||
|
.andExpect(jsonPath("$.cancelledReason").value("Material nicht verfügbar"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue