diff --git a/.gitignore b/.gitignore
index 667aaef..3bafc06 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@ target/
.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
+scripts/
### STS ###
.apt_generated
diff --git a/CLAUDE.md b/CLAUDE.md
deleted file mode 100644
index 621ef2f..0000000
--- a/CLAUDE.md
+++ /dev/null
@@ -1,230 +0,0 @@
-# CLAUDE.md
-
-This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
-
-## Development Commands
-
-### Build and Test
-- `mvn clean compile` - Compile the project
-- `mvn clean test` - Run unit tests (excludes e2e tests)
-- `mvn clean verify` - Run all tests including e2e tests
-- `mvn clean package` - Build the JAR file
-- `mvn spring-boot:run` - Run the application locally
-
-### Local Development
-- Run with `local` Spring profile for development (uses testcontainers for Kafka)
-- Access Kafka UI at `http://localhost:8088` when running locally
-- Docker daemon must be running for local development
-
-### Testing
-- Unit tests: `mvn test`
-- E2E tests: `mvn failsafe:integration-test`
-- Single test: `mvn test -Dtest=TestClassName`
-
-## Architecture Overview
-
-### Domain-Driven Design Structure
-The codebase follows a hexagonal architecture with clear domain boundaries:
-
-- **Domain Layer** (`order.domain`): Core business logic with `OrderFacade` as the main entry point
-- **Application Layer** (`api`): REST controllers, gRPC services, and Kafka consumers
-- **Infrastructure Layer** (`order.adapter`): Database, Kafka, and external service implementations
-
-### Key Components
-- **OrderFacade**: Main domain service interface for order operations
-- **Order Aggregates**: Domain entities (Order, OrderItem, OrderStatus)
-- **Repository Pattern**: Configurable implementations (DB, in-memory)
-- **Event Publisher**: Kafka-based domain event publishing
-- **Payment Integration**: Kafka-based communication with payment service
-
-### Configuration Profiles
-- `local`: Uses testcontainers for Kafka and PostgreSQL
-- `dev`: Development environment configuration
-- `compose`: Docker compose environment
-- `test`: Test environment with H2 database
-
-### Kafka Topics
-- `payment-processed-succeeded/failed`: Payment result events
-- `payment-request`: Payment initiation
-- `order-status-updated`: Order state changes
-- `cart-event`: Cart-related events
-
-### Configurable Components
-The service uses Spring profiles to switch between implementations:
-- Repository: `db` or `inmemory`
-- Payment Client: `kafka` or `inmemory`
-- Event Publisher: `kafka` or `inmemory`
-- ID Generator: `random` or `fixed`
-
-### gRPC Integration
-The service includes gRPC endpoints using protobuf definitions from the `com.ecmsp:protos` dependency.
-
-### Database
-- PostgreSQL for production
-- H2 for testing
-- JPA entities with Hibernate
-- Flyway for migrations (enabled by default)
-
-## API Endpoints for E2E Testing
-
-### REST API Endpoints
-
-#### Base URL
-- **REST API**: `http://localhost:8300/api/orders`
-- **Health Check**: `http://localhost:8300/health`
-
-#### OrdersController (`/api/orders`)
-
-**GET /api/orders**
-- List all orders (TODO: requires admin authorization)
-- Response: `200 OK` with array of `OrderDetailsResponse`
-
-**GET /api/orders/user/{userId}**
-- List orders by user ID (uses JWT context from gateway)
-- Requires: `X-User-Id` header (injected by gateway)
-- Response: `200 OK` with array of `OrderDetailsResponse`
-
-**GET /api/orders/{orderId}**
-- Get order by ID
-- Path parameter: `orderId` (UUID)
-- Response: `200 OK` with `OrderDetailsResponse` or `404 Not Found`
-
-**POST /api/orders**
-- Create a new order
-- Headers:
- - `X-Correlation-Id` (optional): UUID for request correlation
-- Request body: `CreateOrderRequest`
-```json
-{
- "clientId": "uuid",
- "items": [
- {
- "itemId": "uuid",
- "quantity": 1,
- "price": 99.99,
- "returnable": true
- }
- ]
-}
-```
-- Response: `201 Created` with `OrderDetailsResponse`
-
-**PUT /api/orders/{orderId}**
-- Update order status
-- Path parameter: `orderId` (UUID)
-- Request body: `UpdateOrderRequest`
-```json
-{
- "orderStatus": "PAID"
-}
-```
-- Valid statuses: `PENDING`, `PROCESSING`, `PAID`, `FAILED`, `CANCELLED`, `COMPLETED`
-- Response: `200 OK` with `OrderDetailsResponse`
-
-**DELETE /api/orders/{orderId}**
-- Delete an order
-- Path parameter: `orderId` (UUID)
-- Response: `204 No Content`
-
-**GET /api/orders/{orderId}/returnability**
-- Get order returnability details
-- Path parameter: `orderId` (UUID)
-- Response: `200 OK` with `OrderReturnabilityResponse` or `404 Not Found`
-
-**GET /api/orders/{orderId}/returnable**
-- Check if order can be returned (boolean)
-- Path parameter: `orderId` (UUID)
-- Response: `200 OK` with boolean value
-
-#### InternalOrdersController (`/api/internal/orders`)
-
-**POST /api/internal/orders/order-id-mappings**
-- Create order ID mapping (only available when `order.id-generator.type=fixed`)
-- Request body: `OrderIdMappingDto`
-```json
-{
- "correlationId": "uuid",
- "orderId": "uuid"
-}
-```
-- Response: `200 OK`
-
-#### HealthController (`/health`)
-
-**GET /health**
-- Health check endpoint
-- Response: `200 OK` with `"OK"` string
-
-### gRPC API Endpoints
-
-#### Service Configuration
-- **gRPC Server**: `localhost:7300`
-- **Proto Package**: `com.ecmsp.order.v1`
-- **Service**: `OrderService`
-
-#### gRPC Methods
-
-**GetOrder**
-- Request: `GetOrderRequest { string order_id }`
-- Response: `GetOrderResponse` with order details
-- Errors: `NOT_FOUND` if order doesn't exist, `INTERNAL` for other errors
-
-**CreateOrder**
-- Request: `CreateOrderRequest` with client ID and items
-- Response: `CreateOrderResponse` with created order details
-- Note: Context/metadata support is planned for future versions
-- Errors: `INTERNAL` on failure
-
-**UpdateOrder**
-- Request: `UpdateOrderRequest` with order ID and new status
-- Response: `UpdateOrderResponse` with updated order details
-- Errors: `NOT_FOUND` if order doesn't exist, `INTERNAL` for other errors
-
-**DeleteOrder**
-- Request: `DeleteOrderRequest { string order_id }`
-- Response: `DeleteOrderResponse { bool success }`
-- Errors: `INTERNAL` on failure
-
-**ListOrders**
-- Request: `ListOrdersRequest` (empty)
-- Response: `ListOrdersResponse` with array of orders
-- Errors: `INTERNAL` on failure
-
-### Response Models
-
-**OrderDetailsResponse**
-```json
-{
- "orderId": "uuid",
- "clientId": "uuid",
- "orderStatus": "PENDING|PROCESSING|PAID|FAILED|CANCELLED|COMPLETED",
- "date": "2025-09-30T12:00:00",
- "items": [
- {
- "itemId": "uuid",
- "quantity": 1
- }
- ]
-}
-```
-
-### E2E Testing Notes
-
-1. **Authentication**: The service expects JWT context via headers (injected by API Gateway)
- - Use `/api/orders/user/{userId}` endpoint to test gateway integration
-
-2. **Correlation IDs**: Include `X-Correlation-Id` header for request tracing
-
-3. **Order Lifecycle**:
- - Orders start in `PENDING` status
- - Payment processing moves to `PROCESSING` then `PAID` or `FAILED`
- - Can be `CANCELLED` by user or system
- - Final status is `COMPLETED` after delivery
-
-4. **Kafka Integration**: Creating orders triggers payment requests via Kafka
- - Topic: `payment-request`
- - Listen on: `payment-processed-succeeded` or `payment-processed-failed` for results
-
-5. **Database State**: Orders are persisted to PostgreSQL (or H2 in tests)
-
-6. **Proto Definitions**: gRPC contracts are defined in `com.ecmsp:protos` dependency
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 050d820..71d98a8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -85,9 +85,7 @@
spring-boot-starter-data-jpa
-
+
@@ -97,7 +95,7 @@
com.ecmsp
protos
- 1.0.0-20251005.125206-32
+ 1.0.0-20251007.110512-35
diff --git a/src/e2e-test/java/com/ecmsp/orderservice/e2e/tests/CreateOrderE2ETest.java b/src/e2e-test/java/com/ecmsp/orderservice/e2e/tests/CreateOrderE2ETest.java
index a4330d0..e01c14c 100644
--- a/src/e2e-test/java/com/ecmsp/orderservice/e2e/tests/CreateOrderE2ETest.java
+++ b/src/e2e-test/java/com/ecmsp/orderservice/e2e/tests/CreateOrderE2ETest.java
@@ -26,17 +26,21 @@ public class CreateOrderE2ETest {
private static final List ITEMS = List.of(
new CartItem(
ITEM_1_ID,
+ null,
"Item 1",
new java.math.BigDecimal("10.00"),
2,
+ null,
"Description for Item 1",
false
),
new CartItem(
ITEM_2_ID,
+ null,
"Item 2",
new java.math.BigDecimal("20.00"),
1,
+ null,
"Description for Item 2",
true
)
diff --git a/src/main/java/com/ecmsp/orderservice/api/grpc/OrderGrpcMapper.java b/src/main/java/com/ecmsp/orderservice/api/grpc/OrderGrpcMapper.java
index 2a9a593..4196e53 100644
--- a/src/main/java/com/ecmsp/orderservice/api/grpc/OrderGrpcMapper.java
+++ b/src/main/java/com/ecmsp/orderservice/api/grpc/OrderGrpcMapper.java
@@ -1,14 +1,15 @@
package com.ecmsp.orderservice.api.grpc;
-import com.ecmsp.order.v1.*;
+
+import com.ecmsp.order.v1.GetOrderItemsResponse;
+import com.ecmsp.order.v1.GetOrderResponse;
+import com.ecmsp.order.v1.GetOrderStatusResponse;
+import com.ecmsp.order.v1.OrderItemDetails;
+import com.ecmsp.orderservice.order.domain.Order;
import com.ecmsp.orderservice.order.domain.OrderItem;
import com.ecmsp.orderservice.order.domain.OrderStatus;
-
-import com.ecmsp.orderservice.order.domain.*;
import org.springframework.stereotype.Component;
-import java.math.BigDecimal;
import java.util.List;
-import java.util.UUID;
import static com.ecmsp.orderservice.order.domain.OrderStatus.*;
@@ -21,12 +22,13 @@ public GetOrderResponse toOrderResponse(Order order) {
.map(this::toOrderItemDetails)
.toList();
- return GetOrderResponse.newBuilder()
+ GetOrderResponse.Builder builder = GetOrderResponse.newBuilder()
.setOrderId(order.orderId().toString())
.setOrderStatus(toOrderStatusProto(order.orderStatus()))
.setDate(order.date().toString())
- .addAllItems(itemDetails)
- .build();
+ .addAllItems(itemDetails);
+
+ return builder.build();
}
public GetOrderResponse toGetOrderResponse(Order order) {
@@ -35,34 +37,6 @@ public GetOrderResponse toGetOrderResponse(Order order) {
- public CreateOrderResponse toCreateOrderResponse(Order order) {
- List itemDetails = order.items().stream()
- .map(this::toOrderItemDetails)
- .toList();
-
- return CreateOrderResponse.newBuilder()
- .setOrderId(order.orderId().toString())
- .setClientId(order.clientId().toString())
- .setOrderStatus(toOrderStatusProto(order.orderStatus()))
- .setDate(order.date().toString())
- .addAllItems(itemDetails)
- .build();
- }
-
-
- public UpdateOrderResponse toUpdateOrderResponse(Order order) {
- List itemDetails = order.items().stream()
- .map(this::toOrderItemDetails)
- .toList();
-
- return UpdateOrderResponse.newBuilder()
- .setOrderId(order.orderId().toString())
- .setClientId(order.clientId().toString())
- .setOrderStatus(toOrderStatusProto(order.orderStatus()))
- .setDate(order.date().toString())
- .addAllItems(itemDetails)
- .build();
- }
public com.ecmsp.order.v1.OrderStatus toOrderStatusProto(OrderStatus orderStatusDomain) {
@@ -72,7 +46,12 @@ public com.ecmsp.order.v1.OrderStatus toOrderStatusProto(OrderStatus orderStatus
case PAID -> com.ecmsp.order.v1.OrderStatus.ORDER_STATUS_PAID;
case FAILED -> com.ecmsp.order.v1.OrderStatus.ORDER_STATUS_FAILED;
case CANCELLED -> com.ecmsp.order.v1.OrderStatus.ORDER_STATUS_CANCELLED;
- default -> com.ecmsp.order.v1.OrderStatus.ORDER_STATUS_UNSPECIFIED;
+ case SHIPPED -> com.ecmsp.order.v1.OrderStatus.ORDER_STATUS_SHIPPED;
+ case DELIVERED -> com.ecmsp.order.v1.OrderStatus.ORDER_STATUS_DELIVERED;
+ case RETURN_REQUESTED -> com.ecmsp.order.v1.OrderStatus.ORDER_STATUS_RETURN_REQUESTED;
+ case RETURN_PROCESSING -> com.ecmsp.order.v1.OrderStatus.ORDER_STATUS_RETURN_PROCESSING;
+ case RETURNED -> com.ecmsp.order.v1.OrderStatus.ORDER_STATUS_RETURNED;
+ case UNSPECIFIED -> com.ecmsp.order.v1.OrderStatus.ORDER_STATUS_UNSPECIFIED;
};
}
@@ -84,19 +63,16 @@ public OrderStatus toOrderStatusDomain(com.ecmsp.order.v1.OrderStatus orderStatu
case ORDER_STATUS_PAID -> PAID;
case ORDER_STATUS_FAILED -> FAILED;
case ORDER_STATUS_CANCELLED -> CANCELLED;
- default -> UNSPECIFIED;
+ case ORDER_STATUS_SHIPPED -> OrderStatus.SHIPPED;
+ case ORDER_STATUS_DELIVERED -> OrderStatus.DELIVERED;
+ case ORDER_STATUS_RETURN_REQUESTED -> OrderStatus.RETURN_REQUESTED;
+ case ORDER_STATUS_RETURN_PROCESSING -> OrderStatus.RETURN_PROCESSING;
+ case ORDER_STATUS_RETURNED -> OrderStatus.RETURNED;
+ case ORDER_STATUS_UNSPECIFIED, UNRECOGNIZED -> UNSPECIFIED;
};
}
- public OrderToCreate toOrderToCreate(CreateOrderRequest request) {
- return new OrderToCreate(
- new ClientId(UUID.fromString(request.getClientId())),
- request.getItemsList().stream()
- .map(this::toOrderItem)
- .toList()
- );
- }
public GetOrderStatusResponse toGetOrderStatusResponse(Order order) {
return GetOrderStatusResponse.newBuilder()
@@ -115,22 +91,19 @@ public GetOrderItemsResponse toGetOrderItemsResponse(Order order) {
.build();
}
- //TODO: COMPATITLE WITH SCHEMA DEFINITIONS BUT NOT THIS SERVICE DOMAIN
private OrderItemDetails toOrderItemDetails(OrderItem item) {
return OrderItemDetails.newBuilder()
.setItemId(item.itemId().toString())
+ .setVariantId(item.variantId().toString())
.setQuantity(item.quantity())
+ .setPrice(item.price().doubleValue())
+ .setIsReturnable(item.isReturnable())
+ .setImageUrl(item.imageUrl())
+ .setVariantId(item.variantId().toString())
.build();
- }
- private OrderItem toOrderItem(com.ecmsp.order.v1.OrderItem item) {
- return new OrderItem(
- new ItemId(UUID.fromString(item.getItemId())),
- item.getQuantity(),
- BigDecimal.valueOf(item.getPrice()),
- //TODO: variant_id should be added
- true // Default to returnable for items created via gRPC
- );
+
}
+
}
\ No newline at end of file
diff --git a/src/main/java/com/ecmsp/orderservice/api/grpc/OrderGrpcService.java b/src/main/java/com/ecmsp/orderservice/api/grpc/OrderGrpcService.java
index 3d86c23..dcd8c67 100644
--- a/src/main/java/com/ecmsp/orderservice/api/grpc/OrderGrpcService.java
+++ b/src/main/java/com/ecmsp/orderservice/api/grpc/OrderGrpcService.java
@@ -4,8 +4,10 @@
import com.ecmsp.order.v1.OrderServiceGrpc;
import com.ecmsp.orderservice.application.security.grpc.UserContextGrpcHolder;
import com.ecmsp.orderservice.application.security.UserContextData;
-import com.ecmsp.orderservice.order.domain.*;
-import com.ecmsp.orderservice.order.domain.OrderStatus;
+import com.ecmsp.orderservice.order.domain.ClientId;
+import com.ecmsp.orderservice.order.domain.OrderFacade;
+import com.ecmsp.orderservice.order.domain.OrderId;
+import com.ecmsp.orderservice.order.domain.Order;
import io.grpc.Status;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;
@@ -107,83 +109,4 @@ public void listOrdersByUserId(ListOrdersByUserIdRequest request, StreamObserver
}
}
- //? NOT SURE IF THIS IS NEEDED
- @Override
- public void updateOrder(UpdateOrderRequest request, StreamObserver responseObserver) {
- try {
- UUID orderId = UUID.fromString(request.getOrderId());
- Optional order = orderFacade.findOrderById(new OrderId(orderId));
-
- if (order.isEmpty()) {
- responseObserver.onError(Status.NOT_FOUND.withDescription("Order not found").asRuntimeException());
- return;
- }
-
- // Auto-advance order status based on current status
- OrderStatus currentStatus = order.get().orderStatus();
- OrderStatus nextStatus = switch (currentStatus) {
- case PENDING -> OrderStatus.PROCESSING;
- case PROCESSING -> OrderStatus.PAID;
-// case PAID -> OrderStatus.COMPLETED; COMPLETED status does not exist in our domain
- default -> currentStatus;
- };
-
- OrderToUpdate orderToUpdate = new OrderToUpdate(new OrderId(orderId), nextStatus);
- Order updatedOrder = orderFacade.updateOrder(orderToUpdate);
- UpdateOrderResponse response = orderGrpcMapper.toUpdateOrderResponse(updatedOrder);
- responseObserver.onNext(response);
- responseObserver.onCompleted();
- } catch (OrderException e) {
- responseObserver.onError(Status.NOT_FOUND.withDescription(e.getMessage()).asRuntimeException());
- } catch (Exception e){
- responseObserver.onError(Status.INTERNAL.withDescription(e.getMessage()).asRuntimeException());
- }
-
- }
-
-
-
- /**
- * @deprecated This method is deprecated and will be removed in a future version.
- * Order creation should be done through the REST API or other designated endpoints.
- */
- @Deprecated
- @Override
- public void createOrder(CreateOrderRequest request, StreamObserver responseObserver) {
- try {
- OrderToCreate orderToCreate = orderGrpcMapper.toOrderToCreate(request);
- Order createdOrder = orderFacade.createOrder(orderToCreate, null);
- CreateOrderResponse response = orderGrpcMapper.toCreateOrderResponse(createdOrder);
- responseObserver.onNext(response);
- responseObserver.onCompleted();
- } catch (Exception e) {
- responseObserver.onError(Status.INTERNAL.withDescription(e.getMessage()).asRuntimeException());
- }
- }
-
-
-
-
- /**
- * @deprecated This method is deprecated and will be removed in a future version.
- * Order deletion should be done through the REST API or other designated endpoints.
- */
- @Deprecated
- @Override
- public void deleteOrder(DeleteOrderRequest request, StreamObserver responseObserver) {
- try {
- UUID orderId = UUID.fromString(request.getOrderId());
- orderFacade.deleteOrder(new OrderId(orderId));
- DeleteOrderResponse response = DeleteOrderResponse.newBuilder()
- .setSuccess(true)
- .build();
- responseObserver.onNext(response);
- responseObserver.onCompleted();
- } catch (Exception e) {
- responseObserver.onError(Status.INTERNAL.withDescription(e.getMessage()).asRuntimeException());
- }
- }
-
-
-
}
diff --git a/src/main/java/com/ecmsp/orderservice/api/grpc/ReturnGrpcMapper.java b/src/main/java/com/ecmsp/orderservice/api/grpc/ReturnGrpcMapper.java
index 3544d6b..769f934 100644
--- a/src/main/java/com/ecmsp/orderservice/api/grpc/ReturnGrpcMapper.java
+++ b/src/main/java/com/ecmsp/orderservice/api/grpc/ReturnGrpcMapper.java
@@ -5,6 +5,7 @@
import com.ecmsp.orderservice.order.domain.returns.ItemToReturnDetails;
import com.ecmsp.orderservice.order.domain.returns.ReturnOrder;
import com.ecmsp.orderservice.order.domain.returns.ReturnToCreate;
+import com.ecmsp.orderservice.order.domain.returns.ReturnStatus;
import org.springframework.stereotype.Component;
import java.util.List;
@@ -67,7 +68,7 @@ private ItemReturnDetails toItemReturnDetails(ItemToReturnDetails dto) {
}
- private com.ecmsp.order.v1.returns.v1.ReturnStatus toReturnStatusProto(com.ecmsp.orderservice.order.domain.returns.ReturnStatus status) {
+ private com.ecmsp.order.v1.returns.v1.ReturnStatus toReturnStatusProto(ReturnStatus status) {
return switch (status) {
case REQUESTED -> com.ecmsp.order.v1.returns.v1.ReturnStatus.RETURN_STATUS_REQUESTED;
case PROCESSING -> com.ecmsp.order.v1.returns.v1.ReturnStatus.RETURN_STATUS_PROCESSING;
diff --git a/src/main/java/com/ecmsp/orderservice/api/kafka/CartCreatedEvent.java b/src/main/java/com/ecmsp/orderservice/api/kafka/CartCreatedEvent.java
index 43b5d98..7354f6f 100644
--- a/src/main/java/com/ecmsp/orderservice/api/kafka/CartCreatedEvent.java
+++ b/src/main/java/com/ecmsp/orderservice/api/kafka/CartCreatedEvent.java
@@ -1,9 +1,6 @@
package com.ecmsp.orderservice.api.kafka;
-import com.ecmsp.orderservice.order.domain.ClientId;
-import com.ecmsp.orderservice.order.domain.ItemId;
-import com.ecmsp.orderservice.order.domain.OrderItem;
-import com.ecmsp.orderservice.order.domain.OrderToCreate;
+import com.ecmsp.orderservice.order.domain.*;
import java.math.BigDecimal;
import java.util.List;
@@ -17,9 +14,11 @@ public record CartCreatedEvent(
public record CartItem(
String itemId,
+ String variantId,
String name,
BigDecimal price,
int quantity,
+ String imageUrl,
String description,
boolean isReturnable
) {
@@ -27,12 +26,16 @@ public record CartItem(
public static OrderToCreate toOrder(CartCreatedEvent cartEvent) {
return new OrderToCreate(
+ /* reservationId */ null,
/* clientId */ new ClientId(UUID.fromString(cartEvent.clientId())),
/* items */ cartEvent.items().stream()
.map(cartItem -> new OrderItem(
new ItemId(UUID.fromString(cartItem.itemId())),
+ new VariantId(UUID.fromString(cartItem.variantId())),
cartItem.quantity(),
cartItem.price(),
+ cartItem.imageUrl(),
+ cartItem.description(),
cartItem.isReturnable()
)).toList()
);
diff --git a/src/main/java/com/ecmsp/orderservice/api/rest/order/OrderControllerMapper.java b/src/main/java/com/ecmsp/orderservice/api/rest/order/OrderControllerMapper.java
index 2adb799..4a1743b 100644
--- a/src/main/java/com/ecmsp/orderservice/api/rest/order/OrderControllerMapper.java
+++ b/src/main/java/com/ecmsp/orderservice/api/rest/order/OrderControllerMapper.java
@@ -32,7 +32,7 @@ OrderReturnabilityResponse toOrderReturnabilityResponse(Order order) {
.map(item -> new OrderReturnabilityResponse.ReturnableItemDto(
/* itemId = */ item.itemId().value(),
/* quantity = */ item.quantity(),
- /* priceAtTimeOfOrder = */ item.priceAtTimeOfOrder()
+ /* price = */ item.price()
))
.toList()
);
diff --git a/src/main/java/com/ecmsp/orderservice/api/rest/order/OrdersController.java b/src/main/java/com/ecmsp/orderservice/api/rest/order/OrdersController.java
index 6cebde7..2b4f4ea 100644
--- a/src/main/java/com/ecmsp/orderservice/api/rest/order/OrdersController.java
+++ b/src/main/java/com/ecmsp/orderservice/api/rest/order/OrdersController.java
@@ -64,6 +64,7 @@ public ResponseEntity getOrderById(@PathVariable UUID orde
public ResponseEntity createOrder(@RequestBody CreateOrderRequest request, @RequestHeader(value = "X-Correlation-Id", required = false) String correlationId) {
Context context = new Context(new CorrelationId(UUID.fromString(correlationId)));
OrderToCreate orderToCreate = new OrderToCreate(
+ null,
/* clientId = */ new ClientId(request.clientId()),
/* items = */ request.items().stream()
.map(CreateOrderRequest.Item::toOrderItem)
diff --git a/src/main/java/com/ecmsp/orderservice/api/rest/order/dto/CreateOrderRequest.java b/src/main/java/com/ecmsp/orderservice/api/rest/order/dto/CreateOrderRequest.java
index fdaff15..22f2b32 100644
--- a/src/main/java/com/ecmsp/orderservice/api/rest/order/dto/CreateOrderRequest.java
+++ b/src/main/java/com/ecmsp/orderservice/api/rest/order/dto/CreateOrderRequest.java
@@ -2,12 +2,14 @@
import com.ecmsp.orderservice.order.domain.ItemId;
import com.ecmsp.orderservice.order.domain.OrderItem;
+import com.ecmsp.orderservice.order.domain.VariantId;
import java.math.BigDecimal;
import java.util.List;
import java.util.UUID;
public record CreateOrderRequest(
+ UUID variantId,
UUID clientId,
List- items
) {
@@ -15,12 +17,23 @@ public record CreateOrderRequest(
public record Item(
UUID itemId,
+ UUID variantId,
int quantity,
double price,
+ String imageUrl,
+ String description,
boolean returnable
) {
public OrderItem toOrderItem() {
- return new OrderItem(new ItemId(itemId), quantity, BigDecimal.valueOf(price), returnable);
+ return new OrderItem(
+ new ItemId(itemId),
+ new VariantId(variantId),
+ quantity,
+ BigDecimal.valueOf(price),
+ imageUrl,
+ description,
+ returnable
+ );
}
}
diff --git a/src/main/java/com/ecmsp/orderservice/order/adapter/repository/db/OrderEntity.java b/src/main/java/com/ecmsp/orderservice/order/adapter/repository/db/OrderEntity.java
index 25d1261..7a0f54d 100644
--- a/src/main/java/com/ecmsp/orderservice/order/adapter/repository/db/OrderEntity.java
+++ b/src/main/java/com/ecmsp/orderservice/order/adapter/repository/db/OrderEntity.java
@@ -30,6 +30,9 @@ class OrderEntity {
@Column(name = "client_id", nullable = false)
private UUID clientId;
+ @Column(name = "reservation_id")
+ private UUID reservationId;
+
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
private List items;
@@ -74,6 +77,14 @@ public void setItems(List items) {
this.items = items;
}
+ public UUID getReservationId() {
+ return reservationId;
+ }
+
+ public void setReservationId(UUID reservationId) {
+ this.reservationId = reservationId;
+ }
+
@Override
public String toString() {
return "Order{" +
@@ -81,6 +92,7 @@ public String toString() {
", orderStatus=" + orderStatus +
", date=" + date +
", clientId=" + clientId +
+ ", reservationId=" + reservationId +
'}';
}
}
diff --git a/src/main/java/com/ecmsp/orderservice/order/adapter/repository/db/OrderEntityMapper.java b/src/main/java/com/ecmsp/orderservice/order/adapter/repository/db/OrderEntityMapper.java
index 4328377..b61f817 100644
--- a/src/main/java/com/ecmsp/orderservice/order/adapter/repository/db/OrderEntityMapper.java
+++ b/src/main/java/com/ecmsp/orderservice/order/adapter/repository/db/OrderEntityMapper.java
@@ -9,11 +9,20 @@ class OrderEntityMapper {
public Order toOrder(OrderEntity orderEntity) {
return new Order(
/* orderId = */ new OrderId(orderEntity.getOrderId()),
+ /* reservationId = */ orderEntity.getReservationId() != null ? new ReservationId(orderEntity.getReservationId()) : null,
/* clientId = */ new ClientId(orderEntity.getClientId()),
/* orderStatus = */ orderEntity.getOrderStatus(),
/* date = */ orderEntity.getDate(),
/* items = */ orderEntity.getItems().stream()
- .map(item -> new OrderItem( new ItemId(item.getItemId()), item.getQuantity(), item.getPrice(), item.getIsReturnable()))
+ .map(item -> new OrderItem(
+ new ItemId(item.getItemId()),
+ item.getVariantId() != null ? new VariantId(item.getVariantId()) : null,
+ item.getQuantity(),
+ item.getPrice(),
+ item.getImageUrl(),
+ item.getDescription(),
+ item.getIsReturnable()
+ ))
.toList()
);
}
@@ -21,6 +30,7 @@ public Order toOrder(OrderEntity orderEntity) {
public OrderEntity toOrderEntity(Order order) {
OrderEntity orderEntity = OrderEntity.builder()
.orderId(order.orderId().value())
+ .reservationId(order.reservationId() != null ? order.reservationId().value() : null)
.clientId(order.clientId().value())
.orderStatus(order.orderStatus())
.date(order.date())
@@ -29,8 +39,12 @@ public OrderEntity toOrderEntity(Order order) {
List itemEntities = order.items().stream()
.map(item -> OrderItemEntity.builder()
.itemId(item.itemId().value())
+ .variantId(item.variantId() != null ? item.variantId().value() : null)
.quantity(item.quantity())
- .price(item.priceAtTimeOfOrder())
+ .price(item.price())
+ .imageUrl(item.imageUrl())
+ .description(item.description())
+ .isReturnable(item.isReturnable())
.build())
.toList();
diff --git a/src/main/java/com/ecmsp/orderservice/order/adapter/repository/db/OrderItemEntity.java b/src/main/java/com/ecmsp/orderservice/order/adapter/repository/db/OrderItemEntity.java
index e6beaf2..9119173 100644
--- a/src/main/java/com/ecmsp/orderservice/order/adapter/repository/db/OrderItemEntity.java
+++ b/src/main/java/com/ecmsp/orderservice/order/adapter/repository/db/OrderItemEntity.java
@@ -22,6 +22,9 @@ class OrderItemEntity {
@Column(name = "item_id")
private UUID itemId;
+ @Column(name = "variant_id")
+ private UUID variantId;
+
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id", nullable = false)
private OrderEntity order;
@@ -32,6 +35,11 @@ class OrderItemEntity {
@Column(name = "price", nullable = false)
private BigDecimal price;
+ @Column(name = "image_url")
+ private String imageUrl;
+
+ @Column(name = "description")
+ private String description;
@Column(name = "is_returnable", nullable = false)
private Boolean isReturnable;
@@ -78,4 +86,28 @@ public void setIsReturnable(Boolean isReturnable) {
this.isReturnable = isReturnable;
}
+ public UUID getVariantId() {
+ return variantId;
+ }
+
+ public void setVariantId(UUID variantId) {
+ this.variantId = variantId;
+ }
+
+ public String getImageUrl() {
+ return imageUrl;
+ }
+
+ public void setImageUrl(String imageUrl) {
+ this.imageUrl = imageUrl;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
}
diff --git a/src/main/java/com/ecmsp/orderservice/order/domain/DefaultOrderFacade.java b/src/main/java/com/ecmsp/orderservice/order/domain/DefaultOrderFacade.java
index a99f481..d25cdf2 100644
--- a/src/main/java/com/ecmsp/orderservice/order/domain/DefaultOrderFacade.java
+++ b/src/main/java/com/ecmsp/orderservice/order/domain/DefaultOrderFacade.java
@@ -43,6 +43,7 @@ public Order createOrder(OrderToCreate orderToCreate, Context context) {
Order order = new Order(
/* orderId */ orderIdGenerator.generate(context.correlationId()),
+ orderToCreate.reservationId(),
/* clientId */ orderToCreate.clientId(),
/* orderStatus */ OrderStatus.PENDING, // Assuming default status is PENDING
/* date */ LocalDateTime.now(clock),
@@ -69,6 +70,7 @@ public Order updateOrder(OrderToUpdate orderToUpdate) {
Order updatedOrder = new Order(
currentOrder.orderId(),
+ currentOrder.reservationId(),
currentOrder.clientId(),
orderToUpdate.newStatus(),
currentOrder.date(),
diff --git a/src/main/java/com/ecmsp/orderservice/order/domain/Order.java b/src/main/java/com/ecmsp/orderservice/order/domain/Order.java
index c8b6c67..fb3f08d 100644
--- a/src/main/java/com/ecmsp/orderservice/order/domain/Order.java
+++ b/src/main/java/com/ecmsp/orderservice/order/domain/Order.java
@@ -7,14 +7,19 @@
public record Order(
OrderId orderId,
+ ReservationId reservationId,
ClientId clientId,
OrderStatus orderStatus,
LocalDateTime date,
List items
) {
+
+ private static final int RETURNABLE_PERIOD_DAYS = 14;
+
+
public BigDecimal totalPrice() {
return items.stream()
- .map(OrderItem::priceAtTimeOfOrder)
+ .map(OrderItem::price)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
@@ -23,7 +28,7 @@ public boolean isReturnable() {
}
public boolean isWithinReturnPeriod() {
- return ChronoUnit.DAYS.between(date, LocalDateTime.now()) <= 14;
+ return ChronoUnit.DAYS.between(date, LocalDateTime.now()) <= RETURNABLE_PERIOD_DAYS;
}
public boolean hasReturnableItems() {
diff --git a/src/main/java/com/ecmsp/orderservice/order/domain/OrderItem.java b/src/main/java/com/ecmsp/orderservice/order/domain/OrderItem.java
index 86c3794..b6458d2 100644
--- a/src/main/java/com/ecmsp/orderservice/order/domain/OrderItem.java
+++ b/src/main/java/com/ecmsp/orderservice/order/domain/OrderItem.java
@@ -4,9 +4,11 @@
public record OrderItem(
ItemId itemId,
- //TODO: we need to add variantId and make a call to product service: getVariantDetails(variantId) to get: price, image_url, description to present it to user or just keep all these fields in db and don't make another call after order is finalized -> I think 2nd option is better for performance
+ VariantId variantId,
int quantity,
- BigDecimal priceAtTimeOfOrder,
+ BigDecimal price,
+ String imageUrl,
+ String description,
boolean isReturnable
) {
}
diff --git a/src/main/java/com/ecmsp/orderservice/order/domain/OrderStatus.java b/src/main/java/com/ecmsp/orderservice/order/domain/OrderStatus.java
index 9575802..59b34ab 100644
--- a/src/main/java/com/ecmsp/orderservice/order/domain/OrderStatus.java
+++ b/src/main/java/com/ecmsp/orderservice/order/domain/OrderStatus.java
@@ -1,10 +1,19 @@
package com.ecmsp.orderservice.order.domain;
public enum OrderStatus {
+ UNSPECIFIED,
PENDING,
PROCESSING,
PAID,
FAILED,
CANCELLED,
- UNSPECIFIED // Added to match the protobuf enum
+
+ // Post-fulfillment states
+ SHIPPED,
+ DELIVERED,
+
+ // Return states
+ RETURN_REQUESTED,
+ RETURN_PROCESSING,
+ RETURNED
}
\ No newline at end of file
diff --git a/src/main/java/com/ecmsp/orderservice/order/domain/OrderToCreate.java b/src/main/java/com/ecmsp/orderservice/order/domain/OrderToCreate.java
index 787cee0..57fcb09 100644
--- a/src/main/java/com/ecmsp/orderservice/order/domain/OrderToCreate.java
+++ b/src/main/java/com/ecmsp/orderservice/order/domain/OrderToCreate.java
@@ -2,7 +2,9 @@
import java.util.List;
+
public record OrderToCreate(
+ ReservationId reservationId,
ClientId clientId,
List items
) {
diff --git a/src/main/java/com/ecmsp/orderservice/order/domain/ReservationId.java b/src/main/java/com/ecmsp/orderservice/order/domain/ReservationId.java
new file mode 100644
index 0000000..5880b51
--- /dev/null
+++ b/src/main/java/com/ecmsp/orderservice/order/domain/ReservationId.java
@@ -0,0 +1,12 @@
+package com.ecmsp.orderservice.order.domain;
+
+import java.util.UUID;
+
+public record ReservationId(UUID value) {
+
+ @Override
+ public String toString(){
+ return value.toString();
+ }
+
+}
diff --git a/src/main/resources/db/migration/V1__Create_initial_schema.sql b/src/main/resources/db/migration/V1__Create_initial_schema.sql
index 608f6aa..0face51 100644
--- a/src/main/resources/db/migration/V1__Create_initial_schema.sql
+++ b/src/main/resources/db/migration/V1__Create_initial_schema.sql
@@ -1,21 +1,24 @@
CREATE TABLE orders
(
- order_id UUID PRIMARY KEY,
- order_status VARCHAR(30) NOT NULL,
- date TIMESTAMP NOT NULL,
- client_id UUID NOT NULL
+ order_id UUID PRIMARY KEY,
+ reservation_id UUID,
+ order_status VARCHAR(30) NOT NULL,
+ date TIMESTAMP NOT NULL,
+ client_id UUID NOT NULL
);
CREATE TABLE order_item
(
order_item_id UUID PRIMARY KEY,
- item_id UUID NOT NULL,
+ item_id UUID NOT NULL,
+ variant_id UUID,
order_id UUID NOT NULL,
item_name VARCHAR NOT NULL,
quantity INTEGER NOT NULL,
price DECIMAL NOT NULL,
+ image_url VARCHAR,
description VARCHAR,
is_returnable BOOLEAN NOT NULL,
CONSTRAINT fk_order_item_order
diff --git a/src/main/resources/db/migration/V2__Insert_example_data.sql b/src/main/resources/db/migration/V2__Insert_example_data.sql
index ab0f49f..1b07ea4 100644
--- a/src/main/resources/db/migration/V2__Insert_example_data.sql
+++ b/src/main/resources/db/migration/V2__Insert_example_data.sql
@@ -2,52 +2,52 @@
-- Flyway migration to insert example data into orders and order_item tables
-- Insert example orders
-INSERT INTO orders (order_id, order_status, date, client_id) VALUES
- ('550e8400-e29b-41d4-a716-446655440001', 'PENDING', '2024-01-15 10:30:00', '123e4567-e89b-12d3-a456-426614174001'),
- ('550e8400-e29b-41d4-a716-446655440002', 'PAID', '2024-01-16 14:22:15', '123e4567-e89b-12d3-a456-426614174002'),
- ('550e8400-e29b-41d4-a716-446655440003', 'PROCESSING', '2024-01-17 09:45:30', '123e4567-e89b-12d3-a456-426614174001'),
- ('550e8400-e29b-41d4-a716-446655440004', 'CANCELLED', '2024-01-18 16:10:45', '123e4567-e89b-12d3-a456-426614174003'),
- ('550e8400-e29b-41d4-a716-446655440005', 'PROCESSING', '2024-01-19 11:20:00', '123e4567-e89b-12d3-a456-426614174002'),
- ('550e8400-e29b-41d4-a716-446655440006', 'PAID', '2024-01-20 13:55:12', '123e4567-e89b-12d3-a456-426614174004'),
- ('550e8400-e29b-41d4-a716-446655440007', 'PENDING', '2024-01-21 08:15:30', '123e4567-e89b-12d3-a456-426614174001'),
- ('550e8400-e29b-41d4-a716-446655440008', 'FAILED', '2024-01-22 15:40:25', '123e4567-e89b-12d3-a456-426614174005'),
+INSERT INTO orders (order_id, reservation_id, order_status, date, client_id) VALUES
+ ('550e8400-e29b-41d4-a716-446655440001', 'c1d2e3f4-a5b6-7890-cdef-012345678901', 'PENDING', '2024-01-15 10:30:00', '123e4567-e89b-12d3-a456-426614174001'),
+ ('550e8400-e29b-41d4-a716-446655440002', 'c1d2e3f4-a5b6-7890-cdef-012345678902', 'PAID', '2024-01-16 14:22:15', '123e4567-e89b-12d3-a456-426614174002'),
+ ('550e8400-e29b-41d4-a716-446655440003', 'c1d2e3f4-a5b6-7890-cdef-012345678903', 'PROCESSING', '2024-01-17 09:45:30', '123e4567-e89b-12d3-a456-426614174001'),
+ ('550e8400-e29b-41d4-a716-446655440004', 'c1d2e3f4-a5b6-7890-cdef-012345678904', 'CANCELLED', '2024-01-18 16:10:45', '123e4567-e89b-12d3-a456-426614174003'),
+ ('550e8400-e29b-41d4-a716-446655440005', 'c1d2e3f4-a5b6-7890-cdef-012345678905', 'PROCESSING', '2024-01-19 11:20:00', '123e4567-e89b-12d3-a456-426614174002'),
+ ('550e8400-e29b-41d4-a716-446655440006', 'c1d2e3f4-a5b6-7890-cdef-012345678906', 'PAID', '2024-01-20 13:55:12', '123e4567-e89b-12d3-a456-426614174004'),
+ ('550e8400-e29b-41d4-a716-446655440007', 'c1d2e3f4-a5b6-7890-cdef-012345678907', 'PENDING', '2024-01-21 08:15:30', '123e4567-e89b-12d3-a456-426614174001'),
+ ('550e8400-e29b-41d4-a716-446655440008', 'c1d2e3f4-a5b6-7890-cdef-012345678908', 'FAILED', '2024-01-22 15:40:25', '123e4567-e89b-12d3-a456-426614174005'),
--second order for the first client for testing
- ('550e8400-e29b-41d4-a716-446655440009', 'PENDING', '2024-01-15 11:30:00', '123e4567-e89b-12d3-a456-426614174001');
+ ('550e8400-e29b-41d4-a716-446655440009', 'c1d2e3f4-a5b6-7890-cdef-012345678909', 'PENDING', '2024-01-15 11:30:00', '123e4567-e89b-12d3-a456-426614174001');
-- Insert example order items
-INSERT INTO order_item (order_item_id, item_id, order_id, item_name, quantity, price, description, is_returnable) VALUES
+INSERT INTO order_item (order_item_id, item_id, variant_id, order_id, item_name, quantity, price, image_url, description, is_returnable) VALUES
-- Order 1 items
- ('f1b2c3d4-e5f6-7890-abcd-ef1234567890', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', '550e8400-e29b-41d4-a716-446655440001', 'Wireless Headphones', 2, 129.99, 'Premium noise-cancelling wireless headphones', true),
- ('f1b2c3d4-e5f6-7890-abcd-ef1234567891', 'a1b2c3d4-e5f6-7890-abcd-ef1234567891', '550e8400-e29b-41d4-a716-446655440001', 'Phone Case', 1, 24.99, 'Protective silicone phone case', true),
+ ('f1b2c3d4-e5f6-7890-abcd-ef1234567890', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', 'b1b2c3d4-e5f6-7890-abcd-ef1234567890', '550e8400-e29b-41d4-a716-446655440001', 'Wireless Headphones', 2, 129.99, 'https://example.com/images/headphones.jpg', 'Premium noise-cancelling wireless headphones', true),
+ ('f1b2c3d4-e5f6-7890-abcd-ef1234567891', 'a1b2c3d4-e5f6-7890-abcd-ef1234567891', 'b1b2c3d4-e5f6-7890-abcd-ef1234567891', '550e8400-e29b-41d4-a716-446655440001', 'Phone Case', 1, 24.99, 'https://example.com/images/phone-case.jpg', 'Protective silicone phone case', true),
-- Order 2 items
- ('f1b2c3d4-e5f6-7890-abcd-ef1234567892', 'a1b2c3d4-e5f6-7890-abcd-ef1234567892', '550e8400-e29b-41d4-a716-446655440002', 'Laptop Stand', 1, 89.99, 'Adjustable aluminum laptop stand', true),
- ('f1b2c3d4-e5f6-7890-abcd-ef1234567893', 'a1b2c3d4-e5f6-7890-abcd-ef1234567893', '550e8400-e29b-41d4-a716-446655440002', 'USB-C Cable', 3, 19.99, '6ft USB-C charging cable', false),
- ('f1b2c3d4-e5f6-7890-abcd-ef1234567894', 'a1b2c3d4-e5f6-7890-abcd-ef1234567894', '550e8400-e29b-41d4-a716-446655440002', 'Wireless Mouse', 1, 45.99, 'Ergonomic wireless optical mouse', true),
+ ('f1b2c3d4-e5f6-7890-abcd-ef1234567892', 'a1b2c3d4-e5f6-7890-abcd-ef1234567892', 'b1b2c3d4-e5f6-7890-abcd-ef1234567892', '550e8400-e29b-41d4-a716-446655440002', 'Laptop Stand', 1, 89.99, 'https://example.com/images/laptop-stand.jpg', 'Adjustable aluminum laptop stand', true),
+ ('f1b2c3d4-e5f6-7890-abcd-ef1234567893', 'a1b2c3d4-e5f6-7890-abcd-ef1234567893', 'b1b2c3d4-e5f6-7890-abcd-ef1234567893', '550e8400-e29b-41d4-a716-446655440002', 'USB-C Cable', 3, 19.99, 'https://example.com/images/usb-cable.jpg', '6ft USB-C charging cable', false),
+ ('f1b2c3d4-e5f6-7890-abcd-ef1234567894', 'a1b2c3d4-e5f6-7890-abcd-ef1234567894', 'b1b2c3d4-e5f6-7890-abcd-ef1234567894', '550e8400-e29b-41d4-a716-446655440002', 'Wireless Mouse', 1, 45.99, 'https://example.com/images/mouse.jpg', 'Ergonomic wireless optical mouse', true),
-- Order 3 items
- ('f1b2c3d4-e5f6-7890-abcd-ef1234567895', 'a1b2c3d4-e5f6-7890-abcd-ef1234567895', '550e8400-e29b-41d4-a716-446655440003', 'Monitor', 1, 299.99, '27-inch 4K LED monitor', true),
- ('f1b2c3d4-e5f6-7890-abcd-ef1234567896', 'a1b2c3d4-e5f6-7890-abcd-ef1234567896', '550e8400-e29b-41d4-a716-446655440003', 'Keyboard', 1, 79.99, 'Mechanical gaming keyboard', true),
+ ('f1b2c3d4-e5f6-7890-abcd-ef1234567895', 'a1b2c3d4-e5f6-7890-abcd-ef1234567895', 'b1b2c3d4-e5f6-7890-abcd-ef1234567895', '550e8400-e29b-41d4-a716-446655440003', 'Monitor', 1, 299.99, 'https://example.com/images/monitor.jpg', '27-inch 4K LED monitor', true),
+ ('f1b2c3d4-e5f6-7890-abcd-ef1234567896', 'a1b2c3d4-e5f6-7890-abcd-ef1234567896', 'b1b2c3d4-e5f6-7890-abcd-ef1234567896', '550e8400-e29b-41d4-a716-446655440003', 'Keyboard', 1, 79.99, 'https://example.com/images/keyboard.jpg', 'Mechanical gaming keyboard', true),
-- Order 4 items (cancelled order)
- ('f1b2c3d4-e5f6-7890-abcd-ef1234567897', 'a1b2c3d4-e5f6-7890-abcd-ef1234567897', '550e8400-e29b-41d4-a716-446655440004', 'Webcam', 1, 149.99, 'HD webcam with auto-focus', true),
+ ('f1b2c3d4-e5f6-7890-abcd-ef1234567897', 'a1b2c3d4-e5f6-7890-abcd-ef1234567897', 'b1b2c3d4-e5f6-7890-abcd-ef1234567897', '550e8400-e29b-41d4-a716-446655440004', 'Webcam', 1, 149.99, 'https://example.com/images/webcam.jpg', 'HD webcam with auto-focus', true),
-- Order 5 items
- ('f1b2c3d4-e5f6-7890-abcd-ef1234567898', 'a1b2c3d4-e5f6-7890-abcd-ef1234567898', '550e8400-e29b-41d4-a716-446655440005', 'Tablet', 1, 399.99, '10-inch Android tablet', true),
- ('f1b2c3d4-e5f6-7890-abcd-ef1234567899', 'a1b2c3d4-e5f6-7890-abcd-ef1234567899', '550e8400-e29b-41d4-a716-446655440005', 'Tablet Case', 1, 34.99, 'Leather tablet case with stand', true),
- ('f1b2c3d4-e5f6-7890-abcd-ef123456789a', 'a1b2c3d4-e5f6-7890-abcd-ef123456789a', '550e8400-e29b-41d4-a716-446655440005', 'Screen Protector', 2, 12.99, 'Tempered glass screen protector', false),
+ ('f1b2c3d4-e5f6-7890-abcd-ef1234567898', 'a1b2c3d4-e5f6-7890-abcd-ef1234567898', 'b1b2c3d4-e5f6-7890-abcd-ef1234567898', '550e8400-e29b-41d4-a716-446655440005', 'Tablet', 1, 399.99, 'https://example.com/images/tablet.jpg', '10-inch Android tablet', true),
+ ('f1b2c3d4-e5f6-7890-abcd-ef1234567899', 'a1b2c3d4-e5f6-7890-abcd-ef1234567899', 'b1b2c3d4-e5f6-7890-abcd-ef1234567899', '550e8400-e29b-41d4-a716-446655440005', 'Tablet Case', 1, 34.99, 'https://example.com/images/tablet-case.jpg', 'Leather tablet case with stand', true),
+ ('f1b2c3d4-e5f6-7890-abcd-ef123456789a', 'a1b2c3d4-e5f6-7890-abcd-ef123456789a', 'b1b2c3d4-e5f6-7890-abcd-ef123456789a', '550e8400-e29b-41d4-a716-446655440005', 'Screen Protector', 2, 12.99, 'https://example.com/images/screen-protector.jpg', 'Tempered glass screen protector', false),
-- Order 6 items
- ('f1b2c3d4-e5f6-7890-abcd-ef123456789b', 'a1b2c3d4-e5f6-7890-abcd-ef123456789b', '550e8400-e29b-41d4-a716-446655440006', 'Bluetooth Speaker', 1, 79.99, 'Portable waterproof Bluetooth speaker', true),
- ('f1b2c3d4-e5f6-7890-abcd-ef123456789c', 'a1b2c3d4-e5f6-7890-abcd-ef123456789c', '550e8400-e29b-41d4-a716-446655440006', 'Power Bank', 1, 49.99, '20000mAh portable power bank', true),
+ ('f1b2c3d4-e5f6-7890-abcd-ef123456789b', 'a1b2c3d4-e5f6-7890-abcd-ef123456789b', 'b1b2c3d4-e5f6-7890-abcd-ef123456789b', '550e8400-e29b-41d4-a716-446655440006', 'Bluetooth Speaker', 1, 79.99, 'https://example.com/images/speaker.jpg', 'Portable waterproof Bluetooth speaker', true),
+ ('f1b2c3d4-e5f6-7890-abcd-ef123456789c', 'a1b2c3d4-e5f6-7890-abcd-ef123456789c', 'b1b2c3d4-e5f6-7890-abcd-ef123456789c', '550e8400-e29b-41d4-a716-446655440006', 'Power Bank', 1, 49.99, 'https://example.com/images/power-bank.jpg', '20000mAh portable power bank', true),
-- Order 7 items
- ('f1b2c3d4-e5f6-7890-abcd-ef123456789d', 'a1b2c3d4-e5f6-7890-abcd-ef123456789d', '550e8400-e29b-41d4-a716-446655440007', 'Smart Watch', 1, 249.99, 'Fitness tracking smart watch', true),
- ('f1b2c3d4-e5f6-7890-abcd-ef123456789e', 'a1b2c3d4-e5f6-7890-abcd-ef123456789e', '550e8400-e29b-41d4-a716-446655440007', 'Watch Band', 2, 29.99, 'Silicone sport watch band', false),
+ ('f1b2c3d4-e5f6-7890-abcd-ef123456789d', 'a1b2c3d4-e5f6-7890-abcd-ef123456789d', 'b1b2c3d4-e5f6-7890-abcd-ef123456789d', '550e8400-e29b-41d4-a716-446655440007', 'Smart Watch', 1, 249.99, 'https://example.com/images/smartwatch.jpg', 'Fitness tracking smart watch', true),
+ ('f1b2c3d4-e5f6-7890-abcd-ef123456789e', 'a1b2c3d4-e5f6-7890-abcd-ef123456789e', 'b1b2c3d4-e5f6-7890-abcd-ef123456789e', '550e8400-e29b-41d4-a716-446655440007', 'Watch Band', 2, 29.99, 'https://example.com/images/watch-band.jpg', 'Silicone sport watch band', false),
-- Order 8 items
- ('f1b2c3d4-e5f6-7890-abcd-ef123456789f', 'a1b2c3d4-e5f6-7890-abcd-ef123456789f', '550e8400-e29b-41d4-a716-446655440008', 'Gaming Headset', 1, 159.99, 'Professional gaming headset with microphone', true),
- ('f1b2c3d4-e5f6-7890-abcd-ef1234567800', 'a1b2c3d4-e5f6-7890-abcd-ef1234567800', '550e8400-e29b-41d4-a716-446655440008', 'Mouse Pad', 1, 19.99, 'Large gaming mouse pad', true),
+ ('f1b2c3d4-e5f6-7890-abcd-ef123456789f', 'a1b2c3d4-e5f6-7890-abcd-ef123456789f', 'b1b2c3d4-e5f6-7890-abcd-ef123456789f', '550e8400-e29b-41d4-a716-446655440008', 'Gaming Headset', 1, 159.99, 'https://example.com/images/gaming-headset.jpg', 'Professional gaming headset with microphone', true),
+ ('f1b2c3d4-e5f6-7890-abcd-ef1234567800', 'a1b2c3d4-e5f6-7890-abcd-ef1234567800', 'b1b2c3d4-e5f6-7890-abcd-ef1234567800', '550e8400-e29b-41d4-a716-446655440008', 'Mouse Pad', 1, 19.99, 'https://example.com/images/mouse-pad.jpg', 'Large gaming mouse pad', true),
-- Order 9 items (second order for the first client) - testing
- ('f1b2c3d4-e5f6-7890-abcd-ef1234567801', 'a1b2c3d4-e5f6-7890-abcd-ef1234567801', '550e8400-e29b-41d4-a716-446655440009', 'Mouse Pad', 1, 19.99, 'Large gaming mouse pad', true);
+ ('f1b2c3d4-e5f6-7890-abcd-ef1234567801', 'a1b2c3d4-e5f6-7890-abcd-ef1234567801', 'b1b2c3d4-e5f6-7890-abcd-ef1234567801', '550e8400-e29b-41d4-a716-446655440009', 'Mouse Pad', 1, 19.99, 'https://example.com/images/mouse-pad.jpg', 'Large gaming mouse pad', true);
diff --git a/src/test/java/com/ecmsp/orderservice/grpc/OrderGrpcServiceTest.java b/src/test/java/com/ecmsp/orderservice/grpc/OrderGrpcServiceTest.java
index 7363acc..50d4b9a 100644
--- a/src/test/java/com/ecmsp/orderservice/grpc/OrderGrpcServiceTest.java
+++ b/src/test/java/com/ecmsp/orderservice/grpc/OrderGrpcServiceTest.java
@@ -3,8 +3,10 @@
import com.ecmsp.order.v1.*;
import com.ecmsp.orderservice.api.grpc.OrderGrpcMapper;
import com.ecmsp.orderservice.api.grpc.OrderGrpcService;
-import com.ecmsp.orderservice.order.domain.*;
-import com.ecmsp.orderservice.order.domain.OrderStatus;
+import com.ecmsp.orderservice.order.domain.ClientId;
+import com.ecmsp.orderservice.order.domain.OrderFacade;
+import com.ecmsp.orderservice.order.domain.OrderId;
+import com.ecmsp.orderservice.order.domain.Order;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import io.grpc.stub.StreamObserver;
@@ -45,7 +47,7 @@ class OrderGrpcServiceTest {
@BeforeEach
void setUp() {
- testOrder = new Order(ORDER_ID, CLIENT_ID, null, null, null);
+ testOrder = new Order(ORDER_ID, null, CLIENT_ID, null, null, null);
}
@Nested
@@ -134,194 +136,6 @@ void should_return_internal_error_when_exception_occurs() {
}
}
- @Nested
- @DisplayName("Create Order Tests")
- class CreateOrderTests {
-
- private final StreamObserver responseObserver = mock(StreamObserver.class);
-
- @Test
- @DisplayName("Should create order successfully")
- void should_create_order_successfully() {
- // given
- CreateOrderRequest request = CreateOrderRequest.newBuilder()
- .setClientId(CLIENT_UUID.toString())
- .build();
- OrderToCreate orderToCreate = new OrderToCreate(CLIENT_ID, List.of());
- CreateOrderResponse expectedResponse = CreateOrderResponse.newBuilder()
- .setOrderId(ORDER_UUID.toString())
- .build();
-
- when(orderGrpcMapper.toOrderToCreate(request)).thenReturn(orderToCreate);
- when(orderFacade.createOrder(orderToCreate, null)).thenReturn(testOrder);
- when(orderGrpcMapper.toCreateOrderResponse(testOrder)).thenReturn(expectedResponse);
-
- // when
- orderGrpcService.createOrder(request, responseObserver);
-
- // then
- verify(orderGrpcMapper).toOrderToCreate(request);
- verify(orderFacade).createOrder(orderToCreate, null);
- verify(orderGrpcMapper).toCreateOrderResponse(testOrder);
- verify(responseObserver).onNext(expectedResponse);
- verify(responseObserver).onCompleted();
- verify(responseObserver, never()).onError(any());
- }
-
- @Test
- @DisplayName("Should handle exception during order creation")
- void should_handle_exception_during_order_creation() {
- // given
- CreateOrderRequest request = CreateOrderRequest.newBuilder()
- .setClientId(CLIENT_UUID.toString())
- .build();
- OrderToCreate orderToCreate = new OrderToCreate(CLIENT_ID, List.of());
-
- when(orderGrpcMapper.toOrderToCreate(request)).thenReturn(orderToCreate);
- when(orderFacade.createOrder(orderToCreate, null)).thenThrow(new RuntimeException("Creation failed"));
-
- // when
- orderGrpcService.createOrder(request, responseObserver);
-
- // then
- verify(responseObserver).onError(any(StatusRuntimeException.class));
- verify(responseObserver, never()).onNext(any());
- verify(responseObserver, never()).onCompleted();
- }
- }
-
- @Nested
- @DisplayName("Update Order Tests")
- class UpdateOrderTests {
-
-
- private final StreamObserver responseObserver = mock(StreamObserver.class);
-
- @Test
- @DisplayName("Should auto-advance order from PENDING to PROCESSING")
- void should_auto_advance_order_from_pending_to_processing() {
- // given
- UpdateOrderRequest request = UpdateOrderRequest.newBuilder()
- .setOrderId(ORDER_UUID.toString())
- .build();
- Order pendingOrder = new Order(ORDER_ID, CLIENT_ID, OrderStatus.PENDING, null, null);
- Order processingOrder = new Order(ORDER_ID, CLIENT_ID, OrderStatus.PROCESSING, null, null);
- UpdateOrderResponse expectedResponse = UpdateOrderResponse.newBuilder()
- .setOrderId(ORDER_UUID.toString())
- .build();
-
- when(orderFacade.findOrderById(ORDER_ID)).thenReturn(Optional.of(pendingOrder));
- when(orderFacade.updateOrder(argThat(orderToUpdate ->
- orderToUpdate.orderId().equals(ORDER_ID) &&
- orderToUpdate.newStatus() == OrderStatus.PROCESSING
- ))).thenReturn(processingOrder);
- when(orderGrpcMapper.toUpdateOrderResponse(processingOrder)).thenReturn(expectedResponse);
-
- // when
- orderGrpcService.updateOrder(request, responseObserver);
-
- // then
- verify(orderFacade).findOrderById(ORDER_ID);
- verify(orderFacade).updateOrder(any(OrderToUpdate.class));
- verify(orderGrpcMapper).toUpdateOrderResponse(processingOrder);
- verify(responseObserver).onNext(expectedResponse);
- verify(responseObserver).onCompleted();
- verify(responseObserver, never()).onError(any());
- }
-
- @Test
- @DisplayName("Should return NOT_FOUND when order does not exist for update")
- void should_return_not_found_when_order_does_not_exist_for_update() {
- // given
- UpdateOrderRequest request = UpdateOrderRequest.newBuilder()
- .setOrderId(ORDER_UUID.toString())
- .build();
-
- when(orderFacade.findOrderById(ORDER_ID)).thenReturn(Optional.empty());
-
- // when
- orderGrpcService.updateOrder(request, responseObserver);
-
- // then
- ArgumentCaptor exceptionCaptor = ArgumentCaptor.forClass(StatusRuntimeException.class);
- verify(responseObserver).onError(exceptionCaptor.capture());
-
- StatusRuntimeException exception = exceptionCaptor.getValue();
- assertThat(exception.getStatus().getCode()).isEqualTo(Status.NOT_FOUND.getCode());
-
- verify(responseObserver, never()).onNext(any());
- verify(responseObserver, never()).onCompleted();
- }
-
- @Test
- @DisplayName("Should return INTERNAL error when update fails with unexpected exception")
- void should_return_internal_error_when_update_fails() {
- // given
- UpdateOrderRequest request = UpdateOrderRequest.newBuilder()
- .setOrderId(ORDER_UUID.toString())
- .build();
-
- when(orderFacade.findOrderById(ORDER_ID)).thenThrow(new RuntimeException("Unexpected error"));
-
- // when
- orderGrpcService.updateOrder(request, responseObserver);
-
- // then
- ArgumentCaptor exceptionCaptor = ArgumentCaptor.forClass(StatusRuntimeException.class);
- verify(responseObserver).onError(exceptionCaptor.capture());
-
- StatusRuntimeException exception = exceptionCaptor.getValue();
- assertThat(exception.getStatus().getCode()).isEqualTo(Status.INTERNAL.getCode());
-
- verify(responseObserver, never()).onNext(any());
- verify(responseObserver, never()).onCompleted();
- }
- }
-
- @Nested
- @DisplayName("Delete Order Tests")
- class DeleteOrderTests {
-
-
- private final StreamObserver responseObserver = mock(StreamObserver.class);
-
- @Test
- @DisplayName("Should delete order successfully")
- void should_delete_order_successfully() {
- // given
- DeleteOrderRequest request = DeleteOrderRequest.newBuilder()
- .setOrderId(ORDER_UUID.toString())
- .build();
-
- // when
- orderGrpcService.deleteOrder(request, responseObserver);
-
- // then
- verify(orderFacade).deleteOrder(ORDER_ID);
- verify(responseObserver).onNext(argThat(DeleteOrderResponse::getSuccess));
- verify(responseObserver).onCompleted();
- verify(responseObserver, never()).onError(any());
- }
-
- @Test
- @DisplayName("Should handle exception during order deletion")
- void should_handle_exception_during_order_deletion() {
- // given
- DeleteOrderRequest request = DeleteOrderRequest.newBuilder()
- .setOrderId(ORDER_UUID.toString())
- .build();
-
- doThrow(new RuntimeException("Delete failed")).when(orderFacade).deleteOrder(ORDER_ID);
-
- // when
- orderGrpcService.deleteOrder(request, responseObserver);
-
- // then
- verify(responseObserver).onError(any(StatusRuntimeException.class));
- verify(responseObserver, never()).onNext(any());
- verify(responseObserver, never()).onCompleted();
- }
- }
@Nested
@DisplayName("Get Order Status Tests")
diff --git a/src/test/java/com/ecmsp/orderservice/order/adapter/repository/inmemory/InMemoryOrderRepositoryTest.java b/src/test/java/com/ecmsp/orderservice/order/adapter/repository/inmemory/InMemoryOrderRepositoryTest.java
index edb4c1c..734f5c4 100644
--- a/src/test/java/com/ecmsp/orderservice/order/adapter/repository/inmemory/InMemoryOrderRepositoryTest.java
+++ b/src/test/java/com/ecmsp/orderservice/order/adapter/repository/inmemory/InMemoryOrderRepositoryTest.java
@@ -26,6 +26,7 @@ class InMemoryOrderRepositoryTest {
private static final Order ORDER_1 = new Order(
/* orderId = */ ORDER_1_ID,
+ /* reservationId = */ null,
/* clientId = */ CLIENT_1_ID,
/* orderStatus = */ OrderStatus.PENDING,
/* date = */ DATE_2025_07_10_15_00_00,
@@ -34,6 +35,7 @@ class InMemoryOrderRepositoryTest {
private static final Order ORDER_2 = new Order(
/* orderId = */ ORDER_2_ID,
+ /* reservationId = */ null,
/* clientId = */ CLIENT_2_ID,
/* orderStatus = */ OrderStatus.PENDING,
/* date = */ DATE_2025_07_11_15_00_00,
@@ -146,6 +148,7 @@ void should_update_order() {
// and:
Order updatedOrder = new Order(
ORDER_1_ID,
+ null,
CLIENT_1_ID,
OrderStatus.PROCESSING,
DATE_2025_07_10_15_00_00,
@@ -171,6 +174,7 @@ void should_throw_exception_when_update_order_that_does_not_exist() {
// and:
Order updatedOrder2 = new Order(
ORDER_2_ID,
+ null,
CLIENT_2_ID,
OrderStatus.PROCESSING,
DATE_2025_07_11_15_00_00,
diff --git a/src/test/java/com/ecmsp/orderservice/order/api/OrdersControllerTest.java b/src/test/java/com/ecmsp/orderservice/order/api/OrdersControllerTest.java
index b1bf07d..1722d75 100644
--- a/src/test/java/com/ecmsp/orderservice/order/api/OrdersControllerTest.java
+++ b/src/test/java/com/ecmsp/orderservice/order/api/OrdersControllerTest.java
@@ -23,6 +23,7 @@ public class OrdersControllerTest {
private static final Order ORDER_1 = new Order(
/* orderId = */ new OrderId(UUID.fromString("3745fd3b-62b1-40a1-ab32-57aa2ecf562f")),
+ /* reservationId = */ null,
/* clientId = */ new ClientId(UUID.fromString("b74d2425-a3ad-4138-ae70-3c4ecbcf5803")),
/* orderStatus = */ OrderStatus.PENDING,
/* date = */ LocalDateTime.of(2025, 7, 10, 15, 0, 0),
@@ -31,6 +32,7 @@ public class OrdersControllerTest {
private static final Order ORDER_2 = new Order(
/* orderId = */ new OrderId(UUID.fromString("605c2114-faaa-447d-adfd-582408389958")),
+ /* reservationId = */ null,
/* clientId = */ new ClientId(UUID.fromString("85f30610-467c-49a1-a50a-9686aa6089dc")),
/* orderStatus = */ OrderStatus.PENDING,
/* date = */ LocalDateTime.of(2025, 7, 11, 6, 0, 0),
diff --git a/src/test/java/com/ecmsp/orderservice/order/domain/OrderFacadeTest.java b/src/test/java/com/ecmsp/orderservice/order/domain/OrderFacadeTest.java
index 4fd40ed..6c7cedc 100644
--- a/src/test/java/com/ecmsp/orderservice/order/domain/OrderFacadeTest.java
+++ b/src/test/java/com/ecmsp/orderservice/order/domain/OrderFacadeTest.java
@@ -17,20 +17,28 @@ public class OrderFacadeTest {
private static final OrderId ORDER_1_ID = new OrderId(UUID.fromString("123e4567-e89b-12d3-a456-426614174000"));
private static final OrderId ORDER_2_ID = new OrderId(UUID.fromString("9e349a18-1203-4224-829c-dc15700c68a5"));
+ private static final ReservationId RESERVATION_1_ID = new ReservationId(UUID.fromString("c5e75ab0-a110-4a2a-b6f4-c4573e6f548e"));
+
private static final ClientId CLIENT_1_ID = new ClientId(UUID.fromString("b5d1eec8-c3ea-4b55-8cec-900b5c018381"));
private static final ClientId CLIENT_2_ID = new ClientId(UUID.fromString("b259c7f1-483b-4700-accc-1554542eb8f5"));
private static final List ITEMS = List.of(
new OrderItem(
/* itemId = */ new ItemId(UUID.fromString("66d155e8-2d57-44fa-9adc-580e1e4f9cc9")),
+ /* variantId = */ null,
/* quantity = */ 2,
/* price = */ BigDecimal.valueOf(10),
+ /* imageUrl = */ null,
+ /* description = */ null,
/* isReturnable = */ true
),
new OrderItem(
/* itemId = */ new ItemId(UUID.fromString("473c1579-12b1-49b0-b90e-253782c874a5")),
+ /* variantId = */ null,
/* quantity = */ 1,
/* price = */ BigDecimal.valueOf(20),
+ /* imageUrl = */ null,
+ /* description = */ null,
/* isReturnable = */ false
)
);
@@ -41,6 +49,7 @@ public class OrderFacadeTest {
private static final Order ORDER_1 = new Order(
/* orderId = */ ORDER_1_ID,
+ /* reservationId = */ null,
/* clientId = */ CLIENT_1_ID,
/* orderStatus = */ OrderStatus.PENDING,
/* date = */ DATE_2025_07_10_15_00_00,
@@ -49,6 +58,7 @@ public class OrderFacadeTest {
private static final Order ORDER_2 = new Order(
/* orderId = */ ORDER_2_ID,
+ /* reservationId = */ null,
/* clientId = */ CLIENT_2_ID,
/* orderStatus = */ OrderStatus.PENDING,
/* date = */ DATE_2025_07_11_15_00_00,
@@ -70,12 +80,13 @@ void should_create_order() {
// when:
- Order createdOrder = facade.createOrder(new OrderToCreate(CLIENT_1_ID, ITEMS), new Context(null));
+ Order createdOrder = facade.createOrder(new OrderToCreate(RESERVATION_1_ID, CLIENT_1_ID, ITEMS), new Context(null));
// then:
assertThat(createdOrder).isEqualTo(
new Order(
/* orderId = */ ORDER_1_ID,
+ /* reservationId = */ RESERVATION_1_ID,
/* clientId = */ CLIENT_1_ID,
/* orderStatus = */ OrderStatus.PENDING,
/* date = */ DATE_2025_07_10_15_00_00,
@@ -101,7 +112,7 @@ void should_fail_when_create_order_with_existing_id() {
// when:
var error = assertThatThrownBy(() ->
// Trying to create an order with the same ID: ORDER_1_ID
- facade.createOrder(new OrderToCreate(CLIENT_1_ID, ITEMS), new Context(null))
+ facade.createOrder(new OrderToCreate(RESERVATION_1_ID, CLIENT_1_ID, ITEMS), new Context(null))
);
// then:
@@ -135,6 +146,7 @@ void should_update_order() {
assertThat(updatedOrder).isEqualTo(
new Order(
/* orderId = */ ORDER_1_ID,
+ /* reservationId = */ null,
/* clientId = */ CLIENT_1_ID,
/* orderStatus = */ OrderStatus.PAID,
/* date = */ DATE_2025_07_10_15_00_00,
@@ -275,12 +287,15 @@ void should_return_true_when_order_is_returnable() {
List returnableItems = List.of(
new OrderItem(
new ItemId(UUID.fromString("66d155e8-2d57-44fa-9adc-580e1e4f9cc9")),
+ null,
2,
BigDecimal.valueOf(10),
+ null,
+ null,
true
)
);
- Order returnableOrder = new Order(ORDER_1_ID, CLIENT_1_ID, OrderStatus.PAID, recentDate, returnableItems);
+ Order returnableOrder = new Order(ORDER_1_ID, null, CLIENT_1_ID, OrderStatus.PAID, recentDate, returnableItems);
TestOrderRepository orderRepository = new TestOrderRepository(List.of(returnableOrder));
OrderFacade facade = new DefaultOrderFacade(
@@ -306,12 +321,15 @@ void should_return_false_when_order_is_too_old() {
List returnableItems = List.of(
new OrderItem(
new ItemId(UUID.fromString("66d155e8-2d57-44fa-9adc-580e1e4f9cc9")),
+ null,
2,
BigDecimal.valueOf(10),
+ null,
+ null,
true
)
);
- Order oldOrder = new Order(ORDER_1_ID, CLIENT_1_ID, OrderStatus.PAID, oldDate, returnableItems);
+ Order oldOrder = new Order(ORDER_1_ID, null, CLIENT_1_ID, OrderStatus.PAID, oldDate, returnableItems);
TestOrderRepository orderRepository = new TestOrderRepository(List.of(oldOrder));
OrderFacade facade = new DefaultOrderFacade(
@@ -337,12 +355,15 @@ void should_return_false_when_no_items_are_returnable() {
List nonReturnableItems = List.of(
new OrderItem(
new ItemId(UUID.fromString("66d155e8-2d57-44fa-9adc-580e1e4f9cc9")),
+ null,
2,
BigDecimal.valueOf(10),
+ null,
+ null,
false // not returnable
)
);
- Order nonReturnableOrder = new Order(ORDER_1_ID, CLIENT_1_ID, OrderStatus.PAID, recentDate, nonReturnableItems);
+ Order nonReturnableOrder = new Order(ORDER_1_ID, null, CLIENT_1_ID, OrderStatus.PAID, recentDate, nonReturnableItems);
TestOrderRepository orderRepository = new TestOrderRepository(List.of(nonReturnableOrder));
OrderFacade facade = new DefaultOrderFacade(
@@ -367,18 +388,24 @@ void should_return_only_returnable_items_within_return_period() {
LocalDateTime recentDate = LocalDateTime.now().minusDays(7); // 7 days ago
OrderItem returnableItem = new OrderItem(
new ItemId(UUID.fromString("66d155e8-2d57-44fa-9adc-580e1e4f9cc9")),
+ null,
2,
BigDecimal.valueOf(10),
+ null,
+ null,
true
);
OrderItem nonReturnableItem = new OrderItem(
new ItemId(UUID.fromString("473c1579-12b1-49b0-b90e-253782c874a5")),
+ null,
1,
BigDecimal.valueOf(20),
+ null,
+ null,
false
);
List mixedItems = List.of(returnableItem, nonReturnableItem);
- Order mixedOrder = new Order(ORDER_1_ID, CLIENT_1_ID, OrderStatus.PAID, recentDate, mixedItems);
+ Order mixedOrder = new Order(ORDER_1_ID, null, CLIENT_1_ID, OrderStatus.PAID, recentDate, mixedItems);
TestOrderRepository orderRepository = new TestOrderRepository(List.of(mixedOrder));
OrderFacade facade = new DefaultOrderFacade(
@@ -403,11 +430,14 @@ void should_return_empty_list_when_order_is_outside_return_period() {
LocalDateTime oldDate = LocalDateTime.now().minusDays(20); // 20 days ago
OrderItem returnableItem = new OrderItem(
new ItemId(UUID.fromString("66d155e8-2d57-44fa-9adc-580e1e4f9cc9")),
+ null,
2,
BigDecimal.valueOf(10),
+ null,
+ null,
true
);
- Order oldOrder = new Order(ORDER_1_ID, CLIENT_1_ID, OrderStatus.PAID, oldDate, List.of(returnableItem));
+ Order oldOrder = new Order(ORDER_1_ID, null, CLIENT_1_ID, OrderStatus.PAID, oldDate, List.of(returnableItem));
TestOrderRepository orderRepository = new TestOrderRepository(List.of(oldOrder));
OrderFacade facade = new DefaultOrderFacade(