From 701c0b792ca70d58bb40f7af778f65559b6a46b9 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 8 Mar 2019 12:30:42 +0530 Subject: [PATCH 1/2] feat: Pre mark attendance for Leave Application --- erpnext/hr/doctype/attendance/attendance.json | 35 +++++++++++++++- erpnext/hr/doctype/attendance/attendance.py | 3 +- .../leave_application/leave_application.py | 31 +++++++------- .../test_leave_application.py | 41 ++++++++++++++++++- 4 files changed, 91 insertions(+), 19 deletions(-) diff --git a/erpnext/hr/doctype/attendance/attendance.json b/erpnext/hr/doctype/attendance/attendance.json index 97d28e7e88..2459b7a6df 100644 --- a/erpnext/hr/doctype/attendance/attendance.json +++ b/erpnext/hr/doctype/attendance/attendance.json @@ -218,6 +218,39 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "leave_application", + "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": "Leave Application", + "length": 0, + "no_copy": 0, + "options": "Leave Application", + "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, @@ -428,7 +461,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-01-30 11:28:13.075959", + "modified": "2019-03-08 12:00:14.043535", "modified_by": "Administrator", "module": "HR", "name": "Attendance", diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py index 74a53035ad..7dd9f0e10c 100644 --- a/erpnext/hr/doctype/attendance/attendance.py +++ b/erpnext/hr/doctype/attendance/attendance.py @@ -40,7 +40,8 @@ class Attendance(Document): def validate_attendance_date(self): date_of_joining = frappe.db.get_value("Employee", self.employee, "date_of_joining") - if getdate(self.attendance_date) > getdate(nowdate()): + # leaves can be marked for future dates + if self.status not in ('On Leave', 'Half Day') and getdate(self.attendance_date) > getdate(nowdate()): frappe.throw(_("Attendance can not be marked for future dates")) elif date_of_joining and getdate(self.attendance_date) < getdate(date_of_joining): frappe.throw(_("Attendance date can not be less than employee's joining date")) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index b85f38b295..819bbf9d20 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -117,28 +117,29 @@ class LeaveApplication(Document): def update_attendance(self): if self.status == "Approved": - attendance = frappe.db.sql("""select name from `tabAttendance` where employee = %s\ - and (attendance_date between %s and %s) and docstatus < 2""",(self.employee, self.from_date, self.to_date), as_dict=1) + for dt in daterange(getdate(self.from_date), getdate(self.to_date)): + date = dt.strftime("%Y-%m-%d") + status = "Half Day" if date == self.half_day_date else "On Leave" - if attendance: - for d in attendance: - doc = frappe.get_doc("Attendance", d.name) - if getdate(self.half_day_date) == doc.attendance_date: - status = "Half Day" - else: - status = "On Leave" - frappe.db.sql("""update `tabAttendance` set status = %s, leave_type = %s\ - where name = %s""",(status, self.leave_type, d.name)) + attendance_name = frappe.db.exists('Attendance', dict(employee = self.employee, + attenance_date = date, docstatus = ('!=', 2))) - elif getdate(self.to_date) <= getdate(nowdate()): - for dt in daterange(getdate(self.from_date), getdate(self.to_date)): - date = dt.strftime("%Y-%m-%d") + if attendance_name: + # update existing attendance, change absent to on leave + doc = frappe.get_doc('Attendance', attendance_date) + if doc.status != status: + doc.db_set('status', status) + doc.db_set('leave_type', self.leave_type) + doc.db_set('leave_application', self.name) + else: + # make new attendance and submit it doc = frappe.new_doc("Attendance") doc.employee = self.employee doc.attendance_date = date doc.company = self.company doc.leave_type = self.leave_type - doc.status = "Half Day" if date == self.half_day_date else "On Leave" + doc.leave_application = self.name + doc.status = status doc.flags.ignore_validate = True doc.insert(ignore_permissions=True) doc.submit() diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index a682e8b76f..d3dcca1da0 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -7,7 +7,7 @@ import unittest from erpnext.hr.doctype.leave_application.leave_application import LeaveDayBlockedError, OverlapError, NotAnOptionalHoliday, get_leave_balance_on from frappe.permissions import clear_user_permissions_for_doctype -from frappe.utils import add_days, nowdate, now_datetime +from frappe.utils import add_days, nowdate, now_datetime, getdate test_dependencies = ["Leave Allocation", "Leave Block List"] @@ -67,6 +67,43 @@ class TestLeaveApplication(unittest.TestCase): application.to_date = "2013-01-05" return application + def test_attendance_creation(self): + '''check attendance is automatically created on leave approval''' + make_allocation_record() + application = self.get_application(_test_records[0]) + application.status = 'Approved' + application.from_date = '2018-01-01' + application.to_date = '2018-01-03' + application.insert() + application.submit() + + attendance = frappe.get_all('Attendance', ['name', 'status', 'attendance_date'], dict(leave_application = application.name)) + + # attendance created for all 3 days + self.assertEqual(len(attendance), 3) + + # all on leave + self.assertTrue(all([d.status == 'On Leave' for d in attendance])) + + # dates + dates = [d.attendance_date for d in attendance] + for d in ('2018-01-01', '2018-01-02', '2018-01-03'): + self.assertTrue(getdate(d) in dates) + + def test_overwrite_attendance(self): + # employee marked as absent + doc = frappe.new_doc("Attendance") + doc.employee = '_T-Employee-00001' + doc.attendance_date = '2018-01-01' + doc.company = '_Test Company' + doc.status = 'Absent' + doc.flags.ignore_validate = True + doc.insert(ignore_permissions=True) + doc.submit() + + # now check if the status has been updated + self.test_attendance_creation() + def test_block_list(self): self._clear_roles() @@ -428,7 +465,7 @@ def make_allocation_record(employee=None, leave_type=None): "employee": employee or "_T-Employee-00001", "leave_type": leave_type or "_Test Leave Type", "from_date": "2013-01-01", - "to_date": "2015-12-31", + "to_date": "2019-12-31", "new_leaves_allocated": 30 }) From 96842c9de6f336ce23926e145cfe777bcc1e5559 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 8 Mar 2019 12:36:43 +0530 Subject: [PATCH 2/2] fix(typo): leave_application.py --- erpnext/hr/doctype/leave_application/leave_application.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 819bbf9d20..cb0484f58f 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -126,7 +126,7 @@ class LeaveApplication(Document): if attendance_name: # update existing attendance, change absent to on leave - doc = frappe.get_doc('Attendance', attendance_date) + doc = frappe.get_doc('Attendance', attendance_name) if doc.status != status: doc.db_set('status', status) doc.db_set('leave_type', self.leave_type)