diff --git a/backend/pmr_api.py b/backend/pmr_api.py index 4ddbcad..55ab24e 100644 --- a/backend/pmr_api.py +++ b/backend/pmr_api.py @@ -650,3 +650,134 @@ def get_patient_history(cus_code): except Exception as e: logging.error(f"환자 이전 처방 조회 오류: {e}") return jsonify({'success': False, 'error': str(e)}), 500 + + +# ───────────────────────────────────────────────────────────── +# API: 환자 OTC 구매 이력 +# ───────────────────────────────────────────────────────────── +@pmr_bp.route('/api/patient//otc', methods=['GET']) +def get_patient_otc_history(cus_code): + """ + 환자 OTC (일반의약품) 구매 이력 조회 + + Args: + cus_code: 환자 고유코드 (CusCode = SL_CD_custom) + + Query Params: + - limit: 최대 조회 건수 (기본 20, 최대 100) + """ + try: + limit = min(int(request.args.get('limit', 20)), 100) + + conn = get_mssql_connection('PM_PRES') + cursor = conn.cursor() + + # OTC 거래 목록 조회 (PRESERIAL = 'V' = OTC 판매) + cursor.execute(""" + SELECT + m.SL_NO_order, + m.SL_DT_appl, + m.InsertTime, + m.SL_MY_sale, + m.SL_NM_custom + FROM SALE_MAIN m + WHERE m.SL_CD_custom = ? + AND m.PRESERIAL = 'V' + ORDER BY m.InsertTime DESC + """, (cus_code,)) + + # 먼저 거래 목록 수집 + orders = [] + for row in cursor.fetchall(): + orders.append({ + 'order_no': row.SL_NO_order, + 'date': row.SL_DT_appl, + 'datetime': row.InsertTime.strftime('%Y-%m-%d %H:%M') if row.InsertTime else '', + 'amount': int(row.SL_MY_sale or 0), + 'customer_name': row.SL_NM_custom or '' + }) + + # 최근 limit개만 + orders = orders[:limit] + + if not orders: + conn.close() + return jsonify({ + 'success': True, + 'cus_code': cus_code, + 'count': 0, + 'purchases': [] + }) + + # 각 거래의 품목 조회 + purchases = [] + for order in orders: + cursor.execute(""" + SELECT + s.DrugCode, + g.GoodsName, + s.SL_NM_item, + s.SL_TOTAL_PRICE, + mc.PRINT_TYPE + FROM SALE_SUB s + LEFT JOIN PM_DRUG.dbo.CD_GOODS g ON s.DrugCode = g.DrugCode + LEFT JOIN PM_DRUG.dbo.CD_MC mc ON s.DrugCode = mc.DRUGCODE + WHERE s.SL_NO_order = ? + ORDER BY s.DrugCode + """, (order['order_no'],)) + + items = [] + for item_row in cursor.fetchall(): + items.append({ + 'drug_code': item_row.DrugCode or '', + 'name': item_row.GoodsName or item_row.DrugCode or '', + 'quantity': int(item_row.SL_NM_item or 0), + 'price': int(item_row.SL_TOTAL_PRICE or 0), + 'category': item_row.PRINT_TYPE or '' + }) + + purchases.append({ + **order, + 'items': items, + 'item_count': len(items) + }) + + conn.close() + + # 통계 계산 + total_amount = sum(p['amount'] for p in purchases) + total_visits = len(purchases) + + # 자주 구매하는 품목 집계 + item_freq = {} + for p in purchases: + for item in p['items']: + key = item['drug_code'] + if key not in item_freq: + item_freq[key] = { + 'name': item['name'], + 'category': item['category'], + 'count': 0, + 'total_qty': 0 + } + item_freq[key]['count'] += 1 + item_freq[key]['total_qty'] += item['quantity'] + + # 빈도순 정렬 + frequent_items = sorted(item_freq.values(), key=lambda x: x['count'], reverse=True)[:10] + + return jsonify({ + 'success': True, + 'cus_code': cus_code, + 'count': len(purchases), + 'summary': { + 'total_visits': total_visits, + 'total_amount': total_amount, + 'frequent_items': frequent_items + }, + 'purchases': purchases + }) + + except Exception as e: + logging.error(f"환자 OTC 구매 이력 조회 오류: {e}") + return jsonify({'success': False, 'error': str(e)}), 500 diff --git a/backend/templates/pmr.html b/backend/templates/pmr.html index 9480d4b..871139a 100644 --- a/backend/templates/pmr.html +++ b/backend/templates/pmr.html @@ -150,6 +150,125 @@ color: #92400e !important; margin-left: 5px; } + .detail-header .rx-info .otc-badge { + background: #dbeafe !important; + color: #1e40af !important; + cursor: pointer; + transition: all 0.2s; + } + .detail-header .rx-info .otc-badge:hover { + background: #bfdbfe !important; + transform: scale(1.05); + } + + /* OTC 모달 */ + .otc-modal { + display: none; + position: fixed; + top: 0; left: 0; right: 0; bottom: 0; + background: rgba(0,0,0,0.6); + z-index: 1000; + overflow-y: auto; + } + .otc-modal-content { + max-width: 600px; + margin: 40px auto; + background: #fff; + border-radius: 16px; + box-shadow: 0 20px 60px rgba(0,0,0,0.3); + overflow: hidden; + } + .otc-modal-header { + background: linear-gradient(135deg, #3b82f6, #60a5fa); + color: #fff; + padding: 20px 25px; + display: flex; + justify-content: space-between; + align-items: center; + } + .otc-modal-header h3 { margin: 0; font-size: 1.2rem; } + .otc-modal-close { + background: none; + border: none; + color: #fff; + font-size: 1.5rem; + cursor: pointer; + opacity: 0.8; + } + .otc-modal-close:hover { opacity: 1; } + .otc-summary { + display: flex; + gap: 20px; + padding: 15px 25px; + background: #f8fafc; + border-bottom: 1px solid #e2e8f0; + } + .otc-summary-item { + text-align: center; + } + .otc-summary-item .num { + font-size: 1.5rem; + font-weight: 700; + color: #1e40af; + } + .otc-summary-item .label { + font-size: 0.75rem; + color: #64748b; + } + .otc-frequent { + padding: 15px 25px; + border-bottom: 1px solid #e2e8f0; + } + .otc-frequent h4 { + margin: 0 0 10px 0; + font-size: 0.9rem; + color: #475569; + } + .otc-frequent-list { + display: flex; + flex-wrap: wrap; + gap: 8px; + } + .otc-frequent-item { + background: #eff6ff; + color: #1e40af; + padding: 4px 12px; + border-radius: 20px; + font-size: 0.8rem; + } + .otc-purchases { + max-height: 300px; + overflow-y: auto; + padding: 15px 25px; + } + .otc-purchase { + border: 1px solid #e2e8f0; + border-radius: 10px; + margin-bottom: 12px; + overflow: hidden; + } + .otc-purchase-header { + background: #f1f5f9; + padding: 10px 15px; + display: flex; + justify-content: space-between; + font-size: 0.85rem; + } + .otc-purchase-header .date { color: #475569; font-weight: 600; } + .otc-purchase-header .amount { color: #1e40af; font-weight: 600; } + .otc-purchase-items { + padding: 10px 15px; + } + .otc-purchase-item { + display: flex; + justify-content: space-between; + padding: 5px 0; + font-size: 0.85rem; + border-bottom: 1px solid #f1f5f9; + } + .otc-purchase-item:last-child { border-bottom: none; } + .otc-purchase-item .name { color: #1e293b; } + .otc-purchase-item .qty { color: #64748b; } /* 약품 목록 */ .medication-list { @@ -443,6 +562,19 @@ + +
+
+
+

💊 OTC 구매 이력

+ +
+
+
+
+
+
+