Skip to content

Commit a865522

Browse files
[IMP] mail_whatsapp_template: Add support for buttons and variables in WhatsApp templates
1 parent d02a993 commit a865522

9 files changed

Lines changed: 896 additions & 24 deletions

mail_gateway_whatsapp/models/mail_gateway_whatsapp.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ def _send_payload(
293293
"template": {
294294
"name": whatsapp_template.template_name,
295295
"language": {"code": whatsapp_template.language},
296+
"components": whatsapp_template.prepare_value_to_send(),
296297
},
297298
}
298299
)

mail_gateway_whatsapp/models/mail_whatsapp_template.py

Lines changed: 589 additions & 17 deletions
Large diffs are not rendered by default.

mail_gateway_whatsapp/security/ir.model.access.csv

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
22
access_whatsapp_composer,access.whatsapp.composer,model_whatsapp_composer,base.group_user,1,1,1,0
33
access_mail_whatsapp_template_group_system,mail_whatsapp_template_group_system,model_mail_whatsapp_template,base.group_system,1,1,1,1
44
access_mail_whatsapp_template_group_user,mail_whatsapp_template_group_user,model_mail_whatsapp_template,base.group_user,1,0,0,0
5+
access_mail_whatsapp_template_button_group_system,access_mail_whatsapp_template_button_group_system,model_mail_whatsapp_template_button,base.group_system,1,1,1,1
6+
access_mail_whatsapp_template_button_group_user,access_mail_whatsapp_template_button_group_user,model_mail_whatsapp_template_button,base.group_user,1,0,0,0
7+
access_mail_whatsapp_template_variable_group_system,access_mail_whatsapp_template_variable_group_system,model_mail_whatsapp_template_variable,base.group_system,1,1,1,1
8+
access_mail_whatsapp_template_variable_group_user,access_mail_whatsapp_template_variable_group_user,model_mail_whatsapp_template_variable,base.group_user,1,0,0,0

mail_gateway_whatsapp/tests/test_mail_gateway_whatsapp.py

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from markupsafe import Markup
1010

