""" 연차 상세 내역 뷰 """ from PyQt5.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QTableWidget, QTableWidgetItem, QHeaderView, QGroupBox, QMessageBox, QInputDialog, QLineEdit, QDateEdit, QComboBox, QDoubleSpinBox, QMenu, QAction) from PyQt5.QtCore import Qt, QDate from PyQt5.QtGui import QColor from datetime import datetime 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 apply_dark_titlebar class LeaveView(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.leave_view')) self.setModal(True) self.setMinimumSize(700, 450) layout = QVBoxLayout() layout.setSpacing(6) layout.setContentsMargins(12, 10, 12, 10) # 제목 + 잔액 + 설정 한 줄 header_layout = QHBoxLayout() title = QLabel("연차 관리") title.setObjectName("dialog_title") header_layout.addWidget(title) header_layout.addStretch() self.balance_label = QLabel("잔여: 0일") self.balance_label.setObjectName("badge_leave") header_layout.addWidget(self.balance_label) set_balance_button = QPushButton("잔여 설정") set_balance_button.clicked.connect(self.set_balance) header_layout.addWidget(set_balance_button) layout.addLayout(header_layout) # 사용 내역 used_group = QGroupBox("📤 사용 내역") used_layout = QVBoxLayout() used_layout.setSpacing(4) used_layout.setContentsMargins(8, 20, 8, 6) self.used_table = QTableWidget() self.used_table.setColumnCount(4) self.used_table.setHorizontalHeaderLabels(["날짜", "구분", "사용", "사유"]) self.used_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents) self.used_table.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeToContents) self.used_table.horizontalHeader().setSectionResizeMode(2, QHeaderView.ResizeToContents) self.used_table.horizontalHeader().setSectionResizeMode(3, 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_context_menu) used_layout.addWidget(self.used_table) used_group.setLayout(used_layout) layout.addWidget(used_group) # 버튼들 button_layout = QHBoxLayout() add_leave_button = QPushButton("➕ 연차 사용 추가") add_leave_button.clicked.connect(self.add_leave_record) button_layout.addWidget(add_leave_button) close_button = QPushButton("닫기") close_button.clicked.connect(self.close) button_layout.addWidget(close_button) layout.addLayout(button_layout) self.setLayout(layout) def load_data(self): """데이터 로드""" # 잔액 업데이트 balance = self.db.get_leave_balance() hours = balance * 8 self.balance_label.setText(f"잔여: {balance}일 (총 {hours}시간)") # 사용 내역 로드 (잔액 조정 제외) records = self.db.get_leave_records(exclude_bulk=True) self.used_table.setRowCount(len(records)) for i, record in enumerate(records): date_item = QTableWidgetItem(record['date']) date_item.setTextAlignment(Qt.AlignCenter) date_item.setData(Qt.UserRole, record['id']) type_item = QTableWidgetItem(record['leave_type']) type_item.setTextAlignment(Qt.AlignCenter) days = record['days'] hours = days * 8 if days == 1.0: days_str = "1일" elif days == 0.5: days_str = "0.5일 (4시간)" elif hours < 8: days_str = f"{days}일 ({hours}시간)" else: days_str = f"{days}일" days_item = QTableWidgetItem(days_str) days_item.setTextAlignment(Qt.AlignCenter) days_item.setForeground(QColor(231, 76, 60)) # 빨간색 memo_item = QTableWidgetItem(record['memo'] or "") self.used_table.setItem(i, 0, date_item) self.used_table.setItem(i, 1, type_item) self.used_table.setItem(i, 2, days_item) self.used_table.setItem(i, 3, memo_item) def show_context_menu(self, position): """사용 내역 우클릭 메뉴""" selected_rows = self.used_table.selectionModel().selectedRows() if not selected_rows: return menu = QMenu(self) delete_action = QAction("삭제", self) delete_action.triggered.connect(self.delete_leave_record) menu.addAction(delete_action) menu.exec_(self.used_table.viewport().mapToGlobal(position)) def delete_leave_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) type_item = self.used_table.item(row, 1) days_item = self.used_table.item(row, 2) leave_id = date_item.data(Qt.UserRole) reply = QMessageBox.question( self, "삭제 확인", f"다음 연차 사용 기록을 삭제하시겠습니까?\n\n" f"날짜: {date_item.text()}\n" f"구분: {type_item.text()}\n" f"사용: {days_item.text()}", QMessageBox.Yes | QMessageBox.No ) if reply == QMessageBox.Yes: self.db.delete_leave_record(leave_id) self.load_data() def set_balance(self): """연차 개수 설정 (시간 단위)""" current_balance = self.db.get_leave_balance() current_hours = current_balance * 8 hours, ok = QInputDialog.getDouble( self, "연차 시간 설정", "연차 잔여 시간을 입력하세요 (0.5시간 단위):\n" "예) 8시간 = 1일, 4시간 = 0.5일(반차), 2시간 = 0.25일, 0.5시간 = 30분", current_hours, 0.0, 999.0, 1 # 소수점 첫째자리까지 (0.5 단위) ) if ok: # 0.5시간 단위로 반올림 hours = round(hours * 2) / 2 # 시간을 일수로 변환 days = hours / 8.0 self.db.set_leave_balance(days) QMessageBox.information( self, "설정 완료", f"연차 잔여 개수가 {days}일 ({hours}시간)로 설정되었습니다." ) self.load_data() def add_leave_record(self): """연차 사용 기록 추가 다이얼로그""" dialog = AddLeaveDialog(self, self.db) if dialog.exec_() == QDialog.Accepted: self.load_data() class AddLeaveDialog(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("연차 사용 기록 추가") self.setModal(True) self.setMinimumWidth(360) layout = QVBoxLayout() layout.setSpacing(8) layout.setContentsMargins(12, 10, 12, 10) # 제목 title = QLabel("연차 사용 기록 추가") title.setObjectName("dialog_subtitle") title.setAlignment(Qt.AlignCenter) layout.addWidget(title) # 날짜 + 구분 한 줄 row1 = QHBoxLayout() date_label = QLabel("날짜:") date_label.setObjectName("field_label") date_label.setFixedWidth(40) self.date_edit = QDateEdit() self.date_edit.setDate(QDate.currentDate()) self.date_edit.setCalendarPopup(True) row1.addWidget(date_label) row1.addWidget(self.date_edit) row1.addSpacing(8) type_label = QLabel("구분:") type_label.setObjectName("field_label") type_label.setFixedWidth(40) self.type_combo = QComboBox() self.type_combo.addItem("연차", "annual") self.type_combo.addItem("반차", "half") self.type_combo.addItem("반반차", "quarter") self.type_combo.addItem("시간", "hourly") self.type_combo.currentIndexChanged.connect(self.on_type_changed) row1.addWidget(type_label) row1.addWidget(self.type_combo) layout.addLayout(row1) # 사용 시간 (시간 연차용) hours_layout = QHBoxLayout() hours_label = QLabel("시간:") hours_label.setObjectName("field_label") hours_label.setFixedWidth(40) self.hours_spin = QDoubleSpinBox() self.hours_spin.setRange(0.5, 8.0) self.hours_spin.setSingleStep(0.5) self.hours_spin.setValue(1.0) self.hours_spin.setSuffix(" 시간") self.hours_spin.setEnabled(False) hours_layout.addWidget(hours_label) hours_layout.addWidget(self.hours_spin) layout.addLayout(hours_layout) # 사유 memo_layout = QHBoxLayout() memo_label = QLabel("사유:") memo_label.setObjectName("field_label") memo_label.setFixedWidth(40) self.memo_input = QLineEdit() self.memo_input.setPlaceholderText("예) 개인 사유, 병원 방문 등") memo_layout.addWidget(memo_label) memo_layout.addWidget(self.memo_input) layout.addLayout(memo_layout) # 안내 info_label = QLabel("※ 잔여 연차가 자동 차감됩니다.") info_label.setObjectName("note_text") layout.addWidget(info_label) # 버튼 button_layout = QHBoxLayout() save_button = QPushButton("저장") save_button.setObjectName("btn_primary") save_button.clicked.connect(self.save_record) button_layout.addWidget(save_button) cancel_button = QPushButton("취소") cancel_button.clicked.connect(self.reject) button_layout.addWidget(cancel_button) layout.addLayout(button_layout) self.setLayout(layout) def on_type_changed(self, index): """연차 유형 변경 시""" leave_type = self.type_combo.currentData() # 시간 연차일 때만 시간 입력 활성화 self.hours_spin.setEnabled(leave_type == "hourly") def save_record(self): """기록 저장""" date = self.date_edit.date().toString("yyyy-MM-dd") leave_type = self.type_combo.currentData() leave_type_name = self.type_combo.currentText() memo = self.memo_input.text().strip() # 사용 일수 계산 if leave_type == "annual": days = 1.0 elif leave_type == "half": days = 0.5 elif leave_type == "quarter": days = 0.25 elif leave_type == "hourly": hours = self.hours_spin.value() days = hours / 8.0 else: days = 1.0 # 잔여 연차 확인 current_balance = self.db.get_leave_balance() if current_balance < days: QMessageBox.warning( self, "잔여 연차 부족", f"잔여 연차가 부족합니다.\n현재 잔여: {current_balance}일\n사용 요청: {days}일" ) return # 확인 메시지 hours = days * 8 reply = QMessageBox.question( self, "연차 사용 기록 추가", f"날짜: {date}\n" f"구분: {leave_type_name}\n" f"사용: {days}일 ({hours}시간)\n" f"사유: {memo if memo else '(없음)'}\n\n" f"이 기록을 추가하시겠습니까?", QMessageBox.Yes | QMessageBox.No ) if reply == QMessageBox.Yes: try: # 연차 사용 기록 추가 (파라미터 순서: days, date, leave_type, memo) self.db.use_leave(days, date, leave_type_name, memo) QMessageBox.information( self, "추가 완료", f"{days}일 ({hours}시간)의 연차 사용이 기록되었습니다." ) self.accept() except Exception as e: QMessageBox.critical( self, "오류", f"연차 기록 추가 중 오류가 발생했습니다:\n{str(e)}" ) # 테스트 코드 if __name__ == "__main__": from PyQt5.QtWidgets import QApplication app = QApplication(sys.argv) dialog = LeaveView() dialog.exec_()