Skip to content

Commit

Permalink
[IMP] project: improve ux
Browse files Browse the repository at this point in the history
Before this committ:
- Stats buttons were hidden when records are zero
- Some wordings were insignificant
- Existing filters don't allow us to check blocked, blocking and tasks soon in overtime and some other information
- Product form fields need some changes in their order

After this commit:
- All stats button are displayed when records are zero
- Wordings fixed
- New filters added to check blocked, blocking tasks and tasks in overtime soon
- Fields were rearanged

task-2726465

closes #82253

Related: odoo/enterprise#23257
Related: odoo/upgrade#3185
Signed-off-by: Laurent Stukkens (ltu) <[email protected]>
  • Loading branch information
RaoufGhrissi committed Jun 2, 2022
1 parent ca200e2 commit 83760b9
Show file tree
Hide file tree
Showing 18 changed files with 119 additions and 115 deletions.
23 changes: 22 additions & 1 deletion addons/hr_timesheet/models/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from odoo import models, fields, api, _
from odoo.exceptions import UserError, ValidationError, RedirectWarning

from odoo.addons.rating.models.rating_data import OPERATOR_MAPPING

PROJECT_TASK_READABLE_FIELDS = {
'allow_subtasks',
Expand Down Expand Up @@ -252,6 +252,7 @@ class Task(models.Model):
analytic_account_active = fields.Boolean("Active Analytic Account", compute='_compute_analytic_account_active', compute_sudo=True)
allow_timesheets = fields.Boolean("Allow timesheets", related='project_id.allow_timesheets', help="Timesheets can be logged on this task.", readonly=True)
remaining_hours = fields.Float("Remaining Hours", compute='_compute_remaining_hours', store=True, readonly=True, help="Total remaining time, can be re-estimated periodically by the assignee of the task.")
remaining_hours_percentage = fields.Float(compute='_compute_remaining_hours_percentage', search='_search_remaining_hours_percentage')
effective_hours = fields.Float("Hours Spent", compute='_compute_effective_hours', compute_sudo=True, store=True, help="Time spent on this task, excluding its sub-tasks.")
total_hours_spent = fields.Float("Total Hours", compute='_compute_total_hours_spent', store=True, help="Time spent on this task, including its sub-tasks.")
progress = fields.Float("Progress", compute='_compute_progress_hours', store=True, group_operator="avg", help="Display progress of current task.")
Expand Down Expand Up @@ -301,6 +302,26 @@ def _compute_progress_hours(self):
task.progress = 0.0
task.overtime = 0

@api.depends('planned_hours', 'remaining_hours')
def _compute_remaining_hours_percentage(self):
for task in self:
if task.planned_hours > 0.0:
task.remaining_hours_percentage = task.remaining_hours / task.planned_hours
else:
task.remaining_hours_percentage = 0.0

def _search_remaining_hours_percentage(self, operator, value):
if operator not in OPERATOR_MAPPING:
raise NotImplementedError('This operator %s is not supported in this search method.' % operator)
query = f"""
SELECT id
FROM {self._table}
WHERE remaining_hours > 0
AND planned_hours > 0
AND remaining_hours / planned_hours {operator} %s
"""
return [('id', 'inselect', (query, (value,)))]

@api.depends('effective_hours', 'subtask_effective_hours', 'planned_hours')
def _compute_remaining_hours(self):
for task in self:
Expand Down
7 changes: 4 additions & 3 deletions addons/hr_timesheet/views/project_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,12 @@
<field name="inherit_id" ref="project.view_task_form2" />
<field name="groups_id" eval="[(6,0, (ref('hr_timesheet.group_hr_timesheet_user'),))]"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='child_ids']/tree/field[@name='kanban_state']" position="after">
<xpath expr="//field[@name='child_ids']/tree/field[@name='company_id']" position="after">
<field name="planned_hours" widget="timesheet_uom_no_toggle" sum="Initially Planned Hours" optional="hide"/>
<field name="effective_hours" widget="timesheet_uom" sum="Effective Hours" optional="hide"/>
<field name="remaining_hours" widget="timesheet_uom" sum="Remaining Hours" optional="hide" decoration-danger="progress &gt;= 100" decoration-warning="progress &gt;= 80 and progress &lt; 100"/>
<field name="subtask_effective_hours" string="Sub-tasks Hours Spent" widget="timesheet_uom" sum="Sub-tasks Hours Spent" optional="hide"/>
<field name="total_hours_spent" string="Total Hours" widget="timesheet_uom" sum="Total Hours" optional="hide"/>
<field name="remaining_hours" widget="timesheet_uom" sum="Remaining Hours" optional="hide" decoration-danger="progress &gt;= 100" decoration-warning="progress &gt;= 80 and progress &lt; 100"/>
<field name="progress" widget="progressbar" optional="hide"/>
</xpath>
<xpath expr="//notebook/page[@name='description_page']" position="after">
Expand Down Expand Up @@ -245,9 +245,9 @@
<field name="allow_subtasks" invisible="1" />
<field name="planned_hours" widget="timesheet_uom_no_toggle" sum="Initially Planned Hours" optional="hide"/>
<field name="effective_hours" widget="timesheet_uom" sum="Effective Hours" optional="hide"/>
<field name="remaining_hours" widget="timesheet_uom" sum="Remaining Hours" optional="hide" decoration-danger="progress &gt;= 100" decoration-warning="progress &gt;= 80 and progress &lt; 100"/>
<field name="subtask_effective_hours" widget="timesheet_uom" attrs="{'invisible' : [('allow_subtasks', '=', False)]}" optional="hide"/>
<field name="total_hours_spent" widget="timesheet_uom" attrs="{'invisible' : [('allow_subtasks', '=', False)]}" optional="hide"/>
<field name="remaining_hours" widget="timesheet_uom" sum="Remaining Hours" optional="hide" decoration-danger="progress &gt;= 100" decoration-warning="progress &gt;= 80 and progress &lt; 100"/>
<field name="progress" widget="progressbar" optional="hide" groups="hr_timesheet.group_hr_timesheet_user"/>
</xpath>
</field>
Expand Down Expand Up @@ -351,6 +351,7 @@
</xpath>
<xpath expr="//filter[@name='late']" position='after'>
<filter string="Tasks in Overtime" name="overtime" domain="[('overtime', '&gt;', 0)]"/>
<filter string="Tasks Soon in Overtime" name="remaining_hours_percentage" domain="[('remaining_hours_percentage', '&lt;=', 0.2)]"/>
</xpath>
</field>
</record>
Expand Down
1 change: 0 additions & 1 deletion addons/project/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
'views/project_project_stage_views.xml',
'wizard/project_share_wizard_views.xml',
'views/project_collaborator_views.xml',
'wizard/project_report_wizard_views.xml',
'views/project_views.xml',
'views/project_milestone_views.xml',
'views/res_partner_views.xml',
Expand Down
33 changes: 25 additions & 8 deletions addons/project/models/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,16 +308,16 @@ def _read_group_stage_ids(self, stages, domain, order):
"with Tasks (or optionally Issues if the Issue Tracker module is installed).")
alias_value = fields.Char(string='Alias email', compute='_compute_alias_value')
privacy_visibility = fields.Selection([
('followers', 'Invited employees'),
('employees', 'All employees'),
('portal', 'Invited portal users and all employees'),
('followers', 'Invited internal users'),
('employees', 'All internal users'),
('portal', 'Invited portal users and all internal users'),
],
string='Visibility', required=True,
default='portal',
help="Defines the visibility of the tasks of the project:\n"
"- Invited employees: employees may only see the followed project and tasks.\n"
"- All employees: employees may see all project and tasks.\n"
"- Invited portal users and all employees: employees may see everything."
"- Invited internal users: internal users may only see the followed project and tasks.\n"
"- All internal users: internal users may see all project and tasks.\n"
"- Invited portal users and all internal users: internal users may see everything."
" Invited portal users may see project and tasks followed by.\n"
" them or by someone of their company.")
doc_count = fields.Integer(compute='_compute_attached_docs_count', string="Number of documents attached")
Expand Down Expand Up @@ -531,6 +531,7 @@ def copy(self, default=None):
project.message_subscribe(partner_ids=follower.partner_id.ids, subtype_ids=follower.subtype_ids.ids)
if 'tasks' not in default:
self.map_tasks(project.id)

