[REF] various: cleanup alias usage and empty list help overrides
PURPOSE
Cleanup alias definitions and usage to have as few invalid or dummy
data as possible. Cleanup empty list helpers override.
SPECIFCIATIONS
hr_expense: cleanup alias management
* globally avoid crashes if the alias data has been removed;
* recreate Xml ID record if it has been removed as it is considered as
being a master data;
* avoid issues when trying to configure the expense alias in settings if
alias has been manually removed. Setting a name now creates the alias
and an XmlID if it has been removed;
* rename a generic 'use_mail_gateway' field into 'expense_use_mail_gateway'
in order to avoid conflicts with future changes in generic settings linked
to email gateway and configuration
* fix empty list helper: it is now translated and correctly encode parameters
given to 'mailto';
hr_recruitment, maintenance: remove unused aliases
Those modules hold default aliases to respectively create applicants or
maintenance request. Issue with those generic aliases are
* they are not bound to any context (container document). For example jobs
is not linked to any job record. Applicants will not have any job_id set;
* they are not displayed anywhere. Applicant kanban view displays a helper
when having no content, with an email address to start populating the
view. This emails is however based on job in context, aka alias record
created through the alias mixin on the job.
* maintenance does not even try to use aliases in any view or code and just
defines a dummy alias;
* as default data they can be wrong: taking ``jobs`` and ``helpdesk`` is
not necessarily a good idea. Indeed it depends on mail server configuration
(e.g. not always owning all possible emails of a domain when using its own
address like gmail). This may also conflicts with existing email addresses
or already-defined aliases without being really configurable;
* they are not configurable. They are not displayed in any configuration flow
(setting or container document). Only going into technical menus to find all
aliases allows to find them;
mail: improve mail alias views to ease usage
mail, various: cleanup empty list help message overrides
Purpose is to cleanup code: stop using help parameter name (use help_message as
help is a keyword), try to call super(), ...
LINKS
Task-2710804 (Mail: Clean MailThread Posting API)
Prepares Task-36879 (Mail: Support MultiCompany Aliases)
closes odoo/odoo#105992
Related: odoo/upgrade#4046
Related: odoo/enterprise#34089
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
@@ -1234,13 +1234,13 @@ class Lead(models.Model):
|
||||
}
|
||||
|
||||
@api.model
|
||||
def get_empty_list_help(self, help):
|
||||
def get_empty_list_help(self, help_message):
|
||||
""" This method returns the action helpers for the leads. If help is already provided
|
||||
on the action, the same is returned. Otherwise, we build the help message which
|
||||
contains the alias responsible for creating the lead (if available) and return it.
|
||||
"""
|
||||
if not is_html_empty(help):
|
||||
return help
|
||||
if not is_html_empty(help_message):
|
||||
return help_message
|
||||
|
||||
help_title, sub_title = "", ""
|
||||
if self._context.get('default_type') == 'lead':
|
||||
@@ -1258,7 +1258,9 @@ class Lead(models.Model):
|
||||
email = '%s@%s' % (alias_record.alias_name, alias_record.alias_domain)
|
||||
email_link = "<b><a href='mailto:%s'>%s</a></b>" % (email, email)
|
||||
sub_title = _('Use the top left <i>Create</i> button, or send an email to %s to test the email gateway.') % (email_link)
|
||||
return '<p class="o_view_nocontent_smiling_face">%s</p><p class="oe_view_nocontent_alias">%s</p>' % (help_title, sub_title)
|
||||
return super().get_empty_list_help(
|
||||
f'<p class="o_view_nocontent_smiling_face">{help_title}</p><p class="oe_view_nocontent_alias">{sub_title}</p>'
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# BUSINESS
|
||||
|
||||
@@ -6,6 +6,7 @@ import logging
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.addons.iap.tools import iap_tools
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import is_html_empty
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -185,10 +186,15 @@ class CRMLeadMiningRequest(models.Model):
|
||||
self.company_size_max = self.company_size_min
|
||||
|
||||
@api.model
|
||||
def get_empty_list_help(self, help_string):
|
||||
def get_empty_list_help(self, help_message):
|
||||
if not is_html_empty(help_message):
|
||||
return help_message
|
||||
|
||||
help_title = _('Create a Lead Mining Request')
|
||||
sub_title = _('Generate new leads based on their country, industry, size, etc.')
|
||||
return '<p class="o_view_nocontent_smiling_face">%s</p><p class="oe_view_nocontent_alias">%s</p>' % (help_title, sub_title)
|
||||
return super().get_empty_list_help(
|
||||
f'<p class="o_view_nocontent_smiling_face">{help_title}</p><p class="oe_view_nocontent_alias">{sub_title}</p>'
|
||||
)
|
||||
|
||||
def _prepare_iap_payload(self):
|
||||
"""
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import hr_employee
|
||||
from . import account_move
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
import re
|
||||
from markupsafe import Markup
|
||||
import werkzeug
|
||||
|
||||
from odoo import api, fields, Command, models, _
|
||||
from odoo.tools import float_round
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
@@ -403,17 +405,23 @@ class HrExpense(models.Model):
|
||||
|
||||
@api.model
|
||||
def get_empty_list_help(self, help_message):
|
||||
return super(HrExpense, self).get_empty_list_help(help_message or '' + self._get_empty_list_mail_alias())
|
||||
return super().get_empty_list_help((help_message or '') + self._get_empty_list_mail_alias())
|
||||
|
||||
@api.model
|
||||
def _get_empty_list_mail_alias(self):
|
||||
use_mailgateway = self.env['ir.config_parameter'].sudo().get_param('hr_expense.use_mailgateway')
|
||||
alias_record = use_mailgateway and self.env.ref('hr_expense.mail_alias_expense') or False
|
||||
if alias_record and alias_record.alias_domain and alias_record.alias_name:
|
||||
return Markup("""
|
||||
<p>
|
||||
Or send your receipts at <a href="mailto:%(email)s?subject=Lunch%%20with%%20customer%%3A%%20%%2412.32">%(email)s</a>.
|
||||
</p>""") % {'email': '%s@%s' % (alias_record.alias_name, alias_record.alias_domain)}
|
||||
expense_alias = self.env.ref('hr_expense.mail_alias_expense') if use_mailgateway else False
|
||||
if expense_alias and expense_alias.alias_domain and expense_alias.alias_name:
|
||||
alias_email = f'{expense_alias.alias_name}@{expense_alias.alias_domain}'
|
||||
# encode, but force %20 encoding for space instead of a + (URL / mailto difference)
|
||||
params = werkzeug.urls.url_encode({'subject': _("Lunch with customer $12.32")}).replace('+', '%20')
|
||||
return Markup(
|
||||
"""<p>%(send_string)s <a href="mailto:%(alias_email)s?%(params)s">%(alias_email)s</a></p>"""
|
||||
) % {
|
||||
'alias_email': alias_email,
|
||||
'params': params,
|
||||
'send_string': _("Or send your receipts at"),
|
||||
}
|
||||
return ""
|
||||
|
||||
# ----------------------------------------
|
||||
|
||||
@@ -6,14 +6,13 @@ from odoo import api, fields, models
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
expense_alias_prefix = fields.Char(
|
||||
hr_expense_alias_prefix = fields.Char(
|
||||
'Default Alias Name for Expenses',
|
||||
compute='_compute_expense_alias_prefix',
|
||||
compute='_compute_hr_expense_alias_prefix',
|
||||
store=True,
|
||||
readonly=False)
|
||||
use_mailgateway = fields.Boolean(string='Let your employees record expenses by email',
|
||||
config_parameter='hr_expense.use_mailgateway')
|
||||
|
||||
hr_expense_use_mailgateway = fields.Boolean(string='Let your employees record expenses by email',
|
||||
config_parameter='hr_expense.use_mailgateway')
|
||||
module_hr_payroll_expense = fields.Boolean(string='Reimburse Expenses in Payslip')
|
||||
module_hr_expense_extract = fields.Boolean(string='Send bills to OCR to generate expenses')
|
||||
expense_journal_id = fields.Many2one('account.journal', related='company_id.expense_journal_id', readonly=False)
|
||||
@@ -22,17 +21,32 @@ class ResConfigSettings(models.TransientModel):
|
||||
@api.model
|
||||
def get_values(self):
|
||||
res = super(ResConfigSettings, self).get_values()
|
||||
expense_alias = self.env.ref('hr_expense.mail_alias_expense', raise_if_not_found=False)
|
||||
res.update(
|
||||
expense_alias_prefix=self.env.ref('hr_expense.mail_alias_expense').alias_name,
|
||||
hr_expense_alias_prefix=expense_alias.alias_name if expense_alias else False,
|
||||
)
|
||||
return res
|
||||
|
||||
def set_values(self):
|
||||
super().set_values()
|
||||
alias = self.env.ref('hr_expense.mail_alias_expense', raise_if_not_found=False)
|
||||
if alias and alias.alias_name != self.expense_alias_prefix:
|
||||
alias.alias_name = self.expense_alias_prefix
|
||||
expense_alias = self.env.ref('hr_expense.mail_alias_expense', raise_if_not_found=False)
|
||||
if not expense_alias and self.hr_expense_alias_prefix:
|
||||
# create data again
|
||||
alias = self.env['mail.alias'].sudo().create({
|
||||
'alias_contact': 'employees',
|
||||
'alias_model_id': self.env['ir.model']._get_id('hr.expense'),
|
||||
'alias_name': self.hr_expense_alias_prefix,
|
||||
})
|
||||
self.env['ir.model.data'].sudo().create({
|
||||
'name': 'mail_alias_expense',
|
||||
'module': 'hr_expense',
|
||||
'model': 'mail.alias',
|
||||
'noupdate': True,
|
||||
'res_id': alias.id,
|
||||
})
|
||||
elif expense_alias and expense_alias.alias_name != self.hr_expense_alias_prefix:
|
||||
expense_alias.alias_name = self.hr_expense_alias_prefix
|
||||
|
||||
@api.depends('use_mailgateway')
|
||||
def _compute_expense_alias_prefix(self):
|
||||
self.filtered(lambda w: not w.use_mailgateway).update({'expense_alias_prefix': False})
|
||||
@api.depends('hr_expense_use_mailgateway')
|
||||
def _compute_hr_expense_alias_prefix(self):
|
||||
self.filtered(lambda w: not w.hr_expense_use_mailgateway).hr_expense_alias_prefix = False
|
||||
|
||||
@@ -15,22 +15,22 @@
|
||||
id="create_expense_setting"
|
||||
title="Send an email to this email alias with the receipt in attachment to create an expense in one click. If the first word of the mail subject contains the category's internal reference or the category name, the corresponding category will automatically be set. Type the expense amount in the mail subject to set it on the expense too.">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="use_mailgateway"/>
|
||||
<field name="hr_expense_use_mailgateway"/>
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<label string="Incoming Emails" for="use_mailgateway"/>
|
||||
<label string="Incoming Emails" for="hr_expense_use_mailgateway"/>
|
||||
<div class="text-muted">
|
||||
Create expenses from incoming emails
|
||||
</div>
|
||||
<div class="content-group" attrs="{'invisible': ['|', ('use_mailgateway', '=', False), ('alias_domain', 'in', ['localhost', '', False])]}">
|
||||
<div class="content-group" attrs="{'invisible': ['|', ('hr_expense_use_mailgateway', '=', False), ('alias_domain', 'in', ['localhost', '', False])]}">
|
||||
<div class="mt16">
|
||||
<label for="expense_alias_prefix" string="Alias" class="o_light_label"/>
|
||||
<field name="expense_alias_prefix" class="oe_inline"/>
|
||||
<label for="hr_expense_alias_prefix" string="Alias" class="o_light_label"/>
|
||||
<field name="hr_expense_alias_prefix" class="oe_inline"/>
|
||||
<span>@</span>
|
||||
<field name="alias_domain" class="oe_inline" readonly="1" force_save="1"/>
|
||||
<field name="alias_domain" class="oe_inline" readonly="1"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-group" attrs="{'invisible': ['|', ('use_mailgateway', '=', False), ('alias_domain', 'not in', ['localhost', '', False])]}">
|
||||
<div class="content-group" attrs="{'invisible': ['|', ('hr_expense_use_mailgateway', '=', False), ('alias_domain', 'not in', ['localhost', '', False])]}">
|
||||
<div class="mt16">
|
||||
<button type="action" name="base_setup.action_general_configuration" icon="fa-arrow-right" string="Setup your domain alias" class="btn-link"/>
|
||||
</div>
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
'security/hr_recruitment_security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'data/digest_data.xml',
|
||||
'data/mail_alias_data.xml',
|
||||
'data/mail_message_subtype_data.xml',
|
||||
'data/mail_template_data.xml',
|
||||
'data/mail_templates.xml',
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="mail_alias_jobs" model="mail.alias">
|
||||
<field name="alias_name">jobs</field>
|
||||
<field name="alias_model_id" ref="model_hr_applicant"/>
|
||||
<field name="alias_user_id" ref="base.user_admin"/>
|
||||
<field name="alias_parent_model_id" ref="model_hr_job"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -434,7 +434,7 @@ class Applicant(models.Model):
|
||||
self._update_employee_from_applicant()
|
||||
return res
|
||||
|
||||
def get_empty_list_help(self, help):
|
||||
def get_empty_list_help(self, help_message):
|
||||
if 'active_id' in self.env.context and self.env.context.get('active_model') == 'hr.job':
|
||||
alias_id = self.env['hr.job'].browse(self.env.context['active_id']).alias_id
|
||||
else:
|
||||
@@ -453,9 +453,9 @@ class Applicant(models.Model):
|
||||
email = alias_id.display_name
|
||||
email_link = "<a href='mailto:%s'>%s</a>" % (email, email)
|
||||
nocontent_values['email_link'] = email_link
|
||||
nocontent_body += """<p class="o_copy_paste_email">%(email_link)s</p>"""
|
||||
nocontent_body += """<p class="o_copy_paste_email oe_view_nocontent_alias">%(email_link)s</p>"""
|
||||
|
||||
return nocontent_body % nocontent_values
|
||||
return super().get_empty_list_help(nocontent_body % nocontent_values)
|
||||
|
||||
@api.model
|
||||
def get_view(self, view_id=None, view_type='form', **options):
|
||||
|
||||
@@ -338,14 +338,14 @@ class MailThread(models.AbstractModel):
|
||||
return super(MailThread, self.with_context(mail_notrack=True)).copy_data(default=default)
|
||||
|
||||
@api.model
|
||||
def get_empty_list_help(self, help):
|
||||
def get_empty_list_help(self, help_message):
|
||||
""" Override of BaseModel.get_empty_list_help() to generate an help message
|
||||
that adds alias information. """
|
||||
model = self._context.get('empty_list_help_model')
|
||||
res_id = self._context.get('empty_list_help_id')
|
||||
catchall_domain = self.env['ir.config_parameter'].sudo().get_param("mail.catchall.domain")
|
||||
document_name = self._context.get('empty_list_help_document_name', _('document'))
|
||||
nothing_here = not help
|
||||
nothing_here = is_html_empty(help_message)
|
||||
alias = None
|
||||
|
||||
if catchall_domain and model and res_id: # specific res_id -> find its alias (i.e. section_id specified)
|
||||
@@ -369,28 +369,28 @@ class MailThread(models.AbstractModel):
|
||||
if alias:
|
||||
email_link = "<a href='mailto:%(email)s'>%(email)s</a>" % {'email': alias.display_name}
|
||||
if nothing_here:
|
||||
return "<p class='o_view_nocontent_smiling_face'>%(dyn_help)s</p>" % {
|
||||
return super().get_empty_list_help("<p class='o_view_nocontent_smiling_face'>%(dyn_help)s</p>" % {
|
||||
'dyn_help': _("Add a new %(document)s or send an email to %(email_link)s",
|
||||
document=document_name,
|
||||
email_link=email_link,
|
||||
)
|
||||
}
|
||||
})
|
||||
# do not add alias two times if it was added previously
|
||||
if "oe_view_nocontent_alias" not in help:
|
||||
return "%(static_help)s<p class='oe_view_nocontent_alias'>%(dyn_help)s</p>" % {
|
||||
'static_help': help,
|
||||
if "oe_view_nocontent_alias" not in help_message:
|
||||
return super().get_empty_list_help("%(static_help)s<p class='oe_view_nocontent_alias'>%(dyn_help)s</p>" % {
|
||||
'static_help': help_message,
|
||||
'dyn_help': _("Create new %(document)s by sending an email to %(email_link)s",
|
||||
document=document_name,
|
||||
email_link=email_link,
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
if nothing_here:
|
||||
return "<p class='o_view_nocontent_smiling_face'>%(dyn_help)s</p>" % {
|
||||
return super().get_empty_list_help("<p class='o_view_nocontent_smiling_face'>%(dyn_help)s</p>" % {
|
||||
'dyn_help': _("Create new %(document)s", document=document_name),
|
||||
}
|
||||
})
|
||||
|
||||
return help
|
||||
return super().get_empty_list_help(help_message)
|
||||
|
||||
# ------------------------------------------------------
|
||||
# MODELS / CRUD HELPERS
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button name="open_document" string="Open Document"
|
||||
type="object" class="oe_link"
|
||||
type="object" class="oe_link" icon="fa-sitemap"
|
||||
attrs="{'invisible': ['|', ('alias_model_id', '=', False), ('alias_force_thread_id', '=', 0)]}"/>
|
||||
<button name="open_parent_document" string="Open Parent Document"
|
||||
type="object" class="oe_link" icon="fa-sitemap"
|
||||
@@ -42,9 +42,18 @@
|
||||
<tree string="Alias">
|
||||
<field name="alias_name"/>
|
||||
<field name="alias_model_id"/>
|
||||
<field name="alias_force_thread_id" optional="hide"/>
|
||||
<field name="alias_user_id"/>
|
||||
<field name="alias_defaults"/>
|
||||
<field name="alias_parent_model_id"/>
|
||||
<field name="alias_parent_thread_id" optional="hide"/>
|
||||
<field name="alias_defaults" optional="hide"/>
|
||||
<field name="alias_contact"/>
|
||||
<button name="open_document" type="object"
|
||||
string="Open Document" icon="fa-sitemap"
|
||||
attrs="{'invisible': ['|', ('alias_model_id', '=', False), ('alias_force_thread_id', '=', 0)]}"/>
|
||||
<button name="open_parent_document" type="object"
|
||||
string="Open Owner" icon="fa-sitemap"
|
||||
attrs="{'invisible': ['|', ('alias_parent_model_id', '=', False), ('alias_parent_thread_id', '=', 0)]}"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -63,8 +72,12 @@
|
||||
<separator/>
|
||||
<filter string="Active" name="active" domain="[('alias_name', '!=', False)]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="User" name="User" context="{'group_by':'alias_user_id'}"/>
|
||||
<filter string="Model" name="Model" context="{'group_by':'alias_model_id'}"/>
|
||||
<filter string="Owner" name="groupby_user_id"
|
||||
context="{'group_by': 'alias_user_id'}"/>
|
||||
<filter string="Document Model" name="groupby_alias_model_id"
|
||||
context="{'group_by': 'alias_model_id'}"/>
|
||||
<filter string="Container Model" name="groupby_alias_model_id"
|
||||
context="{'group_by': 'alias_parent_model_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
'security/maintenance.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'data/maintenance_data.xml',
|
||||
'data/mail_alias_data.xml',
|
||||
'data/mail_activity_type_data.xml',
|
||||
'data/mail_message_subtype_data.xml',
|
||||
'views/maintenance_views.xml',
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!-- email alias for maintenance requests -->
|
||||
<record id="mail_alias_equipment" model="mail.alias">
|
||||
<field name="alias_name">helpdesk</field>
|
||||
<field name="alias_model_id" ref="model_maintenance_request"/>
|
||||
<field name="alias_user_id" ref="base.user_admin"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -1901,11 +1901,11 @@ class MrpProduction(models.Model):
|
||||
return action
|
||||
|
||||
@api.model
|
||||
def get_empty_list_help(self, help):
|
||||
def get_empty_list_help(self, help_message):
|
||||
self = self.with_context(
|
||||
empty_list_help_document_name=_("manufacturing order"),
|
||||
)
|
||||
return super(MrpProduction, self).get_empty_list_help(help)
|
||||
return super(MrpProduction, self).get_empty_list_help(help_message)
|
||||
|
||||
def _log_downside_manufactured_quantity(self, moves_modification, cancel=False):
|
||||
|
||||
|
||||
@@ -644,11 +644,11 @@ class ProductProduct(models.Model):
|
||||
return prices
|
||||
|
||||
@api.model
|
||||
def get_empty_list_help(self, help):
|
||||
def get_empty_list_help(self, help_message):
|
||||
self = self.with_context(
|
||||
empty_list_help_document_name=_("product"),
|
||||
)
|
||||
return super(ProductProduct, self).get_empty_list_help(help)
|
||||
return super(ProductProduct, self).get_empty_list_help(help_message)
|
||||
|
||||
def get_product_multiline_description_sale(self):
|
||||
""" Compute a multiline description of this product, in the context of sales
|
||||
|
||||
@@ -1283,11 +1283,11 @@ class ProductTemplate(models.Model):
|
||||
return {}
|
||||
|
||||
@api.model
|
||||
def get_empty_list_help(self, help):
|
||||
def get_empty_list_help(self, help_message):
|
||||
self = self.with_context(
|
||||
empty_list_help_document_name=_("product"),
|
||||
)
|
||||
return super(ProductTemplate, self).get_empty_list_help(help)
|
||||
return super(ProductTemplate, self).get_empty_list_help(help_message)
|
||||
|
||||
@api.model
|
||||
def get_import_templates(self):
|
||||
|
||||
Reference in New Issue
Block a user