Exemple #1
0
	def test_skip_auto_attendance_for_duplicate_record(self):
		# Skip auto attendance in case of duplicate attendance record
		from erpnext.hr.doctype.attendance.attendance import mark_attendance
		from erpnext.hr.doctype.employee_checkin.test_employee_checkin import make_checkin

		employee = make_employee("*****@*****.**", company="_Test Company")

		shift_type = setup_shift_type()
		date = getdate()

		# mark attendance
		mark_attendance(employee, date, "Present")
		make_shift_assignment(shift_type.name, employee, date)

		timestamp = datetime.combine(date, get_time("08:00:00"))
		log_in = make_checkin(employee, timestamp)
		self.assertEqual(log_in.shift, shift_type.name)

		timestamp = datetime.combine(date, get_time("12:00:00"))
		log_out = make_checkin(employee, timestamp)
		self.assertEqual(log_out.shift, shift_type.name)

		# auto attendance should skip marking
		shift_type.process_auto_attendance()

		log_in.reload()
		log_out.reload()
		self.assertEqual(log_in.skip_auto_attendance, 1)
		self.assertEqual(log_out.skip_auto_attendance, 1)
Exemple #2
0
    def mark_absent_for_dates_with_no_attendance(self, employee):
        """Marks Absents for the given employee on working days in this shift which have no attendance marked.
		The Absent is marked starting from 'process_attendance_after' or employee creation date.
		"""
        date_of_joining, relieving_date, employee_creation = frappe.db.get_value(
            "Employee", employee,
            ["date_of_joining", "relieving_date", "creation"])
        if not date_of_joining:
            date_of_joining = employee_creation.date()
        start_date = max(getdate(self.process_attendance_after),
                         date_of_joining)
        actual_shift_datetime = get_actual_start_end_datetime_of_shift(
            employee, get_datetime(self.last_sync_of_checkin), True)
        last_shift_time = actual_shift_datetime[0] if actual_shift_datetime[
            0] else get_datetime(self.last_sync_of_checkin)
        prev_shift = get_employee_shift(
            employee,
            last_shift_time.date() - timedelta(days=1), True, 'reverse')
        if prev_shift:
            end_date = min(
                prev_shift.start_datetime.date(), relieving_date
            ) if relieving_date else prev_shift.start_datetime.date()
        else:
            return
        holiday_list_name = self.holiday_list
        if not holiday_list_name:
            holiday_list_name = get_holiday_list_for_employee(employee, False)
        dates = get_filtered_date_list(employee,
                                       start_date,
                                       end_date,
                                       holiday_list=holiday_list_name)
        for date in dates:
            shift_details = get_employee_shift(employee, date, True)
            if shift_details and shift_details.shift_type.name == self.name:
                mark_attendance(employee, date, self.name, 'Absent')
Exemple #3
0
	def test_skip_auto_attendance_for_overlapping_shift(self):
		# Skip auto attendance in case of overlapping shift attendance record
		# this case won't occur in case of shift assignment, since it will not allow overlapping shifts to be assigned
		# can happen if manual attendance records are created
		from erpnext.hr.doctype.attendance.attendance import mark_attendance
		from erpnext.hr.doctype.employee_checkin.test_employee_checkin import make_checkin

		employee = make_employee("*****@*****.**", company="_Test Company")
		shift_1 = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="10:00:00")
		shift_2 = setup_shift_type(shift_type="Shift 2", start_time="09:30:00", end_time="11:00:00")

		date = getdate()

		# mark attendance
		mark_attendance(employee, date, "Present", shift=shift_1.name)
		make_shift_assignment(shift_2.name, employee, date)

		timestamp = datetime.combine(date, get_time("09:30:00"))
		log_in = make_checkin(employee, timestamp)
		self.assertEqual(log_in.shift, shift_2.name)

		timestamp = datetime.combine(date, get_time("11:00:00"))
		log_out = make_checkin(employee, timestamp)
		self.assertEqual(log_out.shift, shift_2.name)

		# auto attendance should be skipped for shift 2
		# since it is already marked for overlapping shift 1
		shift_2.process_auto_attendance()

		log_in.reload()
		log_out.reload()
		self.assertEqual(log_in.skip_auto_attendance, 1)
		self.assertEqual(log_out.skip_auto_attendance, 1)
	def test_unmarked_days_excluding_holidays(self):
		now = now_datetime()
		previous_month = now.month - 1
		first_day = now.replace(day=1).replace(month=previous_month).date()

		employee = make_employee(
			"*****@*****.**", date_of_joining=add_days(first_day, -1)
		)
		frappe.db.delete("Attendance", {"employee": employee})

		frappe.db.set_value("Employee", employee, "holiday_list", self.holiday_list)

		first_sunday = get_first_sunday(self.holiday_list, for_date=first_day)
		mark_attendance(employee, first_day, "Present")
		month_name = get_month_name(first_day)

		unmarked_days = get_unmarked_days(employee, month_name, exclude_holidays=True)
		unmarked_days = [getdate(date) for date in unmarked_days]

		# attendance already marked for the day
		self.assertNotIn(first_day, unmarked_days)
		# attendance unmarked
		self.assertIn(getdate(add_days(first_day, 1)), unmarked_days)
		# holidays not considered in unmarked days
		self.assertNotIn(first_sunday, unmarked_days)
