From a68aa49dc473dd609ccb8e4d37f63e68265d82be Mon Sep 17 00:00:00 2001 From: Asmita Hase Date: Fri, 31 Jan 2025 18:00:13 +0530 Subject: [PATCH] fix: show validation message before changing shift start time chore: added tests --- hrms/hr/doctype/shift_type/shift_type.py | 19 ++++++++++ hrms/hr/doctype/shift_type/test_shift_type.py | 36 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/hrms/hr/doctype/shift_type/shift_type.py b/hrms/hr/doctype/shift_type/shift_type.py index 6dc8cefb56..ff91e55518 100644 --- a/hrms/hr/doctype/shift_type/shift_type.py +++ b/hrms/hr/doctype/shift_type/shift_type.py @@ -6,6 +6,7 @@ from itertools import groupby import frappe +from frappe import _ from frappe.model.document import Document from frappe.utils import cint, create_batch, get_datetime, get_time, getdate @@ -25,6 +26,24 @@ class ShiftType(Document): + def validate(self): + if self.is_field_modified("start_time") and self.unlinked_checkins_exist(): + frappe.throw( + title=_("Unlinked Logs Found"), + msg=_( + "Mark attendance for the exsiting check in/out logs before changing important shift settings" + ), + ) + + def is_field_modified(self, fieldname): + return not self.is_new() and self.has_value_changed(fieldname) + + def unlinked_checkins_exist(self): + return frappe.db.exists( + "Employee Checkin", + {"shift": self.name, "attendance": ["is", "not set"], "skip_auto_attendance": 0}, + ) + @frappe.whitelist() def process_auto_attendance(self): if ( diff --git a/hrms/hr/doctype/shift_type/test_shift_type.py b/hrms/hr/doctype/shift_type/test_shift_type.py index 722f003e52..139f35be86 100644 --- a/hrms/hr/doctype/shift_type/test_shift_type.py +++ b/hrms/hr/doctype/shift_type/test_shift_type.py @@ -688,6 +688,42 @@ def test_mark_attendance_for_default_shift_when_shift_assignment_is_not_overlapp "Absent", ) + def test_validation_for_unlinked_logs_before_changing_important_shift_configuration(self): + # the important shift configuration is start time, it is used to sort logs chronologically + shift = setup_shift_type(shift_type="Test Shift", start_time="10:00:00", end_time="18:00:00") + employee = make_employee( + "test_employee4_attendance@example.com", company="_Test Company", default_shift=shift.name + ) + + from hrms.hr.doctype.employee_checkin.test_employee_checkin import make_checkin + + in_time = datetime.combine(getdate(), get_time("10:00:00")) + check_in = make_checkin(employee, in_time) + check_in.fetch_shift() + # Case 1: raise valdiation error if shift time is being changed and checkin logs exists + shift.start_time = get_time("10:15:00") + self.assertRaises(frappe.ValidationError, shift.save) + + # don't raise validation error if something else is being changed + # even if checkin logs exists, it's probably fine + shift.reload() + shift.begin_check_in_before_shift_start_time = 120 + shift.save() + self.assertEqual( + frappe.get_value("Shift Type", shift.name, "begin_check_in_before_shift_start_time"), 120 + ) + out_time = datetime.combine(getdate(), get_time("18:00:00")) + check_out = make_checkin(employee, out_time) + check_out.fetch_shift() + shift.process_auto_attendance() + + # Case 2: allow shift time to change if no unlinked logs exist + shift.start_time = get_time("10:15:00") + shift.save() + self.assertEqual( + get_time(frappe.get_value("Shift Type", shift.name, "start_time")), get_time("10:15:00") + ) + def setup_shift_type(**args): args = frappe._dict(args)