- 모던 다크 미니멀 테마(NanumSquare 번들, 단일 accent #4DABF7, 8px radius, flat 버튼, 다크 기본값) - 라인 아이콘 시스템(ui/icons.py, QtSvg) — 앱 전반 이모지 교체 - 다크 깨짐 수정: 테이블 헤더/코너 흰색, 도움말 탭 흰 라인, 트레이/미니위젯 메뉴 - fix: 자동 적립 OFF가 자동 퇴근 경로에서 무시되던 버그(게이팅) - feat: 연장근무 적립 기록 삭제(우클릭) - 테스트 3건 추가 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
572 lines
21 KiB
Python
572 lines
21 KiB
Python
"""
|
|
연장근무 상세 내역 뷰
|
|
"""
|
|
from PyQt5.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QLabel,
|
|
QPushButton, QTableWidget, QTableWidgetItem,
|
|
QHeaderView, QGroupBox, QMessageBox, QMenu, QAction,
|
|
QDateEdit, QSpinBox, QLineEdit, QComboBox)
|
|
from PyQt5.QtCore import Qt, QDate
|
|
from PyQt5.QtGui import QColor
|
|
import sys
|
|
import os
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
from core.database import Database
|
|
from core.i18n import tr
|
|
from ui.styles import get_theme, ThemeColors, apply_dark_titlebar
|
|
|
|
|
|
class OvertimeView(QDialog):
|
|
"""연장근무 상세 내역 다이얼로그"""
|
|
|
|
def __init__(self, parent=None, db=None):
|
|
super().__init__(parent)
|
|
self.db = db if db else Database()
|
|
self.init_ui()
|
|
self.load_data()
|
|
apply_dark_titlebar(self)
|
|
|
|
def init_ui(self):
|
|
"""UI 초기화"""
|
|
self.setWindowTitle(tr('window.overtime_view'))
|
|
self.setModal(True)
|
|
self.setMinimumSize(800, 500)
|
|
|
|
layout = QVBoxLayout()
|
|
layout.setSpacing(6)
|
|
layout.setContentsMargins(12, 10, 12, 10)
|
|
|
|
# 제목 + 잔액 한 줄
|
|
header_layout = QHBoxLayout()
|
|
title = QLabel(tr('view.overtime.title'))
|
|
title.setObjectName("dialog_title")
|
|
header_layout.addWidget(title)
|
|
header_layout.addStretch()
|
|
self.balance_label = QLabel(tr('view.overtime.balance_zero'))
|
|
self.balance_label.setObjectName("badge_balance")
|
|
header_layout.addWidget(self.balance_label)
|
|
layout.addLayout(header_layout)
|
|
|
|
# 적립 내역
|
|
earned_group = QGroupBox(tr('view.overtime.earned_group'))
|
|
earned_layout = QVBoxLayout()
|
|
earned_layout.setSpacing(4)
|
|
earned_layout.setContentsMargins(8, 20, 8, 6)
|
|
|
|
self.earned_table = QTableWidget()
|
|
self.earned_table.setColumnCount(3)
|
|
self.earned_table.setHorizontalHeaderLabels([
|
|
tr('view.overtime.col_date'),
|
|
tr('view.overtime.col_earned'),
|
|
tr('view.overtime.col_memo'),
|
|
])
|
|
self.earned_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents)
|
|
self.earned_table.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeToContents)
|
|
self.earned_table.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch)
|
|
self.earned_table.setAlternatingRowColors(True)
|
|
self.earned_table.setEditTriggers(QTableWidget.NoEditTriggers)
|
|
self.earned_table.setSelectionBehavior(QTableWidget.SelectRows)
|
|
self.earned_table.setContextMenuPolicy(Qt.CustomContextMenu)
|
|
self.earned_table.customContextMenuRequested.connect(self.show_earned_context_menu)
|
|
earned_layout.addWidget(self.earned_table)
|
|
|
|
add_earned_button = QPushButton(tr('view.overtime.btn_add_earned'))
|
|
add_earned_button.clicked.connect(self.add_earned_record)
|
|
earned_layout.addWidget(add_earned_button)
|
|
|
|
earned_group.setLayout(earned_layout)
|
|
layout.addWidget(earned_group)
|
|
|
|
# 사용 내역
|
|
used_group = QGroupBox(tr('view.overtime.used_group'))
|
|
used_layout = QVBoxLayout()
|
|
used_layout.setSpacing(4)
|
|
used_layout.setContentsMargins(8, 20, 8, 6)
|
|
|
|
self.used_table = QTableWidget()
|
|
self.used_table.setColumnCount(3)
|
|
self.used_table.setHorizontalHeaderLabels([
|
|
tr('view.overtime.col_date'),
|
|
tr('view.overtime.col_used'),
|
|
tr('view.overtime.col_reason'),
|
|
])
|
|
self.used_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents)
|
|
self.used_table.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeToContents)
|
|
self.used_table.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch)
|
|
self.used_table.setAlternatingRowColors(True)
|
|
self.used_table.setEditTriggers(QTableWidget.NoEditTriggers)
|
|
self.used_table.setSelectionBehavior(QTableWidget.SelectRows)
|
|
self.used_table.setContextMenuPolicy(Qt.CustomContextMenu)
|
|
self.used_table.customContextMenuRequested.connect(self.show_used_context_menu)
|
|
used_layout.addWidget(self.used_table)
|
|
|
|
add_used_button = QPushButton(tr('view.overtime.btn_add_used'))
|
|
add_used_button.clicked.connect(self.add_used_record)
|
|
used_layout.addWidget(add_used_button)
|
|
|
|
used_group.setLayout(used_layout)
|
|
layout.addWidget(used_group)
|
|
|
|
# 닫기 버튼
|
|
close_button = QPushButton(tr('btn.close'))
|
|
close_button.clicked.connect(self.close)
|
|
layout.addWidget(close_button)
|
|
|
|
self.setLayout(layout)
|
|
|
|
def load_data(self):
|
|
"""데이터 로드"""
|
|
# 잔액 업데이트
|
|
balance = self.db.get_total_overtime_balance()
|
|
hours = balance // 60
|
|
minutes = balance % 60
|
|
self.balance_label.setText(tr('view.overtime.balance_fmt',
|
|
h=hours, m=minutes, total=balance))
|
|
|
|
# 적립 내역 로드
|
|
conn = self.db.get_connection()
|
|
cursor = conn.cursor()
|
|
|
|
cursor.execute('''
|
|
SELECT ob.date, ob.earned_minutes, ob.work_record_id, wr.memo, ob.id
|
|
FROM overtime_bank ob
|
|
LEFT JOIN work_records wr ON ob.work_record_id = wr.id
|
|
ORDER BY ob.date DESC
|
|
''')
|
|
earned_records = cursor.fetchall()
|
|
manual_label = tr('msg.manual_added')
|
|
|
|
self.earned_table.setRowCount(len(earned_records))
|
|
for i, record in enumerate(earned_records):
|
|
date_item = QTableWidgetItem(record[0])
|
|
date_item.setTextAlignment(Qt.AlignCenter)
|
|
date_item.setData(Qt.UserRole, record[4]) # overtime_bank.id 저장 (삭제용)
|
|
|
|
minutes = record[1]
|
|
hours = minutes // 60
|
|
mins = minutes % 60
|
|
if hours > 0:
|
|
time_str = tr('view.break.duration_fmt', h=hours, m=mins)
|
|
else:
|
|
time_str = tr('view.break.duration_min_only', m=mins)
|
|
time_item = QTableWidgetItem(time_str)
|
|
time_item.setTextAlignment(Qt.AlignCenter)
|
|
time_item.setForeground(QColor(81, 207, 102)) # 적립 = 그린 (#51CF66)
|
|
|
|
# work_record_id NULL이면 "수동 추가", 아니면 wr.memo
|
|
memo_text = manual_label if record[2] is None else (record[3] or "")
|
|
memo_item = QTableWidgetItem(memo_text)
|
|
|
|
self.earned_table.setItem(i, 0, date_item)
|
|
self.earned_table.setItem(i, 1, time_item)
|
|
self.earned_table.setItem(i, 2, memo_item)
|
|
|
|
# 사용 내역 로드 (잔액 조정 제외)
|
|
cursor.execute('''
|
|
SELECT id, date, used_minutes, reason
|
|
FROM overtime_usage
|
|
WHERE COALESCE(reason, '') != '잔액 조정'
|
|
ORDER BY date DESC
|
|
''')
|
|
used_records = cursor.fetchall()
|
|
|
|
self.used_table.setRowCount(len(used_records))
|
|
for i, record in enumerate(used_records):
|
|
# ID를 숨겨진 데이터로 저장
|
|
date_item = QTableWidgetItem(record[1])
|
|
date_item.setTextAlignment(Qt.AlignCenter)
|
|
date_item.setData(Qt.UserRole, record[0]) # ID 저장
|
|
|
|
minutes = record[2]
|
|
hours = minutes // 60
|
|
mins = minutes % 60
|
|
if hours > 0:
|
|
time_str = tr('view.break.duration_fmt', h=hours, m=mins)
|
|
else:
|
|
time_str = tr('view.break.duration_min_only', m=mins)
|
|
time_item = QTableWidgetItem(time_str)
|
|
time_item.setTextAlignment(Qt.AlignCenter)
|
|
time_item.setForeground(QColor(250, 82, 82)) # 사용 = 레드 (#FA5252)
|
|
|
|
reason_item = QTableWidgetItem(record[3] or "")
|
|
|
|
self.used_table.setItem(i, 0, date_item)
|
|
self.used_table.setItem(i, 1, time_item)
|
|
self.used_table.setItem(i, 2, reason_item)
|
|
|
|
conn.close()
|
|
|
|
def show_used_context_menu(self, position):
|
|
"""사용 내역 우클릭 메뉴"""
|
|
# 선택된 행이 있는지 확인
|
|
selected_rows = self.used_table.selectionModel().selectedRows()
|
|
if not selected_rows:
|
|
return
|
|
|
|
# 컨텍스트 메뉴 생성
|
|
menu = QMenu(self)
|
|
delete_action = QAction(tr('view.overtime.menu_delete'), self)
|
|
delete_action.triggered.connect(self.delete_used_record)
|
|
menu.addAction(delete_action)
|
|
|
|
# 메뉴 표시
|
|
menu.exec_(self.used_table.viewport().mapToGlobal(position))
|
|
|
|
def delete_used_record(self):
|
|
"""사용 기록 삭제"""
|
|
# 선택된 행 가져오기
|
|
selected_rows = self.used_table.selectionModel().selectedRows()
|
|
if not selected_rows:
|
|
return
|
|
|
|
row = selected_rows[0].row()
|
|
date_item = self.used_table.item(row, 0)
|
|
time_item = self.used_table.item(row, 1)
|
|
reason_item = self.used_table.item(row, 2)
|
|
|
|
# ID 가져오기
|
|
usage_id = date_item.data(Qt.UserRole)
|
|
|
|
# 확인 메시지
|
|
reply = QMessageBox.question(
|
|
self,
|
|
tr('msg.confirm_delete.title'),
|
|
tr('view.overtime.delete_confirm_body',
|
|
date=date_item.text(), time=time_item.text(),
|
|
reason=reason_item.text()),
|
|
QMessageBox.Yes | QMessageBox.No
|
|
)
|
|
|
|
if reply == QMessageBox.Yes:
|
|
# 데이터베이스에서 삭제
|
|
conn = self.db.get_connection()
|
|
cursor = conn.cursor()
|
|
cursor.execute('DELETE FROM overtime_usage WHERE id = ?', (usage_id,))
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
# 화면 새로고침
|
|
self.load_data()
|
|
|
|
# 부모 윈도우의 잔액도 업데이트
|
|
if self.parent() and hasattr(self.parent(), 'update_overtime_balance'):
|
|
self.parent().update_overtime_balance()
|
|
|
|
def show_earned_context_menu(self, position):
|
|
"""적립 내역 우클릭 메뉴 (삭제)."""
|
|
selected_rows = self.earned_table.selectionModel().selectedRows()
|
|
if not selected_rows:
|
|
return
|
|
menu = QMenu(self)
|
|
delete_action = QAction(tr('view.overtime.menu_delete'), self)
|
|
delete_action.triggered.connect(self.delete_earned_record)
|
|
menu.addAction(delete_action)
|
|
menu.exec_(self.earned_table.viewport().mapToGlobal(position))
|
|
|
|
def delete_earned_record(self):
|
|
"""적립 기록 삭제 (overtime_bank에서 제거 → 잔액 즉시 감소)."""
|
|
selected_rows = self.earned_table.selectionModel().selectedRows()
|
|
if not selected_rows:
|
|
return
|
|
row = selected_rows[0].row()
|
|
date_item = self.earned_table.item(row, 0)
|
|
time_item = self.earned_table.item(row, 1)
|
|
|
|
# 행에 저장된 overtime_bank.id
|
|
bank_id = date_item.data(Qt.UserRole)
|
|
if bank_id is None:
|
|
return
|
|
|
|
reply = QMessageBox.question(
|
|
self,
|
|
tr('msg.confirm_delete.title'),
|
|
tr('view.overtime.delete_earned_confirm_body',
|
|
date=date_item.text(), time=time_item.text()),
|
|
QMessageBox.Yes | QMessageBox.No
|
|
)
|
|
|
|
if reply == QMessageBox.Yes:
|
|
self.db.delete_overtime_earned(bank_id)
|
|
self.load_data()
|
|
# 부모 윈도우 잔액 업데이트
|
|
if self.parent() and hasattr(self.parent(), 'update_overtime_balance'):
|
|
self.parent().update_overtime_balance()
|
|
|
|
def add_earned_record(self):
|
|
"""수동 적립 추가"""
|
|
dialog = AddOvertimeEarnedDialog(self, self.db)
|
|
if dialog.exec_() == QDialog.Accepted:
|
|
self.load_data()
|
|
# 부모 윈도우 업데이트
|
|
if self.parent() and hasattr(self.parent(), 'update_overtime_balance'):
|
|
self.parent().update_overtime_balance()
|
|
|
|
def add_used_record(self):
|
|
"""수동 사용 추가"""
|
|
dialog = AddOvertimeUsedDialog(self, self.db)
|
|
if dialog.exec_() == QDialog.Accepted:
|
|
self.load_data()
|
|
# 부모 윈도우 업데이트
|
|
if self.parent() and hasattr(self.parent(), 'update_overtime_balance'):
|
|
self.parent().update_overtime_balance()
|
|
|
|
|
|
class AddOvertimeEarnedDialog(QDialog):
|
|
"""추가근무 수동 적립 다이얼로그"""
|
|
|
|
def __init__(self, parent=None, db=None):
|
|
super().__init__(parent)
|
|
self.db = db
|
|
self.init_ui()
|
|
apply_dark_titlebar(self)
|
|
|
|
def init_ui(self):
|
|
"""UI 초기화"""
|
|
self.setWindowTitle(tr('view.overtime.manual_earned_title'))
|
|
self.setModal(True)
|
|
self.setMinimumWidth(360)
|
|
|
|
layout = QVBoxLayout()
|
|
layout.setSpacing(8)
|
|
layout.setContentsMargins(12, 10, 12, 10)
|
|
|
|
# 제목
|
|
title = QLabel(tr('view.overtime.manual_earned_title'))
|
|
title.setObjectName("dialog_subtitle")
|
|
title.setAlignment(Qt.AlignCenter)
|
|
layout.addWidget(title)
|
|
|
|
# 날짜
|
|
date_layout = QHBoxLayout()
|
|
date_label = QLabel(tr('view.overtime.field_date'))
|
|
date_label.setObjectName("field_label")
|
|
date_label.setFixedWidth(60)
|
|
self.date_edit = QDateEdit()
|
|
self.date_edit.setDate(QDate.currentDate())
|
|
self.date_edit.setCalendarPopup(True)
|
|
date_layout.addWidget(date_label)
|
|
date_layout.addWidget(self.date_edit)
|
|
layout.addLayout(date_layout)
|
|
|
|
# 시간 (30분 단위)
|
|
time_layout = QHBoxLayout()
|
|
time_label = QLabel(tr('view.overtime.field_time'))
|
|
time_label.setObjectName("field_label")
|
|
time_label.setFixedWidth(60)
|
|
self.hour_spin = QSpinBox()
|
|
self.hour_spin.setRange(0, 23)
|
|
self.hour_spin.setSuffix(tr('view.overtime.unit_hour_suffix'))
|
|
self.minute_combo = QComboBox()
|
|
self.minute_combo.addItems([tr('view.overtime.minute_0'),
|
|
tr('view.overtime.minute_30')])
|
|
time_layout.addWidget(time_label)
|
|
time_layout.addWidget(self.hour_spin)
|
|
time_layout.addWidget(self.minute_combo)
|
|
layout.addLayout(time_layout)
|
|
|
|
# 메모
|
|
memo_layout = QHBoxLayout()
|
|
memo_label = QLabel(tr('view.overtime.field_memo'))
|
|
memo_label.setObjectName("field_label")
|
|
memo_label.setFixedWidth(60)
|
|
self.memo_edit = QLineEdit()
|
|
self.memo_edit.setPlaceholderText(tr('view.overtime.placeholder_memo'))
|
|
memo_layout.addWidget(memo_label)
|
|
memo_layout.addWidget(self.memo_edit)
|
|
layout.addLayout(memo_layout)
|
|
|
|
# 버튼
|
|
button_layout = QHBoxLayout()
|
|
save_button = QPushButton(tr('btn.save'))
|
|
save_button.setObjectName("btn_primary")
|
|
save_button.clicked.connect(self.save)
|
|
cancel_button = QPushButton(tr('btn.cancel'))
|
|
cancel_button.clicked.connect(self.reject)
|
|
button_layout.addWidget(save_button)
|
|
button_layout.addWidget(cancel_button)
|
|
layout.addLayout(button_layout)
|
|
|
|
self.setLayout(layout)
|
|
|
|
def save(self):
|
|
"""저장"""
|
|
# 시간 계산 (30분 단위)
|
|
hours = self.hour_spin.value()
|
|
minutes = 0 if self.minute_combo.currentIndex() == 0 else 30
|
|
total_minutes = hours * 60 + minutes
|
|
|
|
if total_minutes == 0:
|
|
QMessageBox.warning(self, tr('msg.input_error.title'),
|
|
tr('view.overtime.zero_add_error'))
|
|
return
|
|
|
|
date = self.date_edit.date().toString("yyyy-MM-dd")
|
|
memo = self.memo_edit.text().strip()
|
|
|
|
# DB에 저장 (work_record_id는 NULL)
|
|
self.db.add_overtime_earned(None, total_minutes, date)
|
|
|
|
# 메모가 있으면 work_records 업데이트 (기존 메모 보존)
|
|
if memo:
|
|
record = self.db.get_work_record(date)
|
|
if record:
|
|
existing_memo = record.get('memo', '') or ''
|
|
# 기존 메모가 있으면 줄바꿈 후 추가
|
|
if existing_memo:
|
|
new_memo = f"{existing_memo}\n[수동 적립] {memo}"
|
|
else:
|
|
new_memo = f"[수동 적립] {memo}"
|
|
|
|
conn = self.db.get_connection()
|
|
cursor = conn.cursor()
|
|
cursor.execute('''
|
|
UPDATE work_records
|
|
SET memo = ?
|
|
WHERE date = ?
|
|
''', (new_memo, date))
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
QMessageBox.information(
|
|
self,
|
|
tr('msg.save_success.title'),
|
|
tr('view.overtime.saved_earned', h=hours, m=minutes)
|
|
)
|
|
self.accept()
|
|
|
|
|
|
class AddOvertimeUsedDialog(QDialog):
|
|
"""추가근무 수동 사용 다이얼로그"""
|
|
|
|
def __init__(self, parent=None, db=None):
|
|
super().__init__(parent)
|
|
self.db = db
|
|
self.init_ui()
|
|
apply_dark_titlebar(self)
|
|
|
|
def init_ui(self):
|
|
"""UI 초기화"""
|
|
self.setWindowTitle(tr('view.overtime.manual_used_title'))
|
|
self.setModal(True)
|
|
self.setMinimumWidth(360)
|
|
|
|
layout = QVBoxLayout()
|
|
layout.setSpacing(8)
|
|
layout.setContentsMargins(12, 10, 12, 10)
|
|
|
|
# 제목 + 잔액 한 줄
|
|
header_layout = QHBoxLayout()
|
|
title = QLabel(tr('view.overtime.manual_used_title'))
|
|
title.setObjectName("dialog_subtitle")
|
|
header_layout.addWidget(title)
|
|
header_layout.addStretch()
|
|
balance = self.db.get_total_overtime_balance()
|
|
hours = balance // 60
|
|
minutes = balance % 60
|
|
if hours > 0:
|
|
balance_text = tr('view.break.duration_fmt', h=hours, m=minutes)
|
|
else:
|
|
balance_text = tr('view.break.duration_min_only', m=minutes)
|
|
balance_label = QLabel(f"{tr('view.overtime.balance_zero').split(':')[0]}: {balance_text}")
|
|
balance_label.setObjectName("badge_balance")
|
|
header_layout.addWidget(balance_label)
|
|
layout.addLayout(header_layout)
|
|
|
|
# 날짜
|
|
date_layout = QHBoxLayout()
|
|
date_label = QLabel(tr('view.overtime.field_date'))
|
|
date_label.setObjectName("field_label")
|
|
date_label.setFixedWidth(60)
|
|
self.date_edit = QDateEdit()
|
|
self.date_edit.setDate(QDate.currentDate())
|
|
self.date_edit.setCalendarPopup(True)
|
|
date_layout.addWidget(date_label)
|
|
date_layout.addWidget(self.date_edit)
|
|
layout.addLayout(date_layout)
|
|
|
|
# 시간 (30분 단위)
|
|
time_layout = QHBoxLayout()
|
|
time_label = QLabel(tr('view.overtime.field_time'))
|
|
time_label.setObjectName("field_label")
|
|
time_label.setFixedWidth(60)
|
|
self.hour_spin = QSpinBox()
|
|
self.hour_spin.setRange(0, 23)
|
|
self.hour_spin.setSuffix(tr('view.overtime.unit_hour_suffix'))
|
|
self.minute_combo = QComboBox()
|
|
self.minute_combo.addItems([tr('view.overtime.minute_0'),
|
|
tr('view.overtime.minute_30')])
|
|
time_layout.addWidget(time_label)
|
|
time_layout.addWidget(self.hour_spin)
|
|
time_layout.addWidget(self.minute_combo)
|
|
layout.addLayout(time_layout)
|
|
|
|
# 사유
|
|
reason_layout = QHBoxLayout()
|
|
reason_label = QLabel(tr('view.overtime.field_reason'))
|
|
reason_label.setObjectName("field_label")
|
|
reason_label.setFixedWidth(60)
|
|
self.reason_edit = QLineEdit()
|
|
self.reason_edit.setPlaceholderText(tr('view.overtime.placeholder_reason'))
|
|
reason_layout.addWidget(reason_label)
|
|
reason_layout.addWidget(self.reason_edit)
|
|
layout.addLayout(reason_layout)
|
|
|
|
# 버튼
|
|
button_layout = QHBoxLayout()
|
|
save_button = QPushButton(tr('btn.save'))
|
|
save_button.setObjectName("btn_primary")
|
|
save_button.clicked.connect(self.save)
|
|
cancel_button = QPushButton(tr('btn.cancel'))
|
|
cancel_button.clicked.connect(self.reject)
|
|
button_layout.addWidget(save_button)
|
|
button_layout.addWidget(cancel_button)
|
|
layout.addLayout(button_layout)
|
|
|
|
self.setLayout(layout)
|
|
|
|
def save(self):
|
|
"""저장"""
|
|
# 시간 계산 (30분 단위)
|
|
hours = self.hour_spin.value()
|
|
minutes = 0 if self.minute_combo.currentIndex() == 0 else 30
|
|
total_minutes = hours * 60 + minutes
|
|
|
|
if total_minutes == 0:
|
|
QMessageBox.warning(self, tr('msg.input_error.title'),
|
|
tr('view.overtime.zero_use_error'))
|
|
return
|
|
|
|
# 잔액 확인
|
|
balance = self.db.get_total_overtime_balance()
|
|
if total_minutes > balance:
|
|
QMessageBox.warning(
|
|
self,
|
|
tr('view.overtime.balance_short_title'),
|
|
tr('view.overtime.balance_short_body',
|
|
req_h=hours, req_m=minutes,
|
|
bal_h=balance // 60, bal_m=balance % 60)
|
|
)
|
|
return
|
|
|
|
date = self.date_edit.date().toString("yyyy-MM-dd")
|
|
reason = self.reason_edit.text().strip() or tr('msg.manual_added')
|
|
|
|
# DB에 저장
|
|
self.db.add_overtime_usage(None, total_minutes, date, reason)
|
|
|
|
QMessageBox.information(
|
|
self,
|
|
tr('msg.save_success.title'),
|
|
tr('view.overtime.saved_used', h=hours, m=minutes)
|
|
)
|
|
self.accept()
|
|
|
|
|
|
# 테스트 코드
|
|
if __name__ == "__main__":
|
|
from PyQt5.QtWidgets import QApplication
|
|
|
|
app = QApplication(sys.argv)
|
|
dialog = OvertimeView()
|
|
dialog.exec_()
|