Exemple #5
0
	def test_unmarked_days_as_per_joining_and_relieving_dates(self):
		now = now_datetime()
		previous_month = now.month - 1
		first_day = now.replace(day=1).replace(month=previous_month).date()

		doj = add_days(first_day, 1)
		relieving_date = add_days(first_day, 5)
		employee = make_employee(
			"*****@*****.**", date_of_joining=doj, relieving_date=relieving_date
		)

		frappe.db.set_value("Employee", employee, "holiday_list", self.holiday_list)

		attendance_date = add_days(first_day, 2)
		mark_attendance(employee, attendance_date, "Present")
		month_name = get_month_name(first_day)

		unmarked_days = get_unmarked_days(employee, month_name)
		unmarked_days = [getdate(date) for date in unmarked_days]

		# attendance already marked for the day
		self.assertNotIn(attendance_date, unmarked_days)
		# date before doj not in unmarked days
		self.assertNotIn(add_days(doj, -1), unmarked_days)
		# date after relieving not in unmarked days
		self.assertNotIn(add_days(relieving_date, 1), unmarked_days)
Exemple #6
0
	def test_duplicate_attendance_with_shift(self):
		from erpnext.hr.doctype.shift_type.test_shift_type import setup_shift_type

		employee = make_employee("*****@*****.**", company="_Test Company")
		date = nowdate()

		shift_1 = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="10:00:00")
		mark_attendance(employee, date, "Present", shift=shift_1.name)

		# attendance record with shift
		attendance = frappe.get_doc(
			{
				"doctype": "Attendance",
				"employee": employee,
				"attendance_date": date,
				"status": "Absent",
				"company": "_Test Company",
				"shift": shift_1.name,
			}
		)

		self.assertRaises(DuplicateAttendanceError, attendance.insert)

		# attendance record without any shift
		attendance = frappe.get_doc(
			{
				"doctype": "Attendance",
				"employee": employee,
				"attendance_date": date,
				"status": "Absent",
				"company": "_Test Company",
			}
		)

		self.assertRaises(DuplicateAttendanceError, attendance.insert)
Exemple #7
0
	def test_duplicate_attendance(self):
		employee = make_employee("*****@*****.**", company="_Test Company")
		date = nowdate()

		mark_attendance(employee, date, "Present")
		attendance = frappe.get_doc(
			{
				"doctype": "Attendance",
				"employee": employee,
				"attendance_date": date,
				"status": "Absent",
				"company": "_Test Company",
			}
		)

		self.assertRaises(DuplicateAttendanceError, attendance.insert)
Exemple #8
0
	def test_mark_absent(self):
		from erpnext.hr.doctype.employee.test_employee import make_employee
		employee = make_employee("*****@*****.**")
		date = nowdate()
		frappe.db.delete('Attendance', {'employee':employee, 'attendance_date':date})
		from erpnext.hr.doctype.attendance.attendance import mark_attendance
		attendance = mark_attendance(employee, date, 'Absent')
		fetch_attendance = frappe.get_value('Attendance', {'employee':employee, 'attendance_date':date, 'status':'Absent'})
		self.assertEqual(attendance, fetch_attendance)
Exemple #9
0
	def test_mark_absent(self):
		employee = make_employee("*****@*****.**")
		date = nowdate()

		attendance = mark_attendance(employee, date, "Absent")
		fetch_attendance = frappe.get_value(
			"Attendance", {"employee": employee, "attendance_date": date, "status": "Absent"}
		)
		self.assertEqual(attendance, fetch_attendance)
