Skip to content

feat: 입고 확정 기능 구현#94

Merged
JoonKyoLee merged 6 commits intomainfrom
feat/update-receipt-to-confirmed
Nov 24, 2025
Merged

feat: 입고 확정 기능 구현#94
JoonKyoLee merged 6 commits intomainfrom
feat/update-receipt-to-confirmed

Conversation

@JoonKyoLee
Copy link
Contributor

@JoonKyoLee JoonKyoLee commented Nov 24, 2025

✨ 작업 내용

  • 입고 확정 기능 구현

📝 적용 범위

  • /receipt

📌 참고 사항

Summary by CodeRabbit

  • 새로운 기능

    • 입고 확정 API 엔드포인트 추가 — 사용자가 입고를 확정할 수 있습니다.
    • 입고 확정 응답으로 성공 여부 반환
  • 버그 수정 / 검증

    • 이미 취소되거나 이미 확정된 입고에 대한 검증 및 관련 오류 응답 추가
  • 테스트

    • 입고 확정 성공 및 오류(사용자/입고 미존재, 접근거부 등) 케이스에 대한 포괄적 테스트 추가

✏️ Tip: You can customize this high-level summary in your review settings.

@JoonKyoLee JoonKyoLee self-assigned this Nov 24, 2025
@JoonKyoLee JoonKyoLee added the enhancement New feature or request label Nov 24, 2025
@coderabbitai
Copy link

coderabbitai bot commented Nov 24, 2025

Walkthrough

입고를 CONFIRMED 상태로 전환하는 PATCH /{receiptId}/confirm 엔드포인트와 관련 서비스·도메인·응답 DTO·메시지·예외 코드를 추가하고, 해당 흐름에 대한 단위/컨트롤러 테스트를 포함합니다. (입고 확정 성공 메시지 및 상태 검증/예외 추가)

Changes

Cohort / File(s) Summary
열거형 상수
src/main/java/com/almang/inventory/global/api/SuccessMessage.java
CONFIRM_RECEIPT_SUCCESS("입고 확정 성공") 상수 추가
오류 코드
src/main/java/com/almang/inventory/global/exception/ErrorCode.java
RECEIPT_ALREADY_CANCELED, RECEIPT_ALREADY_CONFIRMED 오류 코드 추가
도메인 로직
src/main/java/com/almang/inventory/receipt/domain/Receipt.java
confirm() 메서드 추가: 상태 전환(-> CONFIRMED) 및 이미 취소/확정된 경우 예외 처리
서비스 레이어
src/main/java/com/almang/inventory/receipt/service/ReceiptService.java
confirmReceipt(Long receiptId, Long userId) 트랜잭션 메서드 추가: 사용자/상점 검증, 영수증 조회, 도메인 confirm 호출 및 응답 반환
컨트롤러 엔드포인트
src/main/java/com/almang/inventory/receipt/controller/ReceiptController.java
PATCH /{receiptId}/confirm 엔드포인트 추가: 인증된 사용자로부터 요청 받아 서비스 호출, ConfirmReceiptResponseSuccessMessage.CONFIRM_RECEIPT_SUCCESS 반환
응답 DTO
src/main/java/com/almang/inventory/receipt/dto/response/ConfirmReceiptResponse.java
public record ConfirmReceiptResponse(boolean success) 추가
테스트
src/test/java/com/almang/inventory/receipt/controller/ReceiptControllerTest.java, src/test/java/com/almang/inventory/receipt/service/ReceiptServiceTest.java
성공 경로 및 USER_NOT_FOUND, RECEIPT_NOT_FOUND, RECEIPT_ACCESS_DENIED 예외 케이스를 검증하는 테스트 메서드 추가

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Controller as ReceiptController
    participant Service as ReceiptService
    participant UserRepo as UserRepository
    participant ReceiptRepo as ReceiptRepository
    participant Domain as Receipt (Domain)

    User->>Controller: PATCH /{receiptId}/confirm
    Controller->>Controller: 인증 확인 (`@AuthenticationPrincipal`)
    Controller->>Service: confirmReceipt(receiptId, userId)
    
    Service->>UserRepo: findById(userId)
    alt USER_NOT_FOUND
        UserRepo-->>Service: 없음
        Service-->>Controller: UserNotFoundException
        Controller-->>User: 404
    else 사용자 존재
        UserRepo-->>Service: User
        Service->>ReceiptRepo: findById(receiptId)
        alt RECEIPT_NOT_FOUND
            ReceiptRepo-->>Service: 없음
            Service-->>Controller: ReceiptNotFoundException
            Controller-->>User: 404
        else 입고 존재
            ReceiptRepo-->>Service: Receipt
            Service->>Service: storeId 비교 (접근 검증)
            alt RECEIPT_ACCESS_DENIED
                Service-->>Controller: AccessDeniedException
                Controller-->>User: 403
            else 접근 허용
                Service->>Domain: receipt.confirm()
                Domain->>Domain: status = CONFIRMED
                Service->>ReceiptRepo: save()
                Service-->>Controller: ConfirmReceiptResponse(true)
                Controller-->>User: 200 + CONFIRM_RECEIPT_SUCCESS
            end
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

