From 259c28e266fe4969201d53741b3e875384a354d1 Mon Sep 17 00:00:00 2001 From: Dries Peeters Date: Mon, 26 Jan 2026 14:47:31 +0100 Subject: [PATCH] fix(time-tracking): recalculate duration when start/end time is edited - TimeTrackingService.update_entry() now calls calculate_duration() when start_time or end_time is changed so duration_seconds stays correct. - Add test_update_entry_recalculates_duration_when_start_end_edited (fixes #451). --- app/services/time_tracking_service.py | 3 +++ .../test_time_tracking_service_complete.py | 25 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/app/services/time_tracking_service.py b/app/services/time_tracking_service.py index c7f3238a..8ba156a4 100644 --- a/app/services/time_tracking_service.py +++ b/app/services/time_tracking_service.py @@ -383,6 +383,9 @@ class TimeTrackingService: entry.start_time = start_time if end_time is not None: entry.end_time = end_time + # Recompute stored duration when start or end time changed + if entry.end_time and (start_time is not None or end_time is not None): + entry.calculate_duration() if notes is not None: entry.notes = notes if tags is not None: diff --git a/tests/test_services/test_time_tracking_service_complete.py b/tests/test_services/test_time_tracking_service_complete.py index 663d928c..9a537833 100644 --- a/tests/test_services/test_time_tracking_service_complete.py +++ b/tests/test_services/test_time_tracking_service_complete.py @@ -23,6 +23,31 @@ class TestTimeTrackingServiceComplete: assert result["entry"].notes == "Updated notes" assert result["entry"].billable is False + def test_update_entry_recalculates_duration_when_start_end_edited(self, app, user, project, time_entry): + """Duration should be recomputed when start_time or end_time is updated (issue #451).""" + service = TimeTrackingService() + old_duration = time_entry.duration_seconds + + # Change start/end so the span is exactly 15 minutes + new_start = time_entry.start_time.replace(minute=0, second=0, microsecond=0) + new_end = new_start + timedelta(minutes=15) + expected_seconds = 15 * 60 # 900 + + result = service.update_entry( + entry_id=time_entry.id, + user_id=user.id, + is_admin=False, + start_time=new_start, + end_time=new_end, + ) + + assert result["success"] is True + assert result["entry"].start_time == new_start + assert result["entry"].end_time == new_end + assert result["entry"].duration_seconds == expected_seconds, ( + f"duration_seconds should be {expected_seconds} after editing start/end, got {result['entry'].duration_seconds} (old was {old_duration})" + ) + def test_update_entry_not_found(self, app, user): """Test update with non-existent entry""" service = TimeTrackingService()