fix(auto attendance): changes as requested in review
> removed unused field from shift assignment > renamed 'biometric_rf_id' to 'attendance_device_id' > added more test cases > few other minor changes after demo
This commit is contained in:
		
							parent
							
								
									23cedfd6c8
								
							
						
					
					
						commit
						66e459b35d
					
				| @ -4,8 +4,17 @@ from __future__ import unicode_literals | ||||
| 
 | ||||
| import frappe | ||||
| import unittest | ||||
| from frappe.utils import nowdate | ||||
| 
 | ||||
| test_records = frappe.get_test_records('Attendance') | ||||
| 
 | ||||
| class TestAttendance(unittest.TestCase): | ||||
| 	pass | ||||
| 	def test_mark_absent(self): | ||||
| 		from erpnext.hr.doctype.employee.test_employee import make_employee | ||||
| 		employee = make_employee("test_mark_absent@example.com") | ||||
| 		date = nowdate() | ||||
| 		frappe.db.delete('Attendance', {'employee':employee, 'attendance_date':date}) | ||||
| 		from erpnext.hr.doctype.attendance.attendance import mark_absent | ||||
| 		attendance = mark_absent(employee, date) | ||||
| 		fetch_attendance = frappe.get_value('Attendance', {'employee':employee, 'attendance_date':date, 'status':'Absent'}) | ||||
| 		self.assertEqual(attendance, fetch_attendance) | ||||
|  | ||||
| @ -16,7 +16,7 @@ | ||||
|   "middle_name", | ||||
|   "last_name", | ||||
|   "employee_name", | ||||
|   "biometric_rf_id", | ||||
|   "attendance_device_id", | ||||
|   "image", | ||||
|   "column_break1", | ||||
|   "company", | ||||
| @ -511,6 +511,7 @@ | ||||
|    "options": "Email" | ||||
|   }, | ||||
|   { | ||||
|    "default": "0", | ||||
|    "fieldname": "unsubscribed", | ||||
|    "fieldtype": "Check", | ||||
|    "label": "Unsubscribed" | ||||
| @ -749,9 +750,9 @@ | ||||
|    "label": "Old Parent" | ||||
|   }, | ||||
|   { | ||||
|    "fieldname": "biometric_rf_id", | ||||
|    "fieldname": "attendance_device_id", | ||||
|    "fieldtype": "Data", | ||||
|    "label": "Biometric/RF tag ID ", | ||||
|    "label": "Attendance Device ID (Biometric/RF tag ID)", | ||||
|    "no_copy": 1, | ||||
|    "unique": 1 | ||||
|   } | ||||
| @ -759,7 +760,7 @@ | ||||
|  "icon": "fa fa-user", | ||||
|  "idx": 24, | ||||
|  "image_field": "image", | ||||
|  "modified": "2019-05-08 14:32:06.443825", | ||||
|  "modified": "2019-05-29 17:33:11.988538", | ||||
|  "modified_by": "Administrator", | ||||
|  "module": "HR", | ||||
|  "name": "Employee", | ||||
|  | ||||
| @ -319,12 +319,12 @@ def get_holiday_list_for_employee(employee, raise_exception=True): | ||||
| 
 | ||||
| 	return holiday_list | ||||
| 
 | ||||
| def is_holiday(employee, date=None): | ||||
| def is_holiday(employee, date=None, raise_exception=True): | ||||
| 	'''Returns True if given Employee has an holiday on the given date | ||||
| 	:param employee: Employee `name` | ||||
| 	:param date: Date to check. Will check for today if None''' | ||||
| 
 | ||||
| 	holiday_list = get_holiday_list_for_employee(employee) | ||||
| 	holiday_list = get_holiday_list_for_employee(employee, raise_exception) | ||||
| 	if not date: | ||||
| 		date = today() | ||||
| 
 | ||||
|  | ||||
| @ -15,23 +15,24 @@ class EmployeeAttendanceLog(Document): | ||||
| 
 | ||||
| 
 | ||||
| @frappe.whitelist() | ||||
| def add_log_based_on_biometric_rf_id(biometric_rf_id, timestamp, device_id=None, log_type=None): | ||||
| 	"""Finds the relevant Employee using the biometric_rf_id and creates a Employee Attendance Log. | ||||
| def add_log_based_on_employee_field(employee_field_value, timestamp, device_id=None, log_type=None, employee_fieldname='attendance_device_id'): | ||||
| 	"""Finds the relevant Employee using the employee field value and creates a Employee Attendance Log. | ||||
| 
 | ||||
