Clock_out_Time_Calculator/ui/recurring_leave_dialog.py
KINDNICK 5fb8655a47 v2.11.0: UI 전면 다크 리디자인 + 라인 아이콘 + 적립 가드/삭제
- 모던 다크 미니멀 테마(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>
2026-06-04 18:21:54 +09:00

200 lines
7.1 KiB
Python

"""
반복 연차 등록/관리 다이얼로그.
지원: 매주/격주 요일, 매월 N일.
"""
from __future__ import annotations
from datetime import date
from PyQt5.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QLabel,
QPushButton, QComboBox, QDateEdit, QSpinBox,
QDoubleSpinBox, QLineEdit, QGroupBox,
QListWidget, QListWidgetItem, QMessageBox,
QCheckBox, QButtonGroup, QRadioButton)
from PyQt5.QtCore import QDate, Qt
from core.recurring_leaves import describe_pattern
from ui.styles import apply_dark_titlebar
_KO_WEEKDAYS = [('', 'mon'), ('', 'tue'), ('', 'wed'),
('', 'thu'), ('', 'fri'), ('', 'sat'), ('', 'sun')]
class RecurringLeaveDialog(QDialog):
"""반복 연차 패턴 추가/삭제."""
def __init__(self, parent=None, db=None):
super().__init__(parent)
self.db = db
self.setWindowTitle("반복 연차 관리")
self.setMinimumSize(540, 480)
self._build_ui()
self._reload_list()
apply_dark_titlebar(self)
def _build_ui(self):
layout = QVBoxLayout()
# 기존 패턴 목록
list_group = QGroupBox("등록된 반복 패턴")
lg = QVBoxLayout()
self.list_widget = QListWidget()
self.list_widget.setMinimumHeight(160)
lg.addWidget(self.list_widget)
del_btn = QPushButton("선택 삭제")
del_btn.clicked.connect(self._delete_selected)
lg.addWidget(del_btn)
list_group.setLayout(lg)
layout.addWidget(list_group)
# 신규 등록
add_group = QGroupBox("신규 패턴 추가")
ag = QVBoxLayout()
# 패턴 종류
kind_row = QHBoxLayout()
kind_row.addWidget(QLabel("주기:"))
self.kind_group = QButtonGroup(self)
self.rb_weekly = QRadioButton("매주")
self.rb_weekly.setChecked(True)
self.rb_biweekly = QRadioButton("격주")
self.rb_monthly = QRadioButton("매월 N일")
for rb in (self.rb_weekly, self.rb_biweekly, self.rb_monthly):
self.kind_group.addButton(rb)
kind_row.addWidget(rb)
kind_row.addStretch()
ag.addLayout(kind_row)
# 요일 체크박스 (weekly/biweekly)
wd_row = QHBoxLayout()
wd_row.addWidget(QLabel("요일:"))
self.weekday_checks = []
for ko, en in _KO_WEEKDAYS:
cb = QCheckBox(ko)
self.weekday_checks.append((cb, en))
wd_row.addWidget(cb)
wd_row.addStretch()
ag.addLayout(wd_row)
# 매월 N일
month_row = QHBoxLayout()
month_row.addWidget(QLabel("매월:"))
self.day_of_month = QSpinBox()
self.day_of_month.setRange(1, 31)
self.day_of_month.setValue(15)
self.day_of_month.setSuffix("")
month_row.addWidget(self.day_of_month)
month_row.addStretch()
ag.addLayout(month_row)
# 차감 일수
days_row = QHBoxLayout()
days_row.addWidget(QLabel("차감:"))
self.days_combo = QComboBox()
self.days_combo.addItem("1.0일 (종일)", 1.0)
self.days_combo.addItem("0.5일 (반차)", 0.5)
self.days_combo.addItem("0.25일 (반반차)", 0.25)
days_row.addWidget(self.days_combo)
days_row.addStretch()
ag.addLayout(days_row)
# 시작/종료 날짜
date_row = QHBoxLayout()
date_row.addWidget(QLabel("시작:"))
self.start_edit = QDateEdit()
self.start_edit.setDate(QDate.currentDate())
self.start_edit.setCalendarPopup(True)
date_row.addWidget(self.start_edit)
date_row.addWidget(QLabel("종료:"))
self.end_edit = QDateEdit()
self.end_edit.setDate(QDate.currentDate().addMonths(6))
self.end_edit.setCalendarPopup(True)
date_row.addWidget(self.end_edit)
self.no_end_check = QCheckBox("종료 없음 (무기한)")
self.no_end_check.toggled.connect(
lambda v: self.end_edit.setEnabled(not v)
)
date_row.addWidget(self.no_end_check)
date_row.addStretch()
ag.addLayout(date_row)
# 메모
memo_row = QHBoxLayout()
memo_row.addWidget(QLabel("메모:"))
self.memo_edit = QLineEdit()
self.memo_edit.setPlaceholderText("예: 육아 단축근무")
memo_row.addWidget(self.memo_edit)
ag.addLayout(memo_row)
# 추가 버튼
add_btn = QPushButton("추가")
add_btn.setObjectName("btn_primary")
add_btn.clicked.connect(self._save)
ag.addWidget(add_btn)
add_group.setLayout(ag)
layout.addWidget(add_group)
# 닫기
close_btn = QPushButton("닫기")
close_btn.clicked.connect(self.close)
layout.addWidget(close_btn)
self.setLayout(layout)
def _reload_list(self):
self.list_widget.clear()
for r in self.db.get_recurring_leaves():
desc = describe_pattern(r['pattern'])
end = r.get('end_date') or '무기한'
text = (f"[{r['id']}] {desc} · {r['days']}일 ({r['leave_type']}) "
f"· {r['start_date']} ~ {end}")
if r.get('memo'):
text += f"{r['memo']}"
item = QListWidgetItem(text)
item.setData(Qt.UserRole, r['id'])
self.list_widget.addItem(item)
def _delete_selected(self):
item = self.list_widget.currentItem()
if not item:
return
rec_id = item.data(Qt.UserRole)
reply = QMessageBox.question(
self, "삭제 확인",
f"이 반복 패턴을 삭제하시겠습니까?\n\n{item.text()}",
QMessageBox.Yes | QMessageBox.No,
)
if reply == QMessageBox.Yes:
self.db.delete_recurring_leave(rec_id)
self._reload_list()
def _build_pattern(self) -> str | None:
if self.rb_monthly.isChecked():
return f"monthly:{self.day_of_month.value()}"
# weekly/biweekly
chosen = [en for cb, en in self.weekday_checks if cb.isChecked()]
if not chosen:
return None
prefix = 'weekly' if self.rb_weekly.isChecked() else 'biweekly'
return f"{prefix}:" + ",".join(chosen)
def _save(self):
pattern = self._build_pattern()
if not pattern:
QMessageBox.warning(self, "입력 오류", "최소 한 개 요일을 선택하세요.")
return
days = self.days_combo.currentData()
leave_type = self.days_combo.currentText().split(' ')[1].strip('()')
start = self.start_edit.date().toString('yyyy-MM-dd')
end = None if self.no_end_check.isChecked() else self.end_edit.date().toString('yyyy-MM-dd')
memo = self.memo_edit.text().strip()
self.db.add_recurring_leave(pattern, leave_type, days, start, end, memo)
QMessageBox.information(self, "추가 완료",
f"반복 패턴이 등록되었습니다.\n{describe_pattern(pattern)}")
self.memo_edit.clear()
self._reload_list()