feat(drug-usage): 환자 뱃지 + limit 5000 + 현재고 IM_total
- 조제목록에 환자 뱃지 표시 (3명 이하: 전체, 3명 초과: 최근 3명 + 외 N명) - API에 unique_patients, recent_patients 필드 추가 - limit 1000 → 5000 증가 - 현재고: 계산 방식 → IM_total.IM_QT_sale_debit (실제 DB 값)
This commit is contained in:
parent
6db31785fa
commit
88a23c26c1
@ -8786,14 +8786,17 @@ def api_drug_usage():
|
|||||||
rx_info = rx_data.get(code, {})
|
rx_info = rx_data.get(code, {})
|
||||||
import_info = import_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 = {
|
item = {
|
||||||
'drug_code': code,
|
'drug_code': code,
|
||||||
'goods_name': rx_info.get('goods_name', ''),
|
'goods_name': rx_info.get('goods_name', ''),
|
||||||
'category': rx_info.get('category', ''),
|
'category': rx_info.get('category', ''),
|
||||||
'rx_count': rx_info.get('rx_count', 0),
|
'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_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에서 조회
|
# 약품명이 없으면 (입고만 있는 경우) PM_DRUG에서 조회
|
||||||
@ -8807,6 +8810,20 @@ def api_drug_usage():
|
|||||||
|
|
||||||
items.append(item)
|
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)
|
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})
|
"""), {'drug_code': drug_code, 'start_date': start_date, 'end_date': end_date})
|
||||||
|
|
||||||
items = []
|
items = []
|
||||||
|
seen_patients = set()
|
||||||
|
recent_patients = [] # 최근 조제받은 환자 (중복 제외, 최대 3명)
|
||||||
|
|
||||||
for row in result:
|
for row in result:
|
||||||
dosage = float(row.dosage) if row.dosage else 0
|
dosage = float(row.dosage) if row.dosage else 0
|
||||||
freq = float(row.frequency) if row.frequency else 0
|
freq = float(row.frequency) if row.frequency else 0
|
||||||
days = int(row.days) if row.days 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({
|
items.append({
|
||||||
'rx_date': row.rx_date or '',
|
'rx_date': row.rx_date or '',
|
||||||
'expiry_date': row.expiry_date or '',
|
'expiry_date': row.expiry_date or '',
|
||||||
'patient_name': row.patient_name or '',
|
'patient_name': patient,
|
||||||
'institution_name': row.institution_name or '',
|
'institution_name': row.institution_name or '',
|
||||||
'dosage': dosage,
|
'dosage': dosage,
|
||||||
'frequency': freq,
|
'frequency': freq,
|
||||||
@ -8934,6 +8962,8 @@ def api_drug_usage_prescriptions(drug_code):
|
|||||||
'success': True,
|
'success': True,
|
||||||
'drug_code': drug_code,
|
'drug_code': drug_code,
|
||||||
'total_count': len(items),
|
'total_count': len(items),
|
||||||
|
'unique_patients': len(seen_patients),
|
||||||
|
'recent_patients': recent_patients,
|
||||||
'items': items
|
'items': items
|
||||||
})
|
})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@ -371,6 +371,25 @@
|
|||||||
color: #10b981;
|
color: #10b981;
|
||||||
font-weight: 500;
|
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 {
|
.detail-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: separate;
|
border-collapse: separate;
|
||||||
@ -508,11 +527,12 @@
|
|||||||
<th class="sortable" data-sort="import_count">입고건수</th>
|
<th class="sortable" data-sort="import_count">입고건수</th>
|
||||||
<th class="sortable" data-sort="rx_total_qty">조제량</th>
|
<th class="sortable" data-sort="rx_total_qty">조제량</th>
|
||||||
<th class="sortable" data-sort="import_total_qty">입고량</th>
|
<th class="sortable" data-sort="import_total_qty">입고량</th>
|
||||||
|
<th class="sortable" data-sort="current_stock">현재고</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="drugUsageBody">
|
<tbody id="drugUsageBody">
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="7" class="empty-state">
|
<td colspan="8" class="empty-state">
|
||||||
<div class="icon">📊</div>
|
<div class="icon">📊</div>
|
||||||
<p>기간을 선택하고 조회 버튼을 클릭하세요</p>
|
<p>기간을 선택하고 조회 버튼을 클릭하세요</p>
|
||||||
</td>
|
</td>
|
||||||
@ -584,7 +604,7 @@
|
|||||||
btn.textContent = '조회 중...';
|
btn.textContent = '조회 중...';
|
||||||
tbody.innerHTML = `
|
tbody.innerHTML = `
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="7" class="loading-state">
|
<td colspan="8" class="loading-state">
|
||||||
<div class="loading-spinner"></div>
|
<div class="loading-spinner"></div>
|
||||||
<p>데이터를 불러오는 중...</p>
|
<p>데이터를 불러오는 중...</p>
|
||||||
</td>
|
</td>
|
||||||
@ -596,7 +616,7 @@
|
|||||||
start_date: startDate.replace(/-/g, ''),
|
start_date: startDate.replace(/-/g, ''),
|
||||||
end_date: endDate.replace(/-/g, ''),
|
end_date: endDate.replace(/-/g, ''),
|
||||||
date_type: dateType,
|
date_type: dateType,
|
||||||
limit: 1000 // 전체 조회 후 클라이언트에서 페이징
|
limit: 5000 // 전체 조회 후 클라이언트에서 페이징
|
||||||
});
|
});
|
||||||
if (search) params.append('search', search);
|
if (search) params.append('search', search);
|
||||||
|
|
||||||
@ -621,7 +641,7 @@
|
|||||||
} else {
|
} else {
|
||||||
tbody.innerHTML = `
|
tbody.innerHTML = `
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="7" class="empty-state">
|
<td colspan="8" class="empty-state">
|
||||||
<div class="icon">⚠️</div>
|
<div class="icon">⚠️</div>
|
||||||
<p>오류: ${escapeHtml(data.error || '알 수 없는 오류')}</p>
|
<p>오류: ${escapeHtml(data.error || '알 수 없는 오류')}</p>
|
||||||
</td>
|
</td>
|
||||||
@ -631,7 +651,7 @@
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
tbody.innerHTML = `
|
tbody.innerHTML = `
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="7" class="empty-state">
|
<td colspan="8" class="empty-state">
|
||||||
<div class="icon">❌</div>
|
<div class="icon">❌</div>
|
||||||
<p>조회 실패: ${escapeHtml(err.message)}</p>
|
<p>조회 실패: ${escapeHtml(err.message)}</p>
|
||||||
</td>
|
</td>
|
||||||
@ -700,7 +720,7 @@
|
|||||||
if (filteredData.length === 0) {
|
if (filteredData.length === 0) {
|
||||||
tbody.innerHTML = `
|
tbody.innerHTML = `
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="7" class="empty-state">
|
<td colspan="8" class="empty-state">
|
||||||
<div class="icon">📭</div>
|
<div class="icon">📭</div>
|
||||||
<p>조회 결과가 없습니다</p>
|
<p>조회 결과가 없습니다</p>
|
||||||
</td>
|
</td>
|
||||||
@ -725,9 +745,10 @@
|
|||||||
<td class="num-cell num-secondary">${formatNumber(item.import_count)}</td>
|
<td class="num-cell num-secondary">${formatNumber(item.import_count)}</td>
|
||||||
<td class="num-cell num-highlight">${formatNumber(item.rx_total_qty)}</td>
|
<td class="num-cell num-highlight">${formatNumber(item.rx_total_qty)}</td>
|
||||||
<td class="num-cell num-secondary">${formatNumber(item.import_total_qty)}</td>
|
<td class="num-cell num-secondary">${formatNumber(item.import_total_qty)}</td>
|
||||||
|
<td class="num-cell">${formatNumber(item.current_stock)}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="detail-row" id="detail-${escapeHtml(item.drug_code)}" style="display:${expandedDrugCode === item.drug_code ? 'table-row' : 'none'};">
|
<tr class="detail-row" id="detail-${escapeHtml(item.drug_code)}" style="display:${expandedDrugCode === item.drug_code ? 'table-row' : 'none'};">
|
||||||
<td colspan="7">
|
<td colspan="8">
|
||||||
<div class="detail-panel">
|
<div class="detail-panel">
|
||||||
<div class="detail-left" id="imports-${escapeHtml(item.drug_code)}">
|
<div class="detail-left" id="imports-${escapeHtml(item.drug_code)}">
|
||||||
<h4>📦 입고목록 <span class="count"></span></h4>
|
<h4>📦 입고목록 <span class="count"></span></h4>
|
||||||
@ -846,8 +867,25 @@
|
|||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
|
|
||||||
if (data.success && data.items.length > 0) {
|
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 =>
|
||||||
|
`<span class="patient-badge">${escapeHtml(p)}</span>`
|
||||||
|
).join('');
|
||||||
|
} else {
|
||||||
|
// 3명 초과: 최근 3명 + 외 N명
|
||||||
|
patientBadges = recentPatients.map(p =>
|
||||||
|
`<span class="patient-badge">${escapeHtml(p)}</span>`
|
||||||
|
).join('') + `<span class="patient-badge more">외 ${uniqueCount - 3}명</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
container.innerHTML = `
|
container.innerHTML = `
|
||||||
<h4>💊 조제목록 <span class="count">(${data.total_count}건)</span></h4>
|
<h4>💊 조제목록 <span class="count">(${data.total_count}건)</span> <span class="patient-info">${patientBadges}</span></h4>
|
||||||
<table class="detail-table" style="table-layout:fixed;">
|
<table class="detail-table" style="table-layout:fixed;">
|
||||||
<colgroup>
|
<colgroup>
|
||||||
<col style="width:65px;">
|
<col style="width:65px;">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user