added transcript report and gradesheet and some requirement changes

This commit is contained in:
Truptimayee Dash 2025-08-11 13:15:18 +05:30
parent d672e9c566
commit 5d064a313c
23 changed files with 985 additions and 31 deletions

View File

@ -1,7 +1,7 @@
{ {
'name': 'School_Management', 'name': 'School_Management',
'version': '1.0', 'version': '1.0',
'depends': ['base', 'web', 'mail','product','hr'], 'depends': ['base', 'web', 'mail', 'product', 'hr', 'hr_attendance'],
'license': 'LGPL-3', 'license': 'LGPL-3',
'data': [ 'data': [
'security/security.xml', 'security/security.xml',
@ -24,7 +24,9 @@
'views/school_student_views.xml', 'views/school_student_views.xml',
'views/fee_structure_views.xml', 'views/fee_structure_views.xml',
'views/fee_element_views.xml', 'views/fee_element_views.xml',
'views/gradesheet.xml',
'views/menu.xml', 'views/menu.xml',
], ],
'installable': True, 'installable': True,
'application': True, 'application': True,

View File

@ -19,6 +19,8 @@ from . import school_student
from . import fee_structure from . import fee_structure
from . import fee_element from . import fee_element
from . import school_fee_structure_line from . import school_fee_structure_line
from . import gradesheet
from . import gradesheet_line

Binary file not shown.

Binary file not shown.

242
models/gradesheet.py Normal file
View File

@ -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)

103
models/gradesheet_line.py Normal file
View File

@ -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.")

View File

@ -1,6 +1,6 @@
from odoo import models, fields # from odoo import models, fields
class SchoolCourseSchedule(models.Model): # class SchoolCourseSchedule(models.Model):
_inherit = 'hr.leave' # not 'school.course.schedule' # _inherit = 'hr.leave' # not 'school.course.schedule'

View File

@ -1,4 +1,5 @@
from odoo import models, fields from odoo import models, fields, api
from odoo.exceptions import ValidationError
class SchoolNotice(models.Model): class SchoolNotice(models.Model):
_name = 'school.notice.board' _name = 'school.notice.board'
@ -22,8 +23,6 @@ class SchoolReportDummy(models.Model):
name = fields.Char("Report Name") name = fields.Char("Report Name")
date_generated = fields.Date("Generated On", default=fields.Date.today) date_generated = fields.Date("Generated On", default=fields.Date.today)
class SchoolFeesReport(models.Model): class SchoolFeesReport(models.Model):
_name = 'school.fees.report' _name = 'school.fees.report'
_description = 'Fees Report' _description = 'Fees Report'
@ -31,12 +30,140 @@ class SchoolFeesReport(models.Model):
name = fields.Char("Report Name") name = fields.Char("Report Name")
amount = fields.Float("Amount") amount = fields.Float("Amount")
class SchoolTranscriptReport(models.Model): class TranscriptReport(models.Model):
_name = 'school.transcript.report' _name = 'school.transcript.report'
_description = 'Transcript Report' _description = 'Transcript Report'
student_id = fields.Many2one('school.application', string="Student", ondelete='set null') name = fields.Char(string="Report Name", default="Transcript Report", readonly=True)
grade = fields.Char("Grade") 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): class SchoolScholarshipReport(models.Model):
_name = 'school.scholarship.report' _name = 'school.scholarship.report'

View File

@ -34,7 +34,7 @@ class SchoolStudent(models.Model):
'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',
'name': 'Fee Slip', 'name': 'Fee Slip',
'res_model': 'school.enrollment.fee.summary', 'res_model': 'school.enrollment.fee.summary',
'view_mode': 'tree,form', 'view_mode': 'list,form',
'domain': [('student_ref', '=', self.id)], 'domain': [('student_ref', '=', self.id)],
'target': 'current', 'target': 'current',
} }

View File

