bemade_fsm: added a test for contact setting on SO shipping address change. Fixes #53.
This commit is contained in:
parent
6f37902f42
commit
eea5b54a4f
8 changed files with 221 additions and 20 deletions
|
|
@ -4,3 +4,4 @@ from . import sale_order
|
||||||
from . import equipment
|
from . import equipment
|
||||||
from . import task
|
from . import task
|
||||||
from . import res_partner
|
from . import res_partner
|
||||||
|
from . import fsm_visit
|
||||||
|
|
|
||||||
37
bemade_fsm/models/fsm_visit.py
Normal file
37
bemade_fsm/models/fsm_visit.py
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
from odoo import models, fields, api, _
|
||||||
|
|
||||||
|
|
||||||
|
class FSMVisit(models.Model):
|
||||||
|
_name = "bemade_fsm.visit"
|
||||||
|
_description = 'Represents a single visit by assigned service personnel.'
|
||||||
|
|
||||||
|
label = fields.Text(string="Label",
|
||||||
|
required=True,
|
||||||
|
related='so_section_id.name',
|
||||||
|
readonly=False)
|
||||||
|
approx_date = fields.Date(string='Approximate Date')
|
||||||
|
so_section_id = fields.Many2one(comodel_name="sale.order.line",
|
||||||
|
string="Sale Order Section",
|
||||||
|
help="The section on the sale order that represents the labour and parts for "
|
||||||
|
"this visit")
|
||||||
|
sale_order_id = fields.Many2one(comodel_name="sale.order",
|
||||||
|
string="Sales Order",
|
||||||
|
required=True)
|
||||||
|
is_completed = fields.Boolean(string="Completed",
|
||||||
|
related="so_section_id.is_fully_delivered")
|
||||||
|
is_invoiced = fields.Boolean(string="Invoiced",
|
||||||
|
compute="_compute_is_invoiced")
|
||||||
|
|
||||||
|
def _compute_is_invoiced(self):
|
||||||
|
self.is_invoiced = False
|
||||||
|
|
||||||
|
@api.model_create_multi
|
||||||
|
def create(self, vals_list):
|
||||||
|
recs = super().create(vals_list)
|
||||||
|
for i, rec in enumerate(recs):
|
||||||
|
rec.so_section_id = rec.env['sale.order.line'].create({
|
||||||
|
'order_id': rec.sale_order_id.id,
|
||||||
|
'display_type': 'line_section',
|
||||||
|
'name': vals_list[i]['label'],
|
||||||
|
})
|
||||||
|
return recs
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
from odoo import fields, models, api, _, Command
|
from odoo import fields, models, api, _, Command
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
class SaleOrder(models.Model):
|
class SaleOrder(models.Model):
|
||||||
|
|
@ -24,6 +25,9 @@ class SaleOrder(models.Model):
|
||||||
inverse='_inverse_default_contacts',
|
inverse='_inverse_default_contacts',
|
||||||
string='Work Order Recipients',
|
string='Work Order Recipients',
|
||||||
store=True)
|
store=True)
|
||||||
|
visit_ids = fields.One2many(comodel_name='bemade_fsm.visit',
|
||||||
|
inverse_name="sale_order_id",
|
||||||
|
readonly=False)
|
||||||
|
|
||||||
@api.onchange('partner_shipping_id')
|
@api.onchange('partner_shipping_id')
|
||||||
def _onchange_partner_shipping_id(self):
|
def _onchange_partner_shipping_id(self):
|
||||||
|
|
@ -53,6 +57,12 @@ class SaleOrder(models.Model):
|
||||||
class SaleOrderLine(models.Model):
|
class SaleOrderLine(models.Model):
|
||||||
_inherit = 'sale.order.line'
|
_inherit = 'sale.order.line'
|
||||||
|
|
||||||
|
visit_id = fields.One2many(comodel_name="bemade_fsm.visit",
|
||||||
|
inverse_name="so_section_id",
|
||||||
|
string="Visit")
|
||||||
|
is_fully_delivered = fields.Boolean(string="Fully Delivered",
|
||||||
|
compute="_compute_is_fully_delivered")
|
||||||
|
|
||||||
def _timesheet_create_task(self, project):
|
def _timesheet_create_task(self, project):
|
||||||
""" Generate task for the given so line, and link it.
|
""" Generate task for the given so line, and link it.
|
||||||
:param project: record of project.project in which the task should be created
|
:param project: record of project.project in which the task should be created
|
||||||
|
|
@ -78,7 +88,7 @@ class SaleOrderLine(models.Model):
|
||||||
task.child_ids.write({'sale_order_id': None, 'sale_line_id': None, })
|
task.child_ids.write({'sale_order_id': None, 'sale_line_id': None, })
|
||||||
return task
|
return task
|
||||||
|
|
||||||
def _generate_task_name(template = None):
|
def _generate_task_name(template=None):
|
||||||
template_name = template and template.name
|
template_name = template and template.name
|
||||||
return f"{self.order_id.name}: {self.order_id.partner_shipping_id.name} - {self.name} ({template_name})"
|
return f"{self.order_id.name}: {self.order_id.partner_shipping_id.name} - {self.name} ({template_name})"
|
||||||
|
|
||||||
|
|
@ -116,3 +126,27 @@ class SaleOrderLine(models.Model):
|
||||||
task.equipment_id = self.order_id.equipment_id
|
task.equipment_id = self.order_id.equipment_id
|
||||||
task.name = _generate_task_name(tmpl)
|
task.name = _generate_task_name(tmpl)
|
||||||
return task
|
return task
|
||||||
|
|
||||||
|
@api.depends('order_id.order_line', 'display_type', 'qty_to_deliver', 'order_id.order_line.qty_to_deliver',
|
||||||
|
'order_id.order_line.display_type')
|
||||||
|
def _compute_is_fully_delivered(self):
|
||||||
|
if not self.display_type:
|
||||||
|
self.is_fully_delivered = self.qty_to_deliver == 0
|
||||||
|
elif self.display_type == 'line_note':
|
||||||
|
self.is_fully_delivered = True
|
||||||
|
else:
|
||||||
|
for line in self.order_id.order_line:
|
||||||
|
# Iterate through the lines on the SO in order until we find this one in the list
|
||||||
|
found = False
|
||||||
|
if line == self:
|
||||||
|
found = True
|
||||||
|
if not found:
|
||||||
|
continue
|
||||||
|
# If we've hit the next section without returning False, all lines in this section were delivered
|
||||||
|
if found and line.display_type == 'line_section':
|
||||||
|
self.is_fully_delivered = True
|
||||||
|
return
|
||||||
|
if line.qty_to_deliver > 0:
|
||||||
|
self.is_fully_delivered = False
|
||||||
|
return
|
||||||
|
self.is_fully_delivered = True
|
||||||
|
|
|
||||||
|
|
@ -3,4 +3,5 @@ access_bemade_fsm_task_template,bemade_fsm_task_template,model_project_task_temp
|
||||||
access_bemade_fsm_task_template,bemade_fsm_task_template,model_project_task_template,project.group_project_user,1,1,1,1
|
access_bemade_fsm_task_template,bemade_fsm_task_template,model_project_task_template,project.group_project_user,1,1,1,1
|
||||||
access_bemade_fsm_equipment,bemade_fsm_equipment,model_bemade_fsm_equipment,base.group_user,1,1,1,1
|
access_bemade_fsm_equipment,bemade_fsm_equipment,model_bemade_fsm_equipment,base.group_user,1,1,1,1
|
||||||
access_bemade_fsm_equipment_tag,bemade_fsm_equipment_tag,model_bemade_fsm_equipment_tag,base.group_user,1,1,1,1
|
access_bemade_fsm_equipment_tag,bemade_fsm_equipment_tag,model_bemade_fsm_equipment_tag,base.group_user,1,1,1,1
|
||||||
access_bemade_fsm_equipment_type,access_bemade_fsm_equipment_type,model_bemade_fsm_equipment_type,base.group_user,1,0,0,0
|
access_bemade_fsm_equipment_type,access_bemade_fsm_equipment_type,model_bemade_fsm_equipment_type,base.group_user,1,0,0,0
|
||||||
|
access_bemade_fsm_visit,access_bemade_fsm_visit,model_bemade_fsm_visit,base.group_user,1,1,1,1
|
||||||
|
|
|
||||||
|
|
|
@ -4,3 +4,4 @@ from . import test_sale_order
|
||||||
from . import test_equipment
|
from . import test_equipment
|
||||||
from . import test_fsm_contact_setting
|
from . import test_fsm_contact_setting
|
||||||
from . import test_so_task_contacts
|
from . import test_so_task_contacts
|
||||||
|
from . import test_fsm_visit
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,33 @@
|
||||||
from odoo.tests import TransactionCase, HttpCase, tagged
|
from odoo.tests import TransactionCase, HttpCase, tagged, Form
|
||||||
from odoo import Command
|
from odoo import Command
|
||||||
from .test_bemade_fsm_common import FSMManagerUserTransactionCase
|
from .test_bemade_fsm_common import FSMManagerUserTransactionCase
|
||||||
|
|
||||||
|
|
||||||
|
def create_base_contacts(cls):
|
||||||
|
Partner = cls.env['res.partner']
|
||||||
|
cls.parent_co = Partner.create({
|
||||||
|
'name': 'Parent Co',
|
||||||
|
'company_type': 'company',
|
||||||
|
})
|
||||||
|
cls.contact_1 = Partner.create({
|
||||||
|
'name': 'Contact 1',
|
||||||
|
'company_type': 'person',
|
||||||
|
'parent_id': cls.parent_co.id,
|
||||||
|
})
|
||||||
|
cls.contact_2 = Partner.create({
|
||||||
|
'name': 'Contact 2',
|
||||||
|
'company_type': 'person',
|
||||||
|
'parent_id': cls.parent_co.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
@tagged("-at_install", "post_install")
|
@tagged("-at_install", "post_install")
|
||||||
class SaleOrderFSMContactsCase(FSMManagerUserTransactionCase):
|
class SaleOrderFSMContactsCase(FSMManagerUserTransactionCase):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
super().setUpClass()
|
super().setUpClass()
|
||||||
Partner = cls.env['res.partner']
|
create_base_contacts(cls)
|
||||||
cls.parent_co = Partner.create({
|
|
||||||
'name': 'Parent Co',
|
|
||||||
'company_type': 'company',
|
|
||||||
})
|
|
||||||
cls.contact_1 = Partner.create({
|
|
||||||
'name': 'Contact 1',
|
|
||||||
'company_type': 'person',
|
|
||||||
'parent_id': cls.parent_co.id,
|
|
||||||
})
|
|
||||||
cls.contact_2 = Partner.create({
|
|
||||||
'name': 'Contact 2',
|
|
||||||
'company_type': 'person',
|
|
||||||
'parent_id': cls.parent_co.id,
|
|
||||||
})
|
|
||||||
|
|
||||||
def test_site_contacts(self):
|
def test_site_contacts(self):
|
||||||
# Make sure the SO pulls the defaults from the partner correctly
|
# Make sure the SO pulls the defaults from the partner correctly
|
||||||
|
|
@ -56,10 +60,11 @@ class SaleOrderFSMContactsCase(FSMManagerUserTransactionCase):
|
||||||
so.work_order_contacts != so.partner_id.work_order_contacts and len(so.partner_id.work_order_contacts) == 2)
|
so.work_order_contacts != so.partner_id.work_order_contacts and len(so.partner_id.work_order_contacts) == 2)
|
||||||
|
|
||||||
|
|
||||||
class SaleOrderMultiLocationContactsTest(SaleOrderFSMContactsCase):
|
class SaleOrderMultiLocationContactsTest(FSMManagerUserTransactionCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
super().setUpClass()
|
super().setUpClass()
|
||||||
|
create_base_contacts(cls)
|
||||||
cls.shipping_location = cls.env['res.partner'].create({
|
cls.shipping_location = cls.env['res.partner'].create({
|
||||||
'name': 'Shipping location',
|
'name': 'Shipping location',
|
||||||
'company_type': 'company',
|
'company_type': 'company',
|
||||||
|
|
@ -90,3 +95,18 @@ class SaleOrderMultiLocationContactsTest(SaleOrderFSMContactsCase):
|
||||||
self.assertEqual(so.partner_shipping_id, self.shipping_location)
|
self.assertEqual(so.partner_shipping_id, self.shipping_location)
|
||||||
self.assertEqual(so.site_contacts, self.shipping_location.site_contacts)
|
self.assertEqual(so.site_contacts, self.shipping_location.site_contacts)
|
||||||
self.assertEqual(so.work_order_contacts, self.shipping_location.work_order_contacts)
|
self.assertEqual(so.work_order_contacts, self.shipping_location.work_order_contacts)
|
||||||
|
|
||||||
|
def test_onchange_shipping_address(self):
|
||||||
|
so = self.env['sale.order'].create({
|
||||||
|
'partner_id': self.parent_co.id,
|
||||||
|
'partner_shipping_id': self.parent_co.id,
|
||||||
|
})
|
||||||
|
self.assertFalse(so.work_order_contacts)
|
||||||
|
self.assertFalse(so.site_contacts)
|
||||||
|
|
||||||
|
form = Form(so)
|
||||||
|
form.partner_shipping_id = self.shipping_location
|
||||||
|
form.save()
|
||||||
|
|
||||||
|
self.assertEqual(so.work_order_contacts, self.shipping_location.work_order_contacts)
|
||||||
|
self.assertEqual(so.site_contacts, self.shipping_location.site_contacts)
|
||||||
|
|
|
||||||
91
bemade_fsm/tests/test_fsm_visit.py
Normal file
91
bemade_fsm/tests/test_fsm_visit.py
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
from odoo.tests import TransactionCase, tagged, Form
|
||||||
|
|
||||||
|
|
||||||
|
@tagged('-at_install', 'post_install')
|
||||||
|
class FSMVisitTest(TransactionCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super().setUpClass()
|
||||||
|
|
||||||
|
def test_create_visit_sets_name_on_section(self):
|
||||||
|
so = self.generate_sale_order()
|
||||||
|
self.add_service_so_line(so)
|
||||||
|
|
||||||
|
visit = self.generate_visit(so)
|
||||||
|
|
||||||
|
self.assertTrue(visit.so_section_id)
|
||||||
|
self.assertEqual(visit.so_section_id.name, visit.label)
|
||||||
|
|
||||||
|
def test_change_visit_section_name(self):
|
||||||
|
so = self.generate_sale_order()
|
||||||
|
visit = self.generate_visit(so, label="First Label")
|
||||||
|
line = visit.so_section_id
|
||||||
|
|
||||||
|
line.name = "Second Label"
|
||||||
|
|
||||||
|
self.assertEqual(visit.label, "Second Label")
|
||||||
|
|
||||||
|
def test_change_visit_label_changes_section_name(self):
|
||||||
|
so = self.generate_sale_order()
|
||||||
|
visit = self.generate_visit(so, label="First Label")
|
||||||
|
line = visit.so_section_id
|
||||||
|
|
||||||
|
visit.label = "Second Label"
|
||||||
|
|
||||||
|
self.assertEqual(line.name, "Second Label")
|
||||||
|
|
||||||
|
def test_visit_completes_when_task_completes(self):
|
||||||
|
so = self.generate_sale_order()
|
||||||
|
visit = self.generate_visit(so)
|
||||||
|
sol = self.add_service_so_line(so, task=True)
|
||||||
|
so.action_confirm()
|
||||||
|
task = so.order_line.filtered(lambda l: l.task_id).task_id
|
||||||
|
|
||||||
|
task.action_fsm_validate()
|
||||||
|
|
||||||
|
self.assertTrue(visit.is_completed)
|
||||||
|
|
||||||
|
def generate_visit(self, sale_order, label="Test Label"):
|
||||||
|
return self.env['bemade_fsm.visit'].create([{
|
||||||
|
'sale_order_id': sale_order.id,
|
||||||
|
'label': label,
|
||||||
|
}])
|
||||||
|
|
||||||
|
def generate_sale_order(self, partner_id=None):
|
||||||
|
so = self.env['sale.order'].create({
|
||||||
|
'partner_id': partner_id or self.generate_partner().id,
|
||||||
|
'client_order_ref': 'Test',
|
||||||
|
})
|
||||||
|
return so
|
||||||
|
|
||||||
|
def add_service_so_line(self, sale_order, task: bool = False):
|
||||||
|
""" Generates a sales order line for a service product.
|
||||||
|
|
||||||
|
:param sale_order: The sales order to which the new line is to be added
|
||||||
|
:param task: If true, the created line will be for a product with service_tracking=task_global_project
|
||||||
|
"""
|
||||||
|
if not task:
|
||||||
|
service_tracking = 'no'
|
||||||
|
project = False
|
||||||
|
else:
|
||||||
|
project = self.env.ref("industry_fsm.fsm_project")
|
||||||
|
service_tracking = 'task_global_project'
|
||||||
|
product = self.env['product.product'].create({
|
||||||
|
'name': 'Test Product',
|
||||||
|
'type': 'service',
|
||||||
|
'service_tracking': service_tracking,
|
||||||
|
'project_id': project and project.id,
|
||||||
|
'price': 100.0,
|
||||||
|
'sale_ok': True,
|
||||||
|
})
|
||||||
|
return self.env['sale.order.line'].create({
|
||||||
|
'product_id': product.id,
|
||||||
|
'order_id': sale_order.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
def generate_partner(self):
|
||||||
|
partner = self.env['res.partner'].create({
|
||||||
|
'name': 'Test Partner',
|
||||||
|
'company_type': 'company',
|
||||||
|
})
|
||||||
|
return partner
|
||||||
|
|
@ -8,7 +8,11 @@
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<xpath expr="//page[@name='other_information']" position="before">
|
<xpath expr="//page[@name='other_information']" position="before">
|
||||||
<page name="field_service" string="Field Service">
|
<page name="field_service" string="Field Service">
|
||||||
<group name="field_service_info">
|
<group name="fsm_visits" string="Service Visits">
|
||||||
|
<field name="visit_ids"
|
||||||
|
context="{'tree_view_ref': 'bemade_fsm.bemade_fsm_visit_tree'}"/>
|
||||||
|
</group>
|
||||||
|
<group name="field_service_info" string="Contacts and Equipment">
|
||||||
<field name="equipment_id"
|
<field name="equipment_id"
|
||||||
domain="[('partner_location_id', '=', partner_shipping_id)]"
|
domain="[('partner_location_id', '=', partner_shipping_id)]"
|
||||||
context="{'default_partner_location_id': partner_shipping_id,}"/>
|
context="{'default_partner_location_id': partner_shipping_id,}"/>
|
||||||
|
|
@ -21,5 +25,17 @@
|
||||||
</xpath>
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
<record id="bemade_fsm_visit_tree" model="ir.ui.view">
|
||||||
|
<field name="name">bemade_fsm.visit.tree</field>
|
||||||
|
<field name="model">bemade_fsm.visit</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree editable="bottom">
|
||||||
|
<field name="label"/>
|
||||||
|
<field name="approx_date"/>
|
||||||
|
<field name="is_completed" widget="boolean"/>
|
||||||
|
<field name="is_invoiced" widget="boolean"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
Loading…
Reference in a new issue