Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public enum SuccessMessage {
GET_ORDER_SUCCESS("발주 조회 성공"),
GET_ORDER_LIST_SUCCESS("발주 목록 조회 성공"),
UPDATE_ORDER_SUCCESS("발주 수정 성공"),
GET_ORDER_ITEM_SUCCESS("발주 아이템 조회 성공"),
;

private final String message;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.almang.inventory.order.domain.OrderStatus;
import com.almang.inventory.order.dto.request.CreateOrderRequest;
import com.almang.inventory.order.dto.request.UpdateOrderRequest;
import com.almang.inventory.order.dto.response.OrderItemResponse;
import com.almang.inventory.order.dto.response.OrderResponse;
import com.almang.inventory.order.service.OrderService;
import io.swagger.v3.oas.annotations.Operation;
Expand Down Expand Up @@ -102,4 +103,19 @@ public ResponseEntity<ApiResponse<OrderResponse>> updateOrder(
ApiResponse.success(SuccessMessage.UPDATE_ORDER_SUCCESS.getMessage(), response)
);
}

@GetMapping("/item/{orderItemId}")
@Operation(summary = "발주 아이템 조회", description = "발주 아이템 조회합니다.")
public ResponseEntity<ApiResponse<OrderItemResponse>> getOrderItem(
@PathVariable Long orderItemId,
@AuthenticationPrincipal CustomUserPrincipal userPrincipal
) {
Long userId = userPrincipal.getId();
log.info("[OrderController] 발주 아이템 조회 요청 - orderItemId: {}, userId: {}", orderItemId, userId);
OrderItemResponse response = orderService.getOrderItem(orderItemId, userId);

return ResponseEntity.ok(
ApiResponse.success(SuccessMessage.GET_ORDER_ITEM_SUCCESS.getMessage(), response)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ public record OrderItemResponse(
Long productId,
Integer quantity,
Integer unitPrice,
Integer amount
Integer amount,
String note
) {
public static OrderItemResponse from(OrderItem orderItem) {
if (orderItem.getOrder() == null || orderItem.getProduct() == null) {
Expand All @@ -22,7 +23,8 @@ public static OrderItemResponse from(OrderItem orderItem) {
orderItem.getProduct().getId(),
orderItem.getQuantity(),
orderItem.getUnitPrice(),
orderItem.getAmount()
orderItem.getAmount(),
orderItem.getNote()
);
}
}
25 changes: 25 additions & 0 deletions src/main/java/com/almang/inventory/order/service/OrderService.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.almang.inventory.order.dto.request.CreateOrderRequest;
import com.almang.inventory.order.dto.request.UpdateOrderItemRequest;
import com.almang.inventory.order.dto.request.UpdateOrderRequest;
import com.almang.inventory.order.dto.response.OrderItemResponse;
import com.almang.inventory.order.dto.response.OrderResponse;
import com.almang.inventory.order.repository.OrderItemRepository;
import com.almang.inventory.order.repository.OrderRepository;
Expand Down Expand Up @@ -115,6 +116,19 @@ public OrderResponse updateOrder(Long orderId, UpdateOrderRequest request, Long
return OrderResponse.of(order, order.getItems());
}

@Transactional(readOnly = true)
public OrderItemResponse getOrderItem(Long orderItemId, Long userId) {
User user = findUserById(userId);
Store store = user.getStore();

log.info("[OrderService] 발주 상세 조회 요청 - userId: {}, storeId: {}", userId, store.getId());
OrderItem orderItem = findOrderItemById(orderItemId);
validateOrderItemAccess(orderItem, store);

log.info("[OrderService] 발주 상세 조회 성공 - orderItemId: {}", orderItem.getId());
return OrderItemResponse.from(orderItem);
}

private List<OrderItem> createOrderItems(List<CreateOrderItemRequest> requests, Store store) {
List<OrderItem> items = new ArrayList<>();

Expand Down Expand Up @@ -270,4 +284,15 @@ private void updateOrderItems(Order order, UpdateOrderRequest request) {
}
order.updateTotalPrice(calculateTotalPrice(order.getItems()));
}

private OrderItem findOrderItemById(Long orderItemId) {
return orderItemRepository.findById(orderItemId)
.orElseThrow(() -> new BaseException(ErrorCode.ORDER_ITEM_NOT_FOUND));
}

private void validateOrderItemAccess(OrderItem orderItem, Store store) {
if (!orderItem.getOrder().getStore().getId().equals(store.getId())) {
throw new BaseException(ErrorCode.ORDER_ITEM_ACCESS_DENIED);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.almang.inventory.order.dto.request.CreateOrderRequest;
import com.almang.inventory.order.dto.request.UpdateOrderItemRequest;
import com.almang.inventory.order.dto.request.UpdateOrderRequest;
import com.almang.inventory.order.dto.response.OrderItemResponse;
import com.almang.inventory.order.dto.response.OrderResponse;
import com.almang.inventory.order.service.OrderService;

Expand Down Expand Up @@ -563,4 +564,72 @@ private UsernamePasswordAuthenticationToken auth() {
.value(ErrorCode.VENDOR_CHANGE_NOT_ALLOWED.getMessage()))
.andExpect(jsonPath("$.data").doesNotExist());
}

@Test
void 발주_아이템_조회에_성공한다() throws Exception {
// given
Long orderItemId = 1L;

OrderItemResponse response = new OrderItemResponse(
orderItemId,
100L,
10L,
5,
1000,
5000,
"비고입니다"
);

when(orderService.getOrderItem(anyLong(), anyLong()))
.thenReturn(response);

// when & then
mockMvc.perform(get("/api/v1/order/item/{orderItemId}", orderItemId)
.with(authentication(auth())))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(200))
.andExpect(jsonPath("$.message")
.value(SuccessMessage.GET_ORDER_ITEM_SUCCESS.getMessage()))
.andExpect(jsonPath("$.data.orderItemId").value(orderItemId))
.andExpect(jsonPath("$.data.orderId").value(100L))
.andExpect(jsonPath("$.data.productId").value(10L));
}

@Test
void 발주_아이템_조회시_존재하지_않으면_예외가_발생한다() throws Exception {
// given
Long notExistOrderItemId = 9999L;

when(orderService.getOrderItem(anyLong(), anyLong()))
.thenThrow(new BaseException(ErrorCode.ORDER_ITEM_NOT_FOUND));

// when & then
mockMvc.perform(get("/api/v1/order/item/{orderItemId}", notExistOrderItemId)
.with(authentication(auth())))
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.status")
.value(ErrorCode.ORDER_ITEM_NOT_FOUND.getHttpStatus().value()))
.andExpect(jsonPath("$.message")
.value(ErrorCode.ORDER_ITEM_NOT_FOUND.getMessage()))
.andExpect(jsonPath("$.data").doesNotExist());
}

@Test
void 발주_아이템_조회시_다른_상점의_아이템이면_접근_거부_예외가_발생한다() throws Exception {
// given
Long orderItemId = 1L;

when(orderService.getOrderItem(anyLong(), anyLong()))
.thenThrow(new BaseException(ErrorCode.ORDER_ITEM_ACCESS_DENIED));

// when & then
mockMvc.perform(get("/api/v1/order/item/{orderItemId}", orderItemId)
.with(authentication(auth())))
.andExpect(status().isForbidden())
.andExpect(jsonPath("$.status")
.value(ErrorCode.ORDER_ITEM_ACCESS_DENIED.getHttpStatus().value()))
.andExpect(jsonPath("$.message")
.value(ErrorCode.ORDER_ITEM_ACCESS_DENIED.getMessage()))
.andExpect(jsonPath("$.data").doesNotExist());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.almang.inventory.order.dto.request.CreateOrderRequest;
import com.almang.inventory.order.dto.request.UpdateOrderItemRequest;
import com.almang.inventory.order.dto.request.UpdateOrderRequest;
import com.almang.inventory.order.dto.response.OrderItemResponse;
import com.almang.inventory.order.dto.response.OrderResponse;
import com.almang.inventory.order.repository.OrderRepository;
import com.almang.inventory.product.domain.Product;
Expand Down Expand Up @@ -888,4 +889,86 @@ private Product newProduct(Store store, Vendor vendor, String name, String code)
.isInstanceOf(BaseException.class)
.hasMessageContaining(ErrorCode.ORDER_ITEM_ACCESS_DENIED.getMessage());
}

@Test
void 발주_상세_조회에_성공한다() {
// given
Store store = newStore("테스트 상점");
User user = newUser(store, "order_item_reader");
Vendor vendor = newVendor(store, "발주처1");
Product product = newProduct(store, vendor, "상품1", "P001");

CreateOrderRequest createRequest = new CreateOrderRequest(
vendor.getId(),
"발주 메시지",
3,
List.of(new CreateOrderItemRequest(product.getId(), 2, 1000, "비고"))
);

OrderResponse created = orderService.createOrder(createRequest, user.getId());
Long orderId = created.orderId();

Order order = orderRepository.findById(orderId)
.orElseThrow();
OrderItem orderItem = order.getItems().get(0);
Long orderItemId = orderItem.getId();

// when
OrderItemResponse response = orderService.getOrderItem(orderItemId, user.getId());

// then
assertThat(response).isNotNull();
assertThat(response.orderItemId()).isEqualTo(orderItemId);
assertThat(response.productId()).isEqualTo(product.getId());
assertThat(response.quantity()).isEqualTo(2);
assertThat(response.unitPrice()).isEqualTo(1000);
assertThat(response.amount()).isEqualTo(2 * 1000);
assertThat(response.note()).isEqualTo("비고");
}

@Test
void 발주_상세_조회시_발주_항목이_존재하지_않으면_예외가_발생한다() {
// given
Store store = newStore("테스트 상점");
User user = newUser(store, "order_item_reader");
Long notExistOrderItemId = 9999L;

// when & then
assertThatThrownBy(() -> orderService.getOrderItem(notExistOrderItemId, user.getId()))
.isInstanceOf(BaseException.class)
.hasMessageContaining(ErrorCode.ORDER_ITEM_NOT_FOUND.getMessage());
}

@Test
void 발주_상세_조회시_다른_상점의_발주_항목이면_접근_거부_예외가_발생한다() {
// given
Store store1 = newStore("상점1");
Store store2 = newStore("상점2");

User userOfStore1 = newUser(store1, "user1");
User userOfStore2 = newUser(store2, "user2");

Vendor vendorOfStore2 = newVendor(store2, "상점2 발주처");
Product productOfStore2 = newProduct(store2, vendorOfStore2, "상점2 상품", "P999");

OrderResponse created = orderService.createOrder(
new CreateOrderRequest(
vendorOfStore2.getId(),
"상점2 발주",
2,
List.of(new CreateOrderItemRequest(productOfStore2.getId(), 3, 1000, "비고"))
),
userOfStore2.getId()
);

Long orderId = created.orderId();
Order order2 = orderRepository.findById(orderId)
.orElseThrow();
Long orderItemId = order2.getItems().get(0).getId();

// when & then
assertThatThrownBy(() -> orderService.getOrderItem(orderItemId, userOfStore1.getId()))
.isInstanceOf(BaseException.class)
.hasMessageContaining(ErrorCode.ORDER_ITEM_ACCESS_DENIED.getMessage());
}
}