diff --git a/__manifest__.py b/__manifest__.py index 26e1899..0871508 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -1,7 +1,7 @@ { 'name': 'School_Management', 'version': '1.0', - 'depends': ['base', 'web', 'mail','product','hr'], + 'depends': ['base', 'web', 'mail', 'product', 'hr', 'hr_attendance'], 'license': 'LGPL-3', 'data': [ 'security/security.xml', @@ -23,8 +23,10 @@ 'views/school_reporting_views.xml', 'views/school_student_views.xml', 'views/fee_structure_views.xml', - 'views/fee_element_views.xml', - 'views/menu.xml', + 'views/fee_element_views.xml', + 'views/gradesheet.xml', + 'views/menu.xml', + ], 'installable': True, 'application': True, diff --git a/models/__init__.py b/models/__init__.py index f65f0e8..2ac231a 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -19,6 +19,8 @@ from . import school_student from . import fee_structure from . import fee_element from . import school_fee_structure_line +from . import gradesheet +from . import gradesheet_line diff --git a/models/__pycache__/__init__.cpython-310.pyc b/models/__pycache__/__init__.cpython-310.pyc index eecf7aa..3ac9522 100644 Binary files a/models/__pycache__/__init__.cpython-310.pyc and b/models/__pycache__/__init__.cpython-310.pyc differ diff --git a/models/__pycache__/enrollment.cpython-310.pyc b/models/__pycache__/enrollment.cpython-310.pyc index de63302..d75d331 100644 Binary files a/models/__pycache__/enrollment.cpython-310.pyc and b/models/__pycache__/enrollment.cpython-310.pyc differ diff --git a/models/__pycache__/gradesheet.cpython-310.pyc b/models/__pycache__/gradesheet.cpython-310.pyc new file mode 100644 index 0000000..ae59369 Binary files /dev/null and b/models/__pycache__/gradesheet.cpython-310.pyc differ diff --git a/models/__pycache__/gradesheet_line.cpython-310.pyc b/models/__pycache__/gradesheet_line.cpython-310.pyc new file mode 100644 index 0000000..5573f11 Binary files /dev/null and b/models/__pycache__/gradesheet_line.cpython-310.pyc differ diff --git a/models/__pycache__/schedule.cpython-310.pyc b/models/__pycache__/schedule.cpython-310.pyc index 26e5b4c..96c10f0 100644 Binary files a/models/__pycache__/schedule.cpython-310.pyc and b/models/__pycache__/schedule.cpython-310.pyc differ diff --git a/models/__pycache__/school_class.cpython-310.pyc b/models/__pycache__/school_class.cpython-310.pyc index 81cf391..4c5681e 100644 Binary files a/models/__pycache__/school_class.cpython-310.pyc and b/models/__pycache__/school_class.cpython-310.pyc differ diff --git a/models/__pycache__/school_course_schedule.cpython-310.pyc b/models/__pycache__/school_course_schedule.cpython-310.pyc index 524770f..0c2f428 100644 Binary files a/models/__pycache__/school_course_schedule.cpython-310.pyc and b/models/__pycache__/school_course_schedule.cpython-310.pyc differ diff --git a/models/__pycache__/school_misc_menus.cpython-310.pyc b/models/__pycache__/school_misc_menus.cpython-310.pyc index b8dd894..cd0f4a9 100644 Binary files a/models/__pycache__/school_misc_menus.cpython-310.pyc and b/models/__pycache__/school_misc_menus.cpython-310.pyc differ diff --git a/models/__pycache__/school_student.cpython-310.pyc b/models/__pycache__/school_student.cpython-310.pyc index c8c2892..96a1148 100644 Binary files a/models/__pycache__/school_student.cpython-310.pyc and b/models/__pycache__/school_student.cpython-310.pyc differ diff --git a/models/__pycache__/school_subject.cpython-310.pyc b/models/__pycache__/school_subject.cpython-310.pyc index 50eae0c..5b386ef 100644 Binary files a/models/__pycache__/school_subject.cpython-310.pyc and b/models/__pycache__/school_subject.cpython-310.pyc differ diff --git a/models/__pycache__/school_subject_teacher_info.cpython-310.pyc b/models/__pycache__/school_subject_teacher_info.cpython-310.pyc index 38749b9..9dd99bd 100644 Binary files a/models/__pycache__/school_subject_teacher_info.cpython-310.pyc and b/models/__pycache__/school_subject_teacher_info.cpython-310.pyc differ diff --git a/models/__pycache__/teacher_attendance.cpython-310.pyc b/models/__pycache__/teacher_attendance.cpython-310.pyc index 7f5b1fa..0950e54 100644 Binary files a/models/__pycache__/teacher_attendance.cpython-310.pyc and b/models/__pycache__/teacher_attendance.cpython-310.pyc differ diff --git a/models/gradesheet.py b/models/gradesheet.py new file mode 100644 index 0000000..f403734 --- /dev/null +++ b/models/gradesheet.py @@ -0,0 +1,242 @@ +from odoo import models, fields, api +from odoo.exceptions import ValidationError, UserError + +class SchoolGradesheet(models.Model): + _name = 'school.gradesheet' + _description = 'Student Gradesheet Report' + _order = 'session, class_name, course_id' + _inherit = ['mail.thread', 'mail.activity.mixin'] + + # Filter Fields + session = fields.Char(string="Session", required=True, help="Academic Session (e.g., 2024-2025)") + # application_id = fields.Many2one('school.application', string="Application", required=True) + # student_id = fields.Many2one('res.partner', string='Student', required=True) + # roll_number = fields.Char(string="Roll Number") + obtained_marks = fields.Float( + string="Obtained Marks", + default=0.0 + ) + total_marks = fields.Float( + string="Total Marks", + default=100.0 + ) + percentage = fields.Float( + string="Percentage (%)", + compute="_compute_percentage", + store=True + ) + final_grade = fields.Selection([ + ('A+', 'A+ (90-100%)'), + ('A', 'A (80-89%)'), + ('B+', 'B+ (70-79%)'), + ('B', 'B (60-69%)'), + ('C', 'C (50-59%)'), + ('D', 'D (40-49%)'), + ('F', 'F (Below 40%)') + ], string="Grade", default='F') + + status = fields.Selection([ + ('pass', 'Pass'), + ('fail', 'Fail') + ], string="Status", default='fail') + + remarks = fields.Text(string="Remarks") + class_name = fields.Selection([ + ('1', 'Class 1'), ('2', 'Class 2'), ('3', 'Class 3'), + ('4', 'Class 4'), ('5', 'Class 5'), ('6', 'Class 6'), + ('7', 'Class 7'), ('8', 'Class 8'), ('9', 'Class 9'), + ('10', 'Class 10'), ('11', 'Class 11'), ('12', 'Class 12'), + ], string="Class", required=True) + + course_id = fields.Many2one('school.course', string="Course", required=True) + subject_id = fields.Many2one('school.subject', string="Subject") + + # Report Generation Status + state = fields.Selection([ + ('draft', 'Draft'), + ('generated', 'Generated'), + ('finalized', 'Finalized') + ], string="Status", default='draft', tracking=True) + + # Generated Data + student_grade_ids = fields.One2many( + 'school.gradesheet.line', 'gradesheet_id', string="Student Grades" + ) + + # Report Info + generated_date = fields.Datetime(string="Generated Date", readonly=True) + generated_by = fields.Many2one('res.users', string="Generated By", readonly=True) + total_students = fields.Integer(string="Total Students", compute="_compute_student_stats", store=True) + passed_students = fields.Integer(string="Passed Students", compute="_compute_student_stats", store=True) + failed_students = fields.Integer(string="Failed Students", compute="_compute_student_stats", store=True) + pass_percentage = fields.Float(string="Pass Percentage", compute="_compute_student_stats", store=True) + + @api.depends('student_grade_ids', 'student_grade_ids.final_grade', 'student_grade_ids.status') + def _compute_student_stats(self): + for record in self: + total = len(record.student_grade_ids) + passed = len(record.student_grade_ids.filtered(lambda x: x.status == 'pass')) + failed = total - passed + + record.total_students = total + record.passed_students = passed + record.failed_students = failed + record.pass_percentage = (passed / total * 100) if total > 0 else 0.0 + + @api.onchange('class_name') + def _onchange_class_name(self): + if self.class_name: + return { + 'domain': { + 'course_id': [('class_id.name', '=', f'Class {self.class_name}')], + 'subject_id': [('class_name', '=', f'Class {self.class_name}')], #Add this + } + } + return { + 'domain': { + 'course_id': [], + 'subject_id': [], + } + } + + def action_fetch_student_marks(self): + """Fetch student marks and generate gradesheet""" + self.ensure_one() + + if not all([self.session, self.class_name, self.course_id]): + raise UserError("Please fill all required fields: Session, Class, and Course.") + + # Clear existing lines + self.student_grade_ids.unlink() + + # Get enrolled students for the course + enrollments = self.env['school.enrollment'].search([ + ('course_id', '=', self.course_id.id), + ('class_name', '=', self.class_name), + ('session', '=', self.session), + ('status', '=', 'confirmed') + ]) + + if not enrollments: + raise UserError(f"No enrolled students found for Class {self.class_name}, Course {self.course_id.name}, Session {self.session}") + + grade_lines = [] + for enrollment in enrollments: + # Get student marks (you may need to adapt this based on your marks/exam model) + student_marks = self._get_student_marks(enrollment) + + grade_lines.append({ + 'gradesheet_id': self.id, + 'student_id': enrollment.student_id.id, + 'enrollment_id': enrollment.id, + 'student_name': enrollment.student_id.name, + 'roll_number': enrollment.application_id.roll_no if enrollment.application_id else '', + 'obtained_marks': student_marks.get('obtained_marks', 0), + 'total_marks': student_marks.get('total_marks', 100), + 'percentage': student_marks.get('percentage', 0), + 'final_grade': student_marks.get('grade', 'F'), + 'status': student_marks.get('status', 'fail'), + 'remarks': student_marks.get('remarks', ''), + 'subject_id': self.subject_id.id, + }) + + # Create grade lines + self.env['school.gradesheet.line'].create(grade_lines) + + # Update gradesheet status + self.write({ + 'state': 'generated', + 'generated_date': fields.Datetime.now(), + 'generated_by': self.env.user.id, + }) + + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'message': f'Gradesheet generated successfully! Found {len(grade_lines)} students.', + 'type': 'success', + 'sticky': False, + } + } + + def _get_student_marks(self, enrollment): + """ + Get student marks from your existing marks/exam system + This is a placeholder - adapt according to your actual marks model + """ + # Example: If you have a student.marks model or exam.result model + # marks_record = self.env['student.marks'].search([ + # ('student_id', '=', enrollment.student_id.id), + # ('course_id', '=', self.course_id.id), + # ('session', '=', self.session) + # ], limit=1) + + # For demo purposes, generating sample marks + # Replace this with actual marks fetching logic + import random + obtained = random.randint(30, 95) + total = 100 + percentage = (obtained / total) * 100 + + # Grade calculation + if percentage >= 90: + grade = 'A+' + status = 'pass' + elif percentage >= 80: + grade = 'A' + status = 'pass' + elif percentage >= 70: + grade = 'B+' + status = 'pass' + elif percentage >= 60: + grade = 'B' + status = 'pass' + elif percentage >= 50: + grade = 'C' + status = 'pass' + elif percentage >= 40: + grade = 'D' + status = 'pass' + else: + grade = 'F' + status = 'fail' + + return { + 'obtained_marks': obtained, + 'total_marks': total, + 'percentage': percentage, + 'grade': grade, + 'status': status, + 'remarks': 'Good' if status == 'pass' else 'Needs Improvement' + } + + def action_finalize_gradesheet(self): + """Finalize the gradesheet""" + self.ensure_one() + if self.state != 'generated': + raise UserError("Please generate the gradesheet first.") + + self.state = 'finalized' + return True + + def action_reset_to_draft(self): + """Reset gradesheet to draft""" + self.ensure_one() + self.student_grade_ids.unlink() + self.write({ + 'state': 'draft', + 'generated_date': False, + 'generated_by': False, + }) + return True + + def action_print_gradesheet(self): + """Print gradesheet report""" + self.ensure_one() + if not self.student_grade_ids: + raise UserError("No student data found. Please fetch student marks first.") + + return self.env.ref('school_management.report_school_gradesheet').report_action(self) + + diff --git a/models/gradesheet_line.py b/models/gradesheet_line.py new file mode 100644 index 0000000..10eb472 --- /dev/null +++ b/models/gradesheet_line.py @@ -0,0 +1,103 @@ +from odoo import models, fields, api +from odoo.exceptions import ValidationError + +class SchoolGradesheetLine(models.Model): + _name = 'school.gradesheet.line' + _description = 'Student Grade Line' + _order = 'student_name' + + gradesheet_id = fields.Many2one('school.gradesheet', string="Gradesheet", ondelete="cascade") + application_id = fields.Many2one('school.application', string="Application", required=True) + student_id = fields.Many2one('school.application', string="Student", required=True) + student_name = fields.Char(related='student_id.name', string="Student Name", store=True) + roll_number = fields.Char(related='student_id.roll_no', string="Roll Number", store=True) + subject_id = fields.Many2one('school.subject', string="Subject") + + + enrollment_id = fields.Many2one( + 'school.enrollment', + string="Enrollment" + ) + + obtained_marks = fields.Float( + string="Obtained Marks", + default=0.0 + ) + total_marks = fields.Float( + string="Total Marks", + default=100.0 + ) + percentage = fields.Float( + string="Percentage (%)", + compute="_compute_percentage", + store=True + ) + + # Grade Info + final_grade = fields.Selection([ + ('A+', 'A+ (90-100%)'), + ('A', 'A (80-89%)'), + ('B+', 'B+ (70-79%)'), + ('B', 'B (60-69%)'), + ('C', 'C (50-59%)'), + ('D', 'D (40-49%)'), + ('F', 'F (Below 40%)') + ], string="Grade", default='F') + + status = fields.Selection([ + ('pass', 'Pass'), + ('fail', 'Fail') + ], string="Status", default='fail') + + remarks = fields.Text(string="Remarks") + + # Compute percentage + @api.depends('obtained_marks', 'total_marks') + def _compute_percentage(self): + for record in self: + if record.total_marks > 0: + record.percentage = (record.obtained_marks / record.total_marks) * 100 + else: + record.percentage = 0.0 + + # Auto-update grade and status on marks change + @api.onchange('obtained_marks', 'total_marks') + def _onchange_marks(self): + for record in self: + if record.total_marks > 0: + percentage = (record.obtained_marks / record.total_marks) * 100 + + if percentage >= 90: + record.final_grade = 'A+' + record.status = 'pass' + elif percentage >= 80: + record.final_grade = 'A' + record.status = 'pass' + elif percentage >= 70: + record.final_grade = 'B+' + record.status = 'pass' + elif percentage >= 60: + record.final_grade = 'B' + record.status = 'pass' + elif percentage >= 50: + record.final_grade = 'C' + record.status = 'pass' + elif percentage >= 40: + record.final_grade = 'D' + record.status = 'pass' + else: + record.final_grade = 'F' + record.status = 'fail' + else: + record.percentage = 0.0 + record.final_grade = 'F' + record.status = 'fail' + + # Validation to prevent invalid marks + @api.constrains('obtained_marks', 'total_marks') + def _check_marks(self): + for record in self: + if record.obtained_marks < 0 or record.total_marks < 0: + raise ValidationError("Marks cannot be negative.") + if record.obtained_marks > record.total_marks: + raise ValidationError("Obtained marks cannot exceed total marks.") diff --git a/models/school_course_schedule.py b/models/school_course_schedule.py index 2f3154e..e8fa7e3 100644 --- a/models/school_course_schedule.py +++ b/models/school_course_schedule.py @@ -1,6 +1,6 @@ -from odoo import models, fields +# from odoo import models, fields -class SchoolCourseSchedule(models.Model): - _inherit = 'hr.leave' # not 'school.course.schedule' +# class SchoolCourseSchedule(models.Model): +# _inherit = 'hr.leave' # not 'school.course.schedule' \ No newline at end of file diff --git a/models/school_misc_menus.py b/models/school_misc_menus.py index bbcce01..4dfde6c 100644 --- a/models/school_misc_menus.py +++ b/models/school_misc_menus.py @@ -1,4 +1,5 @@ -from odoo import models, fields +from odoo import models, fields, api +from odoo.exceptions import ValidationError class SchoolNotice(models.Model): _name = 'school.notice.board' @@ -22,8 +23,6 @@ class SchoolReportDummy(models.Model): name = fields.Char("Report Name") date_generated = fields.Date("Generated On", default=fields.Date.today) - - class SchoolFeesReport(models.Model): _name = 'school.fees.report' _description = 'Fees Report' @@ -31,12 +30,140 @@ class SchoolFeesReport(models.Model): name = fields.Char("Report Name") amount = fields.Float("Amount") -class SchoolTranscriptReport(models.Model): +class TranscriptReport(models.Model): _name = 'school.transcript.report' _description = 'Transcript Report' - student_id = fields.Many2one('school.application', string="Student", ondelete='set null') - grade = fields.Char("Grade") + name = fields.Char(string="Report Name", default="Transcript Report", readonly=True) + session = fields.Selection([ + ('2023-24', '2023-24'), + ('2024-25', '2024-25'), + ('2025-26', '2025-26'), + ('2026-27', '2026-27'), + ], string="Session", required=True) + + student_id = fields.Many2one('school.application', string="Student", required=True) + + # Student Information Fields (computed from student_id) + student_name = fields.Char(related='student_id.name', string="Student Name", readonly=True) + student_address = fields.Text(related='student_id.address', string="Address", readonly=True) + student_phone = fields.Char(related='student_id.phone_no', string="Phone", readonly=True) + student_email = fields.Char(related='student_id.email', string="Email", readonly=True) + student_dob = fields.Date(related='student_id.date_of_birth', string="Date of Birth", readonly=True) + student_guardian = fields.Char(related='student_id.father_name', string="Guardian", readonly=True) + student_academic_year = fields.Char(related='student_id.academic_year', string="Academic Year", readonly=True) + student_class = fields.Selection(related='student_id.class_name', string="Class", readonly=True) + + # School Information Fields + school_id = fields.Many2one('res.company', string="School", default=lambda self: self.env.company, readonly=True) + school_name = fields.Char(related='school_id.name', string="School Name", readonly=True) + school_address = fields.Text(string="School Address", compute='_compute_school_address', readonly=True) + school_state = fields.Char(related='school_id.state_id.name', string="State", readonly=True) + school_phone = fields.Char(related='school_id.phone', string="School Phone", readonly=True) + school_email = fields.Char(related='school_id.email', string="School Email", readonly=True) + school_academic_year = fields.Char(string="School Academic Year", default="2024-25", readonly=True) + school_classes = fields.Char(string="Classes", default="Class 1-12", readonly=True) + + # Subject Information + subject_ids = fields.Many2many('school.subject', string="Subjects", compute='_compute_subjects', readonly=True) + subject_names = fields.Char(string="Subject Names", compute='_compute_subject_names', readonly=True) + + # Report Status + report_generated = fields.Boolean(string="Report Generated", default=False) + generated_date = fields.Datetime(string="Generated Date") + + @api.depends('school_id') + def _compute_school_address(self): + for record in self: + if record.school_id: + address_parts = [] + if record.school_id.street: + address_parts.append(record.school_id.street) + if record.school_id.street2: + address_parts.append(record.school_id.street2) + if record.school_id.city: + address_parts.append(record.school_id.city) + if record.school_id.zip: + address_parts.append(record.school_id.zip) + record.school_address = ', '.join(address_parts) + else: + record.school_address = '' + + @api.depends('student_id') + def _compute_subjects(self): + for record in self: + if record.student_id and record.student_id.class_name: + # Get subjects for the student's class + subjects = self.env['school.subject'].search([ + ('class_name', '=', 'Class ' + record.student_id.class_name) + ]) + record.subject_ids = subjects + else: + record.subject_ids = False + + @api.depends('subject_ids') + def _compute_subject_names(self): + for record in self: + if record.subject_ids: + subject_names = [] + for subject in record.subject_ids: + # Get the display name for the subject + subject_display = dict(subject._fields['name'].selection).get(subject.name, subject.name) + subject_names.append(subject_display) + record.subject_names = ', '.join(subject_names) + else: + record.subject_names = '' + + def action_fetch_report(self): + """Fetch and prepare the report data""" + self.ensure_one() + if not self.student_id: + raise ValidationError("Please select a student first.") + if not self.session: + raise ValidationError("Please select a session first.") + + # Mark report as generated + self.report_generated = True + self.generated_date = fields.Datetime.now() + + # You can add more logic here to fetch additional data + # For example: get grades, attendance, etc. + + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'type': 'success', + 'message': 'Report data fetched successfully!', + 'next': {'type': 'ir.actions.act_window_close'}, + } + } + + def action_download_report(self): + """Download the transcript report""" + self.ensure_one() + if not self.report_generated: + raise ValidationError("Please fetch the report first before downloading.") + + # Here you would typically call a report action + # For now, we'll show a notification + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'type': 'success', + 'message': 'Report download functionality will be implemented with PDF generation.', + 'sticky': False, + } + } + + @api.model + def create(self, vals): + # Auto-generate report name with session and student info + if 'student_id' in vals and 'session' in vals: + student = self.env['school.application'].browse(vals['student_id']) + vals['name'] = f"Transcript Report - {student.name} - {vals['session']}" + return super().create(vals) class SchoolScholarshipReport(models.Model): _name = 'school.scholarship.report' @@ -59,4 +186,4 @@ class SchoolConfigSettings(models.Model): _description = 'School Configuration Settings' key = fields.Char("Setting Key", required=True) - value = fields.Char("Setting Value") + value = fields.Char("Setting Value") \ No newline at end of file diff --git a/models/school_student.py b/models/school_student.py index 4785084..0d63c11 100644 --- a/models/school_student.py +++ b/models/school_student.py @@ -34,7 +34,7 @@ class SchoolStudent(models.Model): 'type': 'ir.actions.act_window', 'name': 'Fee Slip', 'res_model': 'school.enrollment.fee.summary', - 'view_mode': 'tree,form', + 'view_mode': 'list,form', 'domain': [('student_ref', '=', self.id)], 'target': 'current', } diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv index 4e2cb33..dfbd2e1 100644 --- a/security/ir.model.access.csv +++ b/security/ir.model.access.csv @@ -4,7 +4,7 @@ access_school_application,School Application,model_school_application,,1,1,1,1 access_school_enrollment,School Enrollment,model_school_enrollment,,1,1,1,1 access_school_enrollment_subject,School Enrollment Subject,model_school_enrollment_subject,,1,1,1,1 access_school_enrollment_fee_summary,School Enrollment Fee Summary,model_school_enrollment_fee_summary,,1,1,1,1 -access_school_class_schedule,School Class Schedule,model_school_class_schedule,,1,1,1,1 +access_school_class_schedule,School Class Schedule,model_school_class_schedule,base.group_user,1,1,1,1 access_school_class,School Class,model_school_class,,1,1,1,1 access_school_subject,School Subject,model_school_subject,,1,1,1,1 access_school_course,School Course,model_school_course,,1,1,1,1 @@ -23,5 +23,7 @@ access_school_report_card,Report Card,model_school_report_card,,1,1,1,1 access_school_student,Student,model_school_student,,1,1,1,1 access_school_fee_structure,School Fee Structure,model_school_fee_structure,,1,1,1,1 access_school_fee_element,School Fee Element,model_school_fee_element,,1,1,1,1 -access_school_fee_component,School Fee Component,model_school_fee_component,,1,0,0,0 +access_school_fee_component,School Fee Component,model_school_fee_component,,1,1,1,1 +access_school_gradesheet,School Gradesheet,model_school_gradesheet,,1,1,1,1 +access_school_gradesheet_line,School Gradesheet Line,model_school_gradesheet_line,,1,1,1,1 diff --git a/views/gradesheet.xml b/views/gradesheet.xml new file mode 100644 index 0000000..3231412 --- /dev/null +++ b/views/gradesheet.xml @@ -0,0 +1,394 @@ + + + + + + school.gradesheet.list + school.gradesheet + + + + + + + + + + + + + + + + + + school.gradesheet.form + school.gradesheet + +
+
+
+ + + + + {"readonly": [["state", "!=", "draft"]]} + + + {"readonly": [["state", "!=", "draft"]]} + + + {"readonly": [["state", "!=", "draft"]]} + + + + + {"invisible": [["state", "==", "draft"]]} + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + + school.gradesheet.search + school.gradesheet + + + + + + + + + + + + + + + + + + + + + + + + + + + school.gradesheet.line.list + school.gradesheet.line + + + + + + + + + + + + + + + + + school.gradesheet.kanban + school.gradesheet + + + + + + + + + + + + + +
+
+
+
+ + + + +
+
+
+
+ Class +
+
+
+
+ +
+
+
+
+ Students: +
+
+ Passed: +
+
+
+
+
+
+ % +
+
+
+
+
+
+
+
+
+
+
+ + + + school.gradesheet.pivot + school.gradesheet + + + + + + + + + + + + + + + school.gradesheet.graph + school.gradesheet + + + + + + + + + + + + Gradesheets + school.gradesheet + kanban,list,form,pivot,graph + {'search_default_filter_current_session': 1} + +

+ Create your first Gradesheet! +

+

+ Generate gradesheets for students by selecting session, class, and course. + Fetch student marks automatically from your existing data. +

+
+
+ + + + Student Grades + school.gradesheet.line + list + + + + + Gradesheet Report + school.gradesheet + qweb-pdf + school_management.report_gradesheet_template + school_management.report_gradesheet_template + + report + + + + + + +
\ No newline at end of file diff --git a/views/menu.xml b/views/menu.xml index 4ccad7b..4630ad3 100644 --- a/views/menu.xml +++ b/views/menu.xml @@ -103,7 +103,7 @@ @@ -119,7 +119,13 @@ parent="menu_school_reporting" action="action_school_report_card" sequence="5"/> - + + + @@ -11,6 +12,7 @@ + school.fees.report.form school.fees.report @@ -26,48 +28,123 @@ - Fees Report school.fees.report list,form - school.transcript.report.list school.transcript.report - + + - + + + + + school.transcript.report.form school.transcript.report
+
+
- - +

Transcript Report Generator

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
- - + Transcript Report school.transcript.report list,form + +

+ Create a transcript report +

+

+ Select a session and student to generate transcript reports. +

+
- school.scholarship.report.list @@ -80,6 +157,7 @@ + school.scholarship.report.form school.scholarship.report @@ -96,14 +174,12 @@ - Scholarship Report school.scholarship.report list,form - school.report.card.list @@ -116,6 +192,7 @@ + school.report.card.form school.report.card @@ -132,11 +209,10 @@ - Report Card school.report.card list,form -
+ \ No newline at end of file