11+
from odoo import Command
1112
from odoo.exceptions import UserError
1213
from odoo.tests import Form, RecordCapturer
1314
from odoo.tests.common import tagged
@@ -382,3 +383,164 @@ def test_compose(self):
382383
post_mock.assert_called()
383384
channel.invalidate_recordset()
384385
self.assertTrue(channel.message_ids)
386+
387+
def test_send_message_with_variable(self):
388+
ctx = {
389+
"default_res_model": self.partner._name,
390+
"default_res_id": self.partner.id,
391+
"default_number_field_name": "mobile",
392+
"default_composition_mode": "comment",
393+
"default_gateway_id": self.gateway.id,
394+
}
395+
tmpl_with_vars = self.env["mail.whatsapp.template"].create(
396+
{
397+
"name": "Partner Vars",
398+
"category": "utility",
399+
"language": "es",
400+
"header": "Hi {{1}}",
401+
"body": "Name: {{1}} · Tel: {{2}}",
402+
"gateway_id": self.gateway.id,
403+
"variable_ids": [Command.clear()],
404+
"state": "approved",
405+
"is_supported": True,
406+
"model_id": self.env["ir.model"]._get("res.partner").id,
407+
}
408+
)
409+
self.env["mail.whatsapp.template.variable"].create(
410+
{
411+
"name": "{{1}}",
412+
"line_type": "header",
413+
"template_id": tmpl_with_vars.id,
414+
"field_name": "name",
415+
}
416+
)
417+
self.env["mail.whatsapp.template.variable"].create(
418+
{
419+
"name": "{{1}}",
420+
"line_type": "body",
421+
"template_id": tmpl_with_vars.id,
422+
"field_name": "name",
423+
}
424+
)
425+
self.env["mail.whatsapp.template.variable"].create(
426+
{
427+
"name": "{{2}}",
428+
"line_type": "body",
429+
"template_id": tmpl_with_vars.id,
430+
"field_name": "mobile",
431+
}
432+
)
433+
self.env["mail.whatsapp.template.button"].create(
434+
{
435+
"name": "mobile",
436+
"button_type": "phone_number",
437+
"template_id": tmpl_with_vars.id,
438+
"call_number": "+34666555444",
439+
}
440+
)
441+
self.assertEqual(len(tmpl_with_vars.variable_ids), 3)
442+
self.assertEqual(len(tmpl_with_vars.button_ids), 1)
443+
self.gateway.whatsapp_account_id = "123456"
444+
form_composer = Form(self.env["whatsapp.composer"].with_context(**ctx))
445+
form_composer.template_id = tmpl_with_vars
446+
self.assertTrue(form_composer.is_required_template)
447+
self.assertTrue(form_composer._get_modifier("template_id", "required"))
448+
composer = form_composer.save()
449+
channel = self.partner._whatsapp_get_channel(
450+
composer.number_field_name, composer.gateway_id
451+
)
452+
message_domain = [
453+
("gateway_type", "=", "whatsapp"),
454+
("model", "=", channel._name),
455+
("res_id", "=", channel.id),
456+
]
457+
with (
458+
RecordCapturer(self.env["mail.message"], message_domain) as capture,
459+
patch("requests.post") as post_mock,
460+
):
461+
post_mock.return_value = MagicMock()
462+
composer.action_send_whatsapp()
463+
# Assertions
464+
self.assertEqual(len(capture.records), 1)
465+
channel.invalidate_recordset()
466+
self.assertTrue(channel.message_ids)
467+
468+
def test_send_message_with_dynamic_button(self):
469+
ctx = {
470+
"default_res_model": self.partner._name,
471+
"default_res_id": self.partner.id,
472+
"default_number_field_name": "mobile",
473+
"default_composition_mode": "comment",
474+
"default_gateway_id": self.gateway.id,
475+
}
476+
tmpl_with_vars_dynamic = self.env["mail.whatsapp.template"].create(
477+
{
478+
"name": "Dynamic Button",
479+
"category": "utility",
480+
"language": "es",
481+
"body": "Name: {{1}} · Tel: {{2}}",
482+
"gateway_id": self.gateway.id,
483+
"variable_ids": [Command.clear()],
484+
"state": "approved",
485+
"is_supported": True,
486+
"model_id": self.env["ir.model"]._get("res.partner").id,
487+
}
488+
)
489+
self.assertEqual(len(tmpl_with_vars_dynamic.variable_ids), 0)
490+
self.env["mail.whatsapp.template.variable"].create(
491+
{
492+
"name": "{{1}}",
493+
"line_type": "body",
494+
"template_id": tmpl_with_vars_dynamic.id,
495+
"field_name": "name",
496+
}
497+
)
498+
self.env["mail.whatsapp.template.variable"].create(
499+
{
500+
"name": "{{2}}",
501+
"line_type": "body",
502+
"template_id": tmpl_with_vars_dynamic.id,
503+
"field_name": "mobile",
504+
}
505+
)
506+
self.env["mail.whatsapp.template.button"].create(
507+
{
508+
"name": "1",
509+
"button_type": "url",
510+
"url_type": "dynamic",
511+
"template_id": tmpl_with_vars_dynamic.id,
512+
"website_url": "https://www.odoo.com",
513+
}
514+
)
515+
self.assertEqual(len(tmpl_with_vars_dynamic.variable_ids), 3)
516+
button = tmpl_with_vars_dynamic.variable_ids.search(
517+
[
518+
("line_type", "=", "button"),
519+
("template_id", "=", tmpl_with_vars_dynamic.id),
520+
]
521+
)
522+
button.field_name = "name"
523+
self.gateway.whatsapp_account_id = "123456"
524+
form_composer = Form(self.env["whatsapp.composer"].with_context(**ctx))
525+
form_composer.template_id = tmpl_with_vars_dynamic
526+
self.assertTrue(form_composer.is_required_template)
527+
self.assertTrue(form_composer._get_modifier("template_id", "required"))
528+
composer = form_composer.save()
529+
channel = self.partner._whatsapp_get_channel(
530+
composer.number_field_name, composer.gateway_id
531+
)
532+
message_domain = [
533+
("gateway_type", "=", "whatsapp"),
534+
("model", "=", channel._name),
535+
("res_id", "=", channel.id),
536+
]
537+
with (
538+
RecordCapturer(self.env["mail.message"], message_domain) as capture,
539+
patch("requests.post") as post_mock,
540+
):
541+
post_mock.return_value = MagicMock()
542+
composer.action_send_whatsapp()
543+
# Assertions
544+
self.assertEqual(len(capture.records), 1)
545+
channel.invalidate_recordset()
546+
self.assertTrue(channel.message_ids)