return project

@api.model
Expand Down Expand Up @@ -857,7 +858,7 @@ def _get_stat_buttons(self):
'number': f'{round(100 * self.rating_avg_percentage, 2)} %',
'action_type': 'object',
'action': 'action_view_all_rating',
'show': self.rating_active and self.rating_count > 0,
'show': self.rating_active,
'sequence': 15,
})
if self.user_has_groups('project.group_project_user'):
Expand Down Expand Up @@ -1124,7 +1125,7 @@ def _read_group_personal_stage_type_ids(self, stages, domain, order):
working_days_close = fields.Float(compute='_compute_elapsed', string='Working Days to Close', store=True, group_operator="avg")
# customer portal: include comment and incoming emails in communication history
website_message_ids = fields.One2many(domain=lambda self: [('model', '=', self._name), ('message_type', 'in', ['email', 'comment'])])
is_private = fields.Boolean(compute='_compute_is_private')
is_private = fields.Boolean(compute='_compute_is_private', search='_search_is_private')
allow_milestones = fields.Boolean(related='project_id.allow_milestones')
milestone_id = fields.Many2one(
'project.milestone',
Expand Down Expand Up @@ -1153,6 +1154,7 @@ def _read_group_personal_stage_type_ids(self, stages, domain, order):
column2="task_id", string="Block", copy=False,
domain="[('allow_task_dependencies', '=', True), ('id', '!=', id)]")
dependent_tasks_count = fields.Integer(string="Dependent Tasks", compute='_compute_dependent_tasks_count')
is_blocked = fields.Boolean(compute='_compute_is_blocked', store=True, recursive=True)

