POS에서 삽입한 데이터를 GUI로 조회할 수 있는 툴

This commit is contained in:
2025-07-21 17:36:12 +09:00
parent b2a63cdc76
commit 4e6b246066

195
lib/pos_view_gui.py Normal file
View File

@ -0,0 +1,195 @@
import sys
import os
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
import customtkinter as ctk
import tkinter.font as tkFont
from tkinter import messagebox, ttk
from tkcalendar import DateEntry
from datetime import datetime, timedelta
from sqlalchemy import select, func, between
from conf import db_schema, db
# Windows DPI Awareness 설정 (윈도우 전용)
if sys.platform == "win32":
import ctypes
try:
ctypes.windll.shcore.SetProcessDpiAwareness(1) # SYSTEM_AWARE = 1
except Exception:
pass
pos_table = db_schema.pos
engine = db.engine
class PosViewGUI(ctk.CTk):
def __init__(self):
super().__init__()
self.title("POS 데이터 조회")
self.geometry("900x500")
self.configure(fg_color="#f0f0f0") # 배경색 맞춤
ctk.set_appearance_mode("light")
ctk.set_default_color_theme("blue")
# 폰트 세팅 - NanumGothic이 없으면 Arial 대체
try:
self.label_font = ("NanumGothic", 13)
except Exception:
self.label_font = ("Arial", 13)
# Treeview 스타일 설정 (ttk 스타일)
style = ttk.Style(self)
style.theme_use('default')
style.configure("Treeview",
font=("NanumGothic", 12),
rowheight=30) # 높이 조절로 글씨 깨짐 방지
style.configure("Treeview.Heading",
font=("NanumGothic", 13, "bold"))
# --- 위젯 배치 ---
# 날짜 범위
ctk.CTkLabel(self, text="시작일:", anchor="w", font=self.label_font, fg_color="#f0f0f0")\
.grid(row=0, column=0, padx=10, pady=5, sticky="e")
self.start_date_entry = DateEntry(self, width=12, background='darkblue', foreground='white')
self.start_date_entry.grid(row=0, column=1, padx=10, pady=5, sticky="w")
ctk.CTkLabel(self, text="종료일:", anchor="w", font=self.label_font, fg_color="#f0f0f0")\
.grid(row=0, column=2, padx=10, pady=5, sticky="e")
self.end_date_entry = DateEntry(self, width=12, background='darkblue', foreground='white')
self.end_date_entry.grid(row=0, column=3, padx=10, pady=5, sticky="w")
# 대분류
ctk.CTkLabel(self, text="대분류 :", anchor="w", font=self.label_font, fg_color="#f0f0f0")\
.grid(row=1, column=0, padx=10, pady=5, sticky="e")
self.ca01_combo = ctk.CTkComboBox(self, values=["전체"], width=180)
self.ca01_combo.grid(row=1, column=1, padx=10, pady=5, sticky="w")
self.ca01_combo.configure(command=self.on_ca01_selected)
# 소분류
ctk.CTkLabel(self, text="소분류 :", anchor="w", font=self.label_font, fg_color="#f0f0f0")\
.grid(row=1, column=2, padx=10, pady=5, sticky="e")
self.ca03_combo = ctk.CTkComboBox(self, values=["전체"], width=180)
self.ca03_combo.grid(row=1, column=3, padx=10, pady=5, sticky="w")
# 상품명
ctk.CTkLabel(self, text="상품명 :", anchor="w", font=self.label_font, fg_color="#f0f0f0")\
.grid(row=2, column=0, padx=10, pady=5, sticky="e")
self.name_entry = ctk.CTkEntry(self, width=280)
self.name_entry.grid(row=2, column=1, columnspan=3, padx=10, pady=5, sticky="w")
# 조회 버튼
self.search_btn = ctk.CTkButton(self, text="조회", command=self.search,
fg_color="#0d6efd", hover_color="#0b5ed7", text_color="white")
self.search_btn.grid(row=3, column=0, columnspan=4, pady=10)
# 결과 Treeview
self.DISPLAY_COLUMNS = ['ca01', 'ca02', 'ca03', 'name', 'qty', 'tot_amount', 'tot_discount', 'actual_amount']
self.COLUMN_LABELS = {
'ca01': '대분류',
'ca02': '중분류',
'ca03': '소분류',
'name': '상품명',
'qty': '수량',
'tot_amount': '총매출액',
'tot_discount': '총할인액',
'actual_amount': '실매출액'
}
self.tree = ttk.Treeview(self, columns=self.DISPLAY_COLUMNS, show='headings', height=15)
for col in self.DISPLAY_COLUMNS:
self.tree.heading(col, text=self.COLUMN_LABELS[col])
self.tree.column(col, width=120, anchor='center')
self.tree.grid(row=4, column=0, columnspan=4, padx=10, pady=10, sticky="nsew")
# 그리드 가중치 설정 (창 크기에 따라 트리뷰 확장)
self.grid_rowconfigure(4, weight=1)
for col_index in range(4):
self.grid_columnconfigure(col_index, weight=1)
# 날짜 기본값 설정 (전날부터 7일 전까지)
end_date = datetime.today().date() - timedelta(days=1)
start_date = end_date - timedelta(days=6)
self.start_date_entry.set_date(start_date)
self.end_date_entry.set_date(end_date)
# 초기 대분류, 소분류 콤보박스 값 불러오기
self.load_ca01_options()
def on_ca01_selected(self, value):
# print("대분류 선택됨:", value) 디버깅용
self.load_ca03_options()
def load_ca01_options(self):
start_date = self.start_date_entry.get_date()
end_date = self.end_date_entry.get_date()
with engine.connect() as conn:
stmt = select(pos_table.c.ca01).where(
between(pos_table.c.date, start_date, end_date)
).distinct().order_by(pos_table.c.ca01)
result = conn.execute(stmt)
ca01_list = [row[0] for row in result.fetchall()]
self.ca01_combo.configure(values=['전체'] + ca01_list)
self.ca01_combo.set('전체')
self.load_ca03_options()
def load_ca03_options(self):
start_date = self.start_date_entry.get_date()
end_date = self.end_date_entry.get_date()
ca01_val = self.ca01_combo.get()
with engine.connect() as conn:
stmt = select(pos_table.c.ca03).where(
between(pos_table.c.date, start_date, end_date)
)
if ca01_val != '전체':
stmt = stmt.where(pos_table.c.ca01 == ca01_val)
stmt = stmt.distinct().order_by(pos_table.c.ca03)
result = conn.execute(stmt)
ca03_list = [row[0] for row in result.fetchall()]
self.ca03_combo.configure(values=['전체'] + ca03_list)
self.ca03_combo.set('전체') # 항상 기본값으로 초기화
def search(self):
start_date = self.start_date_entry.get_date()
end_date = self.end_date_entry.get_date()
ca01_val = self.ca01_combo.get()
ca03_val = self.ca03_combo.get()
name_val = self.name_entry.get().strip()
conditions = [between(pos_table.c.date, start_date, end_date)]
if ca01_val != '전체':
conditions.append(pos_table.c.ca01 == ca01_val)
if ca03_val != '전체':
conditions.append(pos_table.c.ca03 == ca03_val)
if name_val:
conditions.append(pos_table.c.name.like(f"%{name_val}%"))
with engine.connect() as conn:
stmt = select(
pos_table.c.ca01,
pos_table.c.ca02,
pos_table.c.ca03,
pos_table.c.name,
func.sum(pos_table.c.qty).label("qty"),
func.sum(pos_table.c.tot_amount).label("tot_amount"),
func.sum(pos_table.c.tot_discount).label("tot_discount"),
func.sum(pos_table.c.actual_amount).label("actual_amount")
).where(*conditions).group_by(pos_table.c.barcode).order_by(pos_table.c.ca01, pos_table.c.ca03)
result = conn.execute(stmt).mappings().all()
self.tree.delete(*self.tree.get_children())
for row in result:
values = tuple(row[col] for col in self.DISPLAY_COLUMNS)
self.tree.insert('', 'end', values=values)
if __name__ == "__main__":
try:
import tkcalendar
except ImportError:
print("tkcalendar가 설치되어 있지 않습니다. 'pip install tkcalendar'로 설치해주세요.")
sys.exit(1)
app = PosViewGUI()
app.mainloop()