Files
odoo_source/addons/project/models/project_task_recurrence.py
T
Abdelouahab (abla) e94a1dd2f9 [FIX] project: fix closing recurring task
to reproduce:
=============
- make a task recureent
- change its state to a closing one
-> Validation Error

Problem:
========
- when creating the next occurence, we insert the followers of the original
task in the new one, but it happens that we create a `mail_followers` twice
which violates an SQL constraint of `mail_followers`.
https://github.com/odoo/odoo/blob/ea170be9089ee6c784664475ac4ec1218d23dccd/addons/mail/models/mail_thread.py#L253-L262

the create method will create `mail_followers` for the task and as the condition
that follows is truthy in this use-case `_insert_followers` will be called
and create `mail_followers` again.

- In previous versions we didn't face this issue because the creation of
occurences was done by a cron, so the condition was never truthy thanks to
`and self.env.user.active`.

Solution:
=========
to make sure the conditon stays falsy, we set `mail_create_nosubscribe`
in context to `True` when creating the next occurence.

opw-3742737

closes odoo/odoo#156945

X-original-commit: 64ca453bc36740ce4afcd46c1afbbb31abffdf7c
Signed-off-by: Xavier Bol (xbo) <xbo@odoo.com>
Signed-off-by: Abdelouahab Laaroussi (abla) <abla@odoo.com>
2024-03-08 09:42:57 +00:00

110 lines
3.8 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import _, api, fields, models, Command
from odoo.exceptions import ValidationError
from dateutil.relativedelta import relativedelta
class ProjectTaskRecurrence(models.Model):
_name = 'project.task.recurrence'
_description = 'Task Recurrence'
task_ids = fields.One2many('project.task', 'recurrence_id', copy=False)
repeat_interval = fields.Integer(string='Repeat Every', default=1)
repeat_unit = fields.Selection([
('day', 'Days'),
('week', 'Weeks'),
('month', 'Months'),
('year', 'Years'),
], default='week')
repeat_type = fields.Selection([
('forever', 'Forever'),
('until', 'Until'),
], default="forever", string="Until")
repeat_until = fields.Date(string="End Date")
@api.constrains('repeat_interval')
def _check_repeat_interval(self):
if self.filtered(lambda t: t.repeat_interval <= 0):
raise ValidationError(_('The interval should be greater than 0'))
@api.constrains('repeat_type', 'repeat_until')
def _check_repeat_until_date(self):
today = fields.Date.today()
if self.filtered(lambda t: t.repeat_type == 'until' and t.repeat_until < today):
raise ValidationError(_('The end date should be in the future'))
@api.model
def _get_recurring_fields_to_copy(self):
return [
'analytic_account_id',
'company_id',
'description',
'displayed_image_id',
'email_cc',
'message_partner_ids',
'name',
'parent_id',
'partner_id',
'allocated_hours',
'project_id',
'project_privacy_visibility',
'recurrence_id',
'recurring_task',
'sequence',
'tag_ids',
'user_ids',
]
@api.model
def _get_recurring_fields_to_postpone(self):
return [
'date_deadline',
]
def _get_last_task_id_per_recurrence_id(self):
return {} if not self else {
recurrence.id: max_task_id
for recurrence, max_task_id in self.env['project.task'].sudo()._read_group(
[('recurrence_id', 'in', self.ids)],
['recurrence_id'],
['id:max'],
)
}
def _get_recurrence_delta(self):
return relativedelta(**{
f"{self.repeat_unit}s": self.repeat_interval
})
def _create_next_occurrence(self, occurrence_from):
self.ensure_one()
# Prevent double mail_followers creation
self = self.with_context(mail_create_nosubscribe=True)
self.env['project.task'].sudo().create(
self._create_next_occurrence_values(occurrence_from)
)
def _create_next_occurrence_values(self, occurrence_from):
self.ensure_one()
fields_to_copy = occurrence_from.read(self._get_recurring_fields_to_copy()).pop()
create_values = {
field: value[0] if isinstance(value, tuple) else value
for field, value in fields_to_copy.items()
}
fields_to_postpone = occurrence_from.read(self._get_recurring_fields_to_postpone()).pop()
fields_to_postpone.pop('id', None)
create_values.update({
field: value and value + self._get_recurrence_delta()
for field, value in fields_to_postpone.items()
})
create_values['stage_id'] = occurrence_from.project_id.type_ids[0].id if occurrence_from.project_id.type_ids else occurrence_from.stage_id.id
create_values['child_ids'] = [
Command.create(self._create_next_occurrence_values(child)) for child in occurrence_from.with_context(active_test=False).child_ids
]
return create_values