Exemple #10
0
	def test_allow_attendance_with_different_shifts(self):
		# allows attendance with 2 different non-overlapping shifts
		from erpnext.hr.doctype.shift_type.test_shift_type import setup_shift_type

		employee = make_employee("*****@*****.**", company="_Test Company")
		date = nowdate()

		shift_1 = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="10:00:00")
		shift_2 = setup_shift_type(shift_type="Shift 2", start_time="11:00:00", end_time="12:00:00")

		mark_attendance(employee, date, "Present", shift_1.name)
		frappe.get_doc(
			{
				"doctype": "Attendance",
				"employee": employee,
				"attendance_date": date,
				"status": "Absent",
				"company": "_Test Company",
				"shift": shift_2.name,
			}
		).insert()
Exemple #11
0
	def test_monthly_attendance_sheet_report(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")

		# mark different attendance status on first 3 days of previous month
		mark_attendance(self.employee, previous_month_first, "Absent")
		mark_attendance(self.employee, previous_month_first + relativedelta(days=1), "Present")
		mark_attendance(self.employee, previous_month_first + relativedelta(days=2), "On Leave")

		filters = frappe._dict(
			{
				"month": previous_month,
				"year": now.year,
				"company": company,
			}
		)
		report = execute(filters=filters)
		employees = report[1][0]
		datasets = report[3]["data"]["datasets"]
		absent = datasets[0]["values"]
		present = datasets[1]["values"]
		leaves = datasets[2]["values"]

		# ensure correct attendance is reflect on the report
		self.assertIn(self.employee, employees)
		self.assertEqual(absent[0], 1)
		self.assertEqual(present[1], 1)
		self.assertEqual(leaves[2], 1)
Exemple #12
0
    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
Exemple #13
0
	def test_overlapping_shift_attendance_validation(self):
		from erpnext.hr.doctype.shift_type.test_shift_type import setup_shift_type

		employee = make_employee("*****@*****.**", company="_Test Company")
		date = nowdate()

		shift_1 = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="10:00:00")
		shift_2 = setup_shift_type(shift_type="Shift 2", start_time="09:30:00", end_time="11:00:00")

		mark_attendance(employee, date, "Present", shift=shift_1.name)

		# attendance record with overlapping shift
		attendance = frappe.get_doc(
			{
				"doctype": "Attendance",
				"employee": employee,
				"attendance_date": date,
				"status": "Absent",
				"company": "_Test Company",
				"shift": shift_2.name,
			}
		)

		self.assertRaises(OverlappingShiftAttendanceError, attendance.insert)
Exemple #14
0
	def mark_absent_for_dates_with_no_attendance(self, employee):
		"""Marks Absents for the given employee on working days in this shift which have no attendance marked.
		The Absent is marked starting from 'process_attendance_after' or employee creation date.
		"""
		date_of_joining, relieving_date, employee_creation = frappe.db.get_value(
			"Employee", employee, ["date_of_joining", "relieving_date", "creation"]
		)
		if not date_of_joining:
			date_of_joining = employee_creation.date()
		start_date = max(getdate(self.process_attendance_after), date_of_joining)
		actual_shift_datetime = get_actual_start_end_datetime_of_shift(
			employee, get_datetime(self.last_sync_of_checkin), True
		)
		last_shift_time = (
			actual_shift_datetime[0]
			if actual_shift_datetime[0]
			else get_datetime(self.last_sync_of_checkin)
		)
		prev_shift = get_employee_shift(
			employee, last_shift_time.date() - timedelta(days=1), True, "reverse"
		)
		if prev_shift:
			end_date = (
				min(prev_shift.start_datetime.date(), relieving_date)
				if relieving_date
				else prev_shift.start_datetime.date()
			)
		else:
			return
		holiday_list_name = self.holiday_list
		if not holiday_list_name:
			holiday_list_name = get_holiday_list_for_employee(employee, False)
		dates = get_filtered_date_list(employee, start_date, end_date, holiday_list=holiday_list_name)
		for date in dates:
			shift_details = get_employee_shift(employee, date, True)
			if shift_details and shift_details.shift_type.name == self.name:
				attendance = mark_attendance(employee, date, "Absent", self.name)
				if attendance:
					frappe.get_doc(
						{
							"doctype": "Comment",
							"comment_type": "Comment",
							"reference_doctype": "Attendance",
							"reference_name": attendance,
							"content": frappe._("Employee was marked Absent due to missing Employee Checkins."),
						}
					).insert(ignore_permissions=True)
