[FIX] Various fixes:

- Error with the sequence number.
- Visible texts that should be in uppercases.
- order_id should only be visible if group_stock_request_order
  option is enabled.
- adds more tests
- adds consistency between models company-wise
This commit is contained in:
mreficent
2018-04-11 17:21:15 +02:00
committed by Kitti U
parent 0e8b47cf51
commit d9150b8335
16 changed files with 275 additions and 30 deletions

View File

@@ -4,7 +4,7 @@
{
"name": "Stock Request",
"summary": "Internal request for stock",
"version": "11.0.2.0.0",
"version": "11.0.2.1.0",
"license": "LGPL-3",
"website": "https://github.com/stock-logistics-warehouse",
"author": "Eficent, "

View File

@@ -1,4 +1,3 @@
from . import stock_request
from . import stock_request_allocation
from . import stock_request_order
@@ -7,3 +6,6 @@ from . import stock_picking
from . import procurement_rule
from . import stock_move_line
from . import res_config_settings
from . import stock_warehouse
from . import stock_location
from . import stock_location_route

View File

@@ -11,4 +11,4 @@ class ResConfigSettings(models.TransientModel):
implied_group='stock_request.group_stock_request_order')
module_stock_request_purchase = fields.Boolean(
string='Stock requests for purchase')
string='Stock Requests for Purchases')

View File

@@ -0,0 +1,26 @@
# Copyright 2018 Eficent Business and IT Consulting Services, S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import api, models, _
from odoo.exceptions import ValidationError
class StockLocation(models.Model):
_inherit = 'stock.location'
@api.constrains('company_id')
def _check_company_stock_request(self):
if any(rec.company_id and self.env['stock.request'].search(
[('company_id', '!=', rec.company_id.id),
('location_id', '=', rec.id)], limit=1) for rec in self):
raise ValidationError(
_('You cannot change the company of the location, as it is '
'already assigned to stock requests that belong to '
'another company.'))
if any(rec.company_id and self.env['stock.request.order'].search(
[('company_id', '!=', rec.company_id.id),
('warehouse_id', '=', rec.id)], limit=1) for rec in self):
raise ValidationError(
_('You cannot change the company of the location, as it is '
'already assigned to stock request orders that belong to '
'another company.'))

View File

@@ -0,0 +1,19 @@
# Copyright 2018 Eficent Business and IT Consulting Services, S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import api, models, _
from odoo.exceptions import ValidationError
class StockLocationRoute(models.Model):
_inherit = 'stock.location.route'
@api.constrains('company_id')
def _check_company_stock_request(self):
if any(rec.company_id and self.env['stock.request'].search(
[('company_id', '!=', rec.company_id.id),
('route_id', '=', rec.id)], limit=1) for rec in self):
raise ValidationError(
_('You cannot change the company of the route, as it is '
'already assigned to stock requests that belong to '
'another company.'))

View File

@@ -1,7 +1,8 @@
# Copyright 2017 Eficent Business and IT Consulting Services, S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import api, fields, models
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
class StockMove(models.Model):
@@ -26,3 +27,13 @@ class StockMove(models.Model):
res['allocation_ids'] = [(4, m.id) for m in
self.mapped('allocation_ids')]
return res
@api.constrains('company_id')
def _check_company_stock_request(self):
if any(self.env['stock.request.allocation'].search(
[('company_id', '!=', rec.company_id.id),
('stock_move_id', '=', rec.id)], limit=1)
for rec in self):
raise ValidationError(
_('The company of the stock request must match with '
'that of the location.'))

View File

