153 lines
6.8 KiB
Python
153 lines
6.8 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from functools import reduce
|
|
from werkzeug.urls import url_join
|
|
|
|
from odoo import api, models, fields, _
|
|
from odoo.exceptions import UserError, ValidationError
|
|
|
|
class WhatsAppTemplateVariable(models.Model):
|
|
_name = 'whatsapp.template.variable'
|
|
_description = 'WhatsApp Template Variable'
|
|
_order = 'line_type desc, name, id'
|
|
|
|
name = fields.Char(string="Placeholder", required=True)
|
|
button_id = fields.Many2one('whatsapp.template.button', ondelete='cascade')
|
|
wa_template_id = fields.Many2one(comodel_name='whatsapp.template', required=True, ondelete='cascade')
|
|
model = fields.Char(string="Model Name", related='wa_template_id.model')
|
|
|
|
line_type = fields.Selection([
|
|
('button', 'Button'),
|
|
('header', 'Header'),
|
|
('location', 'Location'),
|
|
('body', 'Body')], string="Variable location", required=True)
|
|
field_type = fields.Selection([
|
|
('user_name', 'User Name'),
|
|
('user_mobile', 'User Mobile'),
|
|
('free_text', 'Free Text'),
|
|
('portal_url', 'Portal Link'),
|
|
('field', 'Field of Model')], string="Type", default='free_text', required=True)
|
|
field_name = fields.Char(string="Field")
|
|
demo_value = fields.Char(string="Sample Value", default="Sample Value", required=True)
|
|
|
|
_sql_constraints = [
|
|
(
|
|
'name_type_template_unique',
|
|
'UNIQUE(name, line_type, wa_template_id, button_id)',
|
|
'Variable names must be unique for a given template'
|
|
),
|
|
]
|
|
|
|
@api.constrains('field_type', 'demo_value')
|
|
def _check_demo_values(self):
|
|
if self.filtered(lambda var: var.field_type == 'free_text' and not var.demo_value):
|
|
raise ValidationError(_('Free Text template variables must have a demo value.'))
|
|
if self.filtered(lambda var: var.field_type == 'field' and not var.field_name):
|
|
raise ValidationError(_("Field template variables must be associated with a field."))
|
|
for var in self.filtered('button_id'):
|
|
if not var.demo_value.startswith(var.button_id.website_url):
|
|
raise ValidationError(_('Demo value of a dynamic url must start with the non-dynamic part'
|
|
'of the url such as "https://www.example.com/menu?id=20"'))
|
|
|
|
@api.constrains('field_name')
|
|
def _check_field_name(self):
|
|
is_system = self.user_has_groups('base.group_system')
|
|
for variable in self.filtered('field_name'):
|
|
model = self.env[variable.model]
|
|
if not is_system:
|
|
if not model.check_access_rights('read', raise_exception=False):
|
|
model_description = self.env['ir.model']._get(variable.model).display_name
|
|
raise ValidationError(
|
|
_("You can not select field of %(model)s.", model=model_description)
|
|
)
|
|
safe_fields = model._get_whatsapp_safe_fields() if hasattr(model, '_get_whatsapp_safe_fields') else []
|
|
if variable.field_name not in safe_fields:
|
|
raise ValidationError(
|
|
_("You are not allowed to use field %(field)s, contact your administrator.",
|
|
field=variable.field_name)
|
|
)
|
|
try:
|
|
model._find_value_from_field_path(variable.field_name)
|
|
except UserError as err:
|
|
raise ValidationError(
|
|
_("'%(field)s' does not seem to be a valid field path", field=variable.field_name)
|
|
) from err
|
|
|
|
@api.constrains('name')
|
|
def _check_name(self):
|
|
for variable in self:
|
|
if variable.line_type == 'location' and variable.name not in {'name', 'address', 'latitude', 'longitude'}:
|
|
raise ValidationError(
|
|
_("Location variable should be 'name', 'address', 'latitude' or 'longitude'. Cannot parse '%(placeholder)s'",
|
|
placeholder=variable.name))
|
|
if variable.line_type != 'location' and not variable._extract_variable_index():
|
|
raise ValidationError(
|
|
_('"Template variable should be in format {{number}}. Cannot parse "%(placeholder)s"',
|
|
placeholder=variable.name))
|
|
|
|
@api.constrains('button_id', 'line_type')
|
|
def _check_button_id(self):
|
|
for variable in self:
|
|
if variable.line_type == 'button' and not variable.button_id:
|
|
raise ValidationError(_('Button variables must be linked to a button.'))
|
|
|
|
@api.depends('line_type', 'name')
|
|
def _compute_display_name(self):
|
|
for variable in self:
|
|
if variable.line_type in ('body', 'location'):
|
|
variable.display_name = f'{variable.line_type} - {variable.name}'
|
|
elif variable.line_type == 'button':
|
|
variable.display_name = f'{variable.line_type} "{variable.button_id.name}" - {variable.name}'
|
|
else:
|
|
variable.display_name = variable.line_type
|
|
|
|
@api.onchange('model')
|
|
def _onchange_model_id(self):
|
|
self.field_name = False
|
|
|
|
@api.onchange('field_type')
|
|
def _onchange_field_type(self):
|
|
if self.field_type != 'field':
|
|
self.field_name = False
|
|
|
|
def _get_variables_value(self, record):
|
|
value_by_name = {}
|
|
user = self.env.user
|
|
for variable in self:
|
|
if variable.field_type == 'user_name':
|
|
value = user.name
|
|
elif variable.field_type == 'user_mobile':
|
|
value = user.mobile
|
|
elif variable.field_type == 'field':
|
|
value = variable._find_value_from_field_chain(record)
|
|
elif variable.field_type == 'portal_url':
|
|
portal_url = record._whatsapp_get_portal_url()
|
|
value = url_join(variable.get_base_url(), (portal_url or ''))
|
|
else:
|
|
value = variable.demo_value
|
|
|
|
value_str = value and str(value) or ''
|
|
if variable.button_id:
|
|
value_by_name[f"button-{variable.button_id.name}"] = value_str
|
|
else:
|
|
value_by_name[f"{variable.line_type}-{variable.name}"] = value_str
|
|
|
|
return value_by_name
|
|
|
|
# ------------------------------------------------------------
|
|
# TOOLS
|
|
# ------------------------------------------------------------
|
|
|
|
def _find_value_from_field_chain(self, record):
|
|
"""Get the value of field, returning display_name(s) if the field is a model."""
|
|
self.ensure_one()
|
|
return record.sudo(False)._find_value_from_field_path(self.field_name)
|
|
|
|
def _extract_variable_index(self):
|
|
""" Extract variable index, located between '{{}}' markers. """
|
|
self.ensure_one()
|
|
try:
|
|
return int(self.name.lstrip('{{').rstrip('}}'))
|
|
except ValueError:
|
|
return None
|