Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

292 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Procurement Backend

์กฐ๋‹ฌ ์‹œ์Šคํ…œ ๋ฐฑ์—”๋“œ API ์„œ๋ฒ„์ž…๋‹ˆ๋‹ค. ๊ณต๊ธ‰์‚ฌ(Vendor) ๊ด€๋ฆฌ์™€ ๋ฐœ์ฃผ์„œ(PO) ๋„๋ฉ”์ธ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ์ˆ  ์Šคํƒ

๋ถ„๋ฅ˜ ๊ธฐ์ˆ 
Language / Framework Java 25 / Spring Boot 4
ORM Spring Data JPA + PostgreSQL
API ๋ฌธ์„œ Springdoc OpenAPI (Swagger UI)
์ž…๋ ฅ ๊ฒ€์ฆ Bean Validation (@Valid, @Validated)
์—๋Ÿฌ ์‘๋‹ต ProblemDetail ๊ธฐ๋ฐ˜ (RFC 9457)

API ๋ชฉ๋ก ์š”์•ฝ

Vendor (๊ณต๊ธ‰์‚ฌ ๊ด€๋ฆฌ)

# Method Endpoint ๊ธฐ๋Šฅ ์‘๋‹ต ์ฝ”๋“œ
1 GET /api/vendors/stats ๊ณต๊ธ‰์‚ฌ ์ „์ฒด ํ†ต๊ณ„ ์กฐํšŒ 200
2 GET /api/vendors/{vendorCode}/stats ๊ณต๊ธ‰์‚ฌ ์ƒ์„ธ ํ†ต๊ณ„ ์กฐํšŒ 200, 404
3 GET /api/vendors ๊ณต๊ธ‰์‚ฌ ๋ชฉ๋ก ์กฐํšŒ (๊ฒ€์ƒ‰ยทํ•„ํ„ฐยท์ •๋ ฌยทํŽ˜์ด์ง•) 200, 400
4 POST /api/vendors ๊ณต๊ธ‰์‚ฌ ๋“ฑ๋ก 201, 400
5 GET /api/vendors/{vendorCode} ๊ณต๊ธ‰์‚ฌ ์ƒ์„ธ ์กฐํšŒ 200, 404
6 PUT /api/vendors/{vendorCode} ๊ณต๊ธ‰์‚ฌ ์ •๋ณด ์ˆ˜์ • 204, 400, 404, 422
7 PATCH /api/vendors/{vendorCode}/active ๊ณต๊ธ‰์‚ฌ ํ™œ์„ฑ ์—ฌ๋ถ€ ๋ณ€๊ฒฝ 204, 400, 404, 422

PO (๋ฐœ์ฃผ์„œ ๊ด€๋ฆฌ)

# Method Endpoint ๊ธฐ๋Šฅ ์‘๋‹ต ์ฝ”๋“œ
1 POST /api/purchase-orders ๋ฐœ์ฃผ ๋“ฑ๋ก (DRAFT ๋˜๋Š” CONFIRMED) 201, 400, 401, 403, 404, 422
2 PUT /api/purchase-orders/{poNumber} ๋ฐœ์ฃผ ์ˆ˜์ • (DRAFT ์ „์šฉ) 204, 400, 401, 403, 404, 422
3 DELETE /api/purchase-orders/{poNumber} ๋ฐœ์ฃผ ์‚ญ์ œ (DRAFT ์ „์šฉ, ๋ฌผ๋ฆฌ ์‚ญ์ œ) 204, 400, 401, 403, 404, 422

๊ณตํ†ต ์—๋Ÿฌ ์‘๋‹ต ํ˜•์‹

๋ชจ๋“  ์—๋Ÿฌ๋Š” ProblemDetail ํ˜•์‹์œผ๋กœ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค. code ์™€ timestamp ํ•„๋“œ๊ฐ€ ํ•ญ์ƒ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

{
  "status": 404,
  "detail": "๊ณต๊ธ‰์‚ฌ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: VD-999",
  "code": "VENDOR_NOT_FOUND",
  "timestamp": "2026-06-05T10:30:00+09:00"
}

์—๋Ÿฌ ์ฝ”๋“œ ๋ชฉ๋ก

