From bbee9b6cc441357e96500cc1180beb49b1e7ba3a Mon Sep 17 00:00:00 2001 From: Shreya Shah Date: Mon, 14 May 2018 14:30:58 +0530 Subject: [PATCH] [Enhancement] Monthly Attendance Report (#13970) * Update Attendance on Approval of Leave * Separate out leaves on the basis of its type * Remove commented code * Make attendance records if not found * Fix Codacy * Replace bad code in attendance.py --- erpnext/hr/doctype/attendance/attendance.py | 18 +++--- .../leave_application/leave_application.py | 61 +++++++++++++------ .../monthly_attendance_sheet.py | 31 +++++++++- 3 files changed, 82 insertions(+), 28 deletions(-) diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py index 458b2dd5c1..d1809c60cb 100644 --- a/erpnext/hr/doctype/attendance/attendance.py +++ b/erpnext/hr/doctype/attendance/attendance.py @@ -20,17 +20,19 @@ class Attendance(Document): set_employee_name(self) def check_leave_record(self): - leave_record = frappe.db.sql("""select leave_type, half_day from `tabLeave Application` + leave_record = frappe.db.sql("""select leave_type, half_day, half_day_date from `tabLeave Application` where employee = %s and %s between from_date and to_date and status = 'Approved' and docstatus = 1""", (self.employee, self.attendance_date), as_dict=True) if leave_record: - if leave_record[0].half_day: - self.status = 'Half Day' - frappe.msgprint(_("Employee {0} on Half day on {1}").format(self.employee, self.attendance_date)) - else: - self.status = 'On Leave' - self.leave_type = leave_record[0].leave_type - frappe.msgprint(_("Employee {0} on Leave on {1}").format(self.employee, self.attendance_date)) + for d in leave_record: + if d.half_day_date == getdate(self.attendance_date): + self.status = 'Half Day' + frappe.msgprint(_("Employee {0} on Half day on {1}").format(self.employee, self.attendance_date)) + else: + self.status = 'On Leave' + self.leave_type = d.leave_type + frappe.msgprint(_("Employee {0} on Leave on {1}").format(self.employee, self.attendance_date)) + if self.status == "On Leave" and not leave_record: frappe.throw(_("No leave record found for employee {0} for {1}").format(self.employee, self.attendance_date)) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 8d19510624..4e1b54b95b 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -5,12 +5,12 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, get_link_to_form, \ - comma_or, get_fullname + comma_or, get_fullname, nowdate from erpnext.hr.utils import set_employee_name from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee from erpnext.hr.doctype.employee_leave_approver.employee_leave_approver import get_approver_list - +from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import daterange class LeaveDayBlockedError(frappe.ValidationError): pass class OverlapError(frappe.ValidationError): pass @@ -52,6 +52,7 @@ class LeaveApplication(Document): frappe.throw(_("Only Leave Applications with status 'Approved' and 'Rejected' can be submitted")) self.validate_back_dated_application() + self.update_attendance() # notify leave applier about approval self.notify_employee(self.status) @@ -100,6 +101,41 @@ class LeaveApplication(Document): frappe.throw(_("Leave cannot be applied/cancelled before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}") .format(formatdate(future_allocation[0].from_date), future_allocation[0].name)) + 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) + + 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)) + + elif self.from_date <= nowdate(): + for dt in daterange(getdate(self.from_date), getdate(self.to_date)): + date = dt.strftime("%Y-%m-%d") + if not date == self.half_day_date: + doc = frappe.new_doc("Attendance") + doc.employee = self.employee + doc.attendance_date = date + doc.company = self.company + doc.status = "On Leave" + doc.leave_type = self.leave_type + doc.submit() + else: + doc = frappe.new_doc("Attendance") + doc.employee = self.employee + doc.attendance_date = date + doc.company = self.company + doc.status = "Half Day" + doc.leave_type = self.leave_type + doc.submit() + def validate_salary_processed_days(self): if not frappe.db.get_value("Leave Type", self.leave_type, "is_lwp"): return @@ -436,30 +472,17 @@ def add_department_leaves(events, start, end, employee, company): and company=%s""", (department, company)) match_conditions = "and employee in (\"%s\")" % '", "'.join(department_employees) - add_leaves(events, start, end, filter_conditions=match_conditions) - -def add_leaves(events, start, end, filter_conditions=None): - conditions = [] - - if filter_conditions: - conditions.append(filter_conditions) - - if not cint(frappe.db.get_value("HR Settings", None, "show_leaves_of_all_department_members_in_calendar")): - from frappe.desk.reportview import build_match_conditions - match_conditions = build_match_conditions("Leave Application") - - if match_conditions: - conditions.append(match_conditions) + add_leaves(events, start, end, match_conditions=match_conditions) +def add_leaves(events, start, end, match_conditions=None): query = """select name, from_date, to_date, employee_name, half_day, status, employee, docstatus from `tabLeave Application` where from_date <= %(end)s and to_date >= %(start)s <= to_date and docstatus < 2 and status!="Rejected" """ - - if conditions: - query += ' and '.join(conditions) + if match_conditions: + query += match_conditions for d in frappe.db.sql(query, {"start":start, "end": end}, as_dict=True): e = { diff --git a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py index 698c4fbd7c..0c338e0136 100644 --- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py +++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py @@ -22,6 +22,10 @@ def execute(filters=None): holiday_map = get_holiday(holiday_list, filters["month"]) data = [] + leave_types = frappe.db.sql("""select name from `tabLeave Type`""", as_list=True) + leave_list = [d[0] for d in leave_types] + columns.extend(leave_list) + for emp in sorted(att_map): emp_det = emp_map.get(emp) if not emp_det: @@ -49,10 +53,35 @@ def execute(filters=None): elif status == "Half Day": total_p += 0.5 total_a += 0.5 + total_l += 0.5 row += [total_p, total_l, total_a] - data.append(row) + if not filters.get("employee"): + filters.update({"employee": emp}) + conditions += " and employee = %(employee)s" + elif not filters.get("employee") == emp: + filters.update({"employee": emp}) + + leave_details = frappe.db.sql("""select leave_type, status, count(*) as count from `tabAttendance`\ + where leave_type is not NULL %s group by leave_type, status""" % conditions, filters, as_dict=1) + + leaves = {} + for d in leave_details: + if d.status == "Half Day": + d.count = d.count * 0.5 + if d.leave_type in leaves: + leaves[d.leave_type] += d.count + else: + leaves[d.leave_type] = d.count + + for d in leave_list: + if d in leaves: + row.append(leaves[d]) + else: + row.append("0.0") + + data.append(row) return columns, data def get_columns(filters):