reactivate module stock_reserve_sale

This commit is contained in:
Yannick Vaucher
2014-09-10 10:12:05 +02:00
committed by Carlos Roca
parent f8ad653988
commit 21b12f83ad
12 changed files with 846 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Guewen Baconnier
# Copyright 2013 Camptocamp SA
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import model
from . import wizard

View File

@@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Guewen Baconnier
# Copyright 2013 Camptocamp SA
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{'name': 'Stock Reserve Sales',
'version': '0.1',
'author': 'Camptocamp',
'category': 'Warehouse',
'license': 'AGPL-3',
'complexity': 'normal',
'images': [],
'website': "http://www.camptocamp.com",
'description': """
Stock Reserve Sales
===================
Allows to create stock reservations for quotation lines before the
confirmation of the quotation. The reservations might have a validity
date and in any case they are lifted when the quotation is canceled or
confirmed.
Reservations can be done only on "make to stock" and stockable products.
The reserved products are substracted from the virtual stock. It means
that if you reserved a quantity of products which bring the virtual
stock below the minimum, the orderpoint will be triggered and new
purchase orders will be generated. It also implies that the max may be
exceeded if the reservations are canceled.
If you want to prevent sales orders to be confirmed when the stock is
insufficient at the order date, you may want to install the
`sale_exception_nostock` module.
""",
'depends': ['sale_stock',
'stock_reserve',
],
'demo': [],
'data': ['wizard/sale_stock_reserve_view.xml',
'view/sale.xml',
'view/stock_reserve.xml',
],
'test': ['test/sale_reserve.yml',
'test/sale_line_reserve.yml',
],
'installable': True,
'auto_install': False,
}

View File

@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Guewen Baconnier
# Copyright 2013 Camptocamp SA
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import sale
from . import stock_reserve

View File