@ -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,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_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_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_class,School Class,model_school_class,,1,1,1,1
access_school_subject,School Subject,model_school_subject,,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 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_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_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_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

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
4 access_school_enrollment School Enrollment model_school_enrollment 1 1 1 1
5 access_school_enrollment_subject School Enrollment Subject model_school_enrollment_subject 1 1 1 1
6 access_school_enrollment_fee_summary School Enrollment Fee Summary model_school_enrollment_fee_summary 1 1 1 1
7 access_school_class_schedule School Class Schedule model_school_class_schedule base.group_user 1 1 1 1
8 access_school_class School Class model_school_class 1 1 1 1
9 access_school_subject School Subject model_school_subject 1 1 1 1
10 access_school_course School Course model_school_course 1 1 1 1
23 access_school_student Student model_school_student 1 1 1 1
24 access_school_fee_structure School Fee Structure model_school_fee_structure 1 1 1 1
25 access_school_fee_element School Fee Element model_school_fee_element 1 1 1 1
26 access_school_fee_component School Fee Component model_school_fee_component 1 0 1 0 1 0 1
27 access_school_gradesheet School Gradesheet model_school_gradesheet 1 1 1 1
28 access_school_gradesheet_line School Gradesheet Line model_school_gradesheet_line 1 1 1 1
29

394
views/gradesheet.xml Normal file
View File

