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