Exemple #15
0
    def mark_absent_for_dates_with_no_attendance(self, employee):
        """Marks Absents for the given employee on working days in this shift which have no attendance marked.
		The Absent is marked starting from 'process_attendance_after' or employee creation date.
		"""
        start_date, end_date = self.get_start_and_end_dates(employee)

        # no shift assignment found, no need to process absent attendance records
        if start_date is None:
            return

        holiday_list_name = self.holiday_list
        if not holiday_list_name:
            holiday_list_name = get_holiday_list_for_employee(employee, False)

        start_time = get_time(self.start_time)

        for date in daterange(getdate(start_date), getdate(end_date)):
            if is_holiday(holiday_list_name, date):
                # skip marking absent on a holiday
                continue

            timestamp = datetime.combine(date, start_time)
            shift_details = get_employee_shift(employee, timestamp, True)

            if shift_details and shift_details.shift_type.name == self.name:
                attendance = mark_attendance(employee, date, "Absent",
                                             self.name)
                if attendance:
                    frappe.get_doc({
                        "doctype":
                        "Comment",
                        "comment_type":
                        "Comment",
                        "reference_doctype":
                        "Attendance",
                        "reference_name":
                        attendance,
                        "content":
                        frappe.
                        _("Employee was marked Absent due to missing Employee Checkins."
                          ),
                    }).insert(ignore_permissions=True)
Exemple #16
0
	def test_payment_days_based_on_attendance(self):
		from erpnext.hr.doctype.attendance.attendance import mark_attendance
		no_of_days = self.get_no_of_days()

		# Payroll based on attendance
		frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Attendance")
		frappe.db.set_value("Payroll Settings", None, "daily_wages_fraction_for_half_day", 0.75)

		emp_id = make_employee("*****@*****.**")
		frappe.db.set_value("Employee", emp_id, {"relieving_date": None, "status": "Active"})

		frappe.db.set_value("Leave Type", "Leave Without Pay", "include_holiday", 0)

		month_start_date = get_first_day(nowdate())
		month_end_date = get_last_day(nowdate())

		first_sunday = frappe.db.sql("""
			select holiday_date from `tabHoliday`
			where parent = 'Salary Slip Test Holiday List'
				and holiday_date between %s and %s
			order by holiday_date
		""", (month_start_date, month_end_date))[0][0]

		mark_attendance(emp_id, first_sunday, 'Absent', ignore_validate=True) # invalid lwp
		mark_attendance(emp_id, add_days(first_sunday, 1), 'Absent', ignore_validate=True) # counted as absent
		mark_attendance(emp_id, add_days(first_sunday, 2), 'Half Day', leave_type='Leave Without Pay', ignore_validate=True) # valid 0.75 lwp
		mark_attendance(emp_id, add_days(first_sunday, 3), 'On Leave', leave_type='Leave Without Pay', ignore_validate=True) # valid lwp
		mark_attendance(emp_id, add_days(first_sunday, 4), 'On Leave', leave_type='Casual Leave', ignore_validate=True) # invalid lwp
		mark_attendance(emp_id, add_days(first_sunday, 7), 'On Leave', leave_type='Leave Without Pay', ignore_validate=True) # invalid lwp

		ss = make_employee_salary_slip("*****@*****.**", "Monthly")

		self.assertEqual(ss.leave_without_pay, 1.25)
		self.assertEqual(ss.absent_days, 1)

		days_in_month = no_of_days[0]
		no_of_holidays = no_of_days[1]

		self.assertEqual(ss.payment_days, days_in_month - no_of_holidays - 2.25)

		#Gross pay calculation based on attendances
		gross_pay = 78000 - ((78000 / (days_in_month - no_of_holidays)) * flt(ss.leave_without_pay + ss.absent_days))

		self.assertEqual(ss.gross_pay, gross_pay)

		frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave")
Exemple #17
0
    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=2),
                        "Half Day")  # half day

        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)

        # 4 present + half day absent 0.5
        self.assertEqual(row["total_present"], 4.5)
        # 1 present + half day absent 0.5
        self.assertEqual(row["total_absent"], 1.5)
        # leave days + half day leave 0.5
        self.assertEqual(row["total_leaves"],
                         leave_application.total_leave_days + 0.5)

        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)