@@ -392,7 +391,7 @@
context="{'default_project_id': project_id}"
attrs="{'invisible': [('allow_milestones', '=', False)], 'column_invisible': [('parent.allow_milestones', '=', False)]}"
/>
-
+
@@ -427,7 +426,7 @@
context="{'default_project_id': project_id}"
attrs="{'invisible': [('allow_milestones', '=', False)], 'column_invisible': [('parent.allow_milestones', '=', False)]}"
/>
-
+
@@ -625,7 +624,7 @@
-
+
diff --git a/addons/sale_project/__init__.py b/addons/sale_project/__init__.py
index 3226df762b3..852082dcf71 100644
--- a/addons/sale_project/__init__.py
+++ b/addons/sale_project/__init__.py
@@ -4,3 +4,7 @@
from . import models
from . import controllers
from . import report
+
+def _set_allow_billable_in_project(env):
+ projects = env['project.project'].search([('partner_id', '!=', False), ('allow_billable', '=', False)])
+ projects.allow_billable = True
diff --git a/addons/sale_project/__manifest__.py b/addons/sale_project/__manifest__.py
index 1fd54153e9c..f69e0bd5ccb 100644
--- a/addons/sale_project/__manifest__.py
+++ b/addons/sale_project/__manifest__.py
@@ -26,5 +26,6 @@ This module allows to generate a project/task from sales orders.
'sale_project/static/src/components/project_right_side_panel/**/*',
],
},
+ 'post_init_hook': '_set_allow_billable_in_project',
'license': 'LGPL-3',
}
diff --git a/addons/sale_project/controllers/portal.py b/addons/sale_project/controllers/portal.py
index b621011cbdf..43cecdfb4a0 100644
--- a/addons/sale_project/controllers/portal.py
+++ b/addons/sale_project/controllers/portal.py
@@ -9,10 +9,13 @@ from odoo.addons.project.controllers.portal import ProjectCustomerPortal
class SaleProjectCustomerPortal(ProjectCustomerPortal):
- def _task_get_searchbar_groupby(self, milestones_allowed):
- values = super()._task_get_searchbar_groupby(milestones_allowed)
- values['sale_order'] = {'input': 'sale_order', 'label': _lt('Sales Order'), 'order': 8}
- values['sale_line'] = {'input': 'sale_line', 'label': _lt('Sales Order Item'), 'order': 9}
+ def _task_get_searchbar_groupby(self, milestones_allowed, project=False):
+ values = super()._task_get_searchbar_groupby(milestones_allowed, project)
+ if project and not project.sudo()._get_hide_partner():
+ del values['customer']
+ if not project or project.sudo().allow_billable:
+ values['sale_order'] = {'input': 'sale_order', 'label': _lt('Sales Order'), 'order': 8}
+ values['sale_line'] = {'input': 'sale_line', 'label': _lt('Sales Order Item'), 'order': 9}
return dict(sorted(values.items(), key=lambda item: item[1]["order"]))
def _task_get_groupby_mapping(self):
@@ -20,11 +23,14 @@ class SaleProjectCustomerPortal(ProjectCustomerPortal):
groupby_mapping.update(sale_order='sale_order_id', sale_line='sale_line_id')
return groupby_mapping
- def _task_get_searchbar_inputs(self, milestones_allowed):
- values = super()._task_get_searchbar_inputs(milestones_allowed)
- values['sale_order'] = {'input': 'sale_order', 'label': _lt('Search in Sales Order'), 'order': 8}
- values['sale_line'] = {'input': 'sale_line', 'label': _lt('Search in Sales Order Item'), 'order': 9}
- values['invoice'] = {'input': 'invoice', 'label': _lt('Search in Invoice'), 'order': 10}
+ def _task_get_searchbar_inputs(self, milestones_allowed, project=False):
+ values = super()._task_get_searchbar_inputs(milestones_allowed, project)
+ if project and not project.sudo()._get_hide_partner():
+ del values['customer']
+ if not project or project.sudo().allow_billable:
+ values['sale_order'] = {'input': 'sale_order', 'label': _lt('Search in Sales Order'), 'order': 8}
+ values['sale_line'] = {'input': 'sale_line', 'label': _lt('Search in Sales Order Item'), 'order': 9}
+ values['invoice'] = {'input': 'invoice', 'label': _lt('Search in Invoice'), 'order': 10}
return dict(sorted(values.items(), key=lambda item: item[1]["order"]))
def _task_get_search_domain(self, search_in, search):
diff --git a/addons/sale_project/models/project.py b/addons/sale_project/models/project.py
index 66d6be36df9..40a64676efd 100755
--- a/addons/sale_project/models/project.py
+++ b/addons/sale_project/models/project.py
@@ -27,6 +27,7 @@ class Project(models.Model):
has_any_so_with_nothing_to_invoice = fields.Boolean('Has a SO with an invoice status of No', compute='_compute_has_any_so_with_nothing_to_invoice')
invoice_count = fields.Integer(related='analytic_account_id.invoice_count', groups='account.group_account_readonly')
vendor_bill_count = fields.Integer(related='analytic_account_id.vendor_bill_count', groups='account.group_account_readonly')
+ partner_id = fields.Many2one(compute="_compute_partner_id", store=True, readonly=False)
@api.model
def _map_tasks_default_valeus(self, task, project):
@@ -34,6 +35,10 @@ class Project(models.Model):
defaults['sale_line_id'] = False
return defaults
+ @api.depends('allow_billable')
+ def _compute_partner_id(self):
+ self.filtered(lambda project: not project.allow_billable).partner_id = False
+
@api.depends('partner_id')
def _compute_sale_line_id(self):
self.filtered(
@@ -537,6 +542,18 @@ class Project(models.Model):
})
return buttons
+ # ---------------------------------------------------
+ # Actions
+ # ---------------------------------------------------
+
+ def _get_hide_partner(self):
+ return not self.allow_billable
+
+ def action_view_tasks(self):
+ action = super().action_view_tasks()
+ action['context']['hide_partner'] = self._get_hide_partner()
+ return action
+
def action_open_project_vendor_bills(self):
query = self.env['account.move.line']._search([('move_id.move_type', 'in', ['in_invoice', 'in_refund'])])
query.add_where('analytic_distribution ? %s', [str(self.analytic_account_id.id)])
@@ -562,6 +579,7 @@ class Project(models.Model):
action = super().action_project_sharing()
action['context'].update({
'sale_show_partner_name': True,
+ 'hide_partner': self._get_hide_partner(),
})
return action
@@ -605,6 +623,12 @@ class ProjectTask(models.Model):
task.partner_id = sale_order_id.partner_id
task.sale_order_id = sale_order_id
+ @api.depends('allow_billable')
+ def _compute_partner_id(self):
+ billable_task = self.filtered('allow_billable')
+ (self - billable_task).partner_id = False
+ super(ProjectTask, billable_task)._compute_partner_id()
+
@api.depends('partner_id.commercial_partner_id', 'sale_line_id.order_partner_id', 'parent_id.sale_line_id', 'project_id.sale_line_id', 'milestone_id.sale_line_id', 'allow_billable')
def _compute_sale_line(self):
for task in self:
diff --git a/addons/sale_project/tests/test_child_tasks.py b/addons/sale_project/tests/test_child_tasks.py
index 6f91ae99da8..8e86663bb4e 100644
--- a/addons/sale_project/tests/test_child_tasks.py
+++ b/addons/sale_project/tests/test_child_tasks.py
@@ -47,7 +47,7 @@ class TestNestedTaskUpdate(TransactionCase):
self.assertEqual(parent.partner_id, self.project.partner_id, "The partner set on the parent task should the one set on the project linked")
self.assertFalse(child.project_id, "The project set on the subtask should be False by default")
self.assertFalse(child.allow_billable, "The subtask should not be billable as it is parent task since no project is set")
- self.assertEqual(child.partner_id, parent.partner_id, "The partner set on the subtask should be the one of its parent task")
+ self.assertFalse(child.partner_id, "Subtask with no project should not have partner")
def test_creating_subtask_user_id_on_parent_dont_go_on_child(self):
parent = self.env['project.task'].create({'name': 'parent', 'user_ids': [(4, self.user.id)]})
@@ -56,12 +56,12 @@ class TestNestedTaskUpdate(TransactionCase):
def test_creating_subtask_partner_id_on_parent_goes_on_child(self):
parent = self.env['project.task'].create({'name': 'parent', 'partner_id': self.user.partner_id.id, 'project_id': self.project.id})
- child = self.env['project.task'].create({'name': 'child', 'parent_id': parent.id})
+ child = self.env['project.task'].create({'name': 'child', 'parent_id': parent.id, 'project_id': self.project.id})
child._compute_partner_id() # the compute will be triggered since the user set the parent_id.
self.assertEqual(child.partner_id, self.user.partner_id)
# Another case, it is the parent as a default value
- child = self.env['project.task'].with_context(default_parent_id=parent.id).create({'name': 'child'})
+ child = self.env['project.task'].with_context(default_parent_id=parent.id, default_project_id=self.project.id).create({'name': 'child'})
self.assertEqual(child.partner_id, self.user.partner_id)
def test_creating_subtask_sale_line_id_on_parent_goes_on_child_if_same_partner_in_values(self):
@@ -157,10 +157,10 @@ class TestNestedTaskUpdate(TransactionCase):
self.assertFalse(child.user_ids)
def test_linking_partner_id_on_parent_write_on_child(self):
- parent = self.env['project.task'].create({'name': 'parent', 'partner_id': self.user.partner_id.id})
+ parent = self.env['project.task'].create({'name': 'parent', 'partner_id': self.user.partner_id.id, 'project_id': self.project.id})
child = self.env['project.task'].create({'name': 'child', 'partner_id': False})
self.assertFalse(child.partner_id)
- child.write({'parent_id': parent.id})
+ child.write({'parent_id': parent.id, 'project_id': self.project.id})
self.assertEqual(child.partner_id, self.user.partner_id)
def test_linking_sale_line_id_on_parent_write_on_child_if_same_partner(self):
diff --git a/addons/sale_project/views/project_sharing_views.xml b/addons/sale_project/views/project_sharing_views.xml
index d7dd046b4ca..b78261ce644 100644
--- a/addons/sale_project/views/project_sharing_views.xml
+++ b/addons/sale_project/views/project_sharing_views.xml
@@ -9,6 +9,7 @@
+