feat(api): 기간별 사용약품 조회 API
- GET /api/drug-usage 엔드포인트 추가 - 조제건수/입고건수 통합 조회 - 조제일/소진일(expiry) 기준 선택 가능 - 약품명 검색, 약품코드 필터 지원 - QT_GUI 데이터와 100% 일치 검증됨 (살라겐정)
This commit is contained in:
parent
688bdb40f2
commit
04bf7a8535
209
backend/app.py
209
backend/app.py
@ -8619,6 +8619,215 @@ def api_animal_chat_logs():
|
||||
})
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# 기간별 사용약품 조회 API
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
@app.route('/api/drug-usage')
|
||||
def api_drug_usage():
|
||||
"""
|
||||
기간별 사용약품 조회 API
|
||||
|
||||
파라미터:
|
||||
- start_date: 시작일 (YYYYMMDD, 필수)
|
||||
- end_date: 종료일 (YYYYMMDD, 필수)
|
||||
- date_type: dispense (조제일, 기본) / expiry (소진일)
|
||||
- drug_code: 특정 약품코드 필터
|
||||
- search: 약품명 검색
|
||||
- limit: 결과 제한 (기본 100)
|
||||
"""
|
||||
try:
|
||||
# 파라미터 추출
|
||||
start_date = request.args.get('start_date')
|
||||
end_date = request.args.get('end_date')
|
||||
date_type = request.args.get('date_type', 'dispense') # dispense or expiry
|
||||
drug_code = request.args.get('drug_code')
|
||||
search = request.args.get('search')
|
||||
limit = int(request.args.get('limit', 100))
|
||||
|
||||
# 필수 파라미터 확인
|
||||
if not start_date or not end_date:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'start_date와 end_date는 필수입니다 (YYYYMMDD 형식)'
|
||||
}), 400
|
||||
|
||||
# 날짜 형식 검증 (8자리 숫자)
|
||||
if not (start_date.isdigit() and len(start_date) == 8):
|
||||
return jsonify({'success': False, 'error': 'start_date는 YYYYMMDD 형식이어야 합니다'}), 400
|
||||
if not (end_date.isdigit() and len(end_date) == 8):
|
||||
return jsonify({'success': False, 'error': 'end_date는 YYYYMMDD 형식이어야 합니다'}), 400
|
||||
|
||||
pres_session = db_manager.get_session('PM_PRES')
|
||||
|
||||
# ─────────────────────────────────────────
|
||||
# 조제 데이터 쿼리
|
||||
# ─────────────────────────────────────────
|
||||
if date_type == 'expiry':
|
||||
# 소진일(조제만료일) 기준 필터
|
||||
rx_query = """
|
||||
SELECT
|
||||
sp.DrugCode,
|
||||
g.GoodsName,
|
||||
m.PRINT_TYPE as category,
|
||||
COUNT(*) as rx_count,
|
||||
SUM(sp.QUAN * sp.QUAN_TIME * sp.Days) as total_qty
|
||||
FROM PS_sub_pharm sp
|
||||
INNER JOIN PS_main pm ON pm.PreSerial = sp.PreSerial
|
||||
INNER JOIN PM_DRUG.dbo.CD_GOODS g ON sp.DrugCode = g.DrugCode
|
||||
LEFT JOIN PM_DRUG.dbo.CD_MC m ON sp.DrugCode = m.DRUGCODE
|
||||
WHERE sp.PS_Type != '9'
|
||||
AND DATEADD(day, sp.Days, CONVERT(date, pm.Indate, 112))
|
||||
BETWEEN CONVERT(date, :start_date, 112) AND CONVERT(date, :end_date, 112)
|
||||
"""
|
||||
else:
|
||||
# 조제일 기준 필터 (기본)
|
||||
rx_query = """
|
||||
SELECT
|
||||
sp.DrugCode,
|
||||
g.GoodsName,
|
||||
m.PRINT_TYPE as category,
|
||||
COUNT(*) as rx_count,
|
||||
SUM(sp.QUAN * sp.QUAN_TIME * sp.Days) as total_qty
|
||||
FROM PS_sub_pharm sp
|
||||
INNER JOIN PS_main pm ON pm.PreSerial = sp.PreSerial
|
||||
INNER JOIN PM_DRUG.dbo.CD_GOODS g ON sp.DrugCode = g.DrugCode
|
||||
LEFT JOIN PM_DRUG.dbo.CD_MC m ON sp.DrugCode = m.DRUGCODE
|
||||
WHERE pm.Indate BETWEEN :start_date AND :end_date
|
||||
AND sp.PS_Type != '9'
|
||||
"""
|
||||
|
||||
# 약품코드 필터 추가
|
||||
if drug_code:
|
||||
rx_query += " AND sp.DrugCode = :drug_code"
|
||||
|
||||
# 약품명 검색 필터 추가
|
||||
if search:
|
||||
rx_query += " AND g.GoodsName LIKE :search"
|
||||
|
||||
rx_query += " GROUP BY sp.DrugCode, g.GoodsName, m.PRINT_TYPE"
|
||||
rx_query += " ORDER BY rx_count DESC"
|
||||
|
||||
# 파라미터 바인딩
|
||||
params = {'start_date': start_date, 'end_date': end_date}
|
||||
if drug_code:
|
||||
params['drug_code'] = drug_code
|
||||
if search:
|
||||
params['search'] = f'%{search}%'
|
||||
|
||||
rx_result = pres_session.execute(text(rx_query), params)
|
||||
rx_data = {}
|
||||
for row in rx_result:
|
||||
rx_data[row.DrugCode] = {
|
||||
'drug_code': row.DrugCode,
|
||||
'goods_name': row.GoodsName,
|
||||
'category': row.category or '',
|
||||
'rx_count': row.rx_count,
|
||||
'rx_total_qty': float(row.total_qty) if row.total_qty else 0
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────
|
||||
# 입고 데이터 쿼리 (PM_DRUG에서 조회)
|
||||
# ─────────────────────────────────────────
|
||||
drug_session = db_manager.get_session('PM_DRUG')
|
||||
|
||||
import_query = """
|
||||
SELECT
|
||||
ws.DrugCode,
|
||||
COUNT(*) as import_count,
|
||||
SUM(ws.WH_NM_item_a) as total_qty
|
||||
FROM WH_sub ws
|
||||
INNER JOIN WH_main wm ON ws.WH_SR_stock = wm.WH_NO_stock
|
||||
"""
|
||||
|
||||
# 약품명 검색 시 CD_GOODS 조인 추가
|
||||
if search:
|
||||
import_query += " INNER JOIN CD_GOODS g ON ws.DrugCode = g.DrugCode"
|
||||
|
||||
import_query += " WHERE wm.WH_DT_appl BETWEEN :start_date AND :end_date"
|
||||
|
||||
# 약품코드 필터 추가
|
||||
if drug_code:
|
||||
import_query += " AND ws.DrugCode = :drug_code"
|
||||
|
||||
# 약품명 검색 필터 추가
|
||||
if search:
|
||||
import_query += " AND g.GoodsName LIKE :search"
|
||||
|
||||
import_query += " GROUP BY ws.DrugCode"
|
||||
|
||||
import_params = {'start_date': start_date, 'end_date': end_date}
|
||||
if drug_code:
|
||||
import_params['drug_code'] = drug_code
|
||||
if search:
|
||||
import_params['search'] = f'%{search}%'
|
||||
|
||||
import_result = drug_session.execute(text(import_query), import_params)
|
||||
import_data = {}
|
||||
for row in import_result:
|
||||
import_data[row.DrugCode] = {
|
||||
'import_count': row.import_count,
|
||||
'import_total_qty': float(row.total_qty) if row.total_qty else 0
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────
|
||||
# 결과 병합 (drug_code 기준)
|
||||
# ─────────────────────────────────────────
|
||||
all_drug_codes = set(rx_data.keys()) | set(import_data.keys())
|
||||
items = []
|
||||
|
||||
for code in all_drug_codes:
|
||||
rx_info = rx_data.get(code, {})
|
||||
import_info = import_data.get(code, {})
|
||||
|
||||
item = {
|
||||
'drug_code': code,
|
||||
'goods_name': rx_info.get('goods_name', ''),
|
||||
'category': rx_info.get('category', ''),
|
||||
'rx_count': rx_info.get('rx_count', 0),
|
||||
'rx_total_qty': rx_info.get('rx_total_qty', 0),
|
||||
'import_count': import_info.get('import_count', 0),
|
||||
'import_total_qty': import_info.get('import_total_qty', 0)
|
||||
}
|
||||
|
||||
# 약품명이 없으면 (입고만 있는 경우) PM_DRUG에서 조회
|
||||
if not item['goods_name'] and code in import_data:
|
||||
name_result = drug_session.execute(
|
||||
text("SELECT GoodsName FROM CD_GOODS WHERE DrugCode = :code"),
|
||||
{'code': code}
|
||||
).fetchone()
|
||||
if name_result:
|
||||
item['goods_name'] = name_result.GoodsName
|
||||
|
||||
items.append(item)
|
||||
|
||||
# 조제 건수 기준 정렬
|
||||
items.sort(key=lambda x: x['rx_count'], reverse=True)
|
||||
|
||||
# limit 적용
|
||||
items = items[:limit]
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'period': {
|
||||
'start': start_date,
|
||||
'end': end_date,
|
||||
'date_type': date_type
|
||||
},
|
||||
'total_count': len(items),
|
||||
'items': items
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"drug-usage API 오류: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': str(e)
|
||||
}), 500
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import os
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user