# Copyright 2026 Criptomart # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). import logging from markupsafe import Markup from odoo import _, models from odoo.exceptions import UserError from odoo.tools import email_normalize _logger = logging.getLogger(__name__) class ResPartner(models.Model): _inherit = "res.partner" def write(self, vals): res = super().write(vals) if "email" in vals and not self.env.context.get("skip_portal_login_sync"): self._sync_portal_user_login() return res def _get_portal_users_to_sync(self): self.ensure_one() group_portal = self.env.ref("base.group_portal") return self.sudo().user_ids.filtered(lambda u: group_portal in u.groups_id) def _sync_portal_user_login(self): """Set the partner email as login of its portal users and send them a password reset email.""" Users = self.env["res.users"].sudo().with_context(active_test=False) for partner in self: users = partner._get_portal_users_to_sync() if not users: continue new_login = email_normalize(partner.email) if not new_login: _logger.info( "Portal login not synced for partner %s: invalid email %r", partner.id, partner.email, ) continue for user in users: if email_normalize(user.login) == new_login: continue if Users.search_count( [("login", "=", new_login), ("id", "!=", user.id)] ): raise UserError( _( "Cannot change the email of %(partner)s: there is " "already a user with login %(login)s.", partner=partner.display_name, login=new_login, ) ) old_login = user.login user.write({"login": new_login}) _logger.info( "Portal user %s login changed from %s to %s", user.id, old_login, new_login, ) if self.env.context.get("portal_login_sync_no_mail"): body = _( "Portal user login changed from %(old)s to %(new)s.", old=old_login, new=new_login, ) else: body = partner._send_portal_login_reset_password( user, old_login, new_login ) partner.sudo().message_post(body=Markup("
%s
") % body) def _send_portal_login_reset_password(self, user, old_login, new_login): try: with self.env.cr.savepoint(): user.action_reset_password() except Exception as e: # Do not block the email change if the reset email cannot be sent _logger.warning( "Password reset email for portal user %s could not be sent: %s", user.id, e, ) return _( "Portal user login changed from %(old)s to %(new)s, but the " "password reset email could not be sent: %(error)s", old=old_login, new=new_login, error=str(e), ) return _( "Portal user login changed from %(old)s to %(new)s and a password " "reset email has been sent.", old=old_login, new=new_login, )