test: validations for duplicate and overlapping shift attendance records
diff --git a/erpnext/hr/doctype/attendance/test_attendance.py b/erpnext/hr/doctype/attendance/test_attendance.py
index 058bc93..ecd14c8 100644
--- a/erpnext/hr/doctype/attendance/test_attendance.py
+++ b/erpnext/hr/doctype/attendance/test_attendance.py
@@ -6,6 +6,8 @@
from frappe.utils import add_days, get_year_ending, get_year_start, getdate, now_datetime, nowdate
from erpnext.hr.doctype.attendance.attendance import (
+ DuplicateAttendanceError,
+ OverlappingShiftAttendanceError,
get_month_map,
get_unmarked_days,
mark_attendance,
@@ -23,11 +25,112 @@
from_date = get_year_start(getdate())
to_date = get_year_ending(getdate())
self.holiday_list = make_holiday_list(from_date=from_date, to_date=to_date)
+ frappe.db.delete("Attendance")
+
+ def test_duplicate_attendance(self):
+ employee = make_employee("test_duplicate_attendance@example.com", 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)
+
+ def test_duplicate_attendance_with_shift(self):
+ from erpnext.hr.doctype.shift_type.test_shift_type import setup_shift_type
+
+ employee = make_employee("test_duplicate_attendance@example.com", 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)
+
+ def test_overlapping_shift_attendance_validation(self):
+ from erpnext.hr.doctype.shift_type.test_shift_type import setup_shift_type
+
+ employee = make_employee("test_overlap_attendance@example.com", 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)
+
+ 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("test_duplicate_attendance@example.com", 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)
+ attendance = frappe.get_doc(
+ {
+ "doctype": "Attendance",
+ "employee": employee,
+ "attendance_date": date,
+ "status": "Absent",
+ "company": "_Test Company",
+ "shift": shift_2.name,
+ }
+ ).insert()
def test_mark_absent(self):
employee = make_employee("test_mark_absent@example.com")
date = nowdate()
- frappe.db.delete("Attendance", {"employee": employee, "attendance_date": date})
+
attendance = mark_attendance(employee, date, "Absent")
fetch_attendance = frappe.get_value(
"Attendance", {"employee": employee, "attendance_date": date, "status": "Absent"}
@@ -42,7 +145,6 @@
employee = make_employee(
"test_unmarked_days@example.com", 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)
@@ -67,8 +169,6 @@
employee = make_employee(
"test_unmarked_days@example.com", 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)
@@ -95,7 +195,6 @@
employee = make_employee(
"test_unmarked_days_as_per_doj@example.com", date_of_joining=doj, relieving_date=relieving_date
)
- frappe.db.delete("Attendance", {"employee": employee})
frappe.db.set_value("Employee", employee, "holiday_list", self.holiday_list)