mail_gateway_whatsapp/tests/test_mail_whatsapp_template.py

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import requests
88

9+
from odoo import Command
910
from odoo.exceptions import UserError
1011
from odoo.tests.common import tagged
1112

@@ -121,15 +122,14 @@ def _patch_request_post(url, *args, **kwargs):
121122
template_2 = self.gateway.whatsapp_template_ids.filtered(
122123
lambda t: t.template_uid == "0987654321"
123124
)
124-
self.assertFalse(template_2.is_supported)
125+
self.assertTrue(template_2.is_supported)
125126
self.assertEqual(template_2.template_name, "test_with_buttons")
126127
self.assertEqual(template_2.category, "marketing")
127128
self.assertEqual(template_2.language, "es")
128129
self.assertEqual(template_2.state, "approved")
129130
self.assertEqual(template_2.header, "Header 2")
130131
self.assertEqual(template_2.body, "Body 2")
131132
self.assertFalse(template_2.footer)
132-
self.assertFalse(template_2.is_supported)
133133

134134
def test_export_template(self):
135135
def _patch_request_post(url, *args, **kwargs):
@@ -168,3 +168,73 @@ def _patch_request_get(url, *args, **kwargs):
168168
with patch.object(requests, "get", _patch_request_get):
169169
new_template.button_sync_template()
170170
self.assertEqual(new_template.footer, "Footer changed")
171+
172+
def test_prepare_values_template_send(self):
173+
partner = self.env["res.partner"].create(
174+
{
175+
"name": "Ada Lovelace",
176+
"mobile": "+34900111222",
177+
}
178+
)
179+
ctx = {
180+
"default_res_model": partner._name,
181+
"default_res_id": partner.id,
182+
}
183+
tmpl = self.env["mail.whatsapp.template"].create(
184+
{
185+
"name": "Test Render",
186+
"category": "utility",
187+
"language": "es",
188+
"header": "Hi {{1}}",
189+
"body": "Name: {{1}} · Tel: {{2}}",
190+
"gateway_id": self.gateway.id,
191+
"variable_ids": [Command.clear()],
192+
"state": "approved",
193+
"is_supported": True,
194+
"model_id": self.env["ir.model"]._get("res.partner").id,
195+
}
196+
)
197+
self.env["mail.whatsapp.template.variable"].create(
198+
{
199+
"name": "{{1}}",
200+
"line_type": "header",
201+
"template_id": tmpl.id,
202+
"field_name": "name",
203+
}
204+
)
205+
self.env["mail.whatsapp.template.variable"].create(
206+
{
207+
"name": "{{1}}",
208+
"line_type": "body",
209+
"template_id": tmpl.id,
210+
"field_name": "name",
211+
}
212+
)
213+
self.env["mail.whatsapp.template.variable"].create(
214+
{
215+
"name": "{{2}}",
216+
"line_type": "body",
217+
"template_id": tmpl.id,
218+
"field_name": "mobile",
219+
}
220+
)
221+
self.env["mail.whatsapp.template.button"].create(
222+
{
223+
"name": "mobile",
224+
"button_type": "phone_number",
225+
"template_id": tmpl.id,
226+
"call_number": "+34666555444",
227+
}
228+
)
229+
components = tmpl.with_context(**ctx).prepare_value_to_send()
230+
231+
header = next(c for c in components if c["type"].upper() == "HEADER")
232+
self.assertEqual([p["type"] for p in header.get("parameters", [])], ["text"])
233+
self.assertEqual(header["parameters"][0]["text"], partner.name)
234+
235+
body = next(c for c in components if c["type"].upper() == "BODY")
236+
self.assertEqual(
237+
[p["type"] for p in body.get("parameters", [])], ["text", "text"]
238+
)
239+
self.assertEqual(body["parameters"][0]["text"], partner.name)
240+
self.assertEqual(body["parameters"][1]["text"], partner.mobile or "")