code HTTP ๋ฐœ์ƒ ์กฐ๊ฑด
VENDOR_NOT_FOUND 404 ํ•ด๋‹น ์ฝ”๋“œ์˜ ๊ณต๊ธ‰์‚ฌ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์„ ๋•Œ
VENDOR_INACTIVE 422 ๋น„ํ™œ์„ฑ ๊ณต๊ธ‰์‚ฌ๋ฅผ ์ˆ˜์ •ํ•˜๋ ค ํ•  ๋•Œ
VENDOR_HAS_ACTIVE_PO 422 ์ง„ํ–‰ ์ค‘ PO(DRAFT/CONFIRMED)๊ฐ€ ์žˆ๋Š” ๊ณต๊ธ‰์‚ฌ๋ฅผ ๋น„ํ™œ์„ฑํ™”ํ•˜๋ ค ํ•  ๋•Œ
VENDOR_DUPLICATE 409 ๊ณต๊ธ‰์‚ฌ ์ฝ”๋“œ UNIQUE ์ œ์•ฝ ์œ„๋ฐ˜
PO_NOT_FOUND 404 ํ•ด๋‹น ๋ฒˆํ˜ธ์˜ ๋ฐœ์ฃผ์„œ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์„ ๋•Œ
PO_INVALID_STATUS 422 ํ—ˆ์šฉ๋˜์ง€ ์•Š๋Š” PO ์ƒํƒœ์—์„œ ์ˆ˜์ •ยท์‚ญ์ œ๋ฅผ ์‹œ๋„ํ•  ๋•Œ (DRAFT ์•„๋‹˜)
PO_VENDOR_INACTIVE 422 ๋น„ํ™œ์„ฑ ๊ณต๊ธ‰์‚ฌ์— ๋ฐœ์ฃผํ•˜๋ ค ํ•  ๋•Œ
PO_ITEM_NOT_FOUND 422 ์ฃผ๋ฌธ ๋ผ์ธ์˜ SKU์— ํ•ด๋‹นํ•˜๋Š” ๋ถ€ํ’ˆ์ด ์กด์žฌํ•˜์ง€ ์•Š์„ ๋•Œ
PO_ITEM_INACTIVE 422 ๋น„ํ™œ์„ฑ ๋ถ€ํ’ˆ์œผ๋กœ ๋ฐœ์ฃผํ•˜๋ ค ํ•  ๋•Œ
PO_WAREHOUSE_INVALID 422 ๋‚ฉํ’ˆ ์ฐฝ๊ณ  ์ฝ”๋“œ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์„ ๋•Œ
PO_DUPLICATE_SKU 422 ํ•œ ๋ฐœ์ฃผ์˜ ์ฃผ๋ฌธ ๋ผ์ธ์— ๋™์ผ SKU๊ฐ€ ์ค‘๋ณต๋  ๋•Œ
PO_DUPLICATE 409 ๋ฐœ์ฃผ ๋ฒˆํ˜ธ UNIQUE ์ œ์•ฝ ์œ„๋ฐ˜
COMMON_INVALID_INPUT 400 ํ•„๋“œยทํŒŒ๋ผ๋ฏธํ„ฐ ๊ฒ€์ฆ ์‹คํŒจ (@Valid ๋ฐ”๋”” / @Validated PathยทQuery)
COMMON_BAD_REQUEST 400 ๊ทธ ์™ธ ์ž˜๋ชป๋œ ์š”์ฒญ (405ยท415ยท๊นจ์ง„ JSON ๋“ฑ ๋ฏธ๋ถ„๋ฅ˜)
COMMON_INTERNAL_ERROR 500 ์ฒ˜๋ฆฌ๋˜์ง€ ์•Š์€ ์„œ๋ฒ„ ๋‚ด๋ถ€ ์˜ค๋ฅ˜

API ์ƒ์„ธ

1. GET /api/vendors/stats โ€” ๊ณต๊ธ‰์‚ฌ ์ „์ฒด ํ†ต๊ณ„ ์กฐํšŒ

๊ณต๊ธ‰์‚ฌ ๋Œ€์‹œ๋ณด๋“œ ์ƒ๋‹จ ์นด๋“œ ์˜์—ญ์— ํ‘œ์‹œํ•  ์ง‘๊ณ„ ์ˆ˜์น˜๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

  • ์ „์ฒด / ํ™œ์„ฑ / ๋น„ํ™œ์„ฑ ๊ณต๊ธ‰์‚ฌ ์ˆ˜
  • ์ง„ํ–‰ ์ค‘ PO(DRAFT + CONFIRMED) ๊ฑด์ˆ˜ ๋ฐ ์ด ๊ธˆ์•ก

Request โ€” ํŒŒ๋ผ๋ฏธํ„ฐ ์—†์Œ

Response 200

{
  "totalCount": 20,
  "activeCount": 18,
  "inactiveCount": 2,
  "activePoCount": 5,
  "activePoAmount": 150000000
}
ํ•„๋“œ ํƒ€์ž… ์„ค๋ช…
totalCount long ์ „์ฒด ๊ณต๊ธ‰์‚ฌ ์ˆ˜
activeCount long ํ™œ์„ฑ ๊ณต๊ธ‰์‚ฌ ์ˆ˜
inactiveCount long ๋น„ํ™œ์„ฑ ๊ณต๊ธ‰์‚ฌ ์ˆ˜ (total - active)
activePoCount long ์ง„ํ–‰ ์ค‘ PO ๊ฑด์ˆ˜ (DRAFT + CONFIRMED)
activePoAmount long ์ง„ํ–‰ ์ค‘ PO ์ด ๊ธˆ์•ก (์› ๋‹จ์œ„)

2. GET /api/vendors/{vendorCode}/stats โ€” ๊ณต๊ธ‰์‚ฌ ์ƒ์„ธ ํ†ต๊ณ„ ์กฐํšŒ

ํŠน์ • ๊ณต๊ธ‰์‚ฌ์˜ ๊ฑฐ๋ž˜ ํ˜„ํ™ฉ์„ ์š”์•ฝํ•œ ํ†ต๊ณ„๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๊ณต๊ธ‰์‚ฌ ์ƒ์„ธ ํŽ˜์ด์ง€์˜ ํ†ต๊ณ„ ์นด๋“œ ์˜์—ญ์—์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

Path Parameter

ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž… ์˜ˆ์‹œ ์„ค๋ช…
vendorCode String VD-014 ๊ณต๊ธ‰์‚ฌ ์ฝ”๋“œ (ํ•„์ˆ˜, ๊ณต๋ฐฑ ๋ถˆ๊ฐ€)

Response 200

