v2.13.0: 익일 퇴근 처리 수정 + 날짜 지정 일일보고 + 연차 직접입력

Fixed:
- clock_out()이 datetime.now().date() 행에 퇴근을 써서 자정 넘긴 퇴근이
  UPDATE 0건으로 유실되던 버그. 출근일(근무일) 기준으로 수정.
  cancel_clock_out() 및 주말/공휴일 판정·외출 집계도 동일 기준으로 통일.
- 캘린더 시간 수정 / 과거기록 추가가 퇴근<=출근을 반려해 익일 퇴근을
  저장할 수 없던 문제. 익일로 해석하고 동시각만 반려.
  총 근무시간·연장근무 음수 계산도 수정.

Added:
- generate_daily_report(target_date) 날짜 파라미터화 + 캘린더 '보고서 복사'
  버튼. 기본값은 진행 중인 근무일(경계시간 전이면 출근일).
- 연차 직접입력 버튼(메인 창) — 미연결 상태였던 use_custom_leave()를
  기존 AddLeaveDialog 재사용으로 연결.
- resolve_work_datetimes() / is_overnight() 공용 헬퍼 + 유닛 테스트 6개.

Changed:
- 연차 빠른 사용에서 날짜·메모 팝업 제거 (연장근무와 동일하게 확인 1회).
- get_today_overtime_usage() / get_total_break_minutes_today()에 선택적
  date_str 파라미터 추가.