@ -0,0 +1,394 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!-- Gradesheet List View -->
<record id="view_school_gradesheet_list" model="ir.ui.view">
<field name="name">school.gradesheet.list</field>
<field name="model">school.gradesheet</field>
<field name="arch" type="xml">
<list string="Gradesheets" decoration-info="state=='draft'" decoration-success="state=='generated'" decoration-muted="state=='finalized'">
<field name="session"/>
<field name="class_name"/>
<field name="course_id"/>
<field name="total_students"/>
<field name="passed_students"/>
<field name="pass_percentage" widget="percentage"/>
<field name="state" widget="badge" decoration-info="state=='draft'" decoration-success="state=='generated'" decoration-muted="state=='finalized'"/>
<field name="generated_date"/>
<field name="generated_by"/>
</list>
</field>
</record>
<!-- Gradesheet Form View -->
<record id="view_school_gradesheet_form" model="ir.ui.view">
<field name="name">school.gradesheet.form</field>
<field name="model">school.gradesheet</field>
<field name="arch" type="xml">
<form string="Gradesheet">
<header>
<button name="action_fetch_student_marks"
string="Fetch Marks"
type="object"
class="btn-primary"
modifiers='{"invisible": [["state", "!=", "draft"]]}'/>
<button name="action_finalize_gradesheet"
string="Finalize"
type="object"
class="btn-success"
modifiers='{"invisible": [["state", "!=", "generated"]]}'/>
<button name="action_print_gradesheet"
string="Print"
type="object"
class="btn-secondary"
modifiers='{"invisible": [["state", "==", "draft"]]}'/>
<button name="action_reset_to_draft"
string="Reset to Draft"
type="object"
class="btn-warning"
confirm="This will reset the gradesheet. Continue?"
modifiers='{"invisible": [["state", "==", "draft"]]}'/>
<field name="state" widget="statusbar" statusbar_visible="draft,generated,finalized"/>
</header>
<sheet>
<group string="Filters">
<field name="session" placeholder="e.g., 2024-2025">
<modifiers>{"readonly": [["state", "!=", "draft"]]}</modifiers>
</field>
<field name="class_name">
<modifiers>{"readonly": [["state", "!=", "draft"]]}</modifiers>
</field>
<field name="course_id" context="{'default_class_name': class_name}">
<modifiers>{"readonly": [["state", "!=", "draft"]]}</modifiers>
</field>
</group>
<group string="Statistics">
<modifiers>{"invisible": [["state", "==", "draft"]]}</modifiers>
<field name="total_students"/>
<field name="passed_students"/>
<field name="failed_students"/>
<field name="pass_percentage" widget="percentage"/>
<field name="generated_date"/>
<field name="generated_by"/>
</group>
<notebook>
<page string="Student Grades">
<field name="student_grade_ids">
<list editable="bottom" decoration-success="status=='pass'" decoration-danger="status=='fail'">
<field name="student_id"/>
<field name="roll_number"/>
<field name="subject_id"/>
<field name="obtained_marks" sum="Total Obtained"/>
<field name="total_marks" sum="Total Max"/>
<field name="percentage" widget="percentage"/>
<field name="final_grade"/>
<field name="status" widget="badge"/>
<field name="remarks"/>
</list>
</field>
</page>
</notebook>
</sheet>
<!-- Chatter -->
<!-- <div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="activity_ids"/>
<field name="message_ids"/>
</div> -->
</form>
</field>
</record>
<!-- Gradesheet Search View -->
<record id="view_school_gradesheet_search" model="ir.ui.view">
<field name="name">school.gradesheet.search</field>
<field name="model">school.gradesheet</field>
<field name="arch" type="xml">
<search string="Search Gradesheets">
<field name="session"/>
<field name="class_name"/>
<field name="course_id"/>
<field name="generated_by"/>
<filter string="Draft" name="filter_draft" domain="[('state', '=', 'draft')]"/>
<filter string="Generated" name="filter_generated" domain="[('state', '=', 'generated')]"/>
<filter string="Finalized" name="filter_finalized" domain="[('state', '=', 'finalized')]"/>
<separator/>
<filter string="Current Session" name="filter_current_session" domain="[('session', 'ilike', '2024')]"/>
<group expand="0" string="Group By">
<filter string="Session" name="group_session" context="{'group_by': 'session'}"/>
<filter string="Class" name="group_class" context="{'group_by': 'class_name'}"/>
<filter string="Status" name="group_state" context="{'group_by': 'state'}"/>
<filter string="Generated By" name="group_generated_by" context="{'group_by': 'generated_by'}"/>
</group>
</search>
</field>
</record>
<!-- Gradesheet Line List View -->
<record id="view_school_gradesheet_line_list" model="ir.ui.view">
<field name="name">school.gradesheet.line.list</field>
<field name="model">school.gradesheet.line</field>
<field name="arch" type="xml">
<list string="Student Grades" decoration-success="status=='pass'" decoration-danger="status=='fail'">
<field name="student_name"/>
<field name="roll_number"/>
<field name="obtained_marks"/>
<field name="total_marks"/>
<field name="percentage" widget="percentage"/>
<field name="final_grade"/>
<field name="status" widget="badge" decoration-success="status=='pass'" decoration-danger="status=='fail'"/>
<field name="remarks"/>
</list>
</field>
</record>
<!-- Gradesheet Kanban View -->
<record id="view_school_gradesheet_kanban" model="ir.ui.view">
<field name="name">school.gradesheet.kanban</field>
<field name="model">school.gradesheet</field>
<field name="arch" type="xml">
<kanban class="o_kanban_small_column" default_group_by="state">
<field name="session"/>
<field name="class_name"/>
<field name="course_id"/>
<field name="total_students"/>
<field name="passed_students"/>
<field name="pass_percentage"/>
<field name="state"/>
<field name="generated_date"/>
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_card oe_kanban_global_click">
<div class="oe_kanban_content">
<div class="row">
<div class="col-12">
<strong><field name="session"/></strong>
<span class="float-right">
<field name="state" widget="label_selection" options="{'classes': {'draft': 'secondary', 'generated': 'success', 'finalized': 'info'}}"/>
</span>
</div>
</div>
<div class="row">
<div class="col-12">
<i class="fa fa-graduation-cap"/> Class <field name="class_name"/>
</div>
</div>
<div class="row">
<div class="col-12">
<i class="fa fa-book" title="Gradesheet"/> <field name="course_id"/>
</div>
</div>
<div class="row mt8">
<div class="col-6">
<small>Students: <field name="total_students"/></small>
</div>
<div class="col-6">
<small>Passed: <field name="passed_students"/></small>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="progress" style="height: 15px;">
<div class="progress-bar bg-success" role="progressbar" t-att-style="'width: ' + record.pass_percentage.raw_value + '%'">
<t t-esc="Math.round(record.pass_percentage.raw_value)"/>%
</div>
</div>
</div>
</div>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<!-- Gradesheet Pivot View -->
<record id="view_school_gradesheet_pivot" model="ir.ui.view">
<field name="name">school.gradesheet.pivot</field>
<field name="model">school.gradesheet</field>
<field name="arch" type="xml">
<pivot string="Gradesheet Analysis">
<field name="session" type="row"/>
<field name="class_name" type="row"/>
<field name="course_id" type="col"/>
<field name="total_students" type="measure"/>
<field name="passed_students" type="measure"/>
<field name="failed_students" type="measure"/>
</pivot>
</field>
</record>
<!-- Gradesheet Graph View -->
<record id="view_school_gradesheet_graph" model="ir.ui.view">
<field name="name">school.gradesheet.graph</field>
<field name="model">school.gradesheet</field>
<field name="arch" type="xml">
<graph string="Pass/Fail Analysis" type="bar">
<field name="class_name" type="row"/>
<field name="passed_students" type="measure"/>
<field name="failed_students" type="measure"/>
</graph>
</field>
</record>
<!-- Action for Gradesheet -->
<record id="action_school_gradesheet" model="ir.actions.act_window">
<field name="name">Gradesheets</field>
<field name="res_model">school.gradesheet</field>
<field name="view_mode">kanban,list,form,pivot,graph</field>
<field name="context">{'search_default_filter_current_session': 1}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create your first Gradesheet!
</p>
<p>
Generate gradesheets for students by selecting session, class, and course.
Fetch student marks automatically from your existing data.
</p>
</field>
</record>
<!-- Action for Gradesheet Lines -->
<record id="action_school_gradesheet_line" model="ir.actions.act_window">
<field name="name">Student Grades</field>
<field name="res_model">school.gradesheet.line</field>
<field name="view_mode">list</field>
</record>
<!-- Gradesheet Report Template -->
<record id="report_school_gradesheet" model="ir.actions.report">
<field name="name">Gradesheet Report</field>
<field name="model">school.gradesheet</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">school_management.report_gradesheet_template</field>
<field name="report_file">school_management.report_gradesheet_template</field>
<field name="binding_model_id" ref="model_school_gradesheet"/>
<field name="binding_type">report</field>
<field name="paperformat_id" ref="base.paperformat_euro"/>
</record>
<!-- Report Template -->
<template id="report_gradesheet_template">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="gradesheet">
<t t-call="web.external_layout">
<div class="page">
<!-- Header -->
<div class="text-center mb-4">
<h2>GRADESHEET REPORT</h2>
<h4 t-field="gradesheet.course_id.name"/>
<p>
<strong>Session:</strong> <span t-field="gradesheet.session"/> |
<strong>Class:</strong> <span t-field="gradesheet.class_name"/> |
<strong>Generated:</strong> <span t-field="gradesheet.generated_date" t-options="{'widget': 'datetime'}"/>
</p>
</div>
<!-- Statistics -->
<div class="row mb-4">
<div class="col-3">
<div class="card text-center">
<div class="card-body">
<h5 class="card-title" t-field="gradesheet.total_students"/>
<p class="card-text">Total Students</p>
</div>
</div>
</div>
<div class="col-3">
<div class="card text-center text-success">
<div class="card-body">
<h5 class="card-title" t-field="gradesheet.passed_students"/>
<p class="card-text">Passed</p>
</div>
</div>
</div>
<div class="col-3">
<div class="card text-center text-danger">
<div class="card-body">
<h5 class="card-title" t-field="gradesheet.failed_students"/>
<p class="card-text">Failed</p>
</div>
</div>
</div>
<div class="col-3">
<div class="card text-center text-info">
<div class="card-body">
<h5 class="card-title"><span t-field="gradesheet.pass_percentage" t-options="{'widget': 'float', 'precision': 2}"/>%</h5>
<p class="card-text">Pass Rate</p>
</div>
</div>
</div>
</div>
<!-- Student Grades Table -->
<table class="table table-bordered table-striped">
<thead class="table-dark">
<tr>
<th>S.No.</th>
<th>Student Name</th>
<th>Roll Number</th>
<th>Obtained Marks</th>
<th>Total Marks</th>
<th>Percentage</th>
<th>Grade</th>
<th>Status</th>
<th>Remarks</th>
</tr>
</thead>
<tbody>
<tr t-foreach="gradesheet.student_grade_ids" t-as="line"
t-att-class="'table-success' if line.status == 'pass' else 'table-danger'">
<td t-esc="line_index + 1"/>
<td t-field="line.student_name"/>
<td t-field="line.roll_number"/>
<td t-field="line.obtained_marks" t-options="{'widget': 'float', 'precision': 1}"/>
<td t-field="line.total_marks" t-options="{'widget': 'float', 'precision': 1}"/>
<td>
<span t-field="line.percentage" t-options="{'widget': 'float', 'precision': 2}"/>%
</td>
<td>
<span t-field="line.final_grade"
t-att-class="'badge badge-success' if line.status == 'pass' else 'badge badge-danger'"/>
</td>
<td>
<span t-field="line.status"
t-att-class="'text-success' if line.status == 'pass' else 'text-danger'"
style="text-transform: uppercase; font-weight: bold;"/>
</td>
<td t-field="line.remarks"/>
</tr>
</tbody>
</table>
<!-- Footer -->
<div class="mt-4">
<div class="row">
<div class="col-6">
<p><strong>Generated By:</strong> <span t-field="gradesheet.generated_by.name"/></p>
<p><strong>Date:</strong> <span t-field="gradesheet.generated_date" t-options="{'widget': 'date'}"/></p>
</div>
<div class="col-6 text-right">
<p><strong>Status:</strong> <span t-field="gradesheet.state" class="badge badge-info"/></p>
</div>
</div>
</div>
</div>
</t>
</t>
</t>
</template>
</odoo>