{
  "activePo": 3,
  "tradeAmount30d": 92400000,
  "avgLeadTimeDays": 5.2
}
ํ•„๋“œ ํƒ€์ž… ์„ค๋ช…
activePo long ํ•ด๋‹น ๊ณต๊ธ‰์‚ฌ์˜ ์ง„ํ–‰ ์ค‘ PO ์ˆ˜ (DRAFT + CONFIRMED)
tradeAmount30d long ์ตœ๊ทผ 30์ผ ๊ฑฐ๋ž˜์•ก. CONFIRMED + RECEIVED ์ƒํƒœ PO๋งŒ ์ง‘๊ณ„ (DRAFT, CANCELED ์ œ์™ธ)
avgLeadTimeDays Double (nullable) ํ‰๊ท  ๋ฆฌ๋“œํƒ€์ž„(์ผ). RECEIVED ์ƒํƒœ PO์˜ orderedAt โ†’ receivedAt ๊ธฐ์ค€. RECEIVED PO๊ฐ€ ์—†์œผ๋ฉด null ๋ฐ˜ํ™˜

Error

์ƒํƒœ code ์„ค๋ช…
404 VENDOR_NOT_FOUND ํ•ด๋‹น ์ฝ”๋“œ์˜ ๊ณต๊ธ‰์‚ฌ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Œ

3. GET /api/vendors โ€” ๊ณต๊ธ‰์‚ฌ ๋ชฉ๋ก ์กฐํšŒ (๊ฒ€์ƒ‰ยทํ•„ํ„ฐยท์ •๋ ฌยทํŽ˜์ด์ง•)

๋“ฑ๋ก๋œ ๊ณต๊ธ‰์‚ฌ๋ฅผ ๊ฒ€์ƒ‰ยทํ•„ํ„ฐยท์ •๋ ฌ๊ณผ ํ•จ๊ป˜ offset ํŽ˜์ด์ง€ ๋‹จ์œ„๋กœ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.

Query Parameters

ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž… ๊ธฐ๋ณธ๊ฐ’ ์„ค๋ช…
page int 0 ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ (0-based, 0 ์ด์ƒ)
size int 10 ํŽ˜์ด์ง€๋‹น ํ•ญ๋ชฉ ์ˆ˜ (1~100)
search String โ€” ๊ณต๊ธ‰์‚ฌ๋ช…ยท์ฝ”๋“œ ๋ถ€๋ถ„ ๊ฒ€์ƒ‰ (๋ฏธ์ž…๋ ฅ ์‹œ ์ „์ฒด)
active Boolean โ€” ํ™œ์„ฑ ์—ฌ๋ถ€ ํ•„ํ„ฐ (๋ฏธ์ž…๋ ฅ ์‹œ ์ „์ฒด)
sort String recentTrade ์ •๋ ฌ ๊ธฐ์ค€. recentTrade(์ตœ๊ทผ ๊ฑฐ๋ž˜์ˆœ) ๋˜๋Š” code(์ฝ”๋“œ ์˜ค๋ฆ„์ฐจ์ˆœ)

recentTrade๋Š” ๊ฐ ๊ณต๊ธ‰์‚ฌ์˜ ๊ฐ€์žฅ ์ตœ๊ทผ ๋ฐœ์ฃผ(orderedAt) ๊ธฐ์ค€ ๋‚ด๋ฆผ์ฐจ์ˆœ์ด๋ฉฐ, ๊ฑฐ๋ž˜ ์ด๋ ฅ์ด ์—†๋Š” ๊ณต๊ธ‰์‚ฌ๋Š” ๋’ค๋กœ ์ •๋ ฌ๋ฉ๋‹ˆ๋‹ค(NULLS LAST).

Response 200

{
  "content": [
    {
      "id": 1,
      "code": "VD-001",
      "name": "(์ฃผ)๋™์„ฑ์ •๋ฐ€",
      "contactPerson": "๊น€์ƒํ˜ธ",
      "phone": "02-1234-5678",
      "address": "๊ฒฝ๊ธฐ ์•ˆ์‚ฐ์‹œ ๋‹จ์›๊ตฌ ๋ณ„๋ง๋กœ 178",
      "active": true,
      "createdAt": "2026-05-01T09:00:00+09:00"
    }
  ],
  "page": 0,
  "size": 10,
  "totalElements": 20,
  "totalPages": 2
}

Error

์ƒํƒœ code ์„ค๋ช…
400 COMMON_INVALID_INPUT size ๋ฒ”์œ„(1~100) ์ดˆ๊ณผ, sort ๋ฏธ์ •์˜ ๊ฐ’, page ์Œ์ˆ˜

4. POST /api/vendors โ€” ๊ณต๊ธ‰์‚ฌ ๋“ฑ๋ก

์ƒˆ๋กœ์šด ๊ณต๊ธ‰์‚ฌ๋ฅผ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค. ๊ณต๊ธ‰์‚ฌ ์ฝ”๋“œ(code)๋Š” ์„œ๋ฒ„์—์„œ ์ž๋™ ์ฑ„๋ฒˆ๋˜๋ฉฐ (VD-001, VD-002, โ€ฆ ํ˜•์‹), ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ง€์ •ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์‹ ๊ทœ ๋“ฑ๋ก๋œ ๊ณต๊ธ‰์‚ฌ์˜ active ๊ธฐ๋ณธ๊ฐ’์€ true์ž…๋‹ˆ๋‹ค.

Request Body

