301 lines
13 KiB
Python
301 lines
13 KiB
Python
import base64
|
|
# pyrefly: ignore [missing-import]
|
|
from odoo import http
|
|
# pyrefly: ignore [missing-import]
|
|
from odoo.http import request
|
|
# pyrefly: ignore [missing-import]
|
|
from odoo.addons.portal.controllers.portal import CustomerPortal
|
|
|
|
|
|
class StudentPortalController(CustomerPortal):
|
|
|
|
def _get_student(self):
|
|
return request.env.user.student_id
|
|
|
|
def _prepare_home_portal_values(self, counters):
|
|
values = super()._prepare_home_portal_values(counters)
|
|
student = self._get_student()
|
|
if student:
|
|
values['student_portal_count'] = 1
|
|
return values
|
|
|
|
@http.route(['/my', '/my/home'], type='http', auth="user", website=True)
|
|
def home(self, **kw):
|
|
student = self._get_student()
|
|
if student:
|
|
return request.redirect('/my/education/dashboard')
|
|
return super().home(**kw)
|
|
|
|
@http.route(['/my/education/dashboard'], type='http', auth="user", website=True)
|
|
def student_dashboard(self, **kw):
|
|
student = self._get_student()
|
|
if not student:
|
|
return request.render("education_portal.portal_no_student")
|
|
|
|
# Fetch notices, circulars, events
|
|
announcements = request.env['education.announcement'].search([('active', '=', True)], limit=5)
|
|
|
|
# Fetch upcoming/ongoing exams for the student's program and academic year
|
|
exams = request.env['education.exam'].sudo().search([
|
|
('state', 'in', ['scheduled', 'ongoing']),
|
|
('program_id', '=', student.program_id.id),
|
|
('academic_year_id', '=', student.academic_year_id.id),
|
|
], order='start_date asc', limit=5)
|
|
|
|
values = {
|
|
'student': student,
|
|
'announcements': announcements,
|
|
'exams': exams,
|
|
'page_name': 'student_dashboard',
|
|
}
|
|
return request.render("education_portal.portal_student_dashboard", values)
|
|
|
|
@http.route(['/my/education/profile'], type='http', auth="user", website=True)
|
|
def student_profile(self, **kw):
|
|
student = self._get_student()
|
|
if not student:
|
|
return request.redirect('/my/education/dashboard')
|
|
|
|
values = {
|
|
'student': student,
|
|
'page_name': 'student_profile',
|
|
'success': kw.get('success'),
|
|
}
|
|
return request.render("education_portal.portal_student_profile", values)
|
|
|
|
@http.route(['/my/education/profile/update'], type='http', auth="user", methods=['POST'], website=True, csrf=True)
|
|
def student_profile_update(self, **post):
|
|
student = self._get_student()
|
|
if not student:
|
|
return request.redirect('/my/education/dashboard')
|
|
|
|
update_vals = {}
|
|
# Editable contact fields
|
|
if 'street' in post:
|
|
update_vals['street'] = post.get('street')
|
|
if 'street2' in post:
|
|
update_vals['street2'] = post.get('street2')
|
|
if 'city' in post:
|
|
update_vals['city'] = post.get('city')
|
|
if 'zip' in post:
|
|
update_vals['zip'] = post.get('zip')
|
|
if 'guardian_phone' in post:
|
|
update_vals['guardian_phone'] = post.get('guardian_phone')
|
|
if 'guardian_email' in post:
|
|
update_vals['guardian_email'] = post.get('guardian_email')
|
|
|
|
if update_vals:
|
|
student.sudo().write(update_vals)
|
|
|
|
return request.redirect('/my/education/profile?success=1')
|
|
|
|
@http.route(['/my/education/finance'], type='http', auth="user", website=True)
|
|
def student_finance(self, **kw):
|
|
student = self._get_student()
|
|
if not student:
|
|
return request.redirect('/my/education/dashboard')
|
|
|
|
ledger_lines = student.env['education.student.ledger'].search([('student_id', '=', student.id)], order='date asc, id asc')
|
|
payments = student.env['education.student.payment'].search([('student_id', '=', student.id), ('status', '=', 'confirmed')], order='payment_date desc, id desc')
|
|
if student.outstanding_amount <= 0:
|
|
unpaid_assignments = student.env['education.fee.assignment'].search([('student_id', '=', student.id), ('payment_status', '!=', 'paid')])
|
|
if unpaid_assignments:
|
|
unpaid_assignments.sudo().write({'payment_status': 'paid'})
|
|
pending_fee_assignments = student.env['education.fee.assignment']
|
|
else:
|
|
pending_fee_assignments = student.env['education.fee.assignment'].search([('student_id', '=', student.id), ('payment_status', '!=', 'paid')], order='assignment_date desc')
|
|
|
|
values = {
|
|
'student': student,
|
|
'ledger_lines': ledger_lines,
|
|
'payments': payments,
|
|
'pending_fee_assignments': pending_fee_assignments,
|
|
'page_name': 'student_finance',
|
|
'success_payment': kw.get('success_payment'),
|
|
}
|
|
return request.render("education_portal.portal_student_finance", values)
|
|
|
|
@http.route(['/my/education/finance/pay'], type='http', auth="user", website=True)
|
|
def student_pay(self, **kw):
|
|
student = self._get_student()
|
|
if not student:
|
|
return request.redirect('/my/education/dashboard')
|
|
|
|
values = {
|
|
'student': student,
|
|
'page_name': 'student_pay',
|
|
'error': kw.get('error'),
|
|
}
|
|
return request.render("education_portal.portal_student_pay", values)
|
|
|
|
@http.route(['/my/education/finance/pay/confirm'], type='http', auth="user", methods=['POST'], website=True)
|
|
def student_pay_confirm(self, **post):
|
|
student = self._get_student()
|
|
if not student:
|
|
return request.redirect('/my/education/dashboard')
|
|
|
|
try:
|
|
amount = float(post.get('amount', 0.0))
|
|
except ValueError:
|
|
amount = 0.0
|
|
|
|
payment_method = post.get('payment_method', 'online')
|
|
|
|
if amount <= 0 or amount > student.outstanding_amount:
|
|
return request.redirect('/my/education/finance/pay?error=1')
|
|
|
|
import time
|
|
# Create payment record
|
|
payment = student.env['education.student.payment'].sudo().create({
|
|
'student_id': student.id,
|
|
'amount': amount,
|
|
'payment_method': payment_method,
|
|
'receipt_number': f"REC-ONLINE-{int(time.time())}-{student.id}",
|
|
'reference_number': post.get('reference_number', 'ONLINE-PORTAL'),
|
|
'remarks': 'Payment submitted via Student Self-Service Portal.',
|
|
'company_id': student.company_id.id,
|
|
})
|
|
payment.action_confirm()
|
|
|
|
try:
|
|
return request.redirect('/my/education/finance?success_payment=1')
|
|
except RuntimeError:
|
|
# pyrefly: ignore [missing-import]
|
|
from odoo.http import Response
|
|
return Response("Success")
|
|
|
|
@http.route(['/my/education/finance/receipt/<int:payment_id>'], type='http', auth="user", website=True)
|
|
def download_receipt(self, payment_id, **kw):
|
|
student = self._get_student()
|
|
if not student:
|
|
return request.redirect('/my/education/dashboard')
|
|
|
|
payment = student.env['education.student.payment'].sudo().browse(payment_id)
|
|
if not payment.exists() or payment.student_id.id != student.id:
|
|
return request.redirect('/my/education/finance')
|
|
|
|
pdf, _ = student.env['ir.actions.report'].sudo()._render_qweb_pdf('education_finance.action_report_receipt_pdf', [payment.id])
|
|
pdfhttpheaders = [
|
|
('Content-Type', 'application/pdf'),
|
|
('Content-Length', len(pdf)),
|
|
('Content-Disposition', f'attachment; filename=Receipt-{payment.receipt_number or "PAY"}.pdf;')
|
|
]
|
|
return request.make_response(pdf, headers=pdfhttpheaders)
|
|
|
|
@http.route(['/my/education/documents'], type='http', auth="user", website=True)
|
|
def student_documents(self, **kw):
|
|
student = self._get_student()
|
|
if not student:
|
|
return request.redirect('/my/education/dashboard')
|
|
|
|
values = {
|
|
'student': student,
|
|
'page_name': 'student_documents',
|
|
'success': kw.get('success'),
|
|
}
|
|
return request.render("education_portal.portal_student_documents", values)
|
|
|
|
@http.route(['/my/education/documents/upload'], type='http', auth="user", methods=['POST'], website=True, csrf=True)
|
|
def student_documents_upload(self, **post):
|
|
student = self._get_student()
|
|
if not student:
|
|
return request.redirect('/my/education/dashboard')
|
|
|
|
vals = {}
|
|
for field in ['image', 'doc_passport', 'doc_aadhaar', 'doc_birth_cert', 'doc_transfer_cert', 'doc_community_cert', 'doc_mark_sheets']:
|
|
file = post.get(field)
|
|
if file and file.filename:
|
|
file_content = file.read()
|
|
vals[field] = base64.b64encode(file_content)
|
|
filename_field = f"{field}_name"
|
|
if hasattr(student, filename_field):
|
|
vals[filename_field] = file.filename
|
|
|
|
if vals:
|
|
student.sudo().write(vals)
|
|
|
|
return request.redirect('/my/education/documents?success=1')
|
|
|
|
@http.route(['/my/education/attendance'], type='http', auth="user", website=True)
|
|
def student_attendance(self, **kw):
|
|
student = self._get_student()
|
|
if not student:
|
|
return request.redirect('/my/education/dashboard')
|
|
|
|
attendance_records = student.env['education.attendance'].search([('student_id', '=', student.id)], order='date desc, id desc')
|
|
|
|
total_sessions = len(attendance_records)
|
|
present_count = len([a for a in attendance_records if a.status == 'present'])
|
|
absent_count = len([a for a in attendance_records if a.status == 'absent'])
|
|
late_count = len([a for a in attendance_records if a.status == 'late'])
|
|
medical_leave_count = len([a for a in attendance_records if a.status == 'medical_leave'])
|
|
on_duty_count = len([a for a in attendance_records if a.status == 'on_duty'])
|
|
|
|
effective_present = present_count + on_duty_count + (late_count * 0.5)
|
|
attendance_pct = round((effective_present / total_sessions * 100), 1) if total_sessions > 0 else 100.0
|
|
|
|
values = {
|
|
'student': student,
|
|
'attendance_records': attendance_records,
|
|
'total_sessions': total_sessions,
|
|
'present_count': present_count,
|
|
'absent_count': absent_count,
|
|
'late_count': late_count,
|
|
'medical_leave_count': medical_leave_count,
|
|
'on_duty_count': on_duty_count,
|
|
'attendance_pct': attendance_pct,
|
|
'page_name': 'student_attendance',
|
|
}
|
|
return request.render("education_portal.portal_student_attendance", values)
|
|
|
|
@http.route(['/my/education/exam/<int:exam_id>'], type='http', auth="user", website=True)
|
|
def student_exam_details(self, exam_id, **kw):
|
|
student = self._get_student()
|
|
if not student:
|
|
return request.redirect('/my/education/dashboard')
|
|
|
|
exam = request.env['education.exam'].sudo().browse(exam_id)
|
|
if not exam.exists():
|
|
return request.redirect('/my/education/dashboard')
|
|
|
|
values = {
|
|
'student': student,
|
|
'exam': exam,
|
|
'page_name': 'student_exam_details',
|
|
}
|
|
return request.render("education_portal.portal_student_exam_details", values)
|
|
|
|
@http.route(['/my/education/announcement/<int:announcement_id>'], type='http', auth="user", website=True)
|
|
def student_announcement_details(self, announcement_id, **kw):
|
|
student = self._get_student()
|
|
if not student:
|
|
return request.redirect('/my/education/dashboard')
|
|
|
|
announcement = request.env['education.announcement'].sudo().browse(announcement_id)
|
|
if not announcement.exists():
|
|
return request.redirect('/my/education/dashboard')
|
|
|
|
values = {
|
|
'student': student,
|
|
'announcement': announcement,
|
|
'page_name': 'student_announcement_details',
|
|
}
|
|
return request.render("education_portal.portal_student_announcement_details", values)
|
|
|
|
from odoo.addons.web.controllers.home import Home
|
|
|
|
class StudentLoginRedirect(Home):
|
|
|
|
def _login_redirect(self, uid, redirect=None):
|
|
redirect_url = super()._login_redirect(uid, redirect=redirect)
|
|
|
|
# Check if the logged-in user is a student
|
|
user = request.env['res.users'].sudo().browse(uid)
|
|
|
|
# If the user is a student and there is no specific redirect requested
|
|
# (or the redirect is just the default website home / or /my)
|
|
if user.student_id and (not redirect or redirect in ['/', '/my']):
|
|
return '/my/education/dashboard'
|
|
|
|
return redirect_url
|