From c78b926565cce283c782a1f58741092a530d4a59 Mon Sep 17 00:00:00 2001 From: Marc Durepos Date: Wed, 10 Sep 2025 09:30:02 -0400 Subject: [PATCH] [MIG] bemade_partner_email_domain to 18.0 --- bemade_partner_email_domain/__init__.py | 2 + bemade_partner_email_domain/__manifest__.py | 34 ++++++ .../controllers/__init__.py | 1 + .../controllers/main.py | 40 +++++++ .../data/mail_template_data.xml | 106 ++++++++++++++++++ .../data/template_data.xml | 18 +++ bemade_partner_email_domain/i18n/fr_CA.po | 41 +++++++ .../models/__init__.py | 1 + .../models/res_partner.py | 97 ++++++++++++++++ .../views/res_partner_views.xml | 20 ++++ 10 files changed, 360 insertions(+) create mode 100644 bemade_partner_email_domain/__init__.py create mode 100644 bemade_partner_email_domain/__manifest__.py create mode 100644 bemade_partner_email_domain/controllers/__init__.py create mode 100644 bemade_partner_email_domain/controllers/main.py create mode 100644 bemade_partner_email_domain/data/mail_template_data.xml create mode 100644 bemade_partner_email_domain/data/template_data.xml create mode 100644 bemade_partner_email_domain/i18n/fr_CA.po create mode 100644 bemade_partner_email_domain/models/__init__.py create mode 100644 bemade_partner_email_domain/models/res_partner.py create mode 100644 bemade_partner_email_domain/views/res_partner_views.xml diff --git a/bemade_partner_email_domain/__init__.py b/bemade_partner_email_domain/__init__.py new file mode 100644 index 0000000..f7209b1 --- /dev/null +++ b/bemade_partner_email_domain/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import controllers diff --git a/bemade_partner_email_domain/__manifest__.py b/bemade_partner_email_domain/__manifest__.py new file mode 100644 index 0000000..83d7854 --- /dev/null +++ b/bemade_partner_email_domain/__manifest__.py @@ -0,0 +1,34 @@ +{ + "name": "Automated Partner Association by Email Domain", + "version": "18.0.0.0.1", + "category": "Extra Tools", + "summary": "Automatically associates partners with companies using matching email domains", + "description": """ + This advanced utility module empowers Odoo users with the capacity to automate partner-company associations + based directly on email domains. It helps in eliminating manual effort by establishing a rule of correlation + between the email domain of a partner and the corresponding division or a company. + + The automation proceeds as follows: + + - On creation of partner records, the system will cross-verify the email domain against the companies + in the records. + - If a match is detected, the partner will automatically be linked with that company. + + This module also takes care of instances where there are multiple companies with the same email domain. + In such cases, an email is dispatched to the partner with a selection interface to finalize the association. + + The module is a significant productivity enhancer for businesses that deal with a high number of partners + and require efficient management. + """, + "depends": ["base", "mail", "website"], + "data": [ + "views/res_partner_views.xml", + "data/mail_template_data.xml", + "data/template_data.xml", + ], + "license": "AGPL-3", + "author": "Bemade", + "website": "https://bemade.org/", + "installable": True, + "auto_install": False, +} diff --git a/bemade_partner_email_domain/controllers/__init__.py b/bemade_partner_email_domain/controllers/__init__.py new file mode 100644 index 0000000..deec4a8 --- /dev/null +++ b/bemade_partner_email_domain/controllers/__init__.py @@ -0,0 +1 @@ +from . import main \ No newline at end of file diff --git a/bemade_partner_email_domain/controllers/main.py b/bemade_partner_email_domain/controllers/main.py new file mode 100644 index 0000000..5ed61c7 --- /dev/null +++ b/bemade_partner_email_domain/controllers/main.py @@ -0,0 +1,40 @@ +# Import necessary libraries +from odoo import http, _ +#from odoo.http import request + + +# Define a controller class that inherits from http.Controller +class DivisionCompanyController(http.Controller): + + # Decorator to set the routing rules for the method 'select_division_company'. + # The URL for this method will be '/select_division_company' + # It has public access and should be used for HTTP requests of type GET. + @http.route('/select_division_company', website=True, type='http', auth='public') + def select_division_company(self, partner_id, access_token, division_id, **kwargs): + + # Search the 'res.partner' model for a record with the given partner_id using Super User access rights + partner = http.request.env['res.partner'].sudo().search([ + ('id', '=', int(partner_id)) + ]) + + # Check if a partner was found with the given ID + if not partner: + # If no partner found, render an error page with a translatable error message + return http.request.render('bemade_partner_email_domain.error_page', + {'error_message': _('The requested partner is not found.')}) + else: + # If the partner exists and the access token matches, proceed with updating the partner's division + if access_token and access_token == partner.access_token: + # Update the partner's parent with the division_id + partner.write({'parent_id': int(division_id)}) + + # Set a confirmation message signaling that the partner has been associated with the division + confirmation_message = _('Partner %s is associated with the division %s.' % (partner.name, division_id)) + + # Render the error page (which in this case acts as a status page) with the confirmation message. + return http.request.render('bemade_partner_email_domain.error_page', + {'confirmation_message': confirmation_message}) + else: + # If the access token was not provided or does not match, render an error page with an error message + return http.request.render('bemade_partner_email_domain.error_page', + {'error_message': _('No Access Token found or not matching for this partner.')}) \ No newline at end of file diff --git a/bemade_partner_email_domain/data/mail_template_data.xml b/bemade_partner_email_domain/data/mail_template_data.xml new file mode 100644 index 0000000..aeff016 --- /dev/null +++ b/bemade_partner_email_domain/data/mail_template_data.xml @@ -0,0 +1,106 @@ + + + + + Division Selection: Choose Your Division + + {{object.company_id.email_formatted or user.email_formatted}} + {{object.id}} + Select Your Division at {{object.company_id.name}} + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+