검토 시 주의할 영역:

  • Receipt.confirm()의 상태 전환 안전성 및 예외 타입/메시지 일관성 확인 (추가된 ErrorCode 사용 검증).
  • ReceiptService.confirmReceipt에서의 접근 권한 검증(상점 소유권 비교)과 트랜잭션 경계(읽기/쓰기/동시성) 검토.
  • 컨트롤러의 에러 핸들링/응답 포맷이 기존 예외 처리 흐름과 일관된지 확인.
  • 테스트에서 이미 CONFIRMED 또는 CANCELED 상태에 대한 추가 케이스(재확정/이미 취소)가 충분히 커버되는지 확인.

Possibly related PRs

Poem

📦 입고를 눌러 확정의 문을 열고,
검증은 삼중, 상태는 단단히,
로그 한 줄로 흐름을 남기네.
테스트는 길을 비추고, 배포는 조용히 웃네. ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 핵심 기능(입고 확정 기능)을 명확하고 간결하게 설명하고 있으며, 파일 나열이나 모호한 표현이 없습니다.
Description check ✅ Passed PR 설명이 템플릿의 모든 필수 섹션을 포함하고 있으며, 작업 내용, 적용 범위, 참고 사항이 명확하게 기재되어 있습니다.
Linked Issues check ✅ Passed 모든 코드 변경이 Issue #86의 '입고 확정 기능 구현' 요구사항을 충족합니다. 컨트롤러 엔드포인트, 서비스 로직, 도메인 모델 메서드, 응답 DTO, 에러 코드, 그리고 테스트가 완전히 구현되었습니다.
Out of Scope Changes check ✅ Passed 모든 변경이 입고 확정 기능 구현과 직접 관련되어 있습니다. 불필요한 리팩토링이나 무관한 기능은 없습니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/update-receipt-to-confirmed

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/test/java/com/almang/inventory/receipt/service/ReceiptServiceTest.java (1)

1437-1489: 기본 에러 케이스 테스트가 충실합니다.

향후 비즈니스 검증 로직 추가 시 다음 테스트 케이스들도 추가하시길 권장합니다:

  • 이미 확정된 입고 재확정 시도
  • 취소된 입고 확정 시도
  • 비활성화된 입고 확정 시도

이러한 엣지 케이스들을 미리 테스트하면 회귀 버그를 방지할 수 있습니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7302889 and 56860d2.