{
  "name": "(์ฃผ)๋™์„ฑ์ •๋ฐ€",
  "contactPerson": "๊น€์ƒํ˜ธ",
  "phone": "02-1234-5678",
  "address": "๊ฒฝ๊ธฐ ์•ˆ์‚ฐ์‹œ ๋‹จ์›๊ตฌ ๋ณ„๋ง๋กœ 178"
}
ํ•„๋“œ ํƒ€์ž… ํ•„์ˆ˜ ๊ฒ€์ฆ ๊ทœ์น™
name String โœ… ๊ณต๋ฐฑ ๋ถˆ๊ฐ€
contactPerson String โœ… ๊ณต๋ฐฑ ๋ถˆ๊ฐ€
phone String โœ… 0XX-XXXX-XXXX ๋˜๋Š” 0X-XXX-XXXX ํ˜•์‹
address String โœ… ๊ณต๋ฐฑ ๋ถˆ๊ฐ€

Response 201 โ€” ์ƒ์„ฑ๋œ ๊ณต๊ธ‰์‚ฌ ์ •๋ณด (VendorResponse)

Error

์ƒํƒœ code ์„ค๋ช…
400 COMMON_INVALID_INPUT ํ•„๋“œ ๊ฒ€์ฆ ์‹คํŒจ (์ƒ์„ธ ๋ฉ”์‹œ์ง€ ํฌํ•จ)

5. GET /api/vendors/{vendorCode} โ€” ๊ณต๊ธ‰์‚ฌ ์ƒ์„ธ ์กฐํšŒ

๊ณต๊ธ‰์‚ฌ ๊ธฐ๋ณธ ์ •๋ณด์™€ ํ•จ๊ป˜ ํ•ด๋‹น ๊ณต๊ธ‰์‚ฌ์˜ ์ตœ๊ทผ PO ์ตœ๋Œ€ 6๊ฑด์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. PO ๋ชฉ๋ก์€ createdAt ๊ธฐ์ค€ ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ์ž…๋‹ˆ๋‹ค.

Path Parameter

ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž… ์˜ˆ์‹œ
vendorCode String VD-014

Response 200

{
  "id": 1,
  "code": "VD-001",
  "name": "(์ฃผ)๋™์„ฑ์ •๋ฐ€",
  "contactPerson": "๊น€์ƒํ˜ธ",
  "phone": "02-1234-5678",
  "address": "๊ฒฝ๊ธฐ ์•ˆ์‚ฐ์‹œ ๋‹จ์›๊ตฌ ๋ณ„๋ง๋กœ 178",
  "active": true,
  "recentPos": [
    {
      "poNumber": "PO-2026-0421",
      "status": "CONFIRMED",
      "totalAmount": 5000000,
      "orderedAt": "2026-05-20T14:32:00+09:00"
    }
  ],
  "createdAt": "2026-05-01T09:00:00+09:00",
  "updatedAt": "2026-05-20T15:00:00+09:00"
}
ํ•„๋“œ ์„ค๋ช…
recentPos ์ตœ๊ทผ PO ์ตœ๋Œ€ 6๊ฑด. poNumber, status, totalAmount, orderedAt ํฌํ•จ
updatedAt ๋งˆ์ง€๋ง‰ ์ˆ˜์ • ์ผ์‹œ (KST, ISO-8601)

Error

์ƒํƒœ code ์„ค๋ช…
404 VENDOR_NOT_FOUND ํ•ด๋‹น ์ฝ”๋“œ์˜ ๊ณต๊ธ‰์‚ฌ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Œ

6. PUT /api/vendors/{vendorCode} โ€” ๊ณต๊ธ‰์‚ฌ ์ •๋ณด ์ˆ˜์ •

๊ณต๊ธ‰์‚ฌ์˜ ๊ธฐ๋ณธ ์ •๋ณด(์ด๋ฆ„, ๋Œ€ํ‘œ์ž, ์ „ํ™”๋ฒˆํ˜ธ, ์ฃผ์†Œ)๋ฅผ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.

  • ๊ณต๊ธ‰์‚ฌ ์ฝ”๋“œ(code)๋Š” ์ˆ˜์ • ๋ถˆ๊ฐ€์ž…๋‹ˆ๋‹ค. ์ฝ”๋“œ๋Š” Path Variable๋กœ๋งŒ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • ๋น„ํ™œ์„ฑ(active: false) ์ƒํƒœ์˜ ๊ณต๊ธ‰์‚ฌ๋Š” ์ˆ˜์ •ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋จผ์ € ํ™œ์„ฑํ™” ํ›„ ์ˆ˜์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ์„ฑ๊ณต ์‹œ ์‘๋‹ต ๋ณธ๋ฌธ ์—†์ด 204 No Content๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

Path Parameter

ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž… ์˜ˆ์‹œ
code String VD-003

Request Body โ€” POST /api/vendors์˜ Request Body์™€ ๋™์ผํ•œ ํ•„๋“œ ๋ฐ ๊ฒ€์ฆ ๊ทœ์น™ ์ ์šฉ

Response 204 No Content

Error

์ƒํƒœ code ์„ค๋ช…
400 COMMON_INVALID_INPUT ํ•„๋“œ ๊ฒ€์ฆ ์‹คํŒจ
404 VENDOR_NOT_FOUND ํ•ด๋‹น ์ฝ”๋“œ์˜ ๊ณต๊ธ‰์‚ฌ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Œ
422 VENDOR_INACTIVE ๋น„ํ™œ์„ฑ ๊ณต๊ธ‰์‚ฌ๋Š” ์ˆ˜์ • ๋ถˆ๊ฐ€