# Project sharing fields
display_parent_task_button = fields.Boolean(compute='_compute_display_parent_task_button', compute_sudo=True)
Expand Down Expand Up @@ -1277,6 +1279,16 @@ def _compute_is_private(self):
for task in self:
task.is_private = not task.project_id and not task.parent_id

def _search_is_private(self, operator, value):
if not isinstance(value, bool):
raise ValueError(_('Value should be True or False (not %s)'), value)
if operator not in ['=', '!=']:
raise NotImplementedError(_('Operation should be = or != (not %s)'), value)
if (operator == '=' and value) or (operator == '!=' and not value):
return [('project_id', '=', False)]
else:
return [('project_id', '!=', False)]

@api.depends('parent_id.ancestor_id')
def _compute_ancestor_id(self):
for task in self:
Expand Down Expand Up @@ -1455,6 +1467,11 @@ def _compute_dependent_tasks_count(self):
for task in tasks_with_dependency:
task.dependent_tasks_count = dependent_tasks_count_dict.get(task.id, 0)

@api.depends('depend_on_ids.is_closed', 'depend_on_ids.is_blocked')
def _compute_is_blocked(self):
for task in self:
task.is_blocked = any(not blocking_task.is_closed or blocking_task.is_blocked for blocking_task in task.depend_on_ids)

@api.depends('partner_id.email')
def _compute_partner_email(self):
for task in self:
Expand Down
1 change: 0 additions & 1 deletion addons/project/security/ir.model.access.csv
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,5 @@ access_project_collaborator_user,project.collaborator.user,model_project_collabo
access_project_collaborator_portal,project.collaborator.portal,model_project_collaborator,base.group_portal,1,0,0,0
access_project_share_manager,project.share.wizard.manager,model_project_share_wizard,project.group_project_manager,1,1,1,0
access_project_personal_stage,project.personal.stage.user,model_project_task_stage_personal,base.group_user,1,1,1,1
access_project_report_wizard_manager,project.report.wizard.project.manager,model_project_report_wizard,project.group_project_manager,1,1,1,0
access_project_milestone_reach_wizard_project_user,project.milestone.reach.wizard.project.user,model_project_milestone_reach_wizard,project.group_project_user,1,1,1,0
access_project_milestone_reach_line_wizard_project_user,project.milestone.reach.line.wizard.project.user,model_project_milestone_reach_line_wizard,project.group_project_user,1,1,1,0
4 changes: 2 additions & 2 deletions addons/project/views/project_sharing_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,12 @@
<field name="arch" type="xml">
<tree string="Tasks" sample="1" delete="0" import="0">
<field name="is_closed" invisible="1" />
<field name="allow_subtasks" invisible="1" />
<field name="sequence" invisible="1" readonly="1"/>
<field name="allow_milestones" invisible="1"/>
<field name="priority" widget="priority" optional="show" nolabel="1"/>
<field name="name" widget="name_with_subtask_count"/>
<field name="child_text" invisible="1"/>
<field name="allow_subtasks" invisible="1" />
<field name="name" widget="name_with_subtask_count"/>
<field name="company_id" invisible="1"/>
<field name="milestone_id" attrs="{'column_invisible': [('allow_milestones', '=', False)]}"/>
<field name="partner_id" optional="hide"/>
Expand Down
Loading

0 comments on commit 83760b9

Please sign in to comment.