[IMP] project: improve the UX of the app

Generic UX improvements for the project app.

task-2501760
See odoo/enterprise#18819

closes odoo/odoo#71232

Signed-off-by: LTU-Odoo <IT-Ideas@users.noreply.github.com>
This commit is contained in:
Cedric Prieels (cpr)
2021-07-23 06:45:50 +00:00
parent 0864034f45
commit 6ed20e3489
11 changed files with 95 additions and 52 deletions
+3
View File
@@ -194,6 +194,9 @@ class Task(models.Model):
'name': _('Timesheets'),
'res_model': 'account.analytic.line',
'view_mode': 'list,form',
'context': {
'default_project_id': self.project_id.id
},
'domain': [('project_id', '!=', False), ('task_id', 'in', tasks.ids)],
}
+7 -4
View File
@@ -175,9 +175,9 @@
</span>
<field name="effective_hours" widget="timesheet_uom" nolabel="1"/>
<button name="action_view_subtask_timesheet" type="object" class="o_td_label o_form_label o_form_subtask_button oe_inline oe_link mr-0" attrs="{'invisible' : ['|', ('allow_subtasks', '=', False), ('subtask_effective_hours', '=', 0.0)]}">
<span class="text-nowrap" attrs="{'invisible' : [('encode_uom_in_days', '=', True)]}">Sub-tasks Hours Spent</span>
<span class="text-nowrap" attrs="{'invisible' : [('encode_uom_in_days', '=', False)]}">Sub-tasks Days Spent</span>
<button name="action_view_subtask_timesheet" type="object" class="o_form_subtask_button oe_inline oe_link" attrs="{'invisible' : ['|', ('allow_subtasks', '=', False), ('subtask_effective_hours', '=', 0.0)]}">
<span class="text-nowrap" attrs="{'invisible' : [('encode_uom_in_days', '=', True)]}">Hours Spent on Sub-tasks:</span>
<span class="text-nowrap" attrs="{'invisible' : [('encode_uom_in_days', '=', False)]}">Days Spent on Sub-tasks:</span>
</button>
<field name="subtask_effective_hours" class="mt-2" widget="timesheet_uom"
attrs="{'invisible' : ['|', ('allow_subtasks', '=', False), ('subtask_effective_hours', '=', 0.0)]}" nolabel="1"/>
@@ -223,9 +223,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="show"/>
<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="show" groups="hr_timesheet.group_hr_timesheet_user"/>
</field>
</field>
@@ -281,6 +281,9 @@
<field name="model">project.task</field>
<field name="inherit_id" ref="project.view_task_search_form"/>
<field name="arch" type="xml">
<xpath expr="//filter[@name='my_tasks']" position='after'>
<filter string="My Team's Tasks" name="my_team_tasks" domain="[('user_id.employee_parent_id.user_id', '=', uid)]"/>
</xpath>
<xpath expr="//filter[@name='late']" position='after'>
<filter string="Tasks in Overtime" name="overtime" domain="[('overtime', '&gt;', 0)]"/>
</xpath>
@@ -9,8 +9,8 @@
<group>
<field name="task_id" invisible="True"/>
<field name="time_spent" string="Duration" class="oe_inline" widget="float_time" required="True"/>
<field name="description" widget="text" placeholder="Describe your activity..."/>
</group>
<field name="description" widget="text" nolabel="1" placeholder="Describe your activity..." />
<footer>
<button string="Save" type="object" name="save_timesheet" class="btn btn-primary" data-hotkey="q"/>
<button string="Delete" type="object" name="action_delete_timesheet" class="btn btn-secondary" data-hotkey="w"/>
+2 -2
View File
@@ -730,7 +730,7 @@ class Task(models.Model):
index=True, tracking=True)
partner_id = fields.Many2one('res.partner',
string='Customer',
compute='_compute_partner_id', recursive=True, store=True, readonly=False,
compute='_compute_partner_id', recursive=True, store=True, readonly=False, tracking=True,
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
partner_is_company = fields.Boolean(related='partner_id.is_company', readonly=True)
commercial_partner_id = fields.Many2one(related='partner_id.commercial_partner_id')
@@ -1491,7 +1491,7 @@ class Task(models.Model):
'name': 'Tasks in Recurrence',
'type': 'ir.actions.act_window',
'res_model': 'project.task',
'view_mode': 'tree,form',
'view_mode': 'tree,form,kanban,calendar,pivot,graph,gantt,activity,map',
'domain': [('recurrence_id', 'in', self.recurrence_id.ids)],
}
@@ -9,6 +9,17 @@ const ProjectCalendarController = CalendarController.extend({
_renderButtonsParameters() {
return Object.assign({}, this._super(...arguments), {scaleDrop: true});
},
/**
* @private
* @param {OdooEvent} event
*/
_onChangeDate: function (event) {
this._super.apply(this, arguments);
this.model.setScale('month');
this.model.setDate(event.data.date);
this.reload();
},
});
export const ProjectCalendarView = CalendarView.extend({
@@ -51,14 +51,13 @@ kanbanExamplesRegistry.add('project', {
}, {
name: _lt('Agile Scrum'),
columns: [_lt('Backlog'), _lt('Sprint Backlog'), _lt('Sprint in Progress'), _lt('Sprint Complete'), _lt('Old Completed Sprint')],
description: escFormat(_lt('Waiting for the next stage: use %s and %s bullets. %s'), greenBullet, redBullet, descriptionActivities),
description: escFormat(_lt('Use %s and %s bullets to indicate the status of a task. %s'), greenBullet, redBullet, descriptionActivities),
bullets: [greenBullet, redBullet],
}, {
name: _lt('Digital Marketing'),
columns: [_lt('Ideas'), _lt('Researching'), _lt('Writing'), _lt('Editing'), _lt('Done')],
description: escFormat(_lt('Everyone can propose ideas, and the Editor marks the best ones ' +
'as %s. Attach all documents or links to the task directly, to have all information about ' +
'a research centralized. %s'), greenBullet, descriptionActivities),
'as %s. Attach all documents or links to the task directly, to have all research information centralized. %s'), greenBullet, descriptionActivities),
bullets: [greenBullet, redBullet],
}, {
name: _lt('Customer Feedback'),
@@ -71,7 +70,7 @@ kanbanExamplesRegistry.add('project', {
name: _lt('Getting Things Done (GTD)'),
columns: [_lt('Inbox'), _lt('Today'), _lt('This Week'), _lt('This Month'), _lt('Long Term')],
description: escFormat(_lt('Fill your Inbox easily with the email gateway. Periodically review your ' +
'Inbox and schedule tasks by moving them to others columns. Every day, you review the ' +
'Inbox and schedule tasks by moving them to other columns. Every day, you review the ' +
'%s column to move important tasks %s. Every Monday, you review the %s column. %s'), _lt('"This Week"'), _lt('"Today"'), _lt('"This Month"'), descriptionActivities),
}, {
name: _lt('Consulting'),
@@ -91,8 +90,8 @@ kanbanExamplesRegistry.add('project', {
name: _lt('T-shirt Printing'),
columns: [_lt('New Orders'), _lt('Logo Design'), _lt('To Print'), _lt('Done')],
description: escFormat(_lt('Communicate with customers on the task using the email gateway. ' +
'Attach logo designs to the task, so that information flow from designers to the workers ' +
'who print the t-shirt. Organize priorities amongst orders %s using the icon. %s'), star, descriptionActivities),
'Attach logo designs to the task, so that information flows from designers to the workers ' +
'who print the t-shirt. Organize priorities amongst orders using the %s icon. %s'), star, descriptionActivities),
bullets: [star],
}, {
name: _lt('Design'),
@@ -111,7 +110,7 @@ kanbanExamplesRegistry.add('project', {
bullets: [greenBullet, redBullet, star, clock],
}, {
name: _lt('Podcast and Video Production'),
columns: [_lt('Research'), _lt('Script'), _lt('Recording'), _lt('Mixing'), _lt('Publishing')],
columns: [_lt('Research'), _lt('Script'), _lt('Recording'), _lt('Mixing'), _lt('Published')],
description: description,
bullets: [greenBullet, redBullet, star, clock],
}],
+58 -35
View File
@@ -19,16 +19,19 @@
<field name="name" string="Task"/>
<field name="tag_ids"/>
<field name="user_id"/>
<field name="partner_id" operator="child_of"/>
<field name="stage_id"/>
<field string="Project" name="display_project_id"/>
<field name="stage_id"/>
<field name="partner_id" operator="child_of"/>
<field name="parent_id"/>
<filter string="My Tasks" name="my_tasks" domain="[('user_id', '=', uid)]"/>
<filter string="Followed Tasks" name="my_followed_tasks" domain="[('message_is_follower', '=', True)]" />
<filter string="Unassigned" name="unassigned" domain="[('user_id', '=', False)]"/>
<separator/>
<filter string="Starred" name="starred" domain="[('priority', 'in', [0, 1])]"/>
<separator/>
<filter string="Late Tasks" name="late" domain="[('date_deadline', '&lt;', context_today().strftime('%Y-%m-%d'))]"/>
<filter string="Rated tasks" name="rating_task" domain="[('rating_last_value', '!=', 0.0)]" groups="project.group_project_rating"/>
<separator/>
<filter string="Rated Tasks" name="rating_task" domain="[('rating_last_value', '!=', 0.0)]" groups="project.group_project_rating"/>
<separator/>
<filter string="Unread Messages" name="message_needaction" domain="[('message_needaction', '=', True)]"/>
<separator/>
@@ -45,12 +48,30 @@
<filter string="Stage" name="stage" context="{'group_by': 'stage_id'}"/>
<filter string="Assigned to" name="user" context="{'group_by': 'user_id'}"/>
<filter string="Project" name="project" context="{'group_by': 'project_id'}"/>
<filter string="Customer" name="customer" context="{'group_by': 'partner_id'}"/>
<filter string="Kanban State" name="kanban_state" context="{'group_by': 'kanban_state'}"/>
<filter string="Deadline" name="date_deadline" context="{'group_by': 'date_deadline'}"/>
<filter string="Creation Date" name="group_create_date" context="{'group_by': 'create_date'}"/>
</group>
</search>
</field>
</record>
<record id="view_task_search_form_extended" model="ir.ui.view">
<field name="name">project.task.search.form.extended</field>
<field name="model">project.task</field>
<field name="inherit_id" ref="view_task_search_form"></field>
<field name="mode">primary</field>
<field name="arch" type="xml">
<xpath expr="//filter[@name='unassigned']" position="after">
<separator/>
<filter string="My Projects" name="my_projects" domain="[('project_id.user_id', '=', uid)]"/>
<filter string="My Favorite Projects" name="my_favorite_projects" domain="[('project_id.favorite_user_ids', 'in', [uid])]"/>
<filter string="Followed Projects" name="my_followed_projects" domain="[('project_id.message_is_follower', '=', True)]"/>
</xpath>
</field>
</record>
<record id="act_project_project_2_project_task_all" model="ir.actions.act_window">
<field name="name">Tasks</field>
<field name="res_model">project.task</field>
@@ -732,7 +753,6 @@
attrs="{'invisible' : [('allow_subtasks', '=', False)]}"
groups="base.group_no_one"
/>
<field name="recurring_task" attrs="{'invisible': ['|', ('allow_recurring_tasks', '=', False), ('parent_id', '!=', False)]}" />
</group>
<group>
<field name="active" invisible="1"/>
@@ -740,6 +760,7 @@
<field name="partner_phone" widget="phone" attrs="{'invisible': True}"/>
<field name="date_deadline" attrs="{'invisible': [('is_closed', '=', True)]}"/>
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color', 'no_create_edit': True}"/>
<field name="recurring_task" attrs="{'invisible': ['|', ('allow_recurring_tasks', '=', False), ('parent_id', '!=', False)]}" />
<field name="legend_blocked" invisible="1"/>
<field name="legend_normal" invisible="1"/>
<field name="legend_done" invisible="1"/>
@@ -769,6 +790,23 @@
</tree>
</field>
</page>
<page name="task_dependencies" string="Blocked By" attrs="{'invisible': [('allow_task_dependencies', '=', False)]}" groups="project.group_project_task_dependencies">
<field name="depend_on_ids" nolabel="1">
<tree editable="bottom">
<field name="is_closed" invisible="1" />
<field name="name" />
<field name="project_id" optional="hide" />
<field name="user_id" widget="many2one_avatar_user" optional="show"/>
<field name="company_id" optional="hide" groups="base.group_multi_company" />
<field name="activity_ids" widget="list_activity" optional="hide"/>
<field name="date_deadline" attrs="{'invisible': [('is_closed', '=', True)]}" optional="show" />
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}" optional="hide"/>
<field name="kanban_state" widget="state_selection" optional="hide" />
<field name="stage_id" optional="show" />
<button class="oe_link float-right" string="View Task" name="action_open_task" type="object"/>
</tree>
</field>
</page>
<page name="recurrence" string="Recurrence" attrs="{'invisible': [('recurring_task', '=', False)]}">
<group>
<group>
@@ -814,7 +852,7 @@
<field name="email_from" invisible="1"/>
<field name="email_cc" groups="base.group_no_one"/>
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
<field name="displayed_image_id" groups="base.group_no_one"/>
<field name="displayed_image_id" groups="base.group_no_one" options="{'no_create': True}"/>
</group>
<group>
<field name="date_assign" groups="base.group_no_one"/>
@@ -830,23 +868,6 @@
</group>
</group>
</page>
<page name="task_dependencies" string="Blocked By" attrs="{'invisible': [('allow_task_dependencies', '=', False)]}" groups="project.group_project_task_dependencies">
<field name="depend_on_ids" nolabel="1">
<tree editable="bottom">
<field name="is_closed" invisible="1" />
<field name="name" />
<field name="project_id" optional="hide" />
<field name="user_id" widget="many2one_avatar_user" optional="show"/>
<field name="company_id" optional="hide" groups="base.group_multi_company" />
<field name="activity_ids" widget="list_activity" optional="hide"/>
<field name="date_deadline" attrs="{'invisible': [('is_closed', '=', True)]}" optional="show" />
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}" optional="hide"/>
<field name="kanban_state" widget="state_selection" optional="hide" />
<field name="stage_id" optional="show" />
<button class="oe_link float-right" string="View Task" name="action_open_task" type="object"/>
</tree>
</field>
</page>
</notebook>
</sheet>
<div class="oe_chatter">
@@ -949,11 +970,6 @@
<a t-if="widget.editable" role="menuitem" type="set_cover" class="dropdown-item" data-field="displayed_image_id">Set Cover Image</a>
<a name="%(portal.portal_share_action)d" role="menuitem" type="action" class="dropdown-item">Share</a>
<a t-if="widget.editable" role="menuitem" type="edit" class="dropdown-item">Edit</a>
<a t-if="widget.editable" role="menuitem" class="dropdown-item" name="toggle_active" type="object">
<t t-if="record.active.raw_value">Archive</t>
<t t-if="!record.active.raw_value">Restore</t>
</a>
<a t-if="widget.deletable" role="menuitem" type="delete" class="dropdown-item">Delete</a>
<div role="separator" class="dropdown-divider"></div>
<ul class="oe_kanban_colorpicker" data-field="color"/>
</div>
@@ -1002,6 +1018,7 @@
<field name="is_closed" invisible="1" />
<field name="allow_subtasks" invisible="1" />
<field name="sequence" invisible="1" readonly="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="project_id" optional="show" readonly="1"/>
@@ -1012,7 +1029,7 @@
<field name="activity_ids" widget="list_activity" optional="show"/>
<field name="date_deadline" optional="hide" widget="remaining_days" attrs="{'invisible': [('is_closed', '=', True)]}"/>
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}" optional="show"/>
<field name="kanban_state" widget="state_selection" optional="hide" readonly="1"/>
<field name="kanban_state" widget="selection" optional="hide" />
<field name="stage_id" invisible="context.get('set_visible',False)" optional="show" readonly="not context.get('default_project_id')"/>
<field name="recurrence_id" invisible="1" />
</tree>
@@ -1041,12 +1058,15 @@
<field eval="2" name="priority"/>
<field name="arch" type="xml">
<calendar date_start="date_deadline" string="Tasks" mode="month" color="user_id" event_limit="5"
hide_time="true" quick_add="False" js_class="project_calendar">
<field name="user_id" avatar_field="avatar_128" filters="1"/>
<field name="date_deadline"/>
hide_time="true" js_class="project_calendar" event_open_popup="true" quick_add="false">
<field name="project_id"/>
<field name="user_id" avatar_field="avatar_128" filters="1"/>
<field name="partner_id" attrs="{'invisible': [('partner_id', '=', False)]}"/>
<field name="priority" widget="priority"/>
<field name="date_deadline"/>
<field name="tag_ids" widget="many2many_tags" attrs="{'invisible': [('tag_ids', '=', [])]}"/>
<field name="stage_id"/>
<field name="kanban_state"/>
</calendar>
</field>
</record>
@@ -1059,8 +1079,11 @@
<field name="project_id" type="row"/>
<field name="stage_id" type="col"/>
<field name="color" invisible="1"/>
<field name="sequence" invisible="1"/>
<field name="rating_last_value" type="measure" string="Rating (/5)"/>
<field name="sequence" invisible="1"/>
<field name="planned_hours" widget="float_time"/>
<field name="working_hours_close" widget="float_time"/>
<field name="working_hours_open" widget="float_time"/>
</pivot>
</field>
</record>
@@ -1120,7 +1143,7 @@
<field name="res_model">project.task</field>
<field name="view_mode">tree,kanban,form,calendar,pivot,graph,activity</field>
<field name="context">{'search_default_my_tasks': 1, 'all_task': 0}</field>
<field name="search_view_id" ref="view_task_search_form"/>
<field name="search_view_id" ref="view_task_search_form_extended"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No tasks found. Let's create one!
@@ -1149,7 +1172,7 @@
<field name="name">Tasks</field>
<field name="res_model">project.task</field>
<field name="view_mode">kanban,tree,form</field>
<field name="search_view_id" ref="view_task_search_form"/>
<field name="search_view_id" ref="view_task_search_form_extended"/>
</record>
<record id="action_view_task_overpassed_draft" model="ir.actions.act_window">
@@ -1158,7 +1181,7 @@
<field name="view_mode">tree,form,calendar,graph,kanban</field>
<field name="domain">[('is_closed', '=', False), ('date_deadline','&lt;',time.strftime('%Y-%m-%d')), ('display_project_id', '!=', False)]</field>
<field name="filter" eval="True"/>
<field name="search_view_id" ref="view_task_search_form"/>
<field name="search_view_id" ref="view_task_search_form_extended"/>
</record>
<!-- Opening task when double clicking on project -->
+1
View File
@@ -81,6 +81,7 @@
<xpath expr="//filter[@name='resource']" position="after">
<filter string="Project" name="groupby_project" context="{'group_by': 'parent_res_name'}"/>
</xpath>
<xpath expr="//filter[@name='responsible']" position="replace"></xpath>
<xpath expr="/search" position="inside">
<filter string="Last 30 Days" name="rating_last_30_days" domain="[
('write_date', '>=', (datetime.datetime.combine(context_today() + relativedelta(days=-30), datetime.time(0,0,0)).to_utc()).strftime('%Y-%m-%d %H:%M:%S'))]"
+1 -1
View File
@@ -61,7 +61,7 @@ class ProjectTask(models.Model):
sale_order_id = fields.Many2one('sale.order', 'Sales Order', compute='_compute_sale_order_id', store=True, help="Sales order to which the task is linked.")
sale_line_id = fields.Many2one(
'sale.order.line', 'Sales Order Item', domain="[('company_id', '=', company_id), ('is_service', '=', True), ('order_partner_id', 'child_of', commercial_partner_id), ('is_expense', '=', False), ('state', 'in', ['sale', 'done'])]",
compute='_compute_sale_line', recursive=True, store=True, readonly=False, copy=False,
compute='_compute_sale_line', recursive=True, store=True, readonly=False, copy=False, tracking=True,
help="Sales Order Item to which the time spent on this task will be added, in order to be invoiced to your customer.")
project_sale_order_id = fields.Many2one('sale.order', string="Project's sale order", related='project_id.sale_order_id')
invoice_count = fields.Integer("Number of invoices", related='sale_order_id.invoice_count')
@@ -87,9 +87,12 @@
<field name="model">project.task</field>
<field name="inherit_id" ref="project.view_task_search_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='stage_id']" position="before">
<xpath expr="//field[@name='partner_id']" position="after">
<field name="sale_order_id" string="Sale Order" filter_domain="['|', ('sale_order_id', 'ilike', self), ('sale_line_id', 'ilike', self)]"/>
</xpath>
<xpath expr="//search/group/filter[@name='customer']" position="after">
<filter string="Sales Order Item" name="sale_order_id" context="{'group_by': 'sale_order_id'}"/>
</xpath>
</field>
</record>
@@ -112,7 +112,7 @@
<xpath expr="//header" position='inside'>
<field name="allow_billable" invisible="1"/>
</xpath>
<xpath expr="//field[@name='email_cc']" position="after">
<xpath expr="//group/field[@name='sequence']" position="before">
<field name="analytic_account_id" groups="base.group_no_one"/>
</xpath>
</field>