diff --git a/bemade_fsm/__manifest__.py b/bemade_fsm/__manifest__.py index 456a968..02e8490 100644 --- a/bemade_fsm/__manifest__.py +++ b/bemade_fsm/__manifest__.py @@ -20,7 +20,7 @@ ######################################################################################## { 'name': 'Improved Field Service Management', - 'version': '17.0.0.1.1', + 'version': '17.0.0.2.0', 'summary': 'Adds functionality necessary for managing field service operations at Durpro.', 'description': 'Adds functionality necessary for managing field service operations at Durpro.', 'category': 'Services/Field Service', diff --git a/bemade_fsm/models/equipment.py b/bemade_fsm/models/equipment.py index 24587ab..ed15c9a 100644 --- a/bemade_fsm/models/equipment.py +++ b/bemade_fsm/models/equipment.py @@ -48,14 +48,14 @@ class Equipment(models.Model): args = args or [] if name: equipments = self.search([ - '|', '|', '|', + '|', '|', ('pid_tag', operator, name), ('name', operator, name), ('partner_location_id.name', operator, name)], limit=limit) else: equipments = self.search(args, limit=limit) - return equipments.name_get() + return [(equipment.id, equipment.display_name) for equipment in equipments] def action_view_equipment(self): return { diff --git a/bemade_fsm/models/task.py b/bemade_fsm/models/task.py index e144222..80843b2 100644 --- a/bemade_fsm/models/task.py +++ b/bemade_fsm/models/task.py @@ -1,7 +1,4 @@ from odoo import fields, models, api, Command, _ -from odoo.exceptions import ValidationError, UserError -from odoo.osv import expression -from collections import defaultdict, namedtuple from odoo.addons.project.models.project_task import CLOSED_STATES import re @@ -23,9 +20,6 @@ class Task(models.Model): relation="task_work_order_contact_rel", column1="task_id", column2="res_partner_id", - compute="_compute_contacts", - inverse="_inverse_contacts", - store=True ) site_contacts = fields.Many2many( @@ -33,9 +27,6 @@ class Task(models.Model): relation="task_site_contact_rel", column1="task_id", column2="res_partner_id", - compute="_compute_contacts", - inverse="_inverse_contacts", - store=True ) # Override related field to make it return false if this is an FSM subtask @@ -77,6 +68,12 @@ class Task(models.Model): def create(self, vals): res = super().create(vals) for rec in res: + if rec.parent_id and rec.is_fsm: + rec.partner_id = rec.parent_id.partner_id + if not rec.work_order_contacts and rec.parent_id: + rec.work_order_contacts = rec.parent_id.work_order_contacts + if not rec.site_contacts and rec.parent_id: + rec.site_contacts = rec.parent_id.site_contacts if rec.sale_order_id: seq = 1 prev_seqs = self.sale_order_id.tasks_ids and \ @@ -87,6 +84,11 @@ class Task(models.Model): seq += max(map(lambda n: int(n.group(1)) if n else 0, matches)) rec.work_order_number = rec.sale_order_id.name.replace('SO', 'SVR', 1) \ + f"-{seq}" + # If the task is linked to a sales order and has no parent, it should inherit SO work order contacts + if not rec.parent_id and not rec.work_order_contacts and rec.sale_order_id.work_order_contacts: + rec.work_order_contacts = rec.sale_order_id.work_order_contacts + if not rec.parent_id and not rec.site_contacts and rec.sale_order_id.site_contacts: + rec.site_contacts = rec.sale_order_id.site_contacts return res def write(self, vals): @@ -101,6 +103,10 @@ class Task(models.Model): # Here we use child_ids instead of _get_all_subtasks() so as to allow for setting propagate_assignment # to false on a child task. to_propagate.child_ids.write({'user_ids': vals['user_ids']}) + if 'site_contacts' in vals and self.child_ids: + self._get_all_subtasks().write({'site_contacts': [Command.set(self.site_contacts.ids)]}) + if 'work_order_contacts' in vals and self.child_ids: + self._get_all_subtasks().write({'work_order_contacts': [Command.set(self.work_order_contacts.ids)]}) return res @api.depends('sale_order_id') @@ -123,30 +129,6 @@ class Task(models.Model): for project in self.project_id } - @api.depends('sale_line_id.order_id.site_contacts', - 'sale_line_id.order_id.work_order_contacts') - def _compute_contacts(self): - """ The work order contacts and site contacts for a given task are taken from the sale order if the task - is related to one, and from the task's customer if there is no sale order related to the task.""" - for rec in self: - site_contacts = self.sale_line_id and self.sale_line_id.order_id.site_contacts or \ - self.partner_id.site_contacts - work_order_contacts = self.sale_line_id and self.sale_line_id.order_id.work_order_contacts or \ - self.partner_id.work_order_contacts - rec.write({ - 'site_contacts': [Command.set(site_contacts.ids)], - 'work_order_contacts': [Command.set(work_order_contacts.ids)] - }) - - def _inverse_contacts(self): - """ If the task is linked to a sales order, the sales order should have its contacts updated to match.""" - for rec in self: - if rec.sale_line_id: - rec.sale_line_id.order_id.write({ - 'work_order_contacts': [Command.set(rec.work_order_contacts.ids)], - 'site_contacts': [Command.set(rec.site_contacts.ids)], - }) - @api.depends('parent_id.visit_id', 'project_id.is_fsm', 'project_id.allow_billable') def _compute_allow_billable(self): for rec in self: diff --git a/bemade_fsm/tests/test_sale_order.py b/bemade_fsm/tests/test_sale_order.py index 1e0561a..fd5d620 100644 --- a/bemade_fsm/tests/test_sale_order.py +++ b/bemade_fsm/tests/test_sale_order.py @@ -211,33 +211,6 @@ class TestSalesOrder(BemadeFSMBaseTest): self.assertEqual(sol.task_id.work_order_contacts, partner.work_order_contacts) self.assertEqual(sol.task_id.site_contacts, partner.site_contacts) - def test_changing_task_contacts_mirrors_with_sale_order(self): - partner = self._generate_partner() - contact = self._generate_partner("Contact", "person", partner) - so = self._generate_sale_order(partner) - product = self._generate_product() - sol = self._generate_sale_order_line(so, product) - so.action_confirm() - task = sol.task_id - task_form = Form(task) - - # Now change the site/work order contact on the task and make sure it feeds back to the sales order - task_form.site_contacts.add(contact) - task_form.work_order_contacts.add(contact) - task_form.save() - - self.assertEqual(task.site_contacts, so.site_contacts) - self.assertEqual(task.work_order_contacts, so.work_order_contacts) - - # Test changing it on the SO feeds back to the task as well - f = Form(so) - f.work_order_contacts.remove(contact.id) - f.site_contacts.remove(contact.id) - f.save() - - self.assertEqual(task.site_contacts, so.site_contacts) - self.assertEqual(task.work_order_contacts, so.work_order_contacts) - def test_tasks_created_at_order_confirmation_have_no_assignees(self): so, visit, sol1, sol2 = self._generate_so_with_one_visit_two_lines() user = self._generate_project_user(name="User", login='login') diff --git a/bemade_fsm/tests/test_task.py b/bemade_fsm/tests/test_task.py index 14b838a..8d5a3ef 100644 --- a/bemade_fsm/tests/test_task.py +++ b/bemade_fsm/tests/test_task.py @@ -1,5 +1,5 @@ from .test_bemade_fsm_common import BemadeFSMBaseTest -from odoo.tests.common import tagged +from odoo.tests.common import tagged, Form from odoo import Command @@ -10,18 +10,21 @@ class TaskTest(BemadeFSMBaseTest): def setUpClass(cls): # Chose to set up all tests the same way since this code was becoming very redundant super().setUpClass() - cls.so = cls._generate_sale_order() - cls.template = cls._generate_task_template(names=['Parent', 'Child', 'Grandchild'], structure=[2, 1]) - cls.product = cls._generate_product(task_template=cls.template) - cls.sol = cls._generate_sale_order_line(sale_order=cls.so, product=cls.product) cls.user = cls._generate_project_manager_user('Bob', 'Bob') - cls.so.action_confirm() - cls.task = cls.sol.task_id + + def _generate_so_with_multilevel_task_template(self): + so = self._generate_sale_order() + template = self._generate_task_template(names=['Parent', 'Child', 'Grandchild'], structure=[2, 1]) + product = self._generate_product(task_template=template) + sol = self._generate_sale_order_line(sale_order=so, product=product) + return so, sol def test_reassigning_assignment_propagating_task_changes_subtasks(self): - task = self.task - task.propagate_assignment = True + so, sol = self._generate_so_with_multilevel_task_template() + so.action_confirm() + task = sol.task_id + task.propagate_assignment = True task.write({ 'user_ids': [Command.set([self.user.id])], 'propagate_assignment': True, @@ -30,7 +33,10 @@ class TaskTest(BemadeFSMBaseTest): self.assertTrue(all([t.user_ids == self.user for t in task | task._get_all_subtasks()])) def test_reassigning_task_doesnt_propagate_by_default(self): - task = self.task + so, sol = self._generate_so_with_multilevel_task_template() + so.action_confirm() + task = sol.task_id + task.write({ 'user_ids': [Command.set([self.user.id])], }) @@ -38,7 +44,9 @@ class TaskTest(BemadeFSMBaseTest): self.assertFalse(any([t.user_ids for t in task.child_ids.child_ids])) def test_unset_propagate_assignment_unsets_for_all_children(self): - task = self.task + so, sol = self._generate_so_with_multilevel_task_template() + so.action_confirm() + task = sol.task_id # First, set propagation and assign task.propagate_assignment = True task.write({ @@ -53,3 +61,66 @@ class TaskTest(BemadeFSMBaseTest): }) self.assertTrue(all([not t.user_ids for t in task | task.child_ids])) self.assertTrue(all([t.user_ids == self.user for t in task.child_ids.child_ids])) + + def test_task_gets_work_order_contacts_from_sale_order(self): + so, sol = self._generate_so_with_multilevel_task_template() + work_order_contacts = self._generate_partner(parent=so.partner_id) | self._generate_partner( + parent=so.partner_id) + so.write({'work_order_contacts': [(6, 0, work_order_contacts.ids)]}) + + so.action_confirm() + task = sol.task_id + + self.assertEqual(task.work_order_contacts, so.work_order_contacts) + # Just a safeguard to make sure we set it properly on the SO + self.assertEqual(len(task.work_order_contacts), 2) + # Make sure all subtasks got the same + for subtask in task._get_all_subtasks(): + self.assertEqual(subtask.work_order_contacts, so.work_order_contacts) + + def test_task_gets_site_contacts_from_sale_order(self): + so, sol = self._generate_so_with_multilevel_task_template() + site_contacts = self._generate_partner(parent=so.partner_id) | self._generate_partner(parent=so.partner_id) + so.write({'site_contacts': [(6, 0, site_contacts.ids)]}) + + so.action_confirm() + task = sol.task_id + + self.assertEqual(task.site_contacts, so.site_contacts) + # Just a safeguard to make sure we set it properly on the SO + self.assertEqual(len(task.site_contacts), 2) + # Make sure all subtasks got the same + for subtask in task._get_all_subtasks(): + self.assertEqual(subtask.site_contacts, so.site_contacts) + + def test_task_gets_work_order_contacts_from_parent(self): + so, sol = self._generate_so_with_multilevel_task_template() + work_order_contacts = self._generate_partner(parent=so.partner_id) | self._generate_partner(parent=so.partner_id) + so.write({'work_order_contacts': [(6, 0, work_order_contacts.ids)]}) + + so.action_confirm() + task = sol.task_id + task.write({'work_order_contacts': [Command.link(self._generate_partner(parent=so.partner_id).id)]}) + for subtask in task._get_all_subtasks(): + self.assertEqual(subtask.work_order_contacts, task.work_order_contacts) + with Form(task) as task_form: + with task_form.child_ids.new() as subtask: + subtask.name = "Subtask 1" + subtask = task.child_ids[-1] + self.assertEqual(subtask.work_order_contacts, task.work_order_contacts) + + def test_task_gets_site_contacts_from_parent(self): + so, sol = self._generate_so_with_multilevel_task_template() + site_contacts = self._generate_partner(parent=so.partner_id) | self._generate_partner(parent=so.partner_id) + so.write({'site_contacts': [(6, 0, site_contacts.ids)]}) + + so.action_confirm() + task = sol.task_id + task.write({'site_contacts': [Command.link(self._generate_partner(parent=so.partner_id).id)]}) + for subtask in task._get_all_subtasks(): + self.assertEqual(subtask.site_contacts, task.site_contacts) + with Form(task) as task_form: + with task_form.child_ids.new() as subtask: + subtask.name = "Subtask 1" + subtask = task.child_ids[-1] + self.assertEqual(subtask.site_contacts, task.site_contacts) diff --git a/bemade_fsm/views/res_partner.xml b/bemade_fsm/views/res_partner.xml index 3061fb2..68d7efb 100644 --- a/bemade_fsm/views/res_partner.xml +++ b/bemade_fsm/views/res_partner.xml @@ -4,8 +4,8 @@ Equipments bemade_fsm.equipment tree,form - {'default_partner_id': active_id} - [("partner_id", "=", active_id)] + {'default_partner_location_id': active_id} + [("partner_location_id", "=", active_id)]