feat(Education): Student Attendance and Leave Enhancements (#22623)

* feat: make Student Attendance doctype submittable

* feat: add attendance related fields in Student Leave Application

* feat: update Attendance records on Leave Application submission

* refactor: better error messages and ORM queries

* fix: show present only for leave applications with mark_as_present enabled in attendance reports

* test: Student Leave Application

* fix: filter for attendance records

* fix: codacy issues
diff --git a/erpnext/education/api.py b/erpnext/education/api.py
index 1a19716..fe033d4 100644
--- a/erpnext/education/api.py
+++ b/erpnext/education/api.py
@@ -104,6 +104,7 @@
 	student_attendance.date = date
 	student_attendance.status = status
 	student_attendance.save()
+	student_attendance.submit()
 
 
 @frappe.whitelist()
@@ -363,9 +364,9 @@
 		select
 			name as program_enrollment, student_name, program, student_batch_name as student_batch,
 			student_category, academic_term, academic_year
-		from 
+		from
 			`tabProgram Enrollment`
-		where 
+		where
 			student = %s and academic_year = %s
 		order by creation''', (student, current_academic_year), as_dict=1)
 
diff --git a/erpnext/education/doctype/student/student.py b/erpnext/education/doctype/student/student.py
index 6b545d9..e0d7514 100644
--- a/erpnext/education/doctype/student/student.py
+++ b/erpnext/education/doctype/student/student.py
@@ -25,7 +25,7 @@
 		for sibling in self.siblings:
 			if sibling.date_of_birth and getdate(sibling.date_of_birth) > getdate():
 				frappe.throw(_("Row {0}:Sibling Date of Birth cannot be greater than today.").format(sibling.idx))
-				
+
 		if self.date_of_birth and getdate(self.date_of_birth) >= getdate(today()):
 			frappe.throw(_("Date of Birth cannot be greater than today."))
 
@@ -157,5 +157,5 @@
 		from `tabStudent Attendance` where
 			student=%s
 			and `date` > date_sub(curdate(), interval 1 year)
-			and status = 'Present'
+			and docstatus = 1 and status = 'Present'
 			group by date''', name))
diff --git a/erpnext/education/doctype/student_attendance/student_attendance.json b/erpnext/education/doctype/student_attendance/student_attendance.json
index 23e10e6..55384b9 100644
--- a/erpnext/education/doctype/student_attendance/student_attendance.json
+++ b/erpnext/education/doctype/student_attendance/student_attendance.json
@@ -1,287 +1,125 @@
 {
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 1, 
- "allow_rename": 0, 
- "autoname": "", 
- "beta": 0, 
- "creation": "2015-11-05 15:20:23.045996", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "Document", 
- "editable_grid": 0, 
- "engine": "InnoDB", 
+ "actions": [],
+ "allow_import": 1,
+ "autoname": "naming_series:",
+ "creation": "2015-11-05 15:20:23.045996",
+ "doctype": "DocType",
+ "document_type": "Document",
+ "engine": "InnoDB",
+ "field_order": [
+  "naming_series",
+  "student",
+  "student_name",
+  "course_schedule",
+  "student_group",
+  "column_break_3",
+  "date",
+  "status",
+  "leave_application",
+  "amended_from"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "student", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 1, 
-   "in_list_view": 0, 
-   "in_standard_filter": 1, 
-   "label": "Student", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Student", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 1, 
-   "set_only_once": 0, 
-   "translatable": 0,
-   "unique": 0
-  }, 
+   "fieldname": "student",
+   "fieldtype": "Link",
+   "in_global_search": 1,
+   "in_standard_filter": 1,
+   "label": "Student",
+   "options": "Student",
+   "reqd": 1,
+   "search_index": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "course_schedule", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Course Schedule", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Course Schedule", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0,
-   "unique": 0
-  }, 
+   "fieldname": "course_schedule",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Course Schedule",
+   "options": "Course Schedule"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "date", 
-   "fieldtype": "Date", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Date", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 1, 
-   "set_only_once": 0, 
-   "translatable": 0,
-   "unique": 0
-  }, 
+   "fieldname": "date",
+   "fieldtype": "Date",
+   "label": "Date",
+   "reqd": 1,
+   "search_index": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_3", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0,
-   "unique": 0
-  }, 
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
    "fetch_from": "student.title",
