diff --git a/addons/hr_timesheet/models/project.py b/addons/hr_timesheet/models/project.py index e2a96e5e5b2..c5fa37badec 100644 --- a/addons/hr_timesheet/models/project.py +++ b/addons/hr_timesheet/models/project.py @@ -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()) diff --git a/addons/hr_timesheet/report/project_report.py b/addons/hr_timesheet/report/project_report.py index e59c680d8a8..fec381b94de 100644 --- a/addons/hr_timesheet/report/project_report.py +++ b/addons/hr_timesheet/report/project_report.py @@ -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): diff --git a/addons/hr_timesheet/report/project_report_view.xml b/addons/hr_timesheet/report/project_report_view.xml index d48ebee50d9..6e0f0796d7e 100644 --- a/addons/hr_timesheet/report/project_report_view.xml +++ b/addons/hr_timesheet/report/project_report_view.xml @@ -10,8 +10,8 @@ hr_timesheet_graphview - - + + @@ -23,39 +23,20 @@ - - + + - - report.project.task.user.view.search.inherit.hr.timesheet - report.project.task.user - - - - - - - - - - - - - - - - report.project.task.user.view.tree.inherit.hr.timesheet report.project.task.user - + diff --git a/addons/hr_timesheet/static/src/views/timesheet_graph/timesheet_graph_model.js b/addons/hr_timesheet/static/src/views/timesheet_graph/timesheet_graph_model.js index b65559a80d6..040238f5cd2 100644 --- a/addons/hr_timesheet/static/src/views/timesheet_graph/timesheet_graph_model.js +++ b/addons/hr_timesheet/static/src/views/timesheet_graph/timesheet_graph_model.js @@ -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 { diff --git a/addons/hr_timesheet/tests/test_timesheet.py b/addons/hr_timesheet/tests/test_timesheet.py index d11ce577b92..0f2c3c5e500 100644 --- a/addons/hr_timesheet/tests/test_timesheet.py +++ b/addons/hr_timesheet/tests/test_timesheet.py @@ -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']), ]) diff --git a/addons/hr_timesheet/views/project_sharing_views.xml b/addons/hr_timesheet/views/project_sharing_views.xml index 86692dd03b3..08751c0ad61 100644 --- a/addons/hr_timesheet/views/project_sharing_views.xml +++ b/addons/hr_timesheet/views/project_sharing_views.xml @@ -140,34 +140,4 @@ - - project.task.tree.inherited - project.task - - - - - - - - - - - - - - - - - project.sharing.project.task.view.search.inherit.timesheet - project.task - - - - - - - - - diff --git a/addons/hr_timesheet/views/project_views.xml b/addons/hr_timesheet/views/project_views.xml index b9290592ea7..643ee53acb8 100644 --- a/addons/hr_timesheet/views/project_views.xml +++ b/addons/hr_timesheet/views/project_views.xml @@ -113,9 +113,9 @@ - - - + + + @@ -260,7 +260,7 @@ - + @@ -268,18 +268,17 @@ project.task.tree.inherited project.task - - + - + - + - + @@ -346,27 +345,13 @@ project.task.view.search.inherit.sale.timesheet.enterprise project.task - + + 10 - - - - - - - - - - - - - project.task.view.search.inherit.hr.timesheet - project.task - - - - - + + + + @@ -376,12 +361,8 @@ project.project - - - - - + diff --git a/addons/project/__manifest__.py b/addons/project/__manifest__.py index 64031268abb..d8b04fdd265 100644 --- a/addons/project/__manifest__.py +++ b/addons/project/__manifest__.py @@ -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', diff --git a/addons/project/models/project.py b/addons/project/models/project.py index 4f2e218026d..7327bf3345f 100644 --- a/addons/project/models/project.py +++ b/addons/project/models/project.py @@ -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 diff --git a/addons/project/report/project_report.py b/addons/project/report/project_report.py index 2cdeea7236f..59779da6e28 100644 --- a/addons/project/report/project_report.py +++ b/addons/project/report/project_report.py @@ -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): diff --git a/addons/project/report/project_report_views.xml b/addons/project/report/project_report_views.xml index c1a80482f36..730b0f9217b 100644 --- a/addons/project/report/project_report_views.xml +++ b/addons/project/report/project_report_views.xml @@ -41,66 +41,15 @@ report.project.task.user.search report.project.task.user + + primary - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Tasks Analysis + + project_id + @@ -119,4 +68,9 @@ + diff --git a/addons/project/views/project_sharing_views.xml b/addons/project/views/project_sharing_views.xml index 695919629ba..3a50cdf5b54 100644 --- a/addons/project/views/project_sharing_views.xml +++ b/addons/project/views/project_sharing_views.xml @@ -113,28 +113,17 @@ project.sharing.project.task.tree project.task + + primary 999 - - - - - - - - - - - - - - - - - - - + + 0 + 0 + + + @@ -247,42 +236,11 @@ project.task.search.form project.task + + primary 999 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -292,10 +250,6 @@ kanban,tree,form [('display_project_id', '=', active_id)] - { - 'default_project_id': active_id, - 'delete': 0, - }

No tasks found. Let's create one! diff --git a/addons/project/views/project_views.xml b/addons/project/views/project_views.xml index 8719f36f11e..1f46cc8bcbb 100644 --- a/addons/project/views/project_views.xml +++ b/addons/project/views/project_views.xml @@ -11,52 +11,96 @@ - + + project.task.search.form project.task - + - - - + + - - - + - - - - + + + - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + project.task.search.form.project.base + project.task + + primary + + + + + + + + + + + + + + + + + + + project.task.search.form.project.base + project.task + + primary + + + + + + + + + + + + project.task.search.form + project.task + + primary + + + + + + @@ -64,41 +108,7 @@ domain="[('my_activity_date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/> - - - - - - - - - - - - - - - - - project.task.search.form.extended - project.task - - primary - - - - - - - - - - - - - - - + @@ -644,27 +654,17 @@ - - + - - - - - - - - - - - + + + @@ -689,20 +690,16 @@ project.project - + - + - - + - - - - - + + @@ -1197,8 +1194,8 @@ @@ -1239,7 +1236,7 @@ + context="{'default_project_id' : project_id, 'search_view_ref' : 'project.view_task_search_form', 'search_default_display_project_id': project_id}"> @@ -1329,7 +1326,7 @@ - + @@ -1493,13 +1490,13 @@ - - project.task.tree + + + project.task.view.tree.main.base project.task - - - + + @@ -1507,35 +1504,62 @@ - - - + + + - - - - - - - - + + + + + + + + + + + + + + project.task.view.tree.base + project.task + + primary + + + 1 + project_task_list + stage_id + + + + - - - - - + + + + + project.task.tree + project.task + + primary + 2 + + + + + project.task.calendar project.task @@ -1662,6 +1686,18 @@ project.task primary + + + + + + + + + open.view.all.tasks.list.view + project.task + + primary personal_stage_type_ids @@ -1691,8 +1727,9 @@ My Tasks project.task kanban,tree,form,calendar,pivot,graph,activity - {'search_default_my_tasks': 1, 'search_default_open_tasks': 1, 'all_task': 0, 'default_user_ids': [(4, uid)]} - + {'search_default_my_tasks': 1, 'all_task': 0, 'default_user_ids': [(4, uid)]} + + [('user_ids', 'in', uid)]

No tasks found. Let's create one! @@ -1738,8 +1775,8 @@ All Tasks project.task tree,kanban,form,calendar,pivot,graph,activity - {'search_default_open_tasks': 1, 'default_user_ids': [(4, uid)]} - + {'default_user_ids': [(4, uid)]} +

