odoo18/addons_extensions/whatsapp/models/whatsapp_template_variable.py

161 lines
7.0 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
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", "button_id")
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.'))
@api.constrains("field_type", "field_name")
def _check_field_name(self):
is_system = self.env.user.has_group('base.group_system')
failing = self.browse()
to_check = self.filtered(lambda v: v.field_type == "field")
missing = to_check.filtered(lambda v: not v.field_name)
if missing:
raise ValidationError(
_("Field template variables %(var_names)s must be associated with a field.",
var_names=", ".join(missing.mapped("name")),
)
)
for variable in to_check:
model = self.env[variable.model]
if not is_system:
if not model.has_access('read'):
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:
failing += variable
if failing:
model_description = self.env['ir.model']._get(failing.mapped('model')[0]).display_name
raise ValidationError(
_("Variables %(field_names)s do not seem to be valid field path for model %(model_name)s.",
field_names=", ".join(failing.mapped("field_name")),
model_name=model_description,
)
)
@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))
elif variable.line_type == 'button' and variable.name != variable.button_id.name:
raise ValidationError(_("Dynamic button variable name must be the same as its respective button's name"))
elif variable.line_type in ('header', 'body') 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):
type_names = dict(self._fields["line_type"]._description_selection(self.env))
for variable in self:
type_name = type_names[variable.line_type or 'body']
variable.display_name = type_name if variable.line_type == 'header' else f'{type_name} - {variable.name}'
@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