106 lines
4.6 KiB
Python

# -*- coding: utf-8 -*-
# pyrefly: ignore [missing-import]
from odoo import api, fields, models
# pyrefly: ignore [missing-import]
from odoo.exceptions import ValidationError
import base64
class EducationStudentCertificate(models.Model):
_name = 'education.student.certificate'
_description = 'Student Certificate'
_inherit = ['mail.thread', 'mail.activity.mixin']
_order = 'issue_date desc, id desc'
name = fields.Char(string='Certificate Number', readonly=True, copy=False, index=True)
student_id = fields.Many2one(
'education.student',
string='Student',
required=True,
ondelete='cascade',
index=True,
tracking=True
)
certificate_type = fields.Selection([
('bonafide', 'Bonafide Certificate'),
('id_card', 'Student ID Card'),
('course_completion', 'Course Completion Certificate'),
('conduct', 'Conduct / Character Certificate'),
], string='Certificate Type', required=True, default='bonafide', tracking=True)
state = fields.Selection([
('draft', 'Draft'),
('requested', 'Requested'),
('approved', 'Approved'),
('issued', 'Issued'),
('rejected', 'Rejected'),
], string='Status', default='draft', required=True, tracking=True, index=True)
request_date = fields.Date(string='Request Date', default=fields.Date.context_today, readonly=True)
issue_date = fields.Date(string='Issue Date', readonly=True, tracking=True)
expiry_date = fields.Date(string='Expiry Date')
issued_by = fields.Many2one('res.users', string='Issued By', readonly=True)
document_file = fields.Binary(string='Certificate PDF', attachment=True, readonly=True)
file_name = fields.Char(string='Certificate Filename', readonly=True)
remarks = fields.Text(string='Student Purpose / Remarks')
rejection_reason = fields.Text(string='Rejection Reason')
company_id = fields.Many2one(
'res.company',
string='Company',
related='student_id.company_id',
store=True,
index=True
)
def action_request(self):
for rec in self:
if rec.student_id.status not in ('active', 'admitted'):
raise ValidationError("Only active or admitted students are eligible to request certificates.")
rec.state = 'requested'
def action_approve(self):
for rec in self:
if not (self.env.is_admin() or self.env.user.has_group('education_student.group_education_student_user')):
raise ValidationError("Only authorized staff can approve certificate requests.")
rec.state = 'approved'
def action_issue(self):
for rec in self:
if not (self.env.is_admin() or self.env.user.has_group('education_student.group_education_student_user')):
raise ValidationError("Only authorized staff can issue certificates.")
if rec.student_id.status not in ('active', 'admitted'):
raise ValidationError("Cannot issue official certificate for a student with inactive status.")
if not rec.name:
rec.name = self.env['ir.sequence'].next_by_code('education.student.certificate') or f"CERT-{rec.id}"
rec.issue_date = fields.Date.context_today(self)
rec.issued_by = self.env.user.id
rec.state = 'issued'
# Generate PDF using existing report if available
report_ref = None
if rec.certificate_type == 'bonafide':
report_ref = 'education_reports.action_report_bonafide_certificate'
elif rec.certificate_type == 'id_card':
report_ref = 'education_reports.action_report_student_id_card'
if report_ref and self.env.ref(report_ref, raise_if_not_found=False):
try:
pdf_content, _ = self.env['ir.actions.report'].sudo()._render_qweb_pdf(report_ref, rec.student_id.ids)
if pdf_content:
rec.document_file = base64.b64encode(pdf_content)
clean_code = (rec.name or 'CERT').replace('/', '_')
rec.file_name = f"{clean_code}.pdf"
except Exception:
# Non-blocking if wkhtmltopdf not present in current environment
pass
def action_reject(self, reason=None):
for rec in self:
if not (self.env.is_admin() or self.env.user.has_group('education_student.group_education_student_user')):
raise ValidationError("Only authorized staff can reject certificate requests.")
rec.state = 'rejected'
if reason:
rec.rejection_reason = reason