From 0733cfaadfdfbd1ac65849358a70c5c9eb0e7458 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 29 Mar 2022 12:37:36 +0530 Subject: [PATCH] test: monthly attendance sheet --- erpnext/hr/doctype/attendance/attendance.py | 7 +- .../test_monthly_attendance_sheet.py | 136 +++++++++++++++++- 2 files changed, 138 insertions(+), 5 deletions(-) diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py index 3ad0f0c7e2a4..7ee26f58db85 100644 --- a/erpnext/hr/doctype/attendance/attendance.py +++ b/erpnext/hr/doctype/attendance/attendance.py @@ -149,7 +149,8 @@ def add_attendance(events, start, end, conditions=None): if e not in events: events.append(e) -def mark_attendance(employee, attendance_date, status, shift=None, leave_type=None, ignore_validate=False): +def mark_attendance(employee, attendance_date, status, shift=None, leave_type=None, ignore_validate=False, + late_entry=False, early_exit=False): if not get_duplicate_attendance_record(employee, attendance_date, shift): company = frappe.db.get_value('Employee', employee, 'company') attendance = frappe.get_doc({ @@ -159,7 +160,9 @@ def mark_attendance(employee, attendance_date, status, shift=None, leave_type=No 'status': status, 'company': company, 'shift': shift, - 'leave_type': leave_type + 'leave_type': leave_type, + 'late_entry': late_entry, + 'early_exit': early_exit }) attendance.flags.ignore_validate = ignore_validate attendance.insert() diff --git a/erpnext/hr/report/monthly_attendance_sheet/test_monthly_attendance_sheet.py b/erpnext/hr/report/monthly_attendance_sheet/test_monthly_attendance_sheet.py index 7a9baf4a9152..87cbdb71872a 100644 --- a/erpnext/hr/report/monthly_attendance_sheet/test_monthly_attendance_sheet.py +++ b/erpnext/hr/report/monthly_attendance_sheet/test_monthly_attendance_sheet.py @@ -1,17 +1,23 @@ import frappe from dateutil.relativedelta import relativedelta from frappe.tests.utils import FrappeTestCase -from frappe.utils import now_datetime +from frappe.utils import add_days, get_year_ending, get_year_start, getdate, now_datetime from erpnext.hr.doctype.attendance.attendance import mark_attendance from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.report.monthly_attendance_sheet.monthly_attendance_sheet import execute +from erpnext.hr.doctype.leave_application.test_leave_application import make_allocation_record +from erpnext.hr.report.monthly_attendance_sheet.monthly_attendance_sheet import ( + execute, + get_total_days_in_month, +) +from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_leave_application +test_dependencies = ['Shift Type'] class TestMonthlyAttendanceSheet(FrappeTestCase): def setUp(self): self.employee = make_employee("test_employee@example.com") - frappe.db.delete('Attendance', {'employee': self.employee}) + frappe.db.delete('Attendance') def test_monthly_attendance_sheet_report(self): now = now_datetime() @@ -43,3 +49,127 @@ def test_monthly_attendance_sheet_report(self): self.assertEqual(absent[0], 1) self.assertEqual(present[1], 1) self.assertEqual(leaves[2], 1) + + def test_monthly_attendance_sheet_with_detailed_view(self): + now = now_datetime() + previous_month = now.month - 1 + previous_month_first = now.replace(day=1).replace(month=previous_month).date() + + company = frappe.db.get_value('Employee', self.employee, 'company') + + # attendance with shift + mark_attendance(self.employee, previous_month_first, 'Absent', 'Day Shift') + mark_attendance(self.employee, previous_month_first + relativedelta(days=1), 'Present', 'Day Shift') + + # attendance without shift + mark_attendance(self.employee, previous_month_first + relativedelta(days=2), 'On Leave') + mark_attendance(self.employee, previous_month_first + relativedelta(days=3), 'Present') + + filters = frappe._dict({ + 'month': previous_month, + 'year': now.year, + 'company': company, + }) + report = execute(filters=filters) + + day_shift_row = report[1][0] + row_without_shift = report[1][1] + + self.assertEqual(day_shift_row['shift'], 'Day Shift') + self.assertEqual(day_shift_row[1], 'A') # absent on the 1st day of the month + self.assertEqual(day_shift_row[2], 'P') # present on the 2nd day + + self.assertEqual(row_without_shift['shift'], None) + self.assertEqual(row_without_shift[3], 'L') # on leave on the 3rd day + self.assertEqual(row_without_shift[4], 'P') # present on the 4th day + + def test_monthly_attendance_sheet_with_summarized_view(self): + now = now_datetime() + previous_month = now.month - 1 + previous_month_first = now.replace(day=1).replace(month=previous_month).date() + + company = frappe.db.get_value('Employee', self.employee, 'company') + + # attendance with shift + mark_attendance(self.employee, previous_month_first, 'Absent', 'Day Shift') + mark_attendance(self.employee, previous_month_first + relativedelta(days=1), 'Present', 'Day Shift') + + mark_attendance(self.employee, previous_month_first + relativedelta(days=3), 'Present') # attendance without shift + mark_attendance(self.employee, previous_month_first + relativedelta(days=4), 'Present', late_entry=1) # late entry + mark_attendance(self.employee, previous_month_first + relativedelta(days=5), 'Present', early_exit=1) # early exit + + leave_application = get_leave_application(self.employee) + + filters = frappe._dict({ + 'month': previous_month, + 'year': now.year, + 'company': company, + 'summarized_view': 1 + }) + report = execute(filters=filters) + + row = report[1][0] + self.assertEqual(row['employee'], self.employee) + self.assertEqual(row['total_present'], 4) + self.assertEqual(row['total_absent'], 1) + self.assertEqual(row['total_leaves'], leave_application.total_leave_days) + + # total days - (days with attendance + leave days) + unmarked_days = get_total_days_in_month(filters) - (5 + leave_application.total_leave_days) + + self.assertEqual(row['unmarked_days'], unmarked_days) + self.assertEqual(row['_test_leave_type'], leave_application.total_leave_days) + self.assertEqual(row['total_late_entries'], 1) + self.assertEqual(row['total_early_exits'], 1) + + def test_attendance_with_group_by_filter(self): + now = now_datetime() + previous_month = now.month - 1 + previous_month_first = now.replace(day=1).replace(month=previous_month).date() + + company = frappe.db.get_value('Employee', self.employee, 'company') + + # attendance with shift + mark_attendance(self.employee, previous_month_first, 'Absent', 'Day Shift') + mark_attendance(self.employee, previous_month_first + relativedelta(days=1), 'Present', 'Day Shift') + + # attendance without shift + mark_attendance(self.employee, previous_month_first + relativedelta(days=2), 'On Leave') + mark_attendance(self.employee, previous_month_first + relativedelta(days=3), 'Present') + + filters = frappe._dict({ + 'month': previous_month, + 'year': now.year, + 'company': company, + 'group_by': 'Department' + }) + report = execute(filters=filters) + + department = frappe.db.get_value('Employee', self.employee, 'department') + department_row = report[1][0] + self.assertIn(department, department_row['department']) + + day_shift_row = report[1][1] + row_without_shift = report[1][2] + + self.assertEqual(day_shift_row['shift'], 'Day Shift') + self.assertEqual(day_shift_row[1], 'A') # absent on the 1st day of the month + self.assertEqual(day_shift_row[2], 'P') # present on the 2nd day + + self.assertEqual(row_without_shift['shift'], None) + self.assertEqual(row_without_shift[3], 'L') # on leave on the 3rd day + self.assertEqual(row_without_shift[4], 'P') # present on the 4th day + + +def get_leave_application(employee): + now = now_datetime() + previous_month = now.month - 1 + + date = getdate() + year_start = getdate(get_year_start(date)) + year_end = getdate(get_year_ending(date)) + allocation = make_allocation_record(employee=employee, from_date=year_start, to_date=year_end) + + from_date = now.replace(day=7).replace(month=previous_month).date() + to_date = now.replace(day=8).replace(month=previous_month).date() + return make_leave_application(employee, from_date, to_date, '_Test Leave Type') \ No newline at end of file