7. PATCH /api/vendors/{vendorCode}/active โ€” ๊ณต๊ธ‰์‚ฌ ํ™œ์„ฑ ์—ฌ๋ถ€ ๋ณ€๊ฒฝ

๊ณต๊ธ‰์‚ฌ๋ฅผ ํ™œ์„ฑํ™”ํ•˜๊ฑฐ๋‚˜ ๋น„ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค.

  • ๋น„ํ™œ์„ฑํ™” ์‹œ ํ•ด๋‹น ๊ณต๊ธ‰์‚ฌ์— ์ง„ํ–‰ ์ค‘ PO(DRAFT ๋˜๋Š” CONFIRMED ์ƒํƒœ)๊ฐ€ 1๊ฑด์ด๋ผ๋„ ์กด์žฌํ•˜๋ฉด 422๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์ง„ํ–‰ ์ค‘์ธ ๋ฐœ์ฃผ ์ฒ˜๋ฆฌ๋ฅผ ๋ณดํ˜ธํ•˜๊ธฐ ์œ„ํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™์ž…๋‹ˆ๋‹ค.
  • ํ™œ์„ฑํ™” ์‹œ์—๋Š” ๋ณ„๋„ ์ œ์•ฝ ์—†์ด ์ฆ‰์‹œ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • ์„ฑ๊ณต ์‹œ ์‘๋‹ต ๋ณธ๋ฌธ ์—†์ด 204 No Content๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

Path Parameter

ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž… ์˜ˆ์‹œ
vendorCode String VD-014

Request Body

{ "active": false }
ํ•„๋“œ ํƒ€์ž… ํ•„์ˆ˜ ์„ค๋ช…
active Boolean โœ… true (ํ™œ์„ฑํ™”) / false (๋น„ํ™œ์„ฑํ™”)

Response 204 No Content

Error

์ƒํƒœ code ์„ค๋ช…
400 COMMON_INVALID_INPUT active ํ•„๋“œ ๋ˆ„๋ฝ
404 VENDOR_NOT_FOUND ํ•ด๋‹น ์ฝ”๋“œ์˜ ๊ณต๊ธ‰์‚ฌ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Œ
422 VENDOR_HAS_ACTIVE_PO ์ง„ํ–‰ ์ค‘ PO๊ฐ€ ์กด์žฌํ•˜์—ฌ ๋น„ํ™œ์„ฑํ™” ๋ถˆ๊ฐ€ (์—๋Ÿฌ ๋ฉ”์‹œ์ง€์— ๊ฑด์ˆ˜ ํฌํ•จ)

PO API ์ƒ์„ธ

๋ชจ๋“  PO API์˜ ๊ธฐ๋ณธ ๊ฒฝ๋กœ๋Š” /api/purchase-orders ์ž…๋‹ˆ๋‹ค. ๋ฐœ์ฃผ ์‹œ๊ฐ ํ•„๋“œ๋Š” ์‹œ๊ฐ„ ์ปจ๋ฒค์…˜์— ๋”ฐ๋ผ offset์ด ๋ช…์‹œ๋œ OffsetDateTime(KST, ISO-8601)์œผ๋กœ ์ฃผ๊ณ ๋ฐ›์Šต๋‹ˆ๋‹ค.

1. POST /api/purchase-orders โ€” ๋ฐœ์ฃผ ๋“ฑ๋ก

์‹ ๊ทœ ๋ฐœ์ฃผ๋ฅผ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.

  • confirm ๊ฐ’์— ๋”ฐ๋ผ ์ƒ์„ฑ ์ƒํƒœ๊ฐ€ ๊ฒฐ์ •๋ฉ๋‹ˆ๋‹ค. false๋ฉด DRAFT, true๋ฉด CONFIRMED(์Šน์ธ์žยท๋ฐœ์ฃผ์ผ์‹œ ๊ธฐ๋ก)๋กœ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
  • ๋ฐœ์ฃผ ๋ฒˆํ˜ธ(poNumber)๋Š” ์„œ๋ฒ„์—์„œ ์ž๋™ ์ฑ„๋ฒˆ๋ฉ๋‹ˆ๋‹ค. ํ˜•์‹์€ PO-{๋…„}-{4์ž๋ฆฌ ์ผ๋ จ๋ฒˆํ˜ธ} (์˜ˆ: PO-2026-0005)์ด๋ฉฐ, 9999 ์ดˆ๊ณผ ์‹œ ์ž๋ฆฟ์ˆ˜๊ฐ€ ์ž๋™ ํ™•์žฅ๋ฉ๋‹ˆ๋‹ค.
  • ๋“ฑ๋ก ๊ณผ์ •์—์„œ ๊ณต๊ธ‰์‚ฌยท๋‚ฉํ’ˆ ์ฐฝ๊ณ ยท๋ถ€ํ’ˆ(SKU)์˜ ์กด์žฌ ๋ฐ ํ™œ์„ฑ ์—ฌ๋ถ€๋ฅผ ๊ฒ€์ฆํ•˜๊ณ , ๋ถ€ํ’ˆ์˜ ์ด๋ฆ„ยท๋‹จ๊ฐ€๋ฅผ ์Šค๋ƒ…์ƒท์œผ๋กœ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

Request Body

