#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 재고 자산 금액 불일치 최종 분석 """ import sqlite3 from datetime import datetime def final_analysis(): conn = sqlite3.connect('database/kdrug.db') conn.row_factory = sqlite3.Row cursor = conn.cursor() print("=" * 80) print("재고 자산 금액 불일치 최종 분석") print("분석 시간:", datetime.now().strftime("%Y-%m-%d %H:%M:%S")) print("=" * 80) print() # 1. 현재 DB의 실제 재고 자산 print("📊 현재 데이터베이스 상태") print("-" * 60) cursor.execute(""" SELECT SUM(quantity_onhand * unit_price_per_g) as total_value, COUNT(*) as lot_count, SUM(quantity_onhand) as total_quantity FROM inventory_lots WHERE is_depleted = 0 AND quantity_onhand > 0 """) current = cursor.fetchone() db_total = current['total_value'] or 0 print(f" DB 재고 자산: ₩{db_total:,.0f}") print(f" 활성 LOT: {current['lot_count']}개") print(f" 총 재고량: {current['total_quantity']:,.1f}g") print() # 2. 입고와 출고 분석 print("💼 입고/출고 분석") print("-" * 60) # 입고 총액 cursor.execute(""" SELECT SUM(line_total) as total_in FROM purchase_receipt_lines """) total_in = cursor.fetchone()['total_in'] or 0 # 복합제 소비 금액 cursor.execute(""" SELECT SUM(cc.quantity_used * il.unit_price_per_g) as total_out FROM compound_consumptions cc JOIN inventory_lots il ON cc.lot_id = il.lot_id """) total_out = cursor.fetchone()['total_out'] or 0 print(f" 총 입고 금액: ₩{total_in:,.0f}") print(f" 총 소비 금액: ₩{total_out:,.0f}") print(f" 예상 잔액: ₩{total_in - total_out:,.0f}") print() # 3. 차이 분석 print("🔍 차이 분석 결과") print("=" * 60) print() ui_value = 5875708 # 화면에 표시되는 금액 expected_value = total_in - total_out print(f" 화면 표시 금액: ₩{ui_value:,.0f}") print(f" DB 계산 금액: ₩{db_total:,.0f}") print(f" 예상 금액 (입고-소비): ₩{expected_value:,.0f}") print() print(" 차이:") print(f" 화면 vs DB: ₩{ui_value - db_total:,.0f}") print(f" 화면 vs 예상: ₩{ui_value - expected_value:,.0f}") print(f" DB vs 예상: ₩{db_total - expected_value:,.0f}") print() # 4. 가능한 원인 분석 print("❗ 불일치 원인 분석") print("-" * 60) # 4-1. 단가 차이 확인 cursor.execute(""" SELECT prl.line_id, h.herb_name, prl.quantity_g as purchase_qty, prl.unit_price_per_g as purchase_price, prl.line_total as purchase_total, il.quantity_onhand as current_qty, il.unit_price_per_g as lot_price, il.quantity_onhand * il.unit_price_per_g as current_value, ABS(prl.unit_price_per_g - il.unit_price_per_g) as price_diff FROM purchase_receipt_lines prl JOIN inventory_lots il ON prl.line_id = il.receipt_line_id JOIN herb_items h ON prl.herb_item_id = h.herb_item_id WHERE il.is_depleted = 0 AND il.quantity_onhand > 0 AND ABS(prl.unit_price_per_g - il.unit_price_per_g) > 0.01 ORDER BY price_diff DESC LIMIT 5 """) price_diffs = cursor.fetchall() if price_diffs: print("\n ⚠️ 입고 단가와 LOT 단가가 다른 항목:") for pd in price_diffs: print(f" {pd['herb_name']}:") print(f" 입고 단가: ₩{pd['purchase_price']:.2f}/g") print(f" LOT 단가: ₩{pd['lot_price']:.2f}/g") print(f" 차이: ₩{pd['price_diff']:.2f}/g") # 4-2. 소비 후 남은 재고 확인 cursor.execute(""" SELECT h.herb_name, il.lot_number, il.quantity_received as original_qty, il.quantity_onhand as current_qty, il.quantity_received - il.quantity_onhand as consumed_qty, il.unit_price_per_g FROM inventory_lots il JOIN herb_items h ON il.herb_item_id = h.herb_item_id WHERE il.is_depleted = 0 AND il.quantity_received > il.quantity_onhand ORDER BY (il.quantity_received - il.quantity_onhand) DESC LIMIT 5 """) consumed_lots = cursor.fetchall() if consumed_lots: print("\n 📉 소비된 재고가 있는 LOT (상위 5개):") for cl in consumed_lots: print(f" {cl['herb_name']} (LOT: {cl['lot_number']})") print(f" 원래: {cl['original_qty']:,.0f}g → 현재: {cl['current_qty']:,.0f}g") print(f" 소비: {cl['consumed_qty']:,.0f}g (₩{cl['consumed_qty'] * cl['unit_price_per_g']:,.0f})") # 4-3. JavaScript 계산 로직 확인 필요 print("\n 💡 추가 확인 필요사항:") print(" 1) 프론트엔드 JavaScript에서 재고 자산을 계산하는 로직") print(" 2) 캐시 또는 세션 스토리지에 저장된 이전 값") print(" 3) inventory_lots_v2 테이블 사용 여부") # inventory_lots_v2 확인 cursor.execute(""" SELECT SUM(quantity_onhand * unit_price_per_g) as v2_total, COUNT(*) as v2_count FROM inventory_lots_v2 WHERE is_depleted = 0 AND quantity_onhand > 0 """) v2_result = cursor.fetchone() if v2_result and v2_result['v2_count'] > 0: v2_total = v2_result['v2_total'] or 0 print(f"\n ⚠️ inventory_lots_v2 테이블 데이터:") print(f" 재고 자산: ₩{v2_total:,.0f}") print(f" LOT 수: {v2_result['v2_count']}개") if abs(v2_total - ui_value) < 100: print(f" → 화면 금액과 일치할 가능성 높음!") print() # 5. 결론 print("📝 결론") print("=" * 60) diff = ui_value - db_total if diff > 0: print(f" 화면에 표시되는 금액(₩{ui_value:,.0f})이") print(f" 실제 DB 금액(₩{db_total:,.0f})보다") print(f" ₩{diff:,.0f} 더 많습니다.") print() print(" 가능한 원인:") print(" 1) 프론트엔드에서 별도의 계산 로직 사용") print(" 2) 캐시된 이전 데이터 표시") print(" 3) inventory_lots_v2 테이블 참조") print(" 4) 재고 보정 내역이 즉시 반영되지 않음") else: print(f" 실제 DB 금액이 화면 표시 금액보다 적습니다.") conn.close() if __name__ == "__main__": final_analysis()