[REF] mail: improve template recipients rendering and generation
When computing recipients based on template we generally create new partners based on emails. This is due to the mail framework managing mainly partners and not low level emails. This code is currently sequential: fetching or creating partners one by one based on found emails, for each record. This is highly inefficient when sending emails on a lot of records based on templates. Purpose of this commit is to improve code and performances by batching. We do queries in batch: one search for all records, create partner in batch. This leads to code being a bit more complicated to correctly remember and dispatch again emails and partners to the correct records. Task-3034875 (Mail: Speedup and batch partners find or create with templates) Part-of: odoo/odoo#99482
This commit is contained in:
@@ -282,6 +282,7 @@ class MailTemplate(models.Model):
|
||||
self.ensure_one()
|
||||
if render_results is None:
|
||||
render_results = {}
|
||||
records_sudo = None
|
||||
|
||||
# if using default recipients -> ``_message_get_default_recipients`` gives
|
||||
# values for email_to, email_cc and partner_ids
|
||||
@@ -297,35 +298,60 @@ class MailTemplate(models.Model):
|
||||
for res_id in res_ids:
|
||||
render_results.setdefault(res_id, {})[field] = generated_field_values[res_id]
|
||||
|
||||
records_company = None
|
||||
# classify records per company for partner fetch/creation
|
||||
if find_or_create_partners and self.model and 'company_id' in self.env[self.model]._fields:
|
||||
records_dict = self.env[self.model].browse(res_ids).sudo().read(['company_id'])
|
||||
records_company = {
|
||||
rec['id']: rec['company_id'][0] if rec['company_id'] else False
|
||||
for rec in records_dict
|
||||
}
|
||||
if not records_sudo:
|
||||
records_sudo = self.env[self.model].browse(res_ids).sudo()
|
||||
records_per_company = {}
|
||||
for read_record in records_sudo.read(['company_id']):
|
||||
company_id = read_record['company_id'][0] if read_record['company_id'] else False
|
||||
records_per_company.setdefault(company_id, []).append(read_record['id'])
|
||||
else:
|
||||
records_per_company = {False: res_ids}
|
||||
|
||||
# consolidate partner_ids: based on partner_to + create partners if requested
|
||||
for res_id in res_ids:
|
||||
record_values = render_results[res_id]
|
||||
# create partners from emails if asked to
|
||||
if find_or_create_partners:
|
||||
for company_id, record_ids in records_per_company.items():
|
||||
all_emails = []
|
||||
email_to_res_ids = {}
|
||||
for res_id in record_ids:
|
||||
record_values = render_results.setdefault(res_id, {})
|
||||
mails = tools.email_split(record_values.pop('email_to', '')) + \
|
||||
tools.email_split(record_values.pop('email_cc', ''))
|
||||
all_emails += mails
|
||||
for mail in mails:
|
||||
email_to_res_ids.setdefault(mail, []).append(res_id)
|
||||
|
||||
partner_ids = record_values.get('partner_ids', [])
|
||||
if find_or_create_partners:
|
||||
mails = tools.email_split(record_values.pop('email_to', '')) + tools.email_split(record_values.pop('email_cc', ''))
|
||||
Partner = self.env['res.partner']
|
||||
if records_company:
|
||||
Partner = Partner.with_context(default_company_id=records_company[res_id])
|
||||
for mail in mails:
|
||||
partner = Partner.find_or_create(mail)
|
||||
partner_ids.append(partner.id)
|
||||
if not all_emails:
|
||||
continue
|
||||
additional_values = {}
|
||||
if company_id:
|
||||
additional_values['company_id'] = company_id
|
||||
partners = self.env['res.partner']._find_or_create_from_emails(
|
||||
all_emails,
|
||||
additional_values=additional_values
|
||||
)
|
||||
for original_email, partner in zip(all_emails, partners):
|
||||
if not partner:
|
||||
continue
|
||||
for res_id in email_to_res_ids[original_email]:
|
||||
render_results[res_id].setdefault('partner_ids', []).append(partner.id)
|
||||
|
||||
# update 'partner_to' rendered value to 'partner_ids'
|
||||
all_partner_to = {
|
||||
int(pid)
|
||||
for record_values in render_results.values()
|
||||
for pid in record_values.get('partner_to', '').split(',')
|
||||
if pid and pid.strip()
|
||||
}
|
||||
existing_pids = set()
|
||||
if all_partner_to:
|
||||
existing_pids = set(self.env['res.partner'].sudo().browse(list(all_partner_to)).exists().ids)
|
||||
for res_id, record_values in render_results.items():
|
||||
partner_to = record_values.pop('partner_to', '')
|
||||
if partner_to:
|
||||
# placeholders could generate '', 3, 2 due to some empty field values
|
||||
tpl_partner_ids = [int(pid) for pid in partner_to.split(',') if pid]
|
||||
partner_ids += self.env['res.partner'].sudo().browse(tpl_partner_ids).exists().ids
|
||||
|
||||
record_values['partner_ids'] = partner_ids
|
||||
tpl_partner_ids = set(int(pid) for pid in partner_to.split(',') if pid) & existing_pids
|
||||
record_values.setdefault('partner_ids', []).extend(tpl_partner_ids)
|
||||
|
||||
return render_results
|
||||
|
||||
|
||||
Reference in New Issue
Block a user