kdrug-inventory-system/dev_scripts/final_inventory_analysis.py
시골약사 ad9ac396e2 chore: 개발 파일 정리 및 구조화
- 개발/테스트 스크립트를 dev_scripts/ 폴더로 이동
- 스크린샷을 screenshots/ 폴더로 이동
- 백업 파일 보존 (.backup)
- 처방 관련 추가 스크립트 포함

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-18 04:44:48 +00:00

193 lines
6.6 KiB
Python

#!/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()