[MIG] bemade_partner_email_domain to 18.0
This commit is contained in:
parent
ba9f1e0c56
commit
c78b926565
10 changed files with 360 additions and 0 deletions
2
bemade_partner_email_domain/__init__.py
Normal file
2
bemade_partner_email_domain/__init__.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
from . import models
|
||||
from . import controllers
|
||||
34
bemade_partner_email_domain/__manifest__.py
Normal file
34
bemade_partner_email_domain/__manifest__.py
Normal file
|
|
@ -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,
|
||||
}
|
||||
1
bemade_partner_email_domain/controllers/__init__.py
Normal file
1
bemade_partner_email_domain/controllers/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from . import main
|
||||
40
bemade_partner_email_domain/controllers/main.py
Normal file
40
bemade_partner_email_domain/controllers/main.py
Normal file
|
|
@ -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.')})
|
||||
106
bemade_partner_email_domain/data/mail_template_data.xml
Normal file
106
bemade_partner_email_domain/data/mail_template_data.xml
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="email_template_select_parent" model="mail.template">
|
||||
<field name="name">Division Selection: Choose Your Division</field>
|
||||
<field name="model_id" ref="base.model_res_partner"/>
|
||||
<field name="email_from">{{object.company_id.email_formatted or user.email_formatted}}</field>
|
||||
<field name="partner_to">{{object.id}}</field>
|
||||
<field name="subject">Select Your Division at {{object.company_id.name}}</field>
|
||||
<field name="body_html" type="html">
|
||||
<table border="0" cellpadding="0" cellspacing="0"
|
||||
style="padding-top: 16px; background-color: #F1F1F1; font-family:Verdana, Arial, sans-serif; color: #454748; width: 100%; border-collapse:separate;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="590"
|
||||
style="padding: 16px; background-color: white; color: #454748; border-collapse:separate;">
|
||||
<tbody>
|
||||
<!-- HEADER -->
|
||||
<tr>
|
||||
<td align="center" style="min-width: 590px;">
|
||||
<!-- Content here -->
|
||||
</td>
|
||||
</tr>
|
||||
<!-- CONTENT -->
|
||||
<tr>
|
||||
<td align="center" style="min-width: 590px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="590"
|
||||
style="min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td valign="top" style="font-size: 13px;">
|
||||
<p>Hello <t t-out="object.name or ''">Brandon Freeman</t>,</p>
|
||||
<p>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.</p>
|
||||
<p>Please click on the link below to select your division:</p>
|
||||
<ul>
|
||||
<t t-foreach="ctx.get('links', {}).items()" t-as="company_name">
|
||||
<li>
|
||||
<a t-attf-href="{{ company_name[1] }}" style="font-size:12px">
|
||||
<t t-esc="company_name[0]"/>
|
||||
</a>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
<p>If you have any questions or did not register with us, please disregard this email or contact us directly.</p>
|
||||
<p>Best regards,</p>
|
||||
<p>The Durpro team.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- More content can be added here -->
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- FOOTER -->
|
||||
<tr>
|
||||
<td align="center" style="min-width: 590px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="590"
|
||||
style="min-width: 590px; background-color: white; font-size: 11px; padding: 0px 8px 0px 8px; border-collapse:separate;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td valign="middle" align="left">
|
||||
<t t-out="object.company_id.name or ''"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="middle" align="left" style="opacity: 0.7;">
|
||||
<t t-out="object.company_id.phone or ''"/>
|
||||
<t t-if="object.company_id.email">
|
||||
| <a t-att-href="'mailto:%s' % object.company_id.email" style="text-decoration:none; color: #454748;" t-out="object.company_id.email or ''"></a>
|
||||
</t>
|
||||
<t t-if="object.company_id.website">
|
||||
| <a t-att-href="'%s' % object.company_id.website" style="text-decoration:none; color: #454748;" t-out="object.company_id.website or ''"></a>
|
||||
</t>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- POWERED BY -->
|
||||
<tr>
|
||||
<td align="center" style="min-width: 590px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="590" style="min-width: 590px; background-color: #F1F1F1; color: #454748; padding: 8px; border-collapse:separate;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="text-align: center; font-size: 13px;">
|
||||
Powered by <a target="_blank" href="https://www.odoo.com?utm_source=db&utm_medium=auth" style="color: #875A7B;">Odoo</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</field>
|
||||
<field name="lang">{{ object.lang }}</field>
|
||||
<field name="auto_delete" eval="True"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
18
bemade_partner_email_domain/data/template_data.xml
Normal file
18
bemade_partner_email_domain/data/template_data.xml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<template id="error_page">
|
||||
<base href="/" />
|
||||
<t t-call="website.layout">
|
||||
<div id="wrap">
|
||||
<t t-if="error_message">
|
||||
<div class="alert alert-danger"><t t-esc="error_message"/></div>
|
||||
</t>
|
||||
<t t-if="confirmation_message">
|
||||
<div class="alert alert-success"><t t-esc="confirmation_message"/></div>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
</data>
|
||||
</odoo>
|
||||
41
bemade_partner_email_domain/i18n/fr_CA.po
Normal file
41
bemade_partner_email_domain/i18n/fr_CA.po
Normal file
|
|
@ -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."
|
||||
1
bemade_partner_email_domain/models/__init__.py
Normal file
1
bemade_partner_email_domain/models/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from . import res_partner
|
||||
97
bemade_partner_email_domain/models/res_partner.py
Normal file
97
bemade_partner_email_domain/models/res_partner.py
Normal file
|
|
@ -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"
|
||||
20
bemade_partner_email_domain/views/res_partner_views.xml
Normal file
20
bemade_partner_email_domain/views/res_partner_views.xml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0"?>
|
||||
<odoo>
|
||||
<record id="partner_email_domain_form" model="ir.ui.view">
|
||||
<field name="name">bemade_partner_email_domain.partner.email.domain.form</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="company_type" position="after">
|
||||
<label for="is_subdivision" string="Division" invisible="is_company == False"/>
|
||||
<field name="is_subdivision" invisible="is_company == False"/>
|
||||
</field>
|
||||
<field name="website" position="before">
|
||||
<field name="email_domain" invisible="is_company == False"/>
|
||||
</field>
|
||||
<field name="parent_id" position="attributes">
|
||||
<attribute name="invisible">is_company == True and is_subdivision == False</attribute>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Loading…
Reference in a new issue