diff --git a/backend/app.py b/backend/app.py index 3e04016..f4d45e1 100644 --- a/backend/app.py +++ b/backend/app.py @@ -3125,6 +3125,23 @@ def api_products(): if animal_only and not is_animal: continue + # APC 조회 (동물약인 경우) + apc = None + if is_animal: + try: + apc_result = drug_session.execute(text(""" + SELECT TOP 1 CD_CD_BARCODE + FROM CD_ITEM_UNIT_MEMBER + WHERE DRUGCODE = :drug_code AND CD_CD_BARCODE LIKE '023%' + """), {'drug_code': row.drug_code}) + apc_row = apc_result.fetchone() + if apc_row: + apc = apc_row[0] + elif row.barcode: + apc = row.barcode # 바코드=APC 케이스 + except: + pass + items.append({ 'drug_code': row.drug_code or '', 'product_name': row.product_name or '', @@ -3134,9 +3151,44 @@ def api_products(): 'supplier': row.supplier or '', 'is_set': bool(row.is_set), 'is_animal_drug': is_animal, - 'stock': int(row.stock) if row.stock else 0 + 'stock': int(row.stock) if row.stock else 0, + 'apc': apc, + 'category': None, # PostgreSQL에서 lazy fetch + 'wholesaler_stock': None }) + # 동물약 분류 Lazy Fetch (PostgreSQL) - 실패해도 무시 + animal_items = [i for i in items if i['is_animal_drug'] and i['apc']] + if animal_items: + try: + from sqlalchemy import create_engine + pg_engine = create_engine('postgresql://admin:trajet6640@192.168.0.87:5432/apdb_master', + connect_args={'connect_timeout': 3}) # 3초 타임아웃 + with pg_engine.connect() as conn: + apc_list = [i['apc'] for i in animal_items if i['apc']] + if apc_list: + placeholders = ','.join([f"'{a}'" for a in apc_list]) + # 분류 + 도매상 재고 조회 + result = conn.execute(text(f""" + SELECT + A.apc, + A.llm_pharm->>'분류' as category, + COALESCE(SUM(I.quantity), 0) as wholesaler_stock + FROM apc A + LEFT JOIN inventory I ON I.apdb_id = A.idx + WHERE A.apc IN ({placeholders}) + GROUP BY A.apc, A.llm_pharm + """)) + pg_map = {row.apc: {'category': row.category, 'ws': int(row.wholesaler_stock)} for row in result} + + for item in items: + if item['apc'] and item['apc'] in pg_map: + item['category'] = pg_map[item['apc']]['category'] + item['wholesaler_stock'] = pg_map[item['apc']]['ws'] + except Exception as pg_err: + logging.warning(f"PostgreSQL 분류 조회 실패 (무시): {pg_err}") + # PostgreSQL 실패해도 MSSQL 데이터는 정상 반환 + return jsonify({ 'success': True, 'items': items, diff --git a/backend/templates/admin_products.html b/backend/templates/admin_products.html index 654ccae..14e6a20 100644 --- a/backend/templates/admin_products.html +++ b/backend/templates/admin_products.html @@ -725,12 +725,23 @@ return; } - tbody.innerHTML = productsData.map((item, idx) => ` + tbody.innerHTML = productsData.map((item, idx) => { + // 분류 뱃지 (동물약만) + const categoryBadge = item.category + ? `${escapeHtml(item.category)}` + : ''; + // 도매상 재고 표시 (동물약만) + const wsStock = (item.wholesaler_stock && item.wholesaler_stock > 0) + ? `(도매 ${item.wholesaler_stock})` + : ''; + + return `
${escapeHtml(item.product_name)} ${item.is_animal_drug ? '🐾 동물약' : ''} + ${categoryBadge}
${escapeHtml(item.supplier) || ''}
@@ -738,13 +749,13 @@ ${item.barcode ? `${item.barcode}` : `없음`} - ${item.stock || 0} + ${item.stock || 0}${wsStock} ${formatPrice(item.sale_price)} - `).join(''); + `}).join(''); } // ── QR 인쇄 관련 ──