@@ -43,8 +43,7 @@ class StockRequest(models.Model):
name = fields.Char(
'Name', copy=False, required=True, readonly=True,
states={'draft': [('readonly', False)]},
default=lambda self: self.env['ir.sequence'].next_by_code(
'stock.request'))
default='/')
state = fields.Selection(selection=REQUEST_STATES, string='Status',
copy=False, default='draft', index=True,
readonly=True, track_visibility='onchange',
@@ -102,7 +101,7 @@ class StockRequest(models.Model):
'stock.request'),
)
expected_date = fields.Datetime(
'Expected date', default=fields.Datetime.now, index=True,
'Expected Date', default=fields.Datetime.now, index=True,
required=True, readonly=True,
states={'draft': [('readonly', False)]},
help="Date when you expect to receive the goods.",
@@ -213,6 +212,31 @@ class StockRequest(models.Model):
result |= location
return result
@api.constrains('company_id', 'product_id', 'warehouse_id',
'location_id', 'route_id')
def _check_company_constrains(self):
""" Check if the related models have the same company """
for rec in self:
if rec.product_id.company_id and \
rec.product_id.company_id != rec.company_id:
raise ValidationError(
_('You have entered a product that is assigned '
'to another company.'))
if rec.location_id.company_id and \
rec.location_id.company_id != rec.company_id:
raise ValidationError(
_('You have entered a location that is '
'assigned to another company.'))
if rec.warehouse_id.company_id != rec.company_id:
raise ValidationError(
_('You have entered a warehouse that is '
'assigned to another company.'))
if rec.route_id and rec.route_id.company_id and \
rec.route_id.company_id != rec.company_id:
raise ValidationError(
_('You have entered a route that is '
'assigned to another company.'))
@api.constrains('product_id')
def _check_product_uom(self):
''' Check if the UoM has the same category as the
@@ -439,6 +463,14 @@ class StockRequest(models.Model):
action['res_id'] = pickings.id
return action
@api.model
def create(self, vals):
upd_vals = vals.copy()
if upd_vals.get('name', '/') == '/':
upd_vals['name'] = self.env['ir.sequence'].next_by_code(
'stock.request')
return super().create(upd_vals)
@api.multi
def unlink(self):
if self.filtered(lambda r: r.state != 'draft'):

View File

@@ -12,6 +12,12 @@ class StockRequestAllocation(models.Model):
comodel_name='stock.request',
required=True, ondelete='cascade',
)
company_id = fields.Many2one(string='Company',
comodel_name='res.company',
readonly=True,
related='stock_request_id.company_id',
store=True,
)
stock_move_id = fields.Many2one(string='Stock Move',
comodel_name='stock.move',
required=True, ondelete='cascade',

View File

@@ -2,7 +2,7 @@
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import api, fields, models, _
from odoo.exceptions import UserError
from odoo.exceptions import UserError, ValidationError
REQUEST_STATES = [
('draft', 'Draft'),
@@ -34,8 +34,7 @@ class StockRequestOrder(models.Model):
name = fields.Char(
'Name', copy=False, required=True, readonly=True,
states={'draft': [('readonly', False)]},
default=lambda self: self.env['ir.sequence'].next_by_code(
'stock.request.order'))
default='/')
state = fields.Selection(selection=REQUEST_STATES, string='Status',
copy=False, default='draft', index=True,
readonly=True, track_visibility='onchange',
@@ -69,7 +68,7 @@ class StockRequestOrder(models.Model):
'stock.request.order'),
)
expected_date = fields.Datetime(
'Expected date', default=fields.Datetime.now, index=True,
'Expected Date', default=fields.Datetime.now, index=True,
required=True, readonly=True,
states={'draft': [('readonly', False)]},
help="Date when you expect to receive the goods.",
@@ -242,8 +241,33 @@ class StockRequestOrder(models.Model):
action['res_id'] = self.stock_request_ids.id
return action
@api.model
def create(self, vals):
upd_vals = vals.copy()
if upd_vals.get('name', '/') == '/':
upd_vals['name'] = self.env['ir.sequence'].next_by_code(
'stock.request.order')
return super().create(upd_vals)
@api.multi
def unlink(self):
if self.filtered(lambda r: r.state != 'draft'):
raise UserError(_('Only orders on draft state can be unlinked'))
return super().unlink()
@api.constrains('warehouse_id', 'company_id')
def _check_warehouse_company(self):
if any(request.warehouse_id.company_id !=
request.company_id for request in self):
raise ValidationError(
_('The company of the stock request must match with '
'that of the warehouse.'))
@api.constrains('location_id', 'company_id')
def _check_location_company(self):
if any(request.location_id.company_id and
request.location_id.company_id !=
request.company_id for request in self):
raise ValidationError(
_('The company of the stock request must match with '
'that of the location.'))

View File

@@ -0,0 +1,28 @@
# Copyright 2018 Eficent Business and IT Consulting Services, S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import api, models, _
from odoo.exceptions import ValidationError
class StockWarehouse(models.Model):
_inherit = 'stock.warehouse'
@api.constrains('company_id')
def _check_company_stock_request(self):
if any(self.env['stock.request'].search(
[('company_id', '!=', rec.company_id.id),
('warehouse_id', '=', rec.id)], limit=1)
for rec in self):
raise ValidationError(
_('You cannot change the company of the warehouse, as it is '
'already assigned to stock requests that belong to '
'another company.'))
if any(self.env['stock.request.order'].search(
[('company_id', '!=', rec.company_id.id),
('warehouse_id', '=', rec.id)], limit=1)
for rec in self):
raise ValidationError(
_('You cannot change the company of the warehouse, as it is '
'already assigned to stock request orders that belong to '
'another company.'))

View File

@@ -36,12 +36,19 @@ class TestStockRequest(common.TransactionCase):
[self.stock_request_manager_group.id],
[self.main_company.id, self.company_2.id])
self.product = self._create_product('SH', 'Shoes', False)
self.product_company_2 = self._create_product('SH', 'Shoes',
self.company_2.id)
self.ressuply_loc = self.env['stock.location'].create({
'name': 'Ressuply',
'location_id': self.warehouse.view_location_id.id,
})
self.ressuply_loc_2 = self.env['stock.location'].create({
'name': 'Ressuply',
'location_id': self.wh2.view_location_id.id,
})
self.route = self.env['stock.location.route'].create({
'name': 'Transfer',
'product_categ_selectable': False,
@@ -50,6 +57,14 @@ class TestStockRequest(common.TransactionCase):
'sequence': 10,
})
self.route_2 = self.env['stock.location.route'].create({
'name': 'Transfer',
'product_categ_selectable': False,
'product_selectable': True,
'company_id': self.company_2.id,
'sequence': 10,
})
self.uom_dozen = self.env['product.uom'].create({
'name': 'Test-DozenA',
'category_id': self.categ_unit.id,
@@ -70,6 +85,19 @@ class TestStockRequest(common.TransactionCase):
'propagate': 'False',
})
self.env['procurement.rule'].create({
'name': 'Transfer',
'route_id': self.route_2.id,
'location_src_id': self.ressuply_loc_2.id,
'location_id': self.wh2.lot_stock_id.id,
'action': 'move',
'picking_type_id': self.wh2.int_type_id.id,
'procure_method': 'make_to_stock',
'warehouse_id': self.wh2.id,
'company_id': self.company_2.id,
'propagate': 'False',
})
def _create_user(self, name, group_ids, company_ids):
return self.env['res.users'].with_context(
{'no_reset_password': True}).create(
@@ -252,10 +280,14 @@ class TestStockRequest(common.TransactionCase):
stock_request.location_id, self.warehouse.lot_stock_id)
def test_stock_request_order_validations_01(self):
""" Testing the discrepancy in warehouse_id between
stock request and order"""
expected_date = fields.Date.today()
vals = {
'company_id': self.main_company.id,
'warehouse_id': self.wh2.id,
'location_id': self.warehouse.lot_stock_id.id,
'expected_date': expected_date,
'stock_request_ids': [(0, 0, {
'product_id': self.product.id,
'product_uom_id': self.product.uom_id.id,
@@ -263,18 +295,22 @@ class TestStockRequest(common.TransactionCase):
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
'expected_date': expected_date,
})]
}
# Select a UoM that is incompatible with the product's UoM
with self.assertRaises(exceptions.ValidationError):
self.env['stock.request.order'].sudo(
self.stock_request_user).create(vals)
def test_stock_request_order_validations_02(self):
""" Testing the discrepancy in location_id between
stock request and order"""
expected_date = fields.Date.today()
vals = {
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.wh2.lot_stock_id.id,
'expected_date': expected_date,
'stock_request_ids': [(0, 0, {
'product_id': self.product.id,
'product_uom_id': self.product.uom_id.id,
@@ -282,19 +318,23 @@ class TestStockRequest(common.TransactionCase):
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
'expected_date': expected_date,
})]
}
# Select a UoM that is incompatible with the product's UoM
with self.assertRaises(exceptions.ValidationError):
self.env['stock.request.order'].sudo(
self.stock_request_user).create(vals)
def test_stock_request_order_validations_03(self):
""" Testing the discrepancy in requested_by between
stock request and order"""
expected_date = fields.Date.today()
vals = {
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
'requested_by': self.stock_request_user.id,
'expected_date': expected_date,
'stock_request_ids': [(0, 0, {
'product_id': self.product.id,
'product_uom_id': self.product.uom_id.id,
@@ -303,22 +343,26 @@ class TestStockRequest(common.TransactionCase):
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
'expected_date': expected_date,
})]
}
# Select a UoM that is incompatible with the product's UoM
with self.assertRaises(exceptions.ValidationError):
self.env['stock.request.order'].sudo(
self.stock_request_user).create(vals)
def test_stock_request_order_validations_04(self):
""" Testing the discrepancy in procurement_group_id between
stock request and order"""
procurement_group = self.env['procurement.group'].create({
'name': 'Procurement',
})
expected_date = fields.Date.today()
vals = {
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
'procurement_group_id': procurement_group.id,
'expected_date': expected_date,
'stock_request_ids': [(0, 0, {
'product_id': self.product.id,
'product_uom_id': self.product.uom_id.id,
@@ -326,19 +370,22 @@ class TestStockRequest(common.TransactionCase):
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
'expected_date': expected_date,
})]
}
# Select a UoM that is incompatible with the product's UoM
with self.assertRaises(exceptions.ValidationError):
self.env['stock.request.order'].sudo(
self.stock_request_user).create(vals)
def test_stock_request_order_validations_05(self):
""" Testing the discrepancy in company between
stock request and order"""
expected_date = fields.Date.today()
vals = {
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
'expected_date': fields.Date.today(),
'company_id': self.company_2.id,
'warehouse_id': self.wh2.id,
'location_id': self.wh2.lot_stock_id.id,
'expected_date': expected_date,
'stock_request_ids': [(0, 0, {
'product_id': self.product.id,
'product_uom_id': self.product.uom_id.id,
@@ -346,18 +393,23 @@ class TestStockRequest(common.TransactionCase):
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
'expected_date': expected_date,
})]
}
# Select a UoM that is incompatible with the product's UoM
with self.assertRaises(exceptions.ValidationError):
self.env['stock.request.order'].sudo(
self.stock_request_user).create(vals)
def test_stock_request_order_validations_06(self):
""" Testing the discrepancy in expected dates between
stock request and order"""
expected_date = fields.Date.today()
child_expected_date = '2015-01-01'
vals = {
'company_id': self.company_2.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
'expected_date': expected_date,
'stock_request_ids': [(0, 0, {
'product_id': self.product.id,
'product_uom_id': self.product.uom_id.id,
@@ -365,18 +417,22 @@ class TestStockRequest(common.TransactionCase):
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
'expected_date': child_expected_date,
})]
}
# Select a UoM that is incompatible with the product's UoM
with self.assertRaises(exceptions.ValidationError):
self.env['stock.request.order'].sudo().create(vals)
def test_stock_request_order_validations_07(self):
""" Testing the discrepancy in picking policy between
stock request and order"""
expected_date = fields.Date.today()
vals = {
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
'picking_policy': 'one',
'expected_date': expected_date,
'stock_request_ids': [(0, 0, {
'product_id': self.product.id,
'product_uom_id': self.product.uom_id.id,
@@ -384,9 +440,9 @@ class TestStockRequest(common.TransactionCase):
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
'expected_date': expected_date,
})]
}
# Select a UoM that is incompatible with the product's UoM
with self.assertRaises(exceptions.ValidationError):
self.env['stock.request.order'].sudo(
self.stock_request_user).create(vals)
@@ -423,10 +479,12 @@ class TestStockRequest(common.TransactionCase):
stock_request.action_confirm()
def test_create_request_01(self):
expected_date = fields.Date.today()
vals = {
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
'expected_date': expected_date,
'stock_request_ids': [(0, 0, {
'product_id': self.product.id,
'product_uom_id': self.product.uom_id.id,
@@ -434,6 +492,7 @@ class TestStockRequest(common.TransactionCase):
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
'expected_date': expected_date,
})]
}
@@ -550,10 +609,12 @@ class TestStockRequest(common.TransactionCase):
picking.action_done()
def test_cancel_request(self):
expected_date = fields.Date.today()
vals = {
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
'expected_date': expected_date,
'stock_request_ids': [(0, 0, {
'product_id': self.product.id,
'product_uom_id': self.product.uom_id.id,
@@ -561,6 +622,7 @@ class TestStockRequest(common.TransactionCase):
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
'expected_date': expected_date,
})]
}
@@ -604,10 +666,12 @@ class TestStockRequest(common.TransactionCase):
self.assertEqual(len(stock_request.sudo().move_ids), 2)
def test_view_actions(self):
expected_date = fields.Date.today()
vals = {
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
'expected_date': expected_date,
'stock_request_ids': [(0, 0, {
'product_id': self.product.id,
'product_uom_id': self.product.uom_id.id,
@@ -615,6 +679,7 @@ class TestStockRequest(common.TransactionCase):
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
'expected_date': expected_date,
})]
}
@@ -644,3 +709,29 @@ class TestStockRequest(common.TransactionCase):
action = stock_request.picking_ids[0].action_view_stock_request()
self.assertEqual(action['type'], 'ir.actions.act_window')
self.assertEqual(action['res_id'], stock_request.id)
def test_stock_request_constrains(self):
vals = {
'product_id': self.product.id,
'product_uom_id': self.product.uom_id.id,
'product_uom_qty': 5.0,
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
}
stock_request = self.stock_request.sudo(
self.stock_request_user).create(vals)
# Cannot assign a warehouse that belongs to another company
with self.assertRaises(exceptions.ValidationError):
stock_request.warehouse_id = self.wh2
# Cannot assign a product that belongs to another company
with self.assertRaises(exceptions.ValidationError):
stock_request.product_id = self.product_company_2
# Cannot assign a location that belongs to another company
with self.assertRaises(exceptions.ValidationError):
stock_request.location_id = self.wh2.lot_stock_id
# Cannot assign a route that belongs to another company
with self.assertRaises(exceptions.ValidationError):
stock_request.route_id = self.route_2

View File

@@ -8,7 +8,7 @@
<odoo>
<record id="res_config_settings_view_form" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit.medical</field>
<field name="name">res.config.settings.view.form.inherit.stock_request</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="base.res_config_settings_view_form"/>
<field name="arch" type="xml">
@@ -16,19 +16,23 @@
<div class="app_settings_block"
data-string="Stock Request" data-key="stock_request"
groups="stock_request.group_stock_request_manager">
<h2>Orders</h2>
<div class="row mt16 o_settings_container" id="stock_request">
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="group_stock_request_order"/>
</div>
<div class="o_setting_right_pane">
<label string="Enable orders"
<label string="Enable Orders"
for="group_stock_request_order"/>
<div class="text-muted">
Activates Stock Request Orders
</div>
</div>
</div>
</div>
<h2>Purchases</h2>
<div class="row mt16 o_settings_container" id="stock_request_purchase">
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="module_stock_request_purchase"/>
@@ -56,5 +60,4 @@
<field name="context">{'module' : 'stock_request'}</field>
</record>
</odoo>

View File

@@ -17,6 +17,7 @@
<field name="requested_product_qty"/>
<field name="allocated_product_qty"/>
<field name="open_product_qty" />
<field name="company_id" groups="base.group_multi_company"/>
</tree>
</field>
</record>
@@ -41,6 +42,7 @@
<field name="requested_product_qty"/>
<field name="allocated_product_qty"/>
<field name="open_product_qty" />
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
</group>
</group>
</sheet>

View File

@@ -13,7 +13,7 @@
sequence="20"/>
<menuitem id="stock_request_order_menu"
name="Stock Requests orders"
name="Stock Requests Orders"
parent="menu_stock_request_root"
action="stock_request_order_action"
groups="group_stock_request_order"

View File

@@ -51,7 +51,7 @@
groups="stock.group_stock_user"
>
<field name="stock_request_count" widget="statinfo"
string="Stock requests"/>
string="Stock Requests"/>
</button>
</div>
<div class="oe_title">
@@ -127,7 +127,7 @@
<record model="ir.actions.act_window"
id="stock_request_order_action">
<field name="name">Stock request orders</field>
<field name="name">Stock Request Orders</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">stock.request.order</field>
<field name="view_type">form</field>

View File

@@ -82,7 +82,7 @@
</div>
<group>
<group>
<field name="order_id" readonly="1"/>
<field name="order_id" readonly="1" groups="stock_request.group_stock_request_order"/>
<field name="product_id"/>
<field name="expected_date"/>
<field name="picking_policy"/>
@@ -91,7 +91,8 @@
<field name="warehouse_id" widget="selection" groups="stock.group_stock_multi_locations"/>
<field name="product_uom_id"
options="{'no_open': True, 'no_create': True}" groups="product.group_uom"/>
<field name="location_id" groups="stock.group_stock_multi_locations"/>
<field name="location_id" groups="stock.group_stock_multi_locations"
domain="['|', ('company_id', '=', company_id), ('company_id', '=', False)]"/>
<field name="route_id"
options="{'no_create': True}" groups="stock.group_stock_multi_locations"/>
<field name="route_ids" invisible="1"/>