Hello Brandon Freeman,

+

We noticed that you have registered with our company by sending an email or using our website. To provide you with the best possible service, we need you to confirm the division of your company that you are working for.

+

Please click on the link below to select your division:

+ +

If you have any questions or did not register with us, please disregard this email or contact us directly.

+

Best regards,

+

The Durpro team.

+
+
+ + + + + + + + + +
+ +
+ + + | + + + | + +
+
+
+ + + + + + +
+ Powered by Odoo +
+
+
+ {{ object.lang }} + +
+
+
diff --git a/bemade_partner_email_domain/data/template_data.xml b/bemade_partner_email_domain/data/template_data.xml new file mode 100644 index 0000000..c703b5e --- /dev/null +++ b/bemade_partner_email_domain/data/template_data.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/bemade_partner_email_domain/i18n/fr_CA.po b/bemade_partner_email_domain/i18n/fr_CA.po new file mode 100644 index 0000000..9f6a648 --- /dev/null +++ b/bemade_partner_email_domain/i18n/fr_CA.po @@ -0,0 +1,41 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * bemade_partner_email_domain +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0\n" +"Report-Msgid-Bugs-To: it@bemade.org\n" +"Last-Translator: chatgpt\n" +"Language-Team: French (http://www.transifex.com/odoo/odoo-14-0/language/fr_CA/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#: model:ir.model.fields,field_description:bemade_partner_email_domain.field_res_partner_access_token +msgid "Access Token" +msgstr "Jeton d'accès" + +#: model:ir.model.fields,field_description:bemade_partner_email_domain.field_res_partner_email_domain +msgid "Email Domain" +msgstr "Domaine de courriel" + +#: model:ir.model.fields,field_description:bemade_partner_email_domain.field_res_partner_is_subdivision +msgid "Subdivision" +msgstr "Subdivision" + +#: code:addons/bemade_partner_email_domain/controllers/main.py:0 +#, python-format +msgid "The requested partner is not found." +msgstr "Le partenaire demandé n'est pas trouvé." + +#: code:addons/bemade_partner_email_domain/controllers/main.py:0 +#, python-format +msgid "No Access Token found or not matching for this partner." +msgstr "Aucun jeton d'accès trouvé ou ne correspondant pas à ce partenaire." + +#: code:addons/bemade_partner_email_domain/controllers/main.py:0 +#, python-format +msgid "Partner %s is associated with the division %s." +msgstr "Le partenaire %s est associé à la division %s." \ No newline at end of file diff --git a/bemade_partner_email_domain/models/__init__.py b/bemade_partner_email_domain/models/__init__.py new file mode 100644 index 0000000..4c58886 --- /dev/null +++ b/bemade_partner_email_domain/models/__init__.py @@ -0,0 +1 @@ +from . import res_partner \ No newline at end of file diff --git a/bemade_partner_email_domain/models/res_partner.py b/bemade_partner_email_domain/models/res_partner.py new file mode 100644 index 0000000..7fe3451 --- /dev/null +++ b/bemade_partner_email_domain/models/res_partner.py @@ -0,0 +1,97 @@ +from odoo import api, fields, models, Command +from odoo.tools.sql import SQL +import uuid + +from odoo.addons.website.controllers.main import Website + + +class Partner(models.Model): + _inherit = "res.partner" + + email_domain = fields.Char(string="Email Domain") + is_subdivision = fields.Boolean(string="Subdivision", default=False) + access_token = fields.Char(string="Access Token") + + def _generate_access_token(self): + return uuid.uuid4().hex + + def _send_selection_email(self, division_companies): + self.ensure_one() + # Generate a token and save it to the partner + access_token = self._generate_access_token() + print(f"partner id: {self.id}") + self.write({"access_token": access_token}) + + # Now include the token in the links + base_url = self.env["ir.config_parameter"].sudo().get_param("web.base.url") + links = { + company.name: ( + f"{base_url}/select_division_company?partner_id={self.id}&access_token={access_token}&division_id={company.id}" + ) + for company in division_companies + } + + print(f"links: {links}") + # Render the email template and send the email + template = self.env.ref( + "bemade_partner_email_domain.email_template_select_parent" + ) + template.with_context(links=links).send_mail(self.id, force_send=True) + + def _check_parent_from_email_domain(self): + for rec in self: + if rec.parent_id: + continue + try: + # Split the email address on '@' and get the domain part + if rec.email: + email_domain = rec.email.split("@")[1].strip() + else: + continue + except IndexError: + # If there's no '@' symbol, the email address is invalid + return False + + sql = """ + SELECT id + FROM res_partner + WHERE email_domain IS NOT NULL + AND email_domain = RIGHT(%s, LENGTH(email_domain)) + AND company_id = %s + """ + self.env.cr.execute( + SQL(sql, email_domain, self.env.company and self.env.company.id or None) + ) + res = self.env.cr.fetchall() + if len(res) > 1: + rec._send_selection_email( + self.env["res.partner"].search( + [("id", "in", [result[0] for result in res])] + ) + ) + elif len(res) == 1: + rec.write({"parent_id": res[0][0]}) + else: + continue + + @api.model_create_multi + def create(self, vals_list): + res = super(Partner, self).create(vals_list) + res._check_parent_from_email_domain() + return res + + def write(self, vals): + res = super(Partner, self).write(vals) + if "email" in vals: + self._check_parent_from_email_domain() + return res + + @api.onchange("is_subdivision") + def _onchange_is_subdivision(self): + """Change the contact type since partners of type "contact" get their address + overwritten when the parent_id is changed.""" + for record in self: + partner = record._origin + if record.is_subdivision and record.type == "contact": + record.type = "other" + partner.type = "other" diff --git a/bemade_partner_email_domain/views/res_partner_views.xml b/bemade_partner_email_domain/views/res_partner_views.xml new file mode 100644 index 0000000..bbe172c --- /dev/null +++ b/bemade_partner_email_domain/views/res_partner_views.xml @@ -0,0 +1,20 @@ + + + + bemade_partner_email_domain.partner.email.domain.form + res.partner + + + + + \ No newline at end of file