diff --git a/app.py b/app.py index cd0134e..4501ebb 100644 --- a/app.py +++ b/app.py @@ -1117,7 +1117,10 @@ def get_patient_compounds(patient_id): c.status, c.notes, c.created_at, - c.created_by + c.created_by, + c.is_custom, + c.custom_summary, + c.custom_type FROM compounds c LEFT JOIN formulas f ON c.formula_id = f.formula_id WHERE c.patient_id = ? @@ -1144,29 +1147,117 @@ def get_patient_compounds(patient_id): @app.route('/api/compounds', methods=['POST']) def create_compound(): - """조제 실행""" + """조제 실행 - 커스텀 처방 감지 포함""" try: data = request.json with get_db() as conn: cursor = conn.cursor() - # 조제 마스터 생성 + formula_id = data.get('formula_id') + + # 커스텀 처방 감지를 위한 준비 + is_custom = False + custom_summary = "" + custom_details = { + 'added': [], + 'removed': [], + 'modified': [] + } + + # formula_id가 있는 경우 원 처방과 비교 + if formula_id: + # 원 처방 구성 조회 (ingredient_code 기반) + cursor.execute(""" + SELECT fi.ingredient_code, hm.herb_name, fi.grams_per_cheop + FROM formula_ingredients fi + JOIN herb_masters hm ON fi.ingredient_code = hm.ingredient_code + WHERE fi.formula_id = ? + """, (formula_id,)) + + # ingredient_code -> herb_item_id 매핑 + original_by_code = {} + for row in cursor.fetchall(): + ingredient_code = row[0] + herb_name = row[1] + grams = row[2] + + # 해당 ingredient_code를 가진 herb_item_id들 조회 + cursor.execute("SELECT herb_item_id FROM herb_items WHERE ingredient_code = ?", + (ingredient_code,)) + herb_ids = [r[0] for r in cursor.fetchall()] + + for herb_id in herb_ids: + original_by_code[herb_id] = { + 'herb_name': herb_name, + 'grams': grams, + 'ingredient_code': ingredient_code + } + + original_ingredients = original_by_code + + # 실제 조제 구성과 비교 + actual_ingredients = {ing['herb_item_id']: ing['grams_per_cheop'] + for ing in data['ingredients']} + + # 추가된 약재 확인 + for ing in data['ingredients']: + herb_id = ing['herb_item_id'] + if herb_id not in original_ingredients: + # 약재명 조회 + cursor.execute("SELECT herb_name FROM herb_items WHERE herb_item_id = ?", (herb_id,)) + herb_name = cursor.fetchone()[0] + custom_details['added'].append(f"{herb_name} {ing['grams_per_cheop']}g") + is_custom = True + + # 제거된 약재 확인 + for herb_id, info in original_ingredients.items(): + if herb_id not in actual_ingredients: + custom_details['removed'].append(info['herb_name']) + is_custom = True + + # 용량 변경된 약재 확인 + for herb_id, original_info in original_ingredients.items(): + if herb_id in actual_ingredients: + original_grams = original_info['grams'] + actual_grams = actual_ingredients[herb_id] + if abs(original_grams - actual_grams) > 0.01: + custom_details['modified'].append( + f"{original_info['herb_name']} {original_grams}g→{actual_grams}g" + ) + is_custom = True + + # 커스텀 요약 생성 + summary_parts = [] + if custom_details['added']: + summary_parts.append(f"추가: {', '.join(custom_details['added'])}") + if custom_details['removed']: + summary_parts.append(f"제거: {', '.join(custom_details['removed'])}") + if custom_details['modified']: + summary_parts.append(f"변경: {', '.join(custom_details['modified'])}") + + custom_summary = " | ".join(summary_parts) if summary_parts else "" + + # 조제 마스터 생성 (커스텀 정보 포함) cursor.execute(""" INSERT INTO compounds (patient_id, formula_id, compound_date, je_count, cheop_total, pouch_total, - prescription_no, notes, created_by) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + prescription_no, notes, created_by, + is_custom, custom_summary, custom_type) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( data.get('patient_id'), - data.get('formula_id'), + formula_id, data.get('compound_date', datetime.now().strftime('%Y-%m-%d')), data['je_count'], data['cheop_total'], data['pouch_total'], data.get('prescription_no'), data.get('notes'), - data.get('created_by', 'system') + data.get('created_by', 'system'), + 1 if is_custom else 0, + custom_summary if is_custom else None, + 'custom' if is_custom else 'standard' )) compound_id = cursor.lastrowid @@ -1178,13 +1269,27 @@ def create_compound(): total_grams = ingredient['total_grams'] origin_country = ingredient.get('origin_country') # 원산지 선택 정보 - # 조제 약재 구성 기록 + # modification_type 결정 + modification_type = 'original' + original_grams = None + + if formula_id and herb_item_id in original_ingredients: + orig_g = original_ingredients[herb_item_id]['grams'] + if abs(orig_g - ingredient['grams_per_cheop']) > 0.01: + modification_type = 'modified' + original_grams = orig_g + elif formula_id and herb_item_id not in original_ingredients: + modification_type = 'added' + + # 조제 약재 구성 기록 (커스텀 정보 포함) cursor.execute(""" INSERT INTO compound_ingredients (compound_id, herb_item_id, - grams_per_cheop, total_grams) - VALUES (?, ?, ?, ?) + grams_per_cheop, total_grams, + modification_type, original_grams) + VALUES (?, ?, ?, ?, ?, ?) """, (compound_id, herb_item_id, - ingredient['grams_per_cheop'], total_grams)) + ingredient['grams_per_cheop'], total_grams, + modification_type, original_grams)) # 재고 차감 (FIFO 방식 - 원산지 지정 시 해당 원산지만) remaining_qty = total_grams diff --git a/check_custom_prescription.py b/check_custom_prescription.py new file mode 100644 index 0000000..29d0efa --- /dev/null +++ b/check_custom_prescription.py @@ -0,0 +1,224 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +커스텀 처방 감지 유틸리티 +조제 시 원 처방과 다른 구성인지 확인 +""" + +import sqlite3 +from typing import Dict, List, Tuple + +def get_connection(): + """데이터베이스 연결""" + return sqlite3.connect('database/kdrug.db') + +def check_custom_prescription(compound_id: int) -> Tuple[bool, Dict]: + """ + 조제가 원 처방과 다른지 확인 + + Returns: + (is_custom, differences_dict) + """ + conn = get_connection() + cursor = conn.cursor() + + # 1. compound의 formula_id 가져오기 + cursor.execute(""" + SELECT c.formula_id, f.formula_name + FROM compounds c + JOIN formulas f ON c.formula_id = f.formula_id + WHERE c.compound_id = ? + """, (compound_id,)) + + result = cursor.fetchone() + if not result: + conn.close() + return False, {"error": "Compound not found"} + + formula_id, formula_name = result + + # 2. 원 처방의 구성 약재 + cursor.execute(""" + SELECT + fi.herb_item_id, + h.herb_name, + fi.grams_per_cheop + FROM formula_ingredients fi + JOIN herb_items h ON fi.herb_item_id = h.herb_item_id + WHERE fi.formula_id = ? + ORDER BY fi.herb_item_id + """, (formula_id,)) + + original_ingredients = {row[0]: { + 'herb_name': row[1], + 'grams_per_cheop': row[2] + } for row in cursor.fetchall()} + + # 3. 실제 조제된 구성 약재 + cursor.execute(""" + SELECT + ci.herb_item_id, + h.herb_name, + ci.grams_per_cheop + FROM compound_ingredients ci + JOIN herb_items h ON ci.herb_item_id = h.herb_item_id + WHERE ci.compound_id = ? + ORDER BY ci.herb_item_id + """, (compound_id,)) + + actual_ingredients = {row[0]: { + 'herb_name': row[1], + 'grams_per_cheop': row[2] + } for row in cursor.fetchall()} + + conn.close() + + # 4. 비교 분석 + differences = { + 'formula_name': formula_name, + 'added': [], + 'removed': [], + 'modified': [], + 'is_custom': False + } + + # 추가된 약재 + for herb_id, info in actual_ingredients.items(): + if herb_id not in original_ingredients: + differences['added'].append({ + 'herb_id': herb_id, + 'herb_name': info['herb_name'], + 'grams_per_cheop': info['grams_per_cheop'] + }) + differences['is_custom'] = True + + # 제거된 약재 + for herb_id, info in original_ingredients.items(): + if herb_id not in actual_ingredients: + differences['removed'].append({ + 'herb_id': herb_id, + 'herb_name': info['herb_name'], + 'grams_per_cheop': info['grams_per_cheop'] + }) + differences['is_custom'] = True + + # 용량 변경된 약재 + for herb_id in set(original_ingredients.keys()) & set(actual_ingredients.keys()): + orig_grams = original_ingredients[herb_id]['grams_per_cheop'] + actual_grams = actual_ingredients[herb_id]['grams_per_cheop'] + + if abs(orig_grams - actual_grams) > 0.01: # 부동소수점 오차 고려 + differences['modified'].append({ + 'herb_id': herb_id, + 'herb_name': original_ingredients[herb_id]['herb_name'], + 'original_grams': orig_grams, + 'actual_grams': actual_grams, + 'difference': actual_grams - orig_grams + }) + differences['is_custom'] = True + + return differences['is_custom'], differences + +def generate_custom_summary(differences: Dict) -> str: + """커스텀 내역을 요약 문자열로 생성""" + summary_parts = [] + + # 추가 + if differences['added']: + added_herbs = [f"{item['herb_name']} {item['grams_per_cheop']}g" + for item in differences['added']] + summary_parts.append(f"추가: {', '.join(added_herbs)}") + + # 제거 + if differences['removed']: + removed_herbs = [item['herb_name'] for item in differences['removed']] + summary_parts.append(f"제거: {', '.join(removed_herbs)}") + + # 수정 + if differences['modified']: + modified_herbs = [f"{item['herb_name']} {item['original_grams']}g→{item['actual_grams']}g" + for item in differences['modified']] + summary_parts.append(f"변경: {', '.join(modified_herbs)}") + + return " | ".join(summary_parts) if summary_parts else "표준 처방" + +def list_all_custom_prescriptions(): + """모든 커스텀 처방 찾기""" + conn = get_connection() + cursor = conn.cursor() + + # 모든 조제 목록 + cursor.execute(""" + SELECT + c.compound_id, + c.compound_date, + p.name as patient_name, + f.formula_name + FROM compounds c + LEFT JOIN patients p ON c.patient_id = p.patient_id + JOIN formulas f ON c.formula_id = f.formula_id + ORDER BY c.compound_date DESC + """) + + compounds = cursor.fetchall() + conn.close() + + custom_compounds = [] + + for compound in compounds: + compound_id = compound[0] + is_custom, differences = check_custom_prescription(compound_id) + + if is_custom: + custom_compounds.append({ + 'compound_id': compound_id, + 'compound_date': compound[1], + 'patient_name': compound[2], + 'formula_name': compound[3], + 'summary': generate_custom_summary(differences), + 'differences': differences + }) + + return custom_compounds + +def demo(): + """데모 실행""" + print("\n" + "="*80) + print("커스텀 처방 감지 시스템") + print("="*80) + + # 전체 커스텀 처방 검색 + custom_prescriptions = list_all_custom_prescriptions() + + if not custom_prescriptions: + print("\n조제 내역이 없거나 모든 조제가 표준 처방입니다.") + + # 테스트용 샘플 데이터 표시 + print("\n[시뮬레이션] 만약 십전대보탕에 구기자를 추가했다면:") + print("-" * 60) + + sample_diff = { + 'formula_name': '십전대보탕', + 'added': [{'herb_name': '구기자', 'grams_per_cheop': 3}], + 'removed': [], + 'modified': [{'herb_name': '인삼', 'original_grams': 5, 'actual_grams': 7}], + 'is_custom': True + } + + summary = generate_custom_summary(sample_diff) + print(f"처방: 십전대보탕 (가감방)") + print(f"변경 내역: {summary}") + print("\n환자 기록 표시:") + print(" 2024-02-17 십전대보탕 가감방 20첩") + print(f" └─ {summary}") + else: + print(f"\n총 {len(custom_prescriptions)}개의 커스텀 처방이 발견되었습니다.\n") + + for cp in custom_prescriptions: + print(f"조제 #{cp['compound_id']} | {cp['compound_date']} | {cp['patient_name']}") + print(f" 처방: {cp['formula_name']} (가감방)") + print(f" 변경: {cp['summary']}") + print() + +if __name__ == "__main__": + demo() \ No newline at end of file diff --git a/docs/조제_프로세스_및_커스텀_처방.md b/docs/조제_프로세스_및_커스텀_처방.md new file mode 100644 index 0000000..07bd639 --- /dev/null +++ b/docs/조제_프로세스_및_커스텀_처방.md @@ -0,0 +1,310 @@ +# 조제 프로세스 및 커스텀 처방 관리 + +## 목차 +1. [조제 프로세스 흐름](#1-조제-프로세스-흐름) +2. [데이터베이스 구조](#2-데이터베이스-구조) +3. [커스텀 처방 처리](#3-커스텀-처방-처리) +4. [구현 제안사항](#4-구현-제안사항) + +--- + +## 1. 조제 프로세스 흐름 + +### 1.1 전체 흐름도 +``` +[처방 선택] → [구성 약재 자동 로드] → [약재 커스터마이징] → [재고 확인] → [조제 실행] → [기록 저장] + ↓ ↓ ↓ ↓ ↓ ↓ +십전대보탕 formula_ingredients 약재 추가/삭제/수정 inventory_lots 재고 차감 compounds + 확인 stock_ledger compound_ingredients +``` + +### 1.2 단계별 상세 프로세스 + +#### Step 1: 처방 선택 +- **테이블**: `formulas` +- **주요 필드**: + - `formula_id`: 처방 ID + - `formula_name`: 처방명 (예: "십전대보탕") + - `base_cheop_per_je`: 1제당 기본 첩수 (보통 20첩) + +#### Step 2: 구성 약재 자동 로드 +- **테이블**: `formula_ingredients` +- **동작**: 선택한 처방의 기본 구성 약재를 자동으로 불러옴 +- **예시**: 십전대보탕 선택 시 인삼, 백출, 복령, 감초 등 10가지 약재 자동 로드 + +#### Step 3: 약재 커스터마이징 +- **가능한 작업**: + - ✅ 약재 추가 (예: 구기자 3g 추가) + - ✅ 약재 삭제 (특정 약재 제외) + - ✅ 용량 수정 (기본 5g → 7g으로 변경) + +#### Step 4: 재고 확인 및 선택 +- **테이블**: `inventory_lots` +- **display_name 표시**: 각 약재의 정확한 variant 확인 + - 예: "건강" → "건강.土[한국산]" vs "건강[페루산]" +- **원산지 선택**: 자동(FIFO) 또는 수동 선택 + +#### Step 5: 조제 실행 및 재고 차감 +- **FIFO 방식**: 오래된 로트부터 우선 소비 +- **재고 부족 체크**: 부족 시 경고 표시 +- **로트별 차감**: `compound_consumptions`에 상세 기록 + +#### Step 6: 조제 기록 저장 +- **compounds 테이블**: 조제 마스터 정보 +- **compound_ingredients 테이블**: 실제 사용된 약재 구성 +- **compound_consumptions 테이블**: 로트별 차감 내역 + +--- + +## 2. 데이터베이스 구조 + +### 2.1 처방 관련 테이블 + +```sql +-- 처방 마스터 (기본 처방) +formulas +├── formula_id (PK) +├── formula_name -- "십전대보탕" +└── base_cheop_per_je -- 20첩 + +-- 처방 기본 구성 +formula_ingredients +├── formula_id (FK) +├── herb_item_id (FK) +└── grams_per_cheop -- 1첩당 용량 + +-- 실제 조제 기록 +compounds +├── compound_id (PK) +├── patient_id (FK) -- 환자 +├── formula_id (FK) -- 원 처방 참조 +├── compound_date -- 조제일 +├── cheop_total -- 총 첩수 +└── notes -- "구기자 3g 추가" 등 커스텀 내역 + +-- 실제 사용 약재 (커스텀 포함) +compound_ingredients +├── compound_id (FK) +├── herb_item_id (FK) +└── grams_per_cheop -- 실제 사용 용량 +``` + +### 2.2 재고 관련 테이블 + +```sql +-- 재고 로트 +inventory_lots +├── lot_id (PK) +├── herb_item_id (FK) +├── display_name -- "갈근.각", "건강.土" 등 +├── quantity_onhand -- 현재 재고량 +└── unit_price_per_g -- g당 단가 + +-- 로트 변형 정보 +lot_variants +├── lot_id (FK) +├── raw_name -- 상세 제품명 +├── form -- 형태 (각, 片, 土) +├── processing -- 가공법 (9증, 酒炙) +└── grade -- 등급 (特, 中, 小) +``` + +--- + +## 3. 커스텀 처방 처리 + +### 3.1 현재 시스템의 처리 방식 + +현재 시스템은 이미 커스텀 처방을 처리할 수 있는 구조를 가지고 있습니다: + +1. **formula_ingredients**: 처방의 기본 구성 (변경되지 않음) +2. **compound_ingredients**: 실제 조제 시 사용된 구성 (커스텀 반영) + +### 3.2 커스텀 처방 식별 방법 + +#### 방법 1: 비교를 통한 자동 감지 +```python +def is_custom_prescription(compound_id): + """조제가 원 처방과 다른지 확인""" + + # 1. compound의 formula_id 확인 + original_formula = get_formula_ingredients(formula_id) + + # 2. 실제 사용된 약재 확인 + actual_ingredients = get_compound_ingredients(compound_id) + + # 3. 비교 + if original_formula != actual_ingredients: + return True, get_differences() + + return False, None +``` + +#### 방법 2: 플래그 추가 (권장) +```sql +-- compounds 테이블에 컬럼 추가 +ALTER TABLE compounds ADD COLUMN is_custom BOOLEAN DEFAULT 0; +ALTER TABLE compounds ADD COLUMN custom_notes TEXT; +``` + +### 3.3 화면 표시 제안 + +#### 조제 내역 표시 예시 + +**원 처방 그대로 조제한 경우:** +``` +조제일: 2024-02-17 +처방: 십전대보탕 +첩수: 20첩 +``` + +**커스텀 조제한 경우:** +``` +조제일: 2024-02-17 +처방: 십전대보탕 (가감방) ⚠️ +첩수: 20첩 +추가: 구기자 3g +제외: 감초 +변경: 인삼 5g → 7g +``` + +--- + +## 4. 구현 제안사항 + +### 4.1 데이터베이스 개선 + +```sql +-- 1. compounds 테이블에 커스텀 플래그 추가 +ALTER TABLE compounds ADD COLUMN is_custom BOOLEAN DEFAULT 0; +ALTER TABLE compounds ADD COLUMN custom_type TEXT; -- 'added', 'removed', 'modified', 'mixed' +ALTER TABLE compounds ADD COLUMN custom_summary TEXT; -- "구기자 3g 추가" + +-- 2. compound_ingredients에 변경 타입 추가 +ALTER TABLE compound_ingredients ADD COLUMN modification_type TEXT; -- 'original', 'added', 'modified' +ALTER TABLE compound_ingredients ADD COLUMN original_grams REAL; -- 원래 용량 (수정된 경우) +``` + +### 4.2 API 개선 제안 + +```python +@app.route('/api/compounds', methods=['POST']) +def create_compound(): + """조제 실행 - 커스텀 처방 감지 포함""" + + data = request.json + formula_id = data.get('formula_id') + ingredients = data.get('ingredients') + + # 원 처방과 비교 + original = get_formula_ingredients(formula_id) + is_custom, differences = compare_ingredients(original, ingredients) + + if is_custom: + # 커스텀 정보 저장 + custom_summary = generate_custom_summary(differences) + # compounds 테이블에 is_custom=1, custom_summary 저장 +``` + +### 4.3 UI 개선 제안 + +#### 조제 화면 +```javascript +// 커스텀 여부 실시간 표시 +function checkCustomization() { + const original = getOriginalFormula(); + const current = getCurrentIngredients(); + + if (hasChanges(original, current)) { + $('#customBadge').show().html('가감방'); + $('#customDetails').html(getChangesSummary()); + } +} +``` + +#### 환자 처방 내역 화면 +```javascript +// 커스텀 처방 구분 표시 +function displayPrescriptionHistory(patient_id) { + // 처방 내역 표시 시 + if (compound.is_custom) { + html += `가감`; + html += `${compound.custom_summary}`; + } +} +``` + +### 4.4 보고서 개선 + +환자 처방 내역서에 커스텀 정보 포함: +``` +=========================================== +환자명: 홍길동 +기간: 2024-01-01 ~ 2024-02-17 +=========================================== + +1. 2024-01-15: 십전대보탕 (20첩) + - 표준 처방 + +2. 2024-02-01: 십전대보탕 가감방 (20첩) + - 추가: 구기자 3g/첩 + - 제외: 감초 + - 용량변경: 인삼 5g → 7g/첩 + +3. 2024-02-17: 쌍화탕 (15첩) + - 표준 처방 +``` + +--- + +## 5. 현재 시스템 활용 방안 + +현재 구조에서도 충분히 커스텀 처방을 관리할 수 있습니다: + +1. **조제 시**: `compound_ingredients`에 실제 사용 약재 저장 +2. **조회 시**: `formula_ingredients`와 비교하여 커스텀 여부 판단 +3. **표시**: 차이점을 계산하여 화면에 표시 + +### 예시 쿼리 + +```sql +-- 커스텀 처방 찾기 +SELECT + c.compound_id, + c.formula_id, + f.formula_name, + CASE + WHEN ci_count != fi_count THEN '가감방' + ELSE '표준방' + END as prescription_type +FROM compounds c +JOIN formulas f ON c.formula_id = f.formula_id +JOIN ( + -- 실제 사용 약재 수 + SELECT compound_id, COUNT(*) as ci_count + FROM compound_ingredients + GROUP BY compound_id +) ci ON c.compound_id = ci.compound_id +LEFT JOIN ( + -- 원 처방 약재 수 + SELECT formula_id, COUNT(*) as fi_count + FROM formula_ingredients + GROUP BY formula_id +) fi ON c.formula_id = fi.formula_id; +``` + +--- + +## 6. 결론 + +현재 시스템은 이미 커스텀 처방을 저장할 수 있는 구조를 갖추고 있습니다: +- `formula_ingredients`: 원 처방 (불변) +- `compound_ingredients`: 실제 조제 (커스텀 가능) + +추가 개선사항: +1. `compounds` 테이블에 `is_custom` 플래그 추가 +2. 커스텀 내역을 요약하여 `custom_summary`에 저장 +3. UI에서 가감방 표시 및 상세 내역 표시 +4. 환자 처방 내역에 커스텀 정보 포함 + +이렇게 하면 "십전대보탕 + 구기자 3g"을 정확히 기록하고 추적할 수 있습니다. \ No newline at end of file diff --git a/migrations/add_custom_prescription_fields.py b/migrations/add_custom_prescription_fields.py new file mode 100644 index 0000000..e01a139 --- /dev/null +++ b/migrations/add_custom_prescription_fields.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +커스텀 처방 관리를 위한 데이터베이스 스키마 업데이트 +""" + +import sqlite3 +from datetime import datetime + +def get_connection(): + """데이터베이스 연결""" + return sqlite3.connect('database/kdrug.db') + +def add_custom_fields(): + """커스텀 처방 관련 필드 추가""" + conn = get_connection() + cursor = conn.cursor() + + print("\n" + "="*60) + print("커스텀 처방 관리를 위한 DB 스키마 업데이트") + print("="*60) + + try: + # 1. compounds 테이블에 커스텀 관련 필드 추가 + print("\n1. compounds 테이블 업데이트...") + + # is_custom 컬럼 추가 + cursor.execute(""" + ALTER TABLE compounds + ADD COLUMN is_custom BOOLEAN DEFAULT 0 + """) + print(" ✓ is_custom 컬럼 추가") + + # custom_summary 컬럼 추가 + cursor.execute(""" + ALTER TABLE compounds + ADD COLUMN custom_summary TEXT + """) + print(" ✓ custom_summary 컬럼 추가") + + # custom_type 컬럼 추가 + cursor.execute(""" + ALTER TABLE compounds + ADD COLUMN custom_type TEXT + """) + print(" ✓ custom_type 컬럼 추가") + + except sqlite3.OperationalError as e: + if "duplicate column" in str(e): + print(" ⚠ 이미 컬럼이 존재합니다.") + else: + raise e + + try: + # 2. compound_ingredients 테이블에 modification 관련 필드 추가 + print("\n2. compound_ingredients 테이블 업데이트...") + + # modification_type 컬럼 추가 + cursor.execute(""" + ALTER TABLE compound_ingredients + ADD COLUMN modification_type TEXT DEFAULT 'original' + """) + print(" ✓ modification_type 컬럼 추가") + + # original_grams 컬럼 추가 (원래 용량 저장) + cursor.execute(""" + ALTER TABLE compound_ingredients + ADD COLUMN original_grams REAL + """) + print(" ✓ original_grams 컬럼 추가") + + except sqlite3.OperationalError as e: + if "duplicate column" in str(e): + print(" ⚠ 이미 컬럼이 존재합니다.") + else: + raise e + + # 3. 인덱스 추가 + try: + print("\n3. 인덱스 생성...") + cursor.execute(""" + CREATE INDEX IF NOT EXISTS idx_compounds_is_custom + ON compounds(is_custom) + """) + print(" ✓ is_custom 인덱스 생성") + + cursor.execute(""" + CREATE INDEX IF NOT EXISTS idx_compounds_patient_custom + ON compounds(patient_id, is_custom) + """) + print(" ✓ patient_id + is_custom 복합 인덱스 생성") + + except sqlite3.OperationalError as e: + print(f" ⚠ 인덱스 생성 중 오류: {e}") + + conn.commit() + + # 4. 스키마 확인 + print("\n4. 업데이트된 스키마 확인...") + + cursor.execute("PRAGMA table_info(compounds)") + columns = cursor.fetchall() + print("\n compounds 테이블 컬럼:") + for col in columns: + if col[1] in ['is_custom', 'custom_summary', 'custom_type']: + print(f" ✓ {col[1]:20s} {col[2]}") + + cursor.execute("PRAGMA table_info(compound_ingredients)") + columns = cursor.fetchall() + print("\n compound_ingredients 테이블 컬럼:") + for col in columns: + if col[1] in ['modification_type', 'original_grams']: + print(f" ✓ {col[1]:20s} {col[2]}") + + conn.close() + + print("\n" + "="*60) + print("✅ DB 스키마 업데이트 완료!") + print("="*60) + +def test_custom_fields(): + """업데이트된 필드 테스트""" + conn = get_connection() + cursor = conn.cursor() + + print("\n테스트: 커스텀 필드 동작 확인...") + + try: + # 테스트 쿼리 + cursor.execute(""" + SELECT + compound_id, + is_custom, + custom_summary, + custom_type + FROM compounds + LIMIT 1 + """) + + result = cursor.fetchone() + if result: + print(" ✓ compounds 테이블 커스텀 필드 정상") + else: + print(" ℹ compounds 테이블이 비어있습니다.") + + cursor.execute(""" + SELECT + compound_ingredient_id, + modification_type, + original_grams + FROM compound_ingredients + LIMIT 1 + """) + + result = cursor.fetchone() + if result: + print(" ✓ compound_ingredients 테이블 커스텀 필드 정상") + else: + print(" ℹ compound_ingredients 테이블이 비어있습니다.") + + except Exception as e: + print(f" ✗ 테스트 실패: {e}") + + conn.close() + +def main(): + """메인 실행""" + add_custom_fields() + test_custom_fields() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/static/app.js b/static/app.js index f91d267..4536e75 100644 --- a/static/app.js +++ b/static/app.js @@ -1,5 +1,8 @@ // 한약 재고관리 시스템 - Frontend JavaScript +// 원래 처방 구성 저장용 전역 변수 +let originalFormulaIngredients = {}; + $(document).ready(function() { // 페이지 네비게이션 $('.sidebar .nav-link').on('click', function(e) { @@ -273,13 +276,22 @@ $(document).ready(function() { const detailRowId = `compound-detail-${compound.compound_id}`; + // 처방명 표시 (가감방 여부 포함) + let formulaDisplay = compound.formula_name || '직접조제'; + if (compound.is_custom && compound.formula_name) { + formulaDisplay = `${compound.formula_name} 가감`; + } + tbody.append(`
십전대보탕을 선택한 후, 약재를 추가/삭제/변경하면 "가감방" 표시가 나타납니다.
+