feat: 소수 환자 약품 뱃지 표시
- 1년간 3명 이하 환자만 사용하는 약품에 환자 이름 뱃지 표시 - 조회 기간 내 사용한 환자는 핑크색으로 강조 - 매출액 컬럼명 변경 (약가 → 매출액) - SUM(DRUPRICE)로 매출액 계산
This commit is contained in:
parent
846883cbfa
commit
ee300f80ca
@ -4088,6 +4088,50 @@ def api_rx_usage():
|
|||||||
|
|
||||||
mssql_session = db_manager.get_session('PM_PRES')
|
mssql_session = db_manager.get_session('PM_PRES')
|
||||||
|
|
||||||
|
# 1년간 사용 환자 3명 이하 약품의 환자 목록 조회 + 조회 기간 내 사용 여부
|
||||||
|
patient_query = text("""
|
||||||
|
WITH PatientUsage AS (
|
||||||
|
SELECT DISTINCT
|
||||||
|
P.DrugCode,
|
||||||
|
M.Paname,
|
||||||
|
MAX(CASE WHEN M.Indate >= :start_date AND M.Indate <= :end_date THEN 1 ELSE 0 END) as used_in_period
|
||||||
|
FROM PS_sub_pharm P
|
||||||
|
JOIN PS_main M ON P.PreSerial = M.PreSerial
|
||||||
|
WHERE M.Indate >= CONVERT(VARCHAR, DATEADD(YEAR, -1, GETDATE()), 112)
|
||||||
|
GROUP BY P.DrugCode, M.Paname
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
PU.DrugCode as drug_code,
|
||||||
|
COUNT(*) as patient_count,
|
||||||
|
STUFF((
|
||||||
|
SELECT ', ' + PU2.Paname
|
||||||
|
FROM PatientUsage PU2
|
||||||
|
WHERE PU2.DrugCode = PU.DrugCode
|
||||||
|
ORDER BY PU2.Paname
|
||||||
|
FOR XML PATH(''), TYPE
|
||||||
|
).value('.', 'NVARCHAR(MAX)'), 1, 2, '') as patient_names,
|
||||||
|
STUFF((
|
||||||
|
SELECT ', ' + PU3.Paname
|
||||||
|
FROM PatientUsage PU3
|
||||||
|
WHERE PU3.DrugCode = PU.DrugCode AND PU3.used_in_period = 1
|
||||||
|
ORDER BY PU3.Paname
|
||||||
|
FOR XML PATH(''), TYPE
|
||||||
|
).value('.', 'NVARCHAR(MAX)'), 1, 2, '') as today_patients
|
||||||
|
FROM PatientUsage PU
|
||||||
|
GROUP BY PU.DrugCode
|
||||||
|
HAVING COUNT(*) <= 3
|
||||||
|
""")
|
||||||
|
|
||||||
|
patient_rows = mssql_session.execute(patient_query, {
|
||||||
|
'start_date': start_date_fmt,
|
||||||
|
'end_date': end_date_fmt
|
||||||
|
}).fetchall()
|
||||||
|
patient_map = {row.drug_code: {
|
||||||
|
'count': row.patient_count,
|
||||||
|
'names': row.patient_names,
|
||||||
|
'today': row.today_patients # 오늘 사용한 환자
|
||||||
|
} for row in patient_rows}
|
||||||
|
|
||||||
# 전문의약품 품목별 사용량 집계 쿼리 (현재고: IM_total.IM_QT_sale_debit, 위치: CD_item_position.CD_NM_sale)
|
# 전문의약품 품목별 사용량 집계 쿼리 (현재고: IM_total.IM_QT_sale_debit, 위치: CD_item_position.CD_NM_sale)
|
||||||
rx_query = text("""
|
rx_query = text("""
|
||||||
SELECT
|
SELECT
|
||||||
@ -4138,6 +4182,9 @@ def api_rx_usage():
|
|||||||
amount = float(row.total_amount or 0)
|
amount = float(row.total_amount or 0)
|
||||||
rx_count = int(row.prescription_count or 0)
|
rx_count = int(row.prescription_count or 0)
|
||||||
|
|
||||||
|
# 소수 환자 약품인지 확인 (1년간 3명 이하)
|
||||||
|
patient_info = patient_map.get(drug_code)
|
||||||
|
|
||||||
items.append({
|
items.append({
|
||||||
'drug_code': drug_code,
|
'drug_code': drug_code,
|
||||||
'product_name': product_name,
|
'product_name': product_name,
|
||||||
@ -4149,7 +4196,10 @@ def api_rx_usage():
|
|||||||
'prescription_count': rx_count,
|
'prescription_count': rx_count,
|
||||||
'current_stock': int(row.current_stock or 0), # 현재고
|
'current_stock': int(row.current_stock or 0), # 현재고
|
||||||
'location': row.location or '', # 약국 내 위치
|
'location': row.location or '', # 약국 내 위치
|
||||||
'thumbnail': None
|
'thumbnail': None,
|
||||||
|
'patient_count': patient_info['count'] if patient_info else None, # 1년간 사용 환자 수 (3명 이하만)
|
||||||
|
'patient_names': patient_info['names'] if patient_info else None, # 환자 이름 (3명 이하만)
|
||||||
|
'today_patients': patient_info['today'] if patient_info else None # 오늘 사용한 환자
|
||||||
})
|
})
|
||||||
|
|
||||||
total_qty += qty
|
total_qty += qty
|
||||||
|
|||||||
@ -1,11 +1,25 @@
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
conn = sqlite3.connect('db/orders.db')
|
conn = sqlite3.connect('db/mileage.db')
|
||||||
cursor = conn.cursor()
|
cur = conn.cursor()
|
||||||
cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
|
|
||||||
tables = [r[0] for r in cursor.fetchall()]
|
# 테이블 목록
|
||||||
print('Tables:', tables)
|
cur.execute("SELECT name FROM sqlite_master WHERE type='table'")
|
||||||
|
tables = cur.fetchall()
|
||||||
|
print('=== Tables ===')
|
||||||
for t in tables:
|
for t in tables:
|
||||||
cursor.execute(f"PRAGMA table_info({t})")
|
print(t[0])
|
||||||
cols = [r[1] for r in cursor.fetchall()]
|
|
||||||
print(f" {t}: {cols}")
|
# wholesaler/limit/setting 관련 테이블 스키마 확인
|
||||||
conn.close()
|
for t in tables:
|
||||||
|
tname = t[0].lower()
|
||||||
|
if 'wholesal' in tname or 'limit' in tname or 'setting' in tname or 'config' in tname:
|
||||||
|
print(f'\n=== {t[0]} schema ===')
|
||||||
|
cur.execute(f'PRAGMA table_info({t[0]})')
|
||||||
|
for col in cur.fetchall():
|
||||||
|
print(col)
|
||||||
|
cur.execute(f'SELECT * FROM {t[0]} LIMIT 5')
|
||||||
|
rows = cur.fetchall()
|
||||||
|
if rows:
|
||||||
|
print('Sample data:')
|
||||||
|
for r in rows:
|
||||||
|
print(r)
|
||||||
|
|||||||
48
backend/check_lasix.py
Normal file
48
backend/check_lasix.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import pyodbc
|
||||||
|
|
||||||
|
conn_str = (
|
||||||
|
'DRIVER={ODBC Driver 17 for SQL Server};'
|
||||||
|
'SERVER=192.168.0.4\\PM2014;'
|
||||||
|
'DATABASE=PM_DRUG;'
|
||||||
|
'UID=sa;'
|
||||||
|
'PWD=tmddls214!%(;'
|
||||||
|
'TrustServerCertificate=yes;'
|
||||||
|
'Connection Timeout=10'
|
||||||
|
)
|
||||||
|
|
||||||
|
conn = pyodbc.connect(conn_str, timeout=10)
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
# 라식스 약품 정보 조회 (전체 컬럼)
|
||||||
|
cur.execute("""
|
||||||
|
SELECT TOP 1 *
|
||||||
|
FROM CD_GOODS
|
||||||
|
WHERE DrugCode = '652100200'
|
||||||
|
""")
|
||||||
|
|
||||||
|
row = cur.fetchone()
|
||||||
|
if row:
|
||||||
|
columns = [desc[0] for desc in cur.description]
|
||||||
|
print("=== 라식스 약품 정보 ===")
|
||||||
|
for i, col in enumerate(columns):
|
||||||
|
if 'price' in col.lower() or 'cost' in col.lower() or 'amount' in col.lower():
|
||||||
|
print(f"{col}: {row[i]}")
|
||||||
|
|
||||||
|
# 처방전에서 라식스 DRUPRICE 확인
|
||||||
|
conn2 = pyodbc.connect(conn_str.replace('PM_DRUG', 'PM_PRES'), timeout=10)
|
||||||
|
cur2 = conn2.cursor()
|
||||||
|
|
||||||
|
cur2.execute("""
|
||||||
|
SELECT TOP 5 DrugCode, QUAN, Days, DRUPRICE
|
||||||
|
FROM PS_sub_pharm
|
||||||
|
WHERE DrugCode = '652100200'
|
||||||
|
ORDER BY Indate DESC
|
||||||
|
""")
|
||||||
|
|
||||||
|
print("\n=== 최근 처방 라식스 DRUPRICE ===")
|
||||||
|
for row in cur2.fetchall():
|
||||||
|
print(f"DrugCode: {row.DrugCode}, QUAN: {row.QUAN}, Days: {row.Days}, DRUPRICE: {row.DRUPRICE}")
|
||||||
|
dose = row.QUAN * row.Days
|
||||||
|
amount = row.DRUPRICE * row.QUAN * row.Days
|
||||||
|
print(f" → 투약량: {dose}, 매출액: {amount:,}")
|
||||||
32
backend/check_orders_db.py
Normal file
32
backend/check_orders_db.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import sqlite3
|
||||||
|
conn = sqlite3.connect('db/orders.db')
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
# 테이블 목록
|
||||||
|
cur.execute("SELECT name FROM sqlite_master WHERE type='table'")
|
||||||
|
tables = cur.fetchall()
|
||||||
|
print('=== Tables in orders.db ===')
|
||||||
|
for t in tables:
|
||||||
|
print(t[0])
|
||||||
|
|
||||||
|
# wholesaler_limits 확인
|
||||||
|
cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='wholesaler_limits'")
|
||||||
|
if cur.fetchone():
|
||||||
|
print('\n=== wholesaler_limits schema ===')
|
||||||
|
cur.execute('PRAGMA table_info(wholesaler_limits)')
|
||||||
|
for col in cur.fetchall():
|
||||||
|
print(col)
|
||||||
|
cur.execute('SELECT * FROM wholesaler_limits')
|
||||||
|
rows = cur.fetchall()
|
||||||
|
print('\n=== Data ===')
|
||||||
|
for r in rows:
|
||||||
|
print(r)
|
||||||
|
else:
|
||||||
|
print('\n❌ wholesaler_limits 테이블 없음!')
|
||||||
|
|
||||||
|
# delivery_schedules 확인
|
||||||
|
cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='delivery_schedules'")
|
||||||
|
if cur.fetchone():
|
||||||
|
print('\n=== delivery_schedules 있음 ===')
|
||||||
|
else:
|
||||||
|
print('\n❌ delivery_schedules 테이블 없음!')
|
||||||
28
backend/check_patient_columns.py
Normal file
28
backend/check_patient_columns.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import pyodbc
|
||||||
|
|
||||||
|
conn_str = (
|
||||||
|
'DRIVER={ODBC Driver 17 for SQL Server};'
|
||||||
|
'SERVER=192.168.0.4\\PM2014;'
|
||||||
|
'DATABASE=PM_PRES;'
|
||||||
|
'UID=sa;'
|
||||||
|
'PWD=tmddls214!%(;'
|
||||||
|
'TrustServerCertificate=yes;'
|
||||||
|
'Connection Timeout=10'
|
||||||
|
)
|
||||||
|
|
||||||
|
conn = pyodbc.connect(conn_str, timeout=10)
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
# PS_main 테이블 컬럼 확인
|
||||||
|
cur.execute("SELECT TOP 1 * FROM PS_main")
|
||||||
|
row = cur.fetchone()
|
||||||
|
columns = [desc[0] for desc in cur.description]
|
||||||
|
print("=== PS_main 컬럼 ===")
|
||||||
|
for col in columns:
|
||||||
|
print(col)
|
||||||
|
|
||||||
|
print("\n=== 샘플 데이터 (환자 관련) ===")
|
||||||
|
cur.execute("SELECT TOP 3 PreSerial, Paname, Indate FROM PS_main ORDER BY Indate DESC")
|
||||||
|
for row in cur.fetchall():
|
||||||
|
print(f"PreSerial: {row.PreSerial}, 환자명: {row.Paname}, 날짜: {row.Indate}")
|
||||||
31
backend/check_price_columns.py
Normal file
31
backend/check_price_columns.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import pyodbc
|
||||||
|
|
||||||
|
conn_str = (
|
||||||
|
'DRIVER={ODBC Driver 17 for SQL Server};'
|
||||||
|
'SERVER=192.168.0.4\\PM2014;'
|
||||||
|
'DATABASE=PM_DRUG;'
|
||||||
|
'UID=sa;'
|
||||||
|
'PWD=tmddls214!%(;'
|
||||||
|
'TrustServerCertificate=yes;'
|
||||||
|
'Connection Timeout=10'
|
||||||
|
)
|
||||||
|
|
||||||
|
conn = pyodbc.connect(conn_str, timeout=10)
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
# CD_GOODS 테이블 전체 컬럼 조회 (라식스)
|
||||||
|
cur.execute("""
|
||||||
|
SELECT *
|
||||||
|
FROM CD_GOODS
|
||||||
|
WHERE DrugCode = '652100200'
|
||||||
|
""")
|
||||||
|
|
||||||
|
row = cur.fetchone()
|
||||||
|
if row:
|
||||||
|
columns = [desc[0] for desc in cur.description]
|
||||||
|
print("=== CD_GOODS 라식스 전체 컬럼 ===")
|
||||||
|
for i, col in enumerate(columns):
|
||||||
|
val = row[i]
|
||||||
|
if val is not None and val != '' and val != 0:
|
||||||
|
print(f"{col}: {val}")
|
||||||
50
backend/create_limits_table.py
Normal file
50
backend/create_limits_table.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import sqlite3
|
||||||
|
|
||||||
|
conn = sqlite3.connect('db/orders.db')
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
# wholesaler_limits 테이블 생성
|
||||||
|
cur.execute('''
|
||||||
|
CREATE TABLE IF NOT EXISTS wholesaler_limits (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
wholesaler_id TEXT NOT NULL UNIQUE,
|
||||||
|
|
||||||
|
-- 한도 설정
|
||||||
|
monthly_limit INTEGER DEFAULT 0, -- 월 한도 (원)
|
||||||
|
warning_threshold REAL DEFAULT 0.9, -- 경고 임계값 (90%)
|
||||||
|
|
||||||
|
-- 우선순위
|
||||||
|
priority INTEGER DEFAULT 1, -- 1이 최우선
|
||||||
|
|
||||||
|
-- 상태
|
||||||
|
is_active INTEGER DEFAULT 1,
|
||||||
|
|
||||||
|
-- 메타
|
||||||
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
''')
|
||||||
|
|
||||||
|
# 기본 데이터 삽입 (각 2000만원)
|
||||||
|
wholesalers = [
|
||||||
|
('geoyoung', 20000000, 0.9, 1),
|
||||||
|
('sooin', 20000000, 0.9, 2),
|
||||||
|
('baekje', 20000000, 0.9, 3),
|
||||||
|
]
|
||||||
|
|
||||||
|
for ws_id, limit, threshold, priority in wholesalers:
|
||||||
|
cur.execute('''
|
||||||
|
INSERT OR REPLACE INTO wholesaler_limits
|
||||||
|
(wholesaler_id, monthly_limit, warning_threshold, priority)
|
||||||
|
VALUES (?, ?, ?, ?)
|
||||||
|
''', (ws_id, limit, threshold, priority))
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
# 확인
|
||||||
|
cur.execute('SELECT * FROM wholesaler_limits')
|
||||||
|
print('=== wholesaler_limits 생성 완료 ===')
|
||||||
|
for row in cur.fetchall():
|
||||||
|
print(row)
|
||||||
|
|
||||||
|
conn.close()
|
||||||
@ -391,6 +391,28 @@
|
|||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.patient-badge {
|
||||||
|
display: inline-block;
|
||||||
|
background: rgba(156, 163, 175, 0.15);
|
||||||
|
color: #9ca3af;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-left: 4px;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.patient-badge.has-today {
|
||||||
|
background: rgba(236, 72, 153, 0.2);
|
||||||
|
color: #ec4899;
|
||||||
|
}
|
||||||
|
|
||||||
|
.today-patient {
|
||||||
|
color: #ec4899;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
/* 수량 관련 */
|
/* 수량 관련 */
|
||||||
.qty-cell {
|
.qty-cell {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -957,7 +979,7 @@
|
|||||||
}
|
}
|
||||||
<div class="product-info">
|
<div class="product-info">
|
||||||
<span class="product-name">${escapeHtml(item.product_name)}</span>
|
<span class="product-name">${escapeHtml(item.product_name)}</span>
|
||||||
<span class="product-code">${item.drug_code}${item.supplier ? ` · ${escapeHtml(item.supplier)}` : ''}${item.location ? ` <span class="location-badge">📍${escapeHtml(item.location)}</span>` : ''}</span>
|
<span class="product-code">${item.drug_code}${item.supplier ? ` · ${escapeHtml(item.supplier)}` : ''}${item.location ? ` <span class="location-badge">📍${escapeHtml(item.location)}</span>` : ''}${item.patient_names ? ` <span class="patient-badge ${item.today_patients ? 'has-today' : ''}" title="${item.patient_count}명 사용${item.today_patients ? ' (오늘: ' + item.today_patients + ')' : ''}">👤${formatPatientNames(item.patient_names, item.today_patients)}</span>` : ''}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@ -1861,6 +1883,23 @@
|
|||||||
return str.replace(/[&<>"']/g, m => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m]));
|
return str.replace(/[&<>"']/g, m => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 환자 이름 포맷 (오늘 사용 환자 강조)
|
||||||
|
function formatPatientNames(allNames, todayNames) {
|
||||||
|
if (!allNames) return '';
|
||||||
|
if (!todayNames) return escapeHtml(allNames);
|
||||||
|
|
||||||
|
const todaySet = new Set(todayNames.split(', ').map(n => n.trim()));
|
||||||
|
const names = allNames.split(', ');
|
||||||
|
|
||||||
|
return names.map(name => {
|
||||||
|
const trimmed = name.trim();
|
||||||
|
if (todaySet.has(trimmed)) {
|
||||||
|
return `<strong class="today-patient">${escapeHtml(trimmed)}</strong>`;
|
||||||
|
}
|
||||||
|
return escapeHtml(trimmed);
|
||||||
|
}).join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
function showToast(message, type = 'info') {
|
function showToast(message, type = 'info') {
|
||||||
const toast = document.getElementById('toast');
|
const toast = document.getElementById('toast');
|
||||||
toast.textContent = message;
|
toast.textContent = message;
|
||||||
|
|||||||
48
backend/test_patient_query.py
Normal file
48
backend/test_patient_query.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import pyodbc
|
||||||
|
|
||||||
|
conn_str = (
|
||||||
|
'DRIVER={ODBC Driver 17 for SQL Server};'
|
||||||
|
'SERVER=192.168.0.4\\PM2014;'
|
||||||
|
'DATABASE=PM_PRES;'
|
||||||
|
'UID=sa;'
|
||||||
|
'PWD=tmddls214!%(;'
|
||||||
|
'TrustServerCertificate=yes;'
|
||||||
|
'Connection Timeout=10'
|
||||||
|
)
|
||||||
|
|
||||||
|
conn = pyodbc.connect(conn_str, timeout=10)
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
# 최근 1년간 약품별 사용 환자 수 + 3명 이하면 이름 표시
|
||||||
|
query = """
|
||||||
|
WITH PatientUsage AS (
|
||||||
|
SELECT DISTINCT
|
||||||
|
P.DrugCode,
|
||||||
|
M.Paname
|
||||||
|
FROM PS_sub_pharm P
|
||||||
|
JOIN PS_main M ON P.PreSerial = M.PreSerial
|
||||||
|
WHERE M.Indate >= CONVERT(VARCHAR, DATEADD(YEAR, -1, GETDATE()), 112)
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
PU.DrugCode,
|
||||||
|
COUNT(*) as patient_count,
|
||||||
|
STUFF((
|
||||||
|
SELECT ', ' + PU2.Paname
|
||||||
|
FROM PatientUsage PU2
|
||||||
|
WHERE PU2.DrugCode = PU.DrugCode
|
||||||
|
ORDER BY PU2.Paname
|
||||||
|
FOR XML PATH(''), TYPE
|
||||||
|
).value('.', 'NVARCHAR(MAX)'), 1, 2, '') as patient_names
|
||||||
|
FROM PatientUsage PU
|
||||||
|
GROUP BY PU.DrugCode
|
||||||
|
HAVING COUNT(*) <= 3
|
||||||
|
ORDER BY COUNT(*), PU.DrugCode
|
||||||
|
"""
|
||||||
|
|
||||||
|
cur.execute(query)
|
||||||
|
rows = cur.fetchall()
|
||||||
|
|
||||||
|
print(f"=== 최근 1년 사용 환자 3명 이하 약품 ({len(rows)}개) ===\n")
|
||||||
|
for row in rows[:20]: # 상위 20개만
|
||||||
|
print(f"[{row.DrugCode}] {row.patient_count}명: {row.patient_names}")
|
||||||
65
backend/test_today_patients.py
Normal file
65
backend/test_today_patients.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import pyodbc
|
||||||
|
|
||||||
|
conn_str = (
|
||||||
|
'DRIVER={ODBC Driver 17 for SQL Server};'
|
||||||
|
'SERVER=192.168.0.4\\PM2014;'
|
||||||
|
'DATABASE=PM_PRES;'
|
||||||
|
'UID=sa;'
|
||||||
|
'PWD=tmddls214!%(;'
|
||||||
|
'TrustServerCertificate=yes;'
|
||||||
|
'Connection Timeout=10'
|
||||||
|
)
|
||||||
|
|
||||||
|
conn = pyodbc.connect(conn_str, timeout=10)
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
# 오늘 날짜 확인
|
||||||
|
cur.execute('SELECT CONVERT(VARCHAR, GETDATE(), 112)')
|
||||||
|
today = cur.fetchone()[0]
|
||||||
|
print(f'오늘 날짜: {today}')
|
||||||
|
|
||||||
|
# 오늘 처방된 약품 중 3명 이하 환자 약품 테스트
|
||||||
|
query = """
|
||||||
|
WITH PatientUsage AS (
|
||||||
|
SELECT DISTINCT
|
||||||
|
P.DrugCode,
|
||||||
|
M.Paname,
|
||||||
|
MAX(CASE WHEN M.Indate = CONVERT(VARCHAR, GETDATE(), 112) THEN 1 ELSE 0 END) as used_today
|
||||||
|
FROM PS_sub_pharm P
|
||||||
|
JOIN PS_main M ON P.PreSerial = M.PreSerial
|
||||||
|
WHERE M.Indate >= CONVERT(VARCHAR, DATEADD(YEAR, -1, GETDATE()), 112)
|
||||||
|
GROUP BY P.DrugCode, M.Paname
|
||||||
|
)
|
||||||
|
SELECT TOP 20
|
||||||
|
PU.DrugCode,
|
||||||
|
COUNT(*) as patient_count,
|
||||||
|
STUFF((
|
||||||
|
SELECT ', ' + PU2.Paname
|
||||||
|
FROM PatientUsage PU2
|
||||||
|
WHERE PU2.DrugCode = PU.DrugCode
|
||||||
|
ORDER BY PU2.Paname
|
||||||
|
FOR XML PATH(''), TYPE
|
||||||
|
).value('.', 'NVARCHAR(MAX)'), 1, 2, '') as patient_names,
|
||||||
|
STUFF((
|
||||||
|
SELECT ', ' + PU3.Paname
|
||||||
|
FROM PatientUsage PU3
|
||||||
|
WHERE PU3.DrugCode = PU.DrugCode AND PU3.used_today = 1
|
||||||
|
ORDER BY PU3.Paname
|
||||||
|
FOR XML PATH(''), TYPE
|
||||||
|
).value('.', 'NVARCHAR(MAX)'), 1, 2, '') as today_patients
|
||||||
|
FROM PatientUsage PU
|
||||||
|
GROUP BY PU.DrugCode
|
||||||
|
HAVING COUNT(*) <= 3
|
||||||
|
ORDER BY
|
||||||
|
CASE WHEN MAX(PU.used_today) = 1 THEN 0 ELSE 1 END, -- 오늘 사용한 것 먼저
|
||||||
|
PU.DrugCode
|
||||||
|
"""
|
||||||
|
|
||||||
|
cur.execute(query)
|
||||||
|
print('\n=== 3명 이하 환자 약품 (오늘 사용 우선) ===')
|
||||||
|
for row in cur.fetchall():
|
||||||
|
today_mark = ' ⭐오늘' if row.today_patients else ''
|
||||||
|
print(f'[{row.DrugCode}] {row.patient_count}명: {row.patient_names}{today_mark}')
|
||||||
|
if row.today_patients:
|
||||||
|
print(f' → 오늘 사용: {row.today_patients}')
|
||||||
40
backend/test_today_rx.py
Normal file
40
backend/test_today_rx.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import pyodbc
|
||||||
|
|
||||||
|
conn_str = (
|
||||||
|
'DRIVER={ODBC Driver 17 for SQL Server};'
|
||||||
|
'SERVER=192.168.0.4\\PM2014;'
|
||||||
|
'DATABASE=PM_PRES;'
|
||||||
|
'UID=sa;'
|
||||||
|
'PWD=tmddls214!%(;'
|
||||||
|
'TrustServerCertificate=yes;'
|
||||||
|
'Connection Timeout=10'
|
||||||
|
)
|
||||||
|
|
||||||
|
conn = pyodbc.connect(conn_str, timeout=10)
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
# 오늘 처방 있는지 확인
|
||||||
|
cur.execute("""
|
||||||
|
SELECT COUNT(*) as cnt, MAX(Indate) as last_date
|
||||||
|
FROM PS_main
|
||||||
|
WHERE Indate = CONVERT(VARCHAR, GETDATE(), 112)
|
||||||
|
""")
|
||||||
|
row = cur.fetchone()
|
||||||
|
print(f'오늘(20260307) 처방 수: {row.cnt}')
|
||||||
|
|
||||||
|
# 최근 처방일
|
||||||
|
cur.execute("SELECT MAX(Indate) FROM PS_main")
|
||||||
|
print(f'최근 처방일: {cur.fetchone()[0]}')
|
||||||
|
|
||||||
|
# 어제 처방 약품 중 3명 이하 확인 (테스트용)
|
||||||
|
cur.execute("""
|
||||||
|
SELECT TOP 5 P.DrugCode, M.Paname, M.Indate
|
||||||
|
FROM PS_sub_pharm P
|
||||||
|
JOIN PS_main M ON P.PreSerial = M.PreSerial
|
||||||
|
WHERE M.Indate = '20260306'
|
||||||
|
ORDER BY P.DrugCode
|
||||||
|
""")
|
||||||
|
print('\n=== 3/6 처방 샘플 ===')
|
||||||
|
for row in cur.fetchall():
|
||||||
|
print(f'{row.DrugCode}: {row.Paname}')
|
||||||
Loading…
Reference in New Issue
Block a user