| 	:param biometric_rf_id: The Biometric/RF tag ID as set up in Employee DocType. | ||||
| 	:param employee_field_value: The value to look for in employee field. | ||||
| 	:param timestamp: The timestamp of the Log. Currently expected in the following format as string: '2019-05-08 10:48:08.000000' | ||||
| 	:param device_id: (optional)Location / Device ID. A short string is expected. | ||||
| 	:param log_type: (optional)Direction of the Punch if available (IN/OUT). | ||||
| 	:param employee_fieldname: (Default: attendance_device_id)Name of the field in Employee DocType based on which employee lookup will happen. | ||||
| 	""" | ||||
| 
 | ||||
| 	if not biometric_rf_id or not timestamp: | ||||
| 		frappe.throw(_("'biometric_rf_id' and 'timestamp' are required.")) | ||||
| 	if not employee_field_value or not timestamp: | ||||
| 		frappe.throw(_("'employee_field_value' and 'timestamp' are required.")) | ||||
| 
 | ||||
| 	employee = frappe.db.get_values("Employee", {"biometric_rf_id": biometric_rf_id}, ["name", "employee_name", "biometric_rf_id"], as_dict=True) | ||||
| 	employee = frappe.db.get_values("Employee", {employee_fieldname: employee_field_value}, ["name", "employee_name", employee_fieldname], as_dict=True) | ||||
| 	if employee: | ||||
| 		employee = employee[0] | ||||
| 	else: | ||||
| 		frappe.throw(_("No Employee found for the given 'biometric_rf_id':{}.").format(biometric_rf_id)) | ||||
| 		frappe.throw(_("No Employee found for the given employee field value. '{}': {}").format(employee_fieldname,employee_field_value)) | ||||
| 
 | ||||
| 	doc = frappe.new_doc("Employee Attendance Log") | ||||
| 	doc.employee = employee.name | ||||
| @ -40,7 +41,6 @@ def add_log_based_on_biometric_rf_id(biometric_rf_id, timestamp, device_id=None, | ||||
| 	doc.device_id = device_id | ||||
| 	doc.log_type = log_type | ||||
| 	doc.insert() | ||||
| 	frappe.db.commit() | ||||
| 	 | ||||
| 	return doc | ||||
| 
 | ||||
|  | ||||
| @ -8,18 +8,18 @@ from frappe.utils import now_datetime, nowdate, to_timedelta | ||||
| import unittest | ||||
| from datetime import timedelta | ||||
| 
 | ||||
| from erpnext.hr.doctype.employee_attendance_log.employee_attendance_log import add_log_based_on_biometric_rf_id, mark_attendance_and_link_log | ||||
| from erpnext.hr.doctype.employee_attendance_log.employee_attendance_log import add_log_based_on_employee_field, mark_attendance_and_link_log | ||||
| from erpnext.hr.doctype.employee.test_employee import make_employee | ||||
| 
 | ||||
| class TestEmployeeAttendanceLog(unittest.TestCase): | ||||
| 	def test_add_log_based_on_biometric_rf_id(self): | ||||
| 		employee = make_employee("test_add_log_based_on_biometric_rf_id@example.com") | ||||
| 	def test_add_log_based_on_employee_field(self): | ||||
| 		employee = make_employee("test_add_log_based_on_employee_field@example.com") | ||||
| 		employee = frappe.get_doc("Employee", employee) | ||||
| 		employee.biometric_rf_id = '3344' | ||||
| 		employee.attendance_device_id = '3344' | ||||
| 		employee.save() | ||||
| 
 | ||||
| 		time_now = now_datetime().__str__()[:-7] | ||||
| 		employee_attendance_log = add_log_based_on_biometric_rf_id('3344', time_now, 'mumbai_first_floor', 'IN') | ||||
| 		employee_attendance_log = add_log_based_on_employee_field('3344', time_now, 'mumbai_first_floor', 'IN') | ||||
| 		self.assertEqual(employee_attendance_log.employee, employee.name) | ||||
| 		self.assertEqual(employee_attendance_log.time, time_now) | ||||
| 		self.assertEqual(employee_attendance_log.device_id, 'mumbai_first_floor') | ||||
|  | ||||
| @ -84,26 +84,3 @@ def get_events(start, end, filters=None): | ||||
| 		fields=['name', '`tabHoliday`.holiday_date', '`tabHoliday`.description', '`tabHoliday List`.color'], | ||||
| 		filters = filters, | ||||
| 		update={"allDay": 1}) | ||||
| 
 | ||||
| def get_holiday_list(employee): | ||||
| 	employee_holiday = frappe.db.get_all('Employee', fields=['name', 'holiday_list', 'company'], filters={'name':employee}) | ||||
| 	if not employee_holiday: | ||||
| 		frappe.throw(_("Employee not found.")) | ||||
| 	if employee_holiday[0].holiday_list: | ||||
| 		return employee_holiday[0].holiday_list | ||||
| 	else: | ||||
| 		company_holiday = frappe.db.get_all('Company', fields=['name', 'default_holiday_list'], filters={'name':employee_holiday[0].company}) | ||||
| 		if company_holiday[0].default_holiday_list: | ||||
| 			return company_holiday[0].default_holiday_list | ||||
| 	return None | ||||
| 
 | ||||
| def is_holiday(holiday_list, for_date): | ||||
| 	"""Returns true if the given date is a holiday in the given holiday list | ||||
| 	""" | ||||
| 	holiday = frappe.get_value('Holiday', { | ||||
| 		'parent': holiday_list, | ||||
| 		'parentfield': 'holidays', | ||||
| 		'parenttype': 'Holiday List', | ||||
| 		'holiday_date': for_date | ||||
| 		}, 'name') | ||||
| 	return bool(holiday) | ||||
| @ -6,39 +6,27 @@ import frappe | ||||
| import unittest | ||||
| from frappe.utils import getdate | ||||
| from datetime import timedelta | ||||
| from erpnext.hr.doctype.employee.test_employee import make_employee | ||||
| 
 | ||||
| 
 | ||||
| class TestHolidayList(unittest.TestCase): | ||||
| 	def test_get_holiday_list(self): | ||||
| 		holiday_list = make_holiday_list("test_get_holiday_list") | ||||
| 		employee = make_employee("test_get_holiday_list@example.com") | ||||
| 		employee = frappe.get_doc("Employee", employee) | ||||
| 		employee.holiday_list = None | ||||
| 		employee.save() | ||||
| 		company = frappe.get_doc("Company", employee.company) | ||||
| 		company_default_holiday_list = company.default_holiday_list | ||||
| 
 | ||||
| 		from erpnext.hr.doctype.holiday_list.holiday_list import get_holiday_list | ||||
| 		holiday_list_name = get_holiday_list(employee.name) | ||||
| 		self.assertEqual(holiday_list_name, company_default_holiday_list) | ||||
| 
 | ||||
| 		employee.holiday_list = holiday_list.name | ||||
| 		employee.save() | ||||
| 		holiday_list_name = get_holiday_list(employee.name) | ||||
| 		self.assertEqual(holiday_list_name, holiday_list.name) | ||||
| 
 | ||||
| 	def test_holiday_list(self): | ||||
| 		today_date = getdate() | ||||
| 		test_holiday_dates = [today_date-timedelta(days=5), today_date-timedelta(days=4)] | ||||
| 		holiday_list = make_holiday_list("test_is_holiday", | ||||
| 			holiday_dates=[ | ||||
| 				{'holiday_date': test_holiday_dates[0], 'description': 'test holiday'}, | ||||
| 				{'holiday_date': test_holiday_dates[1], 'description': 'test holiday2'} | ||||
| 			]) | ||||
| 		fetched_holiday_list = frappe.get_value('Holiday List', holiday_list.name) | ||||
| 		self.assertEqual(holiday_list.name, fetched_holiday_list) | ||||
| 
 | ||||
| def make_holiday_list(name, from_date=getdate()-timedelta(days=10), to_date=getdate(), holiday_dates=None): | ||||
| 	if not frappe.db.get_value("Holiday List", name): | ||||
| 		doc = frappe.get_doc({ | ||||
| 			"doctype": "Holiday List", | ||||
| 			"holiday_list_name": name, | ||||
| 			"from_date" : from_date, | ||||
| 			"to_date" : to_date | ||||
| 			}).insert() | ||||
| 		doc.holidays = holiday_dates | ||||
| 		doc.save() | ||||
| 	else: | ||||
| 		doc = frappe.get_doc("Holiday List", name) | ||||
| 	frappe.delete_doc_if_exists("Holiday List", name, force=1) | ||||
| 	doc = frappe.get_doc({ | ||||
| 		"doctype": "Holiday List", | ||||
| 		"holiday_list_name": name, | ||||
| 		"from_date" : from_date, | ||||
| 		"to_date" : to_date, | ||||
| 		"holidays" : holiday_dates | ||||
| 		}).insert() | ||||
| 	return doc | ||||
|  | ||||
| @ -12,7 +12,7 @@ from frappe import _ | ||||
| from frappe.model.document import Document | ||||
| from erpnext.hr.doctype.shift_assignment.shift_assignment import get_employee_shift_timings, get_employee_shift | ||||
| from erpnext.hr.doctype.employee_attendance_log.employee_attendance_log import mark_attendance_and_link_log | ||||
| from erpnext.hr.doctype.holiday_list.holiday_list import get_holiday_list | ||||
| from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee | ||||
| from erpnext.hr.doctype.attendance.attendance import mark_absent | ||||
| 
 | ||||
| class HRSettings(Document): | ||||
| @ -79,9 +79,9 @@ def process_single_employee_logs(logs, hr_settings=None): | ||||
| 		while logs and logs[0].time <= actual_shift_end: | ||||
| 			single_shift_logs.append(logs.pop(0)) | ||||
| 		process_single_employee_shift_logs(single_shift_logs, shift_details) | ||||
| 	mark_absent_for_days_with_no_attendance(last_log.employee, employee_last_sync, hr_settings) | ||||
| 	mark_absent_for_dates_with_no_attendance(last_log.employee, employee_last_sync, hr_settings) | ||||
| 
 | ||||
| def mark_absent_for_days_with_no_attendance(employee, employee_last_sync, hr_settings=None): | ||||
| def mark_absent_for_dates_with_no_attendance(employee, employee_last_sync, hr_settings=None): | ||||
| 	"""Marks Absents for the given employee on working days which have no attendance marked.  | ||||
| 	The Absent is marked starting from one shift before the employee_last_sync  | ||||
| 	going back to 'hr_settings.process_attendance_after' or employee creation date. | ||||
| @ -100,7 +100,7 @@ def mark_absent_for_days_with_no_attendance(employee, employee_last_sync, hr_set | ||||
| 	if prev_shift: | ||||
| 		end_date = prev_shift.start_datetime.date() | ||||
| 	elif hr_settings.attendance_for_employee_without_shift == 'At least one Employee Attendance Log per day as present': | ||||
| 		for date in get_filtered_date_list(employee, "All Dates", start_date, employee_last_sync.date(), True, get_holiday_list(employee)): | ||||
| 		for date in get_filtered_date_list(employee, "All Dates", start_date, employee_last_sync.date(), True, get_holiday_list_for_employee(employee, False)): | ||||
| 			mark_absent(employee, date) | ||||
| 		return | ||||
| 	else: | ||||
| @ -111,7 +111,7 @@ def mark_absent_for_days_with_no_attendance(employee, employee_last_sync, hr_set | ||||
| 			if get_employee_shift(employee, date, consider_default_shift): | ||||
| 				mark_absent(employee, date) | ||||
| 	elif hr_settings.attendance_for_employee_without_shift == 'At least one Employee Attendance Log per day as present': | ||||
| 		for date in get_filtered_date_list(employee, "All Dates", start_date, employee_last_sync.date(), True, get_holiday_list(employee)): | ||||
| 		for date in get_filtered_date_list(employee, "All Dates", start_date, employee_last_sync.date(), True, get_holiday_list_for_employee(employee, False)): | ||||
| 			mark_absent(employee, date) | ||||
| 	else: | ||||
| 		for date in get_filtered_date_list(employee, "Assigned Shifts", start_date, end_date): | ||||
| @ -155,9 +155,12 @@ def get_filtered_date_list(employee, base_dates_set, start_date, end_date, filte | ||||
| def process_single_employee_shift_logs(logs, shift_details): | ||||
| 	"""Mark Attendance for a set of logs belonging to a single shift. | ||||
| 	Assumtion:  | ||||
| 		1. These logs belongs to an single shift, single employee and is not in a holiday shift. | ||||
| 		1. These logs belongs to an single shift, single employee and is not in a holiday date. | ||||
| 		2. Logs are in chronological order | ||||
| 	""" | ||||
| 	if shift_details.shift_type.enable_auto_attendance: | ||||
| 		mark_attendance_and_link_log(logs, 'Skip', None) | ||||
| 		return | ||||
| 	check_in_out_type = shift_details.shift_type.determine_check_in_and_check_out | ||||
| 	working_hours_calc_type = shift_details.shift_type.working_hours_calculation_based_on | ||||
| 	total_working_hours = calculate_working_hours(logs, check_in_out_type, working_hours_calc_type) | ||||
|  | ||||
| @ -1,429 +1,132 @@ | ||||
| { | ||||
|  "allow_copy": 0,  | ||||
|  "allow_guest_to_view": 0,  | ||||
|  "allow_import": 1,  | ||||
|  "allow_rename": 0,  | ||||
|  "autoname": "HR-SHA-.YY.-.MM.-.#####",  | ||||
|  "beta": 0,  | ||||
|  "creation": "2018-04-13 16:25:04.562730",  | ||||
|  "custom": 0,  | ||||
|  "docstatus": 0,  | ||||
|  "doctype": "DocType",  | ||||
|  "document_type": "",  | ||||
|  "editable_grid": 1,  | ||||
|  "engine": "InnoDB",  | ||||
|  "allow_import": 1, | ||||
|  "autoname": "HR-SHA-.YY.-.MM.-.#####", | ||||
|  "creation": "2018-04-13 16:25:04.562730", | ||||
|  "doctype": "DocType", | ||||
|  "editable_grid": 1, | ||||
|  "engine": "InnoDB", | ||||
|  "field_order": [ | ||||
|   "employee", | ||||
|   "employee_name", | ||||
|   "department", | ||||
|   "shift_type", | ||||
|   "column_break_3", | ||||
|   "company", | ||||
|   "date", | ||||
|   "shift_request", | ||||
|   "amended_from" | ||||
|  ], | ||||
|  "fields": [ | ||||
|   { | ||||
|    "allow_bulk_edit": 0,  | ||||
|    "allow_in_quick_entry": 0,  | ||||
|    "allow_on_submit": 0,  | ||||
|    "bold": 0,  | ||||
|    "collapsible": 0,  | ||||
|    "columns": 0,  | ||||
|    "fieldname": "employee",  | ||||
|    "fieldtype": "Link",  | ||||
|    "hidden": 0,  | ||||
|    "ignore_user_permissions": 0,  | ||||
|    "ignore_xss_filter": 0,  | ||||
|    "in_filter": 0,  | ||||
|    "in_global_search": 0,  | ||||
|    "in_list_view": 0,  | ||||
|    "in_standard_filter": 0,  | ||||
|    "label": "Employee",  | ||||
|    "length": 0,  | ||||
|    "no_copy": 0,  | ||||
|    "options": "Employee",  | ||||
|    "permlevel": 0,  | ||||
|    "precision": "",  | ||||
|    "print_hide": 0,  | ||||
|    "print_hide_if_no_value": 0,  | ||||
|    "read_only": 0,  | ||||
|    "remember_last_selected_value": 0,  | ||||
|    "report_hide": 0,  | ||||
|    "reqd": 1,  | ||||
|    "search_index": 0,  | ||||
|    "set_only_once": 0,  | ||||
|    "translatable": 0,  | ||||
|    "unique": 0 | ||||
|   },  | ||||
|    "fieldname": "employee", | ||||
|    "fieldtype": "Link", | ||||
|    "label": "Employee", | ||||
|    "options": "Employee", | ||||
|    "reqd": 1 | ||||
|   }, | ||||
|   { | ||||
|    "allow_bulk_edit": 0,  | ||||
|    "allow_in_quick_entry": 0,  | ||||
|    "allow_on_submit": 0,  | ||||
|    "bold": 0,  | ||||
|    "collapsible": 0,  | ||||
|    "columns": 0,  | ||||
|    "fetch_from": "employee.employee_name",  | ||||
|    "fieldname": "employee_name",  | ||||
|    "fieldtype": "Data",  | ||||
|    "hidden": 0,  | ||||
|    "ignore_user_permissions": 0,  | ||||
|    "ignore_xss_filter": 0,  | ||||
|    "in_filter": 0,  | ||||
|    "in_global_search": 0,  | ||||
|    "in_list_view": 0,  | ||||
|    "in_standard_filter": 0,  | ||||
|    "label": "Employee Name",  | ||||
|    "length": 0,  | ||||
|    "no_copy": 0,  | ||||
|    "options": "",  | ||||
|    "permlevel": 0,  | ||||
|    "precision": "",  | ||||
|    "print_hide": 0,  | ||||
|    "print_hide_if_no_value": 0,  | ||||
|    "read_only": 1,  | ||||
|    "remember_last_selected_value": 0,  | ||||
|    "report_hide": 0,  | ||||
|    "reqd": 0,  | ||||
|    "search_index": 0,  | ||||
|    "set_only_once": 0,  | ||||
|    "translatable": 0,  | ||||
|    "unique": 0 | ||||
|   },  | ||||
|    "fetch_from": "employee.employee_name", | ||||
|    "fieldname": "employee_name", | ||||
|    "fieldtype": "Data", | ||||
|    "label": "Employee Name", | ||||
|    "read_only": 1 | ||||
|   }, | ||||
|   { | ||||
|    "allow_bulk_edit": 0,  | ||||
|    "allow_in_quick_entry": 0,  | ||||
|    "allow_on_submit": 0,  | ||||
|    "bold": 0,  | ||||
|    "collapsible": 0,  | ||||
|    "columns": 0,  | ||||
|    "fetch_from": "employee.department",  | ||||
|    "fieldname": "department",  | ||||
|    "fieldtype": "Link",  | ||||
|    "hidden": 0,  | ||||
|    "ignore_user_permissions": 0,  | ||||
|    "ignore_xss_filter": 0,  | ||||
|    "in_filter": 0,  | ||||
|    "in_global_search": 0,  | ||||
|    "in_list_view": 0,  | ||||
|    "in_standard_filter": 0,  | ||||
|    "label": "Department",  | ||||
|    "length": 0,  | ||||
|    "no_copy": 0,  | ||||
|    "options": "Department",  | ||||
|    "permlevel": 0,  | ||||
|    "precision": "",  | ||||
|    "print_hide": 0,  | ||||
|    "print_hide_if_no_value": 0,  | ||||
|    "read_only": 1,  | ||||
|    "remember_last_selected_value": 0,  | ||||
|    "report_hide": 0,  | ||||
|    "reqd": 0,  | ||||
|    "search_index": 0,  | ||||
|    "set_only_once": 0,  | ||||
|    "translatable": 0,  | ||||
|    "unique": 0 | ||||
|   },  | ||||
|    "fetch_from": "employee.department", | ||||
|    "fieldname": "department", | ||||
|    "fieldtype": "Link", | ||||
|    "label": "Department", | ||||
|    "options": "Department", | ||||
|    "read_only": 1 | ||||
|   }, | ||||
|   { | ||||
|    "allow_bulk_edit": 0,  | ||||
|    "allow_in_quick_entry": 0,  | ||||
|    "allow_on_submit": 0,  | ||||
|    "bold": 0,  | ||||
|    "collapsible": 0,  | ||||
|    "columns": 0,  | ||||
|    "fieldname": "shift_type",  | ||||
|    "fieldtype": "Link",  | ||||
|    "hidden": 0,  | ||||
|    "ignore_user_permissions": 0,  | ||||
|    "ignore_xss_filter": 0,  | ||||
|    "in_filter": 0,  | ||||
|    "in_global_search": 0,  | ||||
|    "in_list_view": 1,  | ||||
|    "in_standard_filter": 0,  | ||||
|    "label": "Shift Type",  | ||||
|    "length": 0,  | ||||
|    "no_copy": 0,  | ||||
|    "options": "Shift Type",  | ||||
|    "permlevel": 0,  | ||||
|    "precision": "",  | ||||
|    "print_hide": 0,  | ||||
|    "print_hide_if_no_value": 0,  | ||||
|    "read_only": 0,  | ||||
|    "remember_last_selected_value": 0,  | ||||
|    "report_hide": 0,  | ||||
|    "reqd": 1,  | ||||
|    "search_index": 0,  | ||||
|    "set_only_once": 0,  | ||||
|    "translatable": 0,  | ||||
|    "unique": 0 | ||||
|   },  | ||||
|    "fieldname": "shift_type", | ||||
|    "fieldtype": "Link", | ||||
|    "in_list_view": 1, | ||||
|    "label": "Shift Type", | ||||
|    "options": "Shift Type", | ||||
|    "reqd": 1 | ||||
|   }, | ||||
|   { | ||||
|    "allow_bulk_edit": 0,  | ||||
|    "allow_in_quick_entry": 0,  | ||||
|    "allow_on_submit": 0,  | ||||
|    "bold": 0,  | ||||
|    "collapsible": 0,  | ||||
|    "columns": 0,  | ||||
|    "fieldname": "present",  | ||||
|    "fieldtype": "Check",  | ||||
|    "hidden": 0,  | ||||
|    "ignore_user_permissions": 0,  | ||||
|    "ignore_xss_filter": 0,  | ||||
|    "in_filter": 0,  | ||||
|    "in_global_search": 0,  | ||||
|    "in_list_view": 0,  | ||||
|    "in_standard_filter": 0,  | ||||
|    "label": "Present",  | ||||
|    "length": 0,  | ||||
|    "no_copy": 0,  | ||||
|    "permlevel": 0,  | ||||
|    "precision": "",  | ||||
|    "print_hide": 0,  | ||||
|    "print_hide_if_no_value": 0,  | ||||
|    "read_only": 0,  | ||||
|    "remember_last_selected_value": 0,  | ||||
|    "report_hide": 0,  | ||||
|    "reqd": 0,  | ||||
|    "search_index": 0,  | ||||
|    "set_only_once": 0,  | ||||
|    "translatable": 0,  | ||||
|    "unique": 0 | ||||
|   },  | ||||
|    "fieldname": "column_break_3", | ||||
|    "fieldtype": "Column Break" | ||||
|   }, | ||||
|   { | ||||
|    "allow_bulk_edit": 0,  | ||||
|    "allow_in_quick_entry": 0,  | ||||
|    "allow_on_submit": 0,  | ||||
|    "bold": 0,  | ||||
|    "collapsible": 0,  | ||||
|    "columns": 0,  | ||||
|    "fieldname": "column_break_3",  | ||||
|    "fieldtype": "Column Break",  | ||||
|    "hidden": 0,  | ||||
|    "ignore_user_permissions": 0,  | ||||
|    "ignore_xss_filter": 0,  | ||||
|    "in_filter": 0,  | ||||
|    "in_global_search": 0,  | ||||
|    "in_list_view": 0,  | ||||
|    "in_standard_filter": 0,  | ||||
|    "length": 0,  | ||||
|    "no_copy": 0,  | ||||
|    "permlevel": 0,  | ||||
|    "precision": "",  | ||||
|    "print_hide": 0,  | ||||
|    "print_hide_if_no_value": 0,  | ||||
|    "read_only": 0,  | ||||
|    "remember_last_selected_value": 0,  | ||||
|    "report_hide": 0,  | ||||
|    "reqd": 0,  | ||||
|    "search_index": 0,  | ||||
|    "set_only_once": 0,  | ||||
|    "translatable": 0,  | ||||
|    "unique": 0 | ||||
|   },  | ||||
|    "fieldname": "company", | ||||
|    "fieldtype": "Link", | ||||
|    "label": "Company", | ||||
|    "options": "Company", | ||||
|    "reqd": 1 | ||||
|   }, | ||||
|   { | ||||
|    "allow_bulk_edit": 0,  | ||||
|    "allow_in_quick_entry": 0,  | ||||
|    "allow_on_submit": 0,  | ||||
|    "bold": 0,  | ||||
|    "collapsible": 0,  | ||||
|    "columns": 0,  | ||||
|    "fieldname": "company",  | ||||
|    "fieldtype": "Link",  | ||||
|    "hidden": 0,  | ||||
|    "ignore_user_permissions": 0,  | ||||
|    "ignore_xss_filter": 0,  | ||||
|    "in_filter": 0,  | ||||
|    "in_global_search": 0,  | ||||
|    "in_list_view": 0,  | ||||
|    "in_standard_filter": 0,  | ||||
|    "label": "Company",  | ||||
|    "length": 0,  | ||||
|    "no_copy": 0,  | ||||
|    "options": "Company",  | ||||
|    "permlevel": 0,  | ||||
|    "precision": "",  | ||||
|    "print_hide": 0,  | ||||
|    "print_hide_if_no_value": 0,  | ||||
|    "read_only": 0,  | ||||
|    "remember_last_selected_value": 0,  | ||||
|    "report_hide": 0,  | ||||
|    "reqd": 1,  | ||||
|    "search_index": 0,  | ||||
|    "set_only_once": 0,  | ||||
|    "translatable": 0,  | ||||
|    "unique": 0 | ||||
|   },  | ||||
|    "fieldname": "date", | ||||
|    "fieldtype": "Date", | ||||
|    "in_list_view": 1, | ||||
|    "label": "Date" | ||||
|   }, | ||||
|   { | ||||
|    "allow_bulk_edit": 0,  | ||||
|    "allow_in_quick_entry": 0,  | ||||
|    "allow_on_submit": 0,  | ||||
|    "bold": 0,  | ||||
|    "collapsible": 0,  | ||||
|    "columns": 0,  | ||||
|    "fieldname": "date",  | ||||
|    "fieldtype": "Date",  | ||||
|    "hidden": 0,  | ||||
|    "ignore_user_permissions": 0,  | ||||
|    "ignore_xss_filter": 0,  | ||||
|    "in_filter": 0,  | ||||
|    "in_global_search": 0,  | ||||
|    "in_list_view": 1,  | ||||
|    "in_standard_filter": 0,  | ||||
|    "label": "Date",  | ||||
|    "length": 0,  | ||||
|    "no_copy": 0,  | ||||
|    "permlevel": 0,  | ||||
|    "precision": "",  | ||||
|    "print_hide": 0,  | ||||
|    "print_hide_if_no_value": 0,  | ||||
|    "read_only": 0,  | ||||
|    "remember_last_selected_value": 0,  | ||||
|    "report_hide": 0,  | ||||
|    "reqd": 0,  | ||||
|    "search_index": 0,  | ||||
|    "set_only_once": 0,  | ||||
|    "translatable": 0,  | ||||
|    "unique": 0 | ||||
|   },  | ||||
|    "fieldname": "shift_request", | ||||
|    "fieldtype": "Link", | ||||
|    "label": "Shift Request", | ||||
|    "options": "Shift Request", | ||||
|    "read_only": 1 | ||||
|   }, | ||||
|   { | ||||
|    "allow_bulk_edit": 0,  | ||||
|    "allow_in_quick_entry": 0,  | ||||
|    "allow_on_submit": 0,  | ||||
|    "bold": 0,  | ||||
|    "collapsible": 0,  | ||||
|    "columns": 0,  | ||||
|    "fieldname": "shift_request",  | ||||
|    "fieldtype": "Link",  | ||||
|    "hidden": 0,  | ||||
|    "ignore_user_permissions": 0,  | ||||
|    "ignore_xss_filter": 0,  | ||||
|    "in_filter": 0,  | ||||
|    "in_global_search": 0,  | ||||
|    "in_list_view": 0,  | ||||
|    "in_standard_filter": 0,  | ||||
|    "label": "Shift Request",  | ||||
|    "length": 0,  | ||||
|    "no_copy": 0,  | ||||
|    "options": "Shift Request",  | ||||
|    "permlevel": 0,  | ||||
|    "precision": "",  | ||||
|    "print_hide": 0,  | ||||
|    "print_hide_if_no_value": 0,  | ||||
|    "read_only": 1,  | ||||
|    "remember_last_selected_value": 0,  | ||||
|    "report_hide": 0,  | ||||
|    "reqd": 0,  | ||||
|    "search_index": 0,  | ||||
|    "set_only_once": 0,  | ||||
|    "translatable": 0,  | ||||
|    "unique": 0 | ||||
|   },  | ||||
|   { | ||||
|    "allow_bulk_edit": 0,  | ||||
|    "allow_in_quick_entry": 0,  | ||||
|    "allow_on_submit": 0,  | ||||
|    "bold": 0,  | ||||
|    "collapsible": 0,  | ||||
|    "columns": 0,  | ||||
|    "fieldname": "amended_from",  | ||||
|    "fieldtype": "Link",  | ||||
|    "hidden": 0,  | ||||
|    "ignore_user_permissions": 0,  | ||||
|    "ignore_xss_filter": 0,  | ||||
|    "in_filter": 0,  | ||||
|    "in_global_search": 0,  | ||||
|    "in_list_view": 0,  | ||||
|    "in_standard_filter": 0,  | ||||
|    "label": "Amended From",  | ||||
|    "length": 0,  | ||||
|    "no_copy": 1,  | ||||
|    "options": "Shift Assignment",  | ||||
|    "permlevel": 0,  | ||||
|    "print_hide": 1,  | ||||
|    "print_hide_if_no_value": 0,  | ||||
|    "read_only": 1,  | ||||
|    "remember_last_selected_value": 0,  | ||||
|    "report_hide": 0,  | ||||
|    "reqd": 0,  | ||||
|    "search_index": 0,  | ||||
|    "set_only_once": 0,  | ||||
|    "translatable": 0,  | ||||
|    "unique": 0 | ||||
|    "fieldname": "amended_from", | ||||
|    "fieldtype": "Link", | ||||
|    "label": "Amended From", | ||||
|    "no_copy": 1, | ||||
|    "options": "Shift Assignment", | ||||
|    "print_hide": 1, | ||||
|    "read_only": 1 | ||||
|   } | ||||
|  ],  | ||||
|  "has_web_view": 0,  | ||||
|  "hide_heading": 0,  | ||||
|  "hide_toolbar": 0,  | ||||
|  "idx": 0,  | ||||
|  "image_view": 0,  | ||||
|  "in_create": 0,  | ||||
|  "is_submittable": 1,  | ||||
|  "issingle": 0,  | ||||
|  "istable": 0,  | ||||
|  "max_attachments": 0,  | ||||
|  "modified": "2018-08-21 16:15:41.155464",  | ||||
|  "modified_by": "Administrator",  | ||||
|  "module": "HR",  | ||||
|  "name": "Shift Assignment",  | ||||
|  "name_case": "",  | ||||
|  "owner": "Administrator",  | ||||
|  ], | ||||
|  "is_submittable": 1, | ||||
|  "modified": "2019-05-30 15:40:54.418427", | ||||
|  "modified_by": "Administrator", | ||||
|  "module": "HR", | ||||
|  "name": "Shift Assignment", | ||||
|  "owner": "Administrator", | ||||
|  "permissions": [ | ||||
|   { | ||||
|    "amend": 0,  | ||||
|    "cancel": 0,  | ||||
|    "create": 0,  | ||||
|    "delete": 0,  | ||||
|    "email": 1,  | ||||
|    "export": 1,  | ||||
|    "if_owner": 0,  | ||||
|    "import": 0,  | ||||
|    "permlevel": 0,  | ||||
|    "print": 1,  | ||||
|    "read": 1,  | ||||
|    "report": 1,  | ||||
|    "role": "Employee",  | ||||
|    "set_user_permissions": 0,  | ||||
|    "share": 1,  | ||||
|    "submit": 0,  | ||||
|    "write": 0 | ||||
|   },  | ||||
|    "email": 1, | ||||
|    "export": 1, | ||||
|    "print": 1, | ||||
|    "read": 1, | ||||
|    "report": 1, | ||||
|    "role": "Employee", | ||||
|    "share": 1 | ||||
|   }, | ||||
|   { | ||||
|    "amend": 1,  | ||||
|    "cancel": 1,  | ||||
|    "create": 1,  | ||||
|    "delete": 1,  | ||||
|    "email": 1,  | ||||
|    "export": 1,  | ||||
|    "if_owner": 0,  | ||||
|    "import": 0,  | ||||
|    "permlevel": 0,  | ||||
|    "print": 1,  | ||||
|    "read": 1,  | ||||
|    "report": 1,  | ||||
|    "role": "HR Manager",  | ||||
|    "set_user_permissions": 0,  | ||||
|    "share": 1,  | ||||
|    "submit": 1,  | ||||
|    "amend": 1, | ||||
|    "cancel": 1, | ||||
|    "create": 1, | ||||
|    "delete": 1, | ||||
|    "email": 1, | ||||
|    "export": 1, | ||||
|    "print": 1, | ||||
|    "read": 1, | ||||
|    "report": 1, | ||||
|    "role": "HR Manager", | ||||
|    "share": 1, | ||||
|    "submit": 1, | ||||
|    "write": 1 | ||||
|   },  | ||||
|   }, | ||||
|   { | ||||
|    "amend": 0,  | ||||
|    "cancel": 0,  | ||||
|    "create": 1,  | ||||
|    "delete": 0,  | ||||
|    "email": 1,  | ||||
|    "export": 1,  | ||||
|    "if_owner": 0,  | ||||
|    "import": 0,  | ||||
|    "permlevel": 0,  | ||||
|    "print": 1,  | ||||
|    "read": 1,  | ||||
|    "report": 1,  | ||||
|    "role": "HR User",  | ||||
|    "set_user_permissions": 0,  | ||||
|    "share": 1,  | ||||
|    "submit": 1,  | ||||
|    "create": 1, | ||||
|    "email": 1, | ||||
|    "export": 1, | ||||
|    "print": 1, | ||||
|    "read": 1, | ||||
|    "report": 1, | ||||
|    "role": "HR User", | ||||
|    "share": 1, | ||||
|    "submit": 1, | ||||
|    "write": 1 | ||||
|   } | ||||
|  ],  | ||||
|  "quick_entry": 0,  | ||||
|  "read_only": 0,  | ||||
|  "read_only_onload": 0,  | ||||
|  "show_name_in_global_search": 0,  | ||||
|  "sort_field": "modified",  | ||||
|  "sort_order": "DESC",  | ||||
|  "title_field": "employee_name",  | ||||
|  "track_changes": 1,  | ||||
|  "track_seen": 0,  | ||||
|  "track_views": 0 | ||||
|  ], | ||||
|  "sort_field": "modified", | ||||
|  "sort_order": "DESC", | ||||
|  "title_field": "employee_name", | ||||
|  "track_changes": 1 | ||||
| } | ||||
| @ -7,7 +7,7 @@ import frappe | ||||
| from frappe import _ | ||||
| from frappe.model.document import Document | ||||
| from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, now_datetime, nowdate | ||||
| from erpnext.hr.doctype.holiday_list.holiday_list import get_holiday_list, is_holiday | ||||
| from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee, is_holiday | ||||
| from datetime import timedelta, datetime | ||||
| 
 | ||||
