From ea94532cd7f947ed8749afed0b1bc9a8dd867d8d Mon Sep 17 00:00:00 2001 From: KWON Date: Wed, 9 Jul 2025 14:42:09 +0900 Subject: [PATCH] =?UTF-8?q?POS=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=EB=90=9C=20db=5Fschema=20=EB=B0=98=EC=98=81=20?= =?UTF-8?q?=EB=B0=8F=20=EC=BD=94=EB=93=9C=20=EC=9D=BC=EB=B6=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pos_update_gui.py | 159 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 lib/pos_update_gui.py diff --git a/lib/pos_update_gui.py b/lib/pos_update_gui.py new file mode 100644 index 0000000..d02403d --- /dev/null +++ b/lib/pos_update_gui.py @@ -0,0 +1,159 @@ +import sys, os +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) + +import tkinter as tk +import pandas as pd +from tkinter import filedialog, messagebox +from sqlalchemy.dialects.mysql import insert as mysql_insert +from sqlalchemy.exc import IntegrityError + +from conf import db, db_schema +from lib.common import load_config + +CONFIG = load_config() +DATA_DIR = os.path.join(os.path.dirname(__file__), '../data') + + +def update_pos_table(engine, table, df): + with engine.begin() as conn: + for idx, row in df.iterrows(): + data = row.to_dict() + stmt = mysql_insert(table).values(**data) + + # insert ... on duplicate key update (복합 unique key 기준) + update_data = { + 'qty': data['qty'], + 'tot_amount': data['tot_amount'], + 'tot_discount': data['tot_discount'], + 'actual_amount': data['actual_amount'] + } + + stmt = stmt.on_duplicate_key_update(**update_data) + + try: + conn.execute(stmt) + except IntegrityError as e: + print(f"[ERROR] {idx + 1}행 삽입 실패: {e}") + + print("[DONE] 모든 데이터 삽입 완료") + + +def process_file(filepath, table, engine): + print(f"[INFO] 처리 시작: {filepath}") + try: + df = pd.read_excel(filepath, header=5) + df = df[df.iloc[:, 0] != '합계'] + + df.rename(columns={ + '일자': 'date', + '대분류': 'ca01', + '중분류': 'ca02', + '소분류': 'ca03', + '상품코드': 'barcode', + '상품명': 'name', + '수량': 'qty', + '총매출액': 'tot_amount', + '총할인액': 'tot_discount', + '실매출액': 'actual_amount' + }, inplace=True) + + if 'idx' in df.columns: + df = df.drop(columns=['idx']) + + df['date'] = pd.to_datetime(df['date']).dt.date + df['barcode'] = df['barcode'].astype(int) + df['qty'] = df['qty'].astype(int) + df['tot_amount'] = df['tot_amount'].astype(int) + df['tot_discount'] = df['tot_discount'].astype(int) + df['actual_amount'] = df['actual_amount'].astype(int) + + if df.empty: + print("[WARN] 데이터가 없습니다.") + return False, 0 + + except Exception as e: + print(f"[ERROR] 로드 실패: {e}") + return False, 0 + + print(f"[INFO] 데이터 건수: {len(df)}") + update_pos_table(engine, table, df) + print(f"[INFO] 처리 완료: {filepath}") + return True, len(df) + + +def batch_process_files(table, engine): + files = [f for f in os.listdir(DATA_DIR) if f.startswith("일자별 (상품별)") and f.endswith(('.xlsx', '.xls'))] + + if not files: + print("[INFO] 처리할 파일이 없습니다.") + return False + + print(f"[INFO] {len(files)}개의 파일을 찾았습니다.") + total_rows = 0 + deleted_files = 0 + + for fname in files: + full_path = os.path.join(DATA_DIR, fname) + success, count = process_file(full_path, table, engine) + if success: + total_rows += count + try: + os.remove(full_path) + print(f"[INFO] 처리 완료 후 파일 삭제: {fname}") + deleted_files += 1 + except Exception as e: + print(f"[WARN] 파일 삭제 실패: {fname}, {e}") + + print(f"[INFO] 처리된 전체 데이터 건수: {total_rows}") + print(f"[INFO] 삭제된 파일 수: {deleted_files}") + return True + + +def run_pos_update(): + filepath = filedialog.askopenfilename( + filetypes=[("Excel Files", "*.xlsx *.xls")], + title="파일을 선택하세요" + ) + if not filepath: + return + + engine = db.engine + try: + table = db_schema.pos + except AttributeError: + messagebox.showerror("DB 오류", "'pos' 테이블이 db_schema에 정의되어 있지 않습니다.") + return + + if messagebox.askyesno("확인", f"'{os.path.basename(filepath)}' 파일을 'pos' 테이블에 업로드 하시겠습니까?"): + success, count = process_file(filepath, table, engine) + if success: + print(f"[INFO] 수동 선택된 파일 처리 완료: {count}건") + messagebox.showinfo("완료", f"DB 업데이트가 완료되었습니다.\n총 {count}건 처리됨.") + + +def main(): + engine = db.engine + try: + table = db_schema.pos + except AttributeError: + print("[ERROR] 'pos' 테이블이 db_schema에 정의되어 있지 않습니다.") + return + + batch_done = batch_process_files(table, engine) + if not batch_done: + # GUI 시작 + root = tk.Tk() + root.title("POS 데이터 업데이트") + root.geometry("300x150") + + lbl = tk.Label(root, text="POS 데이터 업데이트") + lbl.pack(pady=20) + + btn = tk.Button(root, text="데이터 선택 및 업데이트", command=run_pos_update) + btn.pack() + + root.mainloop() + + +if __name__ == "__main__": + main()