feat: Assign shift for period or create ongoing assignment
This commit is contained in:
parent
e447c79f9d
commit
afa98bb39b
@ -10,9 +10,11 @@
|
|||||||
"employee",
|
"employee",
|
||||||
"employee_name",
|
"employee_name",
|
||||||
"shift_type",
|
"shift_type",
|
||||||
|
"status",
|
||||||
"column_break_3",
|
"column_break_3",
|
||||||
"company",
|
"company",
|
||||||
"date",
|
"start_date",
|
||||||
|
"end_date",
|
||||||
"shift_request",
|
"shift_request",
|
||||||
"department",
|
"department",
|
||||||
"amended_from"
|
"amended_from"
|
||||||
@ -59,12 +61,6 @@
|
|||||||
"options": "Company",
|
"options": "Company",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "date",
|
|
||||||
"fieldtype": "Date",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Date"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "shift_request",
|
"fieldname": "shift_request",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
@ -80,11 +76,36 @@
|
|||||||
"options": "Shift Assignment",
|
"options": "Shift Assignment",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "start_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Start Date",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 1,
|
||||||
|
"fieldname": "end_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "End Date",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 1,
|
||||||
|
"default": "Active",
|
||||||
|
"fieldname": "status",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Status",
|
||||||
|
"options": "Active\nInactive",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2019-12-12 15:49:06.956901",
|
"modified": "2020-06-15 14:27:54.310773",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Shift Assignment",
|
"name": "Shift Assignment",
|
||||||
|
@ -11,38 +11,64 @@ from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
|||||||
from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
|
from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
|
||||||
from datetime import timedelta, datetime
|
from datetime import timedelta, datetime
|
||||||
|
|
||||||
class OverlapError(frappe.ValidationError): pass
|
|
||||||
|
|
||||||
class ShiftAssignment(Document):
|
class ShiftAssignment(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_overlapping_dates()
|
self.validate_overlapping_dates()
|
||||||
|
|
||||||
|
if self.end_date and self.end_date <= self.start_date:
|
||||||
|
frappe.throw(_("End Date should not be less than Start Date"))
|
||||||
|
|
||||||
def validate_overlapping_dates(self):
|
def validate_overlapping_dates(self):
|
||||||
if not self.name:
|
if not self.name:
|
||||||
self.name = "New Shift Assignment"
|
self.name = "New Shift Assignment"
|
||||||
|
|
||||||
d = frappe.db.sql("""
|
condition = """and (
|
||||||
select
|
end_date is null
|
||||||
name, shift_type, date
|
or
|
||||||
from `tabShift Assignment`
|
%(start_date)s between start_date and end_date
|
||||||
where employee = %(employee)s and docstatus < 2
|
"""
|
||||||
and date = %(date)s
|
|
||||||
and name != %(name)s""", {
|
|
||||||
"employee": self.employee,
|
|
||||||
"shift_type": self.shift_type,
|
|
||||||
"date": self.date,
|
|
||||||
"name": self.name
|
|
||||||
}, as_dict = 1)
|
|
||||||
|
|
||||||
for date_overlap in d:
|
if self.end_date:
|
||||||
if date_overlap['name']:
|
condition += """ or
|
||||||
self.throw_overlap_error(date_overlap)
|
%(end_date)s between start_date and end_date
|
||||||
|
or
|
||||||
|
start_date between %(start_date)s and %(end_date)s
|
||||||
|
) """
|
||||||
|
else:
|
||||||
|
condition += """ ) """
|
||||||
|
|
||||||
def throw_overlap_error(self, d):
|
assigned_shifts = frappe.db.sql("""
|
||||||
msg = _("Employee {0} has already applied for {1} on {2} : ").format(self.employee,
|
select name, shift_type, start_date ,end_date, docstatus, status
|
||||||
d['shift_type'], formatdate(d['date'])) \
|
from `tabShift Assignment`
|
||||||
+ """ <b><a href="#Form/Shift Assignment/{0}">{0}</a></b>""".format(d["name"])
|
where
|
||||||
frappe.throw(msg, OverlapError)
|
employee=%(employee)s and docstatus < 2
|
||||||
|
and name != %(name)s
|
||||||
|
and status = "Active"
|
||||||
|
{0}
|
||||||
|
""".format(condition), {
|
||||||
|
"employee": self.employee,
|
||||||
|
"shift_type": self.shift_type,
|
||||||
|
"start_date": self.start_date,
|
||||||
|
"end_date": self.end_date,
|
||||||
|
"name": self.name
|
||||||
|
}, as_dict = 1)
|
||||||
|
|
||||||
|
for shift in assigned_shifts:
|
||||||
|
if shift.name:
|
||||||
|
self.throw_overlap_error(shift)
|
||||||
|
|
||||||
|
def throw_overlap_error(self, shift_details):
|
||||||
|
shift_details = frappe._dict(shift_details)
|
||||||
|
if shift_details.docstatus == 0:
|
||||||
|
msg = _("Employee {0} has already applied for {1}: {2}").format(self.employee, self.shift_type, shift_details.name)
|
||||||
|
if shift_details.docstatus == 1 and shift_details.status == "Active":
|
||||||
|
msg = _("Employee {0} already have Active Shift {1}: {2}").format(self.employee, self.shift_type, shift_details.name)
|
||||||
|
if shift_details.start_date:
|
||||||
|
msg += _(" from {0}").format(getdate(self.start_date).strftime("%d-%m-%Y"))
|
||||||
|
if shift_details.end_date:
|
||||||
|
msg += _(" to {0}").format(getdate(self.end_date).strftime("%d-%m-%Y"))
|
||||||
|
if msg:
|
||||||
|
frappe.throw(msg)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_events(start, end, filters=None):
|
def get_events(start, end, filters=None):
|
||||||
@ -62,19 +88,22 @@ def get_events(start, end, filters=None):
|
|||||||
return events
|
return events
|
||||||
|
|
||||||
def add_assignments(events, start, end, conditions=None):
|
def add_assignments(events, start, end, conditions=None):
|
||||||
query = """select name, date, employee_name,
|
query = """select name, start_date, end_date, employee_name,
|
||||||
employee, docstatus
|
employee, docstatus
|
||||||
from `tabShift Assignment` where
|
from `tabShift Assignment` where
|
||||||
date <= %(date)s
|
start_date >= %(start_date)s
|
||||||
and docstatus < 2"""
|
and docstatus < 2""".format()
|
||||||
if conditions:
|
if conditions:
|
||||||
query += conditions
|
query += conditions
|
||||||
|
|
||||||
for d in frappe.db.sql(query, {"date":start, "date":end}, as_dict=True):
|
for d in frappe.db.sql(query, {"start_date":start}, as_dict=True):
|
||||||
|
from pprint import pprint
|
||||||
|
pprint(d)
|
||||||
e = {
|
e = {
|
||||||
"name": d.name,
|
"name": d.name,
|
||||||
"doctype": "Shift Assignment",
|
"doctype": "Shift Assignment",
|
||||||
"date": d.date,
|
"start_date": d.start_date,
|
||||||
|
"end_date": d.end_date if d.end_date else nowdate(),
|
||||||
"title": cstr(d.employee_name) + \
|
"title": cstr(d.employee_name) + \
|
||||||
cstr(d.shift_type),
|
cstr(d.shift_type),
|
||||||
"docstatus": d.docstatus
|
"docstatus": d.docstatus
|
||||||
@ -92,7 +121,14 @@ def get_employee_shift(employee, for_date=nowdate(), consider_default_shift=Fals
|
|||||||
:param next_shift_direction: One of: None, 'forward', 'reverse'. Direction to look for next shift if shift not found on given date.
|
:param next_shift_direction: One of: None, 'forward', 'reverse'. Direction to look for next shift if shift not found on given date.
|
||||||
"""
|
"""
|
||||||
default_shift = frappe.db.get_value('Employee', employee, 'default_shift')
|
default_shift = frappe.db.get_value('Employee', employee, 'default_shift')
|
||||||
shift_type_name = frappe.db.get_value('Shift Assignment', {'employee':employee, 'date': for_date, 'docstatus': '1'}, 'shift_type')
|
shift_assignment_details = frappe.db.get_value('Shift Assignment', {'employee':employee, 'start_date':('<=', for_date), 'docstatus': '1', 'status': "Active"}, ['shift_type', 'end_date'])
|
||||||
|
|
||||||
|
shift_type_name = shift_assignment_details[0]
|
||||||
|
|
||||||
|
# if end_date present means that shift is over after end_date else it is a ongoing shift.
|
||||||
|
if shift_assignment_details[1] and for_date >= shift_assignment_details[1] :
|
||||||
|
shift_type_name = None
|
||||||
|
|
||||||
if not shift_type_name and consider_default_shift:
|
if not shift_type_name and consider_default_shift:
|
||||||
shift_type_name = default_shift
|
shift_type_name = default_shift
|
||||||
if shift_type_name:
|
if shift_type_name:
|
||||||
@ -117,12 +153,13 @@ def get_employee_shift(employee, for_date=nowdate(), consider_default_shift=Fals
|
|||||||
direction = '<' if next_shift_direction == 'reverse' else '>'
|
direction = '<' if next_shift_direction == 'reverse' else '>'
|
||||||
sort_order = 'desc' if next_shift_direction == 'reverse' else 'asc'
|
sort_order = 'desc' if next_shift_direction == 'reverse' else 'asc'
|
||||||
dates = frappe.db.get_all('Shift Assignment',
|
dates = frappe.db.get_all('Shift Assignment',
|
||||||
'date',
|
'start_date',
|
||||||
{'employee':employee, 'date':(direction, for_date), 'docstatus': '1'},
|
{'employee':employee, 'start_date':(direction, for_date), 'docstatus': '1', "status": "Active"},
|
||||||
as_list=True,
|
as_list=True,
|
||||||
limit=MAX_DAYS, order_by="date "+sort_order)
|
limit=MAX_DAYS, order_by="date "+sort_order)
|
||||||
|
|
||||||
for date in dates:
|
for date in dates:
|
||||||
shift_details = get_employee_shift(employee, date[0], consider_default_shift, None)
|
shift_details = get_employee_shift(employee, date.start_date, consider_default_shift, None)
|
||||||
if shift_details:
|
if shift_details:
|
||||||
shift_type_name = shift_details.shift_type.name
|
shift_type_name = shift_details.shift_type.name
|
||||||
for_date = date[0]
|
for_date = date[0]
|
||||||
@ -134,7 +171,7 @@ def get_employee_shift(employee, for_date=nowdate(), consider_default_shift=Fals
|
|||||||
def get_employee_shift_timings(employee, for_timestamp=now_datetime(), consider_default_shift=False):
|
def get_employee_shift_timings(employee, for_timestamp=now_datetime(), consider_default_shift=False):
|
||||||
"""Returns previous shift, current/upcoming shift, next_shift for the given timestamp and employee
|
"""Returns previous shift, current/upcoming shift, next_shift for the given timestamp and employee
|
||||||
"""
|
"""
|
||||||
# write and verify a test case for midnight shift.
|
# write and verify a test case for midnight shift.
|
||||||
prev_shift = curr_shift = next_shift = None
|
prev_shift = curr_shift = next_shift = None
|
||||||
curr_shift = get_employee_shift(employee, for_timestamp.date(), consider_default_shift, 'forward')
|
curr_shift = get_employee_shift(employee, for_timestamp.date(), consider_default_shift, 'forward')
|
||||||
if curr_shift:
|
if curr_shift:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user