diff --git a/lib/pos_view_gui.py b/lib/pos_view_gui.py index 0897885..971c29e 100644 --- a/lib/pos_view_gui.py +++ b/lib/pos_view_gui.py @@ -9,12 +9,13 @@ from tkcalendar import DateEntry from datetime import datetime, timedelta from sqlalchemy import select, func, between from conf import db_schema, db +from lib import holiday # 휴일 기능 -# Windows DPI Awareness 설정 (윈도우 전용) +# Windows DPI Awareness 설정 if sys.platform == "win32": import ctypes try: - ctypes.windll.shcore.SetProcessDpiAwareness(1) # SYSTEM_AWARE = 1 + ctypes.windll.shcore.SetProcessDpiAwareness(1) except Exception: pass @@ -26,30 +27,23 @@ class PosViewGUI(ctk.CTk): super().__init__() self.title("POS 데이터 조회") - self.geometry("900x500") - self.configure(fg_color="#f0f0f0") # 배경색 맞춤 + self.geometry("1100x700") + 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")) + 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') @@ -60,6 +54,18 @@ class PosViewGUI(ctk.CTk): 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") + # 날짜유형 라디오버튼 + self.date_filter_var = ctk.StringVar(value="전체") + ctk.CTkLabel(self, text="날짜유형:", font=self.label_font, fg_color="#f0f0f0")\ + .grid(row=0, column=4, padx=(10, 0), pady=5, sticky="e") + + ctk.CTkRadioButton(self, text="전체", variable=self.date_filter_var, value="전체")\ + .grid(row=0, column=5, padx=2, pady=5, sticky="w") + ctk.CTkRadioButton(self, text="휴일", variable=self.date_filter_var, value="휴일")\ + .grid(row=0, column=6, padx=2, pady=5, sticky="w") + ctk.CTkRadioButton(self, text="평일", variable=self.date_filter_var, value="평일")\ + .grid(row=0, column=7, padx=2, 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") @@ -82,9 +88,9 @@ class PosViewGUI(ctk.CTk): # 조회 버튼 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) + self.search_btn.grid(row=3, column=0, columnspan=8, pady=10) - # 결과 Treeview + # 상품별 트리뷰 self.DISPLAY_COLUMNS = ['ca01', 'ca02', 'ca03', 'name', 'qty', 'tot_amount', 'tot_discount', 'actual_amount'] self.COLUMN_LABELS = { 'ca01': '대분류', @@ -97,30 +103,40 @@ class PosViewGUI(ctk.CTk): 'actual_amount': '실매출액' } - self.tree = ttk.Treeview(self, columns=self.DISPLAY_COLUMNS, show='headings', height=15) + self.tree = ttk.Treeview(self, columns=self.DISPLAY_COLUMNS, show='headings', height=12) 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.tree.grid(row=4, column=0, columnspan=8, padx=10, pady=10, sticky="nsew") + + # 날짜 요약 트리뷰 + self.date_tree = ttk.Treeview(self, columns=['date', 'qty', 'tot_amount', 'actual_amount'], show='headings', height=6) + self.date_tree.heading('date', text='일자') + self.date_tree.heading('qty', text='수량합') + self.date_tree.heading('tot_amount', text='총매출합') + self.date_tree.heading('actual_amount', text='실매출합') + + for col in ['date', 'qty', 'tot_amount', 'actual_amount']: + self.date_tree.column(col, width=150, anchor='center') + + self.date_tree.grid(row=5, column=0, columnspan=8, padx=10, pady=(0, 10), sticky="nsew") - # 그리드 가중치 설정 (창 크기에 따라 트리뷰 확장) self.grid_rowconfigure(4, weight=1) - for col_index in range(4): + self.grid_rowconfigure(5, weight=1) + for col_index in range(8): 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() @@ -148,16 +164,42 @@ class PosViewGUI(ctk.CTk): result = conn.execute(stmt) ca03_list = [row[0] for row in result.fetchall()] self.ca03_combo.configure(values=['전체'] + ca03_list) - self.ca03_combo.set('전체') # 항상 기본값으로 초기화 + self.ca03_combo.set('전체') def search(self): + print("🔍 date_filter:", date_filter, + "| start:", start_date, "end:", end_date) + if date_filter == "휴일": + valid_dates = holiday.get_holiday_dates(start_date, end_date) + print("🚩 반환된 휴일 날짜 리스트:", valid_dates) + 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() + date_filter = self.date_filter_var.get() + + conditions = [] + + if date_filter == "전체": + conditions.append(between(pos_table.c.date, start_date, end_date)) + else: + if date_filter == "휴일": + valid_dates = holiday.get_holiday_dates(start_date, end_date) + elif date_filter == "평일": + valid_dates = holiday.get_weekday_dates(start_date, end_date) + else: + valid_dates = set() + + if not valid_dates: + messagebox.showinfo("알림", f"{date_filter}에 해당하는 데이터가 없습니다.") + self.tree.delete(*self.tree.get_children()) + self.date_tree.delete(*self.date_tree.get_children()) + return + + conditions.append(pos_table.c.date.in_(valid_dates)) - conditions = [between(pos_table.c.date, start_date, end_date)] if ca01_val != '전체': conditions.append(pos_table.c.ca01 == ca01_val) if ca03_val != '전체': @@ -166,6 +208,7 @@ class PosViewGUI(ctk.CTk): 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, @@ -179,11 +222,42 @@ class PosViewGUI(ctk.CTk): result = conn.execute(stmt).mappings().all() + # 날짜별 요약 + date_stmt = select( + pos_table.c.date, + func.sum(pos_table.c.qty).label("qty"), + func.sum(pos_table.c.tot_amount).label("tot_amount"), + func.sum(pos_table.c.actual_amount).label("actual_amount") + ).where(*conditions).group_by(pos_table.c.date).order_by(pos_table.c.date) + + date_summary = conn.execute(date_stmt).mappings().all() + + # 트리뷰 초기화 self.tree.delete(*self.tree.get_children()) + self.date_tree.delete(*self.date_tree.get_children()) + + # 상품별 출력 for row in result: values = tuple(row[col] for col in self.DISPLAY_COLUMNS) self.tree.insert('', 'end', values=values) + # 날짜별 출력 + total_qty = total_amount = total_actual = 0 + for row in date_summary: + self.date_tree.insert('', 'end', values=( + row['date'].strftime("%Y-%m-%d"), + row['qty'], + row['tot_amount'], + row['actual_amount'] + )) + total_qty += row['qty'] + total_amount += row['tot_amount'] + total_actual += row['actual_amount'] + + # 총합계 추가 + self.date_tree.insert('', 'end', values=("총합계", total_qty, total_amount, total_actual)) + + if __name__ == "__main__": try: import tkcalendar