""" 연차 상세 내역 뷰 """ 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) cal_button = QPushButton("📅 캘린더 보기") cal_button.clicked.connect(self._show_calendar) button_layout.addWidget(cal_button) close_button = QPushButton("닫기") close_button.clicked.connect(self.close) button_layout.addWidget(close_button) layout.addLayout(button_layout) self.setLayout(layout) def _show_calendar(self): from ui.leave_calendar_view import LeaveCalendarView dlg = LeaveCalendarView(self, self.db) dlg.exec_() 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_()