bemade_fsm: major refactoring of tests in progress (broken).
This commit is contained in:
parent
2404c7afb2
commit
1e050584a5
10 changed files with 182 additions and 153 deletions
|
|
@ -41,7 +41,7 @@ class Equipment(models.Model):
|
|||
partner_id = fields.Many2one('res.partner',
|
||||
string="Owner",
|
||||
compute="_compute_partner",
|
||||
search="_search_partner",)
|
||||
search="_search_partner", )
|
||||
|
||||
description = fields.Text(string="Description",
|
||||
tracking=True)
|
||||
|
|
@ -53,9 +53,11 @@ class Equipment(models.Model):
|
|||
|
||||
location_notes = fields.Text(string="Physical Location Notes",
|
||||
tracking=True)
|
||||
task_ids = fields.One2many(comodel_name='project.task',
|
||||
inverse_name='equipment_id',
|
||||
string='Interventions')
|
||||
task_ids = fields.Many2many(comodel_name='project.task',
|
||||
relation="bemade_fsm_task_equipment_rel",
|
||||
column1="equipment_id",
|
||||
column2="task_id",
|
||||
string='Interventions')
|
||||
|
||||
@api.depends('partner_location_id')
|
||||
def _compute_partner(self):
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ class SaleOrderLine(models.Model):
|
|||
vals['user_ids'] = template.assignees.ids
|
||||
vals['tag_ids'] = template.tags.ids
|
||||
vals['planned_hours'] = template.planned_hours
|
||||
vals['equipment_id'] = template.equipment_id.id
|
||||
vals['equipment_ids'] = [Command.set([template.equipment_id.id])]
|
||||
return vals
|
||||
|
||||
tmpl = self.product_id.task_template_id
|
||||
|
|
@ -128,8 +128,8 @@ class SaleOrderLine(models.Model):
|
|||
"This task has been created from: <a href=# data-oe-model=sale.order data-oe-id=%d>%s</a> (%s)") % (
|
||||
self.order_id.id, self.order_id.name, self.product_id.name)
|
||||
task.message_post(body=task_msg)
|
||||
if not task.equipment_id:
|
||||
task.equipment_id = self.order_id.equipment_id
|
||||
if not task.equipment_ids:
|
||||
task.write({'equipment_ids': [Command.set(self.order_id.equipment_id)]})
|
||||
task.name = _generate_task_name(tmpl)
|
||||
return task
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,12 @@ from odoo.exceptions import ValidationError
|
|||
class Task(models.Model):
|
||||
_inherit = "project.task"
|
||||
|
||||
equipment_id = fields.Many2one("bemade_fsm.equipment", string="Equipment to Service", tracking=True)
|
||||
equipment_ids = fields.Many2many(comodel_name="bemade_fsm.equipment",
|
||||
relation="bemade_fsm_task_equipment_rel",
|
||||
column1="task_id",
|
||||
column2="equipment_id",
|
||||
string="Equipment to Service",
|
||||
tracking=True,)
|
||||
|
||||
work_order_contacts = fields.Many2many(comodel_name="res.partner",
|
||||
relation="task_work_order_contact_rel",
|
||||
|
|
|
|||
|
|
@ -3,11 +3,17 @@ from odoo import Command
|
|||
|
||||
|
||||
@tagged("-at_install", "post_install")
|
||||
class FSMManagerUserTransactionCase(TransactionCase):
|
||||
class BemadeFSMBaseTest(TransactionCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.user = cls._generate_project_manager_user('Project Manager', 'misterpm')
|
||||
cls.user_limited = cls._generate_project_user('Project User', 'mruser')
|
||||
cls.env.ref("industry_fsm.fsm_project").write({'allow_subtasks': True})
|
||||
|
||||
@classmethod
|
||||
def _generate_project_manager_user(cls, name, login):
|
||||
user_group_employee = cls.env.ref('base.group_user')
|
||||
user_group_project_user = cls.env.ref('project.group_project_user')
|
||||
user_group_project_manager = cls.env.ref('project.group_project_manager')
|
||||
|
|
@ -26,24 +32,117 @@ class FSMManagerUserTransactionCase(TransactionCase):
|
|||
user_group_sales_manager.id, ]
|
||||
if user_product_customer:
|
||||
group_ids.append(user_product_customer.id)
|
||||
|
||||
# Test user with project access rights for the various tests
|
||||
Users = cls.env['res.users'].with_context({'no_reset_password': True})
|
||||
cls.user = Users.create({
|
||||
'name': 'Project Manager',
|
||||
'login': 'misterpm',
|
||||
'password': 'misterpm',
|
||||
'email': 'mrpm@testco.com',
|
||||
'signature': 'Mr. PM',
|
||||
'groups_id': [Command.set(group_ids)],
|
||||
})
|
||||
group_ids.remove(user_group_fsm_manager.id)
|
||||
group_ids.remove(user_group_project_manager.id)
|
||||
cls.user_limited = Users.create({
|
||||
'name': 'Project User',
|
||||
'login': 'mruser',
|
||||
'password': 'mruser',
|
||||
'email': 'mruser@testco.com',
|
||||
'signature': 'Mr. User',
|
||||
return cls.env['res.users'].with_context({'no_reset_password': True}).create({
|
||||
'name': name,
|
||||
'login': login,
|
||||
'password': login,
|
||||
'email': f"{login}@test.co",
|
||||
'groups_id': [Command.set(group_ids)]
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def _generate_project_user(cls, name, login):
|
||||
user = cls._generate_project_manager_user(name, login)
|
||||
user.write({'groups_id': [Command.unlink(cls.env.ref('project.group_project_manager'))]})
|
||||
user.write({'groups_id': [Command.unlink(cls.env.ref('industry_fsm.group_fsm_manager'))]})
|
||||
|
||||
@classmethod
|
||||
def _generate_partner(cls, name: str = 'Test Company', company_type: str = 'company', parent=None):
|
||||
""" Generates a partner with basic address filled in.
|
||||
|
||||
:param name: The partner's name.
|
||||
:param company_type: The type of partner, either 'company' or 'person' are accepted."""
|
||||
return cls.env['res.partner'].create({
|
||||
'name': name,
|
||||
'company_type': company_type,
|
||||
'street': '123 Street St.',
|
||||
'city': 'Montreal',
|
||||
'state_id': cls.env.ref('base.state_ca_qc').id,
|
||||
'country_id': cls.env.ref('base.ca').id,
|
||||
'parent_id': parent and parent.id,
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def _generate_sale_order(cls, partner, client_order_ref='Test Order', equipment=None):
|
||||
return cls.env['sale.order'].create({
|
||||
'partner_id': partner.id,
|
||||
'client_order_ref': client_order_ref,
|
||||
'equipment_id': equipment.id,
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def _generate_sale_order_line(cls, sale_order, product, project, qty=1.0, uom=None, price=100.0, tax_id=False):
|
||||
return cls.env['sale.order.line'].create({
|
||||
'order_id': sale_order.id,
|
||||
'product_id': product.id,
|
||||
'product_uom_qty': qty,
|
||||
'product_uom': uom or cls.env.ref("uom.product_uom_hour"),
|
||||
'price_unit': price,
|
||||
'tax_id': tax_id,
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def _generate_equipment(cls, name='test equipment', partner=None):
|
||||
return cls.env['bemade_fsm.equipment'].create({
|
||||
'name': name,
|
||||
'partner_location_id': partner and partner.id,
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def _generate_product(cls, name='Test Product', product_type='service', service_tracking='task_global_project',
|
||||
project=None, task_template=None, service_policy='delivered_manual', uom=None):
|
||||
if 'project' in service_tracking and not project:
|
||||
project = cls.env.ref("industry_fsm.fsm_project")
|
||||
uom_id = uom and uom.id or cls.env.ref("uom.product_uom_hour").id
|
||||
return cls.env['product.product'].create({
|
||||
'name': name,
|
||||
'type': product_type,
|
||||
'service_tracking': service_tracking,
|
||||
'project_id': service_tracking in ('task_global_project', 'project_only') and project.id,
|
||||
'project_template_id': service_tracking == 'task_in_project' and project.id,
|
||||
'task_template_id': task_template and task_template.id,
|
||||
'service_policy': service_policy,
|
||||
'uom_id': uom_id,
|
||||
'uom_po_id': uom_id,
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def _generate_fsm_project(cls, name='Test Project'):
|
||||
return cls.env['project.project'].create({
|
||||
'name': name,
|
||||
'allow_material': True,
|
||||
'allow_timesheets': True,
|
||||
'allow_subtasks': True,
|
||||
'allow_quotations': True,
|
||||
'allow_worksheets': True,
|
||||
'is_fsm': True,
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def _generate_task_template(cls, parent=None, structure=[], names=['Test Task Template']):
|
||||
""" Generates a task template with the specified structure and naming.
|
||||
|
||||
:param structure: A list of integers describing the number of tasks for each level of descendants. An empty
|
||||
list represents only one top-level task template.
|
||||
:param names: The name prefixes to be given to the task templates at each level. Each prefix will be followed
|
||||
by a sequential integer for its level. Child 1, Child 2, Grandchild 1, etc."""
|
||||
if len(structure) != len(names) - 1:
|
||||
raise ValueError("The length of the structure argument must contain exactly one element less than the "
|
||||
"names argument.")
|
||||
name = names.pop(0)
|
||||
template = cls.env['project.task.template'].create({
|
||||
'name': name,
|
||||
'parent': parent and parent.id,
|
||||
})
|
||||
parent = template
|
||||
while structure:
|
||||
subtasks = []
|
||||
for i in range(0, structure[0]):
|
||||
subtasks.append(cls.env['project.task.template'].create({
|
||||
'parent': parent.id,
|
||||
'name': names[0] + f" {i}",
|
||||
}))
|
||||
structure.pop(0)
|
||||
names.pop(0)
|
||||
parent = subtasks[0]
|
||||
return template
|
||||
|
|
|
|||
|
|
@ -1,53 +1,34 @@
|
|||
from odoo.tests.common import HttpCase, tagged
|
||||
from .test_bemade_fsm_common import FSMManagerUserTransactionCase
|
||||
from .test_bemade_fsm_common import BemadeFSMBaseTest
|
||||
from odoo import Command
|
||||
from odoo.exceptions import MissingError
|
||||
|
||||
|
||||
@tagged("-at_install", "post_install")
|
||||
class TestEquipmentCommon(FSMManagerUserTransactionCase):
|
||||
class TestEquipment(BemadeFSMBaseTest):
|
||||
def test_crud(self):
|
||||
self.partner_company = self._generate_partner()
|
||||
self.partner_contact = self._generate_partner('Site Contact', 'person', self.partner_company)
|
||||
self.equipment = self._generate_equipment('Test Equipment 1', self.partner_company)
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
# Set up the test partner
|
||||
cls.partner_company = cls.env['res.partner'].create({
|
||||
'name': 'Test Partner Company',
|
||||
'company_type': 'company',
|
||||
'street': '123 Street St.',
|
||||
'city': 'Montreal',
|
||||
'state_id': cls.env['res.country.state'].search([('name', 'ilike', 'Quebec%')]).id,
|
||||
'country_id': cls.env['res.country'].search([('name', '=', 'Canada')]).id
|
||||
})
|
||||
|
||||
cls.partner_contact = cls.env['res.partner'].create({
|
||||
'name': 'Site Contact',
|
||||
'company_type': 'person',
|
||||
'parent_id': cls.partner_company.id,
|
||||
})
|
||||
|
||||
cls.equipment = cls.env['bemade_fsm.equipment'].create({
|
||||
'name': 'Test Equipment 1',
|
||||
'partner_location_id': cls.partner_company.id,
|
||||
})
|
||||
|
||||
|
||||
@tagged('-at_install', 'post_install')
|
||||
class TestEquipmentBase(TestEquipmentCommon):
|
||||
|
||||
def test_crd(self):
|
||||
# Just make sure the basic ORM stuff is OK
|
||||
self.assertTrue(self.equipment in self.partner_company.equipment_ids)
|
||||
self.assertTrue(len(self.partner_company.equipment_ids) == 1)
|
||||
self.partner_company.write({'equipment_ids': [Command.set([])]})
|
||||
|
||||
# Delete should cascade
|
||||
self.partner_company.write({'equipment_ids': [Command.set([])]})
|
||||
with self.assertRaises(MissingError):
|
||||
self.equipment.name
|
||||
|
||||
|
||||
@tagged('-at_install', 'post_install', 'slow')
|
||||
class TestEquipmentTours(HttpCase, TestEquipmentCommon):
|
||||
class TestEquipmentTours(HttpCase, BemadeFSMBaseTest):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls._generate_partner()
|
||||
cls._generate_partner('Site Contact', 'person', cls.partner_company)
|
||||
cls._generate_equipment('Test Equipment 1', cls.partner_company)
|
||||
|
||||
def test_equipment_base_tour(self):
|
||||
self.start_tour('/web', 'equipment_base_tour',
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from odoo.tests import TransactionCase, HttpCase, tagged, Form
|
||||
from odoo import Command
|
||||
from .test_bemade_fsm_common import FSMManagerUserTransactionCase
|
||||
from .test_bemade_fsm_common import BemadeFSMBaseTest
|
||||
|
||||
|
||||
def create_base_contacts(cls):
|
||||
|
|
@ -22,7 +22,7 @@ def create_base_contacts(cls):
|
|||
|
||||
|
||||
@tagged("-at_install", "post_install")
|
||||
class SaleOrderFSMContactsCase(FSMManagerUserTransactionCase):
|
||||
class SaleOrderFSMContactsCase(BemadeFSMBaseTest):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
|
|
@ -60,7 +60,7 @@ class SaleOrderFSMContactsCase(FSMManagerUserTransactionCase):
|
|||
so.work_order_contacts != so.partner_id.work_order_contacts and len(so.partner_id.work_order_contacts) == 2)
|
||||
|
||||
|
||||
class SaleOrderMultiLocationContactsTest(FSMManagerUserTransactionCase):
|
||||
class SaleOrderMultiLocationContactsTest(BemadeFSMBaseTest):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
|
|
|||
|
|
@ -10,20 +10,12 @@ class TestSalesOrder(TestTaskTemplateCommon):
|
|||
cls.partner = cls.env['res.partner'].create({
|
||||
'name': 'Test Partner',
|
||||
})
|
||||
cls.equipment =cls.env['bemade_fsm.equipment'].create({
|
||||
'name': 'test equipment',
|
||||
'partner_location_id': cls.partner.id,
|
||||
})
|
||||
cls.so_equipment = cls.env['bemade_fsm.equipment'].create({
|
||||
'name': 'test equipment 2',
|
||||
'partner_location_id': cls.partner.id,
|
||||
})
|
||||
cls.sale_order1 = cls.env['sale.order'].create({
|
||||
'partner_id': cls.partner.id,
|
||||
'client_order_ref': 'TEST ORDER 1',
|
||||
'state': 'draft',
|
||||
'equipment_id': cls.so_equipment.id,
|
||||
})
|
||||
cls.equipment = TestSalesOrder._generate_equipment()
|
||||
cls.so_equipment = TestSalesOrder._generate_equipment('test equipment 2')
|
||||
|
||||
cls.sale_order1 = TestSalesOrder._generate_equipment(partner=cls.partner, client_order_ref='TEST ORDER 1',
|
||||
equipment=cls.so_equipment)
|
||||
cls.sol_serv_order = TestSalesOrder._generate_sale_order_line(product, project, )
|
||||
cls.sol_serv_order = cls.env['sale.order.line'].create({
|
||||
'name': cls.product_task_global_project.name,
|
||||
'product_id': cls.product_task_global_project.id,
|
||||
|
|
@ -66,6 +58,7 @@ class TestSalesOrder(TestTaskTemplateCommon):
|
|||
'tax_id': False,
|
||||
})
|
||||
|
||||
|
||||
@tagged('-at_install', 'post_install')
|
||||
def test_order_confirmation_simple_template(self):
|
||||
""" Confirming the order should create a task in the global project. """
|
||||
|
|
@ -144,4 +137,4 @@ class TestSaleOrderTour(HttpCase, TestSalesOrder):
|
|||
so.action_confirm()
|
||||
sol = so.order_line.filtered(lambda l: 'Test Product 3' in l.name)
|
||||
self.start_tour('/web', 'sale_order_tour', login='misterpm')
|
||||
self.assertTrue(sol.qty_delivered != 0)
|
||||
self.assertTrue(sol.qty_delivered != 0)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from .test_bemade_fsm_common import FSMManagerUserTransactionCase
|
||||
from .test_bemade_fsm_common import BemadeFSMBaseTest
|
||||
from odoo.tests.common import HttpCase, tagged
|
||||
from odoo.exceptions import MissingError
|
||||
from odoo import Command
|
||||
|
|
@ -6,7 +6,7 @@ from psycopg2.errors import ForeignKeyViolation
|
|||
|
||||
|
||||
@tagged("-at_install", "post_install")
|
||||
class TestTaskTemplateCommon(FSMManagerUserTransactionCase):
|
||||
class TestTaskTemplateCommon(BemadeFSMBaseTest):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
|
|
@ -19,79 +19,26 @@ class TestTaskTemplateCommon(FSMManagerUserTransactionCase):
|
|||
})
|
||||
|
||||
cls.project = cls.env.ref('industry_fsm.fsm_project')
|
||||
cls.project.write({'allow_subtasks': True,})
|
||||
|
||||
cls.product_task_global_project = cls.env['product.product'].create({
|
||||
'name': 'Test Product 1',
|
||||
'type': 'service',
|
||||
'service_tracking': 'task_global_project',
|
||||
'project_id': cls.project.id,
|
||||
'task_template_id': cls.task1.id,
|
||||
'uom_id': hours_uom.id,
|
||||
'uom_po_id': hours_uom.id,
|
||||
'service_policy': 'delivered_manual',
|
||||
})
|
||||
cls.project_template = cls.env['project.project'].create({
|
||||
'name': 'Test Project Template',
|
||||
'allow_material': True,
|
||||
'allow_timesheets': True,
|
||||
'allow_subtasks': True,
|
||||
'allow_quotations': True,
|
||||
'allow_worksheets': True,
|
||||
'is_fsm': True,
|
||||
})
|
||||
cls.product_task_in_project = cls.env['product.product'].create({
|
||||
'name': 'Test Product 2',
|
||||
'type': 'service',
|
||||
'service_tracking': 'task_in_project',
|
||||
'task_template_id': cls.task1.id,
|
||||
'project_template_id': cls.project_template.id,
|
||||
'uom_po_id': hours_uom.id,
|
||||
'uom_id': hours_uom.id,
|
||||
'service_policy': 'delivered_manual',
|
||||
})
|
||||
cls.product_task_global_project = cls._generate_product(name='Test Product 1', task_template=cls.task1)
|
||||
cls.project_template = cls._generate_fsm_project('Test Project Template')
|
||||
cls.product_task_in_project = cls._generate_product(name='Test Product 2', project=cls.project_template,
|
||||
service_tracking='task_in_project', task_template=cls.task1)
|
||||
|
||||
# Set up a task template tree with 2 children and 1 grandchild
|
||||
cls.parent_task = cls.env['project.task.template'].create({
|
||||
'name': 'Parent Template',
|
||||
'planned_hours': cls.PLANNED_HOURS,
|
||||
})
|
||||
cls.child_task_1 = cls.env['project.task.template'].create({
|
||||
'name': 'Child Template 1',
|
||||
'parent': cls.parent_task.id,
|
||||
})
|
||||
cls.child_task_2 = cls.env['project.task.template'].create({
|
||||
'name': 'Child Template 2',
|
||||
'parent': cls.parent_task.id,
|
||||
})
|
||||
cls.parent_task.write({'subtasks': [Command.set([cls.child_task_1.id, cls.child_task_2.id])]})
|
||||
cls.grandchild_task = cls.env['project.task.template'].create({
|
||||
'name': 'Grandchild Template',
|
||||
'parent': cls.child_task_2.id
|
||||
})
|
||||
cls.child_task_2.write({'subtasks': [Command.set([cls.grandchild_task.id])]})
|
||||
cls.parent_task = cls._generate_task_template(structure=[2, 1],
|
||||
names=['Parent Template', 'Child Template',
|
||||
'Grandchild Template'])
|
||||
cls.child_task_1 = cls.parent_task.subtasks[0]
|
||||
cls.child_task_2 = cls.parent_task.subtasks[1]
|
||||
cls.grandchild_task = cls.child_task_1.subtasks[0]
|
||||
|
||||
# Create products using the task tree we just created
|
||||
cls.product_task_tree_global_project = cls.env['product.product'].create({
|
||||
'name': 'Test Product 3',
|
||||
'type': 'service',
|
||||
'service_tracking': 'task_global_project',
|
||||
'project_id': cls.project.id,
|
||||
'task_template_id': cls.parent_task.id,
|
||||
'service_policy': 'delivered_manual',
|
||||
'uom_id': hours_uom.id,
|
||||
'uom_po_id': hours_uom.id,
|
||||
})
|
||||
cls.product_task_tree_in_project = cls.env['product.product'].create({
|
||||
'name': 'Test Product 2',
|
||||
'type': 'service',
|
||||
'service_tracking': 'task_in_project',
|
||||
'task_template_id': cls.parent_task.id,
|
||||
'project_template_id': cls.project_template.id,
|
||||
'uom_po_id': hours_uom.id,
|
||||
'uom_id': hours_uom.id,
|
||||
'service_policy': 'delivered_manual',
|
||||
})
|
||||
cls.product_task_tree_global_project = cls._generate_product(name='Test Product 3',
|
||||
task_template=cls.parent_task)
|
||||
cls.product_task_tree_in_project = cls._generate_product(name="Test Product 2", project=cls.project_template,
|
||||
service_tracking='task_in_project',
|
||||
task_template=cls.parent_task)
|
||||
|
||||
|
||||
@tagged('-at_install', 'post_install')
|
||||
|
|
@ -105,7 +52,7 @@ class TestTaskTemplate(TestTaskTemplateCommon):
|
|||
def test_delete_subtask_template(self):
|
||||
""" Deletion of a child task should be OK even if the parent is on a product. Children of the deleted
|
||||
subtask should be deleted."""
|
||||
self.child_task_2.unlink()
|
||||
self.child_task_1.unlink()
|
||||
# Reading deleted child's name field should be impossible
|
||||
with self.assertRaises(MissingError):
|
||||
test = self.grandchild_task.name
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@
|
|||
<field name="name">bemade_fsm.equipment.tree</field>
|
||||
<field name="model">bemade_fsm.equipment</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Equipment">
|
||||
<tree string="Equipment" editable="bottom">
|
||||
<field name="pid_tag"/>
|
||||
<field name="name"/>
|
||||
<field name="description"/>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@
|
|||
<field name="priority" eval="8"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='partner_id']" position="after">
|
||||
<field name="equipment_id" domain="[('partner_location_id', '=', partner_id)]"/>
|
||||
<field name="equipment_ids"
|
||||
domain="[('partner_location_id', '=', partner_id)]"
|
||||
context="{'tree_view_ref': 'bemade_fsm.equipment_view_tree'}"/>
|
||||
</xpath>
|
||||
<xpath expr="//page[@name='extra_info']" position="after">
|
||||
<page string="Field Service" name="field_service">
|
||||
|
|
|
|||
Loading…
Reference in a new issue