{
  "vendorCode": "VD-001",
  "targetWarehouseCode": "WH-01",
  "expectedArrival": "2026-06-20T09:00:00+09:00",
  "note": "๊ธด๊ธ‰ ๋ฐœ์ฃผ ๊ฑด",
  "confirm": false,
  "lines": [
    { "sku": "SKU-1001", "quantity": 10 },
    { "sku": "SKU-1002", "quantity": 5 }
  ]
}
ํ•„๋“œ ํƒ€์ž… ํ•„์ˆ˜ ๊ฒ€์ฆ ๊ทœ์น™
vendorCode String โœ… ๊ณต๋ฐฑ ๋ถˆ๊ฐ€
targetWarehouseCode String โœ… ๊ณต๋ฐฑ ๋ถˆ๊ฐ€
expectedArrival OffsetDateTime โœ… ์˜ค๋Š˜ ์ดํ›„(ํ˜„์žฌ ํฌํ•จ)
note String โ€” 500์ž ์ดํ•˜
confirm Boolean โœ… false=DRAFT / true=CONFIRMED
lines Array โœ… ์ตœ์†Œ 1๊ฑด, ์ตœ๋Œ€ 100๊ฑด
lines[].sku String โœ… ๊ณต๋ฐฑ ๋ถˆ๊ฐ€, ๋ผ์ธ ๊ฐ„ ์ค‘๋ณต ๋ถˆ๊ฐ€
lines[].quantity Integer โœ… 1 ์ด์ƒ

Response 201

{
  "poNumber": "PO-2026-0005",
  "status": "DRAFT",
  "vendorCode": "VD-001",
  "vendorName": "(์ฃผ)๋™์„ฑ์ •๋ฐ€",
  "targetWarehouseCode": "WH-01",
  "expectedArrival": "2026-06-20T09:00:00+09:00",
  "note": "๊ธด๊ธ‰ ๋ฐœ์ฃผ ๊ฑด",
  "totalAmount": 1500000,
  "lines": [
    {
      "lineNo": 1,
      "sku": "SKU-1001",
      "nameSnapshot": "๋ฒ ์–ด๋ง 6202",
      "unitPriceSnapshot": 100000,
      "quantity": 10
    }
  ],
  "createdAt": "2026-06-09T10:30:00+09:00"
}
ํ•„๋“œ ์„ค๋ช…
status ๋ฐœ์ฃผ ์ƒํƒœ (DRAFT / CONFIRMED)
vendorName ๋“ฑ๋ก ์‹œ์ ์˜ ๊ณต๊ธ‰์‚ฌ๋ช…
totalAmount ๋ผ์ธ๋ณ„ ๋‹จ๊ฐ€ ์Šค๋ƒ…์ƒท ร— ์ˆ˜๋Ÿ‰์˜ ํ•ฉ๊ณ„ (์› ๋‹จ์œ„)
lines[].nameSnapshot / unitPriceSnapshot ๋“ฑ๋ก ์‹œ์ ์˜ ๋ถ€ํ’ˆ ์ด๋ฆ„ยท๋‹จ๊ฐ€ ์Šค๋ƒ…์ƒท

Error

์ƒํƒœ code ์„ค๋ช…
400 COMMON_INVALID_INPUT ํ•„๋“œ ๊ฒ€์ฆ ์‹คํŒจ
404 VENDOR_NOT_FOUND ๊ณต๊ธ‰์‚ฌ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Œ
422 PO_VENDOR_INACTIVE ๋น„ํ™œ์„ฑ ๊ณต๊ธ‰์‚ฌ์—๋Š” ๋ฐœ์ฃผ ๋ถˆ๊ฐ€
422 PO_DUPLICATE_SKU ์ฃผ๋ฌธ ๋ผ์ธ์— ๋™์ผ SKU ์ค‘๋ณต
422 PO_WAREHOUSE_INVALID ๋‚ฉํ’ˆ ์ฐฝ๊ณ ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Œ
422 PO_ITEM_NOT_FOUND ๋ถ€ํ’ˆ(SKU)์ด ์กด์žฌํ•˜์ง€ ์•Š์Œ
422 PO_ITEM_INACTIVE ๋น„ํ™œ์„ฑ ๋ถ€ํ’ˆ์œผ๋กœ๋Š” ๋ฐœ์ฃผ ๋ถˆ๊ฐ€

2. PUT /api/purchase-orders/{poNumber} โ€” ๋ฐœ์ฃผ ์ˆ˜์ •

DRAFT ์ƒํƒœ์ธ ๋ฐœ์ฃผ์˜ ์ •๋ณด๋ฅผ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.

  • DRAFT ์ƒํƒœ์—์„œ๋งŒ ์ˆ˜์ • ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. CONFIRMED ์ดํ›„ ์ƒํƒœ์—์„œ๋Š” 422๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. (์ƒํƒœ ๊ฒ€์ฆ์„ ์ž…๋ ฅ ์ž์› ๊ฒ€์ฆ๋ณด๋‹ค ๋จผ์ € ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.)
  • ์ฃผ๋ฌธ ๋ผ์ธ์€ ์ „์ฒด ๊ต์ฒด(replace)๋ฉ๋‹ˆ๋‹ค. ๊ธฐ์กด ๋ผ์ธ์„ ๋ชจ๋‘ ์ œ๊ฑฐํ•œ ๋’ค ์š”์ฒญ ๋ผ์ธ์œผ๋กœ ๋‹ค์‹œ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • ๋“ฑ๋ก๊ณผ ๋‹ฌ๋ฆฌ ์ƒํƒœ ์ „์ด๋ฅผ ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ confirm ํ•„๋“œ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.
  • ์„ฑ๊ณต ์‹œ ์‘๋‹ต ๋ณธ๋ฌธ ์—†์ด 204 No Content๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

