diff --git a/erpnext/hr/doctype/holiday/holiday.json b/erpnext/hr/doctype/holiday/holiday.json
index 6498530eb1..6bd0ab0dcb 100644
--- a/erpnext/hr/doctype/holiday/holiday.json
+++ b/erpnext/hr/doctype/holiday/holiday.json
@@ -1,87 +1,60 @@
{
- "allow_copy": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2013-02-22 01:27:46",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Setup",
- "editable_grid": 1,
+ "actions": [],
+ "creation": "2013-02-22 01:27:46",
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "holiday_date",
+ "column_break_2",
+ "weekly_off",
+ "section_break_4",
+ "description"
+ ],
"fields": [
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "fieldname": "holiday_date",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 1,
- "label": "Date",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "holiday_date",
- "oldfieldtype": "Date",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "holiday_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "Date",
+ "oldfieldname": "holiday_date",
+ "oldfieldtype": "Date",
+ "reqd": 1
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "fieldname": "description",
- "fieldtype": "Text Editor",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 1,
- "label": "Description",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": "300px",
- "read_only": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0,
+ "fieldname": "description",
+ "fieldtype": "Text Editor",
+ "in_list_view": 1,
+ "label": "Description",
+ "print_width": "300px",
+ "reqd": 1,
"width": "300px"
+ },
+ {
+ "default": "0",
+ "fieldname": "weekly_off",
+ "fieldtype": "Check",
+ "label": "Weekly Off"
+ },
+ {
+ "fieldname": "column_break_2",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "section_break_4",
+ "fieldtype": "Section Break"
}
- ],
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 1,
- "image_view": 0,
- "in_create": 0,
-
- "is_submittable": 0,
- "issingle": 0,
- "istable": 1,
- "max_attachments": 0,
- "modified": "2016-07-11 03:28:00.660849",
- "modified_by": "Administrator",
- "module": "HR",
- "name": "Holiday",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "sort_order": "ASC",
- "track_seen": 0
+ ],
+ "idx": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2020-04-18 19:03:23.507845",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Holiday",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "ASC"
}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/holiday_list/holiday_list.py b/erpnext/hr/doctype/holiday_list/holiday_list.py
index 8c7b6f723f..76dc9429f1 100644
--- a/erpnext/hr/doctype/holiday_list/holiday_list.py
+++ b/erpnext/hr/doctype/holiday_list/holiday_list.py
@@ -23,6 +23,7 @@ class HolidayList(Document):
ch = self.append('holidays', {})
ch.description = self.weekly_off
ch.holiday_date = d
+ ch.weekly_off = 1
ch.idx = last_idx + i + 1
def validate_values(self):
diff --git a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js
index 348c5e7cb7..bd4ed3c4ca 100644
--- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js
+++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js
@@ -31,6 +31,18 @@ frappe.query_reports["Monthly Attendance Sheet"] = {
"options": "Company",
"default": frappe.defaults.get_user_default("Company"),
"reqd": 1
+ },
+ {
+ "fieldname":"group_by",
+ "label": __("Group By"),
+ "fieldtype": "Select",
+ "options": ["","Branch","Grade","Department","Designation"]
+ },
+ {
+ "fieldname":"summarized_view",
+ "label": __("Summarized View"),
+ "fieldtype": "Check",
+ "Default": 0,
}
],
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 9a9e42e5e8..d98ed1b414 100644
--- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py
+++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py
@@ -7,65 +7,127 @@ from frappe.utils import cstr, cint, getdate
from frappe import msgprint, _
from calendar import monthrange
+status_map = {
+ "Absent": "A",
+ "Half Day": "HD",
+ "Holiday": "H",
+ "Weekly Off": "WO",
+ "On Leave": "L",
+ "Present": "P",
+ "Work From Home": "WFH"
+ }
+
+day_abbr = [
+ "Mon",
+ "Tue",
+ "Wed",
+ "Thu",
+ "Fri",
+ "Sat",
+ "Sun"
+]
+
def execute(filters=None):
if not filters: filters = {}
conditions, filters = get_conditions(filters)
columns = get_columns(filters)
att_map = get_attendance_list(conditions, filters)
- emp_map = get_employee_details(filters)
- holiday_list = [emp_map[d]["holiday_list"] for d in emp_map if emp_map[d]["holiday_list"]]
+ if filters.group_by:
+ emp_map, group_by_parameters = get_employee_details(filters.group_by, filters.company)
+ holiday_list = []
+ for parameter in group_by_parameters:
+ h_list = [emp_map[parameter][d]["holiday_list"] for d in emp_map[parameter] if emp_map[parameter][d]["holiday_list"]]
+ holiday_list += h_list
+ else:
+ emp_map = get_employee_details(filters.group_by, filters.company)
+ holiday_list = [emp_map[d]["holiday_list"] for d in emp_map if emp_map[d]["holiday_list"]]
+
+
default_holiday_list = frappe.get_cached_value('Company', filters.get("company"), "default_holiday_list")
holiday_list.append(default_holiday_list)
holiday_list = list(set(holiday_list))
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)
- columns.extend([_("Total Late Entries") + ":Float:120", _("Total Early Exits") + ":Float:120"])
- for emp in sorted(att_map):
- emp_det = emp_map.get(emp)
- if not emp_det:
+ leave_list = None
+ if filters.summarized_view:
+ leave_types = frappe.db.sql("""select name from `tabLeave Type`""", as_list=True)
+ leave_list = [d[0] + ":Float:120" for d in leave_types]
+ columns.extend(leave_list)
+ columns.extend([_("Total Late Entries") + ":Float:120", _("Total Early Exits") + ":Float:120"])
+
+ if filters.group_by:
+ for parameter in group_by_parameters:
+ data.append([ ""+ parameter + ""])
+ record = add_data(emp_map[parameter], att_map, filters, holiday_map, conditions, leave_list=leave_list)
+ data += record
+ else:
+ record = add_data(emp_map, att_map, filters, holiday_map, conditions, leave_list=leave_list)
+ data += record
+
+ return columns, data
+
+
+def add_data(employee_map, att_map, filters, holiday_map, conditions, leave_list=None):
+
+ record = []
+ for emp in employee_map:
+ emp_det = employee_map.get(emp)
+ if not emp_det or emp not in att_map:
continue
- row = [emp, emp_det.employee_name, emp_det.branch, emp_det.department, emp_det.designation,
- emp_det.company]
+ row = []
+ if filters.group_by:
+ row += [" "]
+ row += [emp, emp_det.employee_name]
- total_p = total_a = total_l = 0.0
+ total_p = total_a = total_l = total_h = total_um= 0.0
for day in range(filters["total_days_in_month"]):
+ status = None
status = att_map.get(emp).get(day + 1)
- status_map = {
- "Absent": "A",
- "Half Day": "HD",
- "Holiday":"H",
- "On Leave": "L",
- "Present": "P",
- "Work From Home": "WFH"
- }
if status is None and holiday_map:
emp_holiday_list = emp_det.holiday_list if emp_det.holiday_list else default_holiday_list
- if emp_holiday_list in holiday_map and (day+1) in holiday_map[emp_holiday_list]:
- status = "Holiday"
- row.append(status_map.get(status, ""))
+ if emp_holiday_list in holiday_map:
+ for idx, ele in enumerate(holiday_map[emp_holiday_list]):
+ if day+1 == holiday_map[emp_holiday_list][idx][0]:
+ if holiday_map[emp_holiday_list][idx][1]:
+ status = "Weekly Off"
+ else:
+ status = "Holiday"
+ total_h += 1
- if status == "Present":
- total_p += 1
- elif status == "Absent":
- total_a += 1
- elif status == "On Leave":
- total_l += 1
- elif status == "Half Day":
- total_p += 0.5
- total_a += 0.5
- total_l += 0.5
- row += [total_p, total_l, total_a]
+ # if emp_holiday_list in holiday_map and (day+1) in holiday_map[emp_holiday_list][0]:
+ # if holiday_map[emp_holiday_list][1]:
+ # status= "Weekly Off"
+ # else:
+ # status = "Holiday"
+
+ # += 1
+
+ if not filters.summarized_view:
+ row.append(status_map.get(status, ""))
+ else:
+ if status == "Present":
+ total_p += 1
+ elif status == "Absent":
+ total_a += 1
+ elif status == "On Leave":
+ total_l += 1
+ elif status == "Half Day":
+ total_p += 0.5
+ total_a += 0.5
+ total_l += 0.5
+ elif not status:
+ total_um += 1
+
+ if filters.summarized_view:
+ row += [total_p, total_l, total_a, total_h, total_um]
if not filters.get("employee"):
filters.update({"employee": emp})
@@ -73,43 +135,53 @@ def execute(filters=None):
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)
+ if filters.summarized_view:
+ 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)
- time_default_counts = frappe.db.sql("""select (select count(*) from `tabAttendance` where \
- late_entry = 1 %s) as late_entry_count, (select count(*) from tabAttendance where \
- early_exit = 1 %s) as early_exit_count""" % (conditions, conditions), filters)
+ time_default_counts = frappe.db.sql("""select (select count(*) from `tabAttendance` where \
+ late_entry = 1 %s) as late_entry_count, (select count(*) from tabAttendance where \
+ early_exit = 1 %s) as early_exit_count""" % (conditions, conditions), filters)
- 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
+ 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")
+ for d in leave_list:
+ if d in leaves:
+ row.append(leaves[d])
+ else:
+ row.append("0.0")
- row.extend([time_default_counts[0][0],time_default_counts[0][1]])
- data.append(row)
- return columns, data
+ row.extend([time_default_counts[0][0],time_default_counts[0][1]])
+ record.append(row)
+
+
+ return record
def get_columns(filters):
- columns = [
- _("Employee") + ":Link/Employee:120", _("Employee Name") + "::140", _("Branch")+ ":Link/Branch:120",
- _("Department") + ":Link/Department:120", _("Designation") + ":Link/Designation:120",
- _("Company") + ":Link/Company:120"
+
+ columns = []
+
+ if filters.group_by:
+ columns = [_(filters.group_by)+ ":Link/Branch:120"]
+
+ columns += [
+ _("Employee") + ":Link/Employee:120", _("Employee Name") + ":Link/Employee:120"
]
- for day in range(filters["total_days_in_month"]):
- columns.append(cstr(day+1) +"::20")
-
- columns += [_("Total Present") + ":Float:80", _("Total Leaves") + ":Float:80", _("Total Absent") + ":Float:80"]
+ if not filters.summarized_view:
+ for day in range(filters["total_days_in_month"]):
+ date = str(filters.year) + "-" + str(filters.month)+ "-" + str(day+1)
+ day_name = day_abbr[getdate(date).weekday()]
+ columns.append(cstr(day+1)+ " " +day_name +"::65")
+ else:
+ columns += [_("Total Present") + ":Float:120", _("Total Leaves") + ":Float:120", _("Total Absent") + ":Float:120", _("Total Holidays") + ":Float:120", _("Unmarked Days")+ ":Float:120"]
return columns
def get_attendance_list(conditions, filters):
@@ -140,19 +212,43 @@ def get_conditions(filters):
return conditions, filters
-def get_employee_details(filters):
- emp_map = frappe._dict()
- for d in frappe.db.sql("""select name, employee_name, designation, department, branch, company,
- holiday_list from tabEmployee where company = %s""", (filters.get("company")), as_dict=1):
- emp_map.setdefault(d.name, d)
+def get_employee_details(group_by, company):
+ emp_map = {}
+ query = """select name, employee_name, designation, department, branch, company,
+ holiday_list from `tabEmployee` where company = '%s' """ % frappe.db.escape(company)
- return emp_map
+ if group_by:
+ group_by = group_by.lower()
+ query += " order by " + group_by + " ASC"
+
+ employee_details = frappe.db.sql(query , as_dict=1)
+
+ group_by_parameters = []
+ if group_by:
+
+ group_by_parameters = list(set(detail.get(group_by, "") for detail in employee_details if detail.get(group_by, "")))
+ for parameter in group_by_parameters:
+ emp_map[parameter] = {}
+
+
+ for d in employee_details:
+ if group_by and len(group_by_parameters):
+ if d.get(group_by, None):
+
+ emp_map[d.get(group_by)][d.name] = d
+ else:
+ emp_map[d.name] = d
+
+ if not group_by:
+ return emp_map
+ else:
+ return emp_map, group_by_parameters
def get_holiday(holiday_list, month):
holiday_map = frappe._dict()
for d in holiday_list:
if d:
- holiday_map.setdefault(d, frappe.db.sql_list('''select day(holiday_date) from `tabHoliday`
+ holiday_map.setdefault(d, frappe.db.sql('''select day(holiday_date), weekly_off from `tabHoliday`
where parent=%s and month(holiday_date)=%s''', (d, month)))
return holiday_map