# -*- 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