feat: POS GUI SQLite 연동 및 실시간 동기화 기능 추가
- SQLite 적립 사용자 정보 표시 (이름, 전화번호) * MSSQL 판매 내역과 SQLite 마일리지 데이터 LEFT JOIN * 적립 사용자 녹색 볼드 텍스트로 강조 * 6번째 컬럼 '적립 사용자' 추가 - QR 생성 기능 활성화 * QRGeneratorThread로 백그라운드 처리 * 미리보기 모드 체크박스 추가 * QRLabelPreviewDialog 팝업 구현 - 자동 새로고침 (30초 주기) * QTimer로 주기적으로 refresh_sales() 호출 * 실시간 적립 상태 반영 - 윈도우 크기 1100px로 확대 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
3889e2354f
commit
4581ebb7c5
@ -9,15 +9,19 @@ from datetime import datetime
|
|||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||||||
QPushButton, QLabel, QGroupBox, QTableWidget, QTableWidgetItem,
|
QPushButton, QLabel, QGroupBox, QTableWidget, QTableWidgetItem,
|
||||||
QDialog, QMessageBox, QDateEdit
|
QDialog, QMessageBox, QDateEdit, QCheckBox
|
||||||
)
|
)
|
||||||
from PyQt5.QtCore import QThread, pyqtSignal, Qt, QDate
|
from PyQt5.QtCore import QThread, pyqtSignal, Qt, QDate, QTimer
|
||||||
from PyQt5.QtGui import QFont
|
from PyQt5.QtGui import QFont
|
||||||
|
|
||||||
# 데이터베이스 연결 (backend/ 폴더를 Python 경로에 추가)
|
# 데이터베이스 연결 (backend/ 폴더를 Python 경로에 추가)
|
||||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
||||||
from db.dbsetup import DatabaseManager
|
from db.dbsetup import DatabaseManager
|
||||||
|
|
||||||
|
# QR 생성 모듈 import
|
||||||
|
from utils.qr_token_generator import generate_claim_token, save_token_to_db
|
||||||
|
from utils.qr_label_printer import print_qr_label
|
||||||
|
|
||||||
|
|
||||||
class SalesQueryThread(QThread):
|
class SalesQueryThread(QThread):
|
||||||
"""
|
"""
|
||||||
@ -36,12 +40,19 @@ class SalesQueryThread(QThread):
|
|||||||
self.date_str = date_str
|
self.date_str = date_str
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""스레드 실행 (SALE_MAIN 조회)"""
|
"""스레드 실행 (SALE_MAIN 조회 + SQLite 적립 사용자 조회)"""
|
||||||
conn = None
|
mssql_conn = None
|
||||||
|
sqlite_conn = None
|
||||||
try:
|
try:
|
||||||
db_manager = DatabaseManager()
|
db_manager = DatabaseManager()
|
||||||
conn = db_manager.get_engine('PM_PRES').raw_connection()
|
|
||||||
cursor = conn.cursor()
|
# MSSQL 연결
|
||||||
|
mssql_conn = db_manager.get_engine('PM_PRES').raw_connection()
|
||||||
|
mssql_cursor = mssql_conn.cursor()
|
||||||
|
|
||||||
|
# SQLite 연결
|
||||||
|
sqlite_conn = db_manager.get_sqlite_connection()
|
||||||
|
sqlite_cursor = sqlite_conn.cursor()
|
||||||
|
|
||||||
# 메인 쿼리: SALE_MAIN에서 오늘 판매 내역 조회
|
# 메인 쿼리: SALE_MAIN에서 오늘 판매 내역 조회
|
||||||
query = """
|
query = """
|
||||||
@ -55,27 +66,43 @@ class SalesQueryThread(QThread):
|
|||||||
ORDER BY M.InsertTime DESC
|
ORDER BY M.InsertTime DESC
|
||||||
"""
|
"""
|
||||||
|
|
||||||
cursor.execute(query, self.date_str)
|
mssql_cursor.execute(query, self.date_str)
|
||||||
rows = cursor.fetchall()
|
rows = mssql_cursor.fetchall()
|
||||||
|
|
||||||
sales_list = []
|
sales_list = []
|
||||||
for row in rows:
|
for row in rows:
|
||||||
order_no, insert_time, sale_amount, customer = row
|
order_no, insert_time, sale_amount, customer = row
|
||||||
|
|
||||||
# 품목 수 조회 (SALE_SUB)
|
# 품목 수 조회 (SALE_SUB)
|
||||||
cursor.execute("""
|
mssql_cursor.execute("""
|
||||||
SELECT COUNT(*) FROM SALE_SUB
|
SELECT COUNT(*) FROM SALE_SUB
|
||||||
WHERE SL_NO_order = ?
|
WHERE SL_NO_order = ?
|
||||||
""", order_no)
|
""", order_no)
|
||||||
item_count_row = cursor.fetchone()
|
item_count_row = mssql_cursor.fetchone()
|
||||||
item_count = item_count_row[0] if item_count_row else 0
|
item_count = item_count_row[0] if item_count_row else 0
|
||||||
|
|
||||||
|
# SQLite에서 적립 사용자 조회
|
||||||
|
sqlite_cursor.execute("""
|
||||||
|
SELECT u.nickname, u.phone
|
||||||
|
FROM claim_tokens ct
|
||||||
|
LEFT JOIN users u ON ct.claimed_by_user_id = u.id
|
||||||
|
WHERE ct.transaction_id = ? AND ct.claimed_at IS NOT NULL
|
||||||
|
""", (order_no,))
|
||||||
|
claimed_user = sqlite_cursor.fetchone()
|
||||||
|
|
||||||
|
# 적립 사용자 정보 포맷팅
|
||||||
|
if claimed_user and claimed_user['nickname'] and claimed_user['phone']:
|
||||||
|
claimed_info = f"{claimed_user['nickname']} ({claimed_user['phone']})"
|
||||||
|
else:
|
||||||
|
claimed_info = ""
|
||||||
|
|
||||||
sales_list.append({
|
sales_list.append({
|
||||||
'order_no': order_no,
|
'order_no': order_no,
|
||||||
'time': insert_time.strftime('%H:%M') if insert_time else '--:--',
|
'time': insert_time.strftime('%H:%M') if insert_time else '--:--',
|
||||||
'amount': float(sale_amount) if sale_amount else 0.0,
|
'amount': float(sale_amount) if sale_amount else 0.0,
|
||||||
'customer': customer,
|
'customer': customer,
|
||||||
'item_count': item_count
|
'item_count': item_count,
|
||||||
|
'claimed_user': claimed_info
|
||||||
})
|
})
|
||||||
|
|
||||||
self.query_complete.emit(sales_list)
|
self.query_complete.emit(sales_list)
|
||||||
@ -83,8 +110,150 @@ class SalesQueryThread(QThread):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.query_error.emit(str(e))
|
self.query_error.emit(str(e))
|
||||||
finally:
|
finally:
|
||||||
if conn:
|
if mssql_conn:
|
||||||
conn.close()
|
mssql_conn.close()
|
||||||
|
if sqlite_conn:
|
||||||
|
sqlite_conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
class QRGeneratorThread(QThread):
|
||||||
|
"""
|
||||||
|
QR 토큰 생성 및 라벨 출력 백그라운드 스레드
|
||||||
|
GUI 블로킹 방지
|
||||||
|
"""
|
||||||
|
qr_complete = pyqtSignal(bool, str, str) # 성공 여부, 메시지, 이미지 경로
|
||||||
|
|
||||||
|
def __init__(self, transaction_id, total_amount, transaction_time, preview_mode=False):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
transaction_id (str): POS 거래 ID
|
||||||
|
total_amount (float): 판매 금액
|
||||||
|
transaction_time (datetime): 거래 시간
|
||||||
|
preview_mode (bool): 미리보기 모드
|
||||||
|
"""
|
||||||
|
super().__init__()
|
||||||
|
self.transaction_id = transaction_id
|
||||||
|
self.total_amount = total_amount
|
||||||
|
self.transaction_time = transaction_time
|
||||||
|
self.preview_mode = preview_mode
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""스레드 실행"""
|
||||||
|
try:
|
||||||
|
# 1. Claim Token 생성
|
||||||
|
token_info = generate_claim_token(
|
||||||
|
self.transaction_id,
|
||||||
|
self.total_amount
|
||||||
|
)
|
||||||
|
|
||||||
|
# 2. DB 저장
|
||||||
|
success, error = save_token_to_db(
|
||||||
|
self.transaction_id,
|
||||||
|
token_info['token_hash'],
|
||||||
|
self.total_amount,
|
||||||
|
token_info['claimable_points'],
|
||||||
|
token_info['expires_at'],
|
||||||
|
token_info['pharmacy_id']
|
||||||
|
)
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
self.qr_complete.emit(False, error, "")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 3. QR 라벨 생성
|
||||||
|
if self.preview_mode:
|
||||||
|
# 미리보기
|
||||||
|
success, image_path = print_qr_label(
|
||||||
|
token_info['qr_url'],
|
||||||
|
self.transaction_id,
|
||||||
|
self.total_amount,
|
||||||
|
token_info['claimable_points'],
|
||||||
|
self.transaction_time,
|
||||||
|
preview_mode=True
|
||||||
|
)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
self.qr_complete.emit(
|
||||||
|
True,
|
||||||
|
f"QR 생성 완료 ({token_info['claimable_points']}P)",
|
||||||
|
image_path
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.qr_complete.emit(False, "이미지 생성 실패", "")
|
||||||
|
else:
|
||||||
|
# 실제 인쇄
|
||||||
|
success = print_qr_label(
|
||||||
|
token_info['qr_url'],
|
||||||
|
self.transaction_id,
|
||||||
|
self.total_amount,
|
||||||
|
token_info['claimable_points'],
|
||||||
|
self.transaction_time,
|
||||||
|
preview_mode=False
|
||||||
|
)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
self.qr_complete.emit(
|
||||||
|
True,
|
||||||
|
f"QR 출력 완료 ({token_info['claimable_points']}P)",
|
||||||
|
""
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.qr_complete.emit(False, "프린터 전송 실패", "")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.qr_complete.emit(False, f"오류: {str(e)}", "")
|
||||||
|
|
||||||
|
|
||||||
|
class QRLabelPreviewDialog(QDialog):
|
||||||
|
"""
|
||||||
|
QR 라벨 미리보기 팝업 (barcode_reader_gui.py의 LabelPreviewDialog 참고)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, image_path, transaction_id, parent=None):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
image_path (str): 미리보기 이미지 파일 경로
|
||||||
|
transaction_id (str): 거래 번호
|
||||||
|
parent: 부모 위젯
|
||||||
|
"""
|
||||||
|
super().__init__(parent)
|
||||||
|
self.image_path = image_path
|
||||||
|
self.transaction_id = transaction_id
|
||||||
|
self.init_ui()
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
"""UI 초기화"""
|
||||||
|
self.setWindowTitle(f'QR 라벨 미리보기 - {self.transaction_id}')
|
||||||
|
self.setModal(False) # 모달 아님 (계속 작업 가능)
|
||||||
|
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
|
||||||
|
# 안내 라벨
|
||||||
|
info_label = QLabel('[미리보기] 실제 인쇄하려면 "미리보기 모드" 체크를 해제하세요.')
|
||||||
|
info_label.setStyleSheet('color: #2196F3; font-size: 12px; padding: 10px;')
|
||||||
|
layout.addWidget(info_label)
|
||||||
|
|
||||||
|
# 이미지 표시
|
||||||
|
from PyQt5.QtGui import QPixmap
|
||||||
|
pixmap = QPixmap(self.image_path)
|
||||||
|
|
||||||
|
# 스케일링 (최대 1000px 폭)
|
||||||
|
if pixmap.width() > 1000:
|
||||||
|
pixmap = pixmap.scaledToWidth(1000, Qt.SmoothTransformation)
|
||||||
|
|
||||||
|
image_label = QLabel()
|
||||||
|
image_label.setPixmap(pixmap)
|
||||||
|
image_label.setAlignment(Qt.AlignCenter)
|
||||||
|
layout.addWidget(image_label)
|
||||||
|
|
||||||
|
# 닫기 버튼
|
||||||
|
close_btn = QPushButton('닫기')
|
||||||
|
close_btn.setStyleSheet('background-color: #2196F3; color: white; font-weight: bold; padding: 8px 20px;')
|
||||||
|
close_btn.clicked.connect(self.close)
|
||||||
|
layout.addWidget(close_btn)
|
||||||
|
|
||||||
|
self.setLayout(layout)
|
||||||
|
self.adjustSize()
|
||||||
|
|
||||||
|
|
||||||
class SaleDetailDialog(QDialog):
|
class SaleDetailDialog(QDialog):
|
||||||
@ -197,13 +366,14 @@ class POSSalesGUI(QMainWindow):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self.db_manager = DatabaseManager()
|
self.db_manager = DatabaseManager()
|
||||||
self.sales_thread = None
|
self.sales_thread = None
|
||||||
|
self.qr_thread = None # QR 생성 스레드 추가
|
||||||
self.sales_data = []
|
self.sales_data = []
|
||||||
self.init_ui()
|
self.init_ui()
|
||||||
|
|
||||||
def init_ui(self):
|
def init_ui(self):
|
||||||
"""UI 초기화"""
|
"""UI 초기화"""
|
||||||
self.setWindowTitle('POS 판매 조회')
|
self.setWindowTitle('POS 판매 조회')
|
||||||
self.setGeometry(100, 100, 900, 600)
|
self.setGeometry(100, 100, 1100, 600)
|
||||||
|
|
||||||
# 중앙 위젯
|
# 중앙 위젯
|
||||||
central_widget = QWidget()
|
central_widget = QWidget()
|
||||||
@ -232,13 +402,21 @@ class POSSalesGUI(QMainWindow):
|
|||||||
self.refresh_btn.clicked.connect(self.refresh_sales)
|
self.refresh_btn.clicked.connect(self.refresh_sales)
|
||||||
settings_layout.addWidget(self.refresh_btn)
|
settings_layout.addWidget(self.refresh_btn)
|
||||||
|
|
||||||
# QR 생성 버튼 (Phase 2 준비 - 현재 비활성화)
|
# QR 생성 버튼 (활성화)
|
||||||
self.qr_btn = QPushButton('QR 생성')
|
self.qr_btn = QPushButton('QR 생성')
|
||||||
self.qr_btn.setEnabled(False)
|
self.qr_btn.setEnabled(True) # 활성화!
|
||||||
self.qr_btn.setStyleSheet('background-color: #9E9E9E; color: white; padding: 8px; font-weight: bold;')
|
self.qr_btn.setStyleSheet('background-color: #FF9800; color: white; padding: 8px; font-weight: bold;')
|
||||||
self.qr_btn.setToolTip('후향적 적립 QR (추후 개발)')
|
self.qr_btn.setToolTip('선택된 거래의 QR 적립 라벨 생성')
|
||||||
|
self.qr_btn.clicked.connect(self.generate_qr_label) # 이벤트 연결
|
||||||
settings_layout.addWidget(self.qr_btn)
|
settings_layout.addWidget(self.qr_btn)
|
||||||
|
|
||||||
|
# 미리보기 모드 체크박스 추가
|
||||||
|
self.preview_checkbox = QCheckBox('미리보기 모드')
|
||||||
|
self.preview_checkbox.setChecked(True) # 기본값: 미리보기
|
||||||
|
self.preview_checkbox.setStyleSheet('font-size: 12px; color: #4CAF50;')
|
||||||
|
self.preview_checkbox.setToolTip('체크: PNG 미리보기, 해제: 프린터 직접 출력')
|
||||||
|
settings_layout.addWidget(self.preview_checkbox)
|
||||||
|
|
||||||
settings_layout.addStretch()
|
settings_layout.addStretch()
|
||||||
|
|
||||||
main_layout.addWidget(settings_group)
|
main_layout.addWidget(settings_group)
|
||||||
@ -249,15 +427,16 @@ class POSSalesGUI(QMainWindow):
|
|||||||
sales_group.setLayout(sales_layout)
|
sales_group.setLayout(sales_layout)
|
||||||
|
|
||||||
self.sales_table = QTableWidget()
|
self.sales_table = QTableWidget()
|
||||||
self.sales_table.setColumnCount(5)
|
self.sales_table.setColumnCount(6)
|
||||||
self.sales_table.setHorizontalHeaderLabels([
|
self.sales_table.setHorizontalHeaderLabels([
|
||||||
'주문번호', '시간', '금액', '고객명', '품목수'
|
'주문번호', '시간', '금액', '고객명', '품목수', '적립 사용자'
|
||||||
])
|
])
|
||||||
self.sales_table.setColumnWidth(0, 180)
|
self.sales_table.setColumnWidth(0, 160)
|
||||||
self.sales_table.setColumnWidth(1, 80)
|
self.sales_table.setColumnWidth(1, 70)
|
||||||
self.sales_table.setColumnWidth(2, 120)
|
self.sales_table.setColumnWidth(2, 110)
|
||||||
self.sales_table.setColumnWidth(3, 120)
|
self.sales_table.setColumnWidth(3, 100)
|
||||||
self.sales_table.setColumnWidth(4, 80)
|
self.sales_table.setColumnWidth(4, 70)
|
||||||
|
self.sales_table.setColumnWidth(5, 180)
|
||||||
self.sales_table.setSelectionBehavior(QTableWidget.SelectRows)
|
self.sales_table.setSelectionBehavior(QTableWidget.SelectRows)
|
||||||
self.sales_table.doubleClicked.connect(self.show_sale_detail)
|
self.sales_table.doubleClicked.connect(self.show_sale_detail)
|
||||||
|
|
||||||
@ -283,6 +462,11 @@ class POSSalesGUI(QMainWindow):
|
|||||||
# 초기 조회 실행
|
# 초기 조회 실행
|
||||||
self.refresh_sales()
|
self.refresh_sales()
|
||||||
|
|
||||||
|
# 자동 새로고침 타이머 (30초마다)
|
||||||
|
self.refresh_timer = QTimer()
|
||||||
|
self.refresh_timer.timeout.connect(self.refresh_sales)
|
||||||
|
self.refresh_timer.start(30000) # 30초 = 30000ms
|
||||||
|
|
||||||
def on_date_changed(self, date):
|
def on_date_changed(self, date):
|
||||||
"""날짜 변경 시 유효성 검사"""
|
"""날짜 변경 시 유효성 검사"""
|
||||||
today = QDate.currentDate()
|
today = QDate.currentDate()
|
||||||
@ -339,7 +523,7 @@ class POSSalesGUI(QMainWindow):
|
|||||||
amount_item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
amount_item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
||||||
self.sales_table.setItem(row, 2, amount_item)
|
self.sales_table.setItem(row, 2, amount_item)
|
||||||
|
|
||||||
# 고객명
|
# 고객명 (MSSQL)
|
||||||
self.sales_table.setItem(row, 3, QTableWidgetItem(sale['customer']))
|
self.sales_table.setItem(row, 3, QTableWidgetItem(sale['customer']))
|
||||||
|
|
||||||
# 품목수 (중앙 정렬)
|
# 품목수 (중앙 정렬)
|
||||||
@ -347,6 +531,16 @@ class POSSalesGUI(QMainWindow):
|
|||||||
count_item.setTextAlignment(Qt.AlignCenter)
|
count_item.setTextAlignment(Qt.AlignCenter)
|
||||||
self.sales_table.setItem(row, 4, count_item)
|
self.sales_table.setItem(row, 4, count_item)
|
||||||
|
|
||||||
|
# 적립 사용자 (SQLite)
|
||||||
|
claimed_item = QTableWidgetItem(sale['claimed_user'])
|
||||||
|
if sale['claimed_user']:
|
||||||
|
from PyQt5.QtGui import QColor, QFont
|
||||||
|
claimed_item.setForeground(QColor('#4CAF50'))
|
||||||
|
font = QFont()
|
||||||
|
font.setBold(True)
|
||||||
|
claimed_item.setFont(font)
|
||||||
|
self.sales_table.setItem(row, 5, claimed_item)
|
||||||
|
|
||||||
def on_query_error(self, error_msg):
|
def on_query_error(self, error_msg):
|
||||||
"""DB 조회 에러 처리"""
|
"""DB 조회 에러 처리"""
|
||||||
QMessageBox.critical(self, '오류', f'조회 실패:\n{error_msg}')
|
QMessageBox.critical(self, '오류', f'조회 실패:\n{error_msg}')
|
||||||
@ -364,10 +558,89 @@ class POSSalesGUI(QMainWindow):
|
|||||||
detail_dialog = SaleDetailDialog(order_no, self)
|
detail_dialog = SaleDetailDialog(order_no, self)
|
||||||
detail_dialog.exec_()
|
detail_dialog.exec_()
|
||||||
|
|
||||||
|
def generate_qr_label(self):
|
||||||
|
"""선택된 판매 건에 대해 QR 라벨 생성"""
|
||||||
|
# 선택된 행 확인
|
||||||
|
current_row = self.sales_table.currentRow()
|
||||||
|
if current_row < 0:
|
||||||
|
QMessageBox.warning(self, '경고', '거래를 선택해주세요.')
|
||||||
|
return
|
||||||
|
|
||||||
|
# 거래 정보 가져오기
|
||||||
|
order_no = self.sales_table.item(current_row, 0).text()
|
||||||
|
amount_text = self.sales_table.item(current_row, 2).text()
|
||||||
|
|
||||||
|
# 금액 파싱 (예: "50,000원" → 50000.0)
|
||||||
|
amount = float(amount_text.replace(',', '').replace('원', ''))
|
||||||
|
|
||||||
|
# sales_data에서 거래 시간 찾기
|
||||||
|
sale = next((s for s in self.sales_data if s['order_no'] == order_no), None)
|
||||||
|
if not sale:
|
||||||
|
QMessageBox.warning(self, '오류', '거래 정보를 찾을 수 없습니다.')
|
||||||
|
return
|
||||||
|
|
||||||
|
# 거래 시간 파싱
|
||||||
|
date_str = self.date_edit.date().toString('yyyy-MM-dd')
|
||||||
|
time_str = sale['time']
|
||||||
|
transaction_time = datetime.strptime(f"{date_str} {time_str}", '%Y-%m-%d %H:%M')
|
||||||
|
|
||||||
|
# 중복 방지 확인 (이미 QR 생성된 거래인지)
|
||||||
|
if self.qr_thread and self.qr_thread.isRunning():
|
||||||
|
QMessageBox.warning(self, '경고', 'QR 생성 중입니다. 잠시만 기다려주세요.')
|
||||||
|
return
|
||||||
|
|
||||||
|
# 미리보기 모드 확인
|
||||||
|
preview_mode = self.preview_checkbox.isChecked()
|
||||||
|
|
||||||
|
# QR 생성 스레드 시작
|
||||||
|
self.qr_thread = QRGeneratorThread(
|
||||||
|
order_no,
|
||||||
|
amount,
|
||||||
|
transaction_time,
|
||||||
|
preview_mode
|
||||||
|
)
|
||||||
|
self.qr_thread.qr_complete.connect(self.on_qr_generated)
|
||||||
|
self.qr_thread.start()
|
||||||
|
|
||||||
|
# 상태 표시
|
||||||
|
self.status_label.setText(f'QR 생성 중... ({order_no})')
|
||||||
|
self.status_label.setStyleSheet('color: orange; font-size: 12px; padding: 5px;')
|
||||||
|
self.qr_btn.setEnabled(False)
|
||||||
|
|
||||||
|
def on_qr_generated(self, success, message, image_path):
|
||||||
|
"""QR 생성 완료 시그널 핸들러"""
|
||||||
|
# 버튼 재활성화
|
||||||
|
self.qr_btn.setEnabled(True)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
# 성공
|
||||||
|
self.status_label.setText(f'✓ {message}')
|
||||||
|
self.status_label.setStyleSheet('color: green; font-size: 12px; padding: 5px;')
|
||||||
|
|
||||||
|
# 미리보기 모드면 Dialog 표시
|
||||||
|
if image_path:
|
||||||
|
order_no = self.sales_table.item(self.sales_table.currentRow(), 0).text()
|
||||||
|
preview_dialog = QRLabelPreviewDialog(image_path, order_no, self)
|
||||||
|
preview_dialog.show()
|
||||||
|
else:
|
||||||
|
# 실제 인쇄 완료
|
||||||
|
QMessageBox.information(self, '완료', f'{message}\n프린터: 192.168.0.168')
|
||||||
|
else:
|
||||||
|
# 실패
|
||||||
|
self.status_label.setText('QR 생성 실패')
|
||||||
|
self.status_label.setStyleSheet('color: red; font-size: 12px; padding: 5px;')
|
||||||
|
QMessageBox.critical(self, '오류', f'QR 생성 실패:\n{message}')
|
||||||
|
|
||||||
def closeEvent(self, event):
|
def closeEvent(self, event):
|
||||||
"""종료 시 정리"""
|
"""종료 시 정리"""
|
||||||
|
# 자동 새로고침 타이머 중지
|
||||||
|
if hasattr(self, 'refresh_timer'):
|
||||||
|
self.refresh_timer.stop()
|
||||||
|
|
||||||
if self.sales_thread and self.sales_thread.isRunning():
|
if self.sales_thread and self.sales_thread.isRunning():
|
||||||
self.sales_thread.wait()
|
self.sales_thread.wait()
|
||||||
|
if self.qr_thread and self.qr_thread.isRunning(): # QR 스레드 추가
|
||||||
|
self.qr_thread.wait()
|
||||||
self.db_manager.close_all()
|
self.db_manager.close_all()
|
||||||
event.accept()
|
event.accept()
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user