rename(HR): Employee Attendance Log DocType to Employee Checkin
This commit is contained in:
parent
e0c5176383
commit
32197355ad
@ -37,7 +37,7 @@ def get_data():
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "doctype",
|
"type": "doctype",
|
||||||
"name": "Employee Attendance Log",
|
"name": "Employee Checkin",
|
||||||
"hide_count": True,
|
"hide_count": True,
|
||||||
"onboard": 1,
|
"onboard": 1,
|
||||||
"dependencies": ["Employee"]
|
"dependencies": ["Employee"]
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Employee Attendance Log', {
|
frappe.ui.form.on('Employee Checkin', {
|
||||||
// refresh: function(frm) {
|
// refresh: function(frm) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"autoname": "ATT-LOG-.MM.-.YYYY.-.######",
|
"autoname": "ATT-CKIN-.MM.-.YYYY.-.######",
|
||||||
"creation": "2019-04-25 10:17:11.225671",
|
"creation": "2019-06-10 11:56:34.536413",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
@ -37,6 +37,19 @@
|
|||||||
"label": "Employee Name",
|
"label": "Employee Name",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "log_type",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Log Type",
|
||||||
|
"options": "\nIN\nOUT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "shift",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Shift",
|
||||||
|
"options": "Shift Type",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_4",
|
"fieldname": "column_break_4",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
@ -54,26 +67,6 @@
|
|||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Location / Device ID"
|
"label": "Location / Device ID"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"default": "0",
|
|
||||||
"fieldname": "entry_grace_period_consequence",
|
|
||||||
"fieldtype": "Check",
|
|
||||||
"hidden": 1,
|
|
||||||
"label": "Entry Grace Period Consequence"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"default": "0",
|
|
||||||
"fieldname": "exit_grace_period_consequence",
|
|
||||||
"fieldtype": "Check",
|
|
||||||
"hidden": 1,
|
|
||||||
"label": "Exit Grace Period Consequence"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "log_type",
|
|
||||||
"fieldtype": "Select",
|
|
||||||
"label": "Log Type",
|
|
||||||
"options": "\nIN\nOUT"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"fieldname": "skip_auto_attendance",
|
"fieldname": "skip_auto_attendance",
|
||||||
@ -88,11 +81,18 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "shift",
|
"default": "0",
|
||||||
"fieldtype": "Link",
|
"fieldname": "entry_grace_period_consequence",
|
||||||
"label": "Shift",
|
"fieldtype": "Check",
|
||||||
"options": "Shift Type",
|
"hidden": 1,
|
||||||
"read_only": 1
|
"label": "Entry Grace Period Consequence"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "exit_grace_period_consequence",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "Exit Grace Period Consequence"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "shift_start",
|
"fieldname": "shift_start",
|
||||||
@ -119,10 +119,10 @@
|
|||||||
"label": "Shift Actual End"
|
"label": "Shift Actual End"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2019-06-06 23:09:37.766717",
|
"modified": "2019-06-10 11:56:34.536413",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Employee Attendance Log",
|
"name": "Employee Checkin",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
@ -10,18 +10,18 @@ from frappe import _
|
|||||||
|
|
||||||
from erpnext.hr.doctype.shift_assignment.shift_assignment import get_actual_start_end_datetime_of_shift
|
from erpnext.hr.doctype.shift_assignment.shift_assignment import get_actual_start_end_datetime_of_shift
|
||||||
|
|
||||||
class EmployeeAttendanceLog(Document):
|
class EmployeeCheckin(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_duplicate_log()
|
self.validate_duplicate_log()
|
||||||
self.fetch_shift()
|
self.fetch_shift()
|
||||||
|
|
||||||
def validate_duplicate_log(self):
|
def validate_duplicate_log(self):
|
||||||
doc = frappe.db.exists('Employee Attendance Log', {
|
doc = frappe.db.exists('Employee Checkin', {
|
||||||
'employee': self.employee,
|
'employee': self.employee,
|
||||||
'time': self.time,
|
'time': self.time,
|
||||||
'name': ['!=', self.name]})
|
'name': ['!=', self.name]})
|
||||||
if doc:
|
if doc:
|
||||||
doc_link = frappe.get_desk_link('Employee Attendance Log', doc)
|
doc_link = frappe.get_desk_link('Employee Checkin', doc)
|
||||||
frappe.throw(_('This employee already has a log with the same timestamp.{0}')
|
frappe.throw(_('This employee already has a log with the same timestamp.{0}')
|
||||||
.format("<Br>" + doc_link))
|
.format("<Br>" + doc_link))
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ class EmployeeAttendanceLog(Document):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def add_log_based_on_employee_field(employee_field_value, timestamp, device_id=None, log_type=None, skip_auto_attendance=0, employee_fieldname='attendance_device_id'):
|
def add_log_based_on_employee_field(employee_field_value, timestamp, device_id=None, log_type=None, skip_auto_attendance=0, employee_fieldname='attendance_device_id'):
|
||||||
"""Finds the relevant Employee using the employee field value and creates a Employee Attendance Log.
|
"""Finds the relevant Employee using the employee field value and creates a Employee Checkin.
|
||||||
|
|
||||||
:param employee_field_value: The value to look for in employee field.
|
: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 timestamp: The timestamp of the Log. Currently expected in the following format as string: '2019-05-08 10:48:08.000000'
|
||||||
@ -58,7 +58,7 @@ def add_log_based_on_employee_field(employee_field_value, timestamp, device_id=N
|
|||||||
else:
|
else:
|
||||||
frappe.throw(_("No Employee found for the given employee field value. '{}': {}").format(employee_fieldname,employee_field_value))
|
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 = frappe.new_doc("Employee Checkin")
|
||||||
doc.employee = employee.name
|
doc.employee = employee.name
|
||||||
doc.employee_name = employee.employee_name
|
doc.employee_name = employee.employee_name
|
||||||
doc.time = timestamp
|
doc.time = timestamp
|
||||||
@ -71,10 +71,10 @@ def add_log_based_on_employee_field(employee_field_value, timestamp, device_id=N
|
|||||||
|
|
||||||
|
|
||||||
def mark_attendance_and_link_log(logs, attendance_status, attendance_date, working_hours=None, shift=None):
|
def mark_attendance_and_link_log(logs, attendance_status, attendance_date, working_hours=None, shift=None):
|
||||||
"""Creates an attendance and links the attendance to the Employee Attendance Log.
|
"""Creates an attendance and links the attendance to the Employee Checkin.
|
||||||
Note: If attendance is already present for the given date, the logs are marked as skipped and no exception is thrown.
|
Note: If attendance is already present for the given date, the logs are marked as skipped and no exception is thrown.
|
||||||
|
|
||||||
:param logs: The List of 'Employee Attendance Log'.
|
:param logs: The List of 'Employee Checkin'.
|
||||||
:param attendance_status: Attendance status to be marked. One of: (Present, Absent, Half Day, Skip). Note: 'On Leave' is not supported by this function.
|
:param attendance_status: Attendance status to be marked. One of: (Present, Absent, Half Day, Skip). Note: 'On Leave' is not supported by this function.
|
||||||
:param attendance_date: Date of the attendance to be created.
|
:param attendance_date: Date of the attendance to be created.
|
||||||
:param working_hours: (optional)Number of working hours for the given date.
|
:param working_hours: (optional)Number of working hours for the given date.
|
||||||
@ -82,7 +82,7 @@ def mark_attendance_and_link_log(logs, attendance_status, attendance_date, worki
|
|||||||
log_names = [x.name for x in logs]
|
log_names = [x.name for x in logs]
|
||||||
employee = logs[0].employee
|
employee = logs[0].employee
|
||||||
if attendance_status == 'Skip':
|
if attendance_status == 'Skip':
|
||||||
frappe.db.sql("""update `tabEmployee Attendance Log`
|
frappe.db.sql("""update `tabEmployee Checkin`
|
||||||
set skip_auto_attendance = %s
|
set skip_auto_attendance = %s
|
||||||
where name in %s""", ('1', log_names))
|
where name in %s""", ('1', log_names))
|
||||||
return None
|
return None
|
||||||
@ -100,12 +100,12 @@ def mark_attendance_and_link_log(logs, attendance_status, attendance_date, worki
|
|||||||
}
|
}
|
||||||
attendance = frappe.get_doc(doc_dict).insert()
|
attendance = frappe.get_doc(doc_dict).insert()
|
||||||
attendance.submit()
|
attendance.submit()
|
||||||
frappe.db.sql("""update `tabEmployee Attendance Log`
|
frappe.db.sql("""update `tabEmployee Checkin`
|
||||||
set attendance = %s
|
set attendance = %s
|
||||||
where name in %s""", (attendance.name, log_names))
|
where name in %s""", (attendance.name, log_names))
|
||||||
return attendance
|
return attendance
|
||||||
else:
|
else:
|
||||||
frappe.db.sql("""update `tabEmployee Attendance Log`
|
frappe.db.sql("""update `tabEmployee Checkin`
|
||||||
set skip_auto_attendance = %s
|
set skip_auto_attendance = %s
|
||||||
where name in %s""", ('1', log_names))
|
where name in %s""", ('1', log_names))
|
||||||
return None
|
return None
|
||||||
@ -117,8 +117,8 @@ def calculate_working_hours(logs, check_in_out_type, working_hours_calc_type):
|
|||||||
"""Given a set of logs in chronological order calculates the total working hours based on the parameters.
|
"""Given a set of logs in chronological order calculates the total working hours based on the parameters.
|
||||||
Zero is returned for all invalid cases.
|
Zero is returned for all invalid cases.
|
||||||
|
|
||||||
:param logs: The List of 'Employee Attendance Log'.
|
:param logs: The List of 'Employee Checkin'.
|
||||||
:param check_in_out_type: One of: 'Alternating entries as IN and OUT during the same shift', 'Strictly based on Log Type in Employee Attendance Log'
|
:param check_in_out_type: One of: 'Alternating entries as IN and OUT during the same shift', 'Strictly based on Log Type in Employee Checkin'
|
||||||
:param working_hours_calc_type: One of: 'First Check-in and Last Check-out', 'Every Valid Check-in and Check-out'
|
:param working_hours_calc_type: One of: 'First Check-in and Last Check-out', 'Every Valid Check-in and Check-out'
|
||||||
"""
|
"""
|
||||||
total_hours = 0
|
total_hours = 0
|
||||||
@ -131,7 +131,7 @@ def calculate_working_hours(logs, check_in_out_type, working_hours_calc_type):
|
|||||||
total_hours += time_diff_in_hours(logs[0].time, logs[1].time)
|
total_hours += time_diff_in_hours(logs[0].time, logs[1].time)
|
||||||
del logs[:2]
|
del logs[:2]
|
||||||
|
|
||||||
elif check_in_out_type == 'Strictly based on Log Type in Employee Attendance Log':
|
elif check_in_out_type == 'Strictly based on Log Type in Employee Checkin':
|
||||||
if working_hours_calc_type == 'First Check-in and Last Check-out':
|
if working_hours_calc_type == 'First Check-in and Last Check-out':
|
||||||
first_in_log = logs[find_index_in_dict(logs, 'log_type', 'IN')]
|
first_in_log = logs[find_index_in_dict(logs, 'log_type', 'IN')]
|
||||||
last_out_log = logs[len(logs)-1-find_index_in_dict(reversed(logs), 'log_type', 'OUT')]
|
last_out_log = logs[len(logs)-1-find_index_in_dict(reversed(logs), 'log_type', 'OUT')]
|
||||||
@ -156,3 +156,4 @@ def time_diff_in_hours(start, end):
|
|||||||
|
|
||||||
def find_index_in_dict(dict_list, key, value):
|
def find_index_in_dict(dict_list, key, value):
|
||||||
return next((index for (index, d) in enumerate(dict_list) if d[key] == value), None)
|
return next((index for (index, d) in enumerate(dict_list) if d[key] == value), None)
|
||||||
|
|
||||||
@ -8,10 +8,10 @@ from frappe.utils import now_datetime, nowdate, to_timedelta
|
|||||||
import unittest
|
import unittest
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from erpnext.hr.doctype.employee_attendance_log.employee_attendance_log import add_log_based_on_employee_field, mark_attendance_and_link_log, calculate_working_hours
|
from erpnext.hr.doctype.employee_checkin.employee_checkin import add_log_based_on_employee_field, mark_attendance_and_link_log, calculate_working_hours
|
||||||
from erpnext.hr.doctype.employee.test_employee import make_employee
|
from erpnext.hr.doctype.employee.test_employee import make_employee
|
||||||
|
|
||||||
class TestEmployeeAttendanceLog(unittest.TestCase):
|
class TestEmployeeCheckin(unittest.TestCase):
|
||||||
def test_add_log_based_on_employee_field(self):
|
def test_add_log_based_on_employee_field(self):
|
||||||
employee = make_employee("test_add_log_based_on_employee_field@example.com")
|
employee = make_employee("test_add_log_based_on_employee_field@example.com")
|
||||||
employee = frappe.get_doc("Employee", employee)
|
employee = frappe.get_doc("Employee", employee)
|
||||||
@ -19,26 +19,26 @@ class TestEmployeeAttendanceLog(unittest.TestCase):
|
|||||||
employee.save()
|
employee.save()
|
||||||
|
|
||||||
time_now = now_datetime().__str__()[:-7]
|
time_now = now_datetime().__str__()[:-7]
|
||||||
employee_attendance_log = add_log_based_on_employee_field('3344', time_now, 'mumbai_first_floor', 'IN')
|
employee_checkin = add_log_based_on_employee_field('3344', time_now, 'mumbai_first_floor', 'IN')
|
||||||
self.assertEqual(employee_attendance_log.employee, employee.name)
|
self.assertEqual(employee_checkin.employee, employee.name)
|
||||||
self.assertEqual(employee_attendance_log.time, time_now)
|
self.assertEqual(employee_checkin.time, time_now)
|
||||||
self.assertEqual(employee_attendance_log.device_id, 'mumbai_first_floor')
|
self.assertEqual(employee_checkin.device_id, 'mumbai_first_floor')
|
||||||
self.assertEqual(employee_attendance_log.log_type, 'IN')
|
self.assertEqual(employee_checkin.log_type, 'IN')
|
||||||
|
|
||||||
def test_mark_attendance_and_link_log(self):
|
def test_mark_attendance_and_link_log(self):
|
||||||
employee = make_employee("test_mark_attendance_and_link_log@example.com")
|
employee = make_employee("test_mark_attendance_and_link_log@example.com")
|
||||||
logs = make_n_attendance_logs(employee, 3)
|
logs = make_n_checkins(employee, 3)
|
||||||
mark_attendance_and_link_log(logs, 'Skip', nowdate())
|
mark_attendance_and_link_log(logs, 'Skip', nowdate())
|
||||||
log_names = [log.name for log in logs]
|
log_names = [log.name for log in logs]
|
||||||
logs_count = frappe.db.count('Employee Attendance Log', {'name':['in', log_names], 'skip_auto_attendance':1})
|
logs_count = frappe.db.count('Employee Checkin', {'name':['in', log_names], 'skip_auto_attendance':1})
|
||||||
self.assertEqual(logs_count, 3)
|
self.assertEqual(logs_count, 3)
|
||||||
|
|
||||||
logs = make_n_attendance_logs(employee, 4, 2)
|
logs = make_n_checkins(employee, 4, 2)
|
||||||
now_date = nowdate()
|
now_date = nowdate()
|
||||||
frappe.db.delete('Attendance', {'employee':employee})
|
frappe.db.delete('Attendance', {'employee':employee})
|
||||||
attendance = mark_attendance_and_link_log(logs, 'Present', now_date, 8.2)
|
attendance = mark_attendance_and_link_log(logs, 'Present', now_date, 8.2)
|
||||||
log_names = [log.name for log in logs]
|
log_names = [log.name for log in logs]
|
||||||
logs_count = frappe.db.count('Employee Attendance Log', {'name':['in', log_names], 'attendance':attendance.name})
|
logs_count = frappe.db.count('Employee Checkin', {'name':['in', log_names], 'attendance':attendance.name})
|
||||||
self.assertEqual(logs_count, 4)
|
self.assertEqual(logs_count, 4)
|
||||||
attendance_count = frappe.db.count('Attendance', {'status':'Present', 'working_hours':8.2,
|
attendance_count = frappe.db.count('Attendance', {'status':'Present', 'working_hours':8.2,
|
||||||
'employee':employee, 'attendance_date':now_date})
|
'employee':employee, 'attendance_date':now_date})
|
||||||
@ -46,7 +46,7 @@ class TestEmployeeAttendanceLog(unittest.TestCase):
|
|||||||
|
|
||||||
def test_calculate_working_hours(self):
|
def test_calculate_working_hours(self):
|
||||||
check_in_out_type = ['Alternating entries as IN and OUT during the same shift',
|
check_in_out_type = ['Alternating entries as IN and OUT during the same shift',
|
||||||
'Strictly based on Log Type in Employee Attendance Log']
|
'Strictly based on Log Type in Employee Checkin']
|
||||||
working_hours_calc_type = ['First Check-in and Last Check-out',
|
working_hours_calc_type = ['First Check-in and Last Check-out',
|
||||||
'Every Valid Check-in and Check-out']
|
'Every Valid Check-in and Check-out']
|
||||||
logs_type_1 = [
|
logs_type_1 = [
|
||||||
@ -81,19 +81,19 @@ class TestEmployeeAttendanceLog(unittest.TestCase):
|
|||||||
working_hours = calculate_working_hours(logs_type_2,check_in_out_type[1],working_hours_calc_type[1])
|
working_hours = calculate_working_hours(logs_type_2,check_in_out_type[1],working_hours_calc_type[1])
|
||||||
self.assertEqual(working_hours, 4.5)
|
self.assertEqual(working_hours, 4.5)
|
||||||
|
|
||||||
def make_n_attendance_logs(employee, n, hours_to_reverse=1):
|
def make_n_checkins(employee, n, hours_to_reverse=1):
|
||||||
logs = [make_attendance_log(employee, now_datetime() - timedelta(hours=hours_to_reverse, minutes=n+1))]
|
logs = [make_checkin(employee, now_datetime() - timedelta(hours=hours_to_reverse, minutes=n+1))]
|
||||||
for i in range(n-1):
|
for i in range(n-1):
|
||||||
logs.append(make_attendance_log(employee, now_datetime() - timedelta(hours=hours_to_reverse, minutes=n-i)))
|
logs.append(make_checkin(employee, now_datetime() - timedelta(hours=hours_to_reverse, minutes=n-i)))
|
||||||
return logs
|
return logs
|
||||||
|
|
||||||
|
|
||||||
def make_attendance_log(employee, time=now_datetime()):
|
def make_checkin(employee, time=now_datetime()):
|
||||||
log = frappe.get_doc({
|
log = frappe.get_doc({
|
||||||
"doctype": "Employee Attendance Log",
|
"doctype": "Employee Checkin",
|
||||||
"employee" : employee,
|
"employee" : employee,
|
||||||
"time" : time,
|
"time" : time,
|
||||||
"device_id" : "device1",
|
"device_id" : "device1",
|
||||||
"log_type" : "IN"
|
"log_type" : "IN"
|
||||||
}).insert()
|
}).insert()
|
||||||
return log
|
return log
|
||||||
@ -19,7 +19,7 @@
|
|||||||
"working_hours_threshold_for_half_day",
|
"working_hours_threshold_for_half_day",
|
||||||
"working_hours_threshold_for_absent",
|
"working_hours_threshold_for_absent",
|
||||||
"process_attendance_after",
|
"process_attendance_after",
|
||||||
"last_sync_of_attendance_log",
|
"last_sync_of_checkin",
|
||||||
"grace_period_settings_auto_attendance_section",
|
"grace_period_settings_auto_attendance_section",
|
||||||
"enable_entry_grace_period",
|
"enable_entry_grace_period",
|
||||||
"late_entry_grace_period",
|
"late_entry_grace_period",
|
||||||
@ -65,7 +65,7 @@
|
|||||||
"fieldname": "determine_check_in_and_check_out",
|
"fieldname": "determine_check_in_and_check_out",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Determine Check-in and Check-out",
|
"label": "Determine Check-in and Check-out",
|
||||||
"options": "Alternating entries as IN and OUT during the same shift\nStrictly based on Log Type in Employee Attendance Log"
|
"options": "Alternating entries as IN and OUT during the same shift\nStrictly based on Log Type in Employee Checkin"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "working_hours_calculation_based_on",
|
"fieldname": "working_hours_calculation_based_on",
|
||||||
@ -183,7 +183,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"description": "Mark attendance based on 'Employee Attendance Log' for Employees assigned to this shift.",
|
"description": "Mark attendance based on 'Employee Checkin' for Employees assigned to this shift.",
|
||||||
"fieldname": "enable_auto_attendance",
|
"fieldname": "enable_auto_attendance",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Enable Auto Attendance"
|
"label": "Enable Auto Attendance"
|
||||||
@ -195,13 +195,13 @@
|
|||||||
"label": "Process Attendance After"
|
"label": "Process Attendance After"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Last Known Successful Sync of Employee Attendance Log. Reset this only if you are sure that all Logs are synced from all the locations. Please don't modify this if you are unsure.",
|
"description": "Last Known Successful Sync of Employee Checkin. Reset this only if you are sure that all Logs are synced from all the locations. Please don't modify this if you are unsure.",
|
||||||
"fieldname": "last_sync_of_attendance_log",
|
"fieldname": "last_sync_of_checkin",
|
||||||
"fieldtype": "Datetime",
|
"fieldtype": "Datetime",
|
||||||
"label": "Last Sync of Attendance Log"
|
"label": "Last Sync of Checkin"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2019-05-31 16:02:44.272036",
|
"modified": "2019-06-10 06:02:44.272036",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Shift Type",
|
"name": "Shift Type",
|
||||||
|
|||||||
@ -10,22 +10,22 @@ import frappe
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import cint, getdate
|
from frappe.utils import cint, getdate
|
||||||
from erpnext.hr.doctype.shift_assignment.shift_assignment import get_actual_start_end_datetime_of_shift, get_employee_shift
|
from erpnext.hr.doctype.shift_assignment.shift_assignment import get_actual_start_end_datetime_of_shift, get_employee_shift
|
||||||
from erpnext.hr.doctype.employee_attendance_log.employee_attendance_log import mark_attendance_and_link_log, calculate_working_hours
|
from erpnext.hr.doctype.employee_checkin.employee_checkin import mark_attendance_and_link_log, calculate_working_hours
|
||||||
from erpnext.hr.doctype.attendance.attendance import mark_absent
|
from erpnext.hr.doctype.attendance.attendance import mark_absent
|
||||||
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
||||||
|
|
||||||
class ShiftType(Document):
|
class ShiftType(Document):
|
||||||
def process_auto_attendance(self):
|
def process_auto_attendance(self):
|
||||||
if not cint(self.enable_auto_attendance) or not self.process_attendance_after or not self.last_sync_of_attendance_log:
|
if not cint(self.enable_auto_attendance) or not self.process_attendance_after or not self.last_sync_of_checkin:
|
||||||
return
|
return
|
||||||
filters = {
|
filters = {
|
||||||
'skip_auto_attendance':'0',
|
'skip_auto_attendance':'0',
|
||||||
'attendance':('is', 'not set'),
|
'attendance':('is', 'not set'),
|
||||||
'time':('>=', self.process_attendance_after),
|
'time':('>=', self.process_attendance_after),
|
||||||
'shift_actual_start': ('<', self.last_sync_of_attendance_log),
|
'shift_actual_start': ('<', self.last_sync_of_checkin),
|
||||||
'shift': self.name
|
'shift': self.name
|
||||||
}
|
}
|
||||||
logs = frappe.db.get_list('Employee Attendance Log', fields="*", filters=filters, order_by="employee,time")
|
logs = frappe.db.get_list('Employee Checkin', fields="*", filters=filters, order_by="employee,time")
|
||||||
for key, group in itertools.groupby(logs, key=lambda x: (x['employee'], x['shift_actual_start'])):
|
for key, group in itertools.groupby(logs, key=lambda x: (x['employee'], x['shift_actual_start'])):
|
||||||
single_shift_logs = list(group)
|
single_shift_logs = list(group)
|
||||||
attendance_status, working_hours = self.get_attendance(single_shift_logs)
|
attendance_status, working_hours = self.get_attendance(single_shift_logs)
|
||||||
@ -54,8 +54,8 @@ class ShiftType(Document):
|
|||||||
if not date_of_joining:
|
if not date_of_joining:
|
||||||
date_of_joining = employee_creation.date()
|
date_of_joining = employee_creation.date()
|
||||||
start_date = max(self.process_attendance_after, date_of_joining)
|
start_date = max(self.process_attendance_after, date_of_joining)
|
||||||
actual_shift_datetime = get_actual_start_end_datetime_of_shift(employee, self.last_sync_of_attendance_log, True)
|
actual_shift_datetime = get_actual_start_end_datetime_of_shift(employee, self.last_sync_of_checkin, True)
|
||||||
last_shift_time = actual_shift_datetime[0] if actual_shift_datetime[0] else self.last_sync_of_attendance_log
|
last_shift_time = actual_shift_datetime[0] if actual_shift_datetime[0] else self.last_sync_of_checkin
|
||||||
prev_shift = get_employee_shift(employee, last_shift_time.date()-timedelta(days=1), True, 'reverse')
|
prev_shift = get_employee_shift(employee, last_shift_time.date()-timedelta(days=1), True, 'reverse')
|
||||||
if prev_shift:
|
if prev_shift:
|
||||||
end_date = min(prev_shift.start_datetime.date(), relieving_date) if relieving_date else prev_shift.start_datetime.date()
|
end_date = min(prev_shift.start_datetime.date(), relieving_date) if relieving_date else prev_shift.start_datetime.date()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user