📒 Files selected for processing (7)
  • src/main/java/com/almang/inventory/global/api/SuccessMessage.java (1 hunks)
  • src/main/java/com/almang/inventory/receipt/controller/ReceiptController.java (2 hunks)
  • src/main/java/com/almang/inventory/receipt/domain/Receipt.java (1 hunks)
  • src/main/java/com/almang/inventory/receipt/dto/response/ConfirmReceiptResponse.java (1 hunks)
  • src/main/java/com/almang/inventory/receipt/service/ReceiptService.java (2 hunks)
  • src/test/java/com/almang/inventory/receipt/controller/ReceiptControllerTest.java (2 hunks)
  • src/test/java/com/almang/inventory/receipt/service/ReceiptServiceTest.java (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-22T18:12:13.161Z
Learnt from: JoonKyoLee
Repo: almang2/inventory-server PR: 66
File: src/main/java/com/almang/inventory/order/domain/OrderItem.java:41-53
Timestamp: 2025-11-22T18:12:13.161Z
Learning: In the almang2/inventory-server repository, OrderItem entity update methods (updateQuantity, updatePrice in src/main/java/com/almang/inventory/order/domain/OrderItem.java) do not require null checks because OrderService will validate parameters before calling these update methods, following the same pattern as Product entity updates.
<!--

Applied to files:

  • src/test/java/com/almang/inventory/receipt/controller/ReceiptControllerTest.java
  • src/main/java/com/almang/inventory/receipt/controller/ReceiptController.java
  • src/test/java/com/almang/inventory/receipt/service/ReceiptServiceTest.java
  • src/main/java/com/almang/inventory/receipt/service/ReceiptService.java
🔇 Additional comments (10)
src/main/java/com/almang/inventory/receipt/dto/response/ConfirmReceiptResponse.java (1)

3-5: 깔끔한 응답 DTO 구현입니다.

기존 DeleteReceiptResponse 패턴과 일관성 있게 구현되었습니다.

src/main/java/com/almang/inventory/global/api/SuccessMessage.java (1)

60-60: 성공 메시지가 적절하게 추가되었습니다.

기존 패턴과 일관성 있게 RECEIPT 섹션에 배치되었습니다.

src/main/java/com/almang/inventory/receipt/service/ReceiptService.java (1)

16-16: 필요한 import가 올바르게 추가되었습니다.

src/test/java/com/almang/inventory/receipt/controller/ReceiptControllerTest.java (3)

22-22: 필요한 테스트 의존성이 추가되었습니다.


1140-1158: 확정 성공 케이스 테스트가 잘 작성되었습니다.

응답 상태, 메시지, 데이터를 모두 검증하고 있습니다.


1160-1212: 주요 에러 케이스들이 잘 커버되었습니다.

사용자 없음, 입고 없음, 접근 거부 케이스가 모두 테스트되고 있습니다. 다만, 향후 비즈니스 검증 로직이 추가되면(취소된 입고 확정 시도, 이미 확정된 입고 재확정 등) 해당 케이스들에 대한 테스트도 추가해야 합니다.

src/test/java/com/almang/inventory/receipt/service/ReceiptServiceTest.java (2)

21-21: 테스트 의존성이 올바르게 추가되었습니다.


1402-1435: 통합 테스트가 견고하게 작성되었습니다.

데이터베이스 상태 변경을 실제로 검증하고 있어 신뢰도가 높습니다.

src/main/java/com/almang/inventory/receipt/controller/ReceiptController.java (2)

10-10: 컨트롤러 의존성이 적절히 추가되었습니다.


141-154: 확정 엔드포인트가 RESTful 하게 잘 구현되었습니다.

PATCH 메서드 선택이 적절하며, 인증 처리와 로깅이 기존 패턴과 일관되게 구현되었습니다. Swagger 문서화도 갖추어져 있어 API 사용성이 좋습니다.

Comment on lines +150 to +161
@Transactional
public ConfirmReceiptResponse confirmReceipt(Long receiptId, Long userId) {
User user = findUserById(userId);
Store store = user.getStore();

log.info("[ReceiptService] 입고 확정 요청 - userId: {}, storeId: {}", userId, store.getId());
Receipt receipt = findReceiptByIdAndValidateAccess(receiptId, store);
receipt.confirm();

log.info("[ReceiptService] 입고 확정 성공 - receiptId: {}", receipt.getId());
return new ConfirmReceiptResponse(true);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

입고 확정 전 비즈니스 검증 로직이 부족합니다.

현재 구현은 사용자 권한만 확인하고 입고의 상태는 검증하지 않습니다. 다음 사항들을 추가 검증해야 합니다:

  1. activated 플래그 확인: 삭제된(비활성화된) 입고를 확정할 수 없어야 합니다
  2. 현재 상태 확인: PENDING 상태의 입고만 확정 가능해야 합니다
  3. 데이터 완전성: 입고 아이템이 존재하는지 확인이 필요할 수 있습니다

개선 제안:

 @Transactional
 public ConfirmReceiptResponse confirmReceipt(Long receiptId, Long userId) {
     User user = findUserById(userId);
     Store store = user.getStore();

     log.info("[ReceiptService] 입고 확정 요청 - userId: {}, storeId: {}", userId, store.getId());
     Receipt receipt = findReceiptByIdAndValidateAccess(receiptId, store);
+    
+    if (!receipt.isActivated()) {
+        throw new BaseException(ErrorCode.RECEIPT_ALREADY_DELETED);
+    }
+    if (receipt.getStatus() == ReceiptStatus.CONFIRMED) {
+        throw new BaseException(ErrorCode.RECEIPT_ALREADY_CONFIRMED);
+    }
+    if (receipt.getStatus() == ReceiptStatus.CANCELED) {
+        throw new BaseException(ErrorCode.RECEIPT_ALREADY_CANCELED);
+    }
+    
     receipt.confirm();

     log.info("[ReceiptService] 입고 확정 성공 - receiptId: {}", receipt.getId());
     return new ConfirmReceiptResponse(true);
 }

참고: ErrorCode enum에 해당 에러 코드들을 추가해야 합니다. 비즈니스 검증은 도메인 계층에서 할 수도 있고 서비스 계층에서 할 수도 있습니다만, 일관성을 위해 기존 패턴을 따르세요.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/java/com/almang/inventory/receipt/service/ReceiptService.java around
lines 150-161, add business validation before calling receipt.confirm(): verify
receipt.isActivated() (reject if false), verify receipt.getStatus() ==
ReceiptStatus.PENDING (reject otherwise), and verify receipt.getItems() is not
null/empty (reject if empty). For each failure throw the existing application
exception with an appropriate ErrorCode (add ErrorCode entries if missing, e.g.,
RECEIPT_NOT_ACTIVE, RECEIPT_INVALID_STATUS, RECEIPT_NO_ITEMS) following the
project’s existing exception pattern; perform these checks in the service method
(or delegate to domain methods on Receipt if that matches existing conventions)
and only call receipt.confirm() after all validations pass.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/main/java/com/almang/inventory/global/exception/ErrorCode.java (1)

48-56: 입고 상태 전용 에러코드 분리, 방향 좋습니다.

  • 입고 취소/확정에 대해 별도 에러코드를 둔 덕분에 서비스/컨트롤러 단에서 상태별 분기와 테스트가 훨씬 명확해집니다.
  • 기존 RECEIPT_* 코드들과 HttpStatus, 메시지 스타일이 일관되어 유지보수성도 좋습니다. 짧게 말해, 나중에 상태 다이어그램 그려도 잘 맞을 설계네요. 😄

추가로, HTTP 400/409 중 어떤 것을 비즈니스 밸리데이션에 쓸지 팀 컨벤션을 한번 정리해 두면, 이후 다른 도메인(ORDER, PRODUCT 등) 상태 전이 에러 추가할 때도 기준이 더 분명해질 것 같습니다. (공식 HTTP 스펙이나 Spring REST Best Practice 문서들을 참고해 보셔도 좋겠습니다.)

src/main/java/com/almang/inventory/receipt/domain/Receipt.java (1)

85-91: 이미 취소된 입고에 대한 재취소 방어 로직이 명확합니다.

  • 이미 ReceiptStatus.CANCELED인 경우 예외(RECEIPT_ALREADY_CANCELED)를 던지도록 해서, “취소 → 취소”와 같은 잘못된 상태 전이를 도메인 차원에서 차단하고 있습니다.
  • 이 덕분에 API 계층에서 굳이 상태를 이중으로 검사하지 않아도 되어, 상태 전이 규칙이 한 곳에서 유지됩니다.

참고로, REST API 관점에서는 “취소 요청을 여러 번 보내도 항상 동일한 결과(멱등)”를 선호하는 경우도 있습니다. 현재처럼 두 번째 호출을 에러로 보는지, 아니면 그냥 성공(예: 200/204)으로 간주할지는 팀의 API UX 정책에 따라 갈릴 수 있으니, 클라이언트 사용 패턴을 보면서 한 번쯤 논의해 보셔도 좋겠습니다. (멱등성은 HTTP RFC 및 REST API 설계 Best Practice 문서들에 좋은 논의가 많습니다.)

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 56860d2 and b2e181b.

📒 Files selected for processing (2)
  • src/main/java/com/almang/inventory/global/exception/ErrorCode.java (1 hunks)
  • src/main/java/com/almang/inventory/receipt/domain/Receipt.java (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-22T18:12:13.161Z
Learnt from: JoonKyoLee
Repo: almang2/inventory-server PR: 66
File: src/main/java/com/almang/inventory/order/domain/OrderItem.java:41-53
Timestamp: 2025-11-22T18:12:13.161Z
Learning: In the almang2/inventory-server repository, OrderItem entity update methods (updateQuantity, updatePrice in src/main/java/com/almang/inventory/order/domain/OrderItem.java) do not require null checks because OrderService will validate parameters before calling these update methods, following the same pattern as Product entity updates.
<!--

Applied to files:

  • src/main/java/com/almang/inventory/receipt/domain/Receipt.java
🔇 Additional comments (2)
src/main/java/com/almang/inventory/receipt/domain/Receipt.java (2)

4-6: 도메인 레벨에서 BaseException을 사용하도록 정리한 것 좋습니다.

  • Receipt 엔티티가 직접 BaseException + ErrorCode를 사용하면서, “입고 상태 전이 규칙”이 한 곳(도메인 모델)에서 관리됩니다.
  • 이렇게 하면 서비스/컨트롤러는 상태 전이 로직 대신 “언제 confirm()/deactivate()를 부를지”만 신경 쓰면 되기 때문에 레이어 간 관심사가 잘 분리됩니다.

DDD에서 말하는 애그리거트 불변식(invariant)을 엔티티 메서드로 캡슐화하는 전형적인 패턴이라, 지금 방향을 계속 유지하시면 좋겠습니다. 필요하다면 나중에 Receipt 상태 전이 다이어그램을 간단히 문서화해 두는 것도 추천드립니다.


93-101: 입고 확정(confirm) 상태 전이 검증이 잘 정리되었습니다.

  • !activated || status == CANCELED일 때 RECEIPT_ALREADY_CANCELED 예외를 던져서, “취소된/비활성 입고를 다시 확정”하는 시나리오를 도메인에서 차단하고 있습니다.
  • status == CONFIRMED일 때 RECEIPT_ALREADY_CONFIRMED를 던지는 것도, “재확정”을 명시적으로 에러로 처리해 API 응답을 분명하게 만들어 줍니다.
  • 마지막에 this.status = ReceiptStatus.CONFIRMED;만 변경하고 activated는 건드리지 않아, “활성 + 확정”이라는 상태 조합이 유지되는 점도 자연스럽습니다.

이전 커밋에서 지적되었던 “상태 전이 검증 누락” 이슈가, 지금은 엔티티 메서드 안에서 잘 해결된 형태입니다. 앞으로 ReceiptStatus에 상태가 더 늘어난다면(예: PARTIAL_CONFIRMED 등), 이 메서드 초반 조건문만 확장하면 되니 유지보수도 수월할 것 같습니다. 상태 전이 시나리오는 단위 테스트(성공/취소/이미 확정/이미 취소 케이스)를 충분히 커버해 두시면 더욱 안심할 수 있겠네요. 👍

@JoonKyoLee JoonKyoLee merged commit 1f0bbe2 into main Nov 24, 2025
1 check passed
@JoonKyoLee JoonKyoLee deleted the feat/update-receipt-to-confirmed branch November 25, 2025 03:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] 입고 확정 기능 구현

1 participant