Feat: Monthly attendance sheet Enhancements and UI/UX Improvements (#21331)

* refactor: Employee attendance sheet redesign

* feat: Added weekly off status in monthly Attendance sheet

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
This commit is contained in:
Anurag Mishra 2020-05-05 16:08:05 +05:30 committed by GitHub
parent 98229db3ae
commit ddcc4fa9b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 232 additions and 150 deletions

View File

@ -1,87 +1,60 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"actions": [],
"creation": "2013-02-22 01:27:46",
"custom": 0,
"docstatus": 0,
"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
"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,
"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",
"links": [],
"modified": "2020-04-18 19:03:23.507845",
"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
"sort_field": "modified",
"sort_order": "ASC"
}

View File

@ -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):

View File

@ -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,
}
],

View File

@ -7,53 +7,112 @@ from frappe.utils import cstr, cint, getdate
from frappe import msgprint, _
from calendar import monthrange
status_map = {
"Absent": "A",
"Half Day": "HD",
"Holiday": "<b>H</b>",
"Weekly Off": "<b>WO</b>",
"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)
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_list = None
if filters.summarized_view:
leave_types = frappe.db.sql("""select name from `tabLeave Type`""", as_list=True)
leave_list = [d[0] for d in leave_types]
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"])
for emp in sorted(att_map):
emp_det = emp_map.get(emp)
if not emp_det:
if filters.group_by:
for parameter in group_by_parameters:
data.append([ "<b>"+ parameter + "</b>"])
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":"<b>H</b>",
"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]:
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 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":
@ -64,8 +123,11 @@ def execute(filters=None):
total_p += 0.5
total_a += 0.5
total_l += 0.5
elif not status:
total_um += 1
row += [total_p, total_l, total_a]
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,6 +135,7 @@ def execute(filters=None):
elif not filters.get("employee") == emp:
filters.update({"employee": emp})
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)
@ -96,20 +159,29 @@ def execute(filters=None):
row.append("0.0")
row.extend([time_default_counts[0][0],time_default_counts[0][1]])
data.append(row)
return columns, data
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"
]
if not filters.summarized_view:
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"]
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)
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