diff --git a/hrms/hooks.py b/hrms/hooks.py index 77247f88f7..c0cde70f20 100644 --- a/hrms/hooks.py +++ b/hrms/hooks.py @@ -223,6 +223,7 @@ "hrms.hr.doctype.daily_work_summary_group.daily_work_summary_group.trigger_emails", ], "hourly_long": [ + "hrms.hr.doctype.shift_type.shift_type.update_last_sync_of_checkin", "hrms.hr.doctype.shift_type.shift_type.process_auto_attendance_for_all_shifts", "hrms.hr.doctype.shift_schedule_assignment.shift_schedule_assignment.process_auto_shift_creation", ], diff --git a/hrms/hr/doctype/shift_type/shift_type.js b/hrms/hr/doctype/shift_type/shift_type.js index 47d99e26cf..71cce7e36a 100644 --- a/hrms/hr/doctype/shift_type/shift_type.js +++ b/hrms/hr/doctype/shift_type/shift_type.js @@ -36,4 +36,10 @@ frappe.ui.form.on("Shift Type", { }); }); }, + + auto_update_last_sync: function (frm) { + if (frm.doc.auto_update_last_sync) { + frm.set_value("last_sync_of_checkin", ""); + } + }, }); diff --git a/hrms/hr/doctype/shift_type/shift_type.json b/hrms/hr/doctype/shift_type/shift_type.json index fc7be8bb33..0f72c456af 100644 --- a/hrms/hr/doctype/shift_type/shift_type.json +++ b/hrms/hr/doctype/shift_type/shift_type.json @@ -24,6 +24,7 @@ "working_hours_threshold_for_absent", "process_attendance_after", "last_sync_of_checkin", + "auto_update_last_sync", "grace_period_settings_auto_attendance_section", "enable_late_entry_marking", "late_entry_grace_period", @@ -149,7 +150,8 @@ "description": "Last Known Successful Sync of Employee Checkin. Reset this only if you are sure that all Logs are synced from all the locations. Please don't modify this if you are unsure.", "fieldname": "last_sync_of_checkin", "fieldtype": "Datetime", - "label": "Last Sync of Checkin" + "label": "Last Sync of Checkin", + "read_only_depends_on": "auto_update_last_sync" }, { "default": "0", @@ -176,6 +178,13 @@ "fieldtype": "Select", "label": "Roster Color", "options": "Blue\nCyan\nFuchsia\nGreen\nLime\nOrange\nPink\nRed\nViolet\nYellow" + }, + { + "default": "0", + "description": "Recommended for a single biometric device / checkins via mobile app", + "fieldname": "auto_update_last_sync", + "fieldtype": "Check", + "label": "Automatically update Last Sync of Checkin" } ], "links": [], diff --git a/hrms/hr/doctype/shift_type/shift_type.py b/hrms/hr/doctype/shift_type/shift_type.py index 5cfefd61d3..bdbc0f97f0 100644 --- a/hrms/hr/doctype/shift_type/shift_type.py +++ b/hrms/hr/doctype/shift_type/shift_type.py @@ -346,7 +346,26 @@ def should_mark_attendance(self, employee: str, attendance_date: str) -> bool: return True +def update_last_sync_of_checkin(): + """Called from hooks""" + shifts = frappe.get_all( + "Shift Type", + filters={"enable_auto_attendance": 1, "auto_update_last_sync": 1}, + fields=["name", "last_sync_of_checkin"], + ) + + for shift in shifts: + last_shift_sync = frappe.db.get_value( + "Employee Checkin", {"shift": shift.name}, "time", order_by="time desc" + ) + if not shift.last_sync_of_checkin or get_datetime(last_shift_sync) > get_datetime( + shift.last_sync_of_checkin + ): + frappe.db.set_value("Shift Type", shift.name, "last_sync_of_checkin", last_shift_sync) + + def process_auto_attendance_for_all_shifts(): + """Called from hooks""" shift_list = frappe.get_all("Shift Type", filters={"enable_auto_attendance": "1"}, pluck="name") for shift in shift_list: doc = frappe.get_cached_doc("Shift Type", shift) diff --git a/hrms/hr/doctype/shift_type/test_shift_type.py b/hrms/hr/doctype/shift_type/test_shift_type.py index d97504e505..b1ddea9ce0 100644 --- a/hrms/hr/doctype/shift_type/test_shift_type.py +++ b/hrms/hr/doctype/shift_type/test_shift_type.py @@ -10,6 +10,7 @@ from erpnext.setup.doctype.holiday_list.test_holiday_list import set_holiday_list from hrms.hr.doctype.leave_application.test_leave_application import get_first_sunday +from hrms.hr.doctype.shift_type.shift_type import update_last_sync_of_checkin from hrms.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list from hrms.tests.test_utils import add_date_to_holiday_list @@ -25,6 +26,29 @@ def setUp(self): to_date = get_year_ending(getdate()) self.holiday_list = make_holiday_list(from_date=from_date, to_date=to_date) + def test_auto_update_last_sync_of_checkin(self): + from hrms.hr.doctype.employee_checkin.test_employee_checkin import make_checkin + + shift_type = setup_shift_type() + shift_type.last_sync_of_checkin = None + shift_type.auto_update_last_sync = 1 + shift_type.save() + + employee = make_employee("test_employee_checkin@example.com", company="_Test Company") + date = getdate() + make_shift_assignment(shift_type.name, employee, date) + + make_checkin(employee, datetime.combine(date, get_time("08:00:00"))) + log_2 = make_checkin(employee, datetime.combine(date, get_time("08:45:53"))) + update_last_sync_of_checkin() + shift_type.reload() + self.assertEqual(shift_type.last_sync_of_checkin, log_2.time) + + log_3 = make_checkin(employee, datetime.combine(date, get_time("12:00:00"))) + update_last_sync_of_checkin() + shift_type.reload() + self.assertEqual(shift_type.last_sync_of_checkin, log_3.time) + def test_mark_attendance(self): from hrms.hr.doctype.employee_checkin.test_employee_checkin import make_checkin