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
This commit is contained in:
parent
04a76285e2
commit
833682b03d
@ -104,6 +104,7 @@ def make_attendance_records(student, student_name, status, course_schedule=None,
|
|||||||
student_attendance.date = date
|
student_attendance.date = date
|
||||||
student_attendance.status = status
|
student_attendance.status = status
|
||||||
student_attendance.save()
|
student_attendance.save()
|
||||||
|
student_attendance.submit()
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@ -363,9 +364,9 @@ def get_current_enrollment(student, academic_year=None):
|
|||||||
select
|
select
|
||||||
name as program_enrollment, student_name, program, student_batch_name as student_batch,
|
name as program_enrollment, student_name, program, student_batch_name as student_batch,
|
||||||
student_category, academic_term, academic_year
|
student_category, academic_term, academic_year
|
||||||
from
|
from
|
||||||
`tabProgram Enrollment`
|
`tabProgram Enrollment`
|
||||||
where
|
where
|
||||||
student = %s and academic_year = %s
|
student = %s and academic_year = %s
|
||||||
order by creation''', (student, current_academic_year), as_dict=1)
|
order by creation''', (student, current_academic_year), as_dict=1)
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ class Student(Document):
|
|||||||
for sibling in self.siblings:
|
for sibling in self.siblings:
|
||||||
if sibling.date_of_birth and getdate(sibling.date_of_birth) > getdate():
|
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))
|
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()):
|
if self.date_of_birth and getdate(self.date_of_birth) >= getdate(today()):
|
||||||
frappe.throw(_("Date of Birth cannot be greater than today."))
|
frappe.throw(_("Date of Birth cannot be greater than today."))
|
||||||
|
|
||||||
@ -157,5 +157,5 @@ def get_timeline_data(doctype, name):
|
|||||||
from `tabStudent Attendance` where
|
from `tabStudent Attendance` where
|
||||||
student=%s
|
student=%s
|
||||||
and `date` > date_sub(curdate(), interval 1 year)
|
and `date` > date_sub(curdate(), interval 1 year)
|
||||||
and status = 'Present'
|
and docstatus = 1 and status = 'Present'
|
||||||
group by date''', name))
|
group by date''', name))
|
||||||
|
@ -1,287 +1,125 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_guest_to_view": 0,
|
"allow_import": 1,
|
||||||
"allow_import": 1,
|
"autoname": "naming_series:",
|
||||||
"allow_rename": 0,
|
"creation": "2015-11-05 15:20:23.045996",
|
||||||
"autoname": "",
|
"doctype": "DocType",
|
||||||
"beta": 0,
|
"document_type": "Document",
|
||||||
"creation": "2015-11-05 15:20:23.045996",
|
"engine": "InnoDB",
|
||||||
"custom": 0,
|
"field_order": [
|
||||||
"docstatus": 0,
|
"naming_series",
|
||||||
"doctype": "DocType",
|
"student",
|
||||||
"document_type": "Document",
|
"student_name",
|
||||||
"editable_grid": 0,
|
"course_schedule",
|
||||||
"engine": "InnoDB",
|
"student_group",
|
||||||
|
"column_break_3",
|
||||||
|
"date",
|
||||||
|
"status",
|
||||||
|
"leave_application",
|
||||||
|
"amended_from"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "student",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Link",
|
||||||
"bold": 0,
|
"in_global_search": 1,
|
||||||
"collapsible": 0,
|
"in_standard_filter": 1,
|
||||||
"columns": 0,
|
"label": "Student",
|
||||||
"fieldname": "student",
|
"options": "Student",
|
||||||
"fieldtype": "Link",
|
"reqd": 1,
|
||||||
"hidden": 0,
|
"search_index": 1
|
||||||
"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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "course_schedule",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Link",
|
||||||
"bold": 0,
|
"in_list_view": 1,
|
||||||
"collapsible": 0,
|
"label": "Course Schedule",
|
||||||
"columns": 0,
|
"options": "Course Schedule"
|
||||||
"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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "date",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Date",
|
||||||
"bold": 0,
|
"label": "Date",
|
||||||
"collapsible": 0,
|
"reqd": 1,
|
||||||
"columns": 0,
|
"search_index": 1
|
||||||
"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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "column_break_3",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Column Break"
|
||||||
"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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_from": "student.title",
|
"fetch_from": "student.title",
|
||||||
"fieldname": "student_name",
|
"fieldname": "student_name",
|
||||||
"fieldtype": "Read Only",
|
"fieldtype": "Read Only",
|
||||||
"hidden": 0,
|
"in_global_search": 1,
|
||||||
"ignore_user_permissions": 0,
|
"label": "Student Name"
|
||||||
"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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "student_group",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Link",
|
||||||
"bold": 0,
|
"in_global_search": 1,
|
||||||
"collapsible": 0,
|
"in_standard_filter": 1,
|
||||||
"columns": 0,
|
"label": "Student Group",
|
||||||
"fieldname": "student_group",
|
"options": "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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"default": "Present",
|
||||||
"allow_on_submit": 0,
|
"fieldname": "status",
|
||||||
"bold": 0,
|
"fieldtype": "Select",
|
||||||
"collapsible": 0,
|
"in_list_view": 1,
|
||||||
"columns": 0,
|
"in_standard_filter": 1,
|
||||||
"default": "Present",
|
"label": "Status",
|
||||||
"fieldname": "status",
|
"options": "Present\nAbsent",
|
||||||
"fieldtype": "Select",
|
"reqd": 1
|
||||||
"hidden": 0,
|
},
|
||||||
"ignore_user_permissions": 0,
|
{
|
||||||
"ignore_xss_filter": 0,
|
"fieldname": "leave_application",
|
||||||
"in_filter": 0,
|
"fieldtype": "Link",
|
||||||
"in_global_search": 0,
|
"label": "Leave Application",
|
||||||
"in_list_view": 1,
|
"options": "Student Leave Application",
|
||||||
"in_standard_filter": 1,
|
"read_only": 1
|
||||||
"label": "Status",
|
},
|
||||||
"length": 0,
|
{
|
||||||
"no_copy": 0,
|
"fieldname": "naming_series",
|
||||||
"options": "Present\nAbsent",
|
"fieldtype": "Select",
|
||||||
"permlevel": 0,
|
"label": "Series",
|
||||||
"precision": "",
|
"options": "EDU-ATT-.YYYY.-"
|
||||||
"print_hide": 0,
|
},
|
||||||
"print_hide_if_no_value": 0,
|
{
|
||||||
"read_only": 0,
|
"fieldname": "amended_from",
|
||||||
"remember_last_selected_value": 0,
|
"fieldtype": "Link",
|
||||||
"report_hide": 0,
|
"label": "Amended From",
|
||||||
"reqd": 1,
|
"no_copy": 1,
|
||||||
"search_index": 0,
|
"options": "Student Attendance",
|
||||||
"set_only_once": 0,
|
"print_hide": 1,
|
||||||
"translatable": 0,
|
"read_only": 1
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"is_submittable": 1,
|
||||||
"hide_heading": 0,
|
"links": [],
|
||||||
"hide_toolbar": 0,
|
"modified": "2020-07-08 13:55:42.580181",
|
||||||
"idx": 0,
|
"modified_by": "Administrator",
|
||||||
"image_view": 0,
|
"module": "Education",
|
||||||
"in_create": 0,
|
"name": "Student Attendance",
|
||||||
"is_submittable": 0,
|
"owner": "Administrator",
|
||||||
"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",
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"cancel": 1,
|
||||||
"cancel": 0,
|
"create": 1,
|
||||||
"create": 1,
|
"delete": 1,
|
||||||
"delete": 1,
|
"email": 1,
|
||||||
"email": 1,
|
"export": 1,
|
||||||
"export": 1,
|
"print": 1,
|
||||||
"if_owner": 0,
|
"read": 1,
|
||||||
"import": 0,
|
"report": 1,
|
||||||
"permlevel": 0,
|
"role": "Academics User",
|
||||||
"print": 1,
|
"share": 1,
|
||||||
"read": 1,
|
"submit": 1,
|
||||||
"report": 1,
|
|
||||||
"role": "Academics User",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
"restrict_to_domain": "Education",
|
||||||
"read_only": 0,
|
"sort_field": "modified",
|
||||||
"read_only_onload": 0,
|
"sort_order": "DESC",
|
||||||
"restrict_to_domain": "Education",
|
"title_field": "student_name"
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
|
||||||
"sort_order": "DESC",
|
|
||||||
"title_field": "student_name",
|
|
||||||
"track_changes": 0,
|
|
||||||
"track_seen": 0
|
|
||||||
}
|
}
|
@ -6,52 +6,63 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe import _
|
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
|
from erpnext.education.api import get_student_group_students
|
||||||
|
|
||||||
|
|
||||||
class StudentAttendance(Document):
|
class StudentAttendance(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_date()
|
|
||||||
self.validate_mandatory()
|
self.validate_mandatory()
|
||||||
self.validate_course_schedule()
|
self.set_date()
|
||||||
|
self.set_student_group()
|
||||||
self.validate_student()
|
self.validate_student()
|
||||||
self.validate_duplication()
|
self.validate_duplication()
|
||||||
|
|
||||||
def validate_date(self):
|
def set_date(self):
|
||||||
if self.course_schedule:
|
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):
|
def validate_mandatory(self):
|
||||||
if not (self.student_group or self.course_schedule):
|
if not (self.student_group or self.course_schedule):
|
||||||
frappe.throw(_("""Student Group or Course Schedule is mandatory"""))
|
frappe.throw(_('{0} or {1} is mandatory').format(frappe.bold('Student Group'),
|
||||||
|
frappe.bold('Course Schedule')), title=_('Mandatory Fields'))
|
||||||
def validate_course_schedule(self):
|
|
||||||
|
def set_student_group(self):
|
||||||
if self.course_schedule:
|
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):
|
def validate_student(self):
|
||||||
if self.course_schedule:
|
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:
|
else:
|
||||||
student_group = self.student_group
|
student_group = self.student_group
|
||||||
student_group_students = [d.student for d in get_student_group_students(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:
|
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):
|
def validate_duplication(self):
|
||||||
"""Check if the Attendance Record is Unique"""
|
"""Check if the Attendance Record is Unique"""
|
||||||
attendance_records=None
|
attendance_record = None
|
||||||
if self.course_schedule:
|
if self.course_schedule:
|
||||||
attendance_records= frappe.db.sql("""select name from `tabStudent Attendance` where \
|
attendance_record = frappe.db.exists('Student Attendance', {
|
||||||
student= %s and ifnull(course_schedule, '')= %s and name != %s""",
|
'student': self.student,
|
||||||
(self.student, cstr(self.course_schedule), self.name))
|
'course_schedule': self.course_schedule,
|
||||||
|
'docstatus': ('!=', 2),
|
||||||
|
'name': ('!=', self.name)
|
||||||
|
})
|
||||||
else:
|
else:
|
||||||
attendance_records= frappe.db.sql("""select name from `tabStudent Attendance` where \
|
attendance_record = frappe.db.exists('Student Attendance', {
|
||||||
student= %s and student_group= %s and date= %s and name != %s and \
|
'student': self.student,
|
||||||
(course_schedule is Null or course_schedule='')""",
|
'student_group': self.student_group,
|
||||||
(self.student, self.student_group, self.date, self.name))
|
'date': self.date,
|
||||||
|
'docstatus': ('!=', 2),
|
||||||
if attendance_records:
|
'name': ('!=', self.name),
|
||||||
frappe.throw(_("Attendance Record {0} exists against Student {1}")
|
'course_schedule': ''
|
||||||
.format(attendance_records[0][0], self.student))
|
})
|
||||||
|
|
||||||
|
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'))
|
||||||
|
@ -1,375 +1,158 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_guest_to_view": 0,
|
"autoname": "EDU-SLA-.YYYY.-.#####",
|
||||||
"allow_import": 0,
|
"creation": "2016-11-28 15:38:54.793854",
|
||||||
"allow_rename": 0,
|
"doctype": "DocType",
|
||||||
"autoname": "EDU-SLA-.YYYY.-.#####",
|
"editable_grid": 1,
|
||||||
"beta": 0,
|
"engine": "InnoDB",
|
||||||
"creation": "2016-11-28 15:38:54.793854",
|
"field_order": [
|
||||||
"custom": 0,
|
"student",
|
||||||
"docstatus": 0,
|
"student_name",
|
||||||
"doctype": "DocType",
|
"column_break_3",
|
||||||
"document_type": "",
|
"from_date",
|
||||||
"editable_grid": 1,
|
"to_date",
|
||||||
"engine": "InnoDB",
|
"section_break_5",
|
||||||
|
"attendance_based_on",
|
||||||
|
"student_group",
|
||||||
|
"course_schedule",
|
||||||
|
"mark_as_present",
|
||||||
|
"column_break_11",
|
||||||
|
"reason",
|
||||||
|
"amended_from"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "student",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Link",
|
||||||
"allow_on_submit": 0,
|
"in_global_search": 1,
|
||||||
"bold": 0,
|
"label": "Student",
|
||||||
"collapsible": 0,
|
"options": "Student",
|
||||||
"columns": 0,
|
"reqd": 1
|
||||||
"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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fetch_from": "student.title",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "student_name",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Read Only",
|
||||||
"bold": 0,
|
"in_global_search": 1,
|
||||||
"collapsible": 0,
|
"label": "Student Name",
|
||||||
"columns": 0,
|
"read_only": 1
|
||||||
"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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "column_break_3",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Column Break"
|
||||||
"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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "from_date",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Date",
|
||||||
"allow_on_submit": 0,
|
"in_list_view": 1,
|
||||||
"bold": 0,
|
"in_standard_filter": 1,
|
||||||
"collapsible": 0,
|
"label": "From Date",
|
||||||
"columns": 0,
|
"reqd": 1
|
||||||
"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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "to_date",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Date",
|
||||||
"allow_on_submit": 0,
|
"in_list_view": 1,
|
||||||
"bold": 0,
|
"label": "To Date",
|
||||||
"collapsible": 0,
|
"reqd": 1
|
||||||
"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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"default": "0",
|
||||||
"allow_in_quick_entry": 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",
|
||||||
"allow_on_submit": 0,
|
"fieldname": "mark_as_present",
|
||||||
"bold": 0,
|
"fieldtype": "Check",
|
||||||
"collapsible": 0,
|
"label": "Mark as Present"
|
||||||
"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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "section_break_5",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Section Break"
|
||||||
"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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "reason",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Text",
|
||||||
"allow_on_submit": 0,
|
"label": "Reason"
|
||||||
"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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "amended_from",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Link",
|
||||||
"allow_on_submit": 0,
|
"label": "Amended From",
|
||||||
"bold": 0,
|
"no_copy": 1,
|
||||||
"collapsible": 0,
|
"options": "Student Leave Application",
|
||||||
"columns": 0,
|
"print_hide": 1,
|
||||||
"fieldname": "amended_from",
|
"read_only": 1
|
||||||
"fieldtype": "Link",
|
},
|
||||||
"hidden": 0,
|
{
|
||||||
"ignore_user_permissions": 0,
|
"allow_in_quick_entry": 1,
|
||||||
"ignore_xss_filter": 0,
|
"default": "Student Group",
|
||||||
"in_filter": 0,
|
"fieldname": "attendance_based_on",
|
||||||
"in_global_search": 0,
|
"fieldtype": "Select",
|
||||||
"in_list_view": 0,
|
"label": "Attendance Based On",
|
||||||
"in_standard_filter": 0,
|
"options": "Student Group\nCourse Schedule"
|
||||||
"label": "Amended From",
|
},
|
||||||
"length": 0,
|
{
|
||||||
"no_copy": 1,
|
"allow_in_quick_entry": 1,
|
||||||
"options": "Student Leave Application",
|
"depends_on": "eval:doc.attendance_based_on === \"Student Group\";",
|
||||||
"permlevel": 0,
|
"fieldname": "student_group",
|
||||||
"print_hide": 1,
|
"fieldtype": "Link",
|
||||||
"print_hide_if_no_value": 0,
|
"label": "Student Group",
|
||||||
"read_only": 1,
|
"mandatory_depends_on": "eval:doc.attendance_based_on === \"Student Group\";",
|
||||||
"remember_last_selected_value": 0,
|
"options": "Student Group"
|
||||||
"report_hide": 0,
|
},
|
||||||
"reqd": 0,
|
{
|
||||||
"search_index": 0,
|
"allow_in_quick_entry": 1,
|
||||||
"set_only_once": 0,
|
"depends_on": "eval:doc.attendance_based_on === \"Course Schedule\";",
|
||||||
"translatable": 0,
|
"fieldname": "course_schedule",
|
||||||
"unique": 0
|
"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,
|
"is_submittable": 1,
|
||||||
"hide_heading": 0,
|
"links": [],
|
||||||
"hide_toolbar": 0,
|
"modified": "2020-07-08 13:22:38.329002",
|
||||||
"idx": 0,
|
"modified_by": "Administrator",
|
||||||
"image_view": 0,
|
"module": "Education",
|
||||||
"in_create": 0,
|
"name": "Student Leave Application",
|
||||||
"is_submittable": 1,
|
"owner": "Administrator",
|
||||||
"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",
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 1,
|
"amend": 1,
|
||||||
"cancel": 1,
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 0,
|
"print": 1,
|
||||||
"if_owner": 0,
|
"read": 1,
|
||||||
"import": 0,
|
"report": 1,
|
||||||
"permlevel": 0,
|
"role": "Instructor",
|
||||||
"print": 1,
|
"submit": 1,
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Instructor",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 0,
|
|
||||||
"submit": 1,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 1,
|
"amend": 1,
|
||||||
"cancel": 1,
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
"print": 1,
|
||||||
"import": 0,
|
"read": 1,
|
||||||
"permlevel": 0,
|
"report": 1,
|
||||||
"print": 1,
|
"role": "Academics User",
|
||||||
"read": 1,
|
"share": 1,
|
||||||
"report": 1,
|
"submit": 1,
|
||||||
"role": "Academics User",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 1,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"read_only": 0,
|
"restrict_to_domain": "Education",
|
||||||
"read_only_onload": 0,
|
"show_name_in_global_search": 1,
|
||||||
"restrict_to_domain": "Education",
|
"sort_field": "modified",
|
||||||
"show_name_in_global_search": 1,
|
"sort_order": "DESC",
|
||||||
"sort_field": "modified",
|
"title_field": "student_name"
|
||||||
"sort_order": "DESC",
|
|
||||||
"title_field": "student_name",
|
|
||||||
"track_changes": 0,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
@ -5,17 +5,23 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
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.model.document import Document
|
||||||
from frappe import throw, _
|
|
||||||
|
|
||||||
class StudentLeaveApplication(Document):
|
class StudentLeaveApplication(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_dates()
|
|
||||||
self.validate_duplicate()
|
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):
|
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
|
where
|
||||||
((%(from_date)s > from_date and %(from_date)s < to_date) or
|
((%(from_date)s > from_date and %(from_date)s < to_date) or
|
||||||
(%(to_date)s > from_date and %(to_date)s < to_date) or
|
(%(to_date)s > from_date and %(to_date)s < to_date) or
|
||||||
@ -29,10 +35,57 @@ class StudentLeaveApplication(Document):
|
|||||||
}, as_dict=1)
|
}, as_dict=1)
|
||||||
|
|
||||||
if data:
|
if data:
|
||||||
link = get_link_to_form("Student Leave Application", data[0].name)
|
link = get_link_to_form('Student Leave Application', data[0].name)
|
||||||
frappe.throw(_("Leave application {0} already exists against the student {1}")
|
frappe.throw(_('Leave application {0} already exists against the student {1}')
|
||||||
.format(link, self.student))
|
.format(link, frappe.bold(self.student)), title=_('Duplicate Entry'))
|
||||||
|
|
||||||
def validate_dates(self):
|
def update_attendance(self):
|
||||||
if self.to_date < self.from_date :
|
for dt in daterange(getdate(self.from_date), getdate(self.to_date)):
|
||||||
throw(_("To Date cannot be less than From 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)
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
def get_data():
|
||||||
|
return {
|
||||||
|
'fieldname': 'leave_application',
|
||||||
|
'transactions': [
|
||||||
|
{
|
||||||
|
'items': ['Student Attendance']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -5,8 +5,66 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
from frappe.utils import getdate, add_days
|
||||||
# test_records = frappe.get_test_records('Student Leave Application')
|
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):
|
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'
|
||||||
|
))
|
@ -80,7 +80,7 @@ def get_attendance_count(student, academic_year, academic_term=None):
|
|||||||
from_date, to_date = frappe.db.get_value("Academic Term", academic_term, ["term_start_date", "term_end_date"])
|
from_date, to_date = frappe.db.get_value("Academic Term", academic_term, ["term_start_date", "term_end_date"])
|
||||||
if from_date and to_date:
|
if from_date and to_date:
|
||||||
attendance = dict(frappe.db.sql('''select status, count(student) as no_of_days
|
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''',
|
and date between %s and %s group by status''',
|
||||||
(student, from_date, to_date)))
|
(student, from_date, to_date)))
|
||||||
if "Absent" not in attendance.keys():
|
if "Absent" not in attendance.keys():
|
||||||
|
@ -11,7 +11,7 @@ def execute(filters=None):
|
|||||||
|
|
||||||
if not filters.get("date"):
|
if not filters.get("date"):
|
||||||
msgprint(_("Please select date"), raise_exception=1)
|
msgprint(_("Please select date"), raise_exception=1)
|
||||||
|
|
||||||
columns = get_columns(filters)
|
columns = get_columns(filters)
|
||||||
date = filters.get("date")
|
date = filters.get("date")
|
||||||
|
|
||||||
@ -26,27 +26,27 @@ def execute(filters=None):
|
|||||||
if not student.student in leave_applicants:
|
if not student.student in leave_applicants:
|
||||||
row = [student.student, student.student_name, student.student_group]
|
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)
|
stud_details = frappe.db.get_value("Student", student.student, ['student_email_id', 'student_mobile_number'], as_dict=True)
|
||||||
|
|
||||||
if stud_details.student_email_id:
|
if stud_details.student_email_id:
|
||||||
row+=[stud_details.student_email_id]
|
row+=[stud_details.student_email_id]
|
||||||
else:
|
else:
|
||||||
row+= [""]
|
row+= [""]
|
||||||
|
|
||||||
if stud_details.student_mobile_number:
|
if stud_details.student_mobile_number:
|
||||||
row+=[stud_details.student_mobile_number]
|
row+=[stud_details.student_mobile_number]
|
||||||
else:
|
else:
|
||||||
row+= [""]
|
row+= [""]
|
||||||
if transportation_details.get(student.student):
|
if transportation_details.get(student.student):
|
||||||
row += transportation_details.get(student.student)
|
row += transportation_details.get(student.student)
|
||||||
|
|
||||||
data.append(row)
|
data.append(row)
|
||||||
|
|
||||||
return columns, data
|
return columns, data
|
||||||
|
|
||||||
def get_columns(filters):
|
def get_columns(filters):
|
||||||
columns = [
|
columns = [
|
||||||
_("Student") + ":Link/Student:90",
|
_("Student") + ":Link/Student:90",
|
||||||
_("Student Name") + "::150",
|
_("Student Name") + "::150",
|
||||||
_("Student Group") + "::180",
|
_("Student Group") + "::180",
|
||||||
_("Student Email Address") + "::180",
|
_("Student Email Address") + "::180",
|
||||||
_("Student Mobile No.") + "::150",
|
_("Student Mobile No.") + "::150",
|
||||||
@ -56,15 +56,29 @@ def get_columns(filters):
|
|||||||
return columns
|
return columns
|
||||||
|
|
||||||
def get_absent_students(date):
|
def get_absent_students(date):
|
||||||
absent_students = frappe.db.sql("""select student, student_name, student_group from `tabStudent Attendance`
|
absent_students = frappe.db.sql("""
|
||||||
where status="Absent" and date = %s order by student_group, student_name""", date, as_dict=1)
|
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
|
return absent_students
|
||||||
|
|
||||||
def get_leave_applications(date):
|
def get_leave_applications(date):
|
||||||
leave_applicants = []
|
leave_applicants = []
|
||||||
for student in frappe.db.sql("""select student from `tabStudent Leave Application`
|
leave_applications = frappe.db.sql("""
|
||||||
where docstatus = 1 and from_date <= %s and to_date >= %s""", (date, date)):
|
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])
|
leave_applicants.append(student[0])
|
||||||
|
|
||||||
return leave_applicants
|
return leave_applicants
|
||||||
|
|
||||||
def get_transportation_details(date, student_list):
|
def get_transportation_details(date, student_list):
|
||||||
|
@ -11,7 +11,7 @@ def execute(filters=None):
|
|||||||
|
|
||||||
if not filters.get("date"):
|
if not filters.get("date"):
|
||||||
msgprint(_("Please select date"), raise_exception=1)
|
msgprint(_("Please select date"), raise_exception=1)
|
||||||
|
|
||||||
columns = get_columns(filters)
|
columns = get_columns(filters)
|
||||||
|
|
||||||
active_student_group = get_active_student_group()
|
active_student_group = get_active_student_group()
|
||||||
@ -37,28 +37,28 @@ def execute(filters=None):
|
|||||||
return columns, data
|
return columns, data
|
||||||
|
|
||||||
def get_columns(filters):
|
def get_columns(filters):
|
||||||
columns = [
|
columns = [
|
||||||
_("Student Group") + ":Link/Student Group:250",
|
_("Student Group") + ":Link/Student Group:250",
|
||||||
_("Student Group Strength") + "::170",
|
_("Student Group Strength") + "::170",
|
||||||
_("Present") + "::90",
|
_("Present") + "::90",
|
||||||
_("Absent") + "::90",
|
_("Absent") + "::90",
|
||||||
_("Not Marked") + "::90"
|
_("Not Marked") + "::90"
|
||||||
]
|
]
|
||||||
return columns
|
return columns
|
||||||
|
|
||||||
def get_active_student_group():
|
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)
|
and academic_year=%s order by name""", (frappe.defaults.get_defaults().academic_year), as_dict=1)
|
||||||
return active_student_groups
|
return active_student_groups
|
||||||
|
|
||||||
def get_student_group_strength(student_group):
|
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]
|
where parent = %s and active=1""", student_group)[0][0]
|
||||||
return student_group_strength
|
return student_group_strength
|
||||||
|
|
||||||
def get_student_attendance(student_group, date):
|
def get_student_attendance(student_group, date):
|
||||||
student_attendance = frappe.db.sql("""select count(*) as count, status from `tabStudent Attendance` where \
|
student_attendance = frappe.db.sql("""select count(*) as count, status from `tabStudent Attendance` where
|
||||||
student_group= %s and date= %s and\
|
student_group= %s and date= %s and docstatus = 1 and
|
||||||
(course_schedule is Null or course_schedule='') group by status""",
|
(course_schedule is Null or course_schedule='') group by status""",
|
||||||
(student_group, date), as_dict=1)
|
(student_group, date), as_dict=1)
|
||||||
return student_attendance
|
return student_attendance
|
@ -57,8 +57,9 @@ def get_students_list(students):
|
|||||||
return student_list
|
return student_list
|
||||||
|
|
||||||
def get_attendance_list(from_date, to_date, student_group, students_list):
|
def get_attendance_list(from_date, to_date, student_group, students_list):
|
||||||
attendance_list = frappe.db.sql('''select student, date, status
|
attendance_list = frappe.db.sql('''select student, date, status
|
||||||
from `tabStudent Attendance` where student_group = %s
|
from `tabStudent Attendance` where student_group = %s
|
||||||
|
and docstatus = 1
|
||||||
and date between %s and %s
|
and date between %s and %s
|
||||||
order by student, date''',
|
order by student, date''',
|
||||||
(student_group, from_date, to_date), as_dict=1)
|
(student_group, from_date, to_date), as_dict=1)
|
||||||
@ -75,10 +76,10 @@ def get_attendance_list(from_date, to_date, student_group, students_list):
|
|||||||
def get_students_with_leave_application(from_date, to_date, students_list):
|
def get_students_with_leave_application(from_date, to_date, students_list):
|
||||||
if not students_list: return
|
if not students_list: return
|
||||||
leave_applications = frappe.db.sql("""
|
leave_applications = frappe.db.sql("""
|
||||||
select student, from_date, to_date
|
select student, from_date, to_date
|
||||||
from `tabStudent Leave Application`
|
from `tabStudent Leave Application`
|
||||||
where
|
where
|
||||||
mark_as_present and docstatus = 1
|
mark_as_present = 1 and docstatus = 1
|
||||||
and student in %(students)s
|
and student in %(students)s
|
||||||
and (
|
and (
|
||||||
from_date between %(from_date)s and %(to_date)s
|
from_date between %(from_date)s and %(to_date)s
|
||||||
|
Loading…
x
Reference in New Issue
Block a user