Path Parameter

ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž… ์˜ˆ์‹œ ์„ค๋ช…
poNumber String PO-2026-0005 ๋ฐœ์ฃผ ๋ฒˆํ˜ธ (ํ•„์ˆ˜, ๊ณต๋ฐฑ ๋ถˆ๊ฐ€)

Request Body โ€” POST /api/purchase-orders์˜ Request Body์—์„œ confirm ํ•„๋“œ๋ฅผ ์ œ์™ธํ•œ ๋™์ผํ•œ ํ•„๋“œ ๋ฐ ๊ฒ€์ฆ ๊ทœ์น™ ์ ์šฉ

Response 204 No Content

Error

์ƒํƒœ code ์„ค๋ช…
400 COMMON_INVALID_INPUT ํ•„๋“œ ๊ฒ€์ฆ ์‹คํŒจ
404 PO_NOT_FOUND ๋ฐœ์ฃผ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Œ
404 VENDOR_NOT_FOUND ๊ณต๊ธ‰์‚ฌ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Œ
422 PO_INVALID_STATUS DRAFT ์ƒํƒœ๊ฐ€ ์•„๋‹ˆ์–ด์„œ ์ˆ˜์ • ๋ถˆ๊ฐ€
422 PO_VENDOR_INACTIVE ๋น„ํ™œ์„ฑ ๊ณต๊ธ‰์‚ฌ
422 PO_DUPLICATE_SKU ์ฃผ๋ฌธ ๋ผ์ธ์— ๋™์ผ SKU ์ค‘๋ณต
422 PO_WAREHOUSE_INVALID ๋‚ฉํ’ˆ ์ฐฝ๊ณ ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Œ
422 PO_ITEM_NOT_FOUND ๋ถ€ํ’ˆ(SKU)์ด ์กด์žฌํ•˜์ง€ ์•Š์Œ
422 PO_ITEM_INACTIVE ๋น„ํ™œ์„ฑ ๋ถ€ํ’ˆ

3. DELETE /api/purchase-orders/{poNumber} โ€” ๋ฐœ์ฃผ ์‚ญ์ œ

DRAFT ์ƒํƒœ์ธ ๋ฐœ์ฃผ๋ฅผ ๋ฌผ๋ฆฌ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.

  • DRAFT ์ƒํƒœ์—์„œ๋งŒ ์‚ญ์ œ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. CONFIRMED ์ดํ›„ ์ƒํƒœ์—์„œ๋Š” 422๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉฐ, ์ด ๊ฒฝ์šฐ ์ทจ์†Œ๋Š” cancel API๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • DB์—์„œ ๋ ˆ์ฝ”๋“œ๋ฅผ ์™„์ „ํžˆ ์ œ๊ฑฐํ•˜๋ฉฐ ์ด๋ ฅ์ด ๋‚จ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. (soft delete์ธ cancel๊ณผ ๊ตฌ๋ถ„)
  • ์„ฑ๊ณต ์‹œ ์‘๋‹ต ๋ณธ๋ฌธ ์—†์ด 204 No Content๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

Path Parameter

ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž… ์˜ˆ์‹œ ์„ค๋ช…
poNumber String PO-2026-0005 ๋ฐœ์ฃผ ๋ฒˆํ˜ธ (ํ•„์ˆ˜, ๊ณต๋ฐฑ ๋ถˆ๊ฐ€)

Response 204 No Content

Error

์ƒํƒœ code ์„ค๋ช…
400 COMMON_INVALID_INPUT poNumber ๋ˆ„๋ฝยท๊ณต๋ฐฑ
404 PO_NOT_FOUND ๋ฐœ์ฃผ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Œ
422 PO_INVALID_STATUS DRAFT ์ƒํƒœ๊ฐ€ ์•„๋‹ˆ์–ด์„œ ์‚ญ์ œ ๋ถˆ๊ฐ€

๊ณต๊ธ‰์‚ฌ(Vendor) ์ƒํƒœ ๋จธ์‹ 

๊ณต๊ธ‰์‚ฌ๋Š” active ํ”Œ๋ž˜๊ทธ๋กœ ํ™œ์„ฑ/๋น„ํ™œ์„ฑ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

ํ™œ์„ฑ(active: true) โ”€โ”€๋น„ํ™œ์„ฑํ™”โ”€โ”€โ–บ ๋น„ํ™œ์„ฑ(active: false)
       โ–ฒ                                  โ”‚
       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ํ™œ์„ฑํ™”โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
์ƒํƒœ ์„ค๋ช…
active: true ๋ฐœ์ฃผ ์ƒ์„ฑ ๊ฐ€๋Šฅ, ์ •๋ณด ์ˆ˜์ • ๊ฐ€๋Šฅ
active: false ๋ฐœ์ฃผ ์ƒ์„ฑ ๋ถˆ๊ฐ€, ์ •๋ณด ์ˆ˜์ • ๋ถˆ๊ฐ€

์ „์ด ๊ทœ์น™

์ „์ด ์กฐ๊ฑด ๋น„๊ณ 
ํ™œ์„ฑ โ†’ ๋น„ํ™œ์„ฑ ์ง„ํ–‰ ์ค‘ PO(DRAFT/CONFIRMED)๊ฐ€ 0๊ฑด์ผ ๋•Œ๋งŒ ๊ฐ€๋Šฅ PO ์กด์žฌ ์‹œ 422 VENDOR_HAS_ACTIVE_PO
๋น„ํ™œ์„ฑ โ†’ ํ™œ์„ฑ ์ œ์•ฝ ์—†์Œ

