v2.13.1: 일일보고 시간연차 분 단위 표시
Fixed: - 30분 연차(0.0625일)가 int(days * 8) 절삭으로 "0시간"으로 표시되어 보고서 수신자가 사용 내역을 알 수 없던 문제. 분 단위까지 표시. - 보고서만 1일=8시간으로 하드코딩되어 단축근무(7h30m) 사용자의 반차가 4시간으로 표시되던 오류. DB 정본(days * work_minutes) 기준으로 통일. Added: - utils.time_format.format_leave_amount(days, work_minutes) 공용 헬퍼 + 자체 검사 9건 (python utils/time_format.py) - i18n 키 report.leave_used_days_time (ko/en) 테스트: 유닛 200 / 통합 53 / GUI 8 / i18n 5 통과. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f28f0619a4
commit
21a01df97a
15
CHANGELOG.md
15
CHANGELOG.md
@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||||
|
|
||||||
|
## [2.13.1] — 2026-09-01
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- **일일보고에서 시간 단위 연차가 "0시간"으로 표시되던 문제** — 30분 연차(0.0625일)가
|
||||||
|
`int(days * 8)` 절삭으로 0시간이 되어 보고서를 받는 담당자가 사용 내역을 알 수 없었다.
|
||||||
|
분 단위까지 표시하도록 수정 (`30분`, `1시간 30분`, `1일 4시간`).
|
||||||
|
- **단축근무 사용자의 연차 시간 환산 오류** — 보고서만 1일=8시간으로 하드코딩되어 있어
|
||||||
|
7h30m 근무자의 반차가 4시간으로 표시됐다. DB 정본(`days * work_minutes`)과 동일한
|
||||||
|
기준으로 통일 — 이제 3시간 45분.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- `utils.time_format.format_leave_amount(days, work_minutes)` — 연차 일수를
|
||||||
|
(일수, 나머지 분)으로 분해하는 공용 헬퍼 + 자체 검사(`python utils/time_format.py`).
|
||||||
|
- i18n 키 `report.leave_used_days_time` (`{days}일 {time}` / `{days}d {time}`).
|
||||||
|
|
||||||
## [2.13.0] — 2026-08-12
|
## [2.13.0] — 2026-08-12
|
||||||
|
|
||||||
### Fixed — 익일(자정 넘김) 퇴근
|
### Fixed — 익일(자정 넘김) 퇴근
|
||||||
|
|||||||
@ -450,6 +450,7 @@ _DICT = {
|
|||||||
'report.leave_used': '🌴 연차 사용: {value}',
|
'report.leave_used': '🌴 연차 사용: {value}',
|
||||||
'report.leave_used_days_hours': '{days}일 {hours}시간',
|
'report.leave_used_days_hours': '{days}일 {hours}시간',
|
||||||
'report.leave_used_days': '{days}일',
|
'report.leave_used_days': '{days}일',
|
||||||
|
'report.leave_used_days_time': '{days}일 {time}',
|
||||||
'report.leave_used_hours': '{hours}시간',
|
'report.leave_used_hours': '{hours}시간',
|
||||||
'report.leave_detail': ' - {type}: {time}{memo}',
|
'report.leave_detail': ' - {type}: {time}{memo}',
|
||||||
'report.title': '📋 일일 근무 보고서 - {date}',
|
'report.title': '📋 일일 근무 보고서 - {date}',
|
||||||
@ -1613,6 +1614,7 @@ _DICT = {
|
|||||||
'report.leave_used': '🌴 Leave used: {value}',
|
'report.leave_used': '🌴 Leave used: {value}',
|
||||||
'report.leave_used_days_hours': '{days}d {hours}h',
|
'report.leave_used_days_hours': '{days}d {hours}h',
|
||||||
'report.leave_used_days': '{days}d',
|
'report.leave_used_days': '{days}d',
|
||||||
|
'report.leave_used_days_time': '{days}d {time}',
|
||||||
'report.leave_used_hours': '{hours}h',
|
'report.leave_used_hours': '{hours}h',
|
||||||
'report.leave_detail': ' - {type}: {time}{memo}',
|
'report.leave_detail': ' - {type}: {time}{memo}',
|
||||||
'report.title': '📋 Daily Work Report - {date}',
|
'report.title': '📋 Daily Work Report - {date}',
|
||||||
|
|||||||
@ -4,4 +4,4 @@
|
|||||||
릴리스 시 이 값을 올린 후 git tag → push.
|
릴리스 시 이 값을 올린 후 git tag → push.
|
||||||
CHANGELOG.md의 최상단 항목과 일치시킬 것.
|
CHANGELOG.md의 최상단 항목과 일치시킬 것.
|
||||||
"""
|
"""
|
||||||
__version__ = '2.13.0'
|
__version__ = '2.13.1'
|
||||||
|
|||||||
@ -34,7 +34,7 @@ from ui.settings_view import SettingsView
|
|||||||
from ui.break_view import BreakView
|
from ui.break_view import BreakView
|
||||||
from core.notifier import Notifier
|
from core.notifier import Notifier
|
||||||
from utils.system_tray import SystemTrayIcon
|
from utils.system_tray import SystemTrayIcon
|
||||||
from utils.time_format import format_hours_minutes
|
from utils.time_format import format_hours_minutes, format_leave_amount
|
||||||
from ui.styles import get_theme, ThemeColors, apply_dark_titlebar
|
from ui.styles import get_theme, ThemeColors, apply_dark_titlebar
|
||||||
from utils.debug_log import dlog
|
from utils.debug_log import dlog
|
||||||
|
|
||||||
@ -2265,6 +2265,21 @@ class MainWindow(QMainWindow):
|
|||||||
if self.is_clocked_in and self.clock_in_time:
|
if self.is_clocked_in and self.clock_in_time:
|
||||||
self.update_display()
|
self.update_display()
|
||||||
|
|
||||||
|
def _format_leave_used(self, days: float) -> str:
|
||||||
|
"""연차 사용량을 '30분' / '3시간 45분' / '1일 4시간' 형태로.
|
||||||
|
|
||||||
|
기존에는 `int(days * 8)`로 시간만 뽑아 30분 연차(0.0625일)가 "0시간"으로
|
||||||
|
표시됐다. 환산 기준도 DB 정본(`days * work_minutes`)에 맞춰 단축근무
|
||||||
|
사용자의 반차가 4시간으로 뻥튀기되지 않게 한다.
|
||||||
|
"""
|
||||||
|
d, rem = format_leave_amount(days, self.db.get_work_minutes())
|
||||||
|
if d and rem:
|
||||||
|
return tr('report.leave_used_days_time', days=d,
|
||||||
|
time=format_hours_minutes(rem, omit_zero_minutes=True))
|
||||||
|
if d:
|
||||||
|
return tr('report.leave_used_days', days=d)
|
||||||
|
return format_hours_minutes(rem, omit_zero_minutes=True)
|
||||||
|
|
||||||
def _append_meal_section(self, report_lines, today: str, label: str,
|
def _append_meal_section(self, report_lines, today: str, label: str,
|
||||||
flag: bool, records: list, default_min: int) -> None:
|
flag: bool, records: list, default_min: int) -> None:
|
||||||
"""일일 보고서의 점심/저녁 섹션 출력.
|
"""일일 보고서의 점심/저녁 섹션 출력.
|
||||||
@ -2458,16 +2473,7 @@ class MainWindow(QMainWindow):
|
|||||||
if filtered_leave_records:
|
if filtered_leave_records:
|
||||||
total_leave_days = sum(r['days'] for r in filtered_leave_records)
|
total_leave_days = sum(r['days'] for r in filtered_leave_records)
|
||||||
|
|
||||||
if total_leave_days >= 1:
|
value = self._format_leave_used(total_leave_days)
|
||||||
days = int(total_leave_days)
|
|
||||||
hours = int((total_leave_days - days) * 8)
|
|
||||||
if hours > 0:
|
|
||||||
value = tr('report.leave_used_days_hours', days=days, hours=hours)
|
|
||||||
else:
|
|
||||||
value = tr('report.leave_used_days', days=days)
|
|
||||||
else:
|
|
||||||
hours = int(total_leave_days * 8)
|
|
||||||
value = tr('report.leave_used_hours', hours=hours)
|
|
||||||
report_lines.append(tr('report.leave_used', value=value))
|
report_lines.append(tr('report.leave_used', value=value))
|
||||||
|
|
||||||
for lr in filtered_leave_records:
|
for lr in filtered_leave_records:
|
||||||
@ -2483,19 +2489,7 @@ class MainWindow(QMainWindow):
|
|||||||
'반반차': tr('leave.type.quarter')
|
'반반차': tr('leave.type.quarter')
|
||||||
}.get(lr.get('leave_type', ''), lr.get('leave_type', tr('leave.type.annual')))
|
}.get(lr.get('leave_type', ''), lr.get('leave_type', tr('leave.type.annual')))
|
||||||
|
|
||||||
days_used = lr['days']
|
time_str = self._format_leave_used(lr['days'])
|
||||||
|
|
||||||
# 일수를 시간으로 표시
|
|
||||||
if days_used >= 1:
|
|
||||||
d = int(days_used)
|
|
||||||
h = int((days_used - d) * 8)
|
|
||||||
if h > 0:
|
|
||||||
time_str = tr('report.leave_used_days_hours', days=d, hours=h)
|
|
||||||
else:
|
|
||||||
time_str = tr('report.leave_used_days', days=d)
|
|
||||||
else:
|
|
||||||
h = int(days_used * 8)
|
|
||||||
time_str = tr('report.leave_used_hours', hours=h)
|
|
||||||
|
|
||||||
memo = f" - {lr['memo']}" if lr.get('memo') else ""
|
memo = f" - {lr['memo']}" if lr.get('memo') else ""
|
||||||
report_lines.append(f" - {leave_type_name}: {time_str}{memo}")
|
report_lines.append(f" - {leave_type_name}: {time_str}{memo}")
|
||||||
|
|||||||
@ -31,3 +31,40 @@ def format_hours_minutes(total_minutes: int, *, omit_zero_minutes: bool = False)
|
|||||||
if m == 0:
|
if m == 0:
|
||||||
return f"{h}시간"
|
return f"{h}시간"
|
||||||
return f"{h}시간 {m}분"
|
return f"{h}시간 {m}분"
|
||||||
|
|
||||||
|
|
||||||
|
def format_leave_amount(days: float, work_minutes: int) -> tuple:
|
||||||
|
"""연차 일수를 (완전한 일수, 나머지 분)으로 분해.
|
||||||
|
|
||||||
|
`int(days * 8)`로 시간만 뽑던 표시가 30분 연차(0.0625일)를 "0시간"으로
|
||||||
|
삼켜버리던 문제를 막는다. 환산 기준은 DB 정본과 동일한 work_minutes
|
||||||
|
(`Database.get_leave_minutes_for`가 `days * get_work_minutes()`를 쓴다) —
|
||||||
|
단축근무 7h30m 사용자의 반차는 4시간이 아니라 3시간 45분이다.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
days: 연차 일수 (1.0 / 0.5 / 0.25 / 0.0625 …)
|
||||||
|
work_minutes: 1일 근무 분 (기본 480)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(일수, 나머지 분) — 예: 480분 기준 1.5일 → (1, 240), 0.0625일 → (0, 30)
|
||||||
|
"""
|
||||||
|
unit = int(work_minutes) if work_minutes and work_minutes > 0 else 480
|
||||||
|
total_minutes = int(round(float(days) * unit))
|
||||||
|
return divmod(total_minutes, unit)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 표준 8시간(480분) 기준
|
||||||
|
assert format_leave_amount(0.0625, 480) == (0, 30), '30분 연차'
|
||||||
|
assert format_leave_amount(0.125, 480) == (0, 60), '1시간 연차'
|
||||||
|
assert format_leave_amount(0.25, 480) == (0, 120), '반반차'
|
||||||
|
assert format_leave_amount(0.5, 480) == (0, 240), '반차'
|
||||||
|
assert format_leave_amount(1.0, 480) == (1, 0), '종일'
|
||||||
|
assert format_leave_amount(1.5, 480) == (1, 240), '1.5일'
|
||||||
|
# 단축근무 7h30m(450분) — 반차는 3시간 45분
|
||||||
|
assert format_leave_amount(0.5, 450) == (0, 225), '단축근무 반차'
|
||||||
|
assert format_leave_amount(1.0, 450) == (1, 0), '단축근무 종일'
|
||||||
|
# work_minutes 이상값은 480으로 폴백
|
||||||
|
assert format_leave_amount(0.5, 0) == (0, 240), '0 폴백'
|
||||||
|
assert format_hours_minutes(30, omit_zero_minutes=True) == '30분'
|
||||||
|
print('format_leave_amount 자체 검사 통과')
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user