From 7beb013f1a3e329672f2cc9fe98ac2e4b0941d1e Mon Sep 17 00:00:00 2001 From: krantheman Date: Fri, 13 Sep 2024 15:25:38 +0530 Subject: [PATCH 1/2] feat(Monthly Attendance Sheet): add Include Company Descendants filter --- .../monthly_attendance_sheet.js | 8 +++++++- .../monthly_attendance_sheet.py | 18 ++++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/hrms/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js b/hrms/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js index ee6ad2af3c..0f8e753291 100644 --- a/hrms/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js +++ b/hrms/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js @@ -58,11 +58,17 @@ frappe.query_reports["Monthly Attendance Sheet"] = { fieldtype: "Select", options: ["", "Branch", "Grade", "Department", "Designation"], }, + { + fieldname: "include_company_descendants", + label: __("Include Company Descendants"), + fieldtype: "Check", + default: 1, + }, { fieldname: "summarized_view", label: __("Summarized View"), fieldtype: "Check", - Default: 0, + default: 0, }, ], onload: function () { diff --git a/hrms/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py b/hrms/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py index e74bac6a82..e4e6caf3cd 100644 --- a/hrms/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py +++ b/hrms/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py @@ -9,6 +9,7 @@ from frappe import _ from frappe.query_builder.functions import Count, Extract, Sum from frappe.utils import cint, cstr, getdate +from frappe.utils.nestedset import get_descendants_of Filters = frappe._dict @@ -31,6 +32,11 @@ def execute(filters: Filters | None = None) -> tuple: if not (filters.month and filters.year): frappe.throw(_("Please select month and year.")) + if filters.company: + filters.companies = [filters.company] + if filters.include_company_descendants: + filters.companies.extend(get_descendants_of("Company", filters.company)) + attendance_map = get_attendance_map(filters) if not attendance_map: frappe.msgprint(_("No attendance records found."), alert=True, indicator="orange") @@ -257,7 +263,7 @@ def get_attendance_records(filters: Filters) -> list[dict]: ) .where( (Attendance.docstatus == 1) - & (Attendance.company == filters.company) + & (Attendance.company.isin(filters.companies)) & (Extract("month", Attendance.attendance_date) == filters.month) & (Extract("year", Attendance.attendance_date) == filters.year) ) @@ -288,7 +294,7 @@ def get_employee_related_details(filters: Filters) -> tuple[dict, list]: Employee.company, Employee.holiday_list, ) - .where(Employee.company == filters.company) + .where(Employee.company.isin(filters.companies)) ) if filters.employee: @@ -466,7 +472,7 @@ def get_attendance_summary_and_days(employee: str, filters: Filters) -> tuple[di .where( (Attendance.docstatus == 1) & (Attendance.employee == employee) - & (Attendance.company == filters.company) + & (Attendance.company.isin(filters.companies)) & (Extract("month", Attendance.attendance_date) == filters.month) & (Extract("year", Attendance.attendance_date) == filters.year) ) @@ -479,7 +485,7 @@ def get_attendance_summary_and_days(employee: str, filters: Filters) -> tuple[di .where( (Attendance.docstatus == 1) & (Attendance.employee == employee) - & (Attendance.company == filters.company) + & (Attendance.company.isin(filters.companies)) & (Extract("month", Attendance.attendance_date) == filters.month) & (Extract("year", Attendance.attendance_date) == filters.year) ) @@ -543,7 +549,7 @@ def get_leave_summary(employee: str, filters: Filters) -> dict[str, float]: .where( (Attendance.employee == employee) & (Attendance.docstatus == 1) - & (Attendance.company == filters.company) + & (Attendance.company.isin(filters.companies)) & ((Attendance.leave_type.isnotnull()) | (Attendance.leave_type != "")) & (Extract("month", Attendance.attendance_date) == filters.month) & (Extract("year", Attendance.attendance_date) == filters.year) @@ -577,7 +583,7 @@ def get_entry_exits_summary(employee: str, filters: Filters) -> dict[str, float] .where( (Attendance.docstatus == 1) & (Attendance.employee == employee) - & (Attendance.company == filters.company) + & (Attendance.company.isin(filters.companies)) & (Extract("month", Attendance.attendance_date) == filters.month) & (Extract("year", Attendance.attendance_date) == filters.year) ) From 81bab7a9e5f3e58708fa67a5d9d9e638e12fd7d6 Mon Sep 17 00:00:00 2001 From: krantheman Date: Fri, 13 Sep 2024 16:14:23 +0530 Subject: [PATCH 2/2] test: add test for company filter --- .../test_monthly_attendance_sheet.py | 31 ++++++++++++++++++- hrms/tests/test_utils.py | 4 ++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/hrms/hr/report/monthly_attendance_sheet/test_monthly_attendance_sheet.py b/hrms/hr/report/monthly_attendance_sheet/test_monthly_attendance_sheet.py index 71fa9745e1..bb9c7bfb8b 100644 --- a/hrms/hr/report/monthly_attendance_sheet/test_monthly_attendance_sheet.py +++ b/hrms/hr/report/monthly_attendance_sheet/test_monthly_attendance_sheet.py @@ -15,7 +15,7 @@ make_holiday_list, make_leave_application, ) -from hrms.tests.test_utils import get_first_day_for_prev_month +from hrms.tests.test_utils import create_company, get_first_day_for_prev_month class TestMonthlyAttendanceSheet(FrappeTestCase): @@ -276,6 +276,35 @@ def test_attendance_with_employee_filter(self): self.assertEqual(present[1], 1) self.assertEqual(leaves[2], 1) + def test_attendance_with_company_filter(self): + create_company("Test Parent Company", is_group=1) + create_company("Test Child Company", is_group=1, parent_company="Test Parent Company") + create_company("Test Grandchild Company", parent_company="Test Child Company") + + employee1 = make_employee("test_employee@parent.com", company="Test Parent Company") + employee2 = make_employee("test_employee@child.com", company="Test Child Company") + employee3 = make_employee("test_employee@grandchild.com", company="Test Grandchild Company") + + previous_month_first = get_first_day_for_prev_month() + mark_attendance(employee1, previous_month_first, "Present") + mark_attendance(employee2, previous_month_first, "Present") + mark_attendance(employee3, previous_month_first, "Present") + + filters = frappe._dict( + { + "month": previous_month_first.month, + "year": previous_month_first.year, + "company": "Test Parent Company", + "include_company_descendants": 1, + } + ) + report = execute(filters=filters) + self.assertEqual(len(report[1]), 3) + + filters.include_company_descendants = 0 + report = execute(filters=filters) + self.assertEqual(len(report[1]), 1) + def test_attendance_with_employee_filter_and_summarized_view(self): previous_month_first = get_first_day_for_prev_month() diff --git a/hrms/tests/test_utils.py b/hrms/tests/test_utils.py index b9133a4f01..6c40530cec 100644 --- a/hrms/tests/test_utils.py +++ b/hrms/tests/test_utils.py @@ -88,7 +88,7 @@ def add_date_to_holiday_list(date: str, holiday_list: str) -> None: holiday_list.save() -def create_company(name: str = "_Test Company"): +def create_company(name: str = "_Test Company", is_group: 0 | 1 = 0, parent_company: str | None = None): if frappe.db.exists("Company", name): return frappe.get_doc("Company", name) @@ -98,6 +98,8 @@ def create_company(name: str = "_Test Company"): "company_name": name, "default_currency": "INR", "country": "India", + "is_group": is_group, + "parent_company": parent_company, } ).insert()