refactor: handle shifts spanning over 2 different days
diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment.py b/erpnext/hr/doctype/shift_assignment/shift_assignment.py
index 88aeb4c..6912c76 100644
--- a/erpnext/hr/doctype/shift_assignment/shift_assignment.py
+++ b/erpnext/hr/doctype/shift_assignment/shift_assignment.py
@@ -8,7 +8,7 @@
from frappe import _
from frappe.model.document import Document
from frappe.query_builder import Criterion, Column
-from frappe.utils import cstr, get_link_to_form, get_datetime, getdate, now_datetime, nowdate
+from frappe.utils import cstr, get_link_to_form, get_datetime, get_time, getdate, now_datetime, nowdate
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
@@ -174,7 +174,7 @@
def get_shift_for_time(shifts, for_timestamp):
valid_shifts = []
for entry in shifts:
- shift_details = get_shift_details(entry.shift_type, for_date=for_timestamp.date())
+ shift_details = get_shift_details(entry.shift_type, for_timestamp=for_timestamp)
if get_datetime(shift_details.actual_start) <= get_datetime(for_timestamp) <= get_datetime(shift_details.actual_end):
valid_shifts.append(shift_details)
@@ -245,7 +245,7 @@
# if shift assignment is not found, consider default shift
default_shift = frappe.db.get_value('Employee', employee, 'default_shift')
if not shift_details and consider_default_shift:
- shift_details = get_shift_details(default_shift, for_timestamp.date())
+ shift_details = get_shift_details(default_shift, for_timestamp)
# if its a holiday, reset
if shift_details and is_holiday_date(employee, shift_details):
@@ -373,7 +373,7 @@
return actual_shift_start, actual_shift_end, shift_details
-def get_shift_details(shift_type_name, for_date=None):
+def get_shift_details(shift_type_name, for_timestamp=None):
"""Returns Shift Details which contain some additional information as described below.
'shift_details' contains the following keys:
'shift_type' - Object of DocType Shift Type,
@@ -383,21 +383,34 @@
'actual_end' - datetime of shift end after adding 'allow_check_out_after_shift_end_time'(None is returned if this is zero)
:param shift_type_name: shift type name for which shift_details is required.
- :param for_date: Date on which shift_details are required
+ :param for_timestamp: DateTime value on which shift_details are required
"""
if not shift_type_name:
return None
- if not for_date:
- for_date = nowdate()
- shift_type = frappe.get_doc("Shift Type", shift_type_name)
- start_datetime = datetime.combine(for_date, datetime.min.time()) + shift_type.start_time
- for_date = (
- for_date + timedelta(days=1) if shift_type.start_time > shift_type.end_time else for_date
- )
- end_datetime = datetime.combine(for_date, datetime.min.time()) + shift_type.end_time
- actual_start = start_datetime - timedelta(
- minutes=shift_type.begin_check_in_before_shift_start_time
- )
+ if not for_timestamp:
+ for_timestamp = now_datetime()
+
+ shift_type = frappe.get_doc('Shift Type', shift_type_name)
+ shift_actual_start = shift_type.start_time - timedelta(minutes=shift_type.begin_check_in_before_shift_start_time)
+
+ if shift_type.start_time > shift_type.end_time:
+ # shift spans accross 2 different days
+ if get_time(for_timestamp.time()) >= get_time(shift_actual_start):
+ # if for_timestamp is greater than start time, its in the first day
+ start_datetime = datetime.combine(for_timestamp, datetime.min.time()) + shift_type.start_time
+ for_timestamp = for_timestamp + timedelta(days=1)
+ end_datetime = datetime.combine(for_timestamp, datetime.min.time()) + shift_type.end_time
+ elif get_time(for_timestamp.time()) < get_time(shift_actual_start):
+ # if for_timestamp is less than start time, its in the second day
+ end_datetime = datetime.combine(for_timestamp, datetime.min.time()) + shift_type.end_time
+ for_timestamp = for_timestamp + timedelta(days=-1)
+ start_datetime = datetime.combine(for_timestamp, datetime.min.time()) + shift_type.start_time
+ else:
+ # start and end times fall on the same day
+ start_datetime = datetime.combine(for_timestamp, datetime.min.time()) + shift_type.start_time
+ end_datetime = datetime.combine(for_timestamp, datetime.min.time()) + shift_type.end_time
+
+ actual_start = start_datetime - timedelta(minutes=shift_type.begin_check_in_before_shift_start_time)
actual_end = end_datetime + timedelta(minutes=shift_type.allow_check_out_after_shift_end_time)
return frappe._dict({