@@ -0,0 +1,234 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Guewen Baconnier
# Copyright 2013 Camptocamp SA
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import models, fields, api
from openerp.exceptions import except_orm
from openerp.tools.translate import _
class SaleOrder(models.Model):
_inherit = 'sale.order'
@api.multi
@api.depends('state',
'order_line.reservation_ids',
'order_line.is_stock_reservable')
def _stock_reservation(self):
for sale in self:
has_stock_reservation = False
is_stock_reservable = False
for line in sale.order_line:
if line.reservation_ids:
has_stock_reservation = True
if line.is_stock_reservable:
is_stock_reservable = True
if sale.state not in ('draft', 'sent'):
is_stock_reservable = False
sale.is_stock_reservable = is_stock_reservable
sale.has_stock_reservation = has_stock_reservation
has_stock_reservation = fields.Boolean(
compute='_stock_reservation',
readonly=True,
multi='stock_reservation',
store=True,
string='Has Stock Reservations')
is_stock_reservable = fields.Boolean(
compute='_stock_reservation',
readonly=True,
multi='stock_reservation',
store=True,
string='Can Have Stock Reservations')
@api.multi
def release_all_stock_reservation(self):
line_ids = [line.id for order in self for line in order.order_line]
lines = self.env['sale.order.line'].browse(line_ids)
lines.release_stock_reservation()
return True
@api.multi
def action_button_confirm(self):
self.release_all_stock_reservation()
return super(SaleOrder, self).action_button_confirm()
@api.multi
def action_cancel(self):
self.release_all_stock_reservation()
return super(SaleOrder, self).action_cancel()
class SaleOrderLine(models.Model):
_inherit = 'sale.order.line'
@api.multi
def _get_line_rule(self):
""" Get applicable rule for this product
Reproduce get suitable rule from procurement
to predict source location """
ProcurementRule = self.env['procurement.rule']
product = self.product_id
product_route_ids = [x.id for x in product.route_ids +
product.categ_id.total_route_ids]
rules = ProcurementRule.search([('route_id', 'in', product_route_ids)],
order='route_sequence, sequence',
limit=1)
if not rules:
warehouse = self.order_id.warehouse_id
wh_routes = warehouse.route_ids
wh_route_ids = [route.id for route in wh_routes]
domain = ['|', ('warehouse_id', '=', warehouse.id),
('warehouse_id', '=', False),
('route_id', 'in', wh_route_ids)]
rules = ProcurementRule.search(domain,
order='route_sequence, sequence')
if rules:
return rules[0]
return False
@api.multi
def _get_procure_method(self):
""" Get procure_method depending on product routes """
rule = self._get_line_rule()
if rule:
return rule.procure_method
return False
@api.multi
@api.depends('state',
'product_id.route_ids',
'product_id.type')
def _is_stock_reservable(self):
for line in self:
reservable = False
if (not (line.state != 'draft'
or line._get_procure_method() == 'make_to_order'
or not line.product_id
or line.product_id.type == 'service')
and not line.reservation_ids):
reservable = True
line.is_stock_reservable = reservable
reservation_ids = fields.One2many(
'stock.reservation',
'sale_line_id',
string='Stock Reservation',
copy=False)
is_stock_reservable = fields.Boolean(
compute='_is_stock_reservable',
readonly=True,
string='Can be reserved')
@api.multi
def release_stock_reservation(self):
reserv_ids = [reserv.id for line in self
for reserv in line.reservation_ids]
reservations = self.env['stock.reservation'].browse(reserv_ids)
reservations.release()
return True
def product_id_change(self, cr, uid, ids,
pricelist,
product,
qty=0,
uom=False,
qty_uos=0,
uos=False,
name='',
partner_id=False,
lang=False,
update_tax=True,
date_order=False,
packaging=False,
fiscal_position=False,
flag=False,
context=None):
result = super(SaleOrderLine, self).product_id_change(
cr, uid, ids, pricelist, product, qty=qty, uom=uom,
qty_uos=qty_uos, uos=uos, name=name, partner_id=partner_id,
lang=lang, update_tax=update_tax, date_order=date_order,
packaging=packaging, fiscal_position=fiscal_position,
flag=flag, context=context)
if not ids: # warn only if we change an existing line
return result
assert len(ids) == 1, "Expected 1 ID, got %r" % ids
line = self.browse(cr, uid, ids[0], context=context)
if qty != line.product_uom_qty and line.reservation_ids:
msg = _("As you changed the quantity of the line, "
"the quantity of the stock reservation will "
"be automatically adjusted to %.2f.") % qty
msg += "\n\n"
result.setdefault('warning', {})
if result['warning'].get('message'):
result['warning']['message'] += msg
else:
result['warning'] = {
'title': _('Configuration Error!'),
'message': msg,
}
return result
@api.multi
def write(self, vals):
block_on_reserve = ('product_id',
'product_uom',
'product_uos',
'type')
update_on_reserve = ('price_unit',
'product_uom_qty',
'product_uos_qty')
keys = set(vals.keys())
test_block = keys.intersection(block_on_reserve)
test_update = keys.intersection(update_on_reserve)
if test_block:
for line in self:
if not line.reservation_ids:
continue
raise except_orm(
_('Error'),
_('You cannot change the product or unit of measure '
'of lines with a stock reservation. '
'Release the reservation '
'before changing the product.'))
res = super(SaleOrderLine, self).write(vals)
if test_update:
for line in self:
if not line.reservation_ids:
continue
if len(line.reservation_ids) > 1:
raise except_orm(
_('Error'),
_('Several stock reservations are linked with the '
'line. Impossible to adjust their quantity. '
'Please release the reservation '
'before changing the quantity.'))
line.reservation_ids.write(
{'price_unit': line.price_unit,
'product_uom_qty': line.product_uom_qty,
'product_uos_qty': line.product_uos_qty,
}
)
return res

View File

@@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Guewen Baconnier
# Copyright 2013 Camptocamp SA
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import models, fields, api
class StockReservation(models.Model):
_inherit = 'stock.reservation'
sale_line_id = fields.Many2one(
'sale.order.line',
string='Sale Order Line',
ondelete='cascade',
copy=False)
sale_id = fields.Many2one(
'sale.order',
string='Sale Order',
related='sale_line_id.order_id')
@api.multi
def release(self):
for rec in self:
rec.sale_line_id = False
return super(StockReservation, self).release()

View File

