186 lines
6.8 KiB
Python
186 lines
6.8 KiB
Python
# 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)],
|
|
'groups_id': [(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.<br/><b>Username:</b> {self.email}<br/><b>Temporary Password:</b> {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"""
|
|
<p>Dear {self.full_name},</p>
|
|
<p>Your portal account has been created.</p>
|
|
<p><b>Username:</b> {self.email}</p>
|
|
<p><b>Temporary Password:</b> {temp_password}</p>
|
|
<p>Please log in and update your password.</p>
|
|
""",
|
|
'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']
|
|
|