feat(admin): 제품 사용이력 + 환자 최근처방 모달 기능

- GET /api/products/<drug_code>/usage-history
  - 제품별 처방 이력 조회 (환자명, 수량, 횟수, 일수, 총투약량)
  - 페이지네이션 + 기간 필터 지원

- GET /api/patients/<cus_code>/recent-prescriptions
  - 환자 최근 6개월 처방 내역
  - 약품별 분류(CD_MC.PRINT_TYPE) 표시 (당뇨치료, 고지혈치료 등)

- admin_products.html 모달 2개 추가
  - 제품명 클릭 → 사용이력 모달 (횟수 포함 정확한 총투약량)
  - 환자명 클릭 → 최근처방 모달 (z-index 2100으로 위에 표시)

DB 조인:
- PS_main.PreSerial = PS_sub_pharm.PreSerial
- CD_GOODS + CD_MC (PRINT_TYPE 분류)
This commit is contained in:
thug0bin
2026-03-11 16:50:48 +09:00
parent 83ecf88bd4
commit 688bdb40f2
2 changed files with 502 additions and 1 deletions

View File

@@ -3872,6 +3872,238 @@ def api_drug_purchase_history(drug_code):
return jsonify({'success': False, 'error': str(e)}), 500
# ==================== 처방 사용이력 API ====================
@app.route('/api/products/<drug_code>/usage-history')
def api_product_usage_history(drug_code):
"""
제품 처방 사용이력 조회 API
- PS_main + PS_sub_pharm JOIN
- 페이지네이션, 기간 필터 지원
- 환자명 마스킹 처리
"""
page = int(request.args.get('page', 1))
per_page = int(request.args.get('per_page', 20))
months = int(request.args.get('months', 12))
offset = (page - 1) * per_page
try:
pres_session = db_manager.get_session('PM_PRES')
# 기간 계산 (N개월 전부터)
from datetime import datetime, timedelta
start_date = (datetime.now() - timedelta(days=months * 30)).strftime('%Y%m%d')
# 총 건수 조회 (COUNT)
count_result = pres_session.execute(text("""
SELECT COUNT(*) as total_count
FROM PS_sub_pharm sp
JOIN PS_main pm ON pm.PreSerial = sp.PreSerial
WHERE sp.DrugCode = :drug_code
AND pm.Indate >= :start_date
AND (sp.PS_Type IS NULL OR sp.PS_Type != '9')
"""), {'drug_code': drug_code, 'start_date': start_date})
total_count = count_result.fetchone()[0]
# 데이터 조회 (페이지네이션)
data_result = pres_session.execute(text("""
SELECT
pm.Paname as patient_name,
pm.CusCode as cus_code,
pm.Indate as rx_date,
sp.QUAN as quantity,
sp.QUAN_TIME as times,
sp.Days as days
FROM PS_sub_pharm sp
JOIN PS_main pm ON pm.PreSerial = sp.PreSerial
WHERE sp.DrugCode = :drug_code
AND pm.Indate >= :start_date
AND (sp.PS_Type IS NULL OR sp.PS_Type != '9')
ORDER BY pm.Indate DESC
OFFSET :offset ROWS
FETCH NEXT :per_page ROWS ONLY
"""), {
'drug_code': drug_code,
'start_date': start_date,
'offset': offset,
'per_page': per_page
})
items = []
for row in data_result.fetchall():
patient_name = row.patient_name or ''
cus_code = row.cus_code or ''
# 날짜 포맷팅 (YYYYMMDD -> YYYY-MM-DD)
rx_date = row.rx_date or ''
if len(rx_date) == 8:
rx_date = f"{rx_date[:4]}-{rx_date[4:6]}-{rx_date[6:8]}"
quantity = int(row.quantity) if row.quantity else 1
times = int(row.times) if row.times else 1 # 횟수 (QUAN_TIME)
days = int(row.days) if row.days else 1
total_dose = quantity * times * days # 수량 × 횟수 × 일수
items.append({
'patient_name': patient_name,
'cus_code': cus_code,
'rx_date': rx_date,
'quantity': quantity,
'times': times,
'days': days,
'total_dose': total_dose
})
# 약품명 조회
drug_session = db_manager.get_session('PM_DRUG')
name_result = drug_session.execute(text("""
SELECT GoodsName FROM CD_GOODS WHERE DrugCode = :drug_code
"""), {'drug_code': drug_code})
name_row = name_result.fetchone()
product_name = name_row[0] if name_row else drug_code
# 총 페이지 수 계산
total_pages = (total_count + per_page - 1) // per_page
return jsonify({
'success': True,
'product_name': product_name,
'pagination': {
'page': page,
'per_page': per_page,
'total_count': total_count,
'total_pages': total_pages
},
'items': items
})
except Exception as e:
logging.error(f"사용이력 조회 오류: {e}")
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/api/patients/<cus_code>/recent-prescriptions')
def api_patient_recent_prescriptions(cus_code):
"""
환자 최근 처방 내역 조회 API
- 해당 환자가 최근에 어떤 약을 처방받았는지 확인
- DB 부담 최소화: 최근 6개월, 최대 30건
"""
try:
pres_session = db_manager.get_session('PM_PRES')
drug_session = db_manager.get_session('PM_DRUG')
# 최근 6개월
from datetime import datetime, timedelta
start_date = (datetime.now() - timedelta(days=180)).strftime('%Y%m%d')
# 환자의 최근 처방전 조회 (최대 10건)
rx_result = pres_session.execute(text("""
SELECT TOP 10
pm.PreSerial,
pm.Paname as patient_name,
pm.Indate as rx_date,
pm.Drname as doctor_name,
pm.OrderName as hospital_name
FROM PS_main pm
WHERE pm.CusCode = :cus_code
AND pm.Indate >= :start_date
ORDER BY pm.Indate DESC
"""), {'cus_code': cus_code, 'start_date': start_date})
prescriptions = []
for rx in rx_result.fetchall():
# 날짜 포맷팅
rx_date = rx.rx_date or ''
if len(rx_date) == 8:
rx_date = f"{rx_date[:4]}-{rx_date[4:6]}-{rx_date[6:8]}"
# 해당 처방의 약품 목록 조회
items_result = pres_session.execute(text("""
SELECT
sp.DrugCode,
sp.QUAN as quantity,
sp.QUAN_TIME as times,
sp.Days as days
FROM PS_sub_pharm sp
WHERE sp.PreSerial = :pre_serial
AND (sp.PS_Type IS NULL OR sp.PS_Type != '9')
"""), {'pre_serial': rx.PreSerial})
# 먼저 모든 약품 데이터를 리스트로 가져오기
raw_items = items_result.fetchall()
drug_codes = [item.DrugCode for item in raw_items]
# 약품명 + 성분명 + 분류(PRINT_TYPE) 일괄 조회
drug_info_map = {}
if drug_codes:
placeholders = ','.join([f"'{dc}'" for dc in drug_codes])
name_result = drug_session.execute(text(f"""
SELECT g.DrugCode, g.GoodsName, s.SUNG_HNM, m.PRINT_TYPE
FROM CD_GOODS g
LEFT JOIN CD_SUNG s ON g.SUNG_CODE = s.SUNG_CODE
LEFT JOIN CD_MC m ON g.DrugCode = m.DRUGCODE
WHERE g.DrugCode IN ({placeholders})
"""))
for row in name_result.fetchall():
drug_info_map[row[0]] = {
'name': row[1],
'ingredient': row[2] or '',
'category': row[3] or '' # 분류 (알러지질환약 등)
}
items = []
for item in raw_items:
info = drug_info_map.get(item.DrugCode, {})
drug_name = info.get('name', item.DrugCode)
ingredient = info.get('ingredient', '')
category = info.get('category', '') # 분류 (알러지질환약 등)
quantity = int(item.quantity) if item.quantity else 1
times = int(item.times) if item.times else 1
days = int(item.days) if item.days else 1
items.append({
'drug_code': item.DrugCode,
'drug_name': drug_name,
'category': category, # 분류 추가
'quantity': quantity,
'times': times,
'days': days,
'total_dose': quantity * times * days
})
prescriptions.append({
'pre_serial': rx.PreSerial,
'rx_date': rx_date,
'doctor_name': rx.doctor_name or '',
'hospital_name': rx.hospital_name or '',
'items': items
})
# 환자명
patient_name = prescriptions[0]['items'][0]['drug_name'] if prescriptions else ''
if prescriptions and pres_session.execute(text("""
SELECT TOP 1 Paname FROM PS_main WHERE CusCode = :cus_code
"""), {'cus_code': cus_code}).fetchone():
patient_name = pres_session.execute(text("""
SELECT TOP 1 Paname FROM PS_main WHERE CusCode = :cus_code
"""), {'cus_code': cus_code}).fetchone()[0]
return jsonify({
'success': True,
'cus_code': cus_code,
'patient_name': patient_name,
'prescription_count': len(prescriptions),
'prescriptions': prescriptions
})
except Exception as e:
logging.error(f"환자 처방 조회 오류: {e}")
return jsonify({'success': False, 'error': str(e)}), 500
# ==================== 위치 정보 API ====================
@app.route('/api/locations')