From 969f1b1abad9813846b54ef01054bee8367035cb Mon Sep 17 00:00:00 2001 From: Carmine Date: Wed, 29 Oct 2025 16:56:25 +0100 Subject: [PATCH 1/2] [IMP] website_sale: automatic rating request Add a new rating email template and the _get_rating_request_template method to fetch it. Implement a scheduled cron _cron_send_order_rating_emails to automatically send feedback requests for orders confirmed a configurable number of days ago. Include settings to enable the feature, set the delay, and select the email template, integrating these with website configuration. The email sending logic respects user settings and prevents duplicate emails. task-5129654 --- addons/website_sale/data/ir_cron_data.xml | 8 +++ .../website_sale/data/mail_template_data.xml | 55 +++++++++++++++++++ .../models/res_config_settings.py | 18 ++++++ addons/website_sale/models/sale_order.py | 38 ++++++++++++- .../views/res_config_settings_views.xml | 13 +++++ 5 files changed, 131 insertions(+), 1 deletion(-) diff --git a/addons/website_sale/data/ir_cron_data.xml b/addons/website_sale/data/ir_cron_data.xml index 084d497903fef..3b217891b22b4 100644 --- a/addons/website_sale/data/ir_cron_data.xml +++ b/addons/website_sale/data/ir_cron_data.xml @@ -10,4 +10,12 @@ code + + eCommerce: send order rating request emails + 1 + days + + model._cron_send_order_rating_emails() + code + diff --git a/addons/website_sale/data/mail_template_data.xml b/addons/website_sale/data/mail_template_data.xml index 127a1257b74b6..4eb8b3f667016 100644 --- a/addons/website_sale/data/mail_template_data.xml +++ b/addons/website_sale/data/mail_template_data.xml @@ -210,5 +210,60 @@ + + Ecommerce: Order Rating Request + + Review your recent order! + {{ (object.user_id.email_formatted or object.company_id.email_formatted or user.email_formatted) }} + + + Sent to customers to request feedback on their order + mail.mail_notification_light + +
+

+ Hello , +
+ Take a minute to rate your order! +
+ +

--
+ +
+ + + + + + + + +
+ Product image + +
+ +
+
+ +
+
+ + Review Now + +
+
+

+
+
+ + +
diff --git a/addons/website_sale/models/res_config_settings.py b/addons/website_sale/models/res_config_settings.py index d6b6434481aa4..1f1052b5351cf 100644 --- a/addons/website_sale/models/res_config_settings.py +++ b/addons/website_sale/models/res_config_settings.py @@ -67,6 +67,21 @@ class ResConfigSettings(models.TransientModel): confirmation_email_template_id = fields.Many2one( related='website_id.confirmation_email_template_id', readonly=False ) + send_order_rating_emails = fields.Boolean( + string="Request ratings", + config_parameter='website_sale.send_order_rating_emails', + ) + rating_email_days = fields.Integer( + string="Days After Order to Send Rating Email", + default=5, + config_parameter='website_sale.rating_email_days', + ) + rating_email_template_id = fields.Many2one( + string="Email", + comodel_name='mail.template', + default=lambda self: self.env.ref('website_sale.mail_template_sale_order_rating', raise_if_not_found=False), + config_parameter='website_sale.rating_email_template_id', + ) # Additional settings account_on_checkout = fields.Selection( @@ -120,6 +135,9 @@ def set_values(self): ) ): website._populate_product_feeds() + view = self.env['ir.ui.view'].search([('key', '=', 'website_sale.product_comment')], limit=1) + if view and self.send_order_rating_emails: + view.active = True # === ACTION METHODS === # diff --git a/addons/website_sale/models/sale_order.py b/addons/website_sale/models/sale_order.py index 8765f94e060a1..23f6c4dd90af8 100644 --- a/addons/website_sale/models/sale_order.py +++ b/addons/website_sale/models/sale_order.py @@ -1,7 +1,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. import random -from datetime import datetime +from datetime import datetime, timedelta from dateutil.relativedelta import relativedelta @@ -46,6 +46,7 @@ class SaleOrder(models.Model): is_abandoned_cart = fields.Boolean( string="Abandoned Cart", compute='_compute_abandoned_cart', search='_search_abandoned_cart', ) + rating_email_sent = fields.Boolean(string="Rating email already sent") #=== COMPUTE METHODS ===# @@ -846,6 +847,41 @@ def _get_delivery_methods(self): *self.env['delivery.carrier']._check_company_domain(self.company_id), ]).filtered(lambda carrier: carrier._is_available_for_order(self)) + @api.model + def _cron_send_order_rating_emails(self): + """Send rating request emails to customers a few days after order.""" + send_order_rating_emails = self.env['ir.config_parameter'].sudo().get_bool('website_sale.send_order_rating_emails') + if not send_order_rating_emails: + return + + days = max(0, self.env['ir.config_parameter'].sudo().get_int('website_sale.rating_email_days')) + date_limit = fields.Datetime.now() - timedelta(days=days) + rating_orders = self.search([ + ('state', '=', 'sale'), + ('date_order', '>=', date_limit.replace(hour=0, minute=0, second=0)), + ('date_order', '<=', date_limit.replace(hour=23, minute=59, second=59)), + ('website_id', '!=', False), + ('rating_email_sent', '=', False), + ]) + + for order in rating_orders: + mail_template = order._get_rating_request_template() + if mail_template: + mail_template.send_mail(order.id) + order.rating_email_sent = True + + def _get_rating_request_template(self): + self.ensure_one() + + rating_email_template_id = self.env['ir.config_parameter'].sudo().get_int( + 'website_sale.rating_email_template_id' + ) + rating_email_template = self.env['mail.template'].browse(rating_email_template_id).exists() + if rating_email_template: + return rating_email_template + + return self.env.ref('website_sale.mail_template_sale_order_rating', raise_if_not_found=False) + #=== TOOLING ===# def _is_anonymous_cart(self): diff --git a/addons/website_sale/views/res_config_settings_views.xml b/addons/website_sale/views/res_config_settings_views.xml index dcc9035c63bfd..495397c961ce1 100644 --- a/addons/website_sale/views/res_config_settings_views.xml +++ b/addons/website_sale/views/res_config_settings_views.xml @@ -216,6 +216,19 @@