# 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*