diff --git a/addons/hr_timesheet/report/project_report_view.xml b/addons/hr_timesheet/report/project_report_view.xml index 698c22704a7..fe3fdaf6a76 100644 --- a/addons/hr_timesheet/report/project_report_view.xml +++ b/addons/hr_timesheet/report/project_report_view.xml @@ -39,7 +39,7 @@ - + @@ -52,7 +52,7 @@ report.project.task.user - + diff --git a/addons/project/models/project.py b/addons/project/models/project.py index 4223b57fc9b..665ee2b7bb9 100644 --- a/addons/project/models/project.py +++ b/addons/project/models/project.py @@ -476,6 +476,18 @@ class Project(models.Model): project.message_subscribe(project.partner_id.ids) if vals.get('privacy_visibility'): self._change_privacy_visibility() + if 'name' in vals and self.analytic_account_id: + projects_read_group = self.env['project.project'].read_group( + [('analytic_account_id', 'in', self.analytic_account_id.ids)], + ['analytic_account_id'], + ['analytic_account_id'] + ) + analytic_account_to_update = self.env['account.analytic.account'].browse([ + res['analytic_account_id'][0] + for res in projects_read_group + if res['analytic_account_id'] and res['analytic_account_id_count'] == 1 + ]) + analytic_account_to_update.write({'name': self.name}) return res def action_unlink(self): @@ -628,16 +640,8 @@ class Project(models.Model): } def action_view_kanban_project(self): - self.ensure_one() - return { - 'res_model': 'project.project', - 'type': 'ir.actions.act_window', - 'name': _("Project"), - 'res_id': self.id, - 'views': [(self.env.ref('project.edit_project').id, 'form')], - 'view_mode': 'form', - 'context': {'form_view_initial_mode': 'readonly'} - } + # [XBO] TODO: remove me in master + return # --------------------------------------------- # PROJECT UPDATES @@ -1020,6 +1024,7 @@ class Task(models.Model): dependent_ids = fields.Many2many('project.task', relation="task_dependencies_rel", column1="depends_on_id", column2="task_id", string="Block", domain="[('allow_task_dependencies', '=', True), ('id', '!=', id)]") + dependent_tasks_count = fields.Integer(string="Dependent Tasks", compute='_compute_dependent_tasks_count') # recurrence fields allow_recurring_tasks = fields.Boolean(related='project_id.allow_recurring_tasks') @@ -1275,6 +1280,21 @@ class Task(models.Model): for task in recurring_tasks: task.recurring_count = tasks_count.get(task.recurrence_id.id, 0) + @api.depends('dependent_ids') + def _compute_dependent_tasks_count(self): + tasks_with_dependency = self.filtered('allow_task_dependencies') + (self - tasks_with_dependency).dependent_tasks_count = 0 + if tasks_with_dependency: + group_dependent = self.env['project.task'].read_group([ + ('depend_on_ids', 'in', tasks_with_dependency.ids), + ], ['depend_on_ids'], ['depend_on_ids']) + dependent_tasks_count_dict = { + group['depend_on_ids'][0]: group['depend_on_ids_count'] + for group in group_dependent + } + for task in tasks_with_dependency: + task.dependent_tasks_count = dependent_tasks_count_dict.get(task.id, 0) + @api.depends('partner_id.email') def _compute_partner_email(self): for task in self: @@ -2009,6 +2029,22 @@ class Task(models.Model): 'context': self._context } + def action_dependent_tasks(self): + self.ensure_one() + action = { + 'res_model': 'project.task', + 'type': 'ir.actions.act_window', + 'context': {**self._context, 'default_depend_on_ids': [Command.link(self.id)]}, + } + if self.dependent_tasks_count == 1: + action['view_mode'] = 'form' + action['res_id'] = self.dependent_ids.id + else: + action['name'] = _('Dependent Tasks') + action['view_mode'] = 'tree,form,kanban,calendar,pivot,graph,gantt,activity,map' + action['domain'] = [('depend_on_ids', '=', self.id)] + return action + def action_recurring_tasks(self): return { 'name': 'Tasks in Recurrence', diff --git a/addons/project/models/project_update.py b/addons/project/models/project_update.py index e09d300f290..0aa8672320d 100644 --- a/addons/project/models/project_update.py +++ b/addons/project/models/project_update.py @@ -19,7 +19,7 @@ STATUS_COLOR = { class ProjectUpdate(models.Model): _name = 'project.update' _description = 'Project Update' - _order = 'create_date desc' + _order = 'date desc' _inherit = ['mail.thread.cc', 'mail.activity.mixin'] def default_get(self, fields): diff --git a/addons/project/report/project_report.py b/addons/project/report/project_report.py index 080e6fa66fb..bddd5bd47cc 100644 --- a/addons/project/report/project_report.py +++ b/addons/project/report/project_report.py @@ -11,7 +11,8 @@ class ReportProjectTaskUser(models.Model): _auto = False name = fields.Char(string='Task', readonly=True) - user_id = fields.Many2one('res.users', string='Assigned To', readonly=True) + user_ids = fields.Many2many('res.users', relation='project_task_user_rel', column1='task_id', column2='user_id', + string='Assignees', readonly=True) create_date = fields.Datetime("Create Date", readonly=True) date_assign = fields.Datetime(string='Assignment Date', readonly=True) date_end = fields.Datetime(string='Ending Date', readonly=True) @@ -55,7 +56,6 @@ class ReportProjectTaskUser(models.Model): t.date_end as date_end, t.date_last_stage_update as date_last_stage_update, t.date_deadline as date_deadline, - tu.user_id, t.project_id, t.priority, t.name as name, @@ -82,7 +82,6 @@ class ReportProjectTaskUser(models.Model): t.date_end, t.date_deadline, t.date_last_stage_update, - tu.user_id, t.project_id, t.priority, t.name, diff --git a/addons/project/report/project_report_views.xml b/addons/project/report/project_report_views.xml index c44b0e9d95a..1f01e7f067c 100644 --- a/addons/project/report/project_report_views.xml +++ b/addons/project/report/project_report_views.xml @@ -17,7 +17,7 @@ - + @@ -31,7 +31,7 @@ - + @@ -46,7 +46,7 @@ - + @@ -59,7 +59,7 @@ - + @@ -72,7 +72,7 @@ - + diff --git a/addons/project/report/project_task_burndown_chart_report.py b/addons/project/report/project_task_burndown_chart_report.py index ecc5ced3258..6850267e304 100644 --- a/addons/project/report/project_task_burndown_chart_report.py +++ b/addons/project/report/project_task_burndown_chart_report.py @@ -18,7 +18,8 @@ class ReportProjectTaskBurndownChart(models.Model): display_project_id = fields.Many2one('project.project', readonly=True) stage_id = fields.Many2one('project.task.type', readonly=True) date = fields.Datetime('Date', readonly=True) - user_id = fields.Many2one('res.users', string='Assigned to', readonly=True) + user_ids = fields.Many2many('res.users', relation='project_task_user_rel', column1='task_id', column2='user_id', + string='Assignees', readonly=True) date_assign = fields.Datetime(string='Assignment Date', readonly=True) date_deadline = fields.Date(string='Deadline', readonly=True) partner_id = fields.Many2one('res.partner', string='Customer', readonly=True) @@ -59,7 +60,6 @@ WITH all_moves_stage_task AS ( COALESCE(LAG(mm.date) OVER (PARTITION BY mm.res_id ORDER BY mm.id), pt.create_date) as date_begin, mm.date as date_end, mtv.old_value_integer as stage_id, - usr_rel.user_id, pt.date_assign, pt.date_deadline, pt.partner_id @@ -71,7 +71,6 @@ WITH all_moves_stage_task AS ( JOIN ir_model_fields imf ON mtv.field = imf.id AND imf.model = 'project.task' AND imf.name = 'stage_id' - JOIN project_task_user_rel usr_rel ON pt.id=usr_rel.task_id WHERE pt.active --We compute the last reached stage @@ -83,7 +82,6 @@ WITH all_moves_stage_task AS ( COALESCE(md.date, pt.create_date) as date_begin, (CURRENT_DATE + interval '1 month')::date as date_end, pt.stage_id, - usr_rel.user_id, pt.date_assign, pt.date_deadline, pt.partner_id @@ -99,7 +97,6 @@ WITH all_moves_stage_task AS ( AND mm.model = 'project.task' ORDER BY mm.id DESC FETCH FIRST ROW ONLY) md ON TRUE - JOIN project_task_user_rel usr_rel ON pt.id=usr_rel.task_id WHERE pt.active ) SELECT (task_id*10^7 + 10^6 + to_char(d, 'YYMMDD')::integer)::bigint as id, @@ -108,7 +105,6 @@ SELECT (task_id*10^7 + 10^6 + to_char(d, 'YYMMDD')::integer)::bigint as id, display_project_id, stage_id, d as date, - user_id, date_assign, date_deadline, partner_id, @@ -125,7 +121,6 @@ SELECT (task_id*10^7 + 2*10^6 + to_char(d, 'YYMMDD')::integer)::bigint as id, display_project_id, stage_id, date_trunc('week', d) as date, - user_id, date_assign, date_deadline, partner_id, @@ -144,7 +139,6 @@ SELECT (task_id*10^7 + 3*10^6 + to_char(d, 'YYMMDD')::integer)::bigint as id, display_project_id, stage_id, date_trunc('month', d) as date, - user_id, date_assign, date_deadline, partner_id, @@ -163,7 +157,6 @@ SELECT (task_id*10^7 + 4*10^6 + to_char(d, 'YYMMDD')::integer)::bigint as id, display_project_id, stage_id, date_trunc('quarter', d) as date, - user_id, date_assign, date_deadline, partner_id, @@ -182,7 +175,6 @@ SELECT (task_id*10^7 + 5*10^6 + to_char(d, 'YYMMDD')::integer)::bigint as id, display_project_id, stage_id, date_trunc('year', d) as date, - user_id, date_assign, date_deadline, partner_id, diff --git a/addons/project/report/project_task_burndown_chart_report_views.xml b/addons/project/report/project_task_burndown_chart_report_views.xml index 74a519cc9b2..3cb97e0d8e8 100644 --- a/addons/project/report/project_task_burndown_chart_report_views.xml +++ b/addons/project/report/project_task_burndown_chart_report_views.xml @@ -7,12 +7,10 @@ - - @@ -23,7 +21,6 @@ - diff --git a/addons/project/static/tests/tours/project_update_tour_tests.js b/addons/project/static/tests/tours/project_update_tour_tests.js index ae9c3fe0ccf..39932655efc 100644 --- a/addons/project/static/tests/tours/project_update_tour_tests.js +++ b/addons/project/static/tests/tours/project_update_tour_tests.js @@ -72,91 +72,9 @@ tour.register('project_update_tour', { trigger: ".o_kanban_record .oe_kanban_content", extra_trigger: '.o_kanban_project_tasks', run: "drag_and_drop .o_kanban_group:eq(1) ", -}, { - trigger: '.o_back_button', - content: 'Go back to edit the project created', -}, { - // an invisible element cannot be used as a trigger so this small hack is mandatory for the next step - trigger: '.o_kanban_record:contains("New Project")', - run: function () { - $('.o_kanban_record:contains("New Project") .o_dropdown_kanban').css('visibility', 'visible'); - } -}, { - trigger: '.oe_kanban_global_click :contains("New Project") .o_dropdown_kanban', - content: 'Open the project dropdown' -}, { - trigger: '.o_kanban_record:contains("New Project") .dropdown-menu a:contains("See More")', - content: 'Start editing the project', -}, { - trigger: '.nav-item:contains("Invoicing") .nav-link', - content: 'Start the assignement of a default sale order id to the project' -}, { - trigger: '.o_form_button_edit', - content: 'Start editing the project' -}, { - trigger: 'div[name="sale_line_id"]', - content: 'Select the first sale order of the list', - run: function (actions) { - actions.text('Prepaid', this.$anchor.find('input')); - }, -}, { - trigger: 'ul.ui-autocomplete > li:first-child > a', - content: 'Select the first item on the autocomplete dropdown' -}, { - trigger: '.o_form_button_save', - content: 'Save the modifications' -}, { - trigger: '.o_back_button', - content: 'Go back to the kanban view the project created', -}, { - trigger: '.oe_kanban_global_click :contains("New Project")', - content: 'Open the project' }, { trigger: ".o_project_updates_breadcrumb", content: 'Open Updates' -}, { - trigger: ".oe_button_box .o_stat_text:contains('Sales Order')", - run: function () {} -}, { - trigger: ".o_rightpanel_title:eq(0):contains('Sold')", - content: 'Check the user sees Sold section', - run: function () {} -}, { - trigger: ".o_rightpanel_left_text:eq(0):contains('Prepaid Hours')", - content: 'Check the user sees Sold subsection', - run: function () {}, -}, { - trigger: ".o_rightpanel_title:eq(1):contains('Total Sold')", - content: 'Check the user sees Total Sold section', - run: function () {} -}, { - trigger: ".o_rightpanel_header:eq(1) .o_rightpanel_right_col:contains('Hours')", - content: 'Check the user sees Total Sold hours title', - run: function () {}, -}, { - trigger: ".o_rightpanel_section:eq(2) .o_rightpanel_data_row:contains('Effective')", - content: 'Check the user sees Total Sold subsection row', - run: function () {}, -}, { - trigger: ".o_rightpanel_section:eq(2) .o_rightpanel_data_row:contains('Remaining')", - content: 'Check the user sees Total Sold subsection row', - run: function () {}, -}, { - trigger: ".o_rightpanel_title:eq(2):contains('Profitability')", - content: 'Check the user sees Profitability section', - run: function () {} -}, { - trigger: ".o_rightpanel_section:eq(3) .o_rightpanel_data_row:contains('Revenues')", - content: 'Check the user sees Profitability subsection row', - run: function () {}, -}, { - trigger: ".o_rightpanel_section:eq(3) .o_rightpanel_data_row:contains('Costs')", - content: 'Check the user sees Profitability subsection row', - run: function () {}, -}, { - trigger: ".o_rightpanel_section:eq(3) .o_rightpanel_data_row:contains('Margin')", - content: 'Check the user sees Profitability subsection row', - run: function () {}, }, { trigger: ".o_add_milestone a", content: "Add a first milestone" @@ -205,15 +123,6 @@ tour.register('project_update_tour', { }, { trigger: ".o_field_widget[name=description] h1:contains('Activities')", run: function () {}, -}, { - trigger: ".o_field_widget[name=description] h3:contains('Sold')", - run: function () {}, - }, { - trigger: ".o_field_widget[name=description] td:contains('Customer Care')", - run: function () {}, -}, { - trigger: ".o_field_widget[name=description] h3:contains('Profitability')", - run: function () {}, }, { trigger: ".o_field_widget[name=description] h3:contains('Milestones')", run: function () {}, diff --git a/addons/project/views/project_views.xml b/addons/project/views/project_views.xml index 1832f225bf1..f2ce34e3133 100644 --- a/addons/project/views/project_views.xml +++ b/addons/project/views/project_views.xml @@ -676,7 +676,8 @@ @@ -806,6 +807,15 @@ in Recurrence +
diff --git a/addons/project/wizard/project_task_type_delete.py b/addons/project/wizard/project_task_type_delete.py index 0fbf271f067..573473464c9 100644 --- a/addons/project/wizard/project_task_type_delete.py +++ b/addons/project/wizard/project_task_type_delete.py @@ -58,7 +58,7 @@ class ProjectTaskTypeDelete(models.TransientModel): action = self.env["ir.actions.actions"]._for_xml_id("project.action_view_task") action['domain'] = [('project_id', '=', project_id)] action['context'] = str({ - 'pivot_row_groupby': ['user_id'], + 'pivot_row_groupby': ['user_ids'], 'default_project_id': project_id, }) elif self.env.context.get('stage_view'): diff --git a/addons/project_mail_plugin/controllers/project_client.py b/addons/project_mail_plugin/controllers/project_client.py index 2fdbf445a16..0d0d1cb35c8 100644 --- a/addons/project_mail_plugin/controllers/project_client.py +++ b/addons/project_mail_plugin/controllers/project_client.py @@ -1,5 +1,5 @@ -from odoo import http, _ +from odoo import Command, http, _ from odoo.http import request @@ -39,7 +39,7 @@ class ProjectClient(http.Controller): 'partner_id': partner_id, 'description': email_body, 'project_id': project_id, - 'user_id': request.env.uid, + 'user_ids': [Command.link(request.env.uid)], }) return {'task_id': record.id, 'name': record.name} diff --git a/addons/sale_timesheet/models/product.py b/addons/sale_timesheet/models/product.py index f2f6b5f82c9..18b8bed87cb 100644 --- a/addons/sale_timesheet/models/product.py +++ b/addons/sale_timesheet/models/product.py @@ -27,7 +27,7 @@ class ProductTemplate(models.Model): ], ondelete={'timesheet': 'set default'}) # override domain project_id = fields.Many2one(domain="[('company_id', '=', current_company_id), ('allow_billable', '=', True), ('pricing_type', '=', 'task_rate'), ('allow_timesheets', 'in', [service_policy == 'delivered_timesheet', True])]") - project_template_id = fields.Many2one(domain="[('company_id', '=', current_company_id), ('allow_billable', '=', True), ('pricing_type', '!=', 'task_rate'), ('allow_timesheets', 'in', [service_policy == 'delivered_timesheet', True])]") + project_template_id = fields.Many2one(domain="[('company_id', '=', current_company_id), ('allow_billable', '=', True), ('allow_timesheets', 'in', [service_policy == 'delivered_timesheet', True])]") service_upsell_threshold = fields.Float('Threshold', default=1, help="Percentage of time delivered compared to the prepaid amount that must be reached for the upselling opportunity activity to be triggered.") service_upsell_threshold_ratio = fields.Char(compute='_compute_service_upsell_threshold_ratio') 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 33ecfdeb765..6e1fa4176f7 100644 --- a/addons/sale_timesheet/static/tests/tours/sale_timesheet_tour.js +++ b/addons/sale_timesheet/static/tests/tours/sale_timesheet_tour.js @@ -61,7 +61,7 @@ tour.register('sale_timesheet_tour', { run: function (actions) { actions.text('Project for Freeman', this.$anchor); }, -}, { +}, { trigger: 'button[name="action_view_tasks"]', content: 'Click on Create button to create and enter to this newest project.', run: 'click', @@ -178,13 +178,6 @@ tour.register('sale_timesheet_tour', { actions.click(this.$anchor); } } -}, { - trigger: 'div.o_notebook_headers', - content: 'Click on Invoicing tab to configure the invoicing of this project.', - run: function (actions) { - const notebookId = $('div[name="sale_line_id"]').closest("div.tab-pane").attr('id'); - actions.click(this.$anchor.find(`a[data-toggle="tab"][href="#${notebookId}"]`)); - }, }, { trigger: 'div[name="partner_id"]', content: Markup('Add the customer for this project to select an SO and SOL for this customer (e.g. Brandon Freeman).'), @@ -195,6 +188,13 @@ tour.register('sale_timesheet_tour', { trigger: 'ul.o_partner_autocomplete_dropdown > li:first-child > a:contains(Freeman)', content: 'Select the customer in the autocomplete dropdown', run: 'click', +}, { + trigger: 'div.o_notebook_headers', + content: 'Click on Invoicing tab to configure the invoicing of this project.', + run: function (actions) { + const notebookId = $('div[name="sale_line_id"]').closest("div.tab-pane").attr('id'); + actions.click(this.$anchor.find(`a[data-toggle="tab"][href="#${notebookId}"]`)); + }, }, { trigger: 'div[name="sale_line_id"]', content: 'Select a Sales Order Item as Default Sales Order Item for each task in this project.', @@ -239,36 +239,135 @@ tour.register('sale_timesheet_tour', { content: 'Save Project', }, { trigger: 'li[data-menu-xmlid="project.menu_projects"]', - content: 'Select Projects' + content: 'Select Projects', }, { + // an invisible element cannot be used as a trigger so this small hack is mandatory for the next step trigger: 'div.o_kanban_primary_left :contains("Project for Freeman")', - content: 'Select Project for Freeman' + content: 'Open the project dropdown', + run: function () { + $('.o_kanban_record:contains("Project for Freeman") .o_dropdown_kanban').css('visibility', 'visible'); + }, +}, { + trigger: '.oe_kanban_global_click :contains("Project for Freeman") .o_dropdown_kanban', + content: 'Open the project dropdown', +}, { + trigger: '.o_kanban_record:contains("Project for Freeman") .dropdown-menu a:contains("Edit")', + content: 'Start editing the project', +}, { + trigger: 'div.o_notebook_headers', + content: 'Click on Invoicing tab to configure the invoicing of this project.', + run: function (actions) { + const notebookId = $('div[name="sale_line_id"]').closest("div.tab-pane").attr('id'); + actions.click(this.$anchor.find(`a[data-toggle="tab"][href="#${notebookId}"]`)); + }, +}, { + trigger: 'div[name="sale_line_id"]', + content: 'Select the first sale order of the list', + run: function (actions) { + actions.text('Prepaid', this.$anchor.find('input')); + }, +}, { + trigger: 'ul.ui-autocomplete > li:first-child > a', + content: 'Select the first item on the autocomplete dropdown', +}, { + trigger: '.o_form_button_save', + content: 'Save the modifications', +}, { + trigger: '.o_back_button', + content: 'Go back to the kanban view the project created', +}, { + trigger: '.oe_kanban_global_click :contains("Project for Freeman")', + content: 'Open the project', }, { trigger: ".o_project_updates_breadcrumb", - content: 'Open Updates' + content: 'Open Updates', }, { trigger: ".o_rightpanel_title:eq(0):contains('Sold')", - content: 'Check the user sees Sold section' + content: 'Check the user sees Sold section', + run: function () {}, }, { trigger: ".o_rightpanel_left_text:eq(0):contains('Prepaid Hours')", content: 'Check the user sees Sold subsection', run: function () {}, -}, { +}, { trigger: ".o_rightpanel_title:eq(1):contains('Total Sold')", - content: 'Check the user sees Total Sold section' + content: 'Check the user sees Total Sold section', + run: function () {}, +}, { + trigger: ".oe_button_box .o_stat_text:contains('Sales Order')", + content: 'Check the user sees Sales Order Stat Button', + run: function () {}, +}, { + trigger: ".o_rightpanel_header:eq(1) .o_rightpanel_right_col:contains('Hours')", + content: 'Check the user sees Hours title', + run: function () {}, +}, { + trigger: ".o_rightpanel_section:eq(2) .o_rightpanel_data_row:contains('Effective')", + content: 'Check the user sees Effective subsection row', + run: function () {}, +}, { + trigger: ".o_rightpanel_section:eq(2) .o_rightpanel_data_row:contains('Remaining')", + content: 'Check the user sees Remaining subsection row', + run: function () {}, }, { trigger: ".o_rightpanel_title:eq(2):contains('Profitability')", - content: 'Check the user sees Profitability section' + content: 'Check the user sees Profitability section', + run: function () {}, +}, { + trigger: ".o_rightpanel_section:eq(3) .o_rightpanel_data_row:contains('Revenues')", + content: 'Check the user sees Profitability subsection row', + run: function () {}, +}, { + trigger: ".o_rightpanel_section:eq(3) .o_rightpanel_data_row:contains('Costs')", + content: 'Check the user sees Profitability subsection row', + run: function () {}, +}, { + trigger: ".o_rightpanel_section:eq(3) .o_rightpanel_data_row:contains('Margin')", + content: 'Check the user sees Profitability subsection row', + run: function () {}, }, { trigger: ".o_rightpanel_title:eq(3):contains('Milestones')", - content: 'Check the user sees Milestones section' + content: 'Check the user sees Milestones section', }, { - trigger: ".o-kanban-button-new" + trigger: ".o_add_milestone a", + content: "Add a first milestone", }, { trigger: "input.o_field_widget[name=name]", - run: 'text New update' + content: "Edit new Milestone", + run: 'text New milestone', }, { - trigger: ".o_form_button_save" + trigger: "input.datetimepicker-input[name=deadline]", + content: "Edit new Milestone", + run: 'text 12/12/2099', +}, { + trigger: ".modal-footer button", + content: "Save new Milestone", +}, { + trigger: ".o-kanban-button-new", + content: "Create new Project Update", +}, { + trigger: "input.o_field_widget[name=name]", + content: "Give a name to Project Update", + run: 'text New update', +}, { + trigger: ".o_form_button_save", + content: "Save Project Update", +}, { + trigger: ".o_field_widget[name=description] h3:contains('Sold')", + content: "Sold title must be in description in description", + run: function () {}, + }, { + trigger: ".o_field_widget[name=description] td:contains('Prepaid Hours')", + content: "Prepaid Hours title must be in description", + run: function () {}, +}, { + trigger: ".o_field_widget[name=description] h3:contains('Profitability')", + content: "Profitability title must be in description", + run: function () {}, +}, { + trigger: ".o_field_widget[name=description] h3:contains('Milestones')", + content: "Milestones title must be in description", + run: function () {}, }]); }); diff --git a/addons/sale_timesheet/views/project_task_views.xml b/addons/sale_timesheet/views/project_task_views.xml index b5ba5e96fcd..53721e0f4df 100644 --- a/addons/sale_timesheet/views/project_task_views.xml +++ b/addons/sale_timesheet/views/project_task_views.xml @@ -27,7 +27,7 @@