bemade_fsm: Changes to site and work order contact behaviour
1. Site contacts and work order contacts now get set on create and write of all FSM project tasks. If the task has no parent_id, it will try to set them from the sale order if it has one. Otherwise, they are simply inherited from the parent. 2. Changing the site or work order contact on a task no longer feeds back to the sale order. 3. Changing the site or work order contacts on a parent task propagates the change to its subtasks.
This commit is contained in:
parent
ba40259525
commit
dc04ab952b
6 changed files with 102 additions and 76 deletions
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
<field name="name">Equipments</field>
|
||||
<field name="res_model">bemade_fsm.equipment</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="context">{'default_partner_id': active_id}</field>
|
||||
<field name="domain">[("partner_id", "=", active_id)]</field>
|
||||
<field name="context">{'default_partner_location_id': active_id}</field>
|
||||
<field name="domain">[("partner_location_id", "=", active_id)]</field>
|
||||
</record>
|
||||
|
||||
<record id="partner_equipment_location_view_form" model="ir.ui.view">
|
||||
|
|
|
|||
Loading…
Reference in a new issue