@@ -0,0 +1,129 @@
-
I create a product to test the stock reservation
-
!record {model: product.product, id: product_yogurt}:
default_code: 001yogurt
name: yogurt
type: product
categ_id: product.product_category_1
list_price: 100.0
standard_price: 70.0
uom_id: product.product_uom_kgm
uom_po_id: product.product_uom_kgm
valuation: real_time
cost_method: average
property_stock_account_input: account.o_expense
property_stock_account_output: account.o_income
-
I update the current stock of the yogurt with 10 kgm
-
!record {model: stock.change.product.qty, id: change_qty}:
new_quantity: 10
product_id: product_yogurt
-
!python {model: stock.change.product.qty}: |
context['active_id'] = ref('product_yogurt')
self.change_product_qty(cr, uid, [ref('change_qty')], context=context)
-
In order to test reservation of the sales order, I create a sales order
-
!record {model: sale.order, id: sale_reserve_02}:
partner_id: base.res_partner_2
payment_term: account.account_payment_term
-
And I create a sales order line
-
!record {model: sale.order.line, id: sale_line_reserve_02_01, view: sale.view_order_line_tree}:
name: Yogurt
product_id: product_yogurt
product_uom_qty: 4
product_uom: product.product_uom_kgm
order_id: sale_reserve_02
-
And I create a stock reserve for this line
-
!record {model: sale.stock.reserve, id: wizard_reserve_02_01}:
note: Reservation for the sales order line
-
I call the wizard to reserve the products of the sales order
-
!python {model: sale.stock.reserve}: |
active_id = ref('sale_line_reserve_02_01')
context['active_id'] = active_id
context['active_ids'] = [active_id]
context['active_model'] = 'sale.order.line'
self.button_reserve(cr, uid, [ref('wizard_reserve_02_01')], context=context)
-
I check Virtual stock of yogurt after update reservation
-
!python {model: product.product}: |
product = self.browse(cr, uid, ref('product_yogurt'), context=context)
assert product.virtual_available == 6, "Stock is not updated."
-
I set product_12 to MTO (doesn't work)
-
!record {model: product.product, id: product.product_product_12}:
route_ids:
- stock.route_warehouse0_mto
-
I set MTO for real
-
!python {model: product.product}: |
product = self.browse(cr, uid, ref('product.product_product_12'), context=context)
self.write(cr, uid, ref('product.product_product_12'),
{'route_ids': [(6, False, [ref('stock.route_warehouse0_mto')])]}, context=context)
-
And I create a MTO sales order line
-
!record {model: sale.order.line, id: sale_line_reserve_02_02, view: sale.view_order_line_tree}:
order_id: sale_reserve_02
name: Mouse, Wireless
product_id: product.product_product_12
product_uom_qty: 4
product_uom: product.product_uom_unit
-
And I try to create a stock reserve for this MTO line
-
!record {model: sale.stock.reserve, id: wizard_reserve_02_02}:
note: Reservation for the sales order line
-
I call the wizard to reserve the products of the sales order
-
!python {model: sale.stock.reserve}: |
active_id = ref('sale_line_reserve_02_02')
context['active_id'] = active_id
context['active_ids'] = [active_id]
context['active_model'] = 'sale.order.line'
self.button_reserve(cr, uid, [ref('wizard_reserve_02_02')], context=context)
-
I should not have a stock reservation for a MTO line
-
!python {model: stock.reservation}: |
reserv_ids = self.search(
cr, uid,
[('sale_line_id', '=', ref('sale_line_reserve_02_02'))],
context=context)
assert not reserv_ids, "No stock reservation should be created for MTO lines"
-
And I change the quantity in the first line
-
!record {model: sale.order.line, id: sale_line_reserve_02_01, view: sale.view_order_line_tree}:
product_uom_qty: 5
-
I check Virtual stock of yogurt after change of reservations
-
!python {model: product.product}: |
product = self.browse(cr, uid, ref('product_yogurt'), context=context)
assert product.virtual_available == 5, "Stock is not updated."
-
I release the sales order's reservations for the first line
-
!python {model: sale.order.line}: |
self.release_stock_reservation(cr, uid, [ref('sale_line_reserve_02_01')], context=context)
-
I check Virtual stock of yogurt after release of reservations
-
!python {model: product.product}: |
product = self.browse(cr, uid, ref('product_yogurt'), context=context)
assert product.virtual_available == 10, "Stock is not updated."

View File

@@ -0,0 +1,64 @@
-
I create a product to test the stock reservation
-
!record {model: product.product, id: product_gelato}:
default_code: 001GELATO
name: Gelato
type: product
categ_id: product.product_category_1
list_price: 100.0
standard_price: 70.0
uom_id: product.product_uom_kgm
uom_po_id: product.product_uom_kgm
valuation: real_time
cost_method: average
property_stock_account_input: account.o_expense
property_stock_account_output: account.o_income
-
I update the current stock of the Gelato with 10 kgm
-
!record {model: stock.change.product.qty, id: change_qty}:
new_quantity: 10
product_id: product_gelato
-
!python {model: stock.change.product.qty}: |
context['active_id'] = ref('product_gelato')
self.change_product_qty(cr, uid, [ref('change_qty')], context=context)
-
In order to test reservation of the sales order, I create a sales order
-
!record {model: sale.order, id: sale_reserve_01}:
partner_id: base.res_partner_2
payment_term: account.account_payment_term
order_line:
- product_id: product_gelato
product_uom_qty: 4
-
I call the wizard to reserve the products of the sales order
-
!record {model: sale.stock.reserve, id: wizard_reserve_01}:
note: Reservation for the sales order
-
!python {model: sale.stock.reserve}: |
active_id = ref('sale_reserve_01')
context['active_id'] = active_id
context['active_ids'] = [active_id]
context['active_model'] = 'sale.order'
self.button_reserve(cr, uid, [ref('wizard_reserve_01')], context=context)
-
I check Virtual stock of Gelato after update reservation
-
!python {model: product.product}: |
product = self.browse(cr, uid, ref('product_gelato'), context=context)
assert product.virtual_available == 6, "Stock is not updated."
-
I release the sales order's reservations
-
!python {model: sale.order}: |
self.release_all_stock_reservation(cr, uid, [ref('sale_reserve_01')], context=context)
-
I check Virtual stock of Gelato after release of reservations
-
!python {model: product.product}: |
product = self.browse(cr, uid, ref('product_gelato'), context=context)
assert product.virtual_available == 10, "Stock is not updated."

View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="0">
<record id="view_order_form_reserve" model="ir.ui.view">
<field name="name">sale.order.form.reserve</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale_stock.view_order_form_inherit"/>
<field name="arch" type="xml">
<button name="action_quotation_send" position="before">
<field name="is_stock_reservable" invisible="1"/>
<button name="%(action_sale_stock_reserve)d"
type="action"
string="Reserve Stock"
help="Pre-book products from stock"
attrs="{'invisible': [('is_stock_reservable', '=', False)]}"
/>
</button>
<field name="order_line" position="attributes">
<attribute name="options">{"reload_on_button": 1}</attribute>
</field>
<xpath expr="//field[@name='order_line']/form//field[@name='state']" position="before">
<field name="reservation_ids" invisible="1"/>
<button name="%(action_sale_stock_reserve)d"
type="action"
string="Reserve Stock"
attrs="{'invisible': ['|', ('reservation_ids', '!=', []),
('state', '!=', 'draft')]}" />
<button name="release_stock_reservation"
type="object"
string="Release Reservation"
attrs="{'invisible': ['|', ('reservation_ids', '=', []),
('state', '!=', 'draft')]}" />
</xpath>
<xpath expr="//field[@name='order_line']/tree/field[@name='price_subtotal']" position="after">
<field name="reservation_ids" invisible="1"/>
<field name="is_stock_reservable" invisible="1"/>
<button name="%(action_sale_stock_reserve)d"
type="action"
string="Reserve Stock"
icon="terp-locked"
attrs="{'invisible': [('is_stock_reservable', '=', False)]}" />
<button name="release_stock_reservation"
type="object"
string="Release Reservation"
icon="gtk-undo"
attrs="{'invisible': [('reservation_ids', '=', [])]}" />
</xpath>
<field name="invoiced" position="before">
<label for="has_stock_reservation"/>
<div>
<field name="has_stock_reservation"/>
<button name="release_all_stock_reservation"
string="cancel all"
type="object" class="oe_link"
attrs="{'invisible': [('has_stock_reservation', '=', False)]}"/>
</div>
</field>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="0">
<record id="view_stock_reservation_form" model="ir.ui.view">
<field name="name">stock.reservation.form</field>
<field name="model">stock.reservation</field>
<field name="inherit_id" ref="stock_reserve.view_stock_reservation_form"/>
<field name="arch" type="xml">
<group name="location" position="after">
<group name="sale" string="Sales">
<field name="sale_id"/>
<field name="sale_line_id"/>
</group>
</group>
</field>
</record>
<record id="view_stock_reservation_tree" model="ir.ui.view">
<field name="name">stock.reservation.tree</field>
<field name="model">stock.reservation</field>
<field name="inherit_id" ref="stock_reserve.view_stock_reservation_tree"/>
<field name="arch" type="xml">
<field name="move_id" position="before">
<field name="sale_id"/>
</field>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Guewen Baconnier
# Copyright 2013 Camptocamp SA
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import sale_stock_reserve

