From d97e69ff0241af7403eef4a01f4dcf3f4e06963e Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 17 Aug 2018 01:49:22 +0200 Subject: [PATCH] [FIX] mail: schedule activity with user's today The activity was incorrectly scheduled with a deadline as the now in UTC while the state was computed using "today" in the user's timezone. This made the nightly test fail (run at 23:30 UTC with the user in UTC+2). Use context_today instead of today for all activity_schedule calls To make it easier, use on the context_today default value instead of setting context_today everywhere (which is more error prone in the future) Never use datetime, only date objects add a warning to detect potential errors when passing a datetime Closes #26411 --- addons/hr_expense/models/hr_expense.py | 2 +- addons/hr_holidays/models/hr_leave.py | 4 ++-- addons/hr_holidays/models/hr_leave_allocation.py | 4 ++-- addons/mail/models/ir_actions.py | 3 +-- addons/mail/models/mail_activity.py | 10 +++++++++- addons/sale/models/sale.py | 2 +- addons/sale_purchase/models/purchase_order.py | 2 +- addons/sale_purchase/models/sale_order.py | 4 ++-- 8 files changed, 19 insertions(+), 12 deletions(-) diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py index a3eeae84146..cd5a123c691 100644 --- a/addons/hr_expense/models/hr_expense.py +++ b/addons/hr_expense/models/hr_expense.py @@ -721,7 +721,7 @@ class HrExpenseSheet(models.Model): def activity_update(self): for expense_report in self.filtered(lambda hol: hol.state == 'submit'): self.activity_schedule( - 'hr_expense.mail_act_expense_approval', fields.Date.today(), + 'hr_expense.mail_act_expense_approval', user_id=expense_report._get_responsible_for_approval().id) self.filtered(lambda hol: hol.state == 'approve').activity_feedback(['hr_expense.mail_act_expense_approval']) self.filtered(lambda hol: hol.state == 'cancel').activity_unlink(['hr_expense.mail_act_expense_approval']) diff --git a/addons/hr_holidays/models/hr_leave.py b/addons/hr_holidays/models/hr_leave.py index ad8f6aeb8f9..558194e82ad 100644 --- a/addons/hr_holidays/models/hr_leave.py +++ b/addons/hr_holidays/models/hr_leave.py @@ -621,12 +621,12 @@ class HolidaysRequest(models.Model): to_clean |= holiday elif holiday.state == 'confirm': holiday.activity_schedule( - 'hr_holidays.mail_act_leave_approval', fields.Date.today(), + 'hr_holidays.mail_act_leave_approval', user_id=holiday._get_responsible_for_approval().id) elif holiday.state == 'validate1': holiday.activity_feedback(['hr_holidays.mail_act_leave_approval']) holiday.activity_schedule( - 'hr_holidays.mail_act_leave_second_approval', fields.Date.today(), + 'hr_holidays.mail_act_leave_second_approval', user_id=holiday._get_responsible_for_approval().id) elif holiday.state == 'validate': to_do |= holiday diff --git a/addons/hr_holidays/models/hr_leave_allocation.py b/addons/hr_holidays/models/hr_leave_allocation.py index c643f8b76c5..ffaa77d624e 100644 --- a/addons/hr_holidays/models/hr_leave_allocation.py +++ b/addons/hr_holidays/models/hr_leave_allocation.py @@ -466,12 +466,12 @@ class HolidaysAllocation(models.Model): to_clean |= allocation elif allocation.state == 'confirm': allocation.activity_schedule( - 'hr_holidays.mail_act_leave_allocation_approval', fields.Date.today(), + 'hr_holidays.mail_act_leave_allocation_approval', user_id=allocation._get_responsible_for_approval().id) elif allocation.state == 'validate1': allocation.activity_feedback(['hr_holidays.mail_act_leave_allocation_approval']) allocation.activity_schedule( - 'hr_holidays.mail_act_leave_allocation_second_approval', fields.Date.today(), + 'hr_holidays.mail_act_leave_allocation_second_approval', user_id=allocation._get_responsible_for_approval().id) elif allocation.state == 'validate': to_do |= allocation diff --git a/addons/mail/models/ir_actions.py b/addons/mail/models/ir_actions.py index 16abdfeaf8e..8c84144a12a 100644 --- a/addons/mail/models/ir_actions.py +++ b/addons/mail/models/ir_actions.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from datetime import date from dateutil.relativedelta import relativedelta from odoo import _, api, fields, models @@ -96,7 +95,7 @@ class ServerActions(models.Model): 'activity_type_id': action.activity_type_id.id, } if action.activity_date_deadline_range > 0: - vals['date_deadline'] = date.today() + relativedelta(**{action.activity_date_deadline_range_type: action.activity_date_deadline_range}) + vals['date_deadline'] = fields.Date.context_today(action) + relativedelta(**{action.activity_date_deadline_range_type: action.activity_date_deadline_range}) for record in records: if action.activity_user_type == 'specific': user = action.activity_user_id diff --git a/addons/mail/models/mail_activity.py b/addons/mail/models/mail_activity.py index 6d7b94ad9a3..521caa9eb6b 100644 --- a/addons/mail/models/mail_activity.py +++ b/addons/mail/models/mail_activity.py @@ -4,12 +4,15 @@ from collections import defaultdict from datetime import date, datetime from dateutil.relativedelta import relativedelta +import logging import pytz from odoo import api, exceptions, fields, models, _ from odoo.tools import pycompat +_logger = logging.getLogger(__name__) + class MailActivityType(models.Model): """ Activity Types are used to categorize activities. Each type is a different @@ -579,12 +582,17 @@ class MailActivityMixin(models.AbstractModel): xml_id of activity type instead of directly giving an activity_type_id. It is useful to avoid having various "env.ref" in the code and allow to let the mixin handle access rights. + + :param date_deadline: the day the activity must be scheduled on + the timezone of the user must be considered to set the correct deadline """ if self.env.context.get('mail_activity_automation_skip'): return False if not date_deadline: - date_deadline = fields.Date.today() + date_deadline = fields.Date.context_today(self) + if isinstance(date_deadline, datetime): + _logger.warning("Scheduled deadline should be a date (got %s)", date_deadline) if act_type_xmlid: activity_type = self.sudo().env.ref(act_type_xmlid) else: diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py index 9fddb42b2af..dc91312706d 100644 --- a/addons/sale/models/sale.py +++ b/addons/sale/models/sale.py @@ -366,7 +366,7 @@ class SaleOrder(models.Model): if values['invoice_status'] == 'upselling': for order in self: order.activity_schedule( - 'sale.mail_act_sale_upsell', fields.Date.today(), + 'sale.mail_act_sale_upsell', user_id=order.user_id.id, note=_("Upsell %s for customer %s") % ( order._name, order.id, order.name, diff --git a/addons/sale_purchase/models/purchase_order.py b/addons/sale_purchase/models/purchase_order.py index 6ec184055f7..f54d4ab3490 100644 --- a/addons/sale_purchase/models/purchase_order.py +++ b/addons/sale_purchase/models/purchase_order.py @@ -27,7 +27,7 @@ class PurchaseOrder(models.Model): sale_to_notify_map[sale_order] |= purchase_line for sale_order, purchase_order_lines in sale_to_notify_map.items(): - sale_order.activity_schedule_with_view('mail.mail_activity_data_warning', fields.Datetime.now(), + sale_order.activity_schedule_with_view('mail.mail_activity_data_warning', user_id=sale_order.user_id.id or self.env.uid, views_or_xmlid='sale_purchase.exception_sale_on_purchase_cancellation', render_context={ diff --git a/addons/sale_purchase/models/sale_order.py b/addons/sale_purchase/models/sale_order.py index ebd4895c7c4..cda7e825b64 100644 --- a/addons/sale_purchase/models/sale_order.py +++ b/addons/sale_purchase/models/sale_order.py @@ -58,7 +58,7 @@ class SaleOrder(models.Model): purchase_to_notify_map[purchase_line.order_id] |= purchase_line.sale_line_id for purchase_order, sale_order_lines in purchase_to_notify_map.items(): - purchase_order.activity_schedule_with_view('mail.mail_activity_data_warning', fields.Datetime.now(), + purchase_order.activity_schedule_with_view('mail.mail_activity_data_warning', user_id=purchase_order.user_id.id or self.env.uid, views_or_xmlid='sale_purchase.exception_purchase_on_sale_cancellation', render_context={ @@ -151,7 +151,7 @@ class SaleOrderLine(models.Model): 'sale_orders': sale_lines.mapped('order_id'), 'origin_values': origin_values, } - purchase_order.activity_schedule_with_view('mail.mail_activity_data_warning', fields.Datetime.now(), + purchase_order.activity_schedule_with_view('mail.mail_activity_data_warning', user_id=purchase_order.user_id.id or self.env.uid, views_or_xmlid='sale_purchase.exception_purchase_on_sale_quantity_decreased', render_context=render_context)