kdrug-inventory-system/dev_scripts/check_missing_lots.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

179 lines
6.7 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
LOT이 생성되지 않은 입고 라인 확인
"""
import sqlite3
def check_missing_lots():
conn = sqlite3.connect('database/kdrug.db')
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
print("=" * 80)
print("LOT이 생성되지 않은 입고 라인 분석")
print("=" * 80)
print()
# 1. 전체 입고 라인과 LOT 매칭 상태
print("1. 입고 라인 - LOT 매칭 현황")
print("-" * 60)
cursor.execute("""
SELECT
COUNT(*) as total_lines,
SUM(CASE WHEN il.lot_id IS NOT NULL THEN 1 ELSE 0 END) as lines_with_lot,
SUM(CASE WHEN il.lot_id IS NULL THEN 1 ELSE 0 END) as lines_without_lot,
SUM(prl.line_total) as total_purchase_amount,
SUM(CASE WHEN il.lot_id IS NOT NULL THEN prl.line_total ELSE 0 END) as amount_with_lot,
SUM(CASE WHEN il.lot_id IS NULL THEN prl.line_total ELSE 0 END) as amount_without_lot
FROM purchase_receipt_lines prl
LEFT JOIN inventory_lots il ON prl.line_id = il.receipt_line_id
""")
result = cursor.fetchone()
print(f" 총 입고 라인: {result['total_lines']}")
print(f" ✅ LOT 생성됨: {result['lines_with_lot']}개 (₩{result['amount_with_lot']:,.0f})")
print(f" ❌ LOT 없음: {result['lines_without_lot']}개 (₩{result['amount_without_lot']:,.0f})")
print()
print(f" 총 입고 금액: ₩{result['total_purchase_amount']:,.0f}")
print(f" LOT 없는 금액: ₩{result['amount_without_lot']:,.0f}")
if result['amount_without_lot'] > 0:
print(f"\n ⚠️ LOT이 생성되지 않은 입고 금액이 ₩{result['amount_without_lot']:,.0f} 있습니다!")
print(" 이것이 DB 재고와 예상 재고 차이(₩55,500)의 원인일 가능성이 높습니다.")
# 2. LOT이 없는 입고 라인 상세
if result['lines_without_lot'] > 0:
print("\n2. LOT이 생성되지 않은 입고 라인 상세")
print("-" * 60)
cursor.execute("""
SELECT
pr.receipt_no,
pr.receipt_date,
h.herb_name,
prl.quantity_g,
prl.unit_price_per_g,
prl.line_total,
prl.lot_number,
prl.line_id
FROM purchase_receipt_lines prl
JOIN purchase_receipts pr ON prl.receipt_id = pr.receipt_id
JOIN herb_items h ON prl.herb_item_id = h.herb_item_id
LEFT JOIN inventory_lots il ON prl.line_id = il.receipt_line_id
WHERE il.lot_id IS NULL
ORDER BY prl.line_total DESC
""")
missing_lots = cursor.fetchall()
total_missing_amount = 0
print("\n LOT이 생성되지 않은 입고 라인:")
for i, line in enumerate(missing_lots, 1):
print(f"\n {i}. {line['herb_name']}")
print(f" 입고장: {line['receipt_no']} ({line['receipt_date']})")
print(f" 수량: {line['quantity_g']:,.0f}g")
print(f" 단가: ₩{line['unit_price_per_g']:.2f}/g")
print(f" 금액: ₩{line['line_total']:,.0f}")
print(f" LOT번호: {line['lot_number'] or 'None'}")
print(f" Line ID: {line['line_id']}")
total_missing_amount += line['line_total']
print(f"\n 총 누락 금액: ₩{total_missing_amount:,.0f}")
# 3. 반대로 입고 라인 없는 LOT 확인
print("\n3. 입고 라인과 연결되지 않은 LOT")
print("-" * 60)
cursor.execute("""
SELECT
COUNT(*) as orphan_lots,
SUM(quantity_onhand * unit_price_per_g) as orphan_value,
SUM(quantity_onhand) as orphan_quantity
FROM inventory_lots
WHERE receipt_line_id IS NULL
AND is_depleted = 0
AND quantity_onhand > 0
""")
orphans = cursor.fetchone()
if orphans['orphan_lots'] > 0:
print(f" 입고 라인 없는 LOT: {orphans['orphan_lots']}")
print(f" 해당 재고 가치: ₩{orphans['orphan_value']:,.0f}")
print(f" 해당 재고량: {orphans['orphan_quantity']:,.0f}g")
cursor.execute("""
SELECT
h.herb_name,
il.lot_number,
il.quantity_onhand,
il.unit_price_per_g,
il.quantity_onhand * il.unit_price_per_g as value,
il.received_date
FROM inventory_lots il
JOIN herb_items h ON il.herb_item_id = h.herb_item_id
WHERE il.receipt_line_id IS NULL
AND il.is_depleted = 0
AND il.quantity_onhand > 0
ORDER BY value DESC
LIMIT 5
""")
orphan_lots = cursor.fetchall()
if orphan_lots:
print("\n 상위 5개 입고 라인 없는 LOT:")
for lot in orphan_lots:
print(f" - {lot['herb_name']} (LOT: {lot['lot_number']})")
print(f" 재고: {lot['quantity_onhand']:,.0f}g, 금액: ₩{lot['value']:,.0f}")
else:
print(" ✅ 모든 LOT이 입고 라인과 연결되어 있습니다.")
# 4. 금액 차이 분석
print("\n4. 금액 차이 최종 분석")
print("=" * 60)
# 현재 DB 재고
cursor.execute("""
SELECT SUM(quantity_onhand * unit_price_per_g) as total
FROM inventory_lots
WHERE is_depleted = 0 AND quantity_onhand > 0
""")
db_total = cursor.fetchone()['total'] or 0
# 총 입고 - 소비
cursor.execute("SELECT SUM(line_total) as total FROM purchase_receipt_lines")
total_in = cursor.fetchone()['total'] or 0
cursor.execute("""
SELECT SUM(cc.quantity_used * il.unit_price_per_g) as total
FROM compound_consumptions cc
JOIN inventory_lots il ON cc.lot_id = il.lot_id
""")
total_out = cursor.fetchone()['total'] or 0
expected = total_in - total_out
print(f" DB 재고 자산: ₩{db_total:,.0f}")
print(f" 예상 재고 (입고-소비): ₩{expected:,.0f}")
print(f" 차이: ₩{expected - db_total:,.0f}")
print()
if result['amount_without_lot'] > 0:
print(f" 💡 LOT 없는 입고 금액: ₩{result['amount_without_lot']:,.0f}")
adjusted_expected = (total_in - result['amount_without_lot']) - total_out
print(f" 📊 조정된 예상 재고: ₩{adjusted_expected:,.0f}")
print(f" 조정 후 차이: ₩{adjusted_expected - db_total:,.0f}")
if abs(adjusted_expected - db_total) < 1000:
print("\n ✅ LOT이 생성되지 않은 입고 라인을 제외하면 차이가 거의 없습니다!")
print(" 이것이 차이의 주요 원인입니다.")
conn.close()
if __name__ == "__main__":
check_missing_lots()