-- 한약 재고관리 시스템 데이터베이스 스키마 -- SQLite 기준 -- 1) 도매상/공급업체 CREATE TABLE IF NOT EXISTS suppliers ( supplier_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE, business_no TEXT, contact_person TEXT, phone TEXT, address TEXT, is_active INTEGER DEFAULT 1, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 2) 약재 마스터 (보험코드 9자리 기준) CREATE TABLE IF NOT EXISTS herb_items ( herb_item_id INTEGER PRIMARY KEY AUTOINCREMENT, insurance_code TEXT UNIQUE, -- 보험코드 (9자리) herb_name TEXT NOT NULL, -- 약재명 specification TEXT, -- 규격/품질 default_unit TEXT DEFAULT 'g', -- 기본 단위 is_active INTEGER DEFAULT 1, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 3) 환자 정보 CREATE TABLE IF NOT EXISTS patients ( patient_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, phone TEXT NOT NULL, jumin_no TEXT, -- 주민번호 (암호화 필요) gender TEXT CHECK(gender IN ('M', 'F')), birth_date DATE, address TEXT, notes TEXT, is_active INTEGER DEFAULT 1, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE(phone, name) ); -- 4) 입고장 헤더 CREATE TABLE IF NOT EXISTS purchase_receipts ( receipt_id INTEGER PRIMARY KEY AUTOINCREMENT, supplier_id INTEGER NOT NULL, receipt_date DATE NOT NULL, receipt_no TEXT, -- 입고 번호/전표번호 vat_included INTEGER DEFAULT 1, -- 부가세 포함 여부 vat_rate REAL DEFAULT 0.10, -- 부가세율 total_amount REAL, -- 총 입고액 source_file TEXT, -- Excel 파일명 notes TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (supplier_id) REFERENCES suppliers(supplier_id) ); -- 5) 입고장 상세 라인 CREATE TABLE IF NOT EXISTS purchase_receipt_lines ( line_id INTEGER PRIMARY KEY AUTOINCREMENT, receipt_id INTEGER NOT NULL, herb_item_id INTEGER NOT NULL, origin_country TEXT, -- 원산지 quantity_g REAL NOT NULL, -- 구입량(g) unit_price_per_g REAL NOT NULL, -- g당 단가 (VAT 포함) line_total REAL, -- 라인 총액 expiry_date DATE, -- 유효기간 lot_number TEXT, -- 로트번호 created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (receipt_id) REFERENCES purchase_receipts(receipt_id), FOREIGN KEY (herb_item_id) REFERENCES herb_items(herb_item_id) ); -- 6) 재고 로트 (입고 라인별 재고 관리) CREATE TABLE IF NOT EXISTS inventory_lots ( lot_id INTEGER PRIMARY KEY AUTOINCREMENT, herb_item_id INTEGER NOT NULL, supplier_id INTEGER NOT NULL, receipt_line_id INTEGER NOT NULL, received_date DATE NOT NULL, origin_country TEXT, unit_price_per_g REAL NOT NULL, quantity_received REAL NOT NULL, -- 입고 수량 quantity_onhand REAL NOT NULL, -- 현재 재고 expiry_date DATE, lot_number TEXT, is_depleted INTEGER DEFAULT 0, -- 소진 여부 created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (herb_item_id) REFERENCES herb_items(herb_item_id), FOREIGN KEY (supplier_id) REFERENCES suppliers(supplier_id), FOREIGN KEY (receipt_line_id) REFERENCES purchase_receipt_lines(line_id) ); -- 7) 재고 원장 (모든 재고 변동 기록) CREATE TABLE IF NOT EXISTS stock_ledger ( ledger_id INTEGER PRIMARY KEY AUTOINCREMENT, event_time DATETIME DEFAULT CURRENT_TIMESTAMP, event_type TEXT NOT NULL CHECK(event_type IN ('RECEIPT', 'CONSUME', 'ADJUST', 'DISCARD', 'RETURN')), herb_item_id INTEGER NOT NULL, lot_id INTEGER, quantity_delta REAL NOT NULL, -- 증감량 (+입고, -사용) unit_cost_per_g REAL, reference_table TEXT, -- 참조 테이블 (compounds, adjustments 등) reference_id INTEGER, -- 참조 ID notes TEXT, created_by TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (herb_item_id) REFERENCES herb_items(herb_item_id), FOREIGN KEY (lot_id) REFERENCES inventory_lots(lot_id) ); -- 8) 처방 마스터 (약속 처방) CREATE TABLE IF NOT EXISTS formulas ( formula_id INTEGER PRIMARY KEY AUTOINCREMENT, formula_code TEXT UNIQUE, -- 처방 코드 formula_name TEXT NOT NULL, -- 처방명 (예: 쌍화탕) formula_type TEXT DEFAULT 'CUSTOM', -- INSURANCE(보험), CUSTOM(약속처방) base_cheop INTEGER DEFAULT 20, -- 기본 첩수 (1제 기준) base_pouches INTEGER DEFAULT 30, -- 기본 파우치수 (1제 기준) description TEXT, is_active INTEGER DEFAULT 1, created_by TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 9) 처방 구성 약재 CREATE TABLE IF NOT EXISTS formula_ingredients ( ingredient_id INTEGER PRIMARY KEY AUTOINCREMENT, formula_id INTEGER NOT NULL, herb_item_id INTEGER NOT NULL, grams_per_cheop REAL NOT NULL, -- 1첩당 그램수 notes TEXT, sort_order INTEGER DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (formula_id) REFERENCES formulas(formula_id), FOREIGN KEY (herb_item_id) REFERENCES herb_items(herb_item_id), UNIQUE (formula_id, herb_item_id) ); -- 10) 조제 작업 (처방 실행) CREATE TABLE IF NOT EXISTS compounds ( compound_id INTEGER PRIMARY KEY AUTOINCREMENT, patient_id INTEGER, formula_id INTEGER, compound_date DATE NOT NULL, je_count REAL NOT NULL, -- 제수 (1제, 0.5제 등) cheop_total REAL NOT NULL, -- 총 첩수 pouch_total REAL NOT NULL, -- 총 파우치수 cost_total REAL, -- 원가 총액 sell_price_total REAL, -- 판매 총액 prescription_no TEXT, -- 처방전 번호 status TEXT DEFAULT 'PREPARED', -- PREPARED, DISPENSED, CANCELLED notes TEXT, created_by TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (patient_id) REFERENCES patients(patient_id), FOREIGN KEY (formula_id) REFERENCES formulas(formula_id) ); -- 11) 조제 약재 구성 (실제 조제시 사용된 약재 - 가감 포함) CREATE TABLE IF NOT EXISTS compound_ingredients ( compound_ingredient_id INTEGER PRIMARY KEY AUTOINCREMENT, compound_id INTEGER NOT NULL, herb_item_id INTEGER NOT NULL, grams_per_cheop REAL NOT NULL, -- 1첩당 그램수 (가감 반영) total_grams REAL NOT NULL, -- 총 사용량 notes TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (compound_id) REFERENCES compounds(compound_id), FOREIGN KEY (herb_item_id) REFERENCES herb_items(herb_item_id) ); -- 12) 조제 소비 내역 (로트별 차감) CREATE TABLE IF NOT EXISTS compound_consumptions ( consumption_id INTEGER PRIMARY KEY AUTOINCREMENT, compound_id INTEGER NOT NULL, herb_item_id INTEGER NOT NULL, lot_id INTEGER NOT NULL, quantity_used REAL NOT NULL, -- 사용량(g) unit_cost_per_g REAL NOT NULL, -- 단가 cost_amount REAL, -- 원가액 created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (compound_id) REFERENCES compounds(compound_id), FOREIGN KEY (herb_item_id) REFERENCES herb_items(herb_item_id), FOREIGN KEY (lot_id) REFERENCES inventory_lots(lot_id) ); -- 인덱스 생성 CREATE INDEX IF NOT EXISTS idx_herb_items_name ON herb_items(herb_name); CREATE INDEX IF NOT EXISTS idx_herb_items_code ON herb_items(insurance_code); CREATE INDEX IF NOT EXISTS idx_inventory_lots_herb ON inventory_lots(herb_item_id, is_depleted); CREATE INDEX IF NOT EXISTS idx_stock_ledger_herb ON stock_ledger(herb_item_id, event_time); CREATE INDEX IF NOT EXISTS idx_compounds_patient ON compounds(patient_id); CREATE INDEX IF NOT EXISTS idx_compounds_date ON compounds(compound_date); CREATE INDEX IF NOT EXISTS idx_patients_phone ON patients(phone); -- 뷰 생성 (자주 사용되는 조회) -- 현재 재고 현황 CREATE VIEW IF NOT EXISTS v_current_stock AS SELECT h.herb_item_id, h.insurance_code, h.herb_name, SUM(il.quantity_onhand) as total_quantity, COUNT(DISTINCT il.lot_id) as lot_count, AVG(il.unit_price_per_g) as avg_unit_price FROM herb_items h LEFT JOIN inventory_lots il ON h.herb_item_id = il.herb_item_id AND il.is_depleted = 0 GROUP BY h.herb_item_id, h.insurance_code, h.herb_name; -- 처방별 구성 약재 뷰 CREATE VIEW IF NOT EXISTS v_formula_details AS SELECT f.formula_id, f.formula_name, f.formula_code, h.herb_name, fi.grams_per_cheop, h.insurance_code FROM formulas f JOIN formula_ingredients fi ON f.formula_id = fi.formula_id JOIN herb_items h ON fi.herb_item_id = h.herb_item_id WHERE f.is_active = 1 ORDER BY f.formula_id, fi.sort_order;