fix: duplicate validation for Course Enrollment (#23659)

* fix: duplicate validation for Course Enrollment

* fix: clean-up failing tests
diff --git a/erpnext/education/doctype/course_enrollment/course_enrollment.py b/erpnext/education/doctype/course_enrollment/course_enrollment.py
index b082be2..f7aa6e9 100644
--- a/erpnext/education/doctype/course_enrollment/course_enrollment.py
+++ b/erpnext/education/doctype/course_enrollment/course_enrollment.py
@@ -6,9 +6,13 @@
 import frappe
 from frappe import _
 from frappe.model.document import Document
+from frappe.utils import get_link_to_form
 from functools import reduce
 
 class CourseEnrollment(Document):
+	def validate(self):
+		self.validate_duplication()
+
 	def get_progress(self, student):
 		"""
 		Returns Progress of given student for a particular course enrollment
@@ -27,13 +31,15 @@
 			return []
 
 	def validate_duplication(self):
-		enrollment = frappe.get_all("Course Enrollment", filters={
+		enrollment = frappe.db.exists("Course Enrollment", {
 			"student": self.student,
 			"course": self.course,
-			"program_enrollment": self.program_enrollment
+			"program_enrollment": self.program_enrollment,
+			"name": ("!=", self.name)
 		})
 		if enrollment:
-			frappe.throw(_("Student is already enrolled."))
+			frappe.throw(_("Student is already enrolled via Course Enrollment {0}").format(
+				get_link_to_form("Course Enrollment", enrollment)), title=_('Duplicate Entry'))
 
 	def add_quiz_activity(self, quiz_name, quiz_response, answers, score, status):
 		result = {k: ('Correct' if v else 'Wrong') for k,v in answers.items()}
diff --git a/erpnext/education/doctype/course_enrollment/test_course_enrollment.py b/erpnext/education/doctype/course_enrollment/test_course_enrollment.py
index 5ecace2..e22c7ce 100644
--- a/erpnext/education/doctype/course_enrollment/test_course_enrollment.py
+++ b/erpnext/education/doctype/course_enrollment/test_course_enrollment.py
@@ -17,8 +17,9 @@
 		setup_program()
 		student = create_student({"first_name": "_Test First", "last_name": "_Test Last", "email": "_test_student_1@example.com"})
 		program_enrollment = student.enroll_in_program("_Test Program")
-		course_enrollment = student.enroll_in_course("_Test Course 1", program_enrollment.name)
-		make_course_activity(course_enrollment.name, "Article", "_Test Article 1-1")
+		course_enrollment = frappe.db.get_value("Course Enrollment",
+			{"course": "_Test Course 1", "student": student.name, "program_enrollment": program_enrollment.name}, 'name')
+		make_course_activity(course_enrollment, "Article", "_Test Article 1-1")
 
 	def test_get_progress(self):
 		student = get_student("_test_student_1@example.com")
@@ -30,5 +31,14 @@
 		self.assertTrue(finished in progress)
 		frappe.db.rollback()
 
+	def tearDown(self):
+		for entry in frappe.db.get_all("Course Enrollment"):
+			frappe.delete_doc("Course Enrollment", entry.name)
+
+		for entry in frappe.db.get_all("Program Enrollment"):
+			doc = frappe.get_doc("Program Enrollment", entry.name)
+			doc.cancel()
+			doc.delete()
+
 
 
diff --git a/erpnext/education/doctype/program/test_program.py b/erpnext/education/doctype/program/test_program.py
index edfad0d..d753036 100644
--- a/erpnext/education/doctype/program/test_program.py
+++ b/erpnext/education/doctype/program/test_program.py
@@ -49,6 +49,11 @@
 		self.assertEqual(course[1].name, "_Test Course 2")
 		frappe.db.rollback()
 
+	def tearDown(self):
+		for dt in ["Program", "Course", "Topic", "Article"]:
+			for entry in frappe.get_all(dt):
+				frappe.delete_doc(dt, entry.program)
+
 def make_program(name):
 	program = frappe.get_doc({
 		"doctype": "Program",
diff --git a/erpnext/education/doctype/program_enrollment/test_program_enrollment.py b/erpnext/education/doctype/program_enrollment/test_program_enrollment.py
index c6cbee1..fec6422 100644
--- a/erpnext/education/doctype/program_enrollment/test_program_enrollment.py
+++ b/erpnext/education/doctype/program_enrollment/test_program_enrollment.py
@@ -23,4 +23,13 @@
 		course_enrollments = student.get_all_course_enrollments()
 		self.assertTrue("_Test Course 1" in course_enrollments.keys())
 		self.assertTrue("_Test Course 2" in course_enrollments.keys())
-		frappe.db.rollback()
\ No newline at end of file
+		frappe.db.rollback()
+
+	def tearDown(self):
+		for entry in frappe.db.get_all("Course Enrollment"):
+			frappe.delete_doc("Course Enrollment", entry.name)
+
+		for entry in frappe.db.get_all("Program Enrollment"):
+			doc = frappe.get_doc("Program Enrollment", entry.name)
+			doc.cancel()
+			doc.delete()
\ No newline at end of file
diff --git a/erpnext/education/doctype/student/student.py b/erpnext/education/doctype/student/student.py
index e0d7514..81626f1 100644
--- a/erpnext/education/doctype/student/student.py
+++ b/erpnext/education/doctype/student/student.py
@@ -147,7 +147,7 @@
 			enrollment.save(ignore_permissions=True)
 		except frappe.exceptions.ValidationError:
 			enrollment_name = frappe.get_list("Course Enrollment", filters={"student": self.name, "course": course_name, "program_enrollment": program_enrollment})[0].name
-			return frappe.get_doc("Program Enrollment", enrollment_name)
+			return frappe.get_doc("Course Enrollment", enrollment_name)
 		else:
 			return enrollment
 
diff --git a/erpnext/education/doctype/student/test_student.py b/erpnext/education/doctype/student/test_student.py
index 8610edb..2e52637 100644
--- a/erpnext/education/doctype/student/test_student.py
+++ b/erpnext/education/doctype/student/test_student.py
@@ -42,6 +42,16 @@
 		self.assertTrue("_Test Course 2" in course_enrollments.keys())
 		frappe.db.rollback()
 
+	def tearDown(self):
+		for entry in frappe.db.get_all("Course Enrollment"):
+			frappe.delete_doc("Course Enrollment", entry.name)
+
+		for entry in frappe.db.get_all("Program Enrollment"):
+			doc = frappe.get_doc("Program Enrollment", entry.name)
+			doc.cancel()
+			doc.delete()
+
+
 def create_student(student_dict):
 	student = get_student(student_dict['email'])
 	if not student: