[REF] project,*: refactor projects/tasks views and reports
*=hr_timesheet,sale_project,sale_timesheet,spreadsheet_dashboard_hr_timesheet
The main goal of this PR is to minimize code duplication for the search and list views for tasks and projects.
Task search views
-----------------
The most important change was to merge the search views used in the task reports with the "normal" corresponding search view. The reporting search view should now be almost identical to the normal one in both project and fsm.
The `project.task`/`project.task.user` search views are now following this inheritance diagram:
```
┌─┐
┌─┤1├─┐
│ └─┘ │
│ │
┌▼┐ ┌▼┐
│2│ ┌─┤3├─┐
└─┘ │ └─┘ │
│ │
┌▼┐ ┌▼┐
┌──┤4│ │7├──┐
│ └┬┘ └┬┘ │
│ │ │ │
┌▼┐ ┌▼┐ ┌▼┐ ┌▼┐
│5│ │6│ │8│ │9│
└─┘ └─┘ └─┘ └─┘
```
1. `view_task_search_form_base`: Base search view containing all the fields/filters common to project, project sharing and fsm
2. `project_sharing_project_task_view_search`: Search view for project sharing.
3. `view_task_search_form_project_fsm_base`: Base search view containing all the fields/filters common to project and fsm (not project sharing).
4. `view_task_search_form_project_base`: Base search view for project only, with fields/filter common to the normal search view and the one used in reporting.
5. `view_task_search_form`: Search view for project.
6. `view_task_project_user_search`: Search view for the project report.
7. `project_task_view_search_fsm_base`: Base search view for fsm only, with fields/filter common to the normal search view and the one used in reporting.
8. `project_task_view_search_fsm`: Search view for fsm.
9. `report_project_task_user_fsm_view_search`: Search view for the project report.
With this organization, we can easily add a field to both fsm and project at once, and even to project sharing at the same time. Since the goal is to keep the reproting search views identical to the normal ones, it'll also add the field to both of them.
Task list views
---------------
Similarly to the search views, the task list views were also refactored to minimize code duplication, following this inheritance diagram:
```
┌─┐
┌─┤1├─┐
│ └─┘ │
│ │
┌▼┐ ┌▼┐
│2│ ┌─┤3├─┐
└─┘ │ └─┘ │
│ │
┌▼┐ ┌▼┐
│4│ │5│
└─┘ └─┘
```
1. `project_task_view_tree_main_base`: Base list view containing all the fields/filters common to project, project sharing and fsm.
2. `project_sharing_project_task_view_tree`: List view for project sharing.
3. `project_task_view_tree_base`: Base list view containing all the fields/filters common to project and fsm (not project sharing).
4. `view_task_tree2`: List view for project
5. `project_task_view_list_fsm`: List view for fsm.
Project views
-----------------
The search and list views for projects in fsm now inherit the ones from project, since our goal moving forward will be to keep those as similar as possible.
Other changes
-----------------
- Merged and removed many filters/fields from the task search views.
- Changed `total_hours_spent` and ` subtask_effective_hours` labels in `project.task` model to avoid changing them manually in many views.
- Renamed `state` into `kanban_state`, `hours_planned` into `planned_hours` and `hours_effective` into `effective_hours` in `report.project.task.user` to make it consistent with `project.task` (so we can keep the same views for both of those models).
- Moved the sale line filed in the "Settings" tab in the project form view (so that the `sale_line_employee_ids` field in "Invoicing" takes the full width).
---
Enterprise: https://github.com/odoo/enterprise/pull/34106
Upgrade: https://github.com/odoo/upgrade/pull/4216
Task-2746370
closes odoo/odoo#106015
Related: odoo/upgrade#4216
Related: odoo/enterprise#34106
Signed-off-by: Xavier Bol (xbo) <xbo@odoo.com>
This commit is contained in:
@@ -244,10 +244,10 @@ class Task(models.Model):
|
||||
remaining_hours = fields.Float("Remaining Hours", compute='_compute_remaining_hours', store=True, readonly=True, help="Number of allocated hours minus the number of hours spent.")
|
||||
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)
|
||||
total_hours_spent = fields.Float("Total Hours Spent", compute='_compute_total_hours_spent', store=True, help="Time spent on this task and its sub-tasks (and their own sub-tasks).")
|
||||
total_hours_spent = fields.Float("Total Hours", compute='_compute_total_hours_spent', store=True, help="Time spent on this task and its sub-tasks (and their own sub-tasks).")
|
||||
progress = fields.Float("Progress", compute='_compute_progress_hours', store=True, group_operator="avg")
|
||||
overtime = fields.Float(compute='_compute_progress_hours', store=True)
|
||||
subtask_effective_hours = fields.Float("Sub-tasks Hours Spent", compute='_compute_subtask_effective_hours', recursive=True, store=True, help="Time spent on the sub-tasks (and their own sub-tasks) of this task.")
|
||||
subtask_effective_hours = fields.Float("Hours Spent on Sub-Tasks", compute='_compute_subtask_effective_hours', recursive=True, store=True, help="Time spent on the sub-tasks (and their own sub-tasks) of this task.")
|
||||
timesheet_ids = fields.One2many('account.analytic.line', 'task_id', 'Timesheets')
|
||||
encode_uom_in_days = fields.Boolean(compute='_compute_encode_uom_in_days', default=lambda self: self._uom_in_days())
|
||||
|
||||
|
||||
@@ -7,30 +7,30 @@ from odoo import fields, models, api
|
||||
class ReportProjectTaskUser(models.Model):
|
||||
_inherit = "report.project.task.user"
|
||||
|
||||
hours_planned = fields.Float('Planned Hours', readonly=True)
|
||||
hours_effective = fields.Float('Effective Hours', readonly=True)
|
||||
planned_hours = fields.Float('Initially Planned Hours', readonly=True)
|
||||
effective_hours = fields.Float('Hours Spent', readonly=True)
|
||||
remaining_hours = fields.Float('Remaining Hours', readonly=True)
|
||||
remaining_hours_percentage = fields.Float('Remaining Hours Percentage', readonly=True)
|
||||
progress = fields.Float('Progress', group_operator='avg', readonly=True)
|
||||
overtime = fields.Float(readonly=True)
|
||||
|
||||
def _select(self):
|
||||
select_to_append = """,
|
||||
(t.effective_hours * 100) / NULLIF(t.planned_hours, 0) as progress,
|
||||
t.effective_hours as hours_effective,
|
||||
return super()._select() + """,
|
||||
CASE WHEN COALESCE(t.planned_hours, 0) = 0 THEN 0.0 ELSE LEAST((t.effective_hours * 100) / t.planned_hours, 100) END as progress,
|
||||
t.effective_hours,
|
||||
t.planned_hours - t.effective_hours - t.subtask_effective_hours as remaining_hours,
|
||||
NULLIF(t.planned_hours, 0) as hours_planned,
|
||||
t.overtime as overtime
|
||||
CASE WHEN t.planned_hours > 0 THEN t.remaining_hours / t.planned_hours ELSE 0 END as remaining_hours_percentage,
|
||||
t.planned_hours,
|
||||
t.overtime
|
||||
"""
|
||||
return super(ReportProjectTaskUser, self)._select() + select_to_append
|
||||
|
||||
def _group_by(self):
|
||||
group_by_append = """,
|
||||
return super()._group_by() + """,
|
||||
t.effective_hours,
|
||||
t.subtask_effective_hours,
|
||||
t.planned_hours,
|
||||
t.overtime
|
||||
"""
|
||||
return super(ReportProjectTaskUser, self)._group_by() + group_by_append
|
||||
|
||||
@api.model
|
||||
def _get_view_cache_key(self, view_id=None, view_type='form', **options):
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
<attribute name="js_class">hr_timesheet_graphview</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='project_id']" position='after'>
|
||||
<field name="hours_planned" widget="timesheet_uom" type="measure"/>
|
||||
<field name="hours_effective" widget="timesheet_uom" type="measure"/>
|
||||
<field name="planned_hours" widget="timesheet_uom" type="measure"/>
|
||||
<field name="effective_hours" widget="timesheet_uom" type="measure"/>
|
||||
<field name="remaining_hours" widget="timesheet_uom" type="measure"/>
|
||||
</xpath>
|
||||
</field>
|
||||
@@ -23,39 +23,20 @@
|
||||
<field name="inherit_id" ref="project.view_task_project_user_pivot"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='project_id']" position='after'>
|
||||
<field name="hours_planned" widget="timesheet_uom" type="measure"/>
|
||||
<field name="hours_effective" widget="timesheet_uom" type="measure"/>
|
||||
<field name="planned_hours" widget="timesheet_uom" type="measure"/>
|
||||
<field name="effective_hours" widget="timesheet_uom" type="measure"/>
|
||||
<field name="remaining_hours" widget="timesheet_uom" type="measure"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="report_project_task_user_view_search" model="ir.ui.view">
|
||||
<field name="name">report.project.task.user.view.search.inherit.hr.timesheet</field>
|
||||
<field name="model">report.project.task.user</field>
|
||||
<field name="inherit_id" ref="project.view_task_project_user_search" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//filter[@name='late']" position="after">
|
||||
<filter string="Tasks in Overtime" name="overtime" domain="[('overtime', '>', 0)]"/>
|
||||
</xpath>
|
||||
<xpath expr="//filter[@name='my_favorite_projects']" position="after">
|
||||
<filter string="My Team's Projects" name="my_team_projects" domain="[('project_id.user_id.employee_id.parent_id.user_id', '=', uid), ('project_id.user_id', '!=', uid), ('user_ids', '!=', uid), ('user_ids', '!=', False)]"/>
|
||||
<filter string="My Department's Projects" name="my_department" domain="[('project_id.user_id.employee_id.member_of_department', '=', True)]"/>
|
||||
</xpath>
|
||||
<xpath expr="//filter[@name='followed_by_me']" position="after">
|
||||
<filter string="My Team's Tasks" name="my_team_tasks" domain="[('user_ids.employee_id.parent_id.user_id', '=', uid)]" />
|
||||
<filter string="My Department's Tasks" name="my_department" domain="[('user_ids.employee_id.member_of_department', '=', True)]"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="report_project_task_user_view_tree" model="ir.ui.view">
|
||||
<field name="name">report.project.task.user.view.tree.inherit.hr.timesheet</field>
|
||||
<field name="model">report.project.task.user</field>
|
||||
<field name="inherit_id" ref="project.report_project_task_user_view_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="user_ids" position="after">
|
||||
<field name="hours_effective" optional="hide" widget="float_time"/>
|
||||
<field name="effective_hours" optional="hide" widget="float_time"/>
|
||||
<field name="progress" optional="hide" widget="progressbar"/>
|
||||
</field>
|
||||
</field>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { GraphModel } from "@web/views/graph/graph_model";
|
||||
|
||||
const FIELDS = [
|
||||
'unit_amount', 'effective_hours', 'planned_hours', 'remaining_hours', 'total_hours_spent', 'subtask_effective_hours',
|
||||
'overtime', 'number_hours', 'difference', 'hours_effective', 'hours_planned', 'timesheet_unit_amount'
|
||||
'overtime', 'number_hours', 'difference', 'timesheet_unit_amount'
|
||||
];
|
||||
|
||||
export class hrTimesheetGraphModel extends GraphModel {
|
||||
|
||||
@@ -546,6 +546,6 @@ class TestTimesheet(TestCommonTimesheet):
|
||||
('hr_timesheet.project_invoice_form', '//field[@name="allocated_hours"]', [None, 'Allocated Days']),
|
||||
('hr_timesheet.view_task_form2_inherited', '//field[@name="unit_amount"]', ['Hours Spent', 'Days Spent']),
|
||||
('hr_timesheet.view_task_tree2_inherited', '//field[@name="planned_hours"]', [None, 'Initially Planned Days']),
|
||||
('hr_timesheet.view_task_project_user_graph_inherited', '//field[@name="hours_planned"]', [None, 'Planned Days']),
|
||||
('hr_timesheet.view_task_project_user_graph_inherited', '//field[@name="planned_hours"]', [None, 'Initially Planned Days']),
|
||||
('hr_timesheet.timesheets_analysis_report_pivot_employee', '//field[@name="unit_amount"]', [None, 'Days Spent']),
|
||||
])
|
||||
|
||||
@@ -140,34 +140,4 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="project_sharing_inherit_project_task_view_tree" model="ir.ui.view">
|
||||
<field name="name">project.task.tree.inherited</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="project.project_sharing_project_task_view_tree" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="portal_user_names" position="after">
|
||||
<field name="allow_subtasks" invisible="1"/>
|
||||
<field name="allow_timesheets" invisible="1"/>
|
||||
<field name="planned_hours" widget="timesheet_uom_no_toggle" sum="Initially Planned Hours" optional="hide" attrs="{'column_invisible': [('allow_timesheets', '=', False)]}"/>
|
||||
<field name="effective_hours" widget="timesheet_uom" sum="Effective Hours" optional="hide" attrs="{'column_invisible': [('allow_timesheets', '=', False)]}"/>
|
||||
<field name="subtask_effective_hours" widget="timesheet_uom" attrs="{'column_invisible' : ['|', ('allow_subtasks', '=', False), ('allow_timesheets', '=', False)]}" optional="hide"/>
|
||||
<field name="total_hours_spent" widget="timesheet_uom" attrs="{'column_invisible' : ['|', ('allow_subtasks', '=', False), ('allow_timesheets', '=', False)]}" optional="hide"/>
|
||||
<field name="remaining_hours" widget="timesheet_uom" sum="Remaining Hours" optional="hide" decoration-danger="progress >= 100" decoration-warning="progress >= 80 and progress < 100" attrs="{'column_invisible': [('allow_timesheets', '=', False)]}"/>
|
||||
<field name="progress" widget="progressbar" attrs="{'column_invisible': [('allow_timesheets', '=', False)]}" optional="hide"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="project_sharing_project_task_view_search_inherit_timesheet" model="ir.ui.view">
|
||||
<field name="name">project.sharing.project.task.view.search.inherit.timesheet</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="project.project_sharing_project_task_view_search"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//filter[@name='late']" position='after'>
|
||||
<filter string="Tasks in Overtime" name="overtime" domain="[('overtime', '>', 0)]"/>
|
||||
<filter string="Tasks Soon in Overtime" name="remaining_hours_percentage" domain="[('remaining_hours_percentage', '<=', 0.2)]"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -113,9 +113,9 @@
|
||||
<field name="arch" type="xml">
|
||||
<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" groups="hr_timesheet.group_hr_timesheet_user"/>
|
||||
<field name="effective_hours" widget="timesheet_uom" sum="Effective Hours" optional="hide" groups="hr_timesheet.group_hr_timesheet_user"/>
|
||||
<field name="subtask_effective_hours" string="Sub-tasks Hours Spent" widget="timesheet_uom" sum="Sub-tasks Hours Spent" optional="hide" groups="hr_timesheet.group_hr_timesheet_user"/>
|
||||
<field name="total_hours_spent" string="Total Hours Spent" widget="timesheet_uom" sum="Total Hours" optional="hide" groups="hr_timesheet.group_hr_timesheet_user"/>
|
||||
<field name="effective_hours" widget="timesheet_uom" sum="Hours Spent" optional="hide" groups="hr_timesheet.group_hr_timesheet_user"/>
|
||||
<field name="subtask_effective_hours" widget="timesheet_uom" sum="Sub-tasks Hours Spent" optional="hide" groups="hr_timesheet.group_hr_timesheet_user"/>
|
||||
<field name="total_hours_spent" widget="timesheet_uom" sum="Total Hours" optional="hide" groups="hr_timesheet.group_hr_timesheet_user"/>
|
||||
<field name="remaining_hours" widget="timesheet_uom" sum="Remaining Hours" optional="hide" decoration-danger="progress >= 100" decoration-warning="progress >= 80 and progress < 100" groups="hr_timesheet.group_hr_timesheet_user"/>
|
||||
<field name="progress" widget="progressbar" optional="hide" groups="hr_timesheet.group_hr_timesheet_user"/>
|
||||
</xpath>
|
||||
@@ -260,7 +260,7 @@
|
||||
<field name="inherit_id" ref="project.view_project"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='date']" position="after">
|
||||
<field name="allocated_hours" widget="timesheet_uom_no_toggle" optional="show" attrs="{'invisible' : [('allocated_hours', '=', 0)]}"/>
|
||||
<field name="allocated_hours" widget="timesheet_uom_no_toggle" optional="hide" attrs="{'invisible' : [('allocated_hours', '=', 0)]}"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
@@ -268,18 +268,17 @@
|
||||
<record id="view_task_tree2_inherited" model="ir.ui.view">
|
||||
<field name="name">project.task.tree.inherited</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="project.view_task_tree2" />
|
||||
<field eval="2" name="priority"/>
|
||||
<field name="inherit_id" ref="project.project_task_view_tree_main_base" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="company_id" position="after">
|
||||
<field name="date_deadline" position="before">
|
||||
<field name="allow_subtasks" invisible="1"/>
|
||||
<field name="progress" 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="effective_hours" widget="timesheet_uom" sum="Effective Hours" optional="show"/>
|
||||
<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 >= 100" decoration-warning="progress >= 80 and progress < 100"/>
|
||||
<field name="progress" widget="progressbar" optional="hide" groups="hr_timesheet.group_hr_timesheet_user" attrs="{'invisible' : [('planned_hours', '=', 0)]}"/>
|
||||
<field name="progress" widget="progressbar" optional="show" groups="hr_timesheet.group_hr_timesheet_user" attrs="{'invisible' : [('planned_hours', '=', 0)]}"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
@@ -346,27 +345,13 @@
|
||||
<record id="project_task_view_search" model="ir.ui.view">
|
||||
<field name="name">project.task.view.search.inherit.sale.timesheet.enterprise</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="project.view_task_search_form"/>
|
||||
<field name="inherit_id" ref="project.view_task_search_form_project_fsm_base"/>
|
||||
<field name="priority">10</field>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//filter[@name='followed_by_me']" position='after'>
|
||||
<filter string="My Team's Tasks" name="my_team_tasks" domain="[('user_ids.employee_parent_id.user_id', '=', uid)]"/>
|
||||
<filter string="My Department's Tasks" name="my_department" domain="[('user_ids.employee_id.member_of_department', '=', True)]"/>
|
||||
</xpath>
|
||||
<xpath expr="//filter[@name='tasks_due_today']" position='after'>
|
||||
<filter string="Tasks in Overtime" name="overtime" domain="[('overtime', '>', 0)]"/>
|
||||
<filter string="Tasks Soon in Overtime" name="remaining_hours_percentage" domain="[('remaining_hours_percentage', '<=', 0.2)]"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_task_search_form_hr_extended" model="ir.ui.view">
|
||||
<field name="name">project.task.view.search.inherit.hr.timesheet</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="project.view_task_search_form_extended"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//filter[@name='my_favorite_projects']" position='after'>
|
||||
<filter string="My Team's Projects" name="my_teams_projects" domain="[('project_id.user_id.employee_parent_id.user_id', '=', uid)]"/>
|
||||
<filter string="My Department's Projects" name="my_department" domain="[('manager_id.employee_id.member_of_department', '=', True)]"/>
|
||||
<xpath expr="//filter[@name='blocking']/following-sibling::separator[1]" position="after">
|
||||
<filter string="Timesheets 80%" name="timesheet_80" domain="[('remaining_hours_percentage', '>', 0.0), ('remaining_hours_percentage', '<=', 0.2)]"/>
|
||||
<filter string="Timesheets >100%" name="timesheet_exceeded" domain="[('overtime', '>', 0)]"/>
|
||||
<separator/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
@@ -376,12 +361,8 @@
|
||||
<field name="model">project.project</field>
|
||||
<field name="inherit_id" ref="project.view_project_project_filter"/>
|
||||
<field name="arch" type="xml">
|
||||
<filter name="followed_by_me" position='before'>
|
||||
<filter string="My Team" name="my_team_projects" domain="[('user_id.employee_parent_id.user_id', '=', uid)]"/>
|
||||
<filter string="My Department" name="my_department" domain="[('user_id.employee_id.member_of_department', '=', True)]"/>
|
||||
</filter>
|
||||
<filter name="late_milestones" position="before">
|
||||
<filter string="Projects in Overtime" name="projects_in_overtime" domain="[('is_project_overtime', '=', True)]"/>
|
||||
<filter string="Timesheets Exceeded" name="projects_in_overtime" domain="[('is_project_overtime', '=', True)]"/>
|
||||
</filter>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
'security/ir.model.access.csv',
|
||||
'security/ir.model.access.xml',
|
||||
'data/digest_data.xml',
|
||||
'report/project_report_views.xml',
|
||||
'report/project_task_burndown_chart_report_views.xml',
|
||||
'views/analytic_views.xml',
|
||||
'views/digest_views.xml',
|
||||
@@ -43,6 +42,7 @@
|
||||
'views/project_portal_templates.xml',
|
||||
'views/project_task_templates.xml',
|
||||
'views/project_sharing_templates.xml',
|
||||
'report/project_report_views.xml',
|
||||
'data/ir_cron_data.xml',
|
||||
'data/mail_message_subtype_data.xml',
|
||||
'data/mail_template_data.xml',
|
||||
|
||||
@@ -47,6 +47,12 @@ PROJECT_TASK_READABLE_FIELDS = {
|
||||
'allow_milestones',
|
||||
'milestone_id',
|
||||
'has_late_and_unreached_milestone',
|
||||
'company_id',
|
||||
'date_last_stage_update',
|
||||
'date_assign',
|
||||
'dependent_ids',
|
||||
'is_blocked',
|
||||
'message_is_follower',
|
||||
}
|
||||
|
||||
PROJECT_TASK_WRITABLE_FIELDS = {
|
||||
@@ -272,6 +278,12 @@ class Project(models.Model):
|
||||
# Since project stages are order by sequence first, this should fetch the one with the lowest sequence number.
|
||||
return self.env['project.project.stage'].search([], limit=1)
|
||||
|
||||
@api.model
|
||||
def _search_is_favorite(self, operator, value):
|
||||
if operator not in ['=', '!='] or not isinstance(value, bool):
|
||||
raise NotImplementedError(_('Operation not supported'))
|
||||
return [('favorite_user_ids', 'in' if (operator == '=') == value else 'not in', self.env.uid)]
|
||||
|
||||
def _compute_is_favorite(self):
|
||||
for project in self:
|
||||
project.is_favorite = self.env.user in project.favorite_user_ids
|
||||
@@ -323,8 +335,8 @@ class Project(models.Model):
|
||||
'res.users', 'project_favorite_user_rel', 'project_id', 'user_id',
|
||||
default=_get_default_favorite_user_ids,
|
||||
string='Members')
|
||||
is_favorite = fields.Boolean(compute='_compute_is_favorite', inverse='_inverse_is_favorite', compute_sudo=True,
|
||||
string='Show Project on Dashboard')
|
||||
is_favorite = fields.Boolean(compute='_compute_is_favorite', inverse='_inverse_is_favorite', search='_search_is_favorite',
|
||||
compute_sudo=True, string='Show Project on Dashboard')
|
||||
label_tasks = fields.Char(string='Use Tasks as', default='Tasks', translate=True,
|
||||
help="Name used to refer to the tasks of your project e.g. tasks, tickets, sprints, etc...")
|
||||
tasks = fields.One2many('project.task', 'project_id', string="Task Activities")
|
||||
@@ -507,7 +519,7 @@ class Project(models.Model):
|
||||
read_group = self.env['project.milestone']._read_group([
|
||||
('project_id', 'in', self.filtered('allow_milestones').ids),
|
||||
('is_reached', '=', False),
|
||||
('deadline', '<', today)], ['project_id'], ['project_id'])
|
||||
('deadline', '<=', today)], ['project_id'], ['project_id'])
|
||||
mapped_count = {group['project_id'][0]: group['project_id_count'] for group in read_group}
|
||||
for project in self:
|
||||
project.is_milestone_exceeded = bool(mapped_count.get(project.id, 0))
|
||||
@@ -525,7 +537,7 @@ class Project(models.Model):
|
||||
LEFT JOIN project_milestone M ON P.id = M.project_id
|
||||
WHERE M.is_reached IS false
|
||||
AND P.allow_milestones IS true
|
||||
AND M.deadline < CAST(now() AS date)
|
||||
AND M.deadline <= CAST(now() AS date)
|
||||
"""
|
||||
if (operator == '=' and value is True) or (operator == '!=' and value is False):
|
||||
operator_new = 'inselect'
|
||||
@@ -764,6 +776,10 @@ class Project(models.Model):
|
||||
def action_project_sharing(self):
|
||||
self.ensure_one()
|
||||
action = self.env['ir.actions.act_window']._for_xml_id('project.project_sharing_project_task_action')
|
||||
action['context'] = {
|
||||
'default_project_id': self.id,
|
||||
'delete': False,
|
||||
}
|
||||
action['display_name'] = self.name
|
||||
return action
|
||||
|
||||
@@ -2180,7 +2196,7 @@ class Task(models.Model):
|
||||
late_milestones = self.env['project.milestone'].sudo()._search([ # sudo is needed for the portal user in Project Sharing.
|
||||
('id', 'in', self.milestone_id.ids),
|
||||
('is_reached', '=', False),
|
||||
('deadline', '<', fields.Date.today()),
|
||||
('deadline', '<=', fields.Date.today()),
|
||||
])
|
||||
for task in self:
|
||||
task.has_late_and_unreached_milestone = task.allow_milestones and task.milestone_id.id in late_milestones
|
||||
|
||||
@@ -34,7 +34,7 @@ class ReportProjectTaskUser(models.Model):
|
||||
('0', 'Low'),
|
||||
('1', 'High')
|
||||
], readonly=True, string="Priority")
|
||||
state = fields.Selection([
|
||||
kanban_state = fields.Selection([
|
||||
('normal', 'In Progress'),
|
||||
('blocked', 'Blocked'),
|
||||
('done', 'Ready for Next Stage')
|
||||
@@ -49,15 +49,18 @@ class ReportProjectTaskUser(models.Model):
|
||||
column1='project_task_id', column2='project_tags_id',
|
||||
string='Tags', readonly=True)
|
||||
parent_id = fields.Many2one('project.task', string='Parent Task', readonly=True)
|
||||
ancestor_id = fields.Many2one('project.task', string="Ancestor Task", readonly=True)
|
||||
# We are explicitly not using a related field in order to prevent the recomputing caused by the depends as the model is a report.
|
||||
rating_last_text = fields.Selection(RATING_TEXT, string="Rating Last Text", compute="_compute_rating_last_text", search="_search_rating_last_text")
|
||||
personal_stage_type_ids = fields.Many2many('project.task.type', relation='project_task_user_rel',
|
||||
column1='task_id', column2='stage_id',
|
||||
string="Personal Stage", readonly=True)
|
||||
milestone_id = fields.Many2one('project.milestone', readonly=True)
|
||||
milestone_reached = fields.Boolean('Is Milestone Reached', readonly=True)
|
||||
milestone_deadline = fields.Date('Milestone Deadline', readonly=True)
|
||||
message_is_follower = fields.Boolean(related='task_id.message_is_follower')
|
||||
is_blocked = fields.Boolean('Is Blocked', readonly=True)
|
||||
dependent_ids = fields.Many2many('project.task', relation='task_dependencies_rel', column1='depends_on_id',
|
||||
column2='task_id', string='Block', readonly=True,
|
||||
domain="[('allow_task_dependencies', '=', True), ('id', '!=', id)]")
|
||||
description = fields.Text(readonly=True)
|
||||
|
||||
def _compute_rating_last_text(self):
|
||||
for task_analysis in self:
|
||||
@@ -72,31 +75,33 @@ class ReportProjectTaskUser(models.Model):
|
||||
t.id as id,
|
||||
t.id as task_id,
|
||||
t.active,
|
||||
t.create_date as create_date,
|
||||
t.date_assign as date_assign,
|
||||
t.date_end as date_end,
|
||||
t.date_last_stage_update as date_last_stage_update,
|
||||
t.date_deadline as date_deadline,
|
||||
t.create_date,
|
||||
t.date_assign,
|
||||
t.date_end,
|
||||
t.date_last_stage_update,
|
||||
t.date_deadline,
|
||||
t.project_id,
|
||||
t.priority,
|
||||
t.name as name,
|
||||
t.company_id,
|
||||
t.partner_id,
|
||||
t.parent_id as parent_id,
|
||||
t.ancestor_id as ancestor_id,
|
||||
t.stage_id as stage_id,
|
||||
t.is_closed as is_closed,
|
||||
t.kanban_state as state,
|
||||
t.parent_id,
|
||||
t.stage_id,
|
||||
t.is_closed,
|
||||
t.kanban_state,
|
||||
t.milestone_id,
|
||||
pm.is_reached as milestone_reached,
|
||||
pm.deadline as milestone_deadline,
|
||||
CASE WHEN pm.id IS NOT NULL THEN true ELSE false END as has_late_and_unreached_milestone,
|
||||
t.is_blocked,
|
||||
t.description,
|
||||
NULLIF(t.rating_last_value, 0) as rating_last_value,
|
||||
AVG(rt.rating) as rating_avg,
|
||||
t.working_days_close as working_days_close,
|
||||
t.working_days_open as working_days_open,
|
||||
t.working_hours_open as working_hours_open,
|
||||
t.working_hours_close as working_hours_close,
|
||||
(extract('epoch' from (t.date_deadline-(now() at time zone 'UTC'))))/(3600*24) as delay_endings_days
|
||||
t.working_days_close,
|
||||
t.working_days_open,
|
||||
t.working_hours_open,
|
||||
t.working_hours_close,
|
||||
(extract('epoch' from (t.date_deadline-(now() at time zone 'UTC'))))/(3600*24) as delay_endings_days,
|
||||
CASE WHEN t.project_id IS NOT NULL OR t.parent_id IS NOT NULL THEN false ELSE true END as is_private,
|
||||
COUNT(td.task_id) as dependent_ids_count
|
||||
"""
|
||||
|
||||
def _group_by(self):
|
||||
@@ -109,7 +114,6 @@ class ReportProjectTaskUser(models.Model):
|
||||
t.date_last_stage_update,
|
||||
t.date_deadline,
|
||||
t.project_id,
|
||||
t.ancestor_id,
|
||||
t.priority,
|
||||
t.name,
|
||||
t.company_id,
|
||||
@@ -124,18 +128,21 @@ class ReportProjectTaskUser(models.Model):
|
||||
t.working_hours_open,
|
||||
t.working_hours_close,
|
||||
t.milestone_id,
|
||||
pm.is_reached,
|
||||
pm.deadline
|
||||
pm.id,
|
||||
td.depends_on_id
|
||||
"""
|
||||
|
||||
def _from(self):
|
||||
return f"""
|
||||
project_task t
|
||||
LEFT JOIN rating_rating rt ON rt.res_id = t.id
|
||||
AND rt.res_model = 'project.task'
|
||||
AND rt.consumed = True
|
||||
AND rt.rating >= {RATING_LIMIT_MIN}
|
||||
AND rt.res_model = 'project.task'
|
||||
AND rt.consumed = True
|
||||
AND rt.rating >= {RATING_LIMIT_MIN}
|
||||
LEFT JOIN project_milestone pm ON pm.id = t.milestone_id
|
||||
AND pm.is_reached = False
|
||||
AND pm.deadline <= CAST(now() AS DATE)
|
||||
LEFT JOIN task_dependencies_rel td ON td.depends_on_id = t.id
|
||||
"""
|
||||
|
||||
def _where(self):
|
||||
|
||||
@@ -41,66 +41,15 @@
|
||||
<record id="view_task_project_user_search" model="ir.ui.view">
|
||||
<field name="name">report.project.task.user.search</field>
|
||||
<field name="model">report.project.task.user</field>
|
||||
<field name="inherit_id" ref="project.view_task_search_form_project_fsm_base"/>
|
||||
<field name="mode">primary</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Tasks Analysis">
|
||||
<field name="name" string="Task"/>
|
||||
<field name="tag_ids"/>
|
||||
<field name="user_ids" context="{'active_test': False}"/>
|
||||
<field name="project_id"/>
|
||||
<field name="milestone_id" groups="project.group_project_milestone"/>
|
||||
<field name="ancestor_id" groups="project.group_subtask_project"/>
|
||||
<field name="stage_id"/>
|
||||
<field name="partner_id" operator="child_of"/>
|
||||
<field name="active"/>
|
||||
<field name="rating_last_text"/>
|
||||
<field name="date_assign"/>
|
||||
<field name="date_end"/>
|
||||
<field name="date_deadline"/>
|
||||
<field name="date_last_stage_update"/>
|
||||
<filter string="My Tasks" name="my_tasks" domain="[('user_ids', 'in', uid)]"/>
|
||||
<filter string="Followed Tasks" name="followed_by_me" domain="[('task_id.message_is_follower', '=', True)]"/>
|
||||
<filter string="Unassigned" name="unassigned" domain="[('user_ids', '=', False)]"/>
|
||||
<separator/>
|
||||
<filter string="My Projects" name="own_projects" domain="[('project_id.user_id', '=', uid)]"/>
|
||||
<filter string="My Favorite Projects" name="my_favorite_projects" domain="[('project_id.favorite_user_ids', 'in', [uid])]"/>
|
||||
<separator/>
|
||||
<filter string="High Priority" name="high_priority" domain="[('priority', '=', 1)]"/>
|
||||
<filter string="Low Priority" name="low_priority" domain="[('priority', '=', 0)]"/>
|
||||
<separator/>
|
||||
<filter string="Open" name="open_tasks" domain="[('is_closed', '=', False)]"/>
|
||||
<filter string="Closed" name="closed_tasks" domain="[('is_closed', '=', True)]"/>
|
||||
<separator/>
|
||||
<filter string="Late Milestones" name="late_milestone"
|
||||
domain="[('project_id.allow_milestones', '=', True), ('is_closed', '=', False), ('milestone_reached', '=', False), ('milestone_deadline', '<', context_today().strftime('%Y-%m-%d'))]"
|
||||
groups="project.group_project_milestone"
|
||||
/>
|
||||
<filter string="Late Tasks" name="late" domain="[('date_deadline', '<', context_today().strftime('%Y-%m-%d')), ('is_closed', '=', False)]"/>
|
||||
<filter name="rating_satisfied" string="Satisfied" domain="[('rating_avg', '>=', 3.66)]" groups="project.group_project_rating"/>
|
||||
<filter name="rating_okay" string="Okay" domain="[('rating_avg', '<', 3.66), ('rating_avg', '>=', 2.33)]" groups="project.group_project_rating"/>
|
||||
<filter name="dissatisfied" string="Dissatisfied" domain="[('rating_avg', '<', 2.33), ('rating_last_value', '!=', 0)]" groups="project.group_project_rating"/>
|
||||
<filter name="no_rating" string="No Rating" domain="[('rating_last_value', '=', 0)]" groups="project.group_project_rating"/>
|
||||
<separator/>
|
||||
<filter name="filter_date_deadline" date="date_deadline"/>
|
||||
<filter name="filter_date_assign" date="date_assign"/>
|
||||
<filter name="filter_date_last_stage_update" date="date_last_stage_update"/>
|
||||
<separator/>
|
||||
<filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
|
||||
<group expand="0" string="Extended Filters">
|
||||
<field name="priority"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</group>
|
||||
<group expand="1" string="Group By">
|
||||
<filter string="Stage" name="Stage" context="{'group_by': 'stage_id'}"/>
|
||||
<filter string="Personal Stage" name="personal_stage" context="{'group_by': 'personal_stage_type_ids'}"/>
|
||||
<filter string="Assignees" name="User" context="{'group_by': 'user_ids'}"/>
|
||||
<filter string="Ancestor Task" name="groupby_ancestor_task" context="{'group_by': 'ancestor_id'}" groups="project.group_subtask_project"/>
|
||||
<filter string="Milestone" name="milestone" context="{'group_by': 'milestone_id'}" groups="project.group_project_milestone"/>
|
||||
<filter string="Customer" name="Customer" context="{'group_by': 'partner_id'}"/>
|
||||
<filter string="Kanban State" name="kanban_state" context="{'group_by': 'state'}"/>
|
||||
<filter string="Deadline" name="deadline" context="{'group_by': 'date_deadline'}"/>
|
||||
<filter string="Creation Date" name="group_create_date" context="{'group_by': 'create_date'}"/>
|
||||
</group>
|
||||
<search position="attributes">
|
||||
<attribute name="string">Tasks Analysis</attribute>
|
||||
</search>
|
||||
<field name="display_project_id" position="attributes">
|
||||
<attribute name="name">project_id</attribute>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -119,4 +68,9 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_project_report_task_analysis"
|
||||
name="Tasks Analysis"
|
||||
action="project.action_project_task_user_tree"
|
||||
parent="menu_project_report"
|
||||
sequence="10"/>
|
||||
</odoo>
|
||||
|
||||
@@ -113,28 +113,17 @@
|
||||
<record id="project_sharing_project_task_view_tree" model="ir.ui.view">
|
||||
<field name="name">project.sharing.project.task.tree</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="project_task_view_tree_main_base"/>
|
||||
<field name="mode">primary</field>
|
||||
<field name="priority">999</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Tasks" sample="1" delete="0" import="0">
|
||||
<field name="is_closed" 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="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"/>
|
||||
<field name="portal_user_names" string="Assignees" 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" options="{'hide_label': True}" nolabel="1" optional="show"/>
|
||||
<field name="legend_blocked" invisible="1"/>
|
||||
<field name="legend_normal" invisible="1"/>
|
||||
<field name="legend_done" invisible="1"/>
|
||||
<field name="stage_id" invisible="context.get('set_visible',False)" optional="show"/>
|
||||
<tree position="attributes">
|
||||
<attribute name="delete">0</attribute>
|
||||
<attribute name="import">0</attribute>
|
||||
</tree>
|
||||
<field name="user_ids" position="replace">
|
||||
<field name="portal_user_names" string="Assignees"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -247,42 +236,11 @@
|
||||
<record id="project_sharing_project_task_view_search" model="ir.ui.view">
|
||||
<field name="name">project.task.search.form</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="project.view_task_search_form_project_base"/>
|
||||
<field name="mode">primary</field>
|
||||
<field name="priority">999</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Tasks">
|
||||
<field name="name" string="Task"/>
|
||||
<field name="tag_ids"/>
|
||||
<field name="portal_user_names" string="Assignees"/>
|
||||
<field string="Project" name="display_project_id"/>
|
||||
<field name="milestone_id" groups="project.group_project_milestone"/>
|
||||
<field name="stage_id"/>
|
||||
<field name="partner_id" operator="child_of"/>
|
||||
<filter string="Unassigned" name="unassigned" domain="[('user_ids', '=', False)]"/>
|
||||
<separator/>
|
||||
<filter string="High Priority" name="high_priority" domain="[('priority', '=', 1)]"/>
|
||||
<filter string="Low Priority" name="low_priority" domain="[('priority', '=', 0)]"/>
|
||||
<separator/>
|
||||
<filter string="Late Tasks" name="late" domain="[('date_deadline', '<', context_today().strftime('%Y-%m-%d')), ('is_closed', '=', False)]"/>
|
||||
<filter string="Tasks Due Today" name="tasks_due_today" domain="[('date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Late Milestones" name="late_milestone" domain="[('is_closed', '=', False), ('has_late_and_unreached_milestone', '=', True)]" groups="project.group_project_milestone"/>
|
||||
<separator/>
|
||||
<filter string="Open Tasks" name="open_tasks" domain="[('is_closed', '=', False)]"/>
|
||||
<filter string="Closed Tasks" name="closed_tasks" domain="[('is_closed', '=', True)]"/>
|
||||
<filter string="Closed Last 7 Days" name="closed_last_7_days" domain="[('is_closed', '=', True), ('date_last_stage_update', '>', datetime.datetime.now() - relativedelta(days=7))]"/>
|
||||
<filter string="Closed Last 30 Days" name="closed_last_30_days" domain="[('is_closed', '=', True), ('date_last_stage_update', '>', datetime.datetime.now() - relativedelta(days=30))]"/>
|
||||
<separator/>
|
||||
<filter invisible="1" string="Today Activities" name="activities_today"
|
||||
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter invisible="1" string="Future Activities" name="activities_upcoming_all"
|
||||
domain="[('activity_ids.date_deadline', '>', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Stage" name="stage" context="{'group_by': 'stage_id'}"/>
|
||||
<filter string="Milestone" name="milestone" context="{'group_by': 'milestone_id'}" groups="project.group_project_milestone"/>
|
||||
<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'}"/>
|
||||
</group>
|
||||
</search>
|
||||
<search position="inside"/>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -292,10 +250,6 @@
|
||||
<field name="view_mode">kanban,tree,form</field>
|
||||
<field name="search_view_id" ref="project.project_sharing_project_task_view_search"/>
|
||||
<field name="domain">[('display_project_id', '=', active_id)]</field>
|
||||
<field name="context">{
|
||||
'default_project_id': active_id,
|
||||
'delete': 0,
|
||||
}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
No tasks found. Let's create one!
|
||||
|
||||
@@ -11,52 +11,96 @@
|
||||
<menuitem id="menu_project_config" name="Configuration" parent="menu_main_pm"
|
||||
sequence="100" groups="project.group_project_manager"/>
|
||||
|
||||
<record id="view_task_search_form" model="ir.ui.view">
|
||||
<!-- Base search view, contains the fields and filters common to project, project sharing and fsm -->
|
||||
<record id="view_task_search_form_base" model="ir.ui.view">
|
||||
<field name="name">project.task.search.form</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Tasks">
|
||||
<field name="name" string="Task" filter_domain="['|', ('name', 'ilike', self), ('id', 'ilike', self)]"/>
|
||||
<field name="name" string="Tasks" filter_domain="['|', ('name', 'ilike', self), ('id', 'ilike', self)]"/>
|
||||
<field name="tag_ids"/>
|
||||
<field name="user_ids" filter_domain="[('user_ids.name', 'ilike', self), ('user_ids.active', 'in', [True, False])]"/>
|
||||
<field name="milestone_id" groups="project.group_project_milestone"/>
|
||||
<field name="ancestor_id" groups="project.group_subtask_project"/>
|
||||
<field name="user_ids" filter_domain="[('user_ids', 'ilike', self), ('user_ids.active', 'in', [True, False])]"/>
|
||||
<field name="stage_id"/>
|
||||
<field name="milestone_id" groups="project.group_project_milestone"/>
|
||||
<field name="partner_id" operator="child_of"/>
|
||||
<field name="description"/>
|
||||
<field name="rating_last_text"/>
|
||||
<filter string="My Tasks" name="my_tasks" domain="[('user_ids', 'in', uid)]"/>
|
||||
<filter string="Followed Tasks" name="followed_by_me" domain="[('message_is_follower', '=', True)]"/>
|
||||
<filter string="Followed" name="followed_by_me" domain="[('message_is_follower', '=', True)]"/>
|
||||
<filter string="Unassigned" name="unassigned" domain="[('user_ids', '=', False)]"/>
|
||||
<separator/>
|
||||
<filter string="High Priority" name="high_priority" domain="[('priority', '=', 1)]"/>
|
||||
<filter string="Low Priority" name="low_priority" domain="[('priority', '=', 0)]"/>
|
||||
<separator/>
|
||||
<separator invisible="context.get('default_project_id')"/>
|
||||
<filter string="Favorite Projects" name="favorite_projects" domain="[('project_id.is_favorite', '=', True)]" invisible="context.get('default_project_id')"/>
|
||||
<separator groups="project.group_project_task_dependencies"/>
|
||||
<filter string="Blocked" name="blocked" domain="[('is_blocked', '=', True)]" groups="project.group_project_task_dependencies"/>
|
||||
<filter string="Not Blocked" name="not_blocked" domain="[('is_blocked', '=', False), ('is_private', '=', False)]" groups="project.group_project_task_dependencies"/>
|
||||
<filter string="Not Blocked" name="not_blocked" domain="[('is_blocked', '=', False)]" groups="project.group_project_task_dependencies"/>
|
||||
<separator groups="project.group_project_task_dependencies"/>
|
||||
<filter string="Blocking" name="blocking" domain="[('is_closed', '=', False), ('dependent_ids', '!=', False)]" groups="project.group_project_task_dependencies"/>
|
||||
<filter string="Not Blocking" name="not_blocking" domain="['|', ('is_closed', '=', True), ('dependent_ids', '=', False), ('is_private', '=', False)]" groups="project.group_project_task_dependencies"/>
|
||||
<separator groups="project.group_project_task_dependencies"/>
|
||||
<filter string="Late Milestones" name="late_milestone" domain="[('is_closed', '=', False), ('has_late_and_unreached_milestone', '=', True)]" groups="project.group_project_milestone"/>
|
||||
<filter string="Late Tasks" name="late" domain="[('date_deadline', '<', context_today().strftime('%Y-%m-%d')), ('is_closed', '=', False)]"/>
|
||||
<filter string="Tasks Due Today" name="tasks_due_today" domain="[('date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Stalling for 30 Days+" name="stall_last_30_days" domain="[('is_closed', '=', False), ('date_last_stage_update', '<=', datetime.datetime.now() - relativedelta(days=30))]"/>
|
||||
<separator/>
|
||||
<filter string="Open Tasks" name="open_tasks" domain="[('is_closed', '=', False)]"/>
|
||||
<filter string="Closed Tasks" name="closed_tasks" domain="[('is_closed', '=', True)]"/>
|
||||
<filter string="Closed Last 7 Days" name="closed_last_7_days" domain="[('is_closed', '=', True), ('date_last_stage_update', '>', datetime.datetime.now() - relativedelta(days=7))]"/>
|
||||
<filter string="Closed Last 30 Days" name="closed_last_30_days" domain="[('is_closed', '=', True), ('date_last_stage_update', '>', datetime.datetime.now() - relativedelta(days=30))]"/>
|
||||
<separator/>
|
||||
<filter name="rating_satisfied" string="Satisfied" domain="[('rating_avg', '>=', 3.66)]" groups="project.group_project_rating"/>
|
||||
<filter name="rating_okay" string="Okay" domain="[('rating_avg', '<', 3.66), ('rating_avg', '>=', 2.33)]" groups="project.group_project_rating"/>
|
||||
<filter name="dissatisfied" string="Dissatisfied" domain="[('rating_avg', '<', 2.33), ('rating_last_value', '!=', 0)]" groups="project.group_project_rating"/>
|
||||
<filter name="no_rating" string="No Rating" domain="[('rating_last_value', '=', 0)]" groups="project.group_project_rating"/>
|
||||
<separator/>
|
||||
<filter string="Unread Messages" name="message_needaction" domain="[('message_needaction', '=', True)]" groups="mail.group_mail_notification_type_inbox"/>
|
||||
<filter string="Last Stage Update" name="date_last_stage_update" date="date_last_stage_update"/>
|
||||
<separator/>
|
||||
<filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Assignees" name="user" context="{'group_by': 'user_ids'}"/>
|
||||
<filter string="Stage" name="stage" context="{'group_by': 'stage_id'}"/>
|
||||
<filter string="Milestone" name="milestone" context="{'group_by': 'milestone_id'}" groups="project.group_project_milestone"/>
|
||||
<filter string="Customer" name="customer" context="{'group_by': 'partner_id'}"/>
|
||||
<filter string="Company" name="company_id" context="{'group_by': 'company_id'}" groups="base.group_multi_company"/>
|
||||
<filter string="Creation Date" name="create_date" context="{'group_by': 'create_date'}"/>
|
||||
<filter string="Assignement Date" name="date_assign" context="{'group_by': 'date_assign'}"/>
|
||||
<filter string="Last Stage Update" name="last_stage_update" context="{'group_by': 'date_last_stage_update'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Contains the fields and filters common to projet and fsm -->
|
||||
<record id="view_task_search_form_project_fsm_base" model="ir.ui.view">
|
||||
<field name="name">project.task.search.form.project.base</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="view_task_search_form_base"/>
|
||||
<field name="mode">primary</field>
|
||||
<field name="arch" type="xml">
|
||||
<field name="stage_id" position="after">
|
||||
<field name="display_project_id" string="Project"/>
|
||||
</field>
|
||||
<field name="partner_id" position="after">
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</field>
|
||||
<field name="description" position="after">
|
||||
<filter string="My Tasks" name="my_tasks" domain="[('user_ids', 'in', uid)]"/>
|
||||
</field>
|
||||
<filter name="stage" position="after">
|
||||
<filter string="Project" name="project" context="{'group_by': 'project_id'}"/>
|
||||
</filter>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Base search view for project only -->
|
||||
<record id="view_task_search_form_project_base" model="ir.ui.view">
|
||||
<field name="name">project.task.search.form.project.base</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="view_task_search_form_project_fsm_base"/>
|
||||
<field name="mode">primary</field>
|
||||
<field name="arch" type="xml">
|
||||
<filter name="date_last_stage_update" position="after">
|
||||
<filter string="Deadline" name="date_deadline" date="date_deadline"/>
|
||||
</filter>
|
||||
<filter name="last_stage_update" position="after">
|
||||
<filter string="Deadline" name="deadline" context="{'group_by': 'date_deadline'}"/>
|
||||
</filter>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_task_search_form" model="ir.ui.view">
|
||||
<field name="name">project.task.search.form</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="view_task_search_form_project_base"></field>
|
||||
<field name="mode">primary</field>
|
||||
<field name="arch" type="xml">
|
||||
<filter name="inactive" position="before">
|
||||
<filter string="Unread Messages" name="message_needaction" domain="[('message_needaction', '=', True)]" groups="mail.group_mail_notification_type_inbox"/>
|
||||
<separator/>
|
||||
</filter>
|
||||
<filter name="inactive" position="after">
|
||||
<separator invisible="1"/>
|
||||
<filter invisible="1" string="Late Activities" name="activities_overdue"
|
||||
domain="[('my_activity_date_deadline', '<', context_today().strftime('%Y-%m-%d'))]"
|
||||
help="Show all records which has next action date is before today"/>
|
||||
@@ -64,41 +108,7 @@
|
||||
domain="[('my_activity_date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter invisible="1" string="Future Activities" name="activities_upcoming_all"
|
||||
domain="[('my_activity_date_deadline', '>', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Stage" name="stage" context="{'group_by': 'stage_id'}"/>
|
||||
<filter string="Personal Stage" name="personal_stage" context="{'group_by': 'personal_stage_type_ids'}"/>
|
||||
<filter string="Assignees" name="user" context="{'group_by': 'user_ids'}"/>
|
||||
<filter string="Ancestor Task" name="groupby_ancestor_task" context="{'group_by': 'ancestor_id'}" groups="project.group_subtask_project"/>
|
||||
<filter string="Milestone" name="milestone" context="{'group_by': 'milestone_id'}" groups="project.group_project_milestone"/>
|
||||
<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="//field[@name='user_ids']" position='after'>
|
||||
<field string="Project" name="display_project_id"/>
|
||||
</xpath>
|
||||
<xpath expr="//filter[@name='my_tasks']" position='after'>
|
||||
<filter string="My Private Tasks" name="my_private_task" domain="[('project_id', '=', False), ('user_ids', 'in', uid)]"/>
|
||||
</xpath>
|
||||
<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])]"/>
|
||||
</xpath>
|
||||
<xpath expr="//filter[@name='user']" position='after'>
|
||||
<filter string="Project" name="project" context="{'group_by': 'project_id'}"/>
|
||||
</xpath>
|
||||
</filter>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -644,27 +654,17 @@
|
||||
<field name="name" string="Project"/>
|
||||
<field name="tag_ids"/>
|
||||
<field name="user_id" string="Project Manager"/>
|
||||
<field name="partner_id" string="Customer" filter_domain="[('partner_id', 'child_of', self)]"/>
|
||||
<field name="analytic_account_id"/>
|
||||
<field name="stage_id" groups="project.group_project_stages"/>
|
||||
<field name="partner_id" string="Customer" filter_domain="[('partner_id', 'child_of', self)]"/>
|
||||
<filter string="My Projects" name="own_projects" domain="[('user_id', '=', uid)]"/>
|
||||
<filter string="My Favorites" name="my_projects" domain="[('favorite_user_ids', 'in', uid)]"/>
|
||||
<filter string="Followed" name="followed_by_me" domain="[('message_is_follower', '=', True)]"/>
|
||||
<filter string="Unassigned" name="unassigned_projects" domain="[('user_id', '=', False)]"/>
|
||||
<separator/>
|
||||
<filter string="Late Milestones" name="late_milestones" domain="[('is_milestone_exceeded', '=', True)]" groups="project.group_project_milestone"/>
|
||||
<separator/>
|
||||
<filter string="Open" name="open_project" domain="[('stage_id.fold', '=', False)]" groups="project.group_project_stages"/>
|
||||
<filter string="Closed" name="closed_project" domain="[('stage_id.fold', '=', True)]" groups="project.group_project_stages"/>
|
||||
<separator/>
|
||||
<filter string="Start Date" name="start_date" date="date_start"/>
|
||||
<filter string="End Date" name="end_date" date="date"/>
|
||||
<separator/>
|
||||
<filter name="rating_satisfied" string="Satisfied" domain="[('rating_active', '=', True), ('rating_avg', '>=', 3.66)]" groups="project.group_project_rating"/>
|
||||
<filter name="rating_okay" string="Okay" domain="[('rating_active', '=', True), ('rating_avg', '<', 3.66), ('rating_avg', '>=', 2.33)]" groups="project.group_project_rating"/>
|
||||
<filter name="dissatisfied" string="Dissatisfied" domain="[('rating_active', '=', True), ('rating_avg', '<', 2.33), ('rating_avg', '>', 0)]" groups="project.group_project_rating"/>
|
||||
<filter name="no_rating" string="No Rating" domain="['|', ('rating_active', '=', False), ('rating_avg', '=', 0)]" groups="project.group_project_rating"/>
|
||||
<separator/>
|
||||
<filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
|
||||
<separator/>
|
||||
<filter invisible="1" string="Late Activities" name="activities_overdue"
|
||||
@@ -676,9 +676,10 @@
|
||||
domain="[('my_activity_date_deadline', '>', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Project Manager" name="Manager" context="{'group_by': 'user_id'}"/>
|
||||
<filter string="Customer" name="Partner" context="{'group_by': 'partner_id'}"/>
|
||||
<filter string="Status" name="status" context="{'group_by': 'last_update_status'}"/>
|
||||
<filter string="Stage" name="groupby_stage" context="{'group_by': 'stage_id'}" groups="project.group_project_stages"/>
|
||||
<filter string="Status" name="status" context="{'group_by': 'last_update_status'}"/>
|
||||
<filter string="Customer" name="Partner" context="{'group_by': 'partner_id'}"/>
|
||||
<filter string="Company" name="company" context="{'group_by': 'company_id'}" groups="base.group_multi_company"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
@@ -689,20 +690,16 @@
|
||||
<field name="model">project.project</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree decoration-muted="active == False" string="Projects" multi_edit="1" sample="1" default_order="sequence, name, id">
|
||||
<field name="sequence" optional="show" widget="handle"/>
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="message_needaction" invisible="1"/>
|
||||
<field name="active" invisible="1"/>
|
||||
<field name="is_favorite" nolabel="1" width="1" widget="boolean_favorite"/>
|
||||
<field name="is_favorite" string="Favorite" nolabel="1" width="1" widget="boolean_favorite" optional="hide"/>
|
||||
<field name="display_name" string="Name" class="fw-bold"/>
|
||||
<field name="partner_id" optional="show" string="Customer"/>
|
||||
<field name="privacy_visibility" optional="hide"/>
|
||||
<field name="company_id" optional="show" groups="base.group_multi_company" options="{'no_create': True, 'no_create': True}"/>
|
||||
<field name="company_id" optional="show" groups="base.group_multi_company" options="{'no_create': True, 'no_open': True}"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="analytic_account_id" optional="hide" groups="analytic.group_analytic_accounting"/>
|
||||
<field name="date_start" string="Start Date" widget="daterange" options="{'related_end_date': 'date'}"/>
|
||||
<field name="date" string="End Date" widget="daterange" options="{'related_start_date': 'date_start'}"/>
|
||||
<field name="activity_ids" string="Next Activity" widget="list_activity" optional="hide"/>
|
||||
<field name="my_activity_date_deadline" string="My Deadline" widget="remaining_days" optional="hide"/>
|
||||
<field name="date_start" string="Start Date" widget="daterange" options="{'related_end_date': 'date'}" optional="hide"/>
|
||||
<field name="date" string="End Date" widget="daterange" options="{'related_start_date': 'date_start'}" optional="hide"/>
|
||||
<field name="user_id" optional="show" string="Project Manager" widget="many2one_avatar_user" options="{'no_open':True, 'no_create': True, 'no_create_edit': True}"/>
|
||||
<field name="last_update_color" invisible="1"/>
|
||||
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}" optional="hide"/>
|
||||
@@ -1197,8 +1194,8 @@
|
||||
</page>
|
||||
<page name="sub_tasks_page" string="Sub-tasks" attrs="{'invisible': [('allow_subtasks', '=', False)]}">
|
||||
<field name="child_ids"
|
||||
context="{'search_view_ref' : 'project.view_task_search_form_extended', 'default_project_id': project_id if not parent_id or not display_project_id else display_project_id, 'default_user_ids': user_ids, 'default_parent_id': id,
|
||||
'default_partner_id': partner_id, 'default_milestone_id': allow_milestones and milestone_id, 'search_default_display_project_id': project_id, 'search_default_open_tasks': 1 }"
|
||||
context="{'search_view_ref' : 'project.view_task_search_form', 'default_project_id': project_id if not parent_id or not display_project_id else display_project_id, 'default_user_ids': user_ids, 'default_parent_id': id,
|
||||
'default_partner_id': partner_id, 'default_milestone_id': allow_milestones and milestone_id, 'search_default_display_project_id': project_id}"
|
||||
widget="many2many"
|
||||
domain="['!', ('id', 'parent_of', id)]">
|
||||
<tree editable="bottom" decoration-muted="is_closed == True">
|
||||
@@ -1239,7 +1236,7 @@
|
||||
</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"
|
||||
context="{'default_project_id' : project_id, 'search_view_ref' : 'project.view_task_search_form_extended', 'search_default_display_project_id': project_id, 'search_default_open_tasks': 1}">
|
||||
context="{'default_project_id' : project_id, 'search_view_ref' : 'project.view_task_search_form', 'search_default_display_project_id': project_id}">
|
||||
<tree editable="bottom" decoration-muted="is_closed == True">
|
||||
<field name="allow_milestones" invisible="1"/>
|
||||
<field name="parent_id" invisible="1" />
|
||||
@@ -1329,7 +1326,7 @@
|
||||
<group>
|
||||
<group>
|
||||
<field name="is_analytic_account_id_changed" invisible="1"/>
|
||||
<field name="parent_id" attrs="{'invisible': [('allow_subtasks', '=', False)]}" groups="base.group_no_one" context="{'search_view_ref' : 'project.view_task_search_form_extended','search_default_display_project_id': project_id}"/>
|
||||
<field name="parent_id" attrs="{'invisible': [('allow_subtasks', '=', False)]}" groups="base.group_no_one" context="{'search_view_ref' : 'project.view_task_search_form','search_default_display_project_id': project_id}"/>
|
||||
<field name="analytic_account_id" groups="analytic.group_analytic_accounting" context="{'default_partner_id': partner_id}"/>
|
||||
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
|
||||
<field name="sequence" groups="base.group_no_one"/>
|
||||
@@ -1493,13 +1490,13 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_task_tree2" model="ir.ui.view">
|
||||
<field name="name">project.task.tree</field>
|
||||
<!-- The main base of the project.task tree view -->
|
||||
<record id="project_task_view_tree_main_base" model="ir.ui.view">
|
||||
<field name="name">project.task.view.tree.main.base</field>
|
||||
<field name="model">project.task</field>
|
||||
<field eval="2" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Tasks" multi_edit="1" sample="1" js_class="project_task_list" default_group_by="stage_id">
|
||||
<field name="message_needaction" invisible="1" readonly="1"/>
|
||||
<tree string="Tasks" sample="1">
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="is_closed" invisible="1" />
|
||||
<field name="sequence" invisible="1" readonly="1"/>
|
||||
<field name="allow_milestones" invisible="1"/>
|
||||
@@ -1507,35 +1504,62 @@
|
||||
<field name="priority" widget="priority" optional="show" nolabel="1"/>
|
||||
<field name="child_text" invisible="1"/>
|
||||
<field name="allow_subtasks" invisible="1"/>
|
||||
<field name="name" widget="name_with_subtask_count"/>
|
||||
<field name="project_id" widget="project_private_task" optional="show" readonly="1" options="{'no_open': 1}"/>
|
||||
<field name="milestone_id" attrs="{'invisible': [('allow_milestones', '=', False)]}" context="{'default_project_id': project_id}" groups="project.group_project_milestone"/>
|
||||
<field name="name" string="Title" widget="name_with_subtask_count"/>
|
||||
<field name="project_id" widget="project_private_task" optional="show" readonly="1" options="{'no_open': 1}" invisible="context.get('default_project_id')"/>
|
||||
<field name="milestone_id" attrs="{'invisible': [('allow_milestones', '=', False)]}" context="{'default_project_id': project_id}" groups="project.group_project_milestone" optional="hide"/>
|
||||
<field name="partner_id" optional="hide"/>
|
||||
<field name="parent_id" optional="hide" attrs="{'invisible': [('allow_subtasks', '=', False)]}" groups="base.group_no_one"/>
|
||||
<field name="user_ids" optional="show" widget="many2many_avatar_user" domain="[('share', '=', False), ('active', '=', True)]" options='{"no_quick_create": True}'/>
|
||||
<field name="company_id" groups="base.group_multi_company" optional="show" readonly="True"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="create_date" optional="hide"/>
|
||||
<field name="date_last_stage_update" optional="hide"/>
|
||||
<field name="activity_ids" string="Next Activity" widget="list_activity" optional="show"/>
|
||||
<field name="my_activity_date_deadline" string="My Deadline" widget="remaining_days" optional="hide"/>
|
||||
<field name="user_ids" optional="show" widget="many2many_avatar_user" domain="[('share', '=', False), ('active', '=', True)]"/>
|
||||
<field name="company_id" groups="base.group_multi_company" optional="show" invisible="context.get('default_project_id')"/>
|
||||
<field name="date_deadline" optional="hide" widget="remaining_days" attrs="{'invisible': [('is_closed', '=', True)]}"/>
|
||||
<field name="legend_normal" invisible="1"/>
|
||||
<field name="legend_done" invisible="1"/>
|
||||
<field name="legend_blocked" invisible="1"/>
|
||||
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}" optional="show" context="{'project_id': project_id}"/>
|
||||
<field name="kanban_state" widget="state_selection" optional="show" options="{'hide_label': True}" nolabel="1" required="0"/>
|
||||
<field name="stage_id" invisible="context.get('set_visible',False)" optional="show" readonly="not context.get('default_project_id')"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- common base of the tree view in project app -->
|
||||
<record id="project_task_view_tree_base" model="ir.ui.view">
|
||||
<field name="name">project.task.view.tree.base</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="project_task_view_tree_main_base"/>
|
||||
<field name="mode">primary</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree position="attributes">
|
||||
<attribute name="multi_edit">1</attribute>
|
||||
<attribute name="js_class">project_task_list</attribute>
|
||||
<attribute name="default_group_by">stage_id</attribute>
|
||||
</tree>
|
||||
<field name="date_deadline" position="after">
|
||||
<field name="activity_ids" string="Next Activity" widget="list_activity" optional="show"/>
|
||||
<field name="my_activity_date_deadline" string="My Deadline" widget="remaining_days" options="{'allow_order': '1'}" optional="hide"/>
|
||||
<field name="rating_active" invisible="1"/>
|
||||
<field name="rating_last_text" string="Rating" decoration-danger="rating_last_text == 'ko'"
|
||||
decoration-warning="rating_last_text == 'ok'" decoration-success="rating_last_text == 'top'"
|
||||
attrs="{'invisible': ['|', ('rating_active', '=', False), ('rating_last_text', '=', 'none')]}"
|
||||
class="fw-bold" widget="badge" optional="hide" groups="project.group_project_rating"/>
|
||||
<field name="legend_normal" invisible="1"/>
|
||||
<field name="legend_done" invisible="1"/>
|
||||
<field name="legend_blocked" invisible="1"/>
|
||||
<field name="kanban_state" widget="state_selection" optional="show" options="{'hide_label': True}" nolabel="1" required="0"/>
|
||||
<field name="stage_id" invisible="context.get('set_visible',False)" optional="show" readonly="not context.get('default_project_id')"/>
|
||||
</field>
|
||||
<tree position="inside">
|
||||
<field name="recurrence_id" invisible="1" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- main tree view -->
|
||||
<record id="view_task_tree2" model="ir.ui.view">
|
||||
<field name="name">project.task.tree</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="project_task_view_tree_base"/>
|
||||
<field name="mode">primary</field>
|
||||
<field name="priority">2</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree position="inside"/>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_task_calendar" model="ir.ui.view">
|
||||
<field name="name">project.task.calendar</field>
|
||||
<field name="model">project.task</field>
|
||||
@@ -1662,6 +1686,18 @@
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="view_task_tree2"/>
|
||||
<field name="mode">primary</field>
|
||||
<field name="arch" type="xml">
|
||||
<field name="stage_id" position="replace">
|
||||
<field name="stage_display" optional="show" string="Stage"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="open_view_all_tasks_list_view" model="ir.ui.view">
|
||||
<field name="name">open.view.all.tasks.list.view</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="view_task_tree2"/>
|
||||
<field name="mode">primary</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree position="attributes">
|
||||
<attribute name="default_group_by">personal_stage_type_ids</attribute>
|
||||
@@ -1691,8 +1727,9 @@
|
||||
<field name="name">My Tasks</field>
|
||||
<field name="res_model">project.task</field>
|
||||
<field name="view_mode">kanban,tree,form,calendar,pivot,graph,activity</field>
|
||||
<field name="context">{'search_default_my_tasks': 1, 'search_default_open_tasks': 1, 'all_task': 0, 'default_user_ids': [(4, uid)]}</field>
|
||||
<field name="search_view_id" ref="view_task_search_form_extended"/>
|
||||
<field name="context">{'search_default_my_tasks': 1, 'all_task': 0, 'default_user_ids': [(4, uid)]}</field>
|
||||
<field name="search_view_id" ref="view_task_search_form"/>
|
||||
<field name="domain">[('user_ids', 'in', uid)]</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
No tasks found. Let's create one!
|
||||
@@ -1738,8 +1775,8 @@
|
||||
<field name="name">All Tasks</field>
|
||||
<field name="res_model">project.task</field>
|
||||
<field name="view_mode">tree,kanban,form,calendar,pivot,graph,activity</field>
|
||||
<field name="context">{'search_default_open_tasks': 1, 'default_user_ids': [(4, uid)]}</field>
|
||||
<field name="search_view_id" ref="view_task_search_form_extended"/>
|
||||
<field name="context">{'default_user_ids': [(4, uid)]}</field>
|
||||
<field name="search_view_id" ref="view_task_search_form"/>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
No tasks found. Let's create one!
|
||||
@@ -1755,7 +1792,7 @@
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="act_window_id" ref="action_view_all_task"/>
|
||||
<field name="view_id" ref="open_view_all_tasks_list_view"/>
|
||||
<field name="view_id" ref="open_view_my_tasks_list_view"/>
|
||||
</record>
|
||||
<record id="open_view_all_task_list_kanban" model="ir.actions.act_window.view">
|
||||
<field name="sequence" eval="20"/>
|
||||
@@ -1789,7 +1826,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_extended"/>
|
||||
<field name="search_view_id" ref="view_task_search_form"/>
|
||||
</record>
|
||||
|
||||
<record id="action_view_task_overpassed_draft" model="ir.actions.act_window">
|
||||
@@ -1798,7 +1835,7 @@
|
||||
<field name="view_mode">tree,form,calendar,graph,kanban</field>
|
||||
<field name="domain">[('is_closed', '=', False), ('date_deadline','<',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_extended"/>
|
||||
<field name="search_view_id" ref="view_task_search_form"/>
|
||||
</record>
|
||||
|
||||
<!-- Opening task when double clicking on project -->
|
||||
@@ -1906,12 +1943,6 @@
|
||||
<menuitem id="menu_project_report" name="Reporting"
|
||||
parent="menu_main_pm" sequence="99"/>
|
||||
|
||||
<menuitem id="menu_project_report_task_analysis"
|
||||
name="Tasks Analysis"
|
||||
action="project.action_project_task_user_tree"
|
||||
parent="menu_project_report"
|
||||
sequence="10"/>
|
||||
|
||||
<menuitem id="rating_rating_menu_project"
|
||||
action="rating_rating_action_project_report"
|
||||
parent="menu_project_report"
|
||||
|
||||
@@ -13,7 +13,6 @@ This module allows to generate a project/task from sales orders.
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'security/sale_project_security.xml',
|
||||
'report/project_report_views.xml',
|
||||
'views/product_views.xml',
|
||||
'views/project_task_views.xml',
|
||||
'views/sale_order_views.xml',
|
||||
|
||||
@@ -543,6 +543,13 @@ class Project(models.Model):
|
||||
action_window['res_id'] = vendor_bill_ids[0]
|
||||
return action_window
|
||||
|
||||
def action_project_sharing(self):
|
||||
action = super().action_project_sharing()
|
||||
action['context'].update({
|
||||
'sale_show_partner_name': True,
|
||||
})
|
||||
return action
|
||||
|
||||
class ProjectTask(models.Model):
|
||||
_inherit = "project.task"
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="view_task_project_user_search_inherited" model="ir.ui.view">
|
||||
<field name="name">report.project.task.user.search.inherited</field>
|
||||
<field name="model">report.project.task.user</field>
|
||||
<field name="inherit_id" ref="project.view_task_project_user_search" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='partner_id']" position="after">
|
||||
<field name="sale_order_id"/>
|
||||
</xpath>
|
||||
<xpath expr="//filter[@name='Customer']" position="after">
|
||||
<filter string="Sales Order Item" name="sale_line_id" context="{'group_by': 'sale_line_id'}"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -25,18 +25,4 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="project_sharing_inherit_project_task_view_search" model="ir.ui.view">
|
||||
<field name="name">project.task.search.inherit</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="project.project_sharing_project_task_view_search"/>
|
||||
<field name="arch" type="xml">
|
||||
<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="//group/filter[@name='customer']" position="after">
|
||||
<filter name="sale_order_id" string="Sales Order Id" context="{'group_by': 'sale_order_id'}" />
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -1,6 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="project.action_view_task" model="ir.actions.act_window">
|
||||
<field name="context">{'sale_show_partner_name': True}</field>
|
||||
</record>
|
||||
|
||||
<record id="project.action_view_my_task" model="ir.actions.act_window">
|
||||
<field name="context">{'sale_show_partner_name': True}</field>
|
||||
</record>
|
||||
|
||||
<record id="project.action_view_all_task" model="ir.actions.act_window">
|
||||
<field name="context">{'sale_show_partner_name': True}</field>
|
||||
</record>
|
||||
|
||||
<record id="project.action_project_task_user_tree" model="ir.actions.act_window">
|
||||
<field name="context">{'sale_show_partner_name': True}</field>
|
||||
</record>
|
||||
|
||||
<record id="project_project_view_inherit_project_filter" model="ir.ui.view">
|
||||
<field name="name">project.project.select.inherit.project</field>
|
||||
<field name="model">project.project</field>
|
||||
@@ -86,7 +102,7 @@
|
||||
<record id="view_task_tree2_inherit_sale_project" model="ir.ui.view">
|
||||
<field name="name">project.task.form.inherit.sale.project</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="project.view_task_tree2"/>
|
||||
<field name="inherit_id" ref="project.project_task_view_tree_base"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='partner_id']" position="after">
|
||||
<field name="sale_line_id" optional="hide" options="{'no_create': True}" readonly="1"/>
|
||||
@@ -97,14 +113,11 @@
|
||||
<record id="project_task_view_search" model="ir.ui.view">
|
||||
<field name="name">project.task.search.inherit</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="project.view_task_search_form"/>
|
||||
<field name="inherit_id" ref="project.view_task_search_form_project_base"/>
|
||||
<field name="arch" type="xml">
|
||||
<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_line_id" context="{'group_by': 'sale_line_id'}"/>
|
||||
</xpath>
|
||||
<field name="partner_id" position="after">
|
||||
<field name="sale_order_id" filter_domain="['|', ('sale_order_id', 'ilike', self), ('sale_line_id', 'ilike', self)]"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="project.open_view_project_all_config" model="ir.actions.act_window">
|
||||
<field name="context">{'default_allow_billable': True}</field>
|
||||
<field name="context">{'default_allow_billable': True, 'sale_show_partner_name': True}</field>
|
||||
</record>
|
||||
<record id="project.open_view_project_all_config_group_stage" model="ir.actions.act_window">
|
||||
<field name="context">{'default_allow_billable': True}</field>
|
||||
<field name="context">{'default_allow_billable': True, 'sale_show_partner_name': True}</field>
|
||||
</record>
|
||||
<record id="project.open_view_project_all" model="ir.actions.act_window">
|
||||
<field name="context">{'default_allow_billable': True}</field>
|
||||
<field name="context">{'default_allow_billable': True, 'sale_show_partner_name': True}</field>
|
||||
</record>
|
||||
<record id="project.open_view_project_all_group_stage" model="ir.actions.act_window">
|
||||
<field name="context">{'default_allow_billable': True, 'search_default_groupby_stage': 1}</field>
|
||||
<field name="context">{'default_allow_billable': True, 'sale_show_partner_name': True, 'search_default_groupby_stage': 1}</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import project_report
|
||||
from . import timesheets_analysis_report
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ReportProjectTaskUser(models.Model):
|
||||
_inherit = 'report.project.task.user'
|
||||
|
||||
remaining_hours_so = fields.Float('Remaining Hours on SO', readonly=True)
|
||||
|
||||
def _select(self):
|
||||
return super()._select() + """,
|
||||
sol.remaining_hours as remaining_hours_so
|
||||
"""
|
||||
|
||||
def _group_by(self):
|
||||
return super()._group_by() + """,
|
||||
sol.remaining_hours
|
||||
"""
|
||||
|
||||
def _from(self):
|
||||
return super()._from() + """
|
||||
LEFT JOIN sale_order_line sol ON t.id = sol.task_id
|
||||
"""
|
||||
@@ -144,10 +144,6 @@ tour.register('sale_timesheet_tour', {
|
||||
}, {
|
||||
trigger: 'div[name="partner_id"] ul > li:first-child > a:contains(Freeman)',
|
||||
content: 'Select the customer in the autocomplete dropdown',
|
||||
}, {
|
||||
trigger: 'a.nav-link[name="billing_employee_rate"]',
|
||||
extra_trigger: 'div.o_notebook_headers',
|
||||
content: 'Click on Invoicing tab to configure the invoicing of this project.',
|
||||
}, {
|
||||
trigger: 'div[name="sale_line_id"] input',
|
||||
content: 'Select a Sales Order Item as Default Sales Order Item for each task in this project.',
|
||||
@@ -155,6 +151,10 @@ tour.register('sale_timesheet_tour', {
|
||||
}, {
|
||||
trigger: '[name="sale_line_id"] ul.ui-autocomplete > li:first-child > a:not(:has(i.fa))',
|
||||
content: 'Select the Sales Order Item in the autocomplete dropdown.',
|
||||
}, {
|
||||
trigger: 'a.nav-link[name="billing_employee_rate"]',
|
||||
extra_trigger: 'div.o_notebook_headers',
|
||||
content: 'Click on Invoicing tab to configure the invoicing of this project.',
|
||||
}, {
|
||||
trigger: 'div[name="sale_line_employee_ids"] td.o_field_x2many_list_row_add > a[role="button"]',
|
||||
content: 'Click on Add a line on the mapping list view.',
|
||||
@@ -195,9 +195,9 @@ tour.register('sale_timesheet_tour', {
|
||||
trigger: 'div[name="partner_id"] ul > li:first-child > a:contains(Freeman)',
|
||||
content: 'Select the customer in the autocomplete dropdown',
|
||||
}, {
|
||||
trigger: 'a.nav-link[name="billing_employee_rate"]',
|
||||
trigger: 'a.nav-link[name="settings"]',
|
||||
extra_trigger: 'div.o_notebook_headers',
|
||||
content: 'Click on Invoicing tab to configure the invoicing of this project.',
|
||||
content: 'Click on Settings tab to configure this project.',
|
||||
}, {
|
||||
trigger: 'div[name="sale_line_id"] input',
|
||||
content: 'Select the first sale order of the list',
|
||||
|
||||
@@ -39,15 +39,4 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="project_sharing_inherit_project_task_view_tree_sale_timesheet" model="ir.ui.view">
|
||||
<field name="name">project.task.tree.inherit.sale.timesheet</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="hr_timesheet.project_sharing_inherit_project_task_view_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='remaining_hours']" position="after">
|
||||
<field name="remaining_hours_so" optional="hide" widget="timesheet_uom"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -6,18 +6,12 @@
|
||||
<field name="model">project.project</field>
|
||||
<field name="inherit_id" ref="hr_timesheet.project_invoice_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//page[@name='settings']//field[@name='analytic_account_id']" position="before">
|
||||
<field name="sale_line_id" groups="!sales_team.group_sale_salesman" options="{'no_create': True, 'no_edit': True, 'delete': False, 'no_open': True}"/>
|
||||
<field name="sale_line_id" groups="sales_team.group_sale_salesman" options="{'no_create': True, 'no_edit': True, 'delete': False}"/>
|
||||
</xpath>
|
||||
<xpath expr="//page[@name='settings']" position="after">
|
||||
<page name="billing_employee_rate" string="Invoicing" attrs="{'invisible': ['|', ('allow_billable', '=', False), ('partner_id', '=', False)]}">
|
||||
<group>
|
||||
<group>
|
||||
<field name="display_create_order" invisible="1"/>
|
||||
<field name="pricing_type" invisible="1" widget="radio"/>
|
||||
<field name="timesheet_product_id" string="Default Service" invisible="1" context="{'default_detailed_type': 'service', 'default_service_policy': 'delivered_timesheet', 'default_service_type': 'timesheet'}"/>
|
||||
<field name="sale_order_id" invisible="1" options="{'no_create': True, 'no_edit': True, 'delete': False, 'no_open': True}"/>
|
||||
<field name="sale_line_id" groups="!sales_team.group_sale_salesman" options="{'no_create': True, 'no_edit': True, 'delete': False, 'no_open': True}"/>
|
||||
<field name="sale_line_id" groups="sales_team.group_sale_salesman" options="{'no_create': True, 'no_edit': True, 'delete': False}"/>
|
||||
</group>
|
||||
</group>
|
||||
<field name="sale_line_employee_ids">
|
||||
<tree editable="bottom">
|
||||
<field name="company_id" invisible="1"/>
|
||||
@@ -190,4 +184,15 @@
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="project_task_view_search_inherit_sale_timesheet" model="ir.ui.view">
|
||||
<field name="name">project.task.view.search.inherit</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="hr_timesheet.project_task_view_search"/>
|
||||
<field name="arch" type="xml">
|
||||
<filter name="timesheet_exceeded" position="attributes">
|
||||
<attribute name="domain">['|', ('overtime', '>', 0), ('remaining_hours_so', '<', 0)]</attribute>
|
||||
</filter>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -50,12 +50,12 @@
|
||||
"cells": {
|
||||
"A7": {
|
||||
"style": 1,
|
||||
"content": "[Tasks by Stage](odoo://view/{\"viewType\":\"graph\",\"action\":{\"domain\":[],\"context\":{\"group_by\":[\"stage_id\",\"state\"],\"graph_measure\":\"__count\",\"graph_mode\":\"bar\",\"graph_groupbys\":[\"stage_id\",\"state\"]},\"modelName\":\"report.project.task.user\",\"views\":[[false,\"graph\"],[false,\"pivot\"],[false,\"list\"],[false,\"search\"]]},\"threshold\":0,\"name\":\"Tasks Analysis\"})",
|
||||
"content": "[Tasks by Stage](odoo://view/{\"viewType\":\"graph\",\"action\":{\"domain\":[],\"context\":{\"group_by\":[\"stage_id\",\"kanban_state\"],\"graph_measure\":\"__count\",\"graph_mode\":\"bar\",\"graph_groupbys\":[\"stage_id\",\"kanban_state\"]},\"modelName\":\"report.project.task.user\",\"views\":[[false,\"graph\"],[false,\"pivot\"],[false,\"list\"],[false,\"search\"]]},\"threshold\":0,\"name\":\"Tasks Analysis\"})",
|
||||
"border": 1
|
||||
},
|
||||
"A19": {
|
||||
"style": 1,
|
||||
"content": "[Top Assignees](odoo://view/{\"viewType\":\"pivot\",\"action\":{\"domain\":[[\"user_ids\",\"!=\",false]],\"context\":{\"group_by\":[],\"pivot_measures\":[\"hours_effective\",\"nbr\"],\"pivot_column_groupby\":[],\"pivot_row_groupby\":[\"user_ids\"]},\"modelName\":\"report.project.task.user\",\"views\":[[false,\"graph\"],[false,\"pivot\"],[false,\"list\"],[false,\"search\"]]},\"threshold\":0,\"name\":\"Tasks Analysis\"})",
|
||||
"content": "[Top Assignees](odoo://view/{\"viewType\":\"pivot\",\"action\":{\"domain\":[[\"user_ids\",\"!=\",false]],\"context\":{\"group_by\":[],\"pivot_measures\":[\"effective_hours\",\"nbr\"],\"pivot_column_groupby\":[],\"pivot_row_groupby\":[\"user_ids\"]},\"modelName\":\"report.project.task.user\",\"views\":[[false,\"graph\"],[false,\"pivot\"],[false,\"list\"],[false,\"search\"]]},\"threshold\":0,\"name\":\"Tasks Analysis\"})",
|
||||
"border": 1
|
||||
},
|
||||
"A20": {
|
||||
@@ -105,7 +105,7 @@
|
||||
},
|
||||
"A32": {
|
||||
"style": 1,
|
||||
"content": "[Top Tags](odoo://view/{\"viewType\":\"pivot\",\"action\":{\"domain\":[[\"tag_ids\",\"!=\",false]],\"context\":{\"group_by\":[\"tag_ids\"],\"pivot_measures\":[\"hours_effective\",\"nbr\"],\"pivot_column_groupby\":[],\"pivot_row_groupby\":[\"tag_ids\"]},\"modelName\":\"report.project.task.user\",\"views\":[[false,\"graph\"],[false,\"pivot\"],[false,\"list\"],[false,\"search\"]]},\"threshold\":0,\"name\":\"Tasks Analysis\"})",
|
||||
"content": "[Top Tags](odoo://view/{\"viewType\":\"pivot\",\"action\":{\"domain\":[[\"tag_ids\",\"!=\",false]],\"context\":{\"group_by\":[\"tag_ids\"],\"pivot_measures\":[\"effective_hours\",\"nbr\"],\"pivot_column_groupby\":[],\"pivot_row_groupby\":[\"tag_ids\"]},\"modelName\":\"report.project.task.user\",\"views\":[[false,\"graph\"],[false,\"pivot\"],[false,\"list\"],[false,\"search\"]]},\"threshold\":0,\"name\":\"Tasks Analysis\"})",
|
||||
"border": 1
|
||||
},
|
||||
"A33": {
|
||||
@@ -169,47 +169,47 @@
|
||||
"B21": {
|
||||
"style": 7,
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(1,\"hours_effective\",\"#user_ids\",1)"
|
||||
"content": "=ODOO.PIVOT(1,\"effective_hours\",\"#user_ids\",1)"
|
||||
},
|
||||
"B22": {
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(1,\"hours_effective\",\"#user_ids\",2)"
|
||||
"content": "=ODOO.PIVOT(1,\"effective_hours\",\"#user_ids\",2)"
|
||||
},
|
||||
"B23": {
|
||||
"style": 7,
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(1,\"hours_effective\",\"#user_ids\",3)"
|
||||
"content": "=ODOO.PIVOT(1,\"effective_hours\",\"#user_ids\",3)"
|
||||
},
|
||||
"B24": {
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(1,\"hours_effective\",\"#user_ids\",4)"
|
||||
"content": "=ODOO.PIVOT(1,\"effective_hours\",\"#user_ids\",4)"
|
||||
},
|
||||
"B25": {
|
||||
"style": 7,
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(1,\"hours_effective\",\"#user_ids\",5)"
|
||||
"content": "=ODOO.PIVOT(1,\"effective_hours\",\"#user_ids\",5)"
|
||||
},
|
||||
"B26": {
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(1,\"hours_effective\",\"#user_ids\",6)"
|
||||
"content": "=ODOO.PIVOT(1,\"effective_hours\",\"#user_ids\",6)"
|
||||
},
|
||||
"B27": {
|
||||
"style": 7,
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(1,\"hours_effective\",\"#user_ids\",7)"
|
||||
"content": "=ODOO.PIVOT(1,\"effective_hours\",\"#user_ids\",7)"
|
||||
},
|
||||
"B28": {
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(1,\"hours_effective\",\"#user_ids\",8)"
|
||||
"content": "=ODOO.PIVOT(1,\"effective_hours\",\"#user_ids\",8)"
|
||||
},
|
||||
"B29": {
|
||||
"style": 7,
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(1,\"hours_effective\",\"#user_ids\",9)"
|
||||
"content": "=ODOO.PIVOT(1,\"effective_hours\",\"#user_ids\",9)"
|
||||
},
|
||||
"B30": {
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(1,\"hours_effective\",\"#user_ids\",10)"
|
||||
"content": "=ODOO.PIVOT(1,\"effective_hours\",\"#user_ids\",10)"
|
||||
},
|
||||
"B32": {
|
||||
"style": 5,
|
||||
@@ -223,47 +223,47 @@
|
||||
"B34": {
|
||||
"style": 7,
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(3,\"hours_effective\",\"#tag_ids\",1)"
|
||||
"content": "=ODOO.PIVOT(3,\"effective_hours\",\"#tag_ids\",1)"
|
||||
},
|
||||
"B35": {
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(3,\"hours_effective\",\"#tag_ids\",2)"
|
||||
"content": "=ODOO.PIVOT(3,\"effective_hours\",\"#tag_ids\",2)"
|
||||
},
|
||||
"B36": {
|
||||
"style": 7,
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(3,\"hours_effective\",\"#tag_ids\",3)"
|
||||
"content": "=ODOO.PIVOT(3,\"effective_hours\",\"#tag_ids\",3)"
|
||||
},
|
||||
"B37": {
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(3,\"hours_effective\",\"#tag_ids\",4)"
|
||||
"content": "=ODOO.PIVOT(3,\"effective_hours\",\"#tag_ids\",4)"
|
||||
},
|
||||
"B38": {
|
||||
"style": 7,
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(3,\"hours_effective\",\"#tag_ids\",5)"
|
||||
"content": "=ODOO.PIVOT(3,\"effective_hours\",\"#tag_ids\",5)"
|
||||
},
|
||||
"B39": {
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(3,\"hours_effective\",\"#tag_ids\",6)"
|
||||
"content": "=ODOO.PIVOT(3,\"effective_hours\",\"#tag_ids\",6)"
|
||||
},
|
||||
"B40": {
|
||||
"style": 7,
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(3,\"hours_effective\",\"#tag_ids\",7)"
|
||||
"content": "=ODOO.PIVOT(3,\"effective_hours\",\"#tag_ids\",7)"
|
||||
},
|
||||
"B41": {
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(3,\"hours_effective\",\"#tag_ids\",8)"
|
||||
"content": "=ODOO.PIVOT(3,\"effective_hours\",\"#tag_ids\",8)"
|
||||
},
|
||||
"B42": {
|
||||
"style": 7,
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(3,\"hours_effective\",\"#tag_ids\",9)"
|
||||
"content": "=ODOO.PIVOT(3,\"effective_hours\",\"#tag_ids\",9)"
|
||||
},
|
||||
"B43": {
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(3,\"hours_effective\",\"#tag_ids\",10)"
|
||||
"content": "=ODOO.PIVOT(3,\"effective_hours\",\"#tag_ids\",10)"
|
||||
},
|
||||
"C7": {
|
||||
"style": 5,
|
||||
@@ -369,12 +369,12 @@
|
||||
},
|
||||
"E7": {
|
||||
"style": 1,
|
||||
"content": "[Tasks by State](odoo://view/{\"viewType\":\"graph\",\"action\":{\"domain\":[],\"context\":{\"group_by\":[\"state\"],\"graph_measure\":\"__count\",\"graph_mode\":\"pie\",\"graph_groupbys\":[\"state\"]},\"modelName\":\"report.project.task.user\",\"views\":[[false,\"graph\"],[false,\"pivot\"],[false,\"list\"],[false,\"search\"]]},\"threshold\":0,\"name\":\"Tasks Analysis\"})",
|
||||
"content": "[Tasks by State](odoo://view/{\"viewType\":\"graph\",\"action\":{\"domain\":[],\"context\":{\"group_by\":[\"kanban_state\"],\"graph_measure\":\"__count\",\"graph_mode\":\"pie\",\"graph_groupbys\":[\"kanban_state\"]},\"modelName\":\"report.project.task.user\",\"views\":[[false,\"graph\"],[false,\"pivot\"],[false,\"list\"],[false,\"search\"]]},\"threshold\":0,\"name\":\"Tasks Analysis\"})",
|
||||
"border": 1
|
||||
},
|
||||
"E19": {
|
||||
"style": 1,
|
||||
"content": "[Top Projects](odoo://view/{\"viewType\":\"pivot\",\"action\":{\"domain\":[[\"project_id\",\"!=\",false]],\"context\":{\"group_by\":[\"project_id\"],\"pivot_measures\":[\"hours_effective\",\"nbr\"],\"pivot_column_groupby\":[],\"pivot_row_groupby\":[\"project_id\"]},\"modelName\":\"report.project.task.user\",\"views\":[[false,\"graph\"],[false,\"pivot\"],[false,\"list\"],[false,\"search\"]]},\"threshold\":0,\"name\":\"Tasks Analysis\"})",
|
||||
"content": "[Top Projects](odoo://view/{\"viewType\":\"pivot\",\"action\":{\"domain\":[[\"project_id\",\"!=\",false]],\"context\":{\"group_by\":[\"project_id\"],\"pivot_measures\":[\"effective_hours\",\"nbr\"],\"pivot_column_groupby\":[],\"pivot_row_groupby\":[\"project_id\"]},\"modelName\":\"report.project.task.user\",\"views\":[[false,\"graph\"],[false,\"pivot\"],[false,\"list\"],[false,\"search\"]]},\"threshold\":0,\"name\":\"Tasks Analysis\"})",
|
||||
"border": 1
|
||||
},
|
||||
"E20": {
|
||||
@@ -424,7 +424,7 @@
|
||||
},
|
||||
"E32": {
|
||||
"style": 1,
|
||||
"content": "[Top Customers](odoo://view/{\"viewType\":\"pivot\",\"action\":{\"domain\":[[\"partner_id\",\"!=\",false]],\"context\":{\"group_by\":[\"partner_id\"],\"pivot_measures\":[\"hours_effective\",\"nbr\"],\"pivot_column_groupby\":[],\"pivot_row_groupby\":[\"partner_id\"]},\"modelName\":\"report.project.task.user\",\"views\":[[false,\"graph\"],[false,\"pivot\"],[false,\"list\"],[false,\"search\"]]},\"threshold\":0,\"name\":\"Tasks Analysis\"})",
|
||||
"content": "[Top Customers](odoo://view/{\"viewType\":\"pivot\",\"action\":{\"domain\":[[\"partner_id\",\"!=\",false]],\"context\":{\"group_by\":[\"partner_id\"],\"pivot_measures\":[\"effective_hours\",\"nbr\"],\"pivot_column_groupby\":[],\"pivot_row_groupby\":[\"partner_id\"]},\"modelName\":\"report.project.task.user\",\"views\":[[false,\"graph\"],[false,\"pivot\"],[false,\"list\"],[false,\"search\"]]},\"threshold\":0,\"name\":\"Tasks Analysis\"})",
|
||||
"border": 1
|
||||
},
|
||||
"E33": {
|
||||
@@ -488,47 +488,47 @@
|
||||
"F21": {
|
||||
"style": 7,
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(2,\"hours_effective\",\"#project_id\",1)"
|
||||
"content": "=ODOO.PIVOT(2,\"effective_hours\",\"#project_id\",1)"
|
||||
},
|
||||
"F22": {
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(2,\"hours_effective\",\"#project_id\",2)"
|
||||
"content": "=ODOO.PIVOT(2,\"effective_hours\",\"#project_id\",2)"
|
||||
},
|
||||
"F23": {
|
||||
"style": 7,
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(2,\"hours_effective\",\"#project_id\",3)"
|
||||
"content": "=ODOO.PIVOT(2,\"effective_hours\",\"#project_id\",3)"
|
||||
},
|
||||
"F24": {
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(2,\"hours_effective\",\"#project_id\",4)"
|
||||
"content": "=ODOO.PIVOT(2,\"effective_hours\",\"#project_id\",4)"
|
||||
},
|
||||
"F25": {
|
||||
"style": 7,
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(2,\"hours_effective\",\"#project_id\",5)"
|
||||
"content": "=ODOO.PIVOT(2,\"effective_hours\",\"#project_id\",5)"
|
||||
},
|
||||
"F26": {
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(2,\"hours_effective\",\"#project_id\",6)"
|
||||
"content": "=ODOO.PIVOT(2,\"effective_hours\",\"#project_id\",6)"
|
||||
},
|
||||
"F27": {
|
||||
"style": 7,
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(2,\"hours_effective\",\"#project_id\",7)"
|
||||
"content": "=ODOO.PIVOT(2,\"effective_hours\",\"#project_id\",7)"
|
||||
},
|
||||
"F28": {
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(2,\"hours_effective\",\"#project_id\",8)"
|
||||
"content": "=ODOO.PIVOT(2,\"effective_hours\",\"#project_id\",8)"
|
||||
},
|
||||
"F29": {
|
||||
"style": 7,
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(2,\"hours_effective\",\"#project_id\",9)"
|
||||
"content": "=ODOO.PIVOT(2,\"effective_hours\",\"#project_id\",9)"
|
||||
},
|
||||
"F30": {
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(2,\"hours_effective\",\"#project_id\",10)"
|
||||
"content": "=ODOO.PIVOT(2,\"effective_hours\",\"#project_id\",10)"
|
||||
},
|
||||
"F32": {
|
||||
"style": 5,
|
||||
@@ -542,47 +542,47 @@
|
||||
"F34": {
|
||||
"style": 7,
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(4,\"hours_effective\",\"#partner_id\",1)"
|
||||
"content": "=ODOO.PIVOT(4,\"effective_hours\",\"#partner_id\",1)"
|
||||
},
|
||||
"F35": {
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(4,\"hours_effective\",\"#partner_id\",2)"
|
||||
"content": "=ODOO.PIVOT(4,\"effective_hours\",\"#partner_id\",2)"
|
||||
},
|
||||
"F36": {
|
||||
"style": 7,
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(4,\"hours_effective\",\"#partner_id\",3)"
|
||||
"content": "=ODOO.PIVOT(4,\"effective_hours\",\"#partner_id\",3)"
|
||||
},
|
||||
"F37": {
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(4,\"hours_effective\",\"#partner_id\",4)"
|
||||
"content": "=ODOO.PIVOT(4,\"effective_hours\",\"#partner_id\",4)"
|
||||
},
|
||||
"F38": {
|
||||
"style": 7,
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(4,\"hours_effective\",\"#partner_id\",5)"
|
||||
"content": "=ODOO.PIVOT(4,\"effective_hours\",\"#partner_id\",5)"
|
||||
},
|
||||
"F39": {
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(4,\"hours_effective\",\"#partner_id\",6)"
|
||||
"content": "=ODOO.PIVOT(4,\"effective_hours\",\"#partner_id\",6)"
|
||||
},
|
||||
"F40": {
|
||||
"style": 7,
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(4,\"hours_effective\",\"#partner_id\",7)"
|
||||
"content": "=ODOO.PIVOT(4,\"effective_hours\",\"#partner_id\",7)"
|
||||
},
|
||||
"F41": {
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(4,\"hours_effective\",\"#partner_id\",8)"
|
||||
"content": "=ODOO.PIVOT(4,\"effective_hours\",\"#partner_id\",8)"
|
||||
},
|
||||
"F42": {
|
||||
"style": 7,
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(4,\"hours_effective\",\"#partner_id\",9)"
|
||||
"content": "=ODOO.PIVOT(4,\"effective_hours\",\"#partner_id\",9)"
|
||||
},
|
||||
"F43": {
|
||||
"format": 1,
|
||||
"content": "=ODOO.PIVOT(4,\"hours_effective\",\"#partner_id\",10)"
|
||||
"content": "=ODOO.PIVOT(4,\"effective_hours\",\"#partner_id\",10)"
|
||||
},
|
||||
"G7": {
|
||||
"style": 5,
|
||||
@@ -732,7 +732,7 @@
|
||||
"metaData": {
|
||||
"groupBy": [
|
||||
"stage_id",
|
||||
"state"
|
||||
"kanban_state"
|
||||
],
|
||||
"measure": "__count",
|
||||
"order": null,
|
||||
@@ -748,7 +748,7 @@
|
||||
"domain": [],
|
||||
"groupBy": [
|
||||
"stage_id",
|
||||
"state"
|
||||
"kanban_state"
|
||||
],
|
||||
"orderBy": []
|
||||
},
|
||||
@@ -771,7 +771,7 @@
|
||||
"legendPosition": "right",
|
||||
"metaData": {
|
||||
"groupBy": [
|
||||
"state"
|
||||
"kanban_state"
|
||||
],
|
||||
"measure": "__count",
|
||||
"order": null,
|
||||
@@ -786,7 +786,7 @@
|
||||
},
|
||||
"domain": [],
|
||||
"groupBy": [
|
||||
"state"
|
||||
"kanban_state"
|
||||
],
|
||||
"orderBy": []
|
||||
},
|
||||
@@ -920,7 +920,7 @@
|
||||
"content": "=ODOO.PIVOT(5,\"nbr\")"
|
||||
},
|
||||
"B3": {
|
||||
"content": "=ODOO.PIVOT(5,\"hours_effective\")"
|
||||
"content": "=ODOO.PIVOT(5,\"effective_hours\")"
|
||||
},
|
||||
"B4": {
|
||||
"content": "=ODOO.PIVOT(5,\"working_days_open\")"
|
||||
@@ -936,7 +936,7 @@
|
||||
"content": "=ODOO.PIVOT(6,\"nbr\")"
|
||||
},
|
||||
"C3": {
|
||||
"content": "=ODOO.PIVOT(6,\"hours_effective\")"
|
||||
"content": "=ODOO.PIVOT(6,\"effective_hours\")"
|
||||
},
|
||||
"C4": {
|
||||
"content": "=ODOO.PIVOT(6,\"working_days_open\")"
|
||||
@@ -1078,7 +1078,7 @@
|
||||
"id": "1",
|
||||
"measures": [
|
||||
{
|
||||
"field": "hours_effective"
|
||||
"field": "effective_hours"
|
||||
},
|
||||
{
|
||||
"field": "nbr"
|
||||
@@ -1115,7 +1115,7 @@
|
||||
"id": "2",
|
||||
"measures": [
|
||||
{
|
||||
"field": "hours_effective"
|
||||
"field": "effective_hours"
|
||||
},
|
||||
{
|
||||
"field": "nbr"
|
||||
@@ -1152,7 +1152,7 @@
|
||||
"id": "3",
|
||||
"measures": [
|
||||
{
|
||||
"field": "hours_effective"
|
||||
"field": "effective_hours"
|
||||
},
|
||||
{
|
||||
"field": "nbr"
|
||||
@@ -1189,7 +1189,7 @@
|
||||
"id": "4",
|
||||
"measures": [
|
||||
{
|
||||
"field": "hours_effective"
|
||||
"field": "effective_hours"
|
||||
},
|
||||
{
|
||||
"field": "nbr"
|
||||
@@ -1223,7 +1223,7 @@
|
||||
"field": "nbr"
|
||||
},
|
||||
{
|
||||
"field": "hours_effective"
|
||||
"field": "effective_hours"
|
||||
},
|
||||
{
|
||||
"field": "working_days_open"
|
||||
@@ -1258,7 +1258,7 @@
|
||||
"field": "nbr"
|
||||
},
|
||||
{
|
||||
"field": "hours_effective"
|
||||
"field": "effective_hours"
|
||||
},
|
||||
{
|
||||
"field": "working_days_open"
|
||||
|
||||
Reference in New Issue
Block a user