| class OverlapError(frappe.ValidationError): pass | ||||
| @ -97,8 +97,8 @@ def get_employee_shift(employee, for_date=nowdate(), consider_default_shift=Fals | ||||
| 	if shift_type_name: | ||||
| 		holiday_list_name = frappe.db.get_value('Shift Type', shift_type_name, 'holiday_list') | ||||
| 		if not holiday_list_name: | ||||
| 			holiday_list_name = get_holiday_list(employee) | ||||
| 		if holiday_list_name and is_holiday(holiday_list_name, for_date): | ||||
| 			holiday_list_name = get_holiday_list_for_employee(employee, False) | ||||
| 		if holiday_list_name and is_holiday(holiday_list_name, for_date, False): | ||||
| 			shift_type_name = None | ||||
| 
 | ||||
| 	if not shift_type_name and next_shift_direction: | ||||
|  | ||||
| @ -7,10 +7,10 @@ | ||||
|  "field_order": [ | ||||
|   "start_time", | ||||
|   "end_time", | ||||
|   "disable_auto_attendance_for_this_shift", | ||||
|   "column_break_3", | ||||
|   "holiday_list", | ||||
|   "auto_attendance_configurations_section", | ||||
|   "enable_auto_attendance", | ||||
|   "auto_attendance_settings_section", | ||||
|   "determine_check_in_and_check_out", | ||||
|   "working_hours_calculation_based_on", | ||||
|   "begin_check_in_before_shift_start_time", | ||||
| @ -18,7 +18,7 @@ | ||||
|   "column_break_10", | ||||
|   "working_hours_threshold_for_half_day", | ||||
|   "working_hours_threshold_for_absent", | ||||
|   "grace_period_configuration_auto_attendance_section", | ||||
|   "grace_period_settings_auto_attendance_section", | ||||
|   "enable_entry_grace_period", | ||||
|   "late_entry_grace_period", | ||||
|   "consequence_after", | ||||
| @ -86,33 +86,14 @@ | ||||
|    "precision": "1" | ||||
|   }, | ||||
|   { | ||||
|    "depends_on": "eval:!doc.disable_auto_attendance_for_this_shift", | ||||
|    "fieldname": "auto_attendance_configurations_section", | ||||
|    "fieldtype": "Section Break", | ||||
|    "label": "Auto Attendance Configurations" | ||||
|   }, | ||||
|   { | ||||
|    "default": "45", | ||||
|    "default": "60", | ||||
|    "description": "The time before the shift start time during which Employee Check-in is considered for attendance.", | ||||
|    "fieldname": "begin_check_in_before_shift_start_time", | ||||
|    "fieldtype": "Int", | ||||
|    "label": "Begin check-in before shift start time (in minutes)" | ||||
|   }, | ||||
|   { | ||||
|    "default": "1", | ||||
|    "description": "Don't mark attendance based on Employee Attendance Log.", | ||||
|    "fieldname": "disable_auto_attendance_for_this_shift", | ||||
|    "fieldtype": "Check", | ||||
|    "label": "Disable Auto Attendance for this shift" | ||||
|   }, | ||||
|   { | ||||
|    "depends_on": "eval:!doc.disable_auto_attendance_for_this_shift", | ||||
|    "fieldname": "grace_period_configuration_auto_attendance_section", | ||||
|    "fieldtype": "Section Break", | ||||
|    "hidden": 1, | ||||
|    "label": "Grace Period Configuration For Auto Attendance" | ||||
|   }, | ||||
|   { | ||||
|    "default": "0", | ||||
|    "fieldname": "enable_entry_grace_period", | ||||
|    "fieldtype": "Check", | ||||
|    "label": "Enable Entry Grace Period" | ||||
| @ -144,11 +125,13 @@ | ||||
|    "fieldtype": "Column Break" | ||||
|   }, | ||||
|   { | ||||
|    "default": "0", | ||||
|    "fieldname": "enable_exit_grace_period", | ||||
|    "fieldtype": "Check", | ||||
|    "label": "Enable Exit Grace Period" | ||||
|   }, | ||||
|   { | ||||
|    "default": "0", | ||||
|    "depends_on": "enable_exit_grace_period", | ||||
|    "fieldname": "enable_different_consequence_for_early_exit", | ||||
|    "fieldtype": "Check", | ||||
| @ -177,13 +160,34 @@ | ||||
|    "options": "Half Day\nAbsent" | ||||
|   }, | ||||
|   { | ||||
|    "description": "Time after the end of shift during which check-out is considered for attendance. (Zero to allow till next shift begins)", | ||||
|    "default": "60", | ||||
|    "description": "Time after the end of shift during which check-out is considered for attendance.", | ||||
|    "fieldname": "allow_check_out_after_shift_end_time", | ||||
|    "fieldtype": "Int", | ||||
|    "label": "Allow check-out after shift end time (in minutes)" | ||||
|   }, | ||||
|   { | ||||
|    "depends_on": "enable_auto_attendance", | ||||
|    "fieldname": "auto_attendance_settings_section", | ||||
|    "fieldtype": "Section Break", | ||||
|    "label": "Auto Attendance Settings" | ||||
|   }, | ||||
|   { | ||||
|    "depends_on": "enable_auto_attendance", | ||||
|    "fieldname": "grace_period_settings_auto_attendance_section", | ||||
|    "fieldtype": "Section Break", | ||||
|    "hidden": 1, | ||||
|    "label": "Grace Period Settings For Auto Attendance" | ||||
|   }, | ||||
|   { | ||||
|    "default": "0", | ||||
|    "description": "Mark attendance based on 'Employee Attendance Log' for Employees assigned to this shift.", | ||||
|    "fieldname": "enable_auto_attendance", | ||||
|    "fieldtype": "Check", | ||||
|    "label": "Enable Auto Attendance" | ||||
|   } | ||||
|  ], | ||||
|  "modified": "2019-05-16 18:57:00.150899", | ||||
|  "modified": "2019-05-30 15:31:35.594990", | ||||
|  "modified_by": "Administrator", | ||||
|  "module": "HR", | ||||
|  "name": "Shift Type", | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user