No tasks found. Let's create one! @@ -1755,7 +1792,7 @@ tree - + @@ -1789,7 +1826,7 @@ Tasks project.task kanban,tree,form - + @@ -1798,7 +1835,7 @@ tree,form,calendar,graph,kanban [('is_closed', '=', False), ('date_deadline','<',time.strftime('%Y-%m-%d')), ('display_project_id', '!=', False)] - + @@ -1906,12 +1943,6 @@ - - - - - - report.project.task.user.search.inherited - report.project.task.user - - - - - - - - - - - - diff --git a/addons/sale_project/views/project_sharing_views.xml b/addons/sale_project/views/project_sharing_views.xml index 4c3e2344e0b..59fe6e587fe 100644 --- a/addons/sale_project/views/project_sharing_views.xml +++ b/addons/sale_project/views/project_sharing_views.xml @@ -25,18 +25,4 @@ - - project.task.search.inherit - project.task - - - - - - - - - - - diff --git a/addons/sale_project/views/project_task_views.xml b/addons/sale_project/views/project_task_views.xml index 8fe09e94d00..59c6e6e243a 100644 --- a/addons/sale_project/views/project_task_views.xml +++ b/addons/sale_project/views/project_task_views.xml @@ -1,6 +1,22 @@ + + {'sale_show_partner_name': True} + + + + {'sale_show_partner_name': True} + + + + {'sale_show_partner_name': True} + + + + {'sale_show_partner_name': True} + + project.project.select.inherit.project project.project @@ -86,7 +102,7 @@ project.task.form.inherit.sale.project project.task - + @@ -97,14 +113,11 @@ project.task.search.inherit project.task - + - - - - - - + + + diff --git a/addons/sale_project/views/project_views.xml b/addons/sale_project/views/project_views.xml index 8f1f4839f53..c2d263f76e0 100644 --- a/addons/sale_project/views/project_views.xml +++ b/addons/sale_project/views/project_views.xml @@ -1,15 +1,15 @@ - {'default_allow_billable': True} + {'default_allow_billable': True, 'sale_show_partner_name': True} - {'default_allow_billable': True} + {'default_allow_billable': True, 'sale_show_partner_name': True} - {'default_allow_billable': True} + {'default_allow_billable': True, 'sale_show_partner_name': True} - {'default_allow_billable': True, 'search_default_groupby_stage': 1} + {'default_allow_billable': True, 'sale_show_partner_name': True, 'search_default_groupby_stage': 1} diff --git a/addons/sale_timesheet/report/__init__.py b/addons/sale_timesheet/report/__init__.py index 50dc678f4bc..6c55a4362c2 100644 --- a/addons/sale_timesheet/report/__init__.py +++ b/addons/sale_timesheet/report/__init__.py @@ -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 diff --git a/addons/sale_timesheet/report/project_report.py b/addons/sale_timesheet/report/project_report.py new file mode 100644 index 00000000000..d3d530291ed --- /dev/null +++ b/addons/sale_timesheet/report/project_report.py @@ -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 + """ diff --git a/addons/sale_timesheet/static/tests/tours/sale_timesheet_tour.js b/addons/sale_timesheet/static/tests/tours/sale_timesheet_tour.js index 07265bb6cb4..9c38693a955 100644 --- a/addons/sale_timesheet/static/tests/tours/sale_timesheet_tour.js +++ b/addons/sale_timesheet/static/tests/tours/sale_timesheet_tour.js @@ -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', diff --git a/addons/sale_timesheet/views/project_sharing_views.xml b/addons/sale_timesheet/views/project_sharing_views.xml index eb7b87a5697..cd1f8c86744 100644 --- a/addons/sale_timesheet/views/project_sharing_views.xml +++ b/addons/sale_timesheet/views/project_sharing_views.xml @@ -39,15 +39,4 @@ - - project.task.tree.inherit.sale.timesheet - project.task - - - - - - - - diff --git a/addons/sale_timesheet/views/project_task_views.xml b/addons/sale_timesheet/views/project_task_views.xml index a519b1058be..ef7e6262a71 100644 --- a/addons/sale_timesheet/views/project_task_views.xml +++ b/addons/sale_timesheet/views/project_task_views.xml @@ -6,18 +6,12 @@ project.project + + + + - - - - - - - - - - @@ -190,4 +184,15 @@ + + + project.task.view.search.inherit + project.task + + + + ['|', ('overtime', '>', 0), ('remaining_hours_so', '<', 0)] + + + diff --git a/addons/spreadsheet_dashboard_hr_timesheet/data/files/tasks_dashboard.json b/addons/spreadsheet_dashboard_hr_timesheet/data/files/tasks_dashboard.json index 5ddee3c493b..bf083b4e821 100644 --- a/addons/spreadsheet_dashboard_hr_timesheet/data/files/tasks_dashboard.json +++ b/addons/spreadsheet_dashboard_hr_timesheet/data/files/tasks_dashboard.json @@ -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"