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:
parent
98229db3ae
commit
ddcc4fa9b9
@ -1,87 +1,60 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_import": 0,
|
"creation": "2013-02-22 01:27:46",
|
||||||
"allow_rename": 0,
|
"doctype": "DocType",
|
||||||
"beta": 0,
|
"document_type": "Setup",
|
||||||
"creation": "2013-02-22 01:27:46",
|
"editable_grid": 1,
|
||||||
"custom": 0,
|
"engine": "InnoDB",
|
||||||
"docstatus": 0,
|
"field_order": [
|
||||||
"doctype": "DocType",
|
"holiday_date",
|
||||||
"document_type": "Setup",
|
"column_break_2",
|
||||||
"editable_grid": 1,
|
"weekly_off",
|
||||||
|
"section_break_4",
|
||||||
|
"description"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "holiday_date",
|
||||||
"bold": 0,
|
"fieldtype": "Date",
|
||||||
"collapsible": 0,
|
"in_list_view": 1,
|
||||||
"fieldname": "holiday_date",
|
"label": "Date",
|
||||||
"fieldtype": "Date",
|
"oldfieldname": "holiday_date",
|
||||||
"hidden": 0,
|
"oldfieldtype": "Date",
|
||||||
"ignore_user_permissions": 0,
|
"reqd": 1
|
||||||
"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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "description",
|
||||||
"bold": 0,
|
"fieldtype": "Text Editor",
|
||||||
"collapsible": 0,
|
"in_list_view": 1,
|
||||||
"fieldname": "description",
|
"label": "Description",
|
||||||
"fieldtype": "Text Editor",
|
"print_width": "300px",
|
||||||
"hidden": 0,
|
"reqd": 1,
|
||||||
"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"
|
"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,
|
"idx": 1,
|
||||||
"hide_toolbar": 0,
|
"istable": 1,
|
||||||
"idx": 1,
|
"links": [],
|
||||||
"image_view": 0,
|
"modified": "2020-04-18 19:03:23.507845",
|
||||||
"in_create": 0,
|
"modified_by": "Administrator",
|
||||||
|
"module": "HR",
|
||||||
"is_submittable": 0,
|
"name": "Holiday",
|
||||||
"issingle": 0,
|
"owner": "Administrator",
|
||||||
"istable": 1,
|
"permissions": [],
|
||||||
"max_attachments": 0,
|
"sort_field": "modified",
|
||||||
"modified": "2016-07-11 03:28:00.660849",
|
"sort_order": "ASC"
|
||||||
"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
|
|
||||||
}
|
}
|
@ -23,6 +23,7 @@ class HolidayList(Document):
|
|||||||
ch = self.append('holidays', {})
|
ch = self.append('holidays', {})
|
||||||
ch.description = self.weekly_off
|
ch.description = self.weekly_off
|
||||||
ch.holiday_date = d
|
ch.holiday_date = d
|
||||||
|
ch.weekly_off = 1
|
||||||
ch.idx = last_idx + i + 1
|
ch.idx = last_idx + i + 1
|
||||||
|
|
||||||
def validate_values(self):
|
def validate_values(self):
|
||||||
|
@ -31,6 +31,18 @@ frappe.query_reports["Monthly Attendance Sheet"] = {
|
|||||||
"options": "Company",
|
"options": "Company",
|
||||||
"default": frappe.defaults.get_user_default("Company"),
|
"default": frappe.defaults.get_user_default("Company"),
|
||||||
"reqd": 1
|
"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,
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -7,65 +7,127 @@ from frappe.utils import cstr, cint, getdate
|
|||||||
from frappe import msgprint, _
|
from frappe import msgprint, _
|
||||||
from calendar import monthrange
|
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):
|
def execute(filters=None):
|
||||||
if not filters: filters = {}
|
if not filters: filters = {}
|
||||||
|
|
||||||
conditions, filters = get_conditions(filters)
|
conditions, filters = get_conditions(filters)
|
||||||
columns = get_columns(filters)
|
columns = get_columns(filters)
|
||||||
att_map = get_attendance_list(conditions, 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")
|
default_holiday_list = frappe.get_cached_value('Company', filters.get("company"), "default_holiday_list")
|
||||||
holiday_list.append(default_holiday_list)
|
holiday_list.append(default_holiday_list)
|
||||||
holiday_list = list(set(holiday_list))
|
holiday_list = list(set(holiday_list))
|
||||||
holiday_map = get_holiday(holiday_list, filters["month"])
|
holiday_map = get_holiday(holiday_list, filters["month"])
|
||||||
|
|
||||||
data = []
|
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):
|
leave_list = None
|
||||||
emp_det = emp_map.get(emp)
|
if filters.summarized_view:
|
||||||
if not emp_det:
|
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([ "<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
|
continue
|
||||||
|
|
||||||
row = [emp, emp_det.employee_name, emp_det.branch, emp_det.department, emp_det.designation,
|
row = []
|
||||||
emp_det.company]
|
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"]):
|
for day in range(filters["total_days_in_month"]):
|
||||||
|
status = None
|
||||||
status = att_map.get(emp).get(day + 1)
|
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:
|
if status is None and holiday_map:
|
||||||
emp_holiday_list = emp_det.holiday_list if emp_det.holiday_list else default_holiday_list
|
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"):
|
if not filters.get("employee"):
|
||||||
filters.update({"employee": emp})
|
filters.update({"employee": emp})
|
||||||
@ -73,43 +135,53 @@ def execute(filters=None):
|
|||||||
elif not filters.get("employee") == emp:
|
elif not filters.get("employee") == emp:
|
||||||
filters.update({"employee": emp})
|
filters.update({"employee": emp})
|
||||||
|
|
||||||
leave_details = frappe.db.sql("""select leave_type, status, count(*) as count from `tabAttendance`\
|
if filters.summarized_view:
|
||||||
where leave_type is not NULL %s group by leave_type, status""" % conditions, filters, as_dict=1)
|
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 \
|
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 \
|
late_entry = 1 %s) as late_entry_count, (select count(*) from tabAttendance where \
|
||||||
early_exit = 1 %s) as early_exit_count""" % (conditions, conditions), filters)
|
early_exit = 1 %s) as early_exit_count""" % (conditions, conditions), filters)
|
||||||
|
|
||||||
leaves = {}
|
leaves = {}
|
||||||
for d in leave_details:
|
for d in leave_details:
|
||||||
if d.status == "Half Day":
|
if d.status == "Half Day":
|
||||||
d.count = d.count * 0.5
|
d.count = d.count * 0.5
|
||||||
if d.leave_type in leaves:
|
if d.leave_type in leaves:
|
||||||
leaves[d.leave_type] += d.count
|
leaves[d.leave_type] += d.count
|
||||||
else:
|
else:
|
||||||
leaves[d.leave_type] = d.count
|
leaves[d.leave_type] = d.count
|
||||||
|
|
||||||
for d in leave_list:
|
for d in leave_list:
|
||||||
if d in leaves:
|
if d in leaves:
|
||||||
row.append(leaves[d])
|
row.append(leaves[d])
|
||||||
else:
|
else:
|
||||||
row.append("0.0")
|
row.append("0.0")
|
||||||
|
|
||||||
row.extend([time_default_counts[0][0],time_default_counts[0][1]])
|
row.extend([time_default_counts[0][0],time_default_counts[0][1]])
|
||||||
data.append(row)
|
record.append(row)
|
||||||
return columns, data
|
|
||||||
|
|
||||||
|
return record
|
||||||
|
|
||||||
def get_columns(filters):
|
def get_columns(filters):
|
||||||
columns = [
|
|
||||||
_("Employee") + ":Link/Employee:120", _("Employee Name") + "::140", _("Branch")+ ":Link/Branch:120",
|
columns = []
|
||||||
_("Department") + ":Link/Department:120", _("Designation") + ":Link/Designation:120",
|
|
||||||
_("Company") + ":Link/Company:120"
|
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"]):
|
if not filters.summarized_view:
|
||||||
columns.append(cstr(day+1) +"::20")
|
for day in range(filters["total_days_in_month"]):
|
||||||
|
date = str(filters.year) + "-" + str(filters.month)+ "-" + str(day+1)
|
||||||
columns += [_("Total Present") + ":Float:80", _("Total Leaves") + ":Float:80", _("Total Absent") + ":Float:80"]
|
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
|
return columns
|
||||||
|
|
||||||
def get_attendance_list(conditions, filters):
|
def get_attendance_list(conditions, filters):
|
||||||
@ -140,19 +212,43 @@ def get_conditions(filters):
|
|||||||
|
|
||||||
return conditions, filters
|
return conditions, filters
|
||||||
|
|
||||||
def get_employee_details(filters):
|
def get_employee_details(group_by, company):
|
||||||
emp_map = frappe._dict()
|
emp_map = {}
|
||||||
for d in frappe.db.sql("""select name, employee_name, designation, department, branch, company,
|
query = """select name, employee_name, designation, department, branch, company,
|
||||||
holiday_list from tabEmployee where company = %s""", (filters.get("company")), as_dict=1):
|
holiday_list from `tabEmployee` where company = '%s' """ % frappe.db.escape(company)
|
||||||
emp_map.setdefault(d.name, d)
|
|
||||||
|
|
||||||
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):
|
def get_holiday(holiday_list, month):
|
||||||
holiday_map = frappe._dict()
|
holiday_map = frappe._dict()
|
||||||
for d in holiday_list:
|
for d in holiday_list:
|
||||||
if d:
|
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)))
|
where parent=%s and month(holiday_date)=%s''', (d, month)))
|
||||||
|
|
||||||
return holiday_map
|
return holiday_map
|
||||||
|
Loading…
x
Reference in New Issue
Block a user