사용자
WMS 사용자 계정 관리를 위한 API입니다. 사용자 생성, 조회, 수정, 삭제 및 인증을 처리합니다.
Base URL
/api/v1/users
인증
모든 API 요청에는 Authorization 헤더에 Bearer 토큰이 필요합니다.
Authorization: Bearer <access_token>사용자 역할(Role)
| 역할 | 설명 |
|---|---|
ADMIN | 시스템 전체 관리 권한 |
MANAGER | 창고 운영 관리 권한 |
OPERATOR | 입출고 처리 권한 |
VIEWER | 조회 전용 권한 |
엔드포인트
로그인
POST /api/v1/auth/loginRequest Body
{
"email": "user@example.com",
"password": "string"
}Response 200 OK
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4...",
"expiresIn": 3600,
"user": {
"id": "usr_01HXK2MBNC3V4P",
"name": "홍길동",
"email": "user@example.com",
"role": "OPERATOR"
}
}Response 401 Unauthorized
{
"code": "INVALID_CREDENTIALS",
"message": "이메일 또는 비밀번호가 올바르지 않습니다."
}사용자 목록 조회
GET /api/v1/usersQuery Parameters
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
page | number | 아니오 | 페이지 번호 (기본값: 1) |
limit | number | 아니오 | 페이지당 항목 수 (기본값: 20, 최대: 100) |
role | string | 아니오 | 역할 필터 (ADMIN, MANAGER, OPERATOR, VIEWER) |
isActive | boolean | 아니오 | 활성 여부 필터 |
search | string | 아니오 | 이름 또는 이메일 검색 |
Response 200 OK
{
"data": [
{
"id": "usr_01HXK2MBNC3V4P",
"name": "홍길동",
"email": "hong@example.com",
"role": "OPERATOR",
"isActive": true,
"lastLoginAt": "2024-11-20T09:30:00Z",
"createdAt": "2024-01-15T08:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 85,
"totalPages": 5
}
}사용자 단건 조회
GET /api/v1/users/:idPath Parameters
| 파라미터 | 타입 | 설명 |
|---|---|---|
id | string | 사용자 고유 ID |
Response 200 OK
{
"id": "usr_01HXK2MBNC3V4P",
"name": "홍길동",
"email": "hong@example.com",
"phone": "010-1234-5678",
"role": "OPERATOR",
"warehouseIds": ["wh_01", "wh_02"],
"isActive": true,
"lastLoginAt": "2024-11-20T09:30:00Z",
"createdAt": "2024-01-15T08:00:00Z",
"updatedAt": "2024-11-01T12:00:00Z"
}Response 404 Not Found
{
"code": "USER_NOT_FOUND",
"message": "사용자를 찾을 수 없습니다."
}사용자 생성
POST /api/v1/users권한:
ADMIN,MANAGER
Request Body
{
"name": "김영수",
"email": "kim@example.com",
"password": "Str0ng!Pass",
"phone": "010-9876-5432",
"role": "OPERATOR",
"warehouseIds": ["wh_01"]
}| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
name | string | 예 | 사용자 이름 |
email | string | 예 | 이메일 (고유값) |
password | string | 예 | 최소 8자, 영문+숫자+특수문자 |
phone | string | 아니오 | 연락처 |
role | string | 예 | 사용자 역할 |
warehouseIds | string[] | 아니오 | 접근 허용 창고 ID 목록 |
Response 201 Created
{
"id": "usr_01HXK9ZTRBP2QM",
"name": "김영수",
"email": "kim@example.com",
"role": "OPERATOR",
"isActive": true,
"createdAt": "2024-11-21T10:00:00Z"
}Response 409 Conflict
{
"code": "EMAIL_ALREADY_EXISTS",
"message": "이미 사용 중인 이메일입니다."
}사용자 수정
PATCH /api/v1/users/:id권한:
ADMIN,MANAGER또는 본인
Request Body (변경할 필드만 포함)
{
"name": "김영수",
"phone": "010-1111-2222",
"role": "MANAGER",
"warehouseIds": ["wh_01", "wh_03"],
"isActive": true
}Response 200 OK
{
"id": "usr_01HXK9ZTRBP2QM",
"name": "김영수",
"email": "kim@example.com",
"phone": "010-1111-2222",
"role": "MANAGER",
"isActive": true,
"updatedAt": "2024-11-21T11:30:00Z"
}사용자 삭제
DELETE /api/v1/users/:id권한:
ADMIN
소프트 삭제(Soft Delete)로 처리됩니다. 데이터는 보존되며 isActive가 false로 변경됩니다.
Response 200 OK
{
"id": "usr_01HXK9ZTRBP2QM",
"message": "사용자가 비활성화되었습니다."
}비밀번호 변경
POST /api/v1/users/:id/change-passwordRequest Body
{
"currentPassword": "OldPass123!",
"newPassword": "NewPass456!",
"confirmPassword": "NewPass456!"
}Response 200 OK
{
"message": "비밀번호가 성공적으로 변경되었습니다."
}오류 코드
| 코드 | HTTP 상태 | 설명 |
|---|---|---|
INVALID_CREDENTIALS | 401 | 잘못된 인증 정보 |
TOKEN_EXPIRED | 401 | 만료된 토큰 |
FORBIDDEN | 403 | 권한 없음 |
USER_NOT_FOUND | 404 | 사용자 없음 |
EMAIL_ALREADY_EXISTS | 409 | 이메일 중복 |
VALIDATION_ERROR | 422 | 입력값 유효성 오류 |
입고
상품의 창고 입고 처리를 위한 API입니다. 입고 예정 등록부터 검수, 적치까지의 프로세스를 관리합니다.
Base URL
/api/v1/inbound
입고 상태(Status)
SCHEDULED → ARRIVED → INSPECTING → STOCKED → CANCELLED
| 상태 | 설명 |
|---|---|
SCHEDULED | 입고 예정 등록 완료 |
ARRIVED | 창고 도착 확인 |
INSPECTING | 검수 진행 중 |
STOCKED | 적치 완료 (입고 완료) |
CANCELLED | 입고 취소 |
엔드포인트
입고 목록 조회
GET /api/v1/inboundQuery Parameters
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
page | number | 아니오 | 페이지 번호 (기본값: 1) |
limit | number | 아니오 | 페이지당 항목 수 (기본값: 20) |
status | string | 아니오 | 상태 필터 |
warehouseId | string | 아니오 | 창고 ID 필터 |
supplierId | string | 아니오 | 공급업체 ID 필터 |
scheduledFrom | string | 아니오 | 입고 예정일 시작 (ISO 8601) |
scheduledTo | string | 아니오 | 입고 예정일 종료 (ISO 8601) |
Response 200 OK
{
"data": [
{
"id": "inb_01HXK4RNDQ8WPZ",
"orderNo": "INB-2024-001234",
"warehouseId": "wh_01",
"warehouseName": "서울 물류센터",
"supplierId": "sup_001",
"supplierName": "한국상사",
"status": "ARRIVED",
"scheduledAt": "2024-11-21T09:00:00Z",
"arrivedAt": "2024-11-21T08:45:00Z",
"totalItems": 5,
"totalQuantity": 320,
"createdAt": "2024-11-18T14:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 142,
"totalPages": 8
}
}입고 단건 조회
GET /api/v1/inbound/:idResponse 200 OK
{
"id": "inb_01HXK4RNDQ8WPZ",
"orderNo": "INB-2024-001234",
"warehouseId": "wh_01",
"warehouseName": "서울 물류센터",
"supplierId": "sup_001",
"supplierName": "한국상사",
"status": "INSPECTING",
"scheduledAt": "2024-11-21T09:00:00Z",
"arrivedAt": "2024-11-21T08:45:00Z",
"memo": "냉장 보관 필요",
"items": [
{
"id": "inbi_001",
"productId": "prod_A01",
"productName": "제품 A",
"sku": "SKU-A-001",
"expectedQty": 100,
"receivedQty": 98,
"damagedQty": 2,
"lotNo": "LOT2024112101",
"expiryDate": "2025-11-21",
"locationId": null
}
],
"history": [
{
"status": "SCHEDULED",
"changedAt": "2024-11-18T14:00:00Z",
"changedBy": "홍길동"
},
{
"status": "ARRIVED",
"changedAt": "2024-11-21T08:45:00Z",
"changedBy": "김영수"
}
],
"createdAt": "2024-11-18T14:00:00Z",
"updatedAt": "2024-11-21T09:10:00Z"
}입고 등록 (예약)
POST /api/v1/inbound권한:
ADMIN,MANAGER,OPERATOR
Request Body
{
"warehouseId": "wh_01",
"supplierId": "sup_001",
"scheduledAt": "2024-11-25T10:00:00Z",
"memo": "우선 처리 요청",
"items": [
{
"productId": "prod_A01",
"expectedQty": 200,
"lotNo": "LOT2024112501",
"expiryDate": "2025-11-25"
},
{
"productId": "prod_B02",
"expectedQty": 150
}
]
}| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
warehouseId | string | 예 | 입고 창고 ID |
supplierId | string | 예 | 공급업체 ID |
scheduledAt | string | 예 | 입고 예정 일시 (ISO 8601) |
memo | string | 아니오 | 비고 |
items | array | 예 | 입고 상품 목록 (최소 1개) |
items[].productId | string | 예 | 상품 ID |
items[].expectedQty | number | 예 | 예정 수량 (양수) |
items[].lotNo | string | 아니오 | 로트 번호 |
items[].expiryDate | string | 아니오 | 유통기한 (YYYY-MM-DD) |
Response 201 Created
{
"id": "inb_01HXK9NEW3P5RT",
"orderNo": "INB-2024-001235",
"status": "SCHEDULED",
"scheduledAt": "2024-11-25T10:00:00Z",
"createdAt": "2024-11-21T11:00:00Z"
}도착 확인
POST /api/v1/inbound/:id/arrive입고 차량이 창고에 도착했음을 확인합니다. 상태가 ARRIVED로 변경됩니다.
Request Body
{
"arrivedAt": "2024-11-25T09:50:00Z",
"vehicleNo": "12가 3456",
"driverName": "박기사"
}Response 200 OK
{
"id": "inb_01HXK9NEW3P5RT",
"status": "ARRIVED",
"arrivedAt": "2024-11-25T09:50:00Z"
}검수 처리
POST /api/v1/inbound/:id/inspect상품 검수 결과를 등록합니다. 상태가 INSPECTING으로 변경됩니다.
Request Body
{
"items": [
{
"id": "inbi_001",
"receivedQty": 198,
"damagedQty": 2,
"inspectionMemo": "외포장 파손 2개"
}
]
}Response 200 OK
{
"id": "inb_01HXK9NEW3P5RT",
"status": "INSPECTING",
"items": [
{
"id": "inbi_001",
"expectedQty": 200,
"receivedQty": 198,
"damagedQty": 2,
"discrepancyQty": -2
}
]
}적치 완료
POST /api/v1/inbound/:id/stock검수 완료된 상품을 창고 위치에 배치합니다. 상태가 STOCKED로 변경되며 재고가 증가합니다.
Request Body
{
"items": [
{
"id": "inbi_001",
"locationId": "loc_A-01-01",
"stockedQty": 196
}
]
}Response 200 OK
{
"id": "inb_01HXK9NEW3P5RT",
"status": "STOCKED",
"stockedAt": "2024-11-25T11:30:00Z",
"items": [
{
"id": "inbi_001",
"locationId": "loc_A-01-01",
"stockedQty": 196
}
]
}입고 취소
POST /api/v1/inbound/:id/cancel
STOCKED상태에서는 취소 불가
Request Body
{
"reason": "공급업체 요청으로 일정 변경"
}Response 200 OK
{
"id": "inb_01HXK9NEW3P5RT",
"status": "CANCELLED",
"cancelledAt": "2024-11-21T15:00:00Z",
"reason": "공급업체 요청으로 일정 변경"
}오류 코드
| 코드 | HTTP 상태 | 설명 |
|---|---|---|
INBOUND_NOT_FOUND | 404 | 입고 건 없음 |
INVALID_STATUS_TRANSITION | 422 | 허용되지 않는 상태 전환 |
PRODUCT_NOT_FOUND | 404 | 상품 없음 |
WAREHOUSE_NOT_FOUND | 404 | 창고 없음 |
LOCATION_CAPACITY_EXCEEDED | 422 | 보관 위치 용량 초과 |
ALREADY_STOCKED | 409 | 이미 적치 완료된 입고 건 |
재고
창고 내 재고 현황 조회 및 관리를 위한 API입니다. 실시간 재고 수량, 위치 정보, 재고 조정 기능을 제공합니다.
Base URL
/api/v1/inventory
재고 상태(Status)
| 상태 | 설명 |
|---|---|
AVAILABLE | 정상 출고 가능 재고 |
RESERVED | 출고 예약된 재고 |
DAMAGED | 불량/파손 재고 |
QUARANTINED | 검역/보류 재고 |
EXPIRED | 유통기한 만료 재고 |
엔드포인트
재고 목록 조회
GET /api/v1/inventoryQuery Parameters
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
page | number | 아니오 | 페이지 번호 (기본값: 1) |
limit | number | 아니오 | 페이지당 항목 수 (기본값: 20) |
warehouseId | string | 아니오 | 창고 ID 필터 |
productId | string | 아니오 | 상품 ID 필터 |
locationId | string | 아니오 | 위치 ID 필터 |
status | string | 아니오 | 재고 상태 필터 |
lotNo | string | 아니오 | 로트 번호 필터 |
expiryBefore | string | 아니오 | 이 날짜 이전 만료되는 재고 조회 (YYYY-MM-DD) |
lowStock | boolean | 아니오 | true이면 안전재고 미달 항목만 반환 |
Response 200 OK
{
"data": [
{
"id": "inv_01HXK6TZPQ7MND",
"warehouseId": "wh_01",
"warehouseName": "서울 물류센터",
"productId": "prod_A01",
"productName": "제품 A",
"sku": "SKU-A-001",
"locationId": "loc_A-01-01",
"locationCode": "A-01-01",
"status": "AVAILABLE",
"quantity": 196,
"reservedQty": 50,
"availableQty": 146,
"lotNo": "LOT2024112101",
"expiryDate": "2025-11-21",
"updatedAt": "2024-11-21T11:30:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 503,
"totalPages": 26
},
"summary": {
"totalSkus": 503,
"totalQuantity": 48320,
"lowStockCount": 12
}
}재고 단건 조회
GET /api/v1/inventory/:idResponse 200 OK
{
"id": "inv_01HXK6TZPQ7MND",
"warehouseId": "wh_01",
"warehouseName": "서울 물류센터",
"productId": "prod_A01",
"productName": "제품 A",
"sku": "SKU-A-001",
"category": "전자부품",
"locationId": "loc_A-01-01",
"locationCode": "A-01-01",
"zone": "A",
"status": "AVAILABLE",
"quantity": 196,
"reservedQty": 50,
"availableQty": 146,
"safetyStock": 30,
"lotNo": "LOT2024112101",
"expiryDate": "2025-11-21",
"unitCost": 12500,
"totalValue": 2450000,
"inboundId": "inb_01HXK4RNDQ8WPZ",
"createdAt": "2024-11-21T11:30:00Z",
"updatedAt": "2024-11-21T11:30:00Z"
}상품별 재고 요약 조회
GET /api/v1/inventory/summary/by-product상품 단위로 전체 창고의 재고를 집계합니다.
Query Parameters
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
warehouseId | string | 아니오 | 창고 필터 |
categoryId | string | 아니오 | 카테고리 필터 |
Response 200 OK
{
"data": [
{
"productId": "prod_A01",
"productName": "제품 A",
"sku": "SKU-A-001",
"totalQuantity": 560,
"totalReservedQty": 120,
"totalAvailableQty": 440,
"safetyStock": 100,
"isLowStock": false,
"warehouseBreakdown": [
{
"warehouseId": "wh_01",
"warehouseName": "서울 물류센터",
"quantity": 350
},
{
"warehouseId": "wh_02",
"warehouseName": "인천 물류센터",
"quantity": 210
}
]
}
]
}재고 이동
POST /api/v1/inventory/move동일 창고 내에서 재고를 다른 위치로 이동합니다.
권한:
ADMIN,MANAGER,OPERATOR
Request Body
{
"inventoryId": "inv_01HXK6TZPQ7MND",
"toLocationId": "loc_B-03-02",
"quantity": 50,
"reason": "공간 최적화"
}| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
inventoryId | string | 예 | 이동할 재고 ID |
toLocationId | string | 예 | 이동 목적지 위치 ID |
quantity | number | 예 | 이동 수량 |
reason | string | 아니오 | 이동 사유 |
Response 200 OK
{
"fromInventory": {
"id": "inv_01HXK6TZPQ7MND",
"locationId": "loc_A-01-01",
"quantity": 146
},
"toInventory": {
"id": "inv_01HXK9NEWMOV01",
"locationId": "loc_B-03-02",
"quantity": 50
},
"movedAt": "2024-11-21T13:00:00Z"
}재고 조정
POST /api/v1/inventory/adjust실사 또는 오류 수정 목적으로 재고 수량을 직접 조정합니다.
권한:
ADMIN,MANAGER
Request Body
{
"inventoryId": "inv_01HXK6TZPQ7MND",
"adjustedQty": 190,
"reason": "실사 결과 반영",
"adjustmentType": "STOCK_COUNT"
}| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
inventoryId | string | 예 | 조정할 재고 ID |
adjustedQty | number | 예 | 조정 후 수량 (0 이상) |
reason | string | 예 | 조정 사유 |
adjustmentType | string | 예 | 조정 유형 |
조정 유형 (adjustmentType)
| 값 | 설명 |
|---|---|
STOCK_COUNT | 실사 조정 |
DAMAGE | 파손 처리 |
RETURN | 반품 입고 |
CORRECTION | 시스템 오류 수정 |
Response 200 OK
{
"id": "inv_01HXK6TZPQ7MND",
"previousQty": 196,
"adjustedQty": 190,
"difference": -6,
"adjustmentType": "STOCK_COUNT",
"adjustedAt": "2024-11-21T14:00:00Z",
"adjustedBy": "홍길동"
}재고 상태 변경
PATCH /api/v1/inventory/:id/status재고의 상태를 변경합니다 (예: 정상→불량, 보류→정상).
Request Body
{
"status": "QUARANTINED",
"reason": "품질 이슈 확인 필요",
"quantity": 10
}Response 200 OK
{
"id": "inv_01HXK6TZPQ7MND",
"status": "QUARANTINED",
"quarantinedQty": 10,
"updatedAt": "2024-11-21T14:30:00Z"
}재고 이력 조회
GET /api/v1/inventory/:id/history특정 재고 항목의 변동 이력을 조회합니다.
Response 200 OK
{
"inventoryId": "inv_01HXK6TZPQ7MND",
"history": [
{
"id": "hist_001",
"type": "INBOUND",
"quantityBefore": 0,
"quantityChange": 196,
"quantityAfter": 196,
"referenceId": "inb_01HXK4RNDQ8WPZ",
"referenceNo": "INB-2024-001234",
"memo": "입고 완료",
"createdAt": "2024-11-21T11:30:00Z",
"createdBy": "김영수"
},
{
"id": "hist_002",
"type": "RESERVED",
"quantityBefore": 196,
"quantityChange": -50,
"quantityAfter": 146,
"referenceId": "out_01HXK8MK3P2QW",
"referenceNo": "OUT-2024-000892",
"memo": "출고 예약",
"createdAt": "2024-11-21T13:00:00Z",
"createdBy": "시스템"
}
]
}오류 코드
| 코드 | HTTP 상태 | 설명 |
|---|---|---|
INVENTORY_NOT_FOUND | 404 | 재고 없음 |
INSUFFICIENT_STOCK | 422 | 재고 부족 |
LOCATION_NOT_FOUND | 404 | 위치 없음 |
LOCATION_CAPACITY_EXCEEDED | 422 | 위치 용량 초과 |
INVALID_STATUS_TRANSITION | 422 | 허용되지 않는 상태 전환 |
RESERVED_STOCK_CANNOT_ADJUST | 409 | 예약 재고 조정 불가 |
출고
상품의 창고 출고 처리를 위한 API입니다. 출고 지시부터 피킹, 패킹, 배송 처리까지의 프로세스를 관리합니다.
Base URL
/api/v1/outbound
출고 상태(Status)
PENDING → CONFIRMED → PICKING → PACKING → READY → SHIPPED → CANCELLED
| 상태 | 설명 |
|---|---|
PENDING | 출고 요청 대기 |
CONFIRMED | 출고 확정 (재고 예약 완료) |
PICKING | 피킹 진행 중 |
PACKING | 패킹 진행 중 |
READY | 배송 대기 (출하 준비 완료) |
SHIPPED | 배송 완료 |
CANCELLED | 출고 취소 |
출고 우선순위(Priority)
| 값 | 설명 |
|---|---|
HIGH | 긴급 처리 |
NORMAL | 일반 처리 |
LOW | 배치 처리 |
엔드포인트
출고 목록 조회
GET /api/v1/outboundQuery Parameters
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
page | number | 아니오 | 페이지 번호 (기본값: 1) |
limit | number | 아니오 | 페이지당 항목 수 (기본값: 20) |
status | string | 아니오 | 상태 필터 |
priority | string | 아니오 | 우선순위 필터 |
warehouseId | string | 아니오 | 창고 ID 필터 |
customerId | string | 아니오 | 고객 ID 필터 |
requestedFrom | string | 아니오 | 출고 요청일 시작 (ISO 8601) |
requestedTo | string | 아니오 | 출고 요청일 종료 (ISO 8601) |
shippingDate | string | 아니오 | 출하 예정일 (YYYY-MM-DD) |
Response 200 OK
{
"data": [
{
"id": "out_01HXK8MK3P2QWVN",
"orderNo": "OUT-2024-000892",
"warehouseId": "wh_01",
"warehouseName": "서울 물류센터",
"customerId": "cust_001",
"customerName": "ABC 마트",
"status": "PICKING",
"priority": "HIGH",
"requestedAt": "2024-11-21T08:00:00Z",
"scheduledShipAt": "2024-11-21T17:00:00Z",
"totalItems": 3,
"totalQuantity": 75,
"assignedTo": "이피킹",
"createdAt": "2024-11-21T08:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 67,
"totalPages": 4
}
}출고 단건 조회
GET /api/v1/outbound/:idResponse 200 OK
{
"id": "out_01HXK8MK3P2QWVN",
"orderNo": "OUT-2024-000892",
"externalOrderNo": "ORD-ABCMART-20241121-009",
"warehouseId": "wh_01",
"warehouseName": "서울 물류센터",
"customerId": "cust_001",
"customerName": "ABC 마트",
"shippingAddress": {
"recipient": "ABC 마트 물류팀",
"address1": "경기도 고양시 일산동구 장항동 123",
"address2": "물류창고 3동",
"zipCode": "10401",
"phone": "031-123-4567"
},
"status": "PICKING",
"priority": "HIGH",
"carrierId": "carr_01",
"carrierName": "한진택배",
"trackingNo": null,
"requestedAt": "2024-11-21T08:00:00Z",
"scheduledShipAt": "2024-11-21T17:00:00Z",
"shippedAt": null,
"memo": "오전 배송 필수",
"items": [
{
"id": "outi_001",
"productId": "prod_A01",
"productName": "제품 A",
"sku": "SKU-A-001",
"requestedQty": 50,
"pickedQty": 50,
"packedQty": 0,
"inventoryId": "inv_01HXK6TZPQ7MND",
"locationCode": "A-01-01",
"lotNo": "LOT2024112101"
}
],
"history": [
{
"status": "PENDING",
"changedAt": "2024-11-21T08:00:00Z",
"changedBy": "시스템"
},
{
"status": "CONFIRMED",
"changedAt": "2024-11-21T08:05:00Z",
"changedBy": "홍길동"
},
{
"status": "PICKING",
"changedAt": "2024-11-21T09:00:00Z",
"changedBy": "이피킹"
}
],
"createdAt": "2024-11-21T08:00:00Z",
"updatedAt": "2024-11-21T09:00:00Z"
}출고 등록
POST /api/v1/outbound권한:
ADMIN,MANAGER,OPERATOR
Request Body
{
"warehouseId": "wh_01",
"customerId": "cust_001",
"externalOrderNo": "ORD-ABCMART-20241121-010",
"priority": "NORMAL",
"scheduledShipAt": "2024-11-22T17:00:00Z",
"carrierId": "carr_01",
"shippingAddress": {
"recipient": "ABC 마트 물류팀",
"address1": "경기도 고양시 일산동구 장항동 123",
"zipCode": "10401",
"phone": "031-123-4567"
},
"memo": "파손 주의",
"items": [
{
"productId": "prod_A01",
"requestedQty": 30
},
{
"productId": "prod_B02",
"requestedQty": 20
}
]
}| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
warehouseId | string | 예 | 출고 창고 ID |
customerId | string | 예 | 고객 ID |
externalOrderNo | string | 아니오 | 외부 주문 번호 |
priority | string | 아니오 | 우선순위 (기본값: NORMAL) |
scheduledShipAt | string | 예 | 출하 예정 일시 |
carrierId | string | 아니오 | 택배사 ID |
shippingAddress | object | 예 | 배송지 정보 |
memo | string | 아니오 | 비고 |
items | array | 예 | 출고 상품 목록 (최소 1개) |
Response 201 Created
{
"id": "out_01HXKA3BNCP5RT",
"orderNo": "OUT-2024-000893",
"status": "PENDING",
"createdAt": "2024-11-21T16:00:00Z"
}출고 확정
POST /api/v1/outbound/:id/confirm출고를 확정하고 재고를 예약합니다. 재고 부족 시 실패합니다.
Response 200 OK
{
"id": "out_01HXKA3BNCP5RT",
"status": "CONFIRMED",
"items": [
{
"id": "outi_001",
"productId": "prod_A01",
"requestedQty": 30,
"availableQty": 146,
"inventoryId": "inv_01HXK6TZPQ7MND",
"locationCode": "A-01-01"
}
],
"confirmedAt": "2024-11-21T16:05:00Z"
}피킹 시작
POST /api/v1/outbound/:id/start-pickingRequest Body
{
"assignedTo": "usr_01HXK9ZTRBP2QM"
}Response 200 OK
{
"id": "out_01HXKA3BNCP5RT",
"status": "PICKING",
"assignedTo": "김영수",
"pickingStartedAt": "2024-11-21T16:10:00Z",
"pickingList": [
{
"itemId": "outi_001",
"locationCode": "A-01-01",
"productName": "제품 A",
"sku": "SKU-A-001",
"requestedQty": 30,
"lotNo": "LOT2024112101"
}
]
}피킹 완료
POST /api/v1/outbound/:id/complete-pickingRequest Body
{
"items": [
{
"id": "outi_001",
"pickedQty": 30
}
]
}Response 200 OK
{
"id": "out_01HXKA3BNCP5RT",
"status": "PACKING",
"pickingCompletedAt": "2024-11-21T16:30:00Z"
}패킹 완료
POST /api/v1/outbound/:id/complete-packingRequest Body
{
"items": [
{
"id": "outi_001",
"packedQty": 30
}
],
"packageCount": 2,
"totalWeightKg": 15.5,
"memo": "박스 2개"
}Response 200 OK
{
"id": "out_01HXKA3BNCP5RT",
"status": "READY",
"packageCount": 2,
"totalWeightKg": 15.5,
"packingCompletedAt": "2024-11-21T16:50:00Z"
}출하 처리 (배송 완료)
POST /api/v1/outbound/:id/ship실제 배송이 시작되었음을 등록합니다. 재고가 최종 차감됩니다.
Request Body
{
"trackingNo": "1234567890123",
"shippedAt": "2024-11-21T17:05:00Z"
}Response 200 OK
{
"id": "out_01HXKA3BNCP5RT",
"status": "SHIPPED",
"trackingNo": "1234567890123",
"carrierId": "carr_01",
"carrierName": "한진택배",
"shippedAt": "2024-11-21T17:05:00Z"
}출고 취소
POST /api/v1/outbound/:id/cancel
SHIPPED상태에서는 취소 불가.CONFIRMED이후 취소 시 예약 재고가 자동 해제됩니다.
Request Body
{
"reason": "고객 주문 취소 요청"
}Response 200 OK
{
"id": "out_01HXKA3BNCP5RT",
"status": "CANCELLED",
"cancelledAt": "2024-11-21T16:00:00Z",
"reason": "고객 주문 취소 요청"
}오류 코드
| 코드 | HTTP 상태 | 설명 |
|---|---|---|
OUTBOUND_NOT_FOUND | 404 | 출고 건 없음 |
INSUFFICIENT_STOCK | 422 | 재고 부족으로 확정 불가 |
INVALID_STATUS_TRANSITION | 422 | 허용되지 않는 상태 전환 |
ALREADY_SHIPPED | 409 | 이미 배송 완료된 출고 건 |
PICKING_QTY_MISMATCH | 422 | 피킹 수량 불일치 |
CUSTOMER_NOT_FOUND | 404 | 고객 없음 |
WAREHOUSE_NOT_FOUND | 404 | 창고 없음 |