diff --git a/backend/ecosystem.config.js b/backend/ecosystem.config.js new file mode 100644 index 0000000..85cb7fb --- /dev/null +++ b/backend/ecosystem.config.js @@ -0,0 +1,18 @@ +module.exports = { + apps: [ + { + name: 'pharmacy-flask', + script: 'python', + args: 'app.py', + cwd: 'c:\\Users\\청춘약국\\source\\pharmacy-pos-qr-system\\backend', + interpreter: 'none', + watch: false, + autorestart: true, + max_restarts: 10, + env: { + FLASK_ENV: 'production', + PYTHONIOENCODING: 'utf-8' + } + } + ] +}; diff --git a/backend/scripts/batch_apc_matching.py b/backend/scripts/batch_apc_matching.py new file mode 100644 index 0000000..9d54ec9 --- /dev/null +++ b/backend/scripts/batch_apc_matching.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +""" +동물약 일괄 APC 매칭 - 후보 찾기 +""" +import sys, io +sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') +sys.path.insert(0, 'c:\\Users\\청춘약국\\source\\pharmacy-pos-qr-system\\backend') + +from db.dbsetup import get_db_session +from sqlalchemy import text, create_engine + +# 1. MSSQL 동물약 (APC 없는 것만) +session = get_db_session('PM_DRUG') +result = session.execute(text(""" + SELECT + G.DrugCode, + G.GoodsName, + G.Saleprice, + ( + SELECT TOP 1 U.CD_CD_BARCODE + FROM CD_ITEM_UNIT_MEMBER U + WHERE U.DRUGCODE = G.DrugCode + AND U.CD_CD_BARCODE LIKE '023%' + ) AS APC_CODE + FROM CD_GOODS G + WHERE G.POS_BOON = '010103' + AND G.GoodsSelCode = 'B' + ORDER BY G.GoodsName +""")) + +no_apc = [] +for row in result: + if not row.APC_CODE: + no_apc.append({ + 'code': row.DrugCode, + 'name': row.GoodsName, + 'price': row.Saleprice + }) + +session.close() + +print(f'=== APC 없는 동물약: {len(no_apc)}개 ===\n') + +# 2. PostgreSQL에서 매칭 후보 찾기 +pg = create_engine('postgresql://admin:trajet6640@192.168.0.87:5432/apdb_master').connect() + +matches = [] +for drug in no_apc: + name = drug['name'] + # 제품명에서 검색 키워드 추출 + # (판) 제거, 괄호 내용 제거 + search_name = name.replace('(판)', '').split('(')[0].strip() + + # PostgreSQL 검색 + result = pg.execute(text(""" + SELECT apc, product_name, + llm_pharm->>'사용가능 동물' as target, + llm_pharm->>'분류' as category + FROM apc + WHERE product_name ILIKE :pattern + ORDER BY LENGTH(product_name) + LIMIT 5 + """), {'pattern': f'%{search_name}%'}) + + candidates = list(result) + if candidates: + matches.append({ + 'mssql': drug, + 'candidates': candidates + }) + print(f'✅ {name}') + for c in candidates[:2]: + print(f' → {c.apc}: {c.product_name[:40]}... [{c.target or "?"}]') + else: + print(f'❌ {name} - 매칭 없음') + +pg.close() + +print(f'\n=== 요약 ===') +print(f'APC 없는 제품: {len(no_apc)}개') +print(f'매칭 후보 있음: {len(matches)}개') +print(f'매칭 없음: {len(no_apc) - len(matches)}개') diff --git a/backend/scripts/batch_insert_apc.py b/backend/scripts/batch_insert_apc.py new file mode 100644 index 0000000..479719f --- /dev/null +++ b/backend/scripts/batch_insert_apc.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +""" +확실한 매칭만 일괄 등록 +""" +import sys, io +sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') +sys.path.insert(0, 'c:\\Users\\청춘약국\\source\\pharmacy-pos-qr-system\\backend') + +from db.dbsetup import get_db_session +from sqlalchemy import text +from datetime import datetime + +# 확실한 매칭 목록 (MSSQL 제품명, DrugCode, APC) +MAPPINGS = [ + # 파라캅 + ('파라캅L(5kg이상)', 'LB000003159', '0230338510101'), # 파라캅 L 정 10정 + ('파라캅S(5kg이하)', 'LB000003160', '0230347110106'), # 파라캅 에스 정 10정 + # 세레니아 + ('세레니아정16mg(개멀미약)', 'LB000003353', '0231884610109'), # 세레니아 정 16mg / 4정 + ('세레니아정24mg(개멀미약)', 'LB000003354', '0231884620107'), # 세레니아 정 24mg / 4정 +] + +session = get_db_session('PM_DRUG') +today = datetime.now().strftime('%Y%m%d') + +print('=== 일괄 APC 매핑 ===\n') + +for name, drugcode, apc in MAPPINGS: + # 기존 가격 조회 + existing = session.execute(text(""" + SELECT TOP 1 CD_MY_UNIT, CD_IN_UNIT + FROM CD_ITEM_UNIT_MEMBER + WHERE DRUGCODE = :dc + ORDER BY SN DESC + """), {'dc': drugcode}).fetchone() + + if not existing: + print(f'❌ {name}: 기존 레코드 없음') + continue + + # 이미 APC 있는지 확인 + check = session.execute(text(""" + SELECT 1 FROM CD_ITEM_UNIT_MEMBER + WHERE DRUGCODE = :dc AND CD_CD_BARCODE = :apc + """), {'dc': drugcode, 'apc': apc}).fetchone() + + if check: + print(f'⏭️ {name}: 이미 등록됨') + continue + + # INSERT + try: + session.execute(text(""" + INSERT INTO CD_ITEM_UNIT_MEMBER ( + DRUGCODE, CD_CD_UNIT, CD_NM_UNIT, CD_MY_UNIT, CD_IN_UNIT, + CD_CD_BARCODE, CD_CD_POS, CHANGE_DATE + ) VALUES ( + :drugcode, '015', 1.0, :my_unit, :in_unit, + :barcode, '', :change_date + ) + """), { + 'drugcode': drugcode, + 'my_unit': existing.CD_MY_UNIT, + 'in_unit': existing.CD_IN_UNIT, + 'barcode': apc, + 'change_date': today + }) + session.commit() + print(f'✅ {name} → {apc}') + except Exception as e: + session.rollback() + print(f'❌ {name}: {e}') + +session.close() +print('\n완료!') diff --git a/backend/scripts/debug_matching.py b/backend/scripts/debug_matching.py new file mode 100644 index 0000000..b74915d --- /dev/null +++ b/backend/scripts/debug_matching.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +import sys +import io +sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') + +# 테스트 AI 응답 (실제 응답 시뮬레이션) +ai_response = """ +네, 안텔민은 개와 고양이 모두 사용 가능합니다! + +**안텔민 킹** - 체중 5kg 이상 반려동물용 +**안텔민 뽀삐** - 체중 5kg 이하 소형 반려동물용 + +두 제품 모두 개와 고양이의 내부 기생충 구제에 효과적입니다. +""" + +animal_drugs = [ + {'name': '안텔민킹(5kg이상)', 'code': 'LB000003157'}, + {'name': '안텔민뽀삐(5kg이하)', 'code': 'LB000003158'}, + {'name': '다이로하트정M(12~22kg)', 'code': 'LB000003151'}, +] + +print('=== 현재 매칭 로직 테스트 ===\n') +print(f'AI 응답:\n{ai_response}\n') +print('=' * 50) + +ai_response_lower = ai_response.lower() + +for drug in animal_drugs: + drug_name = drug['name'] + base_name = drug_name.split('(')[0].split('/')[0].strip() + + # suffix 제거 + original_base = base_name + for suffix in ['정', '액', 'L', 'M', 'S', 'XL', 'XS', 'SS', 'mini']: + if base_name.endswith(suffix): + base_name = base_name[:-len(suffix)] + base_name = base_name.strip() + + matched = base_name.lower() in ai_response_lower + + print(f'\n제품: {drug_name}') + print(f' 괄호 앞: {original_base}') + print(f' suffix 제거 후: {base_name}') + print(f' 매칭 결과: {"✅ 매칭됨" if matched else "❌ 매칭 안됨"}') + + if not matched: + # 왜 안 됐는지 확인 + print(f' → "{base_name.lower()}" in 응답? {base_name.lower() in ai_response_lower}') + # 띄어쓰기 변형 체크 + spaced = base_name.replace('킹', ' 킹').replace('뽀삐', ' 뽀삐') + print(f' → 띄어쓰기 변형 "{spaced.lower()}" in 응답? {spaced.lower() in ai_response_lower}') diff --git a/backend/scripts/debug_matching2.py b/backend/scripts/debug_matching2.py new file mode 100644 index 0000000..683f111 --- /dev/null +++ b/backend/scripts/debug_matching2.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +import sys +import io +sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') + +# 실제 AI 응답 +ai_response = """안텔민은 개와 고양이 모두에게 사용할 수 있습니다만, 체중에 따라 복용할 용량이 다릅니다. 🐾 + +- **안텔민**: 5kg 이상 개와 고양이에게 복용 가능. +- **안텔민 뽀삐**: 5kg 미만 소형 반려동물에게 복용 가능. + +따라서, 반려동물의 체중에 맞는 적절한 제품을 선택해야 해요! 🐶 체중을 알려주시면 더 구체적으로 안내해 드릴 수 있어요.""" + +animal_drugs = [ + {'name': '안텔민', 'code': 'S0000001', 'apc': None}, + {'name': '안텔민킹(5kg이상)', 'code': 'LB000003157', 'apc': '0230237810109'}, + {'name': '안텔민뽀삐(5kg이하)', 'code': 'LB000003158', 'apc': '0230237010107'}, +] + +print('=== 매칭 테스트 ===\n') +print(f'AI 응답:\n{ai_response}\n') +print('=' * 50) + +ai_response_lower = ai_response.lower() +ai_response_nospace = ai_response_lower.replace(' ', '') + +for drug in animal_drugs: + drug_name = drug['name'] + base_name = drug_name.split('(')[0].split('/')[0].strip() + + for suffix in ['정', '액', 'L', 'M', 'S', 'XL', 'XS', 'SS', 'mini']: + if base_name.endswith(suffix): + base_name = base_name[:-len(suffix)] + base_name = base_name.strip() + + base_lower = base_name.lower() + base_nospace = base_lower.replace(' ', '') + + in_normal = base_lower in ai_response_lower + in_nospace = base_nospace in ai_response_nospace + matched = len(base_name) >= 2 and (in_normal or in_nospace) + + print(f'\n제품: {drug_name}') + print(f' base_name: "{base_name}"') + print(f' base_nospace: "{base_nospace}"') + print(f' 일반매칭: {in_normal}') + print(f' 공백제거매칭: {in_nospace}') + print(f' 최종: {"✅" if matched else "❌"}') diff --git a/backend/scripts/debug_prompt.py b/backend/scripts/debug_prompt.py new file mode 100644 index 0000000..91c5522 --- /dev/null +++ b/backend/scripts/debug_prompt.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +import sys +import io +sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') +sys.path.insert(0, 'c:\\Users\\청춘약국\\source\\pharmacy-pos-qr-system\\backend') + +from db.dbsetup import get_db_session +from sqlalchemy import text + +# _get_animal_drugs 로직 복제 +drug_session = get_db_session('PM_DRUG') +query = text(""" + SELECT + G.DrugCode, + G.GoodsName, + G.Saleprice, + ( + SELECT TOP 1 U.CD_CD_BARCODE + FROM CD_ITEM_UNIT_MEMBER U + WHERE U.DRUGCODE = G.DrugCode + AND U.CD_CD_BARCODE LIKE '023%' + ORDER BY U.CHANGE_DATE DESC + ) AS APC_CODE + FROM CD_GOODS G + WHERE G.POS_BOON = '010103' + AND G.GoodsSelCode = 'B' + ORDER BY G.GoodsName +""") +rows = drug_session.execute(query).fetchall() + +print('=== AI에 전달되는 보유 제품 목록 ===\n') +for r in rows: + apc = r.APC_CODE + rag_info = "" + if apc: + rag_info = f" [대상: 개, 고양이]" # RAG 정보 시뮬레이션 + + print(f"- {r.GoodsName} ({r.Saleprice:,.0f}원){rag_info}") + +print('\n=== 안텔민 관련 제품만 ===') +for r in rows: + if '안텔민' in r.GoodsName: + print(f" {r.GoodsName} - APC: {r.APC_CODE}") diff --git a/backend/scripts/debug_rag.py b/backend/scripts/debug_rag.py new file mode 100644 index 0000000..79c1e72 --- /dev/null +++ b/backend/scripts/debug_rag.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +import sys +import io +sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') + +from sqlalchemy import create_engine, text + +pg = create_engine('postgresql://admin:trajet6640@192.168.0.87:5432/apdb_master').connect() + +# 안텔민킹 RAG 정보 +apc = '0230237810109' +print(f'=== 안텔민킹 ({apc}) RAG 정보 ===\n') + +result = pg.execute(text(f""" + SELECT + product_name, + llm_pharm->>'사용가능 동물' as target_animals, + llm_pharm->>'분류' as category, + llm_pharm->>'체중/부위' as dosage_weight, + llm_pharm->>'월령금기' as age_restriction + FROM apc + WHERE apc = '{apc}' +""")) + +row = result.fetchone() +if row: + print(f'제품명: {row.product_name}') + print(f'사용가능 동물: {row.target_animals}') + print(f'분류: {row.category}') + print(f'체중/용량: {row.dosage_weight}') + print(f'월령금기: {row.age_restriction}') + +# efficacy_effect도 확인 +result2 = pg.execute(text(f""" + SELECT efficacy_effect FROM apc WHERE apc = '{apc}' +""")) +row2 = result2.fetchone() +if row2 and row2.efficacy_effect: + print(f'\n효능/효과 (원문 일부):') + print(row2.efficacy_effect[:500]) + +pg.close() diff --git a/backend/scripts/debug_rag_prompt.py b/backend/scripts/debug_rag_prompt.py new file mode 100644 index 0000000..1d3c602 --- /dev/null +++ b/backend/scripts/debug_rag_prompt.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +import sys +import io +sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') +sys.path.insert(0, 'c:\\Users\\청춘약국\\source\\pharmacy-pos-qr-system\\backend') + +from db.dbsetup import get_db_session +from sqlalchemy import text, create_engine + +# 1. _get_animal_drugs 시뮬레이션 +drug_session = get_db_session('PM_DRUG') +query = text(""" + SELECT + G.DrugCode, + G.GoodsName, + G.Saleprice, + ( + SELECT TOP 1 U.CD_CD_BARCODE + FROM CD_ITEM_UNIT_MEMBER U + WHERE U.DRUGCODE = G.DrugCode + AND U.CD_CD_BARCODE LIKE '023%' + ORDER BY U.CHANGE_DATE DESC + ) AS APC_CODE + FROM CD_GOODS G + WHERE G.POS_BOON = '010103' + AND G.GoodsSelCode = 'B' + ORDER BY G.GoodsName +""") +rows = drug_session.execute(query).fetchall() + +animal_drugs = [] +for r in rows: + animal_drugs.append({ + 'code': r.DrugCode, + 'name': r.GoodsName, + 'price': float(r.Saleprice) if r.Saleprice else 0, + 'apc': r.APC_CODE + }) + +# 2. _get_animal_drug_rag 시뮬레이션 +apc_codes = [d['apc'] for d in animal_drugs if d.get('apc')] +print(f'APC 코드 목록: {apc_codes}\n') + +rag_data = {} +if apc_codes: + pg = create_engine('postgresql://admin:trajet6640@192.168.0.87:5432/apdb_master').connect() + placeholders = ','.join([f"'{apc}'" for apc in apc_codes]) + result = pg.execute(text(f""" + SELECT apc, product_name, + llm_pharm->>'사용가능 동물' as target_animals, + llm_pharm->>'분류' as category, + llm_pharm->>'체중/부위' as dosage_weight, + llm_pharm->>'기간/용법' as usage_period, + llm_pharm->>'월령금기' as age_restriction + FROM apc + WHERE apc IN ({placeholders}) + """)) + for row in result: + rag_data[row.apc] = { + 'target_animals': row.target_animals or '정보 없음', + 'category': row.category or '', + 'dosage_weight': row.dosage_weight or '', + 'usage_period': row.usage_period or '', + 'age_restriction': row.age_restriction or '' + } + pg.close() + +print(f'RAG 데이터: {rag_data}\n') + +# 3. available_products_text 생성 +print('=== AI에 전달되는 제품 목록 (RAG 포함) ===\n') +for d in animal_drugs: + if '안텔민' in d['name']: + line = f"- {d['name']} ({d['price']:,.0f}원)" + if d.get('apc') and d['apc'] in rag_data: + info = rag_data[d['apc']] + details = [] + if info.get('target_animals'): + details.append(f"대상: {info['target_animals']}") + if info.get('dosage_weight'): + details.append(f"용량: {info['dosage_weight']}") + if info.get('age_restriction'): + details.append(f"금기: {info['age_restriction']}") + if details: + line += f" [{', '.join(details)}]" + print(line) diff --git a/backend/scripts/detailed_search.py b/backend/scripts/detailed_search.py new file mode 100644 index 0000000..0e5fb03 --- /dev/null +++ b/backend/scripts/detailed_search.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +import sys, io +sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') +from sqlalchemy import create_engine, text + +pg = create_engine('postgresql://admin:trajet6640@192.168.0.87:5432/apdb_master').connect() + +# 약국 제품 → PostgreSQL 매칭 (체중/용량 포함) +mappings = [ + # (약국제품명, 검색키워드) + ('제스타제(10정)', '제스타제', '10'), + ('파라캅L(5kg이상)', '파라캅', 'L'), + ('파라캅S(5kg이하)', '파라캅', 'S'), + ('하트캅츄어블(11kg이하)', '하트캅', '11'), + ('넥스가드L(15~30kg)', '넥스가드', '15'), + ('넥스가드xs(2~3.5kg)', '넥스가드', '2'), + ('다이로하트정M(12~22kg)', '다이로하트', '12'), + ('다이로하트정S(5.6~11kg)', '다이로하트', '5.6'), + ('다이로하트정SS(5.6kg이하)', '다이로하트', 'SS'), + ('세레니아정16mg(개멀미약)', '세레니아', '16'), + ('세레니아정24mg(개멀미약)', '세레니아', '24'), + ('하트세이버츄어블M(12~22kg)', '하트세이버', '12'), + ('하트세이버츄어블S(5.6~11kg)', '하트세이버', '5.6'), + ('하트웜솔루션츄어블M(12~22kg)', '하트웜', '12'), + ('하트웜솔루션츄어블S(11kg이하)', '하트웜', '11'), +] + +print('=== 상세 매칭 검색 ===\n') + +for pharm_name, keyword, size in mappings: + result = pg.execute(text(""" + SELECT apc, product_name, packaging, + llm_pharm->>'사용가능 동물' as target + FROM apc + WHERE product_name ILIKE :kw + ORDER BY product_name + LIMIT 10 + """), {'kw': f'%{keyword}%'}) + + print(f'\n📦 {pharm_name} (검색: {keyword}, 사이즈: {size})') + for r in result: + mark = '⭐' if size.lower() in r.product_name.lower() else ' ' + print(f'{mark} {r.apc}: {r.product_name[:50]}') + +pg.close() diff --git a/backend/scripts/insert_apc_gesidin.py b/backend/scripts/insert_apc_gesidin.py new file mode 100644 index 0000000..be3a49c --- /dev/null +++ b/backend/scripts/insert_apc_gesidin.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +import sys, io +sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') +sys.path.insert(0, 'c:\\Users\\청춘약국\\source\\pharmacy-pos-qr-system\\backend') + +from db.dbsetup import get_db_session +from sqlalchemy import text +from datetime import datetime + +session = get_db_session('PM_DRUG') + +# 1. 기존 데이터에서 가격 정보 가져오기 +print('1. 기존 레코드에서 가격 정보 조회...') +existing = session.execute(text(""" + SELECT TOP 1 CD_MY_UNIT, CD_IN_UNIT + FROM CD_ITEM_UNIT_MEMBER + WHERE DRUGCODE = 'LB000003140' + ORDER BY SN DESC +""")).fetchone() + +sale_price = existing.CD_MY_UNIT +purchase_price = existing.CD_IN_UNIT +print(f' 판매가: {sale_price:,.0f}원') +print(f' 입고가: {purchase_price:,.0f}원') + +# 2. 오늘 날짜 +today = datetime.now().strftime('%Y%m%d') +print(f'\n2. 날짜: {today}') + +# 3. INSERT 실행 +print('\n3. INSERT 실행...') +apc_code = '0231093520106' # 복합개시딘 10g + +try: + session.execute(text(""" + INSERT INTO CD_ITEM_UNIT_MEMBER ( + DRUGCODE, CD_CD_UNIT, CD_NM_UNIT, CD_MY_UNIT, CD_IN_UNIT, + CD_CD_BARCODE, CD_CD_POS, CHANGE_DATE + ) VALUES ( + :drugcode, :unit, :nm_unit, :my_unit, :in_unit, + :barcode, :pos, :change_date + ) + """), { + 'drugcode': 'LB000003140', + 'unit': '015', + 'nm_unit': 1.0, + 'my_unit': sale_price, + 'in_unit': purchase_price, + 'barcode': apc_code, + 'pos': '', + 'change_date': today + }) + + session.commit() + print(f' ✅ 성공! APC {apc_code} 추가됨') + + # 4. 확인 + print('\n4. 결과 확인...') + result = session.execute(text(""" + SELECT DRUGCODE, CD_CD_BARCODE, CD_MY_UNIT, SN + FROM CD_ITEM_UNIT_MEMBER + WHERE DRUGCODE = 'LB000003140' AND CD_CD_BARCODE = :apc + """), {'apc': apc_code}) + + row = result.fetchone() + if row: + print(f' DRUGCODE: {row.DRUGCODE}') + print(f' BARCODE: {row.CD_CD_BARCODE}') + print(f' SN: {row.SN}') + +except Exception as e: + session.rollback() + print(f' ❌ 실패: {e}') + +session.close() diff --git a/backend/scripts/insert_apc_poppy.py b/backend/scripts/insert_apc_poppy.py new file mode 100644 index 0000000..7d046a0 --- /dev/null +++ b/backend/scripts/insert_apc_poppy.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- +""" +안텔민뽀삐 APC 추가 실행 (SN 자동 생성) +""" +import sys +import io +sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') +sys.path.insert(0, 'c:\\Users\\청춘약국\\source\\pharmacy-pos-qr-system\\backend') + +from db.dbsetup import get_db_session +from sqlalchemy import text +from datetime import datetime + +session = get_db_session('PM_DRUG') + +# 1. 기존 데이터에서 가격 정보 가져오기 +print('1. 기존 레코드에서 가격 정보 조회...') +existing = session.execute(text(""" + SELECT TOP 1 CD_MY_UNIT, CD_IN_UNIT + FROM CD_ITEM_UNIT_MEMBER + WHERE DRUGCODE = 'LB000003158' + ORDER BY SN DESC +""")).fetchone() + +sale_price = existing.CD_MY_UNIT +purchase_price = existing.CD_IN_UNIT +print(f' 판매가: {sale_price:,.0f}원') +print(f' 입고가: {purchase_price:,.0f}원') + +# 2. 오늘 날짜 +today = datetime.now().strftime('%Y%m%d') +print(f'\n2. 날짜: {today}') + +# 3. INSERT 실행 (SN은 IDENTITY 자동 생성) +print('\n3. INSERT 실행...') +apc_code = '0230237010107' # 안텔민뽀삐 10정 + +try: + session.execute(text(""" + INSERT INTO CD_ITEM_UNIT_MEMBER ( + DRUGCODE, + CD_CD_UNIT, + CD_NM_UNIT, + CD_MY_UNIT, + CD_IN_UNIT, + CD_CD_BARCODE, + CD_CD_POS, + CHANGE_DATE + ) VALUES ( + :drugcode, + :unit, + :nm_unit, + :my_unit, + :in_unit, + :barcode, + :pos, + :change_date + ) + """), { + 'drugcode': 'LB000003158', + 'unit': '015', + 'nm_unit': 1.0, + 'my_unit': sale_price, + 'in_unit': purchase_price, + 'barcode': apc_code, + 'pos': '', + 'change_date': today + }) + + session.commit() + print(f' ✅ 성공! APC {apc_code} 추가됨') + + # 4. 확인 + print('\n4. 결과 확인...') + result = session.execute(text(""" + SELECT * FROM CD_ITEM_UNIT_MEMBER + WHERE DRUGCODE = 'LB000003158' AND CD_CD_BARCODE = :apc + """), {'apc': apc_code}) + + row = result.fetchone() + if row: + print(' --- 추가된 레코드 ---') + for col in result.keys(): + print(f' {col}: {getattr(row, col)}') + +except Exception as e: + session.rollback() + print(f' ❌ 실패: {e}') + +session.close() diff --git a/backend/scripts/prepare_apc_insert.py b/backend/scripts/prepare_apc_insert.py new file mode 100644 index 0000000..62d488d --- /dev/null +++ b/backend/scripts/prepare_apc_insert.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +""" +안텔민뽀삐 APC 추가 준비 스크립트 +- CD_ITEM_UNIT_MEMBER 구조 확인 +- 안텔민킹 레코드 참고 +- INSERT 쿼리 생성 (실행 안 함) +""" +import sys +import io +sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') +sys.path.insert(0, 'c:\\Users\\청춘약국\\source\\pharmacy-pos-qr-system\\backend') + +from db.dbsetup import get_db_session +from sqlalchemy import text + +session = get_db_session('PM_DRUG') + +print('=' * 60) +print('1. CD_ITEM_UNIT_MEMBER 테이블 구조') +print('=' * 60) +result = session.execute(text(""" + SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, CHARACTER_MAXIMUM_LENGTH + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_NAME = 'CD_ITEM_UNIT_MEMBER' + ORDER BY ORDINAL_POSITION +""")) +for r in result: + nullable = 'NULL' if r.IS_NULLABLE == 'YES' else 'NOT NULL' + length = f'({r.CHARACTER_MAXIMUM_LENGTH})' if r.CHARACTER_MAXIMUM_LENGTH else '' + print(f' {r.COLUMN_NAME}: {r.DATA_TYPE}{length} {nullable}') + +print('\n' + '=' * 60) +print('2. 안텔민킹 APC 레코드 (참고용)') +print('=' * 60) +result = session.execute(text(""" + SELECT * FROM CD_ITEM_UNIT_MEMBER + WHERE DRUGCODE = 'LB000003157' + AND CD_CD_BARCODE LIKE '023%' +""")) +row = result.fetchone() +if row: + cols = result.keys() + for col in cols: + val = getattr(row, col) + print(f' {col}: {val}') + +print('\n' + '=' * 60) +print('3. 안텔민뽀삐 현재 레코드') +print('=' * 60) +result2 = session.execute(text(""" + SELECT * FROM CD_ITEM_UNIT_MEMBER + WHERE DRUGCODE = 'LB000003158' + ORDER BY SN DESC +""")) +rows = list(result2) +print(f' 총 {len(rows)}개 레코드') +for row in rows[:3]: + print(f'\n --- SN: {row.SN} ---') + cols = result2.keys() + for col in cols: + val = getattr(row, col) + print(f' {col}: {val}') + +print('\n' + '=' * 60) +print('4. 다음 SN 값 확인') +print('=' * 60) +result3 = session.execute(text("SELECT MAX(SN) as max_sn FROM CD_ITEM_UNIT_MEMBER")) +max_sn = result3.fetchone().max_sn +print(f' 현재 MAX(SN): {max_sn}') +print(f' 다음 SN: {max_sn + 1}') + +session.close() + +print('\n' + '=' * 60) +print('5. PostgreSQL에서 안텔민뽀삐 APC 확인') +print('=' * 60) +from sqlalchemy import create_engine +pg = create_engine('postgresql://admin:trajet6640@192.168.0.87:5432/apdb_master').connect() +result4 = pg.execute(text(""" + SELECT apc, product_name + FROM apc + WHERE product_name ILIKE '%안텔민%뽀삐%' OR product_name ILIKE '%안텔민%5kg%이하%' + ORDER BY apc +""")) +for r in result4: + print(f' APC: {r.apc}') + print(f' 제품명: {r.product_name}') + print() +pg.close() diff --git a/backend/scripts/show_llm_pharm.py b/backend/scripts/show_llm_pharm.py new file mode 100644 index 0000000..e1c9483 --- /dev/null +++ b/backend/scripts/show_llm_pharm.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +import sys, io, json +sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') +from sqlalchemy import create_engine, text + +pg = create_engine('postgresql://admin:trajet6640@192.168.0.87:5432/apdb_master').connect() + +# 안텔민킹 llm_pharm 전체 확인 +result = pg.execute(text(""" + SELECT product_name, llm_pharm FROM apc WHERE apc = '0230237810109' +""")) +row = result.fetchone() + +print('=== 안텔민킹 llm_pharm 전체 키 ===\n') +data = row.llm_pharm +for k in sorted(data.keys()): + val = str(data[k]) + if len(val) > 60: + val = val[:60] + '...' + print(f' {k}: {val}') + +# 동물약 전체 개수 +print('\n=== PostgreSQL 동물약 전체 개수 ===') +result2 = pg.execute(text("SELECT COUNT(*) FROM apc")) +print(f' 전체: {result2.fetchone()[0]}개') + +pg.close() diff --git a/backend/scripts/update_gesidin_category.py b/backend/scripts/update_gesidin_category.py new file mode 100644 index 0000000..e5f85fc --- /dev/null +++ b/backend/scripts/update_gesidin_category.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +import sys, io +sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') +sys.path.insert(0, 'c:\\Users\\청춘약국\\source\\pharmacy-pos-qr-system\\backend') + +from db.dbsetup import get_db_session +from sqlalchemy import text + +session = get_db_session('PM_DRUG') + +print('1. 현재 상태 확인...') +result = session.execute(text(""" + SELECT DrugCode, GoodsName, POS_BOON + FROM CD_GOODS + WHERE DrugCode = 'LB000003140' +""")) +row = result.fetchone() +print(f' {row.GoodsName}: POS_BOON = {row.POS_BOON}') + +print('\n2. POS_BOON을 동물약(010103)으로 업데이트...') +try: + session.execute(text(""" + UPDATE CD_GOODS + SET POS_BOON = '010103' + WHERE DrugCode = 'LB000003140' + """)) + session.commit() + print(' ✅ 성공!') + + # 확인 + result2 = session.execute(text(""" + SELECT DrugCode, GoodsName, POS_BOON + FROM CD_GOODS + WHERE DrugCode = 'LB000003140' + """)) + row2 = result2.fetchone() + print(f' {row2.GoodsName}: POS_BOON = {row2.POS_BOON}') + +except Exception as e: + session.rollback() + print(f' ❌ 실패: {e}') + +session.close()