mail_gateway_whatsapp/tools/const.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,5 @@
7373
("vi", "Vietnamese"),
7474
("zu", "Zulu"),
7575
]
76+
77+
REG_VARIABLE = r"{{[1-9][0-9]*}}"

mail_gateway_whatsapp/views/mail_whatsapp_template_views.xml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
readonly="state != 'draft'"
8585
options="{'no_create': True}"
8686
/>
87+
<field name="model_id" />
8788
<field name="category" readonly="state != 'draft'" />
8889
<field name="language" readonly="state != 'draft'" />
8990
</group>
@@ -104,6 +105,59 @@
104105
<page name="body" string="Body">
105106
<field name="body" readonly="state != 'draft'" />
106107
</page>
108+
<page name="buttons" string="Buttons">
109+
<field name="button_ids" readonly="state != 'draft'">
110+
<list editable="bottom">
111+
<field name="sequence" widget="handle" />
112+
<field name="button_type" />
113+
<field
114+
name="url_type"
115+
readonly="button_type != 'url'"
116+
required="button_type == 'url'"
117+
invisible="button_type != 'url'"
118+
/>
119+
<field name="name" required="1" />
120+
<field
121+
name="website_url"
122+
readonly="button_type != 'url'"
123+
required="button_type == 'url'"
124+
invisible="button_type != 'url'"
125+
placeholder="e.g. https://www.example.com"
126+
/>
127+
<field
128+
name="call_number"
129+
readonly="button_type != 'phone_number'"
130+
required="button_type == 'phone_number'"
131+
invisible="button_type != 'phone_number'"
132+
/>
133+
</list>
134+
</field>
135+
</page>
136+
<page name="variables" string="Variables">
137+
<field name="variable_ids">
138+
<list editable="bottom" create="0" delete="0">
139+
<field name="display_name" string="Name" />
140+
<field name="sample_value" required="1" />
141+
<field
142+
name="field_name"
143+
widget="DynamicModelFieldSelectorChar"
144+
required="1"
145+
options="{'model': 'model'}"
146+
/>
147+
<field name="model" column_invisible="True" />
148+
<field
149+
name="line_type"
150+
column_invisible="True"
151+
force_save="1"
152+
/>
153+
<field
154+
name="name"
155+
column_invisible="True"
156+
force_save="1"
157+
/>
158+
</list>
159+
</field>
160+
</page>
107161
</notebook>
108162
</sheet>
109163
</form>

mail_gateway_whatsapp/wizards/mail_compose_gateway_message.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,22 @@ class MailComposeGatewayMessage(models.TransientModel):
1313
"mail.whatsapp.template",
1414
domain="""[
1515
('state', '=', 'approved'),
16-
('is_supported', '=', True)
16+
('is_supported', '=', True),
17+
('model', '=', model)
1718
]""",
1819
)
1920

2021
@api.onchange("whatsapp_template_id")
2122
def onchange_whatsapp_template_id(self):
2223
if self.whatsapp_template_id:
23-
self.body = markupsafe.Markup(self.whatsapp_template_id.body)
24+
self.body = markupsafe.Markup(
25+
self.whatsapp_template_id.render_body_message()
26+
)
2427

2528
def _action_send_mail(self, auto_commit=False):
2629
if self.whatsapp_template_id:
27-
self = self.with_context(whatsapp_template_id=self.whatsapp_template_id.id)
30+
self = self.with_context(
31+
whatsapp_template_id=self.whatsapp_template_id.id,
32+
res_id=int(self.res_ids.strip("[]")),
33+
)
2834
return super()._action_send_mail(auto_commit=auto_commit)

0 commit comments

Comments
 (0)