Skip to content

Commit 1dff49c

Browse files
committed
[FIX] purchase_request: render HTML notification body correctly
The purchase order confirmation notification posted on the purchase request rendered its <h3>/<ul>/<li> tags as literal text in Odoo 19. The body was built as a plain str while html_escape() (which returns a Markup) was passed as an argument to self.env._(). In Odoo 19 the translation function escapes the whole translated string when any of its arguments is a Markup (odoo/tools/translate.py), so the surrounding HTML markup ended up escaped too. Build the message with Markup().format() from the start, keeping the translatable strings free of HTML and applying html_escape() outside of self.env._(), mirroring the sibling _purchase_request_confirm_done_message_content method. The same broken pattern in purchase_request_allocation is fixed as well.
1 parent 5a8cea8 commit 1dff49c

4 files changed

Lines changed: 45 additions & 36 deletions

File tree

purchase_request/__manifest__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
{
55
"name": "Purchase Request",
66
"author": "ForgeFlow, Odoo Community Association (OCA)",
7-
"version": "19.0.1.0.1",
7+
"version": "19.0.1.0.2",
88
"summary": "Use this module to have notification of requirements of "
99
"materials and/or external services and keep track of such "
1010
"requirements.",

purchase_request/i18n/purchase_request.pot

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,17 @@
44
#
55
msgid ""
66
msgstr ""
7-
"Project-Id-Version: Odoo Server 19.0\n"
7+
"Project-Id-Version: Odoo Server 19.0+e\n"
88
"Report-Msgid-Bugs-To: \n"
9+
"POT-Creation-Date: 2026-06-29 20:25+0000\n"
10+
"PO-Revision-Date: 2026-06-29 20:25+0000\n"
911
"Last-Translator: \n"
1012
"Language-Team: \n"
1113
"MIME-Version: 1.0\n"
1214
"Content-Type: text/plain; charset=UTF-8\n"
1315
"Content-Transfer-Encoding: \n"
1416
"Plural-Forms: \n"
1517

16-
#. module: purchase_request
17-
#. odoo-python
18-
#: code:addons/purchase_request/models/purchase_order.py:0
19-
msgid ""
20-
"<li><b>%(prl_name)s</b>: Ordered quantity %(prl_qty)s %(prl_uom)s, Planned "
21-
"date %(prl_date_planned)s</li>"
22-
msgstr ""
23-
24-
#. module: purchase_request
25-
#. odoo-python
26-
#: code:addons/purchase_request/models/purchase_request_allocation.py:0
27-
msgid ""
28-
"<li><b>%(product_name)s</b>: Received quantity %(product_qty)s "
29-
"%(product_uom)s</li>"
30-
msgstr ""
31-
3218
#. module: purchase_request
3319
#: model_terms:ir.ui.view,arch_db:purchase_request.report_purchase_request
3420
msgid "<strong>Analytic Distribution</strong>"
@@ -482,7 +468,9 @@ msgstr ""
482468

483469
#. module: purchase_request
484470
#: model:ir.model.fields,help:purchase_request.field_purchase_request__message_has_error
471+
#: model:ir.model.fields,help:purchase_request.field_purchase_request__message_has_sms_error
485472
#: model:ir.model.fields,help:purchase_request.field_purchase_request_line__message_has_error
473+
#: model:ir.model.fields,help:purchase_request.field_purchase_request_line__message_has_sms_error
486474
msgid "If checked, some messages have a delivery error."
487475
msgstr ""
488476

@@ -662,6 +650,13 @@ msgstr ""
662650
msgid "Order confirmation %(po_name)s for your Request %(pr_name)s"
663651
msgstr ""
664652

653+
#. module: purchase_request
654+
#. odoo-python
655+
#: code:addons/purchase_request/models/purchase_order.py:0
656+
msgid ""
657+
"Ordered quantity %(prl_qty)s %(prl_uom)s, Planned date %(prl_date_planned)s"
658+
msgstr ""
659+
665660
#. module: purchase_request
666661
#: model:ir.model.fields,field_description:purchase_request.field_purchase_request_line__orderpoint_id
667662
msgid "Orderpoint"
@@ -1014,6 +1009,12 @@ msgstr ""
10141009
msgid "Received quantity"
10151010
msgstr ""
10161011

1012+
#. module: purchase_request
1013+
#. odoo-python
1014+
#: code:addons/purchase_request/models/purchase_request_allocation.py:0
1015+
msgid "Received quantity %(product_qty)s %(product_uom)s"
1016+
msgstr ""
1017+
10171018
#. module: purchase_request
10181019
#: model_terms:ir.ui.view,arch_db:purchase_request.view_purchase_request_form
10191020
msgid "Reject"
@@ -1124,6 +1125,12 @@ msgstr ""
11241125
msgid "Responsible User"
11251126
msgstr ""
11261127

1128+
#. module: purchase_request
1129+
#: model:ir.model.fields,field_description:purchase_request.field_purchase_request__message_has_sms_error
1130+
#: model:ir.model.fields,field_description:purchase_request.field_purchase_request_line__message_has_sms_error
1131+
msgid "SMS Delivery error"
1132+
msgstr ""
1133+
11271134
#. module: purchase_request
11281135
#: model_terms:ir.ui.view,arch_db:purchase_request.purchase_order_line_search_sub
11291136
msgid "Search Purchase Order Line"

purchase_request/models/purchase_order.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,27 @@ def _purchase_request_confirm_message_content(self, request, request_dict=None):
1919
po_name=self.name,
2020
pr_name=request.name,
2121
)
22-
message = f"<h3>{title}</h3><ul>"
23-
message += self.env._(
22+
intro = self.env._(
2423
"The following requested items from Purchase Request %(pr_name)s "
2524
"have now been confirmed in Purchase Order %(po_name)s:",
2625
po_name=self.name,
2726
pr_name=request.name,
2827
)
28+
message = Markup("<h3>{}</h3>{}<ul>").format(title, intro)
2929

3030
for line in request_dict.values():
31-
message += self.env._(
32-
"<li><b>%(prl_name)s</b>: Ordered quantity %(prl_qty)s %(prl_uom)s, "
33-
"Planned date %(prl_date_planned)s</li>",
34-
prl_name=html_escape(line["name"]),
31+
line_text = self.env._(
32+
"Ordered quantity %(prl_qty)s %(prl_uom)s, "
33+
"Planned date %(prl_date_planned)s",
3534
prl_qty=line["product_qty"],
3635
prl_uom=line["product_uom"],
3736
prl_date_planned=line["date_planned"],
3837
)
39-
message += "</ul>"
38+
message += Markup("<li><b>{}</b>: {}</li>").format(
39+
html_escape(line["name"]),
40+
line_text,
41+
)
42+
message += Markup("</ul>")
4043
return message
4144

4245
def _purchase_request_confirm_message(self):
@@ -62,7 +65,7 @@ def _purchase_request_confirm_message(self):
6265
request, requests_dict[request_id]
6366
)
6467
request.message_post(
65-
body=Markup(message),
68+
body=message,
6669
subtype_id=self.env.ref(
6770
"purchase_request.mt_request_po_confirmed"
6871
).id,

purchase_request/models/purchase_request_allocation.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -96,21 +96,20 @@ def _compute_open_product_qty(self):
9696

9797
@api.model
9898
def _purchase_request_confirm_done_message_content(self, message_data):
99-
message = ""
100-
message += self.env._(
99+
message_body = self.env._(
101100
"From last reception this quantity has been "
102101
"allocated to this purchase request"
103102
)
104-
message += "<ul>"
105-
message += self.env._(
106-
"<li><b>%(product_name)s</b>: "
107-
"Received quantity %(product_qty)s %(product_uom)s</li>",
108-
product_name=html_escape(message_data["product_name"]),
103+
line_text = self.env._(
104+
"Received quantity %(product_qty)s %(product_uom)s",
109105
product_qty=message_data["product_qty"],
110106
product_uom=message_data["product_uom"],
111107
)
112-
message += "</ul>"
113-
return message
108+
product_line = Markup("<ul><li><b>{}</b>: {}</li></ul>").format(
109+
html_escape(message_data["product_name"]),
110+
line_text,
111+
)
112+
return Markup("{}{}").format(message_body, product_line)
114113

115114
def _prepare_message_data(self, po_line, request, allocated_qty):
116115
return {
@@ -130,6 +129,6 @@ def _notify_allocation(self, allocated_qty):
130129
message_data = self._prepare_message_data(po_line, request, allocated_qty)
131130
message = self._purchase_request_confirm_done_message_content(message_data)
132131
request.message_post(
133-
body=Markup(message),
132+
body=message,
134133
subtype_id=self.env.ref("mail.mt_note").id,
135134
)

0 commit comments

Comments
 (0)