View File

@ -103,7 +103,7 @@
<menuitem id="menu_school_transcript_report" <menuitem id="menu_school_transcript_report"
name="Transcript Report" name="Transcript Report"
parent="menu_school_reporting" parent="menu_school_reporting"
action="action_school_transcript_report" action="action_transcript_report"
sequence="3"/> sequence="3"/>
<!-- 3. Scholarship Report --> <!-- 3. Scholarship Report -->
@ -120,6 +120,12 @@
action="action_school_report_card" action="action_school_report_card"
sequence="5"/> sequence="5"/>
<!-- 4. Grade Sheet -->
<menuitem id="menu_school_gradesheet"
name="Gradesheets"
parent="menu_school_reporting"
action="action_school_gradesheet"
sequence="6"/>
<!-- Configuration Menu --> <!-- Configuration Menu -->
<menuitem id="menu_school_configuration" <menuitem id="menu_school_configuration"

View File

@ -1,3 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo> <odoo>
<!-- === Action & Views: Fees Report === --> <!-- === Action & Views: Fees Report === -->
@ -11,6 +12,7 @@
</list> </list>
</field> </field>
</record> </record>
<record id="view_fees_report_form" model="ir.ui.view"> <record id="view_fees_report_form" model="ir.ui.view">
<field name="name">school.fees.report.form</field> <field name="name">school.fees.report.form</field>
<field name="model">school.fees.report</field> <field name="model">school.fees.report</field>
@ -26,48 +28,123 @@
</field> </field>
</record> </record>
<record id="action_school_fees_report" model="ir.actions.act_window"> <record id="action_school_fees_report" model="ir.actions.act_window">
<field name="name">Fees Report</field> <field name="name">Fees Report</field>
<field name="res_model">school.fees.report</field> <field name="res_model">school.fees.report</field>
<field name="view_mode">list,form</field> <field name="view_mode">list,form</field>
</record> </record>
<!-- === Action & Views: Transcript Report === --> <!-- === Action & Views: Transcript Report === -->
<record id="view_transcript_report_list" model="ir.ui.view"> <record id="view_transcript_report_list" model="ir.ui.view">
<field name="name">school.transcript.report.list</field> <field name="name">school.transcript.report.list</field>
<field name="model">school.transcript.report</field> <field name="model">school.transcript.report</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<list> <list string="Transcript Reports">
<field name="name"/>
<field name="student_id"/> <field name="student_id"/>
<field name="grade"/> <field name="session"/>
<field name="student_class"/>
<field name="report_generated"/>
<field name="generated_date"/>
</list> </list>
</field> </field>
</record> </record>
<record id="view_transcript_report_form" model="ir.ui.view"> <record id="view_transcript_report_form" model="ir.ui.view">
<field name="name">school.transcript.report.form</field> <field name="name">school.transcript.report.form</field>
<field name="model">school.transcript.report</field> <field name="model">school.transcript.report</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Transcript Report"> <form string="Transcript Report">
<header>
<button name="action_fetch_report"
string="Fetch Report"
type="object"
class="btn-primary"
invisible="report_generated"/>
<button name="action_download_report"
string="Download Report"
type="object"
class="btn-secondary"
invisible="not report_generated"/>
</header>
<sheet> <sheet>
<group> <group>
<field name="student_id"/> <h1>Transcript Report Generator</h1>
<field name="grade"/> </group>
<!-- Selection Fields -->
<group>
<group>
<field name="session" options="{'no_create': True}" placeholder="Select Session..."/>
<field name="student_id" options="{'no_create': True}" placeholder="Select Student..."/>
</group>
<group>
<field name="report_generated" invisible="1"/>
<field name="generated_date" readonly="1" widget="date"
modifiers="{'invisible': [['report_generated', '=', False]]}"/>
</group>
</group>
<!-- Report Content - Only visible after fetch -->
<group modifiers="{'invisible': [['report_generated', '=', False]]}">
<separator string="Report Details"/>
<!-- Student Information Card -->
<group string="Student Information">
<group>
<field name="student_name" readonly="1"/>
<field name="student_address" readonly="1"/>
<field name="student_phone" readonly="1"/>
<field name="student_email" readonly="1"/>
</group>
<group>
<field name="student_dob" readonly="1"/>
<field name="student_guardian" readonly="1"/>
<field name="student_academic_year" readonly="1"/>
<field name="student_class" readonly="1"/>
</group>
<group>
<field name="subject_names" readonly="1" string="Subjects" widget="text"/>
</group>
</group>
<!-- School Information Card -->
<group string="School Information">
<group>
<field name="school_name" readonly="1"/>
<field name="school_address" readonly="1"/>
<field name="school_state" readonly="1"/>
<field name="school_phone" readonly="1"/>
</group>
<group>
<field name="school_email" readonly="1"/>
<field name="school_academic_year" readonly="1"/>
<field name="school_classes" readonly="1"/>
</group>
</group>
</group> </group>
</sheet> </sheet>
</form> </form>
</field> </field>
</record> </record>
<record id="action_transcript_report" model="ir.actions.act_window">
<record id="action_school_transcript_report" model="ir.actions.act_window">
<field name="name">Transcript Report</field> <field name="name">Transcript Report</field>
<field name="res_model">school.transcript.report</field> <field name="res_model">school.transcript.report</field>
<field name="view_mode">list,form</field> <field name="view_mode">list,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a transcript report
</p>
<p>
Select a session and student to generate transcript reports.
</p>
</field>
</record> </record>
<!-- === Action & Views: Scholarship Report === --> <!-- === Action & Views: Scholarship Report === -->
<record id="view_scholarship_report_list" model="ir.ui.view"> <record id="view_scholarship_report_list" model="ir.ui.view">
<field name="name">school.scholarship.report.list</field> <field name="name">school.scholarship.report.list</field>
@ -80,6 +157,7 @@
</list> </list>
</field> </field>
</record> </record>
<record id="view_scholarship_report_form" model="ir.ui.view"> <record id="view_scholarship_report_form" model="ir.ui.view">
<field name="name">school.scholarship.report.form</field> <field name="name">school.scholarship.report.form</field>
<field name="model">school.scholarship.report</field> <field name="model">school.scholarship.report</field>
@ -96,14 +174,12 @@
</field> </field>
</record> </record>
<record id="action_school_scholarship_report" model="ir.actions.act_window"> <record id="action_school_scholarship_report" model="ir.actions.act_window">
<field name="name">Scholarship Report</field> <field name="name">Scholarship Report</field>
<field name="res_model">school.scholarship.report</field> <field name="res_model">school.scholarship.report</field>
<field name="view_mode">list,form</field> <field name="view_mode">list,form</field>
</record> </record>
<!-- === Action & Views: Report Card === --> <!-- === Action & Views: Report Card === -->
<record id="view_report_card_list" model="ir.ui.view"> <record id="view_report_card_list" model="ir.ui.view">
<field name="name">school.report.card.list</field> <field name="name">school.report.card.list</field>
@ -116,6 +192,7 @@
</list> </list>
</field> </field>
</record> </record>
<record id="view_report_card_form" model="ir.ui.view"> <record id="view_report_card_form" model="ir.ui.view">
<field name="name">school.report.card.form</field> <field name="name">school.report.card.form</field>
<field name="model">school.report.card</field> <field name="model">school.report.card</field>
@ -132,7 +209,6 @@
</field> </field>
</record> </record>
<record id="action_school_report_card" model="ir.actions.act_window"> <record id="action_school_report_card" model="ir.actions.act_window">
<field name="name">Report Card</field> <field name="name">Report Card</field>
<field name="res_model">school.report.card</field> <field name="res_model">school.report.card</field>