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
+37 -1
View File
@@ -9,7 +9,7 @@ import pytest
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from core.time_calculator import TimeCalculator
from core.time_calculator import TimeCalculator, resolve_work_datetimes, is_overnight
@pytest.fixture
@@ -172,3 +172,39 @@ class TestHolidayOvertime:
ci, now, include_lunch=True, include_dinner=True
)
assert actual == 0 and earned == 0
class TestResolveWorkDatetimes:
"""익일(자정 넘김) 퇴근 해석 — 야근 기록 저장/수정의 기준."""
def test_same_day(self):
ci, co = resolve_work_datetimes('2026-05-01', '09:00:00', '18:00:00')
assert ci == datetime(2026, 5, 1, 9, 0)
assert co == datetime(2026, 5, 1, 18, 0)
def test_overnight_clock_out_rolls_to_next_day(self):
ci, co = resolve_work_datetimes('2026-05-01', '09:00:00', '01:30:00')
assert co == datetime(2026, 5, 2, 1, 30)
assert (co - ci) == timedelta(hours=16, minutes=30)
def test_equal_times_stay_same_day(self):
# 동시각은 익일로 밀지 않음 (UI에서 반려하는 케이스)
ci, co = resolve_work_datetimes('2026-05-01', '09:00:00', '09:00:00')
assert ci == co
def test_accepts_hh_mm(self):
ci, co = resolve_work_datetimes('2026-05-01', '09:00', '01:30')
assert (co - ci) == timedelta(hours=16, minutes=30)
def test_is_overnight(self):
assert is_overnight('09:00:00', '01:30:00') is True
assert is_overnight('09:00:00', '18:00:00') is False
assert is_overnight('09:00:00', '') is False
def test_overtime_uses_resolved_datetimes(self, calc_8h):
"""익일 퇴근도 정상 연장근무로 계산되는지 (음수 방지)."""
ci, co = resolve_work_datetimes('2026-05-01', '09:00:00', '01:30:00')
actual, earned = calc_8h.calculate_overtime(ci, co, include_lunch=True)
# 09:00 출근 + 8h + 점심 1h → 정시 18:00, 익일 01:30 퇴근 = 450분 초과
assert actual == 450
assert earned == 450