-   "fieldname": "student_name", 
-   "fieldtype": "Read Only", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 1, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Student Name", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0,
-   "unique": 0
-  }, 
+   "fieldname": "student_name",
+   "fieldtype": "Read Only",
+   "in_global_search": 1,
+   "label": "Student Name"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "student_group", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 1, 
-   "in_list_view": 0, 
-   "in_standard_filter": 1, 
-   "label": "Student Group", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Student Group", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0,
-   "unique": 0
-  }, 
+   "fieldname": "student_group",
+   "fieldtype": "Link",
+   "in_global_search": 1,
+   "in_standard_filter": 1,
+   "label": "Student Group",
+   "options": "Student Group"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "Present", 
-   "fieldname": "status", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 1, 
-   "label": "Status", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Present\nAbsent", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0,
-   "unique": 0
+   "default": "Present",
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Status",
+   "options": "Present\nAbsent",
+   "reqd": 1
+  },
+  {
+   "fieldname": "leave_application",
+   "fieldtype": "Link",
+   "label": "Leave Application",
+   "options": "Student Leave Application",
+   "read_only": 1
+  },
+  {
+   "fieldname": "naming_series",
+   "fieldtype": "Select",
+   "label": "Series",
+   "options": "EDU-ATT-.YYYY.-"
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Student Attendance",
+   "print_hide": 1,
+   "read_only": 1
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2018-07-27 10:48:22.301531", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Student Attendance", 
- "name_case": "", 
- "owner": "Administrator", 
+ ],
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2020-07-08 13:55:42.580181",
+ "modified_by": "Administrator",
+ "module": "Education",
+ "name": "Student Attendance",
+ "owner": "Administrator",
  "permissions": [
   {
-   "amend": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Academics User", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Academics User",
+   "share": 1,
+   "submit": 1,
    "write": 1
   }
- ], 
- "quick_entry": 0, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "title_field": "student_name", 
- "track_changes": 0, 
- "track_seen": 0
+ ],
+ "restrict_to_domain": "Education",
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "student_name"
 }
\ No newline at end of file
diff --git a/erpnext/education/doctype/student_attendance/student_attendance.py b/erpnext/education/doctype/student_attendance/student_attendance.py
index 06ac4fb..c1b6850 100644
--- a/erpnext/education/doctype/student_attendance/student_attendance.py
+++ b/erpnext/education/doctype/student_attendance/student_attendance.py
@@ -6,52 +6,63 @@
 import frappe
 from frappe.model.document import Document
 from frappe import _
-from frappe.utils import cstr
+from frappe.utils import get_link_to_form
 from erpnext.education.api import get_student_group_students
 
 
 class StudentAttendance(Document):
 	def validate(self):
-		self.validate_date()
 		self.validate_mandatory()
-		self.validate_course_schedule()
+		self.set_date()
+		self.set_student_group()
 		self.validate_student()
 		self.validate_duplication()
-		
-	def validate_date(self):
+
+	def set_date(self):
 		if self.course_schedule:
-			self.date = frappe.db.get_value("Course Schedule", self.course_schedule, "schedule_date")
-	
+			self.date = frappe.db.get_value('Course Schedule', self.course_schedule, 'schedule_date')
+
 	def validate_mandatory(self):
 		if not (self.student_group or self.course_schedule):
-			frappe.throw(_("""Student Group or Course Schedule is mandatory"""))
-	
-	def validate_course_schedule(self):
+			frappe.throw(_('{0} or {1} is mandatory').format(frappe.bold('Student Group'),
+				frappe.bold('Course Schedule')), title=_('Mandatory Fields'))
+
+	def set_student_group(self):
 		if self.course_schedule:
-			self.student_group = frappe.db.get_value("Course Schedule", self.course_schedule, "student_group")
-	
+			self.student_group = frappe.db.get_value('Course Schedule', self.course_schedule, 'student_group')
+
 	def validate_student(self):
 		if self.course_schedule:
-			student_group = frappe.db.get_value("Course Schedule", self.course_schedule, "student_group")
+			student_group = frappe.db.get_value('Course Schedule', self.course_schedule, 'student_group')
 		else:
 			student_group = self.student_group
 		student_group_students = [d.student for d in get_student_group_students(student_group)]
 		if student_group and self.student not in student_group_students:
-			frappe.throw(_('''Student {0}: {1} does not belong to Student Group {2}'''.format(self.student, self.student_name, student_group)))
+			student_group_doc = get_link_to_form('Student Group', student_group)
+			frappe.throw(_('Student {0}: {1} does not belong to Student Group {2}').format(
+				frappe.bold(self.student), self.student_name, frappe.bold(student_group_doc)))
 
 	def validate_duplication(self):
 		"""Check if the Attendance Record is Unique"""
-		attendance_records=None
+		attendance_record = None
 		if self.course_schedule:
-			attendance_records= frappe.db.sql("""select name from `tabStudent Attendance` where \
-				student= %s and ifnull(course_schedule, '')= %s and name != %s""",
-				(self.student, cstr(self.course_schedule), self.name))
+			attendance_record = frappe.db.exists('Student Attendance', {
+				'student': self.student,
+				'course_schedule': self.course_schedule,
+				'docstatus': ('!=', 2),
+				'name': ('!=', self.name)
+			})
 		else:
-			attendance_records= frappe.db.sql("""select name from `tabStudent Attendance` where \
-				student= %s and student_group= %s and date= %s and name != %s and \
-				(course_schedule is Null or course_schedule='')""",
-				(self.student, self.student_group, self.date, self.name))
-			
-		if attendance_records:
-			frappe.throw(_("Attendance Record {0} exists against Student {1}")
-				.format(attendance_records[0][0], self.student))
+			attendance_record = frappe.db.exists('Student Attendance', {
+				'student': self.student,
+				'student_group': self.student_group,
+				'date': self.date,
+				'docstatus': ('!=', 2),
+				'name': ('!=', self.name),
+				'course_schedule': ''
+			})
+
+		if attendance_record:
+			record = get_link_to_form('Attendance Record', attendance_record)
+			frappe.throw(_('Student Attendance record {0} already exists against the Student {1}')
+				.format(record, frappe.bold(self.student)), title=_('Duplicate Entry'))
diff --git a/erpnext/education/doctype/student_leave_application/student_leave_application.json b/erpnext/education/doctype/student_leave_application/student_leave_application.json
index fe38b87..ad53976 100644
--- a/erpnext/education/doctype/student_leave_application/student_leave_application.json
+++ b/erpnext/education/doctype/student_leave_application/student_leave_application.json
@@ -1,375 +1,158 @@
 {
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "autoname": "EDU-SLA-.YYYY.-.#####", 
- "beta": 0, 
- "creation": "2016-11-28 15:38:54.793854", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
+ "actions": [],
+ "autoname": "EDU-SLA-.YYYY.-.#####",
+ "creation": "2016-11-28 15:38:54.793854",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "student",
+  "student_name",
+  "column_break_3",
+  "from_date",
+  "to_date",
+  "section_break_5",
+  "attendance_based_on",
+  "student_group",
+  "course_schedule",
+  "mark_as_present",
+  "column_break_11",
+  "reason",
+  "amended_from"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "student", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 1, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Student", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Student", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "student",
+   "fieldtype": "Link",
+   "in_global_search": 1,
+   "label": "Student",
+   "options": "Student",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_from": "student.title", 
-   "fieldname": "student_name", 
-   "fieldtype": "Read Only", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 1, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Student Name", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fetch_from": "student.title",
+   "fieldname": "student_name",
+   "fieldtype": "Read Only",
+   "in_global_search": 1,
+   "label": "Student Name",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_3", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "from_date", 
-   "fieldtype": "Date", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 1, 
-   "label": "From Date", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "from_date",
+   "fieldtype": "Date",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "From Date",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "to_date", 
-   "fieldtype": "Date", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "To Date", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "to_date",
+   "fieldtype": "Date",
+   "in_list_view": 1,
+   "label": "To Date",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "description": "Will show the student as Present in Student Monthly Attendance Report", 
-   "fieldname": "mark_as_present", 
-   "fieldtype": "Check", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Mark as Present", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "default": "0",
+   "description": "Check this to mark the student as present in case the student is not attending the institute to participate or represent the institute in any event.\n\n",
+   "fieldname": "mark_as_present",
+   "fieldtype": "Check",
+   "label": "Mark as Present"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_5", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "section_break_5",
+   "fieldtype": "Section Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "reason", 
-   "fieldtype": "Text", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Reason", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "reason",
+   "fieldtype": "Text",
+   "label": "Reason"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "amended_from", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Amended From", 
-   "length": 0, 
-   "no_copy": 1, 
-   "options": "Student Leave Application", 
-   "permlevel": 0, 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Student Leave Application",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "allow_in_quick_entry": 1,
+   "default": "Student Group",
+   "fieldname": "attendance_based_on",
+   "fieldtype": "Select",
+   "label": "Attendance Based On",
+   "options": "Student Group\nCourse Schedule"
+  },
+  {
+   "allow_in_quick_entry": 1,
+   "depends_on": "eval:doc.attendance_based_on === \"Student Group\";",
+   "fieldname": "student_group",
+   "fieldtype": "Link",
+   "label": "Student Group",
+   "mandatory_depends_on": "eval:doc.attendance_based_on === \"Student Group\";",
+   "options": "Student Group"
+  },
+  {
+   "allow_in_quick_entry": 1,
+   "depends_on": "eval:doc.attendance_based_on === \"Course Schedule\";",
+   "fieldname": "course_schedule",
+   "fieldtype": "Link",
+   "label": "Course Schedule",
+   "mandatory_depends_on": "eval:doc.attendance_based_on === \"Course Schedule\";",
+   "options": "Course Schedule"
+  },
+  {
+   "fieldname": "column_break_11",
+   "fieldtype": "Column Break"
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 1, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2018-08-21 16:15:50.807352", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Student Leave Application", 
- "name_case": "", 
- "owner": "Administrator", 
+ ],
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2020-07-08 13:22:38.329002",
+ "modified_by": "Administrator",
+ "module": "Education",
+ "name": "Student Leave Application",
+ "owner": "Administrator",
  "permissions": [
   {
-   "amend": 1, 
-   "cancel": 1, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 0, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Instructor", 
-   "set_user_permissions": 0, 
-   "share": 0, 
-   "submit": 1, 
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Instructor",
+   "submit": 1,
    "write": 1
-  }, 
+  },
   {
-   "amend": 1, 
-   "cancel": 1, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Academics User", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 1, 
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Academics User",
+   "share": 1,
+   "submit": 1,
    "write": 1
   }
- ], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 1, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "title_field": "student_name", 
- "track_changes": 0, 
- "track_seen": 0, 
- "track_views": 0
+ ],
+ "quick_entry": 1,
+ "restrict_to_domain": "Education",
+ "show_name_in_global_search": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "student_name"
 }
\ No newline at end of file
diff --git a/erpnext/education/doctype/student_leave_application/student_leave_application.py b/erpnext/education/doctype/student_leave_application/student_leave_application.py
index 410f0cc..c8841c9 100644
--- a/erpnext/education/doctype/student_leave_application/student_leave_application.py
+++ b/erpnext/education/doctype/student_leave_application/student_leave_application.py
@@ -5,17 +5,23 @@
 from __future__ import unicode_literals
 import frappe
 from frappe import _
-from frappe.utils import get_link_to_form
+from datetime import timedelta
+from frappe.utils import get_link_to_form, getdate
 from frappe.model.document import Document
-from frappe import throw, _
 
 class StudentLeaveApplication(Document):
 	def validate(self):
-		self.validate_dates()
 		self.validate_duplicate()
+		self.validate_from_to_dates('from_date', 'to_date')
+
+	def on_submit(self):
+		self.update_attendance()
+
+	def on_cancel(self):
+		self.cancel_attendance()
 
 	def validate_duplicate(self):
-		data = frappe.db.sql(""" select name from `tabStudent Leave Application`
+		data = frappe.db.sql("""select name from `tabStudent Leave Application`
 			where
 				((%(from_date)s > from_date and %(from_date)s < to_date) or
 				(%(to_date)s > from_date and %(to_date)s < to_date) or
@@ -29,10 +35,57 @@
 		}, as_dict=1)
 
 		if data:
-			link = get_link_to_form("Student Leave Application", data[0].name)
-			frappe.throw(_("Leave application {0} already exists against the student {1}")
-				.format(link, self.student))
+			link = get_link_to_form('Student Leave Application', data[0].name)
+			frappe.throw(_('Leave application {0} already exists against the student {1}')
+				.format(link, frappe.bold(self.student)), title=_('Duplicate Entry'))
 
-	def validate_dates(self):
-		if self.to_date < self.from_date :
-			throw(_("To Date cannot be less than From Date"))
\ No newline at end of file
+	def update_attendance(self):
+		for dt in daterange(getdate(self.from_date), getdate(self.to_date)):
+			date = dt.strftime('%Y-%m-%d')
+
+			attendance = frappe.db.exists('Student Attendance', {
+				'student': self.student,
+				'date': date,
+				'docstatus': ('!=', 2)
+			})
+
+			status = 'Present' if self.mark_as_present else 'Absent'
+			if attendance:
+				# update existing attendance record
+				values = dict()
+				values['status'] = status
+				values['leave_application'] = self.name
+				frappe.db.set_value('Student Attendance', attendance, values)
+			else:
+				# make a new attendance record
+				doc = frappe.new_doc('Student Attendance')
+				doc.student = self.student
+				doc.student_name = self.student_name
+				doc.date = date
+				doc.leave_application = self.name
+				doc.status = status
+				if self.attendance_based_on == 'Student Group':
+					doc.student_group = self.student_group
+				else:
+					doc.course_schedule = self.course_schedule
+				doc.insert(ignore_permissions=True, ignore_mandatory=True)
+				doc.submit()
+
+	def cancel_attendance(self):
+		if self.docstatus == 2:
+			attendance = frappe.db.sql("""
+				SELECT name
+				FROM `tabStudent Attendance`
+				WHERE
+					student = %s and
+					(date between %s and %s) and
+					docstatus < 2
+			""", (self.student, self.from_date, self.to_date), as_dict=1)
+
+			for name in attendance:
+				frappe.db.set_value('Student Attendance', name, 'docstatus', 2)
+
+
+def daterange(start_date, end_date):
+	for n in range(int ((end_date - start_date).days)+1):
+		yield start_date + timedelta(n)
diff --git a/erpnext/education/doctype/student_leave_application/student_leave_application_dashboard.py b/erpnext/education/doctype/student_leave_application/student_leave_application_dashboard.py
new file mode 100644
index 0000000..fdcc147
--- /dev/null
+++ b/erpnext/education/doctype/student_leave_application/student_leave_application_dashboard.py
@@ -0,0 +1,11 @@
+from __future__ import unicode_literals
+
+def get_data():
+	return {
+		'fieldname': 'leave_application',
+		'transactions': [
+			{
+				'items': ['Student Attendance']
+			}
+		]
+	}
\ No newline at end of file
diff --git a/erpnext/education/doctype/student_leave_application/test_student_leave_application.py b/erpnext/education/doctype/student_leave_application/test_student_leave_application.py
index ddb30ac..e9b568a 100644
--- a/erpnext/education/doctype/student_leave_application/test_student_leave_application.py
+++ b/erpnext/education/doctype/student_leave_application/test_student_leave_application.py
@@ -5,8 +5,66 @@
 
 import frappe
 import unittest
-
-# test_records = frappe.get_test_records('Student Leave Application')
+from frappe.utils import getdate, add_days
+from erpnext.education.doctype.student_group.test_student_group import get_random_group
+from erpnext.education.doctype.student.test_student import create_student
 
 class TestStudentLeaveApplication(unittest.TestCase):
