Skip to content

Commit

Permalink
Merge pull request #488 from frappe/mergify/bp/version-14-hotfix/pr-463
Browse files Browse the repository at this point in the history
  • Loading branch information
ruchamahabal authored May 2, 2023
2 parents 7a2c9c8 + 2ea1562 commit bf2e631
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 55 deletions.
30 changes: 0 additions & 30 deletions hrms/hr/doctype/employee_checkin/test_employee_checkin.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,36 +258,6 @@ def test_fetch_shift_spanning_over_two_days(self):
self.assertEqual(log.shift_actual_start, datetime.combine(prev_day, get_time("22:00:00")))
self.assertEqual(log.shift_actual_end, datetime.combine(date, get_time("02:00:00")))

def test_no_shift_fetched_on_holiday_as_per_shift_holiday_list(self):
date = getdate()
from_date = get_year_start(date)
to_date = get_year_ending(date)
holiday_list = make_holiday_list(from_date=from_date, to_date=to_date)

employee = make_employee("[email protected]", company="_Test Company")
setup_shift_type(shift_type="Test Holiday Shift", holiday_list=holiday_list)

first_sunday = get_first_sunday(holiday_list, for_date=date)
timestamp = datetime.combine(first_sunday, get_time("08:00:00"))
log = make_checkin(employee, timestamp)

self.assertIsNone(log.shift)

@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
def test_no_shift_fetched_on_holiday_as_per_employee_holiday_list(self):
employee = make_employee("[email protected]", company="_Test Company")
shift_type = setup_shift_type(shift_type="Test Holiday Shift")
shift_type.holiday_list = None
shift_type.save()

date = getdate()

first_sunday = get_first_sunday(self.holiday_list, for_date=date)
timestamp = datetime.combine(first_sunday, get_time("08:00:00"))
log = make_checkin(employee, timestamp)

self.assertIsNone(log.shift)

def test_consecutive_shift_assignments_overlapping_within_grace_period(self):
# test adjustment for start and end times if they are overlapping
# within "begin_check_in_before_shift_start_time" and "allow_check_out_after_shift_end_time" periods
Expand Down
20 changes: 1 addition & 19 deletions hrms/hr/doctype/shift_assignment/shift_assignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@
from frappe.query_builder import Criterion
from frappe.utils import cstr, get_datetime, get_link_to_form, get_time, getdate, now_datetime

from erpnext.setup.doctype.employee.employee import get_holiday_list_for_employee
from erpnext.setup.doctype.holiday_list.holiday_list import is_holiday

from hrms.hr.utils import validate_active_employee


Expand Down Expand Up @@ -276,7 +273,7 @@ def get_employee_shift(
consider_default_shift: bool = False,
next_shift_direction: str = None,
) -> Dict:
"""Returns a Shift Type for the given employee on the given date. (excluding the holidays)
"""Returns a Shift Type for the given employee on the given date
:param employee: Employee for which shift is required.
:param for_timestamp: DateTime on which shift is required
Expand All @@ -293,10 +290,6 @@ def get_employee_shift(
if not shift_details and consider_default_shift:
shift_details = get_shift_details(default_shift, for_timestamp)

# if its a holiday, reset
if shift_details and is_holiday_date(employee, shift_details):
shift_details = None

# if no shift is found, find next or prev shift assignment based on direction
if not shift_details and next_shift_direction:
shift_details = get_prev_or_next_shift(
Expand Down Expand Up @@ -354,17 +347,6 @@ def get_prev_or_next_shift(
return shift_details or {}


def is_holiday_date(employee: str, shift_details: Dict) -> bool:
holiday_list_name = frappe.db.get_value(
"Shift Type", shift_details.shift_type.name, "holiday_list"
)

if not holiday_list_name:
holiday_list_name = get_holiday_list_for_employee(employee, False)

return holiday_list_name and is_holiday(holiday_list_name, shift_details.start_datetime.date())


def get_employee_shift_timings(
employee: str, for_timestamp: datetime = None, consider_default_shift: bool = False
) -> List[Dict]:
Expand Down
14 changes: 13 additions & 1 deletion hrms/hr/doctype/shift_type/shift_type.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"actions": [],
"autoname": "prompt",
"creation": "2018-04-13 16:22:52.954783",
"doctype": "DocType",
Expand All @@ -15,6 +16,7 @@
"working_hours_calculation_based_on",
"begin_check_in_before_shift_start_time",
"allow_check_out_after_shift_end_time",
"mark_auto_attendance_on_holidays",
"column_break_10",
"working_hours_threshold_for_half_day",
"working_hours_threshold_for_absent",
Expand Down Expand Up @@ -156,12 +158,21 @@
"fieldname": "last_sync_of_checkin",
"fieldtype": "Datetime",
"label": "Last Sync of Checkin"
},
{
"default": "0",
"description": "If enabled, auto attendance will be marked on holidays if Employee Checkins exist",
"fieldname": "mark_auto_attendance_on_holidays",
"fieldtype": "Check",
"label": "Mark Auto Attendance on Holidays"
}
],
"modified": "2019-07-30 01:05:24.660666",
"links": [],
"modified": "2023-05-02 11:49:20.020685",
"modified_by": "Administrator",
"module": "HR",
"name": "Shift Type",
"naming_rule": "Set by user",
"owner": "Administrator",
"permissions": [
{
Expand Down Expand Up @@ -200,5 +211,6 @@
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}
28 changes: 23 additions & 5 deletions hrms/hr/doctype/shift_type/shift_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ def process_auto_attendance(self):

for key, group in itertools.groupby(logs, key=lambda x: (x["employee"], x["shift_start"])):
single_shift_logs = list(group)
attendance_date = single_shift_logs[0].shift_actual_start.date()

if not self.should_mark_attendance(key[0], attendance_date):
continue

(
attendance_status,
working_hours,
Expand All @@ -56,7 +61,7 @@ def process_auto_attendance(self):
mark_attendance_and_link_log(
single_shift_logs,
attendance_status,
single_shift_logs[0].shift_actual_start.date(),
attendance_date,
working_hours,
late_entry,
early_exit,
Expand Down Expand Up @@ -117,10 +122,7 @@ def mark_absent_for_dates_with_no_attendance(self, employee):
if start_date is None:
return

holiday_list_name = self.holiday_list
if not holiday_list_name:
holiday_list_name = get_holiday_list_for_employee(employee, False)

holiday_list_name = self.get_holiday_list(employee)
start_time = get_time(self.start_time)

for date in daterange(getdate(start_date), getdate(end_date)):
Expand Down Expand Up @@ -215,6 +217,22 @@ def get_employees_with_default_shift(self, from_date=None):

return list(set(default_shift_employees) - set(active_shift_assignments))

def get_holiday_list(self, employee: str) -> str:
holiday_list_name = self.holiday_list or get_holiday_list_for_employee(employee, False)
return holiday_list_name

def should_mark_attendance(self, employee: str, attendance_date: str) -> bool:
"""Determines whether attendance should be marked on holidays or not"""
if self.mark_auto_attendance_on_holidays:
# no need to check if date is a holiday or not
# since attendance should be marked on all days
return True

holiday_list = self.get_holiday_list(employee)
if is_holiday(holiday_list, attendance_date):
return False
return True


def process_auto_attendance_for_all_shifts():
shift_list = frappe.get_all("Shift Type", "name", {"enable_auto_attendance": "1"}, as_list=True)
Expand Down
79 changes: 79 additions & 0 deletions hrms/hr/doctype/shift_type/test_shift_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,84 @@ def test_working_hours_threshold_for_absent_and_half_day_2(self):
attendance = frappe.db.get_value("Attendance", {"shift": shift_type.name}, "status")
self.assertEqual(attendance, "Absent")

@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
def test_mark_auto_attendance_on_holiday_enabled(self):
from hrms.hr.doctype.employee_checkin.test_employee_checkin import make_checkin

# add current date as holiday
date = getdate()
holiday_list = frappe.get_doc("Holiday List", self.holiday_list)
holiday_list.append(
"holidays",
{
"holiday_date": date,
"description": "test",
},
)
holiday_list.save()

shift_type = setup_shift_type(
shift_type="Test Holiday Shift", mark_auto_attendance_on_holidays=True
)
shift_type.holiday_list = None
shift_type.save()

employee = make_employee(
"[email protected]", default_shift=shift_type.name, company="_Test Company"
)

# make logs
timestamp = datetime.combine(date, get_time("08:00:00"))
log = make_checkin(employee, timestamp)
timestamp = datetime.combine(date, get_time("12:00:00"))
log = make_checkin(employee, timestamp)

shift_type.process_auto_attendance()

attendance = frappe.db.get_value(
"Attendance", {"employee": employee, "attendance_date": date}, "status"
)
self.assertEqual(attendance, "Present")

@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
def test_mark_auto_attendance_on_holiday_disabled(self):
from hrms.hr.doctype.employee_checkin.test_employee_checkin import make_checkin

# add current date as holiday
date = getdate()
holiday_list = frappe.get_doc("Holiday List", self.holiday_list)
holiday_list.append(
"holidays",
{
"holiday_date": date,
"description": "test",
},
)
holiday_list.save()

shift_type = setup_shift_type(
shift_type="Test Holiday Shift", mark_auto_attendance_on_holidays=False
)
shift_type.holiday_list = None
shift_type.save()

employee = make_employee(
"[email protected]", default_shift=shift_type.name, company="_Test Company"
)

# make logs
timestamp = datetime.combine(date, get_time("08:00:00"))
log = make_checkin(employee, timestamp)
timestamp = datetime.combine(date, get_time("12:00:00"))
log = make_checkin(employee, timestamp)

shift_type.process_auto_attendance()

attendance = frappe.db.get_value(
"Attendance", {"employee": employee, "attendance_date": date}, "status"
)
self.assertIsNone(attendance)

def test_mark_absent_for_dates_with_no_attendance(self):
employee = make_employee("[email protected]", company="_Test Company")
shift_type = setup_shift_type(shift_type="Test Absent with no Attendance")
Expand Down Expand Up @@ -403,6 +481,7 @@ def setup_shift_type(**args):
"allow_check_out_after_shift_end_time": 60,
"process_attendance_after": add_days(date, -2),
"last_sync_of_checkin": now_datetime() + timedelta(days=1),
"mark_auto_attendance_on_holidays": args.mark_auto_attendance_on_holidays or False,
}
)

Expand Down

0 comments on commit bf2e631

Please sign in to comment.