[IMP] stock_location: wizard of warehouse creation improved/fixed/upgraded + various fixes/improvements in stock and stock_location

bzr revid: qdp-launchpad@openerp.com-20130911103223-tkkho1zsr4c0licg
This commit is contained in:
Quentin (OpenERP) 2013-09-11 12:32:23 +02:00
parent 5a22eabb9f
commit 8b181d6e3e
6 changed files with 254 additions and 161 deletions

View File

@ -61,12 +61,13 @@ class procurement_rule(osv.osv):
_defaults = {
'procure_method': 'make_to_stock',
'sequence': 20,
}
class procurement_order(osv.osv):
_inherit = "procurement.order"
_columns = {
'location_id': fields.many2one('stock.location', 'Destination Location'),
'location_id': fields.many2one('stock.location', 'Procurement Location'),
'move_ids': fields.one2many('stock.move', 'procurement_id', 'Moves', help="Moves created by the procurement"),
'move_dest_id': fields.many2one('stock.move', 'Destination Move', help="Move which caused (created) the procurement"),
}

View File

@ -1232,13 +1232,9 @@ class stock_move(osv.osv):
'propagate': True,
}
def _create_procurement(self, cr, uid, move, context=None):
"""
This will create a procurement order
"""
proc_obj = self.pool.get("procurement.order")
def _prepare_procurement_from_move(self, cr, uid, move, context=None):
origin = (move.group_id and (move.group_id.name+":") or "") + (move.rule_id and move.rule_id.name or "/")
return proc_obj.create(cr, uid, {
return {
'name': move.rule_id and move.rule_id.name or "/",
'origin': origin,
'company_id': move.company_id and move.company_id.id or False,
@ -1251,7 +1247,14 @@ class stock_move(osv.osv):
'location_id': move.location_id.id,
'move_dest_id': move.id,
'group_id': move.group_id and move.group_id.id or False,
})
}
def _create_procurement(self, cr, uid, move, context=None):
"""
This will create a procurement order
"""
proc_obj = self.pool.get("procurement.order")
return proc_obj.create(cr, uid, self._prepare_procurement_from_move(cr, uid, move, context=context))
# Check that we do not modify a stock.move which is done
def write(self, cr, uid, ids, vals, context=None):

View File

@ -1449,6 +1449,17 @@
</xpath>
</field>
</record>
<record id="view_procurement_tree_stock_inherit" model="ir.ui.view">
<field name="name">procurement.order.tree.stock.inherit</field>
<field name="model">procurement.order</field>
<field name="inherit_id" ref="procurement.procurement_tree_view"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='origin']" position="before">
<field name="location_id"/>
</xpath>
</field>
</record>

View File