-	pass
+	def setUp(self):
+		frappe.db.sql("""delete from `tabStudent Leave Application`""")
+
+	def test_attendance_record_creation(self):
+		leave_application = create_leave_application()
+		attendance_record = frappe.db.exists('Student Attendance', {'leave_application': leave_application.name, 'status': 'Absent'})
+		self.assertTrue(attendance_record)
+
+		# mark as present
+		date = add_days(getdate(), -1)
+		leave_application = create_leave_application(date, date, 1)
+		attendance_record = frappe.db.exists('Student Attendance', {'leave_application': leave_application.name, 'status': 'Present'})
+		self.assertTrue(attendance_record)
+
+	def test_attendance_record_updated(self):
+		attendance = create_student_attendance()
+		create_leave_application()
+		self.assertEqual(frappe.db.get_value('Student Attendance', attendance.name, 'status'), 'Absent')
+
+	def test_attendance_record_cancellation(self):
+		leave_application = create_leave_application()
+		leave_application.cancel()
+		attendance_status = frappe.db.get_value('Student Attendance', {'leave_application': leave_application.name}, 'docstatus')
+		self.assertTrue(attendance_status, 2)
+
+
+def create_leave_application(from_date=None, to_date=None, mark_as_present=0):
+	student = get_student()
+
+	leave_application = frappe.get_doc({
+		'doctype': 'Student Leave Application',
+		'student': student.name,
+		'attendance_based_on': 'Student Group',
+		'student_group': get_random_group().name,
+		'from_date': from_date if from_date else getdate(),
+		'to_date': from_date if from_date else getdate(),
+		'mark_as_present': mark_as_present
+	}).insert()
+	leave_application.submit()
+	return leave_application
+
+def create_student_attendance(date=None, status=None):
+	student = get_student()
+	attendance = frappe.get_doc({
+		'doctype': 'Student Attendance',
+		'student': student.name,
+		'status': status if status else 'Present',
+		'date': date if date else getdate(),
+		'student_group': get_random_group().name
+	}).insert()
+	return attendance
+
+def get_student():
+	return create_student(dict(
+		email='test_student@gmail.com',
+		first_name='Test',
+		last_name='Student'
+	))
\ No newline at end of file
diff --git a/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.py b/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.py
index c0a7359..17bc367 100644
--- a/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.py
+++ b/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.py
@@ -80,7 +80,7 @@
 		from_date, to_date = frappe.db.get_value("Academic Term", academic_term, ["term_start_date", "term_end_date"])
 	if from_date and to_date:
 		attendance = dict(frappe.db.sql('''select status, count(student) as no_of_days
-			from `tabStudent Attendance` where student = %s
+			from `tabStudent Attendance` where student = %s and docstatus = 1
 			and date between %s and %s group by status''',
 			(student, from_date, to_date)))
 		if "Absent" not in attendance.keys():
diff --git a/erpnext/education/report/absent_student_report/absent_student_report.py b/erpnext/education/report/absent_student_report/absent_student_report.py
index 8e6ce51..4e57cc6 100644
--- a/erpnext/education/report/absent_student_report/absent_student_report.py
+++ b/erpnext/education/report/absent_student_report/absent_student_report.py
@@ -11,7 +11,7 @@
 
 	if not filters.get("date"):
 		msgprint(_("Please select date"), raise_exception=1)
-	
+
 	columns = get_columns(filters)
 	date = filters.get("date")
 
@@ -26,27 +26,27 @@
 		if not student.student in leave_applicants:
 			row = [student.student, student.student_name, student.student_group]
 			stud_details = frappe.db.get_value("Student", student.student, ['student_email_id', 'student_mobile_number'], as_dict=True)
-			
+
 			if stud_details.student_email_id:
 				row+=[stud_details.student_email_id]
 			else:
 				row+= [""]
-			
+
 			if stud_details.student_mobile_number:
 				row+=[stud_details.student_mobile_number]
 			else:
 				row+= [""]
 			if transportation_details.get(student.student):
 				row += transportation_details.get(student.student)
-				
+
 			data.append(row)
-	
+
 	return columns, data
 
 def get_columns(filters):
