diff --git a/backend/app.py b/backend/app.py index f00f39d..a6ae038 100644 --- a/backend/app.py +++ b/backend/app.py @@ -3498,6 +3498,12 @@ def admin_products(): return render_template('admin_products.html') +@app.route('/admin/drug-usage') +def admin_drug_usage(): + """기간별 사용약품 조회 페이지""" + return render_template('admin_drug_usage.html') + + @app.route('/admin/members') def admin_members(): """회원 검색 페이지 (팜IT3000 CD_PERSON, 알림톡/SMS 발송)""" @@ -8828,6 +8834,113 @@ def api_drug_usage(): }), 500 +@app.route('/api/drug-usage//imports') +def api_drug_usage_imports(drug_code): + """약품별 입고 상세 API""" + start_date = request.args.get('start_date', '') + end_date = request.args.get('end_date', '') + + if not start_date or not end_date: + return jsonify({'success': False, 'error': 'start_date, end_date 필수'}), 400 + + try: + drug_session = db_manager.get_session('PM_DRUG') + + result = drug_session.execute(text(""" + SELECT + wm.WH_DT_appl as import_date, + ws.WH_NM_item_a as quantity, + ws.WH_MY_unit_a as unit_price, + c.CD_NM_custom as supplier_name, + c.CD_NM_charge1 as contact_person + FROM WH_sub ws + INNER JOIN WH_main wm ON ws.WH_SR_stock = wm.WH_NO_stock + LEFT JOIN PM_BASE.dbo.CD_custom c ON wm.WH_CD_cust_sale = c.CD_CD_custom + WHERE ws.DrugCode = :drug_code + AND wm.WH_DT_appl BETWEEN :start_date AND :end_date + ORDER BY wm.WH_DT_appl DESC + """), {'drug_code': drug_code, 'start_date': start_date, 'end_date': end_date}) + + items = [] + for row in result: + qty = float(row.quantity) if row.quantity else 0 + price = float(row.unit_price) if row.unit_price else 0 + items.append({ + 'import_date': row.import_date or '', + 'quantity': qty, + 'unit_price': price, + 'amount': qty * price, + 'supplier_name': row.supplier_name or '', + 'person_name': row.contact_person or '' + }) + + return jsonify({ + 'success': True, + 'drug_code': drug_code, + 'total_count': len(items), + 'items': items + }) + except Exception as e: + logging.error(f"drug-usage imports API 오류: {e}") + return jsonify({'success': False, 'error': str(e)}), 500 + + +@app.route('/api/drug-usage//prescriptions') +def api_drug_usage_prescriptions(drug_code): + """약품별 조제(매출) 상세 API""" + start_date = request.args.get('start_date', '') + end_date = request.args.get('end_date', '') + + if not start_date or not end_date: + return jsonify({'success': False, 'error': 'start_date, end_date 필수'}), 400 + + try: + pres_session = db_manager.get_session('PM_PRES') + + result = pres_session.execute(text(""" + SELECT + pm.Indate as rx_date, + CONVERT(varchar, DATEADD(day, sp.Days, CONVERT(date, pm.Indate, 112)), 112) as expiry_date, + pm.Paname as patient_name, + pm.OrderName as institution_name, + sp.QUAN as dosage, + sp.QUAN_TIME as frequency, + sp.Days as days + FROM PS_sub_pharm sp + INNER JOIN PS_main pm ON pm.PreSerial = sp.PreSerial + WHERE sp.DrugCode = :drug_code + AND pm.Indate BETWEEN :start_date AND :end_date + AND (sp.PS_Type IS NULL OR sp.PS_Type != '9') + ORDER BY pm.Indate DESC + """), {'drug_code': drug_code, 'start_date': start_date, 'end_date': end_date}) + + items = [] + 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 + items.append({ + 'rx_date': row.rx_date or '', + 'expiry_date': row.expiry_date or '', + 'patient_name': row.patient_name or '', + 'institution_name': row.institution_name or '', + 'dosage': dosage, + 'frequency': freq, + 'days': days, + 'total_qty': dosage * freq * days + }) + + return jsonify({ + 'success': True, + 'drug_code': drug_code, + 'total_count': len(items), + 'items': items + }) + except Exception as e: + logging.error(f"drug-usage prescriptions API 오류: {e}") + return jsonify({'success': False, 'error': str(e)}), 500 + + if __name__ == '__main__': import os diff --git a/backend/check_2024_apc.py b/backend/check_2024_apc.py new file mode 100644 index 0000000..aba84a6 --- /dev/null +++ b/backend/check_2024_apc.py @@ -0,0 +1,32 @@ +# -*- 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;' +) +conn = pyodbc.connect(conn_str, timeout=10) +cursor = conn.cursor() + +# 2024년 이후 APC (9xx로 시작) 확인 +cursor.execute(''' + SELECT G.GoodsName, U.CD_CD_BARCODE + FROM CD_GOODS G + JOIN CD_ITEM_UNIT_MEMBER U ON G.DrugCode = U.DRUGCODE + WHERE G.POS_BOON = '010103' + AND G.GoodsSelCode = 'B' + AND U.CD_CD_BARCODE LIKE '9%' + AND LEN(U.CD_CD_BARCODE) = 13 + ORDER BY G.GoodsName +''') + +rows = cursor.fetchall() +print(f'=== 2024년 이후 APC 제품: {len(rows)}건 ===') +for row in rows: + print(f' {row.GoodsName} | APC: {row.CD_CD_BARCODE}') + +conn.close() diff --git a/backend/check_oridermyl.py b/backend/check_oridermyl.py new file mode 100644 index 0000000..fcaad85 --- /dev/null +++ b/backend/check_oridermyl.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +from sqlalchemy import create_engine, text + +engine = create_engine('postgresql://admin:trajet6640@192.168.0.87:5432/apdb_master') +with engine.connect() as conn: + # 오리더밀 검색 + result = conn.execute(text(""" + SELECT apc, product_name, item_seq, + llm_pharm->>'분류' as category, + llm_pharm->>'간이분류' as easy_category, + image_url1 + FROM apc + WHERE product_name ILIKE '%오리더밀%' + ORDER BY apc + """)) + + print('=== PostgreSQL 오리더밀 검색 결과 ===') + for row in result: + print(f'APC: {row.apc}') + print(f' 제품명: {row.product_name}') + print(f' item_seq: {row.item_seq}') + print(f' 분류: {row.category}') + print(f' 간이분류: {row.easy_category}') + print(f' 이미지: {row.image_url1}') + print() diff --git a/backend/check_real_2024_apc.py b/backend/check_real_2024_apc.py new file mode 100644 index 0000000..aae2cbf --- /dev/null +++ b/backend/check_real_2024_apc.py @@ -0,0 +1,35 @@ +# -*- 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;' +) +conn = pyodbc.connect(conn_str, timeout=10) +cursor = conn.cursor() + +# 정식 2024년 APC (92%로 시작) 확인 +cursor.execute(''' + SELECT G.GoodsName, U.CD_CD_BARCODE + FROM CD_GOODS G + JOIN CD_ITEM_UNIT_MEMBER U ON G.DrugCode = U.DRUGCODE + WHERE G.POS_BOON = '010103' + AND G.GoodsSelCode = 'B' + AND U.CD_CD_BARCODE LIKE '92%' + AND LEN(U.CD_CD_BARCODE) = 13 + ORDER BY G.GoodsName +''') + +rows = cursor.fetchall() +print(f'=== 정식 2024년 APC (92%) 제품: {len(rows)}건 ===') +for row in rows: + print(f' {row.GoodsName} | APC: {row.CD_CD_BARCODE}') + +if len(rows) == 0: + print(' (없음 - 아직 2024년 이후 허가 제품이 등록 안 됨)') + +conn.close() diff --git a/backend/check_tiergard.py b/backend/check_tiergard.py new file mode 100644 index 0000000..15af3ef --- /dev/null +++ b/backend/check_tiergard.py @@ -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_DRUG;' + 'UID=sa;' + 'PWD=tmddls214!%(;' + 'TrustServerCertificate=yes;' +) +conn = pyodbc.connect(conn_str, timeout=10) +cursor = conn.cursor() + +cursor.execute(''' + SELECT G.GoodsName, G.Saleprice, ISNULL(IT.IM_QT_sale_debit, 0) AS Stock + FROM CD_GOODS G + LEFT JOIN IM_total IT ON G.DrugCode = IT.DrugCode + WHERE G.GoodsName LIKE '%티어가드%' + ORDER BY G.GoodsName +''') + +rows = cursor.fetchall() +print('=== 티어가드 보유 현황 ===') +for row in rows: + print(f'{row.GoodsName} | {row.Saleprice:,.0f}원 | 재고: {int(row.Stock)}개') + +conn.close() diff --git a/backend/check_tiergard_detail.py b/backend/check_tiergard_detail.py new file mode 100644 index 0000000..b8fa2cf --- /dev/null +++ b/backend/check_tiergard_detail.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +from sqlalchemy import create_engine, text +import json + +engine = create_engine('postgresql://admin:trajet6640@192.168.0.87:5432/apdb_master') +with engine.connect() as conn: + result = conn.execute(text(""" + SELECT apc, product_name, llm_pharm, main_ingredient, component_name_ko + FROM apc + WHERE product_name ILIKE '%티어가드%60mg%' + ORDER BY apc + LIMIT 3 + """)) + + print('=== 티어가드 60mg 허가사항 상세 ===') + for row in result: + print(f'APC: {row.apc}') + print(f'제품명: {row.product_name}') + print(f'main_ingredient: {row.main_ingredient}') + print(f'component_name_ko: {row.component_name_ko}') + if row.llm_pharm: + llm = row.llm_pharm if isinstance(row.llm_pharm, dict) else json.loads(row.llm_pharm) + print('llm_pharm 내용:') + for k, v in llm.items(): + print(f' {k}: {v}') + print() diff --git a/backend/check_tiergard_llm.py b/backend/check_tiergard_llm.py new file mode 100644 index 0000000..7e5fa83 --- /dev/null +++ b/backend/check_tiergard_llm.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +from sqlalchemy import create_engine, text +import json + +engine = create_engine('postgresql://admin:trajet6640@192.168.0.87:5432/apdb_master') +with engine.connect() as conn: + # llm_pharm이 있는 티어가드 확인 + result = conn.execute(text(""" + SELECT apc, product_name, llm_pharm + FROM apc + WHERE product_name ILIKE '%티어가드%' + AND llm_pharm IS NOT NULL + AND llm_pharm::text != '{}' + ORDER BY apc + """)) + + print('=== 티어가드 llm_pharm 있는 항목 ===') + for row in result: + print(f'APC: {row.apc}') + print(f'제품명: {row.product_name}') + if row.llm_pharm: + llm = row.llm_pharm if isinstance(row.llm_pharm, dict) else json.loads(row.llm_pharm) + print('llm_pharm:') + for k, v in llm.items(): + if v: + print(f' {k}: {v}') + print() diff --git a/backend/check_tiergard_pg.py b/backend/check_tiergard_pg.py new file mode 100644 index 0000000..1a2875f --- /dev/null +++ b/backend/check_tiergard_pg.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +from sqlalchemy import create_engine, text + +engine = create_engine('postgresql://admin:trajet6640@192.168.0.87:5432/apdb_master') +with engine.connect() as conn: + result = conn.execute(text(""" + SELECT apc, product_name, + llm_pharm->>'체중/부위' as dosage, + llm_pharm->>'주성분' as ingredient + FROM apc + WHERE product_name ILIKE '%티어가드%' + ORDER BY apc + """)) + + print('=== PostgreSQL 티어가드 전체 규격 ===') + for row in result: + print(f'APC: {row.apc}') + print(f' 제품명: {row.product_name}') + print(f' 용량: {row.dosage}') + print(f' 성분: {row.ingredient}') + print() diff --git a/backend/templates/admin_drug_usage.html b/backend/templates/admin_drug_usage.html new file mode 100644 index 0000000..8eb825e --- /dev/null +++ b/backend/templates/admin_drug_usage.html @@ -0,0 +1,967 @@ + + + + + + 기간별 사용약품 조회 - 청춘약국 + + + + + + +
+ +

📊 기간별 사용약품 조회

+

기간 내 조제 및 입고 현황을 한눈에 확인

+
+ +
+ +
+
+
+ +
+ + ~ + +
+
+
+ +
+ + +
+
+
+ + +
+ +
+
+ + + + + +
+
+ + + + + + + + + + + + + + + + + +
약품코드약품명분류조제건수입고건수조제량입고량
+
📊
+

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

+
+
+
+ + + +
+ + + +