fix(hr): Auto Attendance
> fixing minor bug in shift calculation
> adding 'Mark Auto Attendance' button in shift and related fixes
> changed employee checkin naming series
> added aditional validation for employee checkin
> added/updated dashboard for Attendance, Employee, Shift Type
diff --git a/erpnext/hr/doctype/attendance/attendance_dashboard.py b/erpnext/hr/doctype/attendance/attendance_dashboard.py
new file mode 100644
index 0000000..5dd9403
--- /dev/null
+++ b/erpnext/hr/doctype/attendance/attendance_dashboard.py
@@ -0,0 +1,13 @@
+from __future__ import unicode_literals
+from frappe import _
+
+def get_data():
+ return {
+ 'fieldname': 'attendance',
+ 'transactions': [
+ {
+ 'label': '',
+ 'items': ['Employee Checkin']
+ }
+ ]
+ }
diff --git a/erpnext/hr/doctype/employee/employee_dashboard.py b/erpnext/hr/doctype/employee/employee_dashboard.py
index e3cc33d..162b697 100644
--- a/erpnext/hr/doctype/employee/employee_dashboard.py
+++ b/erpnext/hr/doctype/employee/employee_dashboard.py
@@ -9,7 +9,7 @@
'transactions': [
{
'label': _('Leave and Attendance'),
- 'items': ['Attendance', 'Attendance Request', 'Leave Application', 'Leave Allocation']
+ 'items': ['Attendance', 'Attendance Request', 'Leave Application', 'Leave Allocation', 'Employee Checkin']
},
{
'label': _('Lifecycle'),
@@ -40,4 +40,4 @@
'items': ['Training Event', 'Training Result', 'Training Feedback', 'Employee Skill Map']
},
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.json b/erpnext/hr/doctype/employee_checkin/employee_checkin.json
index d340527..15ec7c0 100644
--- a/erpnext/hr/doctype/employee_checkin/employee_checkin.json
+++ b/erpnext/hr/doctype/employee_checkin/employee_checkin.json
@@ -1,6 +1,6 @@
{
"allow_import": 1,
- "autoname": "ATT-CKIN-.MM.-.YYYY.-.######",
+ "autoname": "EMP-CKIN-.MM.-.YYYY.-.######",
"creation": "2019-06-10 11:56:34.536413",
"doctype": "DocType",
"engine": "InnoDB",
@@ -119,7 +119,7 @@
"label": "Shift Actual End"
}
],
- "modified": "2019-06-10 11:56:34.536413",
+ "modified": "2019-06-10 15:33:22.731697",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Checkin",
diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.py b/erpnext/hr/doctype/employee_checkin/employee_checkin.py
index 997897b..b0e15d9 100644
--- a/erpnext/hr/doctype/employee_checkin/employee_checkin.py
+++ b/erpnext/hr/doctype/employee_checkin/employee_checkin.py
@@ -28,6 +28,8 @@
def fetch_shift(self):
shift_actual_timings = get_actual_start_end_datetime_of_shift(self.employee, get_datetime(self.time), True)
if shift_actual_timings[0] and shift_actual_timings[1]:
+ if shift_actual_timings[2].shift_type.determine_check_in_and_check_out == 'Strictly based on Log Type in Employee Checkin' and not self.log_type and not self.skip_auto_attendance:
+ frappe.throw(_('Log Type is required for check-ins falling in the shift: {0}.').format(shift_actual_timings[2].shift_type.name))
if not self.attendance:
self.shift = shift_actual_timings[2].shift_type.name
self.shift_actual_start = shift_actual_timings[0]
diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment.py b/erpnext/hr/doctype/shift_assignment/shift_assignment.py
index 5b732c4..40c78cd 100644
--- a/erpnext/hr/doctype/shift_assignment/shift_assignment.py
+++ b/erpnext/hr/doctype/shift_assignment/shift_assignment.py
@@ -115,11 +115,12 @@
break
else:
direction = '<' if next_shift_direction == 'reverse' else '>'
+ sort_order = 'desc' if next_shift_direction == 'reverse' else 'asc'
dates = frappe.db.get_all('Shift Assignment',
'date',
{'employee':employee, 'date':(direction, for_date), 'docstatus': '1'},
as_list=True,
- limit=MAX_DAYS)
+ limit=MAX_DAYS, order_by="date "+sort_order)
for date in dates:
shift_details = get_employee_shift(employee, date[0], consider_default_shift, None)
if shift_details:
diff --git a/erpnext/hr/doctype/shift_type/shift_type.js b/erpnext/hr/doctype/shift_type/shift_type.js
index feae889..e633545 100644
--- a/erpnext/hr/doctype/shift_type/shift_type.js
+++ b/erpnext/hr/doctype/shift_type/shift_type.js
@@ -3,6 +3,16 @@
frappe.ui.form.on('Shift Type', {
refresh: function(frm) {
-
+ frm.add_custom_button(
+ 'Mark Auto Attendance',
+ () => frm.call({
+ doc: frm.doc,
+ method: 'process_auto_attendance',
+ freeze: true,
+ callback: () => {
+ frappe.msgprint(__("Attendance has been marked as per employee check-ins"));
+ }
+ })
+ );
}
});
diff --git a/erpnext/hr/doctype/shift_type/shift_type.py b/erpnext/hr/doctype/shift_type/shift_type.py
index b77d224..eaf6b1e 100644
--- a/erpnext/hr/doctype/shift_type/shift_type.py
+++ b/erpnext/hr/doctype/shift_type/shift_type.py
@@ -8,7 +8,7 @@
import frappe
from frappe.model.document import Document
-from frappe.utils import cint, getdate
+from frappe.utils import cint, getdate, get_datetime
from erpnext.hr.doctype.shift_assignment.shift_assignment import get_actual_start_end_datetime_of_shift, get_employee_shift
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
@@ -53,9 +53,9 @@
date_of_joining, relieving_date, employee_creation = frappe.db.get_value("Employee", employee, ["date_of_joining", "relieving_date", "creation"])
if not date_of_joining:
date_of_joining = employee_creation.date()
- 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_checkin, True)
- last_shift_time = actual_shift_datetime[0] if actual_shift_datetime[0] else self.last_sync_of_checkin
+ start_date = max(getdate(self.process_attendance_after), date_of_joining)
+ actual_shift_datetime = get_actual_start_end_datetime_of_shift(employee, get_datetime(self.last_sync_of_checkin), True)
+ last_shift_time = actual_shift_datetime[0] if actual_shift_datetime[0] else get_datetime(self.last_sync_of_checkin)
prev_shift = get_employee_shift(employee, last_shift_time.date()-timedelta(days=1), True, 'reverse')
if prev_shift:
end_date = min(prev_shift.start_datetime.date(), relieving_date) if relieving_date else prev_shift.start_datetime.date()
@@ -64,10 +64,11 @@
holiday_list_name = self.holiday_list
if not holiday_list_name:
holiday_list_name = get_holiday_list_for_employee(employee, False)
- for date in get_filtered_date_list(employee, start_date, end_date, holiday_list=holiday_list_name):
+ dates = get_filtered_date_list(employee, start_date, end_date, holiday_list=holiday_list_name)
+ for date in dates:
shift_details = get_employee_shift(employee, date, True)
if shift_details and shift_details.shift_type.name == self.name:
- mark_absent(employee, date)
+ mark_absent(employee, date, self.name)
def get_assigned_employee(self, from_date=None, consider_default_shift=False):
filters = {'date':('>=', from_date), 'shift_type': self.name, 'docstatus': '1'}
diff --git a/erpnext/hr/doctype/shift_type/shift_type_dashboard.py b/erpnext/hr/doctype/shift_type/shift_type_dashboard.py
index 91dfbad..0a49dda 100644
--- a/erpnext/hr/doctype/shift_type/shift_type_dashboard.py
+++ b/erpnext/hr/doctype/shift_type/shift_type_dashboard.py
@@ -2,11 +2,11 @@
from frappe import _
def get_data():
- return {
- 'fieldname': 'shift_type',
- 'transactions': [
- {
- 'items': ['Shift Request', 'Shift Assignment']
- }
- ],
- }
\ No newline at end of file
+ return {
+ 'fieldname': 'shift',
+ 'transactions': [
+ {
+ 'items': ['Attendance', 'Employee Checkin', 'Shift Request', 'Shift Assignment']
+ }
+ ]
+ }