#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 기존 inventory_lots에 variant 정보 적용 """ import sqlite3 import json from datetime import datetime def get_connection(): """데이터베이스 연결""" return sqlite3.connect('database/kdrug.db') def check_current_lots(): """현재 inventory_lots 상태 확인""" conn = get_connection() cursor = conn.cursor() print("\n" + "="*80) print("현재 입고된 Inventory Lots 현황") print("="*80) cursor.execute(""" SELECT l.lot_id, l.herb_item_id, h.herb_name, h.insurance_code, l.quantity_onhand, l.unit_price_per_g, l.origin_country, l.received_date, s.name as supplier_name, l.display_name FROM inventory_lots l JOIN herb_items h ON l.herb_item_id = h.herb_item_id JOIN purchase_receipt_lines prl ON l.receipt_line_id = prl.line_id JOIN purchase_receipts pr ON prl.receipt_id = pr.receipt_id JOIN suppliers s ON pr.supplier_id = s.supplier_id ORDER BY l.received_date DESC, h.herb_name """) lots = cursor.fetchall() print(f"\n총 {len(lots)}개의 로트가 있습니다.\n") for lot in lots[:10]: # 처음 10개만 출력 print(f"Lot #{lot[0]}: {lot[2]} (보험코드: {lot[3]})") print(f" - 재고: {lot[4]:.1f}g, 단가: {lot[5]:.1f}원/g") print(f" - 원산지: {lot[6]}, 공급처: {lot[8]}") print(f" - display_name: {lot[9] if lot[9] else '없음'}") print() conn.close() return lots def insert_sample_catalog_data(): """공급처 카탈로그 샘플 데이터 추가""" conn = get_connection() cursor = conn.cursor() print("\n" + "="*80) print("공급처 카탈로그 샘플 데이터 추가") print("="*80) # 휴먼허브 supplier_id 조회 cursor.execute("SELECT supplier_id FROM suppliers WHERE name = '(주)휴먼허브'") supplier_id = cursor.fetchone() if not supplier_id: print("휴먼허브 공급처를 찾을 수 없습니다.") conn.close() return supplier_id = supplier_id[0] # 휴먼허브 실제 가격 기반 데이터 (원산지별로 구분) catalog_items = [ ('신흥숙지황(9증)', 20.0, '1kg'), ('휴먼갈근[한국산]', 16.8, '1kg'), ('휴먼감초', 22.1, '1kg'), ('휴먼건강[페루산]', 12.4, '1kg'), ('휴먼건강.土[한국산]', 51.4, '1kg'), ('휴먼계지', 5.8, '1kg'), ('휴먼구기자(영하)', 17.9, '1kg'), ('휴먼길경.片', 10.6, '1kg'), ('휴먼대추(한국산)', 20.0, '1kg'), ('휴먼마황', 9.6, '1kg'), ('휴먼반하(생강백반제)', 33.7, '1kg'), ('휴먼백출', 11.8, '1kg'), ('휴먼복령', 11.5, '1kg'), ('휴먼석고', 4.7, '1kg'), ('휴먼세신.中', 129.0, '1kg'), ('휴먼오미자<토매지>', 17.5, '1kg'), ('휴먼용안육', 20.7, '1kg'), ('휴먼육계', 14.6, '1kg'), ('휴먼일당귀', 12.9, '1kg'), ('휴먼자소엽.土', 13.8, '1kg'), ('휴먼작약', 18.7, '1kg'), ('휴먼작약(주자.酒炙)', 24.6, '1kg'), ('휴먼전호[재배]', 14.0, '1kg'), ('휴먼지각', 10.0, '1kg'), ('휴먼지황.건', 11.5, '1kg'), ('휴먼진피', 13.7, '1kg'), ('휴먼창출[북창출]', 13.5, '1kg'), ('휴먼천궁.일', 11.9, '1kg'), ('휴먼황기(직절.小)', 9.9, '1kg'), ] # 기존 데이터 삭제 cursor.execute("DELETE FROM supplier_product_catalog WHERE supplier_id = ?", (supplier_id,)) # 새 데이터 삽입 for item in catalog_items: raw_name, unit_price, package_unit = item try: cursor.execute(""" INSERT INTO supplier_product_catalog (supplier_id, raw_name, unit_price, package_unit, stock_status, last_updated) VALUES (?, ?, ?, ?, '재고있음', date('now')) """, (supplier_id, raw_name, unit_price, package_unit)) print(f" 추가: {raw_name} - {unit_price:.1f}원/g") except sqlite3.IntegrityError: print(f" 중복: {raw_name} (이미 존재)") conn.commit() # 추가된 항목 수 확인 cursor.execute("SELECT COUNT(*) FROM supplier_product_catalog WHERE supplier_id = ?", (supplier_id,)) count = cursor.fetchone()[0] print(f"\n총 {count}개의 카탈로그 항목이 등록되었습니다.") conn.close() def match_lots_with_catalog(): """가격 기반으로 lot과 카탈로그 매칭""" conn = get_connection() cursor = conn.cursor() print("\n" + "="*80) print("가격 기반 Lot-Catalog 매칭") print("="*80) # 한의사랑에서 입고된 lot들 조회 cursor.execute(""" SELECT DISTINCT l.lot_id, h.herb_name, l.unit_price_per_g, s.supplier_id, s.name as supplier_name FROM inventory_lots l JOIN herb_items h ON l.herb_item_id = h.herb_item_id JOIN purchase_receipt_lines prl ON l.receipt_line_id = prl.line_id JOIN purchase_receipts pr ON prl.receipt_id = pr.receipt_id JOIN suppliers s ON pr.supplier_id = s.supplier_id WHERE s.name = '한의사랑' AND l.display_name IS NULL """) lots = cursor.fetchall() matched_count = 0 for lot in lots: lot_id, herb_name, unit_price, supplier_id, supplier_name = lot # 가격 기반 매칭 (±0.5원 허용) cursor.execute(""" SELECT raw_name, unit_price FROM supplier_product_catalog WHERE supplier_id = ? AND ABS(unit_price - ?) < 0.5 """, (supplier_id, unit_price)) matches = cursor.fetchall() if matches: if len(matches) == 1: # 정확히 1개 매칭 raw_name = matches[0][0] # display_name 업데이트 cursor.execute(""" UPDATE inventory_lots SET display_name = ? WHERE lot_id = ? """, (raw_name, lot_id)) # lot_variants 생성 cursor.execute(""" INSERT OR REPLACE INTO lot_variants (lot_id, raw_name, parsed_at, parsed_method) VALUES (?, ?, datetime('now'), 'price_match') """, (lot_id, raw_name)) print(f" 매칭 성공: Lot #{lot_id} {herb_name} -> {raw_name}") matched_count += 1 else: # 여러 개 매칭 - 약재명으로 추가 필터링 print(f" 다중 매칭: Lot #{lot_id} {herb_name} (단가: {unit_price:.1f}원)") for match in matches: print(f" - {match[0]} ({match[1]:.1f}원/g)") # 약재명이 포함된 것 선택 if herb_name in match[0]: raw_name = match[0] cursor.execute(""" UPDATE inventory_lots SET display_name = ? WHERE lot_id = ? """, (raw_name, lot_id)) cursor.execute(""" INSERT OR REPLACE INTO lot_variants (lot_id, raw_name, parsed_at, parsed_method) VALUES (?, ?, datetime('now'), 'price_herb_match') """, (lot_id, raw_name)) print(f" -> 선택: {raw_name}") matched_count += 1 break else: print(f" 매칭 실패: Lot #{lot_id} {herb_name} (단가: {unit_price:.1f}원)") conn.commit() print(f"\n총 {matched_count}/{len(lots)}개의 로트가 매칭되었습니다.") conn.close() return matched_count def parse_variant_attributes(): """raw_name에서 variant 속성 파싱""" conn = get_connection() cursor = conn.cursor() print("\n" + "="*80) print("Variant 속성 파싱") print("="*80) cursor.execute(""" SELECT variant_id, raw_name FROM lot_variants WHERE form IS NULL AND processing IS NULL """) variants = cursor.fetchall() for variant_id, raw_name in variants: # 기본 파싱 로직 form = None processing = None selection_state = None grade = None # 형태 파싱 (각, 片, 절편, 직절, 土 등) if '.각' in raw_name: form = '각' elif '.片' in raw_name or '[片]' in raw_name: form = '片' elif '절편' in raw_name: form = '절편' elif '직절' in raw_name: form = '직절' elif '.土' in raw_name: form = '土' # 가공 파싱 (9증, 酒炙, 비열, 회 등) if '9증' in raw_name: processing = '9증' elif '酒炙' in raw_name or '주자' in raw_name: processing = '酒炙' elif '비열' in raw_name or '非熱' in raw_name: processing = '비열' elif '생강백반제' in raw_name: processing = '생강백반제' elif '.건[회]' in raw_name or '[회]' in raw_name: processing = '회' # 선별상태 파싱 (야생, 토매지, 재배 등) if '야생' in raw_name: selection_state = '야생' elif '토매지' in raw_name: selection_state = '토매지' elif '재배' in raw_name: selection_state = '재배' elif '영하' in raw_name: selection_state = '영하' # 등급 파싱 (특, 名品, 中, 小, 1호, YB2, 당 등) if '[특]' in raw_name or '.특' in raw_name: grade = '특' elif '名品' in raw_name: grade = '名品' elif '.中' in raw_name: grade = '中' elif '.小' in raw_name: grade = '小' elif '1호' in raw_name: grade = '1호' elif 'YB2' in raw_name: grade = 'YB2' elif '.당' in raw_name: grade = '당' elif '[완]' in raw_name: grade = '완' # 업데이트 cursor.execute(""" UPDATE lot_variants SET form = ?, processing = ?, selection_state = ?, grade = ? WHERE variant_id = ? """, (form, processing, selection_state, grade, variant_id)) attributes = [] if form: attributes.append(f"형태:{form}") if processing: attributes.append(f"가공:{processing}") if selection_state: attributes.append(f"선별:{selection_state}") if grade: attributes.append(f"등급:{grade}") if attributes: print(f" {raw_name}") print(f" -> {', '.join(attributes)}") conn.commit() conn.close() def show_results(): """최종 결과 확인""" conn = get_connection() cursor = conn.cursor() print("\n" + "="*80) print("Variant 적용 결과") print("="*80) cursor.execute(""" SELECT l.lot_id, h.herb_name, l.display_name, v.form, v.processing, v.selection_state, v.grade, l.unit_price_per_g, l.quantity_onhand FROM inventory_lots l JOIN herb_items h ON l.herb_item_id = h.herb_item_id LEFT JOIN lot_variants v ON l.lot_id = v.lot_id WHERE l.display_name IS NOT NULL ORDER BY h.herb_name, l.lot_id """) results = cursor.fetchall() current_herb = None for result in results: lot_id, herb_name, display_name, form, processing, selection_state, grade, price, qty = result if herb_name != current_herb: print(f"\n[{herb_name}]") current_herb = herb_name print(f" Lot #{lot_id}: {display_name or herb_name}") print(f" 재고: {qty:.0f}g, 단가: {price:.1f}원/g") attributes = [] if form: attributes.append(f"형태:{form}") if processing: attributes.append(f"가공:{processing}") if selection_state: attributes.append(f"선별:{selection_state}") if grade: attributes.append(f"등급:{grade}") if attributes: print(f" 속성: {', '.join(attributes)}") # 통계 cursor.execute(""" SELECT COUNT(DISTINCT l.lot_id) as total_lots, COUNT(DISTINCT CASE WHEN l.display_name IS NOT NULL THEN l.lot_id END) as lots_with_display, COUNT(DISTINCT v.lot_id) as lots_with_variants FROM inventory_lots l LEFT JOIN lot_variants v ON l.lot_id = v.lot_id """) stats = cursor.fetchone() print("\n" + "-"*40) print(f"전체 로트: {stats[0]}개") print(f"display_name 설정됨: {stats[1]}개") print(f"variant 정보 있음: {stats[2]}개") conn.close() def main(): """메인 실행 함수""" print("\n" + "="*80) print("Inventory Lots Variant System 적용") print("="*80) # 1. 현재 lot 상태 확인 lots = check_current_lots() if not lots: print("입고된 로트가 없습니다.") return # 2. 공급처 카탈로그 데이터 추가 insert_sample_catalog_data() # 3. 가격 기반 매칭 matched = match_lots_with_catalog() if matched > 0: # 4. variant 속성 파싱 parse_variant_attributes() # 5. 결과 확인 show_results() print("\n완료!") if __name__ == "__main__": main()