๋ฐœ์ฃผ์„œ(PO) ์ƒํƒœ ๋จธ์‹ 

๋“ฑ๋ก(POST)ยท์ˆ˜์ •(PUT)ยท์‚ญ์ œ(DELETE)์™€ ์ƒํƒœ ์ „์ด API(confirm/receive/cancel)๊ฐ€ ๋ชจ๋‘ ๊ตฌํ˜„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

DRAFT โ”€โ”€confirm()โ”€โ”€โ–บ CONFIRMED โ”€โ”€receive()โ”€โ”€โ–บ RECEIVED
  โ”‚                      โ”‚
  โ”‚                      โ””โ”€โ”€cancel()โ”€โ”€โ”€โ”€โ”€โ”€โ–บ CANCELED
  โ””โ”€โ”€delete() โ”€โ”€ ๋ฌผ๋ฆฌ ์‚ญ์ œ (DB์—์„œ ์™„์ „ ์ œ๊ฑฐ)

์ƒํƒœ ์„ค๋ช…

์ƒํƒœ ์„ค๋ช… IN_PROGRESS TRADE
DRAFT ์ž‘์„ฑ ์ค‘. ์•„์ง ๋ฐœ์ฃผ๋˜์ง€ ์•Š์€ ์ž„์‹œ ์ƒํƒœ โœ… โŒ
CONFIRMED ๋ฐœ์ฃผ ํ™•์ •. ์Šน์ธ์ž ์ •๋ณด ๋ฐ ๋ฐœ์ฃผ์ผ์‹œ ๊ธฐ๋ก โœ… โœ…
RECEIVED ์ž…๊ณ  ์™„๋ฃŒ. ๋ผ์ธ๋ณ„ ์ˆ˜๋ น ์ˆ˜๋Ÿ‰ ๊ธฐ๋ก โŒ โœ…
CANCELED ์ทจ์†Œ ์ฒ˜๋ฆฌ. DB์— ์ด๋ ฅ์œผ๋กœ ๋‚จ์Œ โŒ โŒ

IN_PROGRESS (DRAFT + CONFIRMED): ๊ณต๊ธ‰์‚ฌ ๋น„ํ™œ์„ฑํ™” ๊ฐ€๋Šฅ ์—ฌ๋ถ€ ํŒ๋‹จ ๋ฐ ์ง„ํ–‰ ์ค‘ PO ํ†ต๊ณ„์— ์‚ฌ์šฉ
TRADE (CONFIRMED + RECEIVED): ๊ฑฐ๋ž˜์•ก ์ง‘๊ณ„(tradeAmount30d) ์‚ฐ์ถœ์— ์‚ฌ์šฉ

์ „์ด ๊ทœ์น™

์ „์ด ์ถœ๋ฐœ ์ƒํƒœ ์„ค๋ช…
confirm() DRAFT CONFIRMED๋กœ ์ „์ด. ์Šน์ธ์ž ์ •๋ณด(approvedBy, approvedName)์™€ ๋ฐœ์ฃผ์ผ์‹œ(orderedAt) ๊ธฐ๋ก
receive() CONFIRMED RECEIVED๋กœ ์ „์ด. ์ž…๊ณ ์ผ์‹œ(receivedAt) ๊ธฐ๋ก. ๋ผ์ธ๋ณ„ ์ˆ˜๋ น ์ˆ˜๋Ÿ‰ ๋ฐ˜์˜
cancel() CONFIRMED CANCELED๋กœ ์ „์ด. DB์— ์ทจ์†Œ ์ด๋ ฅ์œผ๋กœ ๋ณด์กด๋จ (DRAFT๋Š” ์ทจ์†Œ ๋Œ€์ƒ ์•„๋‹˜, delete๋กœ ๋ฌผ๋ฆฌ ์‚ญ์ œ)
delete() DRAFT ์ „์šฉ DB์—์„œ ๋ฌผ๋ฆฌ ์‚ญ์ œ. ๋ชฉ๋ก์—์„œ ์™„์ „ํžˆ ์ œ๊ฑฐ. ์ด๋ ฅ ์—†์Œ

cancel() vs delete() ์ฐจ์ด

๊ตฌ๋ถ„ cancel delete
๋Œ€์ƒ ์ƒํƒœ CONFIRMED๋งŒ ๊ฐ€๋Šฅ DRAFT๋งŒ ๊ฐ€๋Šฅ
์ฒ˜๋ฆฌ ๋ฐฉ์‹ ์ƒํƒœ๋ฅผ CANCELED๋กœ ๋ณ€๊ฒฝ (์†Œํ”„ํŠธ ์‚ญ์ œ) DB์—์„œ ๋ ˆ์ฝ”๋“œ ์™„์ „ ์ œ๊ฑฐ (ํ•˜๋“œ ์‚ญ์ œ)
์ด๋ ฅ ๋ณด์กด โœ… ์ทจ์†Œ ์ด๋ ฅ ๋‚จ์Œ โŒ ํ”์  ์—†์ด ์‚ญ์ œ
์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค ํ™•์ •๋œ ๋ฐœ์ฃผ๋ฅผ ๋˜๋Œ๋ฆด ๋•Œ ์‹ค์ˆ˜๋กœ ๋งŒ๋“  ์ž„์‹œ ๋ฐœ์ฃผ์„œ(DRAFT)๋ฅผ ๋ชฉ๋ก์—์„œ ์™„์ „ํžˆ ์ œ๊ฑฐํ•  ๋•Œ

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages