This commit reviews the UX and UI in Project app and improves the usability of the
new features recently added (subtasks list in kanban card, state field replacing
kanban_state field, etc).
The following changes are made in this commit:
- swap of user_id icon in the kanban box and the removal of the allow_unassign field and setting
- change the ordering of the fields in the subtask list
- make small layout changes in the project.task and project.project kanban cards
- remove the "lock-icon private" sub-title of private tasks, replace it with a
little lock icon on the bottom right icons of the kanban card.
- remove the break tag in the project.task kanban card that was unecessary
given the new display and margin style settings
- lock icon is bigger
- kanban icons are better aligned
- state is as big as avatar
- remove the 'remaining hours on SO' field
- deadline field will be optional and hidden by default in project.task list view
- remove the rating field
- add stage field as optional in project.task list views
- priority and state fields will no longer be optional
- stage_id will be copied when duplicating a task, except when the task is
generated through the recurrence
- remove the tooltip of the tag_ids field
- When duplicating a task having sub-tasks, '(copy)' is no longer
added to the name of the sub-tasks of this task.
- project.task kanban view:
* (+ x tasks) mention next to the name is removed
* The caret is replaced with 'fa-check-square-o x/y'
which will represent the number of sub-tasks closed
compared to the total number of sub-tasks
* Only open subtasks are displayed
* When changing the state of a sub-task to a closing one,
the sub-task is muted and removed from the list on the view reload
* The name of the parent task on the kanban card of sub-tasks is
displayed except when viewing the sub-tasks of a particular task
through the sub-tasks stat button
* project.project kanban view: the fa-check-square-o icon of
milestones is replaced with fa-flag-o
* project.task kanban card: the fa-play and fa-pause icons
are moved on the right of the remaining hours widge.
* Allow users to edit the stage_id in batch from the list view of tasks
if all of the selected tasks are part of the same project.
* the state will have the same size as the avatar
* change the opacity of the tasks that are closed
* state is at the right of subtask list
* the striked should be replaced by the opacity on the kanban card
Enterprise PR: odoo/enterprise#38132
Task-3229873
closes odoo/odoo#116628
X-original-commit: 09b5d5843096d27b5a4ab0f603ad44bdb2e74723
Related: odoo/upgrade#4478
Related: odoo/enterprise#38771
Signed-off-by: Xavier Bol (xbo) <xbo@odoo.com>
Co-authored-by: Panagiotis Kyriakou <paky@odoo.com>
Co-authored-by: Bastien (bvdn) <bvdn@odoo.com>
109 lines
3.8 KiB
Python
109 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',
|
|
'planned_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 {
|
|
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
|
|
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
|