테스트: 유닛 200 / 통합 53 / GUI 8 / i18n 5 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
KINDNICK
2026-08-12 14:06:18 +09:00
co-authored by Claude Opus 5
parent d8c6a9d784
commit f28f0619a4
10 changed files with 324 additions and 136 deletions
+57 -12
View File
@@ -85,6 +85,11 @@ class CalendarView(QDialog):
self.edit_time_button.clicked.connect(self.edit_work_time)
button_layout.addWidget(self.edit_time_button)
self.copy_report_button = QPushButton(tr('cal.copy_report'))
self.copy_report_button.setEnabled(False)
self.copy_report_button.clicked.connect(self.copy_daily_report)
button_layout.addWidget(self.copy_report_button)
self.delete_record_button = QPushButton(tr('cal.delete_record'))
self.delete_record_button.setObjectName("btn_danger")
self.delete_record_button.setEnabled(False)
@@ -196,10 +201,9 @@ class CalendarView(QDialog):
try:
wid = self.db.add_work_record(date_str, data['clock_in'], is_manual=True)
if data.get('clock_out'):
# 총 시간/연장근무 계산
from datetime import datetime as _dt
ci = _dt.strptime(f"{date_str} {data['clock_in']}", '%Y-%m-%d %H:%M:%S')
co = _dt.strptime(f"{date_str} {data['clock_out']}", '%Y-%m-%d %H:%M:%S')
# 총 시간/연장근무 계산 (익일 퇴근이면 퇴근에 +1일)
from core.time_calculator import resolve_work_datetimes
ci, co = resolve_work_datetimes(date_str, data['clock_in'], data['clock_out'])
from core.time_calculator import TimeCalculator
wm = self.db.get_work_minutes()
lunch = self.db.get_setting_int('lunch_duration_minutes', 60)
@@ -275,7 +279,11 @@ class CalendarView(QDialog):
detail += tr('cal.detail_clock_in', time=record['clock_in']) + '\n'
if record.get('clock_out'):
detail += tr('cal.detail_clock_out', time=record['clock_out']) + '\n'
from core.time_calculator import is_overnight
out_str = record['clock_out']
if is_overnight(record['clock_in'], out_str):
out_str = tr('label.next_day_time', time=out_str)
detail += tr('cal.detail_clock_out', time=out_str) + '\n'
detail += tr('cal.detail_total_hours', hours=record.get('total_hours', 0)) + '\n'
if record.get('lunch_break'):
@@ -302,6 +310,7 @@ class CalendarView(QDialog):
self.detail_text.setText(detail)
self.edit_time_button.setEnabled(True)
self.delete_record_button.setEnabled(True)
self.copy_report_button.setEnabled(True)
# 메모 필드 업데이트
self.memo_edit.setPlainText(record.get('memo', ''))
@@ -310,9 +319,22 @@ class CalendarView(QDialog):
self.detail_text.setText(tr('cal.detail_date_fmt', year=selected_date.year, month=selected_date.month, day=selected_date.day) + '\n\n' + tr('cal.no_record'))
self.edit_time_button.setEnabled(False)
self.delete_record_button.setEnabled(False)
self.copy_report_button.setEnabled(False)
self.memo_edit.setPlainText('')
self.save_memo_button.setEnabled(False)
def copy_daily_report(self):
"""선택한 날짜의 일일보고서를 클립보드로 복사.
메인 창의 생성기를 그대로 재사용 — 당일 보고를 놓쳐도 나중에 다시 뽑을 수 있다.
"""
if not self.selected_date_str:
return
main_window = self.parent()
if main_window is None or not hasattr(main_window, 'generate_daily_report'):
return # 단독 실행(__main__) 등 부모가 메인 창이 아닌 경우
main_window.generate_daily_report(self.selected_date_str)
def delete_selected_record(self):
"""선택된 날짜의 출근 기록 삭제"""
if not self.selected_date_str:
@@ -455,6 +477,10 @@ class EditWorkTimeDialog(QDialog):
if self.record.get('clock_out'):
clock_out_time = QTime.fromString(self.record['clock_out'], "HH:mm:ss")
self.clock_out_edit.setTime(clock_out_time)
else:
# 미퇴근 기록: 기본값 00:00은 "익일 자정 퇴근"으로 오인될 수 있으므로
# 출근 시각과 동일하게 두어 save_changes()의 동시각 반려에 걸리게 한다.
self.clock_out_edit.setTime(clock_in_time)
clock_out_layout.addWidget(self.clock_out_edit)
clock_out_plus_btn = QPushButton(tr('cal.btn_plus_30'))
@@ -463,6 +489,15 @@ class EditWorkTimeDialog(QDialog):
clock_out_layout.addWidget(clock_out_plus_btn)
layout.addLayout(clock_out_layout)
# 익일 퇴근 안내 (퇴근 < 출근이면 표시)
self.overnight_note = QLabel('')
self.overnight_note.setObjectName("note_text")
self.overnight_note.setVisible(False)
layout.addWidget(self.overnight_note)
self.clock_in_edit.timeChanged.connect(self._refresh_overnight_note)
self.clock_out_edit.timeChanged.connect(self._refresh_overnight_note)
self._refresh_overnight_note()
# 점심/저녁 체크박스 - 한 줄에
from PyQt5.QtWidgets import QCheckBox
check_layout = QHBoxLayout()
@@ -501,6 +536,18 @@ class EditWorkTimeDialog(QDialog):
new_time = current_time.addSecs(minutes * 60)
time_edit.setTime(new_time)
def _refresh_overnight_note(self):
"""퇴근이 출근보다 이르면 익일 퇴근으로 해석됨을 표시."""
ci = self.clock_in_edit.time()
co = self.clock_out_edit.time()
if co >= ci:
self.overnight_note.setVisible(False)
return
total_min = ci.secsTo(co) // 60 + 24 * 60 # 익일까지
self.overnight_note.setText(tr('edit.overnight_note',
hours=total_min // 60, minutes=total_min % 60))
self.overnight_note.setVisible(True)
def save_changes(self):
"""변경사항 저장"""
clock_in = self.clock_in_edit.time().toString("HH:mm:ss")
@@ -508,8 +555,8 @@ class EditWorkTimeDialog(QDialog):
lunch_break = self.lunch_check.isChecked()
dinner_break = self.dinner_check.isChecked()
# 퇴근 시간이 출근 시간보다 빠른지 확인
if clock_out <= clock_in:
# 퇴근 < 출근은 익일 퇴근(야근)으로 허용. 같은 시각만 반려.
if clock_out == clock_in:
QMessageBox.warning(
self,
tr('cal.time_error_title'),
@@ -519,12 +566,10 @@ class EditWorkTimeDialog(QDialog):
# 근무 시간 계산
from datetime import datetime, timedelta
from core.time_calculator import TimeCalculator
from core.time_calculator import TimeCalculator, resolve_work_datetimes
# 해당 날짜의 datetime 객체 생성
date_obj = datetime.strptime(self.date_str, "%Y-%m-%d").date()
clock_in_dt = datetime.combine(date_obj, datetime.strptime(clock_in, "%H:%M:%S").time())
clock_out_dt = datetime.combine(date_obj, datetime.strptime(clock_out, "%H:%M:%S").time())
# 해당 날짜의 datetime 객체 생성 (익일 퇴근이면 퇴근에 +1일)
clock_in_dt, clock_out_dt = resolve_work_datetimes(self.date_str, clock_in, clock_out)
# 총 근무시간 계산
total_hours = (clock_out_dt - clock_in_dt).total_seconds() / 3600
+93 -101
View File
@@ -25,7 +25,7 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from core.database import Database
from core.event_monitor import EventMonitor
from core.time_calculator import TimeCalculator
from core.time_calculator import TimeCalculator, resolve_work_datetimes
from ui.clock_in_dialog import ClockInDialog
from ui.calendar_view import CalendarView
from ui.stats_view import StatsView
@@ -608,9 +608,11 @@ class MainWindow(QMainWindow):
use_1hour_leave_button = QPushButton(tr('btn.use_1hour'))
use_half_leave_button = QPushButton(tr('btn.half_leave'))
use_full_leave_button = QPushButton(tr('btn.full_leave'))
use_custom_leave_button = QPushButton(tr('btn.custom_input'))
leave_detail_button = QPushButton(tr('btn.detail'))
for btn in [use_30min_leave_button, use_1hour_leave_button, use_half_leave_button, use_full_leave_button, leave_detail_button]:
for btn in [use_30min_leave_button, use_1hour_leave_button, use_half_leave_button,
use_full_leave_button, use_custom_leave_button, leave_detail_button]:
btn.setObjectName("btn_small")
leave_button_layout.addWidget(btn)
@@ -618,6 +620,7 @@ class MainWindow(QMainWindow):
use_1hour_leave_button.clicked.connect(lambda: self.use_leave(1.0/8)) # 0.125일
use_half_leave_button.clicked.connect(lambda: self.use_leave(0.5))
use_full_leave_button.clicked.connect(lambda: self.use_leave(1.0))
use_custom_leave_button.clicked.connect(self.use_custom_leave) # 연장근무와 동일한 상세입력
leave_detail_button.clicked.connect(self.show_leave_detail)
layout.addLayout(leave_button_layout)
@@ -1060,8 +1063,13 @@ class MainWindow(QMainWindow):
# 다이얼로그 종료 후 잔액 업데이트
self.update_overtime_balance()
def use_leave(self, days: float):
"""연차 사용"""
def use_leave(self, days: float, date_str: str = None, memo: str = None):
"""연차 사용.
date_str 생략 시 오늘 날짜로 바로 확인만 받고 처리 — 연장근무 사용
(use_overtime)과 같은 흐름. 날짜·사유를 지정하려면 상세입력
(use_custom_leave → AddLeaveDialog)을 쓴다.
"""
balance = self.db.get_leave_balance()
if balance < days:
@@ -1072,44 +1080,21 @@ class MainWindow(QMainWindow):
)
return
# 사용 날짜 입력
from PyQt5.QtWidgets import QInputDialog, QLineEdit
from datetime import date
today = date.today().isoformat()
date_str, ok = QInputDialog.getText(
self,
tr('msg.leave_use_date.title'),
tr('msg.leave_use_date.body'),
QLineEdit.Normal,
today
)
if not ok or not date_str:
return
# 날짜 형식 검증
try:
datetime.strptime(date_str, "%Y-%m-%d")
except ValueError:
QMessageBox.warning(
self,
tr('msg.input_error.title'),
tr('msg.input_error.date_format')
)
return
# 메모 입력
memo, ok = QInputDialog.getText(
self,
tr('msg.leave_use_reason.title'),
tr('msg.leave_use_reason.body'),
QLineEdit.Normal,
""
)
if not ok:
return
if date_str is None:
date_str = date.today().isoformat()
else:
# 날짜 형식 검증
try:
datetime.strptime(date_str, "%Y-%m-%d")
except ValueError:
QMessageBox.warning(
self,
tr('msg.input_error.title'),
tr('msg.input_error.date_format')
)
return
# 사용 확인
if days == 1.0:
@@ -1197,38 +1182,18 @@ class MainWindow(QMainWindow):
self.use_overtime(minutes)
def use_custom_leave(self):
"""사용자 정의 연차 사용"""
from PyQt5.QtWidgets import QInputDialog
"""연차 상세 입력 — 날짜(캘린더)/유형/시간/사유를 한 화면에서.
balance = self.db.get_leave_balance()
연차 내역 화면의 AddLeaveDialog를 그대로 재사용한다. 그쪽은 잔액 외에
주말·공휴일 차단과 같은 날 1일 초과 중복 검증까지 갖추고 있어,
메인 창에서 바로 띄우는 편이 QInputDialog 연쇄보다 안전하고 간편하다.
"""
from PyQt5.QtWidgets import QDialog
from ui.leave_view import AddLeaveDialog
# 사용할 시간 입력 (시간 단위)
hours, ok = QInputDialog.getDouble(
self,
tr('msg.input.title'),
tr('msg.leave_input.body'),
0.5,
0.5,
80.0,
1
)
if not ok:
return
# 시간을 일수로 변환 (8시간 = 1일)
days = hours / 8.0
if days > balance:
QMessageBox.warning(
self,
tr('msg.leave_short.title'),
tr('msg.leave_short_hours.body', balance=balance, balance_hours=int(balance * 8), days=days, hours=hours)
)
return
# use_leave 메서드 호출
self.use_leave(days)
dialog = AddLeaveDialog(self, self.db)
if dialog.exec_() == QDialog.Accepted:
self.update_leave_balance()
def show_leave_detail(self):
"""연차 상세 내역 보기"""
@@ -1247,12 +1212,32 @@ class MainWindow(QMainWindow):
# 퇴근 완료 -> 퇴근 취소
self.cancel_clock_out()
def current_workday_date(self) -> str:
"""진행 중인(또는 방금 끝난) 근무일 날짜 'YYYY-MM-DD'.
자정~근무일 경계시간(기본 6시) 사이에는 달력상 날짜가 이미 넘어갔어도
출근일이 근무일이다. handle_workday_rollover()가 경계시간 전까지
전날 근무를 유지하는 것과 같은 기준.
"""
if self.is_clocked_in and self.clock_in_time:
return self.clock_in_time.date().isoformat()
now = datetime.now()
boundary = int(self.db.get_setting(WORKDAY_BOUNDARY_HOUR, '6'))
today_str = now.date().isoformat()
# 새벽에 퇴근 직후: 오늘 기록이 아직 없으면 전날이 근무일
if now.hour < boundary and not self.db.get_work_record(today_str):
return (now.date() - timedelta(days=1)).isoformat()
return today_str
def clock_out(self):
"""퇴근 처리"""
if not self.is_clocked_in:
return
now = datetime.now()
# 근무일 = 출근일. 자정 넘겨 퇴근하면 now.date()는 이미 다음 날이므로
# 이 값을 써야 출근일 행에 퇴근이 기록된다 (UPDATE 0건 방지).
workday = self.clock_in_time.date().isoformat()
# 확인 메시지
reply = QMessageBox.question(
@@ -1272,12 +1257,13 @@ class MainWindow(QMainWindow):
self.clock_in_time, now
)
# 주말/공휴일 체크
is_non_working_day = self.time_calc.is_non_working_day(now, self.db)
day_type = self.time_calc.get_day_type(now, self.db)
# 주말/공휴일 체크 — 근무 성격은 출근일로 판정 (금요일 야근이 토요일
# 근무로 뒤바뀌지 않게). 롤오버 경로와 동일 기준.
is_non_working_day = self.time_calc.is_non_working_day(self.clock_in_time, self.db)
day_type = self.time_calc.get_day_type(self.clock_in_time, self.db)
# 오늘의 외출 시간 가져오기
break_minutes = self.db.get_total_break_minutes_today()
# 근무일의 외출 시간 가져오기
break_minutes = self.db.get_total_break_minutes_today(workday)
# 적립 단위(분) — 사용자 설정. 기본 30, 옵션 15/60.
unit_minutes = self.db.get_setting_int('overtime_unit', 30)
@@ -1318,7 +1304,7 @@ class MainWindow(QMainWindow):
overtime_earned = 0 # 적립 스킵 (overtime_actual은 기록용으로 유지)
# DB 업데이트
today = datetime.now().date().isoformat()
today = workday
clock_out_str = now.strftime("%H:%M:%S")
self.db.update_clock_out(
@@ -1328,7 +1314,7 @@ class MainWindow(QMainWindow):
# 연장근무 적립 기록
if overtime_earned > 0:
today_record = self.db.get_today_record()
today_record = self.db.get_work_record(today)
if today_record:
self.db.add_overtime_earned(
today_record['id'], overtime_earned, today
@@ -1387,9 +1373,8 @@ class MainWindow(QMainWindow):
return
try:
# DB에서 퇴근 취소
today = datetime.now().date().isoformat()
success = self.db.cancel_clock_out(today)
# DB에서 퇴근 취소 — 새벽 퇴근이면 달력 날짜가 아니라 출근일 행
success = self.db.cancel_clock_out(self.current_workday_date())
if success:
# 상태 복원
@@ -2315,10 +2300,13 @@ class MainWindow(QMainWindow):
return
report_lines.append("")
def generate_daily_report(self):
"""오늘 하루 근무 내역 보고서 생성 및 클립보드 복사"""
from datetime import date
def generate_daily_report(self, target_date: str = None):
"""근무 내역 보고서 생성 및 클립보드 복사.
Args:
target_date: 'YYYY-MM-DD'. 생략 시 진행 중인 근무일
(자정~경계시간 사이면 출근일). 캘린더에서 과거 날짜로도 호출.
"""
# 도전과제 카운터 (보고서 생성 횟수)
try:
cur = self.db.get_setting_int('daily_report_count', 0)
@@ -2326,10 +2314,10 @@ class MainWindow(QMainWindow):
except Exception as e:
dlog(f"daily report counter failed: {e}")
today = date.today().isoformat()
date_str = target_date or self.current_workday_date()
# 오늘의 근무 기록 조회
work_record = self.db.get_today_record()
# 해당 근무일의 기록 조회
work_record = self.db.get_work_record(date_str)
if not work_record:
QMessageBox.warning(
@@ -2342,17 +2330,21 @@ class MainWindow(QMainWindow):
# 보고서 작성
report_lines = []
report_lines.append("=" * 40)
report_lines.append(tr('report.title', date=today))
report_lines.append(tr('report.title', date=date_str))
report_lines.append("=" * 40)
report_lines.append("")
# 출근/퇴근 시간
clock_in_dt = datetime.fromisoformat(f"{today} {work_record['clock_in']}")
# 출근/퇴근 시간 (퇴근이 출근보다 이르면 익일 퇴근)
clock_in_dt = datetime.fromisoformat(f"{date_str} {work_record['clock_in']}")
report_lines.append(tr('report.clock_in', time=self.format_time(clock_in_dt, include_seconds=True)))
if work_record['clock_out']:
clock_out_dt = datetime.fromisoformat(f"{today} {work_record['clock_out']}")
report_lines.append(tr('report.clock_out', time=self.format_time(clock_out_dt, include_seconds=True)))
_, clock_out_dt = resolve_work_datetimes(
date_str, work_record['clock_in'], work_record['clock_out'])
out_str = self.format_time(clock_out_dt, include_seconds=True)
if clock_out_dt.date() != clock_in_dt.date():
out_str = tr('label.next_day_time', time=out_str)
report_lines.append(tr('report.clock_out', time=out_str))
# 총 근무 시간
total_work_hours = work_record.get('total_hours') or work_record.get('work_hours', 0)
@@ -2367,7 +2359,7 @@ class MainWindow(QMainWindow):
# 외출 / 점심 / 저녁 분리 — break_type 으로 구분 (v2.7.0+)
# 'break'(또는 NULL) = 일반 외출, 'lunch'/'dinner' = 실측 식사 기록
all_break_records = self.db.get_today_break_records()
all_break_records = self.db.get_break_records_by_date(date_str)
real_break_records = [b for b in all_break_records
if (b.get('break_type') or 'break') == 'break']
lunch_records = [b for b in all_break_records if b.get('break_type') == 'lunch']
@@ -2379,9 +2371,9 @@ class MainWindow(QMainWindow):
if real_break_minutes > 0 or has_active_break:
report_lines.append(tr('report.break_time', time=format_hours_minutes(real_break_minutes)))
for br in real_break_records:
break_out_time = datetime.fromisoformat(f"{today} {br['break_out']}")
break_out_time = datetime.fromisoformat(f"{date_str} {br['break_out']}")
if br['break_in']:
break_in_time = datetime.fromisoformat(f"{today} {br['break_in']}")
break_in_time = datetime.fromisoformat(f"{date_str} {br['break_in']}")
# 자정 경계 처리: 복귀 시간이 외출 시간보다 이전이면 다음날로 간주
if break_in_time < break_out_time:
break_in_time += timedelta(days=1)
@@ -2397,7 +2389,7 @@ class MainWindow(QMainWindow):
lunch_flag = bool(work_record.get('lunch_break', False))
if lunch_flag or lunch_records:
self._append_meal_section(
report_lines, today, tr('label.lunch'),
report_lines, date_str, tr('label.lunch'),
lunch_flag, lunch_records,
self.time_calc.lunch_duration_minutes,
)
@@ -2406,7 +2398,7 @@ class MainWindow(QMainWindow):
dinner_flag = bool(work_record.get('dinner_break', False))
if dinner_flag or dinner_records:
self._append_meal_section(
report_lines, today, tr('label.dinner'),
report_lines, date_str, tr('label.dinner'),
dinner_flag, dinner_records,
self.time_calc.dinner_duration_minutes,
)
@@ -2425,8 +2417,8 @@ class MainWindow(QMainWindow):
report_lines.append(tr('report.overtime_banked', time=tr('label.time_hours_minutes', hours=earned_hours, minutes=earned_mins)))
report_lines.append("")
# 오늘 사용한 추가근무
overtime_used_today = self.db.get_today_overtime_usage()
# 해당 날짜에 사용한 추가근무
overtime_used_today = self.db.get_today_overtime_usage(date_str)
if overtime_used_today > 0:
used_hours = overtime_used_today // 60
used_mins = overtime_used_today % 60
@@ -2440,7 +2432,7 @@ class MainWindow(QMainWindow):
FROM overtime_usage
WHERE date = ?
ORDER BY created_at ASC
''', (today,))
''', (date_str,))
usage_records = cursor.fetchall()
conn.close()
@@ -2453,8 +2445,8 @@ class MainWindow(QMainWindow):
report_lines.append(tr('report.overtime_used_detail', time=tr('label.time_hours_minutes', hours=used_h, minutes=used_m), reason=reason_text))
report_lines.append("")
# 오늘 사용한 연차 (일괄 추가 및 수동 조정 제외)
leave_records = self.db.get_leave_records(start_date=today, end_date=today, exclude_bulk=False)
# 해당 날짜에 사용한 연차 (일괄 추가 및 수동 조정 제외)
leave_records = self.db.get_leave_records(start_date=date_str, end_date=date_str, exclude_bulk=False)
# manual 타입이거나 메모에 "일괄 추가"가 포함된 것은 제외
filtered_leave_records = [
+27 -4
View File
@@ -21,7 +21,7 @@ class PastRecordDialog(QDialog):
self.date_str = date_str
self.setWindowTitle(tr('past_record.dialog_title', date=date_str))
self.setModal(True)
self.setFixedSize(380, 320)
self.setFixedSize(380, 348)
layout = QVBoxLayout()
layout.setSpacing(8)
@@ -55,6 +55,15 @@ class PastRecordDialog(QDialog):
co_row.addStretch()
layout.addLayout(co_row)
# 익일 퇴근 안내 (퇴근 < 출근이면 표시)
self.overnight_note = QLabel('')
self.overnight_note.setObjectName("note_text")
self.overnight_note.setVisible(False)
layout.addWidget(self.overnight_note)
self.clock_in_edit.timeChanged.connect(self._refresh_overnight_note)
self.clock_out_edit.timeChanged.connect(self._refresh_overnight_note)
self.clock_out_check.toggled.connect(self._refresh_overnight_note)
# 점심/저녁
meal_row = QHBoxLayout()
self.lunch_check = QCheckBox(tr('past_record.check_lunch'))
@@ -88,11 +97,25 @@ class PastRecordDialog(QDialog):
self.setLayout(layout)
apply_dark_titlebar(self)
def _refresh_overnight_note(self):
"""퇴근이 출근보다 이르면 익일 퇴근으로 해석됨을 표시."""
if not self.clock_out_check.isChecked():
self.overnight_note.setVisible(False)
return
ci = self.clock_in_edit.time()
co = self.clock_out_edit.time()
if co >= ci:
self.overnight_note.setVisible(False)
return
total_min = ci.secsTo(co) // 60 + 24 * 60 # 익일까지
self.overnight_note.setText(tr('edit.overnight_note',
hours=total_min // 60, minutes=total_min % 60))
self.overnight_note.setVisible(True)
def _validate_and_accept(self):
# 퇴근 < 출근은 익일 퇴근(야근)으로 허용. 같은 시각만 반려.
if self.clock_out_check.isChecked():
ci = self.clock_in_edit.time()
co = self.clock_out_edit.time()
if co <= ci:
if self.clock_out_edit.time() == self.clock_in_edit.time():
QMessageBox.warning(self, tr('past_record.input_error_title'),
tr('past_record.input_error_body'))
return