- patient_query: 대체조제 원본 처방 제외 - rx_query: 대체조제 원본 처방 제외 - PS_Type=9는 대체조제시 원래 처방된 약(조제 안됨) - 기타 배치 스크립트 및 문서 추가
261 lines
7.1 KiB
Markdown
261 lines
7.1 KiB
Markdown
# API 개발 가이드 및 트러블슈팅
|
|
|
|
## 📋 목차
|
|
1. [도매상 주문 API 응답 형식](#도매상-주문-api-응답-형식)
|
|
2. [동원약품 API 버그 수정](#동원약품-api-버그-수정)
|
|
|
|
---
|
|
|
|
## 도매상 주문 API 응답 형식
|
|
|
|
### `/api/order/quick-submit` 응답 표준
|
|
|
|
모든 도매상(지오영, 수인, 백제, 동원)의 주문 응답은 **동일한 형식**을 따라야 합니다:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"dry_run": true,
|
|
"cart_only": false,
|
|
"order_id": 123,
|
|
"order_no": "ORD-20260308-001",
|
|
"wholesaler": "dongwon",
|
|
"total_items": 1,
|
|
"success_count": 1,
|
|
"failed_count": 0,
|
|
"results": [
|
|
{
|
|
"item_id": 456,
|
|
"drug_code": "643900470",
|
|
"product_name": "부루펜정200mg",
|
|
"specification": "500정(병)",
|
|
"order_qty": 1,
|
|
"status": "success",
|
|
"result_code": "OK",
|
|
"result_message": "[DRY RUN] 주문 가능: 재고 9, 단가 17,000원",
|
|
"price": 17000
|
|
}
|
|
],
|
|
"note": "장바구니에 담김. 도매상 사이트에서 최종 확정 필요."
|
|
}
|
|
```
|
|
|
|
### ⚠️ 필수 필드
|
|
|
|
| 필드 | 설명 | 비고 |
|
|
|------|------|------|
|
|
| `wholesaler` | 도매상 ID | 프론트엔드에서 결과 모달 표시에 사용 |
|
|
| `success_count` | 성공 개수 | 최상위 레벨에 있어야 함 (summary 안에만 있으면 안됨) |
|
|
| `failed_count` | 실패 개수 | 최상위 레벨에 있어야 함 |
|
|
| `order_no` | 주문번호 | 프론트엔드 결과 모달에 표시 |
|
|
|
|
---
|
|
|
|
## 동원약품 API 버그 수정
|
|
|
|
### 📅 수정일: 2026-03-08
|
|
|
|
### 🐛 문제
|
|
|
|
**증상:**
|
|
- 동원약품으로 주문하면 결과 모달에 "**지오영 주문 결과**"로 표시됨
|
|
- 성공/실패 개수가 "**undefined**"로 표시됨
|
|
|
|
**원인:**
|
|
`submit_dongwon_order()` 함수의 응답에 다음 필드가 누락됨:
|
|
1. `wholesaler` 필드 없음
|
|
2. `success_count`, `failed_count`가 `summary` 객체 안에만 있음 (최상위에 없음)
|
|
3. `order_no` 필드 없음
|
|
|
|
### 🔧 수정 내용
|
|
|
|
**파일:** `backend/order_api.py`
|
|
|
|
**수정 전 (dry_run 응답):**
|
|
```python
|
|
return {
|
|
'success': True,
|
|
'dry_run': True,
|
|
'results': results,
|
|
'summary': {
|
|
'total': len(items),
|
|
'success': success_count,
|
|
'failed': failed_count
|
|
}
|
|
}
|
|
```
|
|
|
|
**수정 후:**
|
|
```python
|
|
return {
|
|
'success': True,
|
|
'dry_run': dry_run,
|
|
'cart_only': cart_only,
|
|
'order_id': order_id,
|
|
'order_no': order['order_no'],
|
|
'wholesaler': 'dongwon',
|
|
'total_items': len(items),
|
|
'success_count': success_count,
|
|
'failed_count': failed_count,
|
|
'results': results
|
|
}
|
|
```
|
|
|
|
### ✅ 검증
|
|
|
|
테스트 절차:
|
|
1. `http://localhost:7001/admin/rx-usage` 접속
|
|
2. 테이블에서 약품 더블클릭 → 도매상 재고 모달 열기
|
|
3. 동원약품 섹션에서 "담기" 버튼 클릭
|
|
4. 장바구니에서 "주문서 생성하기" 클릭
|
|
5. "🧪 테스트" 버튼 클릭
|
|
6. 결과 모달에서 확인:
|
|
- 제목: "🏥 **동원약품** 주문 결과"
|
|
- 성공: "1개"
|
|
- 실패: "0개"
|
|
|
|
---
|
|
|
|
## 프론트엔드 장바구니 구조
|
|
|
|
### `addToCartFromWholesale()` 함수
|
|
|
|
동원약품에서 "담기" 버튼 클릭 시 장바구니에 추가되는 아이템 구조:
|
|
|
|
```javascript
|
|
const cartItem = {
|
|
drug_code: '643900470',
|
|
product_name: '부루펜정200mg',
|
|
supplier: '동원약품',
|
|
qty: 1,
|
|
specification: '500정(병)',
|
|
wholesaler: 'dongwon', // ← 필터링에 사용
|
|
internal_code: '16045',
|
|
dongwon_code: '16045', // ← 동원 API 호출에 사용
|
|
unit_price: 17000
|
|
};
|
|
```
|
|
|
|
### 도매상 필터링 로직
|
|
|
|
```javascript
|
|
const WHOLESALERS = {
|
|
dongwon: {
|
|
filterFn: (item) => item.supplier === '동원약품' || item.wholesaler === 'dongwon'
|
|
}
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## 📝 개발 시 체크리스트
|
|
|
|
새로운 도매상 API 추가 시:
|
|
|
|
- [ ] `submit_xxx_order()` 함수 응답에 `wholesaler` 필드 포함
|
|
- [ ] `success_count`, `failed_count` 최상위 레벨에 포함
|
|
- [ ] `order_no` 필드 포함
|
|
- [ ] 프론트엔드 `WHOLESALERS` 객체에 도매상 추가
|
|
- [ ] `filterFn` 함수 정의
|
|
- [ ] E2E 테스트 수행
|
|
|
|
---
|
|
|
|
## 주문량 조회 API (summary-by-kd)
|
|
|
|
### 📅 추가일: 2025-07-14
|
|
|
|
### 📋 개요
|
|
|
|
전문의약품 사용량 페이지(`/admin/rx-usage`)의 "주문량" 컬럼은 도매상별 주문량을 KD 코드 기준으로 합산하여 표시합니다.
|
|
|
|
### ⚠️ 필수 구현: `/orders/summary-by-kd` 엔드포인트
|
|
|
|
**새로운 도매상 추가 시 반드시 구현해야 합니다!**
|
|
|
|
#### 요청
|
|
|
|
```
|
|
GET /api/{wholesaler}/orders/summary-by-kd?start_date=2025-07-01&end_date=2025-07-14
|
|
```
|
|
|
|
#### 응답 형식 (표준)
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"order_count": 4,
|
|
"period": {
|
|
"start": "2025-07-01",
|
|
"end": "2025-07-14"
|
|
},
|
|
"by_kd_code": {
|
|
"670400830": {
|
|
"product_name": "레바미피드정100mg",
|
|
"spec": "100T",
|
|
"boxes": 2,
|
|
"units": 200
|
|
},
|
|
"643900470": {
|
|
"product_name": "부루펜정200mg",
|
|
"spec": "500정(병)",
|
|
"boxes": 1,
|
|
"units": 500
|
|
}
|
|
},
|
|
"total_products": 2
|
|
}
|
|
```
|
|
|
|
### 현재 구현 상태
|
|
|
|
| 도매상 | 엔드포인트 | KD 코드 집계 | 비고 |
|
|
|--------|------------|--------------|------|
|
|
| 지오영 | `/api/geoyoung/orders/summary-by-kd` | ✅ | 정상 작동 |
|
|
| 수인 | `/api/sooin/orders/summary-by-kd` | ✅ | 정상 작동 |
|
|
| 백제 | `/api/baekje/orders/summary-by-kd` | ✅ | 정상 작동 |
|
|
| 동원 | `/api/dongwon/orders/summary-by-kd` | ⚠️ | 주문 건수만 제공, 품목별 집계 불가 |
|
|
|
|
### 동원약품 한계
|
|
|
|
동원약품 API(`onLineOrderListAX`)는 주문 목록만 반환하고, 각 주문의 상세 품목(items)을 제공하지 않습니다.
|
|
|
|
**향후 개선 필요:**
|
|
- 동원 주문 상세 조회 API 탐색 필요
|
|
- 또는 주문 상세 페이지 크롤링 구현
|
|
|
|
### 프론트엔드 연동
|
|
|
|
`admin_rx_usage.html`의 `loadOrderData()` 함수:
|
|
|
|
```javascript
|
|
// 4사 병렬 조회
|
|
const [geoRes, sooinRes, baekjeRes, dongwonRes] = await Promise.all([
|
|
fetch(`/api/geoyoung/orders/summary-by-kd?start_date=${startDate}&end_date=${endDate}`),
|
|
fetch(`/api/sooin/orders/summary-by-kd?start_date=${startDate}&end_date=${endDate}`),
|
|
fetch(`/api/baekje/orders/summary-by-kd?start_date=${startDate}&end_date=${endDate}`),
|
|
fetch(`/api/dongwon/orders/summary-by-kd?start_date=${startDate}&end_date=${endDate}`)
|
|
]);
|
|
|
|
// 각 도매상 데이터를 KD 코드 기준으로 합산
|
|
if (dongwonRes.success && dongwonRes.by_kd_code) {
|
|
for (const [kd, data] of Object.entries(dongwonRes.by_kd_code)) {
|
|
orderDataByKd[kd].boxes += data.boxes || 0;
|
|
orderDataByKd[kd].units += data.units || 0;
|
|
orderDataByKd[kd].sources.push('동원');
|
|
}
|
|
}
|
|
```
|
|
|
|
### 📝 새 도매상 추가 시 체크리스트
|
|
|
|
- [ ] `{wholesaler}_api.py`에 `/orders/summary-by-kd` 엔드포인트 구현
|
|
- [ ] 응답 형식 표준 준수 (`by_kd_code`, `order_count` 등)
|
|
- [ ] `admin_rx_usage.html`의 `loadOrderData()`에 새 도매상 추가
|
|
- [ ] 합산 로직에 새 도매상 데이터 추가
|
|
- [ ] API 테스트 수행
|
|
|
|
---
|
|
|
|
*마지막 업데이트: 2025-07-14*
|