@ -41,7 +41,7 @@ class stock_location_route(osv.osv):
class stock_warehouse(osv.osv):
_inherit = 'stock.warehouse'
_columns = {
'route_id': fields.many2one('stock.location.route', 'Default Routes', domain="[('warehouse_selectable', '=', True)]", help='Default route through the warehouse'), #TODO: required = True?
'route_id': fields.many2one('stock.location.route', 'Default Route', domain="[('warehouse_selectable', '=', True)]", help='Default route through the warehouse'),
}
@ -49,11 +49,11 @@ class stock_location_path(osv.osv):
_name = "stock.location.path"
_description = "Pushed Flows"
_columns = {
'name': fields.char('Operation', size=64),
'name': fields.char('Operation', size=64, required=True),
'company_id': fields.many2one('res.company', 'Company'),
'route_id': fields.many2one('stock.location.route', 'Route'),
'location_from_id' : fields.many2one('stock.location', 'Source Location', ondelete='cascade', select=1, required=True),
'location_dest_id' : fields.many2one('stock.location', 'Destination Location', ondelete='cascade', select=1, required=True),
'location_from_id': fields.many2one('stock.location', 'Source Location', ondelete='cascade', select=1, required=True),
'location_dest_id': fields.many2one('stock.location', 'Destination Location', ondelete='cascade', select=1, required=True),
'delay': fields.integer('Delay (days)', help="Number of days to do this transition"),
'invoice_state': fields.selection([
("invoiced", "Invoiced"),
@ -281,16 +281,13 @@ class stock_move(osv.osv):
self._push_apply(cr, uid, moves, context=context)
return result
def _create_procurement(self, cr, uid, move, context=None):
def _prepare_procurement_from_move(self, cr, uid, move, context=None):
"""
Next to creating the procurement order, it will propagate the routes
"""
proc_id = super(stock_move, self)._create_procurement(cr, uid, move, context=context)
proc_obj = self.pool.get("procurement.order")
proc_obj.write(cr, uid, [proc_id], {'route_ids': [(4,x.id) for x in move.route_ids]}, context=context)
return proc_id
vals = super(stock_move, self)._prepare_procurement_from_move(cr, uid, move, context=context)
vals['route_ids'] = [(4, x.id) for x in move.route_ids]
return vals
class stock_location(osv.osv):

View File

@ -20,6 +20,7 @@
##############################################################################
from openerp.osv import fields, osv
from openerp.tools.translate import _
class stock_configure_wh(osv.osv_memory):
_name = "stock.configure.wh"
@ -27,26 +28,36 @@ class stock_configure_wh(osv.osv_memory):
_columns = {
'name': fields.char('Warehouse Name', required=True),
'code': fields.char('Warehouse Unique Identifier', size=5, required=True),
'partner_id': fields.many2one('res.partner', 'Partner Related to the company', help='technical field used for usability reason (domain+default value)'),
'address_id': fields.many2one('res.partner', 'Warehouse Address'),
'internal_stock_loc_id': fields.many2one('stock.location', 'Internal Stock Location', required=True),
'input_stock_loc_id': fields.many2one('stock.location', 'Input Location', required=True),
'output_stock_loc_id': fields.many2one('stock.location', 'Output Location', required=True),
'crossdock': fields.boolean('Crossdock', help='Whether this warehouse uses generally crossdock operations or not'),
'packing': fields.boolean('Packing', help='Whether we make packing in that warehouse while making outgoing shipments or not'),
'packing_loc_id': fields.many2one('stock.location', 'Packing Zone Location')
'reception_steps': fields.selection([
('one_step', 'Receive goods directly in stock (1 step)'),
('two_steps', 'Unload in input location then go to stock (2 steps)'),
('three_steps', 'Unload in input location, go through a quality control before being admitted in stock (3 steps)')], 'Incoming Shipments', required=True),
'delivery_steps': fields.selection([
('ship_only', 'Ship directly from stock (Ship only)'),
('pick_ship', 'Bring goods to output location before shipping (Pick + Ship)'),
('pick_pack_ship', 'Make packages into a dedicated location, then bring them to the output location for shipping (Pick + Pack + Ship)')], 'Outgoing Shippings', required=True),
'packing': fields.boolean('Use Packing Operations', help='Whether we make packing in that warehouse while making internal transfers or not'),
}
def onchange_crossdock(self, cr, uid, ids, crossdock=False, context=None):
if crossdock:
return {'value': {'packing': False, 'packing_loc_id': False}}
return True
_defaults = {
'reception_steps': 'one_step',
'delivery_steps': 'ship_only',
'partner_id': lambda self, cr, uid, context: self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.partner_id.id,
}
def onchange_delivery_steps(self, cr, uid, ids, delivery_steps, context=None):
if delivery_steps == 'pick_pack_ship':
return {'value': {'packing': True}}
return {}
def onchange_packing(self, cr, uid, ids, packing=False, stock_loc=False, context=None):
if packing:
return {'value': {'packing_loc_id': stock_loc}}
return True
def _format_rulename(self, cr, uid, obj, from_loc, dest_loc, context=None):
return obj.name + ': ' + from_loc.name + ' -> ' + dest_loc.name
def _format_routename(self, cr, uid, obj, name, context=None):
return obj.name + ': ' + name
def configure_wh(self, cr, uid, ids, context=None):
""" To Import stock inventory according to products available in the selected locations.
@ -59,145 +70,219 @@ class stock_configure_wh(osv.osv_memory):
"""
if context is None:
context = {}
route_obj = self.pool.get('stock.location.route')
pull_obj = self.pool.get('procurement.rule')
inventory_obj = self.pool.get('stock.inventory')
if ids and len(ids):
ids = ids[0]
obj = self.browse(cr, uid, ids, context=context)
data_obj = self.pool.get('ir.model.data')
seq_obj = self.pool.get('ir.sequence')
picking_type_obj = self.pool.get('stock.picking.type')
location_obj = self.pool.get('stock.location')
route_obj = self.pool.get('stock.location.route')
pull_obj = self.pool.get('procurement.rule')
push_obj = self.pool.get('stock.location.path')
wh_obj = self.pool.get('stock.warehouse')
obj = self.browse(cr, uid, ids, context=context)
wh_stock_loc = obj.internal_stock_loc_id.id
wh_input_stock_loc = obj.input_stock_loc_id.id
wh_output_stock_loc = obj.output_stock_loc_id.id
#create the warehouse locations
try:
parent_location_id = data_obj.get_object_reference(cr, uid, 'stock', 'stock_location_locations')[1]
except:
parent_location_id = False
wh_view_location_id = location_obj.create(cr, uid, {
'name': obj.name,
'usage': 'view',
'location_id': parent_location_id
}, context=context)
wh_stock_loc_id = location_obj.create(cr, uid, {
'name': _('Stock'),
'usage': 'internal',
'location_id': wh_view_location_id
}, context=context)
wh_stock_loc = location_obj.browse(cr, uid, wh_stock_loc_id, context=context)
wh_input_stock_loc = wh_output_stock_loc = wh_pack_stock_loc = wh_qc_stock_loc = wh_stock_loc
if obj.reception_steps != 'one_step':
wh_input_stock_loc_id = location_obj.create(cr, uid, {
'name': _('Input'),
'usage': 'internal',
'location_id': wh_view_location_id
}, context=context)
wh_input_stock_loc = location_obj.browse(cr, uid, wh_input_stock_loc_id, context=context)
if obj.reception_steps == 'three_steps':
wh_qc_stock_loc_id = location_obj.create(cr, uid, {
'name': _('Quality Control'),
'usage': 'internal',
'location_id': wh_view_location_id
}, context=context)
wh_qc_stock_loc = location_obj.browse(cr, uid, wh_qc_stock_loc_id, context=context)
if obj.delivery_steps != 'ship_only':
wh_output_stock_loc_id = location_obj.create(cr, uid, {
'name': _('Output'),
'usage': 'internal',
'location_id': wh_view_location_id
}, context=context)
wh_output_stock_loc = location_obj.browse(cr, uid, wh_output_stock_loc_id, context=context)
if obj.delivery_steps == 'pick_pack_ship':
wh_pack_stock_loc_id = location_obj.create(cr, uid, {
'name': _('Packing Zone'),
'usage': 'internal',
'location_id': wh_view_location_id
}, context=context)
wh_pack_stock_loc = location_obj.browse(cr, uid, wh_pack_stock_loc_id, context=context)
customer_loc = data_obj.get_object_reference(cr, uid, 'stock', 'stock_location_customers')[1]
supplier_loc = data_obj.get_object_reference(cr, uid, 'stock', 'stock_location_suppliers')[1]
#fetch customer and supplier locations, for references
try:
customer_loc = data_obj.get_object_reference(cr, uid, 'stock', 'stock_location_customers')[1]
supplier_loc = data_obj.get_object_reference(cr, uid, 'stock', 'stock_location_suppliers')[1]
except:
customer_loc = location_obj.search(cr, uid, [('usage', '=', 'customer')], context=context)
customer_loc = customer_loc and customer_loc[0] or False
supplier_loc = location_obj.search(cr, uid, [('usage', '=', 'supplier')], context=context)
supplier_loc = supplier_loc and supplier_loc[0] or False
if not (customer_loc and supplier_loc):
raise osv.except_osv(_('Error!'), _('Can\'t find any customer or supplier location. The wizard cannot be used'))
customer_loc = location_obj.browse(cr, uid, customer_loc, context=context)
supplier_loc = location_obj.browse(cr, uid, supplier_loc, context=context)
#create route
new_route_id = False
route_data = {
'name': obj.name+': Ship',
#create default route for warehouse
default_route_id = route_obj.create(cr, uid, vals={
'name': 'to be changed later',
'warehouse_selectable': True,
'product_selectable': False,
}
if obj.crossdock:
route_data['name'] = obj.name+': Crossdock'
elif wh_stock_loc != wh_output_stock_loc:
if obj.packing:
route_data['name'] = obj.name+': Pick + Pack + Ship'
else:
route_data['name'] = obj.name+': Pick + Ship'
new_route_id = route_obj.create(cr, uid, vals=route_data, context=context)
'product_selectable': False,
}, context=context)
#create wh
#create warehouse
wh_data = {
'name': obj.name,
'address_id': obj.address_id and obj.address_id.id or False,
'lot_stock_id': wh_stock_loc,
'route_id': new_route_id,
'partner_id': obj.address_id and obj.address_id.id or False,
'lot_stock_id': wh_stock_loc.id,
'route_id': default_route_id,
#TODO what about 'code' ....
}
wh_obj = self.pool.get('stock.warehouse')
new_wh_id = wh_obj.create(cr, uid, vals=wh_data, context=context)
#create in, out, internal picking types for wh
#create in, out, internal picking types for warehouse
#First create new sequence
seq_obj = self.pool.get('ir.sequence')
in_seq_id = seq_obj.create(cr, uid, values={'name': 'Picking in', 'prefix': 'IN', 'padding': 5}, context=context)
out_seq_id = seq_obj.create(cr, uid, values={'name': 'Picking out', 'prefix': 'OUT', 'padding': 5}, context=context)
internal_seq_id = seq_obj.create(cr, uid, values={'name': 'Picking internal', 'prefix': 'INT', 'padding': 5}, context=context)
#then create picking_type
picking_obj = self.pool.get('stock.picking.type')
in_picking_id = picking_obj.create(cr, uid, vals={'name': 'Receptions', 'warehouse_id': new_wh_id, 'code_id': 'incoming', 'sequence_id': in_seq_id, 'default_location_src_id': supplier_loc, 'default_location_dest_id': wh_stock_loc}, context=context)
out_picking_id = picking_obj.create(cr, uid, vals={'name': 'Delivery Orders', 'warehouse_id': new_wh_id, 'code_id': 'outgoing', 'sequence_id': out_seq_id, 'default_location_src_id': wh_stock_loc, 'default_location_dest_id': customer_loc}, context=context)
internal_picking_id = picking_obj.create(cr, uid, vals={'name': 'Internal Transfers', 'warehouse_id': new_wh_id, 'code_id': 'internal', 'sequence_id': internal_seq_id, 'default_location_src_id': wh_stock_loc, 'default_location_dest_id': wh_stock_loc, 'pack': obj.packing}, context=context)
in_seq_id = seq_obj.create(cr, uid, values={'name': obj.name + _(' Picking in'), 'prefix': obj.code + '\IN\\', 'padding': 5}, context=context)
out_seq_id = seq_obj.create(cr, uid, values={'name': obj.name + _(' Picking out'), 'prefix': obj.code + '\OUT\\', 'padding': 5}, context=context)
internal_seq_id = seq_obj.create(cr, uid, values={'name': obj.name + _(' Picking internal'), 'prefix': obj.code + '\INT\\', 'padding': 5}, context=context)
#then create picking_types
in_picking_type_id = picking_type_obj.create(cr, uid, vals={
'name': _('Receptions'),
'warehouse_id': new_wh_id,
'code_id': 'incoming',
'auto_force_assign': True,
'sequence_id': in_seq_id,
'default_location_src_id': supplier_loc.id,
'default_location_dest_id': wh_input_stock_loc.id}, context=context)
out_picking_type_id = picking_type_obj.create(cr, uid, vals={
'name': _('Delivery Orders'),
'warehouse_id': new_wh_id,
'code_id': 'outgoing',
'sequence_id': out_seq_id,
'delivery': True,
'default_location_src_id': wh_output_stock_loc.id,
'default_location_dest_id': customer_loc.id}, context=context)
internal_picking_type_id = picking_type_obj.create(cr, uid, vals={
'name': _('Internal Transfers'),
'warehouse_id': new_wh_id,
'code_id': 'internal',
'sequence_id': internal_seq_id,
'default_location_src_id': wh_stock_loc.id,
'default_location_dest_id': wh_stock_loc.id,
'pack': obj.delivery_steps == 'pick_pack_ship' or obj.packing}, context=context)
#add pull rules to default route
#ship pull rules
pull_data = {
'name': obj.name+': Stock -> Customer',
'location_src_id': wh_stock_loc,
'location_id': customer_loc,
'propagate': True,
'route_id': new_route_id,
'action': 'move',
'picking_type_id': internal_picking_id,
'procure_method': 'make_to_stock'
#defining the route references for further creation
routes_dict = {
'two_steps': (_('Reception in 2 steps'), [(wh_input_stock_loc, wh_stock_loc, internal_picking_type_id)]),
'three_steps': (_('Reception in 3 steps'), [(wh_input_stock_loc, wh_qc_stock_loc, internal_picking_type_id), (wh_qc_stock_loc, wh_stock_loc, internal_picking_type_id)]),
'crossdock': (_('Cross-Dock'), [(wh_input_stock_loc, wh_output_stock_loc, internal_picking_type_id), (wh_output_stock_loc, customer_loc, out_picking_type_id)]),
'ship_only': (_('Ship Only'), [(wh_stock_loc, customer_loc, out_picking_type_id)]),
'pick_ship': (_('Pick + Ship'), [(wh_stock_loc, wh_output_stock_loc, internal_picking_type_id), (wh_output_stock_loc, customer_loc, out_picking_type_id)]),
'pick_pack_ship': (_('Pick + Pack + Ship'), [(wh_stock_loc, wh_pack_stock_loc, internal_picking_type_id), (wh_pack_stock_loc, wh_output_stock_loc, internal_picking_type_id), (wh_output_stock_loc, customer_loc, out_picking_type_id)]),
}
if obj.crossdock:
cross_data = pull_data.copy()
cross_data.update({
'name': obj.name+': Output -> Customer',
'location_src_id': wh_output_stock_loc,
'picking_type_id': out_picking_id,
'procure_method': 'make_to_order',
})
pull_obj.create(cr, uid, vals=cross_data, context=context)
cross_data.update({
'name': obj.name+': Supplier -> Output',
'location_src_id': supplier_loc,
'location_id': wh_output_stock_loc,
'action': 'buy',
'picking_type_id': in_picking_id,
})
pull_obj.create(cr, uid, vals=cross_data, context=context)
#ship rules
elif wh_stock_loc == wh_output_stock_loc:
pull_obj.create(cr, uid, vals=pull_data, context=context)
#pick-pack-ship rules
elif obj.packing:
#if packing zone is the same as output or stock, only create one pull rule, otherwise, create two
if obj.packing_loc_id.id == wh_stock_loc:
pull_data.update({
'name': obj.name + ' Stock -> Output',
'location_id': wh_output_stock_loc,
})
pull_obj.create(cr, uid, vals=pull_data, context=context)
else:
#create push rules for reception and assign them to 'All products' category
if obj.reception_steps != 'one_step':
try:
all_products_categ = data_obj.get_object_reference(cr, uid, 'product', 'product_category_all')[1]
except:
all_products_categ = self.pool.get('product.category').search(cr, uid, [('parent_id', '=', False)], context=context)
all_products_categ = all_products_categ and all_products_categ[0] or False
if not all_products_categ:
raise osv.except_osv(_('Error!'), _('Can\'t find the product category for the reception in several steps. The wizard cannot be used.'))
pull_data.update({
'name': obj.name+' Stock -> Pack',
'location_id': obj.packing_loc_id.id,
})
pull_obj.create(cr, uid, vals=pull_data, context=context)
route_name, values = routes_dict[obj.reception_steps]
new_route_id = route_obj.create(cr, uid, vals={
'name': self._format_routename(cr, uid, obj, route_name, context=context),
'product_categ_selectable': True,
'product_selectable': False,
}, context=context)
for from_loc, dest_loc, pick_type_id in values:
push_data = {
'name': self._format_rulename(cr, uid, obj, from_loc, dest_loc, context=context),
'location_from_id': from_loc.id,
'location_dest_id': dest_loc.id,
'route_id': new_route_id,
'auto': 'manual',
'picking_type_id': pick_type_id,
}
push_obj.create(cr, uid, vals=push_data, context=context)
pull_data.update({
'name': obj.name+' Pack -> Output',
'location_src_id': obj.packing_loc_id.id,
'location_id': wh_output_stock_loc,
'picking_type_id': internal_picking_id,
self.pool.get('product.category').write(cr, uid, all_products_categ, {'route_ids': [(4, new_route_id)]}, context=context)
#create pull rules for delivery, which include all routes in MTS on the warehouse and a specific route MTO to be set on the product
route_name, values = routes_dict[obj.delivery_steps]
route_obj.write(cr, uid, default_route_id, {'name': self._format_routename(cr, uid, obj, route_name, context=context)}, context=context)
mto_route_id = route_obj.create(cr, uid, vals={
'name': self._format_routename(cr, uid, obj, route_name, context=context) + _(' (MTO)'),
'warehouse_selectable': False,
'product_selectable': True,
})
first_rule = True
for from_loc, dest_loc, pick_type_id in values:
pull_obj.create(cr, uid, {
'name': self._format_rulename(cr, uid, obj, from_loc, dest_loc, context=context),
'location_src_id': from_loc.id,
'location_id': dest_loc.id,
'route_id': default_route_id,
'action': 'move',
'picking_type_id': pick_type_id,
'procure_method': first_rule is True and 'make_to_stock' or 'make_to_order',
}, context=context)
if first_rule:
pull_obj.create(cr, uid, {
'name': self._format_rulename(cr, uid, obj, from_loc, dest_loc, context=context),
'location_src_id': from_loc.id,
'location_id': dest_loc.id,
'route_id': mto_route_id,
'action': 'move',
'picking_type_id': pick_type_id,
'procure_method': 'make_to_order',
})
pull_obj.create(cr, uid, vals=pull_data, context=context)
'sequence': 10,
}, context=context)
first_rule = False
pull_data.update({
'name': obj.name+' Output -> Customer',
'location_src_id': wh_output_stock_loc,
'location_id': customer_loc,
'picking_type_id': out_picking_id,
'procure_method': 'make_to_order',
})
pull_obj.create(cr, uid, vals=pull_data, context=context)
#pick-ship rules
else:
pull_data.update({
'name': obj.name + ' Stock -> Output',
'location_id': wh_output_stock_loc,
})
pull_obj.create(cr, uid, vals=pull_data, context=context)
pull_data.update({
'name': obj.name+' Output -> Customer',
'location_src_id': wh_output_stock_loc,
'location_id': customer_loc,
'picking_type_id': out_picking_id,
'procure_method': 'make_to_order',
})
pull_obj.create(cr, uid, vals=pull_data, context=context)
#create a route for cross dock operations, that can be set on products and product categories
route_name, values = routes_dict['crossdock']
crossdock_route_id = route_obj.create(cr, uid, vals={
'name': self._format_routename(cr, uid, obj, route_name, context=context),
'warehouse_selectable': False,
'product_selectable': True,
'product_categ_selectable': True,
})
first_rule = True
for from_loc, dest_loc, pick_type_id in values:
pull_obj.create(cr, uid, {
'name': self._format_rulename(cr, uid, obj, from_loc, dest_loc, context=context),
'location_src_id': from_loc.id,
'location_id': dest_loc.id,
'route_id': crossdock_route_id,
'action': 'move',
'picking_type_id': pick_type_id,
'procure_method': first_rule is True and 'make_to_stock' or 'make_to_order',
'sequence': 10,
}, context=context)
first_rule = False
return {'type': 'ir.actions.act_window_close'}

View File

@ -11,20 +11,16 @@
<group>
<group>
<field name="name"/>
<field name="address_id"/>
<field name="code"/>
<field name="partner_id" invisible="1"/>
<field name="address_id" domain="[('parent_id', '=', partner_id)]" context="{'default_parent_id': partner_id}"/>
</group>
<group>
<field name="crossdock" on_change="onchange_crossdock(crossdock)"/>
<field name="packing" attrs="{'invisible': [('crossdock', '=', True)]}" on_change="onchange_packing(packing, internal_stock_loc_id)"/>
<field name="reception_steps"/>
<field name="delivery_steps" on_change="onchange_delivery_steps(delivery_steps)"/>
<field name="packing" attrs="{'readonly': [('delivery_steps', '==', 'pick_pack_ship')]}"/>
</group>
</group>
<separator string="Warehouse locations"/>
<group>
<field name="internal_stock_loc_id"/>
<field name="input_stock_loc_id"/>
<field name="output_stock_loc_id"/>
<field name="packing_loc_id" attrs="{'invisible': [('packing', '=', False)], 'required': [('packing', '=', True)]}"/>
</group>
<footer>
<button name="configure_wh" string="Configure Warehouse" type="object" class="oe_highlight"/>
or