# pyrefly: ignore [missing-import] from odoo import api, fields, models # pyrefly: ignore [missing-import] from odoo.exceptions import ValidationError import secrets import string class EducationStudent(models.Model): _inherit = 'education.student' portal_user_id = fields.Many2one( 'res.users', string='Portal User', domain=[('share', '=', True)], tracking=True, copy=False, help='Portal user linked to this student.' ) portal_status = fields.Selection([ ('draft', 'Not Created'), ('active', 'Active'), ('deactivated', 'Deactivated') ], string='Portal Status', default='draft', compute='_compute_portal_status', store=True, tracking=True) portal_username = fields.Char( string='Portal Username', related='portal_user_id.login', readonly=True ) portal_email = fields.Char( string='Portal Email', related='portal_user_id.email', readonly=True ) portal_last_login = fields.Datetime( string='Last Login', related='portal_user_id.login_date', readonly=True ) is_education_admin = fields.Boolean(compute='_compute_is_education_admin') @api.depends('portal_user_id', 'portal_user_id.active') def _compute_portal_status(self): for rec in self: if not rec.portal_user_id: rec.portal_status = 'draft' elif rec.portal_user_id.active: rec.portal_status = 'active' else: rec.portal_status = 'deactivated' def _compute_is_education_admin(self): is_admin = self.env.user.has_group('education_student.group_education_student_admin') for rec in self: rec.is_education_admin = is_admin @api.constrains('portal_user_id') def _check_unique_portal_user(self): for rec in self: if rec.portal_user_id: duplicate = self.search([ ('portal_user_id', '=', rec.portal_user_id.id), ('id', '!=', rec.id) ]) if duplicate: raise ValidationError("This Portal User is already linked to another student.") def action_create_portal_user(self): self.ensure_one() if self.portal_user_id: raise ValidationError("This student already has a portal user linked.") if not self.email: raise ValidationError("An email address is required to create a portal user.") # Check if a user with this login/email already exists existing_user = self.env['res.users'].sudo().search([('login', '=', self.email)], limit=1) if existing_user: # Check if this user is already linked to another student other_student = self.search([('portal_user_id', '=', existing_user.id)], limit=1) if other_student: raise ValidationError(f"A user with email {self.email} already exists and is linked to student {other_student.display_name}.") # Link to the existing user self.portal_user_id = existing_user return True # Check or create res.partner partner = self.env['res.partner'].sudo().search([('email', '=', self.email)], limit=1) if not partner: partner = self.env['res.partner'].sudo().create({ 'name': self.full_name, 'email': self.email, 'phone': self.phone or self.mobile, 'type': 'contact', 'company_id': self.company_id.id, }) # Generate secure temporary password alphabet = string.ascii_letters + string.digits + string.punctuation temp_password = ''.join(secrets.choice(alphabet) for i in range(12)) # Create user linked to partner portal_group = self.env.ref('base.group_portal') new_user = self.env['res.users'].sudo().create({ 'name': self.full_name, 'login': self.email, 'email': self.email, 'partner_id': partner.id, 'company_id': self.company_id.id, 'company_ids': [(4, self.company_id.id)], 'group_ids': [(6, 0, [portal_group.id])], 'password': temp_password, }) self.portal_user_id = new_user # Log details to chatter self.message_post(body=f"Portal user created.
Username: {self.email}
Temporary Password: {temp_password}") # Check if SMTP is configured smtp_configured = self.env['ir.mail_server'].sudo().search_count([('active', '=', True)]) > 0 if smtp_configured: try: template = self.env.ref('auth_signup.set_password_email', raise_if_not_found=False) if template: new_user.partner_id.signup_prepare() template.send_mail(new_user.id, force_send=True) else: mail_values = { 'subject': f"Welcome to the University Portal, {self.full_name}", 'body_html': f"""

Dear {self.full_name},

Your portal account has been created.

Username: {self.email}

Temporary Password: {temp_password}

Please log in and update your password.

""", 'email_to': self.email, } mail = self.env['mail.mail'].sudo().create(mail_values) mail.send() except Exception: smtp_configured = False if not smtp_configured: return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': 'Portal User Created', 'message': 'Portal user created successfully. Invitation email was not sent because no outgoing mail server is configured.', 'sticky': True, 'type': 'warning', } } return True def action_open_portal_user(self): self.ensure_one() if self.portal_user_id: return { 'type': 'ir.actions.act_window', 'name': 'Portal User', 'res_model': 'res.users', 'res_id': self.portal_user_id.id, 'view_mode': 'form', 'target': 'current', } class ResUsers(models.Model): _inherit = 'res.users' student_ids = fields.One2many('education.student', 'portal_user_id', string='Students') @property def student_id(self): return self.student_ids[0] if self.student_ids else self.env['education.student']