pharmacy-pos-qr-system/docs/WHOLESALE_API_INTEGRATION.md

200 lines
5.6 KiB
Markdown

# 도매상 API 통합 가이드
> 작성일: 2026-03-06
> 버전: 1.0
## 📦 패키지 구조
```
pharmacy-wholesale-api/ # 별도 리포지토리
├── wholesale/
│ ├── __init__.py # SooinSession, GeoYoungSession 노출
│ ├── base.py # WholesaleSession 공통 인터페이스
│ ├── sooin.py # 수인약품 API
│ └── geoyoung.py # 지오영 API
└── docs/
└── SOOIN.md # 수인약품 상세 문서
pharmacy-pos-qr-system/backend/ # 기존 프로젝트
├── wholesale_path.py # 패키지 경로 설정
├── sooin_api.py # Flask Blueprint (wholesale 사용)
└── geoyoung_api.py # Flask Blueprint (wholesale 사용)
```
---
## 🔌 도매상별 API 특성
| 항목 | 지오영 | 수인약품 |
|------|--------|----------|
| 웹사이트 | gwn.geoweb.kr | sooinpharm.co.kr |
| 인증 방식 | Playwright → requests | Playwright → requests |
| 세션 유효시간 | 30분 | 30분 |
| 검색 코드 | 보험코드 (KD) | KD코드 + 내부코드 (pc) |
| 장바구니 추가 | productCode 필요 | internal_code (pc) 필요 |
| **개별 삭제** | ❌ 없음 | ✅ 체크박스 soft delete |
| 장바구니 조회 | PartialProductCart | Bag.asp |
---
## 🔑 핵심 발견: 코드 체계
### 지오영
```
보험코드 (KD코드) → 검색 → productCode (내부) → 장바구니 추가
```
### 수인약품
```
KD코드 → 검색 → internal_code (pc) → 장바구니 추가
PhysicInfo.asp?pc=32495 에서 추출
```
**⚠️ 중요:** `internal_code`가 없으면 장바구니 추가 불가!
---
## 🛒 수인약품 개별 취소 (Soft Delete)
### 발견 과정
- `kind=delOne` API 존재하지만 작동 안 함
- 체크박스가 실제 "취소" 역할
- `ControlBag.asp` AJAX 엔드포인트 발견
### API 사용법
```python
from wholesale import SooinSession
session = SooinSession()
session.login()
# 장바구니 조회 (체크 상태 포함)
cart = session.get_cart()
# cart['items'][0]['checked'] = False (활성)
# cart['items'][0]['active'] = True
# 항목 취소 (체크)
session.cancel_item(row_index=0)
# 또는
session.cancel_item(product_code="32495")
# 취소 복원 (체크 해제)
session.restore_item(row_index=0)
```
### 내부 동작
```
POST /Service/Order/ControlBag.asp
Content-Type: application/x-www-form-urlencoded; charset=euc-kr
X-Requested-With: XMLHttpRequest
vc=50911 (거래처코드)
pc=32495 (내부 제품코드)
f=true (true=취소, false=복원)
pg= (제품구분, 빈값)
pdno= (제품번호, 빈값)
tmdt= (기한, 빈값)
```
---
## 📊 SQLite 스키마 연동
### order_context (AI 학습용)
```sql
-- 새로 추가된 필드 (2026-03-06)
wholesaler_id TEXT, -- 'geoyoung' 또는 'sooin'
wholesaler_price INTEGER, -- 도매상 가격
internal_code TEXT, -- 도매상 내부 코드
was_cancelled BOOLEAN, -- 취소 여부 (수인 soft delete)
```
### 도매상별 주문 시 기록할 데이터
```python
order_context = {
'drug_code': 'D12345',
'product_name': '아세탑정',
'wholesaler_id': 'sooin',
'internal_code': '32495', # 수인 내부코드
'ordered_spec': '30T',
'ordered_qty': 2,
'wholesaler_price': 4800,
'available_specs': '["30T", "500T"]',
'spec_stocks': '{"30T": 0, "500T": 0}', # 재고 상황
'selection_reason': 'only_option',
'was_cancelled': False
}
```
---
## 🔄 Flask API 엔드포인트
### 수인약품 (/api/sooin/*)
| 메서드 | 경로 | 설명 |
|--------|------|------|
| GET | /stock | 재고 검색 |
| GET | /cart | 장바구니 조회 |
| POST | /order | 장바구니 추가 |
| POST | /cart/clear | 장바구니 비우기 |
| POST | /cart/cancel | **항목 취소 (soft delete)** |
| POST | /cart/restore | **항목 복원** |
| POST | /confirm | 주문 전송 |
### 지오영 (/api/geoyoung/*)
| 메서드 | 경로 | 설명 |
|--------|------|------|
| GET | /stock | 재고 검색 |
| GET | /cart | 장바구니 조회 |
| POST | /order | 장바구니 추가 |
| POST | /cart/clear | 장바구니 비우기 |
| POST | /cart/cancel | **항목 삭제 (hard delete)** |
| POST | /cart/restore | ❌ NOT_SUPPORTED |
| POST | /confirm | 주문 전송 |
### 개별 삭제 API 차이
| 도매상 | cancel 동작 | restore 가능 | 내부 API |
|--------|-------------|-------------|----------|
| 수인 | 체크박스 soft delete | ✅ 가능 | ControlBag.asp |
| 지오영 | 완전 삭제 | ❌ 불가 | DataCart/del |
---
## 📁 관련 문서
| 문서 | 위치 | 내용 |
|------|------|------|
| AI ERP 자동주문 기획 | `docs/AI_ERP_AUTO_ORDER_SYSTEM.md` | 전체 시스템 설계 |
| 지오영 API 분석 | `docs/GEOYOUNG_API_REVERSE_ENGINEERING.md` | 지오영 리버스 엔지니어링 |
| 수인 API 분석 | `pharmacy-wholesale-api/docs/SOOIN.md` | 수인 리버스 엔지니어링 |
| 사용량 조회 가이드 | `docs/RX_USAGE_GEOYOUNG_GUIDE.md` | 처방 사용량 조회 |
---
## ✅ 체크리스트
### 완료
- [x] 지오영 API 연동
- [x] 수인약품 API 연동
- [x] 개별 취소 기능 (수인) - soft delete
- [x] 개별 삭제 기능 (지오영) - hard delete
- [x] Flask Blueprint 통합
- [x] wholesale 패키지 분리
- [x] SQLite 스키마 업데이트
### 진행 예정
- [ ] daily_usage 자동 수집
- [ ] AI 규격 선택 모델
- [ ] AI 도매상 선택 모델
- [ ] 자동 주문 Level 1 (승인 후 실행)
---
*업데이트: 2026-03-06 by 용림 🐉*