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:
karthikeyan5 2019-05-30 16:12:34 +05:30
parent 23cedfd6c8
commit 66e459b35d
11 changed files with 202 additions and 517 deletions

View File

@ -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)

View File

@ -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",

View File

@ -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()

View File

@ -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

View File

@ -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')

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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
}

View File

@ -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:

View File

@ -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",