Files
odoo_source/addons/project/models/project_task_recurrence.py
T
Audric Onockx (auon) 85e9290711 [IMP] *: simplify recurrence
*=hr_timesheet,project,sale_project,sale_timesheet

Currently, the configuration of the recurrence is quite complete
and allows a lot of flexibility, but it is costly
in terms of implementation as it requires a lot of fields.
The goal of this task is thus to simplify this implementation.

In addition, a lot of people are complaining
that tasks are only generated when the recurrence date is reached,
as it doesn't allow to anticipate the planning of field service tasks.
In this task, we are thus going to immediately generate a new task
once the previous one is marked as done.

Concretely, we:
- describe a recurrence in terms of
"once every n day/week/month/year for ever/until a date"
and delete all fields that don't fit into it.

- remove the cron. The new occurrence is created when
marking the last task as done, and copied from the latter.
Deleting the last task deletes the recurrence.
The recurrence fields stay useful, as they form a delta t
that will be added to deadline/planned dates fields to get the new
values.

- use an boolean icon button to activate recurrence,
and place it at the end of deadline field's line.
The recurrence fields appear on the next line.

- delete in the form: the div explaining when the next tasks will be
created and the header to choose how to save the changes in the
recurrence.

task-3084945

closes odoo/odoo#112764

Related: odoo/upgrade#4343
Related: odoo/enterprise#37114
Signed-off-by: Xavier Bol (xbo) <xbo@odoo.com>
2023-03-13 17:45:21 +01:00

112 lines
3.9 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',
'partner_phone',
'planned_hours',
'project_id',
'display_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 {
record['recurrence_id'][0]: record['max_id']
for record in self.env['project.task'].sudo()._read_group(
[('recurrence_id', 'in', self.ids)],
['max_id:max(id)'],
['recurrence_id'],
)
}
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()
if occurrence_from.allow_recurring_tasks:
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
if occurrence_from.allow_subtasks:
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