View File

@@ -0,0 +1,103 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Guewen Baconnier
# Copyright 2013 Camptocamp SA
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import models, fields, api
class SaleStockReserve(models.TransientModel):
_name = 'sale.stock.reserve'
@api.model
def _default_location_id(self):
return self.env['stock.reservation']._default_location_id()
@api.model
def _default_location_dest_id(self):
return self.env['stock.reservation']._default_location_dest_id()
location_id = fields.Many2one(
'stock.location',
'Source Location',
required=True,
default=_default_location_id)
location_dest_id = fields.Many2one(
'stock.location',
'Reservation Location',
required=True,
help="Location where the system will reserve the "
"products.",
default=_default_location_dest_id)
date_validity = fields.Date(
"Validity Date",
help="If a date is given, the reservations will be released "
"at the end of the validity.")
note = fields.Text('Notes')
@api.one
def _prepare_stock_reservation(self, line):
product_uos = line.product_uos.id if line.product_uos else False
return {'product_id': line.product_id.id,
'product_uom': line.product_uom.id,
'product_uom_qty': line.product_uom_qty,
'date_validity': self.date_validity,
'name': "%s (%s)" % (line.order_id.name, line.name),
'location_id': self.location_id.id,
'location_dest_id': self.location_dest_id.id,
'note': self.note,
'product_uos_qty': line.product_uos_qty,
'product_uos': product_uos,
'price_unit': line.price_unit,
'sale_line_id': line.id,
}
@api.multi
def stock_reserve(self, line_ids):
assert len(self.ids) == 1, "Expected 1 ID, got %r" % self.ids
lines = self.env['sale.order.line'].browse(line_ids)
for line in lines:
if not line.is_stock_reservable:
continue
vals = self._prepare_stock_reservation(line)[0]
reserv = self.env['stock.reservation'].create(vals)
reserv.reserve()
return True
@api.multi
def button_reserve(self):
env = self.env
assert len(self.ids) == 1, "Expected 1 ID, got %r" % self.ids
close = {'type': 'ir.actions.act_window_close'}
active_model = env.context.get('active_model')
active_ids = env.context.get('active_ids')
if not (active_model and active_ids):
return close
if active_model == 'sale.order':
sales = env['sale.order'].browse(active_ids)
line_ids = [line.id for sale in sales for line in sale.order_line]
if active_model == 'sale.order.line':
line_ids = active_ids
self.stock_reserve(line_ids)
return close

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="0">
<record id="view_sale_stock_reserve_form" model="ir.ui.view">
<field name="name">sale.stock.reserve.form</field>
<field name="model">sale.stock.reserve</field>
<field name="arch" type="xml">
<form string="Reserve Stock" version="7.0">
<p class="oe_grey">
A stock reservation will be created for the products
of the selected quotation lines. If a validity date is specified,
the reservation will be released once the date has passed.
</p>
<group>
<field name="location_id"/>
<field name="location_dest_id"/>
<field name="date_validity"/>
</group>
<group name="note" string="Notes">
<field name="note" nolabel="1"/>
</group>
<footer>
<button string="Reserve"
name="button_reserve"
type="object"
class="oe_highlight" />
or
<button special="cancel" class="oe_link" string="Cancel"/>
</footer>
</form>
</field>
</record>
<record id="action_sale_stock_reserve" model="ir.actions.act_window">
<field name="name">Reserve Stock for Quotation Lines</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sale.stock.reserve</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</data>
</openerp>