-	columns = [ 
-		_("Student") + ":Link/Student:90", 
-		_("Student Name") + "::150", 
+	columns = [
+		_("Student") + ":Link/Student:90",
+		_("Student Name") + "::150",
 		_("Student Group") + "::180",
 		_("Student Email Address") + "::180",
 		_("Student Mobile No.") + "::150",
@@ -56,15 +56,29 @@
 	return columns
 
 def get_absent_students(date):
-	absent_students = frappe.db.sql("""select student, student_name, student_group from `tabStudent Attendance` 
-		where status="Absent" and date = %s order by student_group, student_name""", date, as_dict=1)
+	absent_students = frappe.db.sql("""
+		SELECT student, student_name, student_group
+		FROM `tabStudent Attendance`
+		WHERE
+			status='Absent' and docstatus=1 and date = %s
+		ORDER BY
+			student_group, student_name""",
+	date, as_dict=1)
 	return absent_students
 
 def get_leave_applications(date):
 	leave_applicants = []
-	for student in frappe.db.sql("""select student from `tabStudent Leave Application` 
-	where docstatus = 1 and from_date <= %s and to_date >= %s""", (date, date)):
+	leave_applications = frappe.db.sql("""
+		SELECT student
+		FROM
+			`tabStudent Leave Application`
+		WHERE
+			docstatus = 1 and mark_as_present = 1 and
+			from_date <= %s and to_date >= %s
+	""", (date, date))
+	for student in leave_applications:
 		leave_applicants.append(student[0])
+
 	return leave_applicants
 
 def get_transportation_details(date, student_list):
diff --git a/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.py b/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.py
index 646e3f7..c65d233 100644
--- a/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.py
+++ b/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.py
@@ -11,7 +11,7 @@
 
 	if not filters.get("date"):
 		msgprint(_("Please select date"), raise_exception=1)
-	
+
 	columns = get_columns(filters)
 
 	active_student_group = get_active_student_group()
@@ -37,28 +37,28 @@
 	return columns, data
 
 def get_columns(filters):
-	columns = [ 
-		_("Student Group") + ":Link/Student Group:250", 
-		_("Student Group Strength") + "::170", 
-		_("Present") + "::90", 
+	columns = [
+		_("Student Group") + ":Link/Student Group:250",
+		_("Student Group Strength") + "::170",
+		_("Present") + "::90",
 		_("Absent") + "::90",
 		_("Not Marked") + "::90"
 	]
 	return columns
 
 def get_active_student_group():
-	active_student_groups = frappe.db.sql("""select name from `tabStudent Group` where group_based_on = "Batch" 
+	active_student_groups = frappe.db.sql("""select name from `tabStudent Group` where group_based_on = "Batch"
 		and academic_year=%s order by name""", (frappe.defaults.get_defaults().academic_year), as_dict=1)
 	return active_student_groups
 
 def get_student_group_strength(student_group):
-	student_group_strength = frappe.db.sql("""select count(*) from `tabStudent Group Student` 
+	student_group_strength = frappe.db.sql("""select count(*) from `tabStudent Group Student`
 		where parent = %s and active=1""", student_group)[0][0]
 	return student_group_strength
 
 def get_student_attendance(student_group, date):
-	student_attendance = frappe.db.sql("""select count(*) as count, status from `tabStudent Attendance` where \
-				student_group= %s and date= %s and\
+	student_attendance = frappe.db.sql("""select count(*) as count, status from `tabStudent Attendance` where
+				student_group= %s and date= %s and docstatus = 1 and
 				(course_schedule is Null or course_schedule='') group by status""",
 				(student_group, date), as_dict=1)
 	return student_attendance
\ No newline at end of file
diff --git a/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.py b/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.py
index 3f1d5b3..d820bfb 100644
--- a/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.py
+++ b/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.py
@@ -57,8 +57,9 @@
 	return student_list
 
 def get_attendance_list(from_date, to_date, student_group, students_list):
-	attendance_list = frappe.db.sql('''select student, date, status 
-		from `tabStudent Attendance` where student_group = %s 
+	attendance_list = frappe.db.sql('''select student, date, status
+		from `tabStudent Attendance` where student_group = %s
+		and docstatus = 1
 		and date between %s and %s
 		order by student, date''',
 		(student_group, from_date, to_date), as_dict=1)
@@ -75,10 +76,10 @@
 def get_students_with_leave_application(from_date, to_date, students_list):
 	if not students_list: return
 	leave_applications = frappe.db.sql("""
-		select student, from_date, to_date 
-		from `tabStudent Leave Application` 
-		where 
-			mark_as_present and docstatus = 1
+		select student, from_date, to_date
+		from `tabStudent Leave Application`
+		where
+			mark_as_present = 1 and docstatus = 1
 			and student in %(students)s
 			and (
 				from_date between %(from_date)s and %(to_date)s