bemade_fsm: added a test for contact setting on SO shipping address change. Fixes #53.

This commit is contained in:
Marc Durepos 2023-07-11 14:28:19 -04:00
parent 6f37902f42
commit eea5b54a4f
8 changed files with 221 additions and 20 deletions

View file

@ -4,3 +4,4 @@ from . import sale_order
from . import equipment
from . import task
from . import res_partner
from . import fsm_visit

View 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

View file

@ -1,4 +1,5 @@
from odoo import fields, models, api, _, Command
from odoo.exceptions import ValidationError
class SaleOrder(models.Model):
@ -24,6 +25,9 @@ class SaleOrder(models.Model):
inverse='_inverse_default_contacts',
string='Work Order Recipients',
store=True)
visit_ids = fields.One2many(comodel_name='bemade_fsm.visit',
inverse_name="sale_order_id",
readonly=False)
@api.onchange('partner_shipping_id')
def _onchange_partner_shipping_id(self):
@ -53,6 +57,12 @@ class SaleOrder(models.Model):
class SaleOrderLine(models.Model):
_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):
""" Generate task for the given so line, and link it.
: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, })
return task
def _generate_task_name(template = None):
def _generate_task_name(template=None):
template_name = template and 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.name = _generate_task_name(tmpl)
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

View file

@ -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_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_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

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
3 access_bemade_fsm_task_template bemade_fsm_task_template model_project_task_template project.group_project_user 1 1 1 1
4 access_bemade_fsm_equipment bemade_fsm_equipment model_bemade_fsm_equipment base.group_user 1 1 1 1
5 access_bemade_fsm_equipment_tag bemade_fsm_equipment_tag model_bemade_fsm_equipment_tag base.group_user 1 1 1 1
6 access_bemade_fsm_equipment_type access_bemade_fsm_equipment_type model_bemade_fsm_equipment_type base.group_user 1 0 0 0
7 access_bemade_fsm_visit access_bemade_fsm_visit model_bemade_fsm_visit base.group_user 1 1 1 1

View file

@ -4,3 +4,4 @@ from . import test_sale_order
from . import test_equipment
from . import test_fsm_contact_setting
from . import test_so_task_contacts
from . import test_fsm_visit

View file

@ -1,29 +1,33 @@
from odoo.tests import TransactionCase, HttpCase, tagged
from odoo.tests import TransactionCase, HttpCase, tagged, Form
from odoo import Command
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")
class SaleOrderFSMContactsCase(FSMManagerUserTransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
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,
})
create_base_contacts(cls)
def test_site_contacts(self):
# 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)
class SaleOrderMultiLocationContactsTest(SaleOrderFSMContactsCase):
class SaleOrderMultiLocationContactsTest(FSMManagerUserTransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
create_base_contacts(cls)
cls.shipping_location = cls.env['res.partner'].create({
'name': 'Shipping location',
'company_type': 'company',
@ -90,3 +95,18 @@ class SaleOrderMultiLocationContactsTest(SaleOrderFSMContactsCase):
self.assertEqual(so.partner_shipping_id, self.shipping_location)
self.assertEqual(so.site_contacts, self.shipping_location.site_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)

View 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

View file

@ -8,7 +8,11 @@
<field name="arch" type="xml">
<xpath expr="//page[@name='other_information']" position="before">
<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"
domain="[('partner_location_id', '=', partner_shipping_id)]"
context="{'default_partner_location_id': partner_shipping_id,}"/>
@ -21,5 +25,17 @@
</xpath>
</field>
</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>
</odoo>