|
1 | 1 | # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors |
2 | 2 | # See license.txt |
3 | 3 |
|
| 4 | +from datetime import timedelta |
| 5 | + |
4 | 6 | import frappe |
5 | | -from frappe.utils import add_days, get_datetime, getdate, nowdate |
| 7 | +from frappe.utils import add_days, get_datetime, getdate, now_datetime, nowdate |
6 | 8 |
|
7 | 9 | from erpnext.setup.doctype.employee.test_employee import make_employee |
8 | 10 |
|
| 11 | +from hrms.hr.doctype.employee_checkin.test_employee_checkin import make_checkin |
| 12 | +from hrms.hr.doctype.overtime_type.test_overtime_type import create_overtime_type |
9 | 13 | from hrms.hr.doctype.shift_assignment.shift_assignment import ( |
10 | 14 | MultipleShiftError, |
11 | 15 | OverlappingShiftError, |
12 | 16 | get_actual_start_end_datetime_of_shift, |
13 | 17 | get_events, |
14 | 18 | ) |
15 | 19 | from hrms.hr.doctype.shift_type.test_shift_type import make_shift_assignment, setup_shift_type |
| 20 | +from hrms.payroll.doctype.salary_component.test_salary_component import create_salary_component |
16 | 21 | from hrms.tests.utils import HRMSTestSuite |
17 | 22 |
|
18 | 23 | test_dependencies = ["Shift Type"] |
@@ -251,3 +256,52 @@ def test_shift_details_on_consecutive_days_with_overlapping_timings(self): |
251 | 256 | self.assertTrue(checkin.shift_type.name == checkout.shift_type.name == "Morning") |
252 | 257 | self.assertEqual(checkin.actual_start, get_datetime(f"{yesterday} 06:00:00")) |
253 | 258 | self.assertEqual(checkout.actual_end, get_datetime(f"{yesterday} 13:00:00")) |
| 259 | + |
| 260 | + def test_auto_attendance_calculates_ot_for_default_shift(self): |
| 261 | + """Ensure overtime is calculated when employee works beyond default shift hours.""" |
| 262 | + salary_component = create_salary_component("Overtime") |
| 263 | + |
| 264 | + overtime_type = create_overtime_type( |
| 265 | + name="_Test Overtime Type", |
| 266 | + maximum_overtime_hours_allowed=5, |
| 267 | + overtime_calculation_method="Fixed Hourly Rate", |
| 268 | + overtime_salary_component=salary_component.name, |
| 269 | + ) |
| 270 | + |
| 271 | + shift_type = setup_shift_type( |
| 272 | + shift_type="_Test OT Shift", |
| 273 | + start_time="08:00:00", |
| 274 | + end_time="17:00:00", |
| 275 | + allow_overtime=1, |
| 276 | + overtime_type=overtime_type.name, |
| 277 | + enable_auto_attendance=1, |
| 278 | + allow_check_out_after_shift_end_time=300, |
| 279 | + last_sync_of_checkin=now_datetime() + timedelta(days=2), |
| 280 | + ) |
| 281 | + |
| 282 | + employee = make_employee( |
| 283 | + "test_ot_default_shift@example.com", |
| 284 | + company="_Test Company", |
| 285 | + default_shift=shift_type.name, |
| 286 | + ) |
| 287 | + |
| 288 | + make_checkin(employee, get_datetime(f"{getdate()} 08:00:00")) |
| 289 | + make_checkin(employee, get_datetime(f"{getdate()} 19:00:00"), log_type="OUT") |
| 290 | + |
| 291 | + shift_type.process_auto_attendance() |
| 292 | + |
| 293 | + attendance = frappe.db.get_value( |
| 294 | + "Attendance", |
| 295 | + { |
| 296 | + "employee": employee, |
| 297 | + "attendance_date": getdate(), |
| 298 | + "docstatus": ["!=", 2], |
| 299 | + }, |
| 300 | + ["overtime_type", "working_hours", "actual_overtime_duration"], |
| 301 | + as_dict=True, |
| 302 | + ) |
| 303 | + |
| 304 | + self.assertIsNotNone(attendance) |
| 305 | + self.assertEqual(attendance.overtime_type, shift_type.overtime_type) |
| 306 | + self.assertEqual(attendance.working_hours, 11.0) |
| 307 | + self.assertEqual(attendance.actual_overtime_duration, 2.0) |
0 commit comments