feat: POS GUI 회원 마일리지 내역 조회 기능 추가

- UserMileageDialog 클래스 추가
  * 전화번호로 회원 정보 및 적립 내역 조회
  * 회원 정보: 이름, 전화번호, 포인트 잔액, 가입일
  * 적립 내역 테이블: 날짜, 구분(적립/사용), 포인트, 잔액, 설명
  * 최근 50건 표시
- 적립 사용자 클릭 시 모달 팝업
  * 적립자명, 전화번호, 적립포인트 컬럼 클릭 가능
  * 녹색 볼드 + 밑줄로 클릭 가능 표시
  * 툴팁 추가: '클릭하여 회원 마일리지 내역 보기'
- cellClicked 이벤트 핸들러 연결

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
시골약사 2026-01-23 17:03:34 +09:00
parent 3b8f8b232c
commit 3102940c09

View File

@ -363,6 +363,151 @@ class SaleDetailDialog(QDialog):
conn.close()
class UserMileageDialog(QDialog):
"""
회원 마일리지 내역 조회 팝업
적립 사용자 클릭 해당 회원의 적립 내역 표시
"""
def __init__(self, phone, parent=None):
"""
Args:
phone: 전화번호
parent: 부모 위젯
"""
super().__init__(parent)
self.phone = phone
self.setWindowTitle(f'회원 마일리지 내역')
self.setModal(True)
self.resize(800, 500)
self.init_ui()
self.load_user_info()
def init_ui(self):
"""UI 초기화"""
layout = QVBoxLayout()
# 회원 정보 그룹박스
info_group = QGroupBox('회원 정보')
info_layout = QVBoxLayout()
info_group.setLayout(info_layout)
self.info_label = QLabel('조회 중...')
self.info_label.setStyleSheet('font-size: 13px; padding: 10px; background: #f5f7fa; border-radius: 8px;')
info_layout.addWidget(self.info_label)
layout.addWidget(info_group)
# 적립 내역 테이블
history_group = QGroupBox('적립 내역')
history_layout = QVBoxLayout()
history_group.setLayout(history_layout)
self.history_table = QTableWidget()
self.history_table.setColumnCount(5)
self.history_table.setHorizontalHeaderLabels([
'날짜', '구분', '포인트', '잔액', '설명'
])
self.history_table.setColumnWidth(0, 150)
self.history_table.setColumnWidth(1, 80)
self.history_table.setColumnWidth(2, 100)
self.history_table.setColumnWidth(3, 100)
self.history_table.setColumnWidth(4, 300)
history_layout.addWidget(self.history_table)
layout.addWidget(history_group)
# 닫기 버튼
close_btn = QPushButton('닫기')
close_btn.setStyleSheet('background-color: #2196F3; color: white; padding: 8px; font-weight: bold;')
close_btn.clicked.connect(self.close)
layout.addWidget(close_btn)
self.setLayout(layout)
def load_user_info(self):
"""SQLite에서 회원 정보 및 적립 내역 조회"""
conn = None
try:
db_manager = DatabaseManager()
conn = db_manager.get_sqlite_connection()
cursor = conn.cursor()
# 전화번호로 사용자 조회
cursor.execute("""
SELECT id, nickname, phone, mileage_balance, created_at
FROM users WHERE phone = ?
""", (self.phone,))
user = cursor.fetchone()
if not user:
self.info_label.setText('등록되지 않은 회원입니다.')
return
# 회원 정보 표시
info_text = f"""
<b>이름:</b> {user['nickname']}<br>
<b>전화번호:</b> {user['phone']}<br>
<b>포인트 잔액:</b> <span style="color: #4CAF50; font-weight: bold;">{user['mileage_balance']:,}P</span><br>
<b>가입일:</b> {user['created_at']}
"""
self.info_label.setText(info_text)
# 적립 내역 조회
cursor.execute("""
SELECT points, balance_after, reason, description, created_at
FROM mileage_ledger
WHERE user_id = ?
ORDER BY created_at DESC
LIMIT 50
""", (user['id'],))
transactions = cursor.fetchall()
# 테이블에 데이터 채우기
self.history_table.setRowCount(len(transactions))
for row_idx, tx in enumerate(transactions):
from PyQt5.QtGui import QColor
# 날짜
date_item = QTableWidgetItem(tx['created_at'])
self.history_table.setItem(row_idx, 0, date_item)
# 구분 (CLAIM, USE 등)
reason_text = '적립' if tx['reason'] == 'CLAIM' else '사용'
reason_item = QTableWidgetItem(reason_text)
reason_item.setTextAlignment(Qt.AlignCenter)
if tx['reason'] == 'CLAIM':
reason_item.setForeground(QColor('#4CAF50'))
else:
reason_item.setForeground(QColor('#f03e3e'))
self.history_table.setItem(row_idx, 1, reason_item)
# 포인트 (우측 정렬)
points_item = QTableWidgetItem(f"{tx['points']:+,}P")
points_item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
if tx['points'] > 0:
points_item.setForeground(QColor('#4CAF50'))
else:
points_item.setForeground(QColor('#f03e3e'))
self.history_table.setItem(row_idx, 2, points_item)
# 잔액 (우측 정렬)
balance_item = QTableWidgetItem(f"{tx['balance_after']:,}P")
balance_item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
self.history_table.setItem(row_idx, 3, balance_item)
# 설명
self.history_table.setItem(row_idx, 4, QTableWidgetItem(tx['description']))
except Exception as e:
QMessageBox.critical(self, '오류', f'회원 정보 조회 실패:\n{str(e)}')
finally:
if conn:
conn.close()
class POSSalesGUI(QMainWindow):
"""
POS 판매 내역 조회 메인 GUI
@ -447,6 +592,7 @@ class POSSalesGUI(QMainWindow):
self.sales_table.setColumnWidth(7, 100)
self.sales_table.setSelectionBehavior(QTableWidget.SelectRows)
self.sales_table.doubleClicked.connect(self.show_sale_detail)
self.sales_table.cellClicked.connect(self.on_cell_clicked)
sales_layout.addWidget(self.sales_table)
@ -546,7 +692,9 @@ class POSSalesGUI(QMainWindow):
claimed_name_item.setForeground(QColor('#4CAF50'))
font = QFont()
font.setBold(True)
font.setUnderline(True) # 밑줄 추가로 클릭 가능 표시
claimed_name_item.setFont(font)
claimed_name_item.setToolTip('클릭하여 회원 마일리지 내역 보기')
self.sales_table.setItem(row, 5, claimed_name_item)
# 전화번호 (SQLite)
@ -555,7 +703,9 @@ class POSSalesGUI(QMainWindow):
claimed_phone_item.setForeground(QColor('#4CAF50'))
font = QFont()
font.setBold(True)
font.setUnderline(True) # 밑줄 추가로 클릭 가능 표시
claimed_phone_item.setFont(font)
claimed_phone_item.setToolTip('클릭하여 회원 마일리지 내역 보기')
self.sales_table.setItem(row, 6, claimed_phone_item)
# 적립포인트 (SQLite)
@ -565,7 +715,9 @@ class POSSalesGUI(QMainWindow):
claimed_points_item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
font = QFont()
font.setBold(True)
font.setUnderline(True) # 밑줄 추가로 클릭 가능 표시
claimed_points_item.setFont(font)
claimed_points_item.setToolTip('클릭하여 회원 마일리지 내역 보기')
self.sales_table.setItem(row, 7, claimed_points_item)
def on_query_error(self, error_msg):
@ -585,6 +737,24 @@ class POSSalesGUI(QMainWindow):
detail_dialog = SaleDetailDialog(order_no, self)
detail_dialog.exec_()
def on_cell_clicked(self, row, column):
"""테이블 셀 클릭 이벤트 - 적립 사용자 클릭 시 마일리지 내역 표시"""
# 컬럼 5(적립자명), 6(전화번호), 7(적립포인트) 중 하나를 클릭했는지 확인
if column not in [5, 6, 7]:
return
# 전화번호 가져오기 (6번 컬럼)
phone_item = self.sales_table.item(row, 6)
if not phone_item or not phone_item.text():
# 적립 사용자가 없는 경우
return
phone = phone_item.text()
# 회원 마일리지 내역 Dialog 표시
mileage_dialog = UserMileageDialog(phone, self)
mileage_dialog.exec_()
def generate_qr_label(self):
"""선택된 판매 건에 대해 QR 라벨 생성"""
# 선택된 행 확인