From 88a23c26c17cf31e8254c26a0d8f589577dec5f7 Mon Sep 17 00:00:00 2001 From: thug0bin Date: Wed, 11 Mar 2026 20:25:42 +0900 Subject: [PATCH] =?UTF-8?q?feat(drug-usage):=20=ED=99=98=EC=9E=90=20?= =?UTF-8?q?=EB=B1=83=EC=A7=80=20+=20limit=205000=20+=20=ED=98=84=EC=9E=AC?= =?UTF-8?q?=EA=B3=A0=20IM=5Ftotal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 조제목록에 환자 뱃지 표시 (3명 이하: 전체, 3명 초과: 최근 3명 + 외 N명) - API에 unique_patients, recent_patients 필드 추가 - limit 1000 → 5000 증가 - 현재고: 계산 방식 → IM_total.IM_QT_sale_debit (실제 DB 값) --- backend/app.py | 36 +++++++++++++++-- backend/templates/admin_drug_usage.html | 54 +++++++++++++++++++++---- 2 files changed, 79 insertions(+), 11 deletions(-) diff --git a/backend/app.py b/backend/app.py index a6ae038..e53b07e 100644 --- a/backend/app.py +++ b/backend/app.py @@ -8786,14 +8786,17 @@ def api_drug_usage(): rx_info = rx_data.get(code, {}) import_info = import_data.get(code, {}) + import_qty = import_info.get('import_total_qty', 0) + rx_qty = rx_info.get('rx_total_qty', 0) 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), + 'rx_total_qty': rx_qty, 'import_count': import_info.get('import_count', 0), - 'import_total_qty': import_info.get('import_total_qty', 0) + 'import_total_qty': import_qty, + 'current_stock': 0 # IM_total에서 나중에 채움 } # 약품명이 없으면 (입고만 있는 경우) PM_DRUG에서 조회 @@ -8807,6 +8810,20 @@ def api_drug_usage(): items.append(item) + # IM_total에서 현재 재고 조회 + if items: + drug_codes = [item['drug_code'] for item in items] + placeholders = ','.join([f"'{c}'" for c in drug_codes]) + stock_result = drug_session.execute(text(f""" + SELECT DrugCode, IM_QT_sale_debit + FROM IM_total + WHERE DrugCode IN ({placeholders}) + """)) + stock_map = {row.DrugCode: float(row.IM_QT_sale_debit) if row.IM_QT_sale_debit else 0 + for row in stock_result} + for item in items: + item['current_stock'] = stock_map.get(item['drug_code'], 0) + # 조제 건수 기준 정렬 items.sort(key=lambda x: x['rx_count'], reverse=True) @@ -8915,14 +8932,25 @@ def api_drug_usage_prescriptions(drug_code): """), {'drug_code': drug_code, 'start_date': start_date, 'end_date': end_date}) items = [] + seen_patients = set() + recent_patients = [] # 최근 조제받은 환자 (중복 제외, 최대 3명) + for row in result: dosage = float(row.dosage) if row.dosage else 0 freq = float(row.frequency) if row.frequency else 0 days = int(row.days) if row.days else 0 + patient = row.patient_name or '' + + # 중복 제외 환자 목록 (최근순, 최대 3명) + if patient and patient not in seen_patients: + seen_patients.add(patient) + if len(recent_patients) < 3: + recent_patients.append(patient) + items.append({ 'rx_date': row.rx_date or '', 'expiry_date': row.expiry_date or '', - 'patient_name': row.patient_name or '', + 'patient_name': patient, 'institution_name': row.institution_name or '', 'dosage': dosage, 'frequency': freq, @@ -8934,6 +8962,8 @@ def api_drug_usage_prescriptions(drug_code): 'success': True, 'drug_code': drug_code, 'total_count': len(items), + 'unique_patients': len(seen_patients), + 'recent_patients': recent_patients, 'items': items }) except Exception as e: diff --git a/backend/templates/admin_drug_usage.html b/backend/templates/admin_drug_usage.html index 8eb825e..0120a19 100644 --- a/backend/templates/admin_drug_usage.html +++ b/backend/templates/admin_drug_usage.html @@ -371,6 +371,25 @@ color: #10b981; font-weight: 500; } + .patient-info { + display: inline-flex; + gap: 4px; + margin-left: 8px; + flex-wrap: wrap; + } + .patient-badge { + display: inline-block; + padding: 2px 8px; + font-size: 11px; + font-weight: 500; + background: #e0f2fe; + color: #0369a1; + border-radius: 12px; + } + .patient-badge.more { + background: #fef3c7; + color: #92400e; + } .detail-table { width: 100%; border-collapse: separate; @@ -508,11 +527,12 @@ 입고건수 조제량 입고량 + 현재고 - +
📊

기간을 선택하고 조회 버튼을 클릭하세요

@@ -584,7 +604,7 @@ btn.textContent = '조회 중...'; tbody.innerHTML = ` - +

데이터를 불러오는 중...

@@ -596,7 +616,7 @@ start_date: startDate.replace(/-/g, ''), end_date: endDate.replace(/-/g, ''), date_type: dateType, - limit: 1000 // 전체 조회 후 클라이언트에서 페이징 + limit: 5000 // 전체 조회 후 클라이언트에서 페이징 }); if (search) params.append('search', search); @@ -621,7 +641,7 @@ } else { tbody.innerHTML = ` - +
⚠️

오류: ${escapeHtml(data.error || '알 수 없는 오류')}

@@ -631,7 +651,7 @@ } catch (err) { tbody.innerHTML = ` - +

조회 실패: ${escapeHtml(err.message)}

@@ -700,7 +720,7 @@ if (filteredData.length === 0) { tbody.innerHTML = ` - +
📭

조회 결과가 없습니다

@@ -725,9 +745,10 @@ ${formatNumber(item.import_count)} ${formatNumber(item.rx_total_qty)} ${formatNumber(item.import_total_qty)} + ${formatNumber(item.current_stock)} - +

📦 입고목록

@@ -846,8 +867,25 @@ const data = await res.json(); if (data.success && data.items.length > 0) { + // 환자 뱃지 생성 + const uniqueCount = data.unique_patients || 0; + const recentPatients = data.recent_patients || []; + let patientBadges = ''; + + if (uniqueCount <= 3) { + // 3명 이하: 전체 표시 + patientBadges = recentPatients.map(p => + `${escapeHtml(p)}` + ).join(''); + } else { + // 3명 초과: 최근 3명 + 외 N명 + patientBadges = recentPatients.map(p => + `${escapeHtml(p)}` + ).join('') + `외 ${uniqueCount - 3}명`; + } + container.innerHTML = ` -

💊 조제목록 (${data.total_count}건)

+

💊 조제목록 (${data.total_count}건) ${patientBadges}