diff --git a/__unported__/stock_reserve/model/product.py b/__unported__/stock_reserve/model/product.py
deleted file mode 100644
index fd3aca601..000000000
--- a/__unported__/stock_reserve/model/product.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# -*- 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 .
-#
-##############################################################################
-
-from openerp.osv import orm
-
-
-class product_product(orm.Model):
- _inherit = 'product.product'
-
- def open_stock_reservation(self, cr, uid, ids, context=None):
- assert len(ids) == 1, "Expected 1 ID, got %r" % ids
- mod_obj = self.pool.get('ir.model.data')
- act_obj = self.pool.get('ir.actions.act_window')
- get_ref = mod_obj.get_object_reference
- __, action_id = get_ref(cr, uid, 'stock_reserve',
- 'action_stock_reservation')
- action = act_obj.read(cr, uid, action_id, context=context)
- action['context'] = {'search_default_draft': 1,
- 'search_default_reserved': 1,
- 'default_product_id': ids[0],
- 'search_default_product_id': ids[0]}
- return action
diff --git a/__unported__/stock_reserve/model/stock_reserve.py b/__unported__/stock_reserve/model/stock_reserve.py
deleted file mode 100644
index 3dbf09094..000000000
--- a/__unported__/stock_reserve/model/stock_reserve.py
+++ /dev/null
@@ -1,181 +0,0 @@
-# -*- 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 .
-#
-##############################################################################
-
-from openerp.osv import orm, fields
-from openerp.tools.translate import _
-
-
-class stock_reservation(orm.Model):
- """ Allow to reserve products.
-
- The fields mandatory for the creation of a reservation are:
-
- * product_id
- * product_qty
- * product_uom
- * name
-
- The following fields are required but have default values that you may
- want to override:
-
- * company_id
- * location_id
- * dest_location_id
-
- Optionally, you may be interested to define:
-
- * date_validity (once passed, the reservation will be released)
- * note
- """
- _name = 'stock.reservation'
- _description = 'Stock Reservation'
- _inherits = {'stock.move': 'move_id'}
-
- _columns = {
- 'move_id': fields.many2one('stock.move',
- 'Reservation Move',
- required=True,
- readonly=True,
- ondelete='cascade',
- select=1),
- 'date_validity': fields.date('Validity Date'),
- }
-
- def get_location_from_ref(self, cr, uid, ref, context=None):
- """ Get a location from a xmlid if allowed
- :param ref: tuple (module, xmlid)
- """
- location_obj = self.pool.get('stock.location')
- data_obj = self.pool.get('ir.model.data')
- get_ref = data_obj.get_object_reference
- try:
- __, location_id = get_ref(cr, uid, *ref)
- location_obj.check_access_rule(cr, uid, [location_id],
- 'read', context=context)
- except (orm.except_orm, ValueError):
- location_id = False
- return location_id
-
- def _default_location_id(self, cr, uid, context=None):
- if context is None:
- context = {}
- move_obj = self.pool.get('stock.move')
- context['picking_type'] = 'internal'
- return move_obj._default_location_source(cr, uid, context=context)
-
- def _default_location_dest_id(self, cr, uid, context=None):
- ref = ('stock_reserve', 'stock_location_reservation')
- return self.get_location_from_ref(cr, uid, ref, context=context)
-
- _defaults = {
- 'type': 'internal',
- 'location_id': _default_location_id,
- 'location_dest_id': _default_location_dest_id,
- 'product_qty': 1.0,
- }
-
- def reserve(self, cr, uid, ids, context=None):
- """ Confirm a reservation
-
- The reservation is done using the default UOM of the product.
- A date until which the product is reserved can be specified.
- """
- move_obj = self.pool.get('stock.move')
- reservations = self.browse(cr, uid, ids, context=context)
- move_ids = [reserv.move_id.id for reserv in reservations]
- move_obj.write(cr, uid, move_ids,
- {'date_expected': fields.datetime.now()},
- context=context)
- move_obj.action_confirm(cr, uid, move_ids, context=context)
- move_obj.force_assign(cr, uid, move_ids, context=context)
- return True
-
- def release(self, cr, uid, ids, context=None):
- if isinstance(ids, (int, long)):
- ids = [ids]
- reservations = self.read(cr, uid, ids, ['move_id'],
- context=context, load='_classic_write')
- move_obj = self.pool.get('stock.move')
- move_ids = [reserv['move_id'] for reserv in reservations]
- move_obj.action_cancel(cr, uid, move_ids, context=context)
- return True
-
- def release_validity_exceeded(self, cr, uid, ids=None, context=None):
- """ Release all the reservation having an exceeded validity date """
- domain = [('date_validity', '<', fields.date.today()),
- ('state', '=', 'assigned')]
- if ids:
- domain.append(('id', 'in', ids))
- reserv_ids = self.search(cr, uid, domain, context=context)
- self.release(cr, uid, reserv_ids, context=context)
- return True
-
- def unlink(self, cr, uid, ids, context=None):
- """ Release the reservation before the unlink """
- self.release(cr, uid, ids, context=context)
- return super(stock_reservation, self).unlink(cr, uid, ids,
- context=context)
-
- def onchange_product_id(self, cr, uid, ids,
- product_id=False,
- context=None):
- move_obj = self.pool.get('stock.move')
- if ids:
- reserv = self.read(cr, uid, ids, ['move_id'], context=context,
- load='_classic_write')
- move_ids = [rv['move_id'] for rv in reserv]
- else:
- move_ids = []
- result = move_obj.onchange_product_id(
- cr, uid, move_ids, prod_id=product_id, loc_id=False,
- loc_dest_id=False, partner_id=False)
- if result.get('value'):
- # only keep the existing fields on the view
- keep = ('product_uom', 'name')
- result['value'] = dict((key, value) for key, value in
- result['value'].iteritems() if
- key in keep)
- return result
-
- def onchange_quantity(self, cr, uid, ids,
- product_id,
- product_qty,
- context=None):
- """ On change of product quantity avoid negative quantities """
- if not product_id or product_qty <= 0.0:
- return {'value': {'product_qty': 0.0}}
- return {}
-
- def open_move(self, cr, uid, ids, context=None):
- assert len(ids) == 1, "1 ID expected, got %r" % ids
- reserv = self.read(cr, uid, ids[0], ['move_id'], context=context,
- load='_classic_write')
- mod_obj = self.pool.get('ir.model.data')
- act_obj = self.pool.get('ir.actions.act_window')
- get_ref = mod_obj.get_object_reference
- __, action_id = get_ref(cr, uid, 'stock', 'action_move_form2')
- action = act_obj.read(cr, uid, action_id, context=context)
- action['name'] = _('Reservation Move')
- # open directly in the form view
- __, view_id = get_ref(cr, uid, 'stock', 'view_move_form')
- action['views'] = [(view_id, 'form')]
- action['res_id'] = reserv['move_id']
- return action
diff --git a/__unported__/stock_reserve/test/stock_reserve.yml b/__unported__/stock_reserve/test/stock_reserve.yml
deleted file mode 100644
index 5cd92e29f..000000000
--- a/__unported__/stock_reserve/test/stock_reserve.yml
+++ /dev/null
@@ -1,63 +0,0 @@
--
- I create a product to test the stock reservation
--
- !record {model: product.product, id: product_sorbet}:
- default_code: 001SORBET
- name: Sorbet
- 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
- procure_method: make_to_stock
- 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 Sorbet with 10 kgm
--
- !record {model: stock.change.product.qty, id: change_qty}:
- new_quantity: 10
- product_id: product_sorbet
--
- !python {model: stock.change.product.qty}: |
- context['active_id'] = ref('stock_reserve.product_sorbet')
- self.change_product_qty(cr, uid, [ref('change_qty')], context=context)
--
- I check Virtual stock of Sorbet after update stock.
--
- !python {model: product.product}: |
- product = self.browse(cr, uid, ref('stock_reserve.product_sorbet'), context=context)
- assert product.virtual_available == 10, "Stock is not updated."
--
- I create a stock reservation for 5 kgm
--
- !record {model: stock.reservation, id: reserv_sorbet1}:
- product_id: product_sorbet
- product_qty: 5.0
- product_uom: product.product_uom_kgm
- name: reserve 5 kgm of sorbet for test
--
- I confirm the reservation
--
- !python {model: stock.reservation}: |
- self.reserve(cr, uid, [ref('reserv_sorbet1')], context=context)
--
- I check Virtual stock of Sorbet after update reservation
--
- !python {model: product.product}: |
- product = self.browse(cr, uid, ref('stock_reserve.product_sorbet'), context=context)
- assert product.virtual_available == 5, "Stock is not updated."
--
- I release the reservation
--
- !python {model: stock.reservation}: |
- self.release(cr, uid, [ref('reserv_sorbet1')], context=context)
--
- I check Virtual stock of Sorbet after update reservation
--
- !python {model: product.product}: |
- product = self.browse(cr, uid, ref('stock_reserve.product_sorbet'), context=context)
- assert product.virtual_available == 10, "Stock is not updated."
diff --git a/__unported__/stock_reserve/view/product.xml b/__unported__/stock_reserve/view/product.xml
deleted file mode 100644
index 9ff624dc1..000000000
--- a/__unported__/stock_reserve/view/product.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
- product.product.form.reserve
- product.product
-
-
-
-
-
-
-
-
-
-
diff --git a/__unported__/stock_reserve/__init__.py b/stock_reserve/__init__.py
similarity index 100%
rename from __unported__/stock_reserve/__init__.py
rename to stock_reserve/__init__.py
diff --git a/__unported__/stock_reserve/__openerp__.py b/stock_reserve/__openerp__.py
similarity index 87%
rename from __unported__/stock_reserve/__openerp__.py
rename to stock_reserve/__openerp__.py
index 99fafbd1f..d1114f1fc 100644
--- a/__unported__/stock_reserve/__openerp__.py
+++ b/stock_reserve/__openerp__.py
@@ -19,7 +19,8 @@
#
##############################################################################
-{'name': 'Stock Reserve',
+{'name': 'Stock Reservation',
+ 'summary': 'Stock reservations on products',
'version': '0.1',
'author': 'Camptocamp',
'category': 'Warehouse',
@@ -28,8 +29,8 @@
'images': [],
'website': "http://www.camptocamp.com",
'description': """
-Stock Reserve
-=============
+Stock Reservation
+=================
Allows to create stock reservations on products.
@@ -42,6 +43,12 @@ 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.
+Contributors
+------------
+
+* Guewen Baconnier
+* Yannick Vaucher
+
""",
'depends': ['stock',
],
@@ -54,5 +61,5 @@ exceeded if the reservations are canceled.
'auto_install': False,
'test': ['test/stock_reserve.yml',
],
- 'installable': False,
+ 'installable': True,
}
diff --git a/__unported__/stock_reserve/data/stock_data.xml b/stock_reserve/data/stock_data.xml
similarity index 100%
rename from __unported__/stock_reserve/data/stock_data.xml
rename to stock_reserve/data/stock_data.xml
diff --git a/__unported__/stock_reserve/model/__init__.py b/stock_reserve/model/__init__.py
similarity index 100%
rename from __unported__/stock_reserve/model/__init__.py
rename to stock_reserve/model/__init__.py
diff --git a/stock_reserve/model/product.py b/stock_reserve/model/product.py
new file mode 100644
index 000000000..fd9ea385b
--- /dev/null
+++ b/stock_reserve/model/product.py
@@ -0,0 +1,84 @@
+# -*- 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 .
+#
+##############################################################################
+
+from openerp import models, fields, api
+
+
+class ProductTemplate(models.Model):
+ _inherit = 'product.template'
+
+ reservation_count = fields.Integer(
+ compute='_reservation_count',
+ string='# Sales')
+
+ @api.multi
+ def _reservation_count(self):
+ StockReservation = self.env['stock.reservation']
+ product_ids = self._get_products()
+ domain = [('product_id', 'in', product_ids),
+ ('state', 'in', ['draft', 'assigned'])]
+ reservations = StockReservation.search(domain)
+ self.reservation_count = sum(reserv.product_uom_qty
+ for reserv in reservations)
+
+ @api.multi
+ def action_view_reservations(self):
+ assert len(self._ids) == 1, "Expected 1 ID, got %r" % self._ids
+ ref = 'stock_reserve.action_stock_reservation_tree'
+ product_ids = self._get_products()
+ action_dict = self._get_act_window_dict(ref)
+ action_dict['domain'] = [('product_id', 'in', product_ids)]
+ action_dict['context'] = {
+ 'search_default_draft': 1,
+ 'search_default_reserved': 1
+ }
+ return action_dict
+
+
+class ProductProduct(models.Model):
+ _inherit = 'product.product'
+
+ reservation_count = fields.Integer(
+ compute='_reservation_count',
+ string='# Sales')
+
+ @api.multi
+ def _reservation_count(self):
+ StockReservation = self.env['stock.reservation']
+ product_id = self._ids[0]
+ domain = [('product_id', '=', product_id),
+ ('state', 'in', ['draft', 'assigned'])]
+ reservations = StockReservation.search(domain)
+ self.reservation_count = sum(reserv.product_uom_qty
+ for reserv in reservations)
+
+ @api.multi
+ def action_view_reservations(self):
+ assert len(self._ids) == 1, "Expected 1 ID, got %r" % self._ids
+ ref = 'stock_reserve.action_stock_reservation_tree'
+ product_id = self._ids[0]
+ action_dict = self.product_tmpl_id._get_act_window_dict(ref)
+ action_dict['domain'] = [('product_id', '=', product_id)]
+ action_dict['context'] = {
+ 'search_default_draft': 1,
+ 'search_default_reserved': 1
+ }
+ return action_dict
diff --git a/stock_reserve/model/stock_reserve.py b/stock_reserve/model/stock_reserve.py
new file mode 100644
index 000000000..0b61a8b69
--- /dev/null
+++ b/stock_reserve/model/stock_reserve.py
@@ -0,0 +1,198 @@
+# -*- 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 .
+#
+##############################################################################
+
+from openerp import models, fields, api
+from openerp.exceptions import except_orm
+from openerp.tools.translate import _
+
+
+class StockReservation(models.Model):
+ """ Allow to reserve products.
+
+ The fields mandatory for the creation of a reservation are:
+
+ * product_id
+ * product_uom_qty
+ * product_uom
+ * name
+
+ The following fields are required but have default values that you may
+ want to override:
+
+ * company_id
+ * location_id
+ * dest_location_id
+
+ Optionally, you may be interested to define:
+
+ * date_validity (once passed, the reservation will be released)
+ * note
+ """
+ _name = 'stock.reservation'
+ _description = 'Stock Reservation'
+ _inherits = {'stock.move': 'move_id'}
+
+ move_id = fields.Many2one(
+ 'stock.move',
+ 'Reservation Move',
+ required=True,
+ readonly=True,
+ ondelete='cascade',
+ select=1)
+ date_validity = fields.Date('Validity Date')
+
+ @api.model
+ def default_get(self, fields_list):
+ """
+ Ensure default value of computed field `product_qty` is not set
+ as it would raise an error
+ """
+ res = super(StockReservation, self).default_get(fields_list)
+ if 'product_qty' in res:
+ del res['product_qty']
+ return res
+
+ @api.model
+ def get_location_from_ref(self, ref):
+ """ Get a location from a xmlid if allowed
+ :param ref: tuple (module, xmlid)
+ """
+ data_obj = self.env['ir.model.data']
+ try:
+ location = data_obj.xmlid_to_object(ref, raise_if_not_found=True)
+ location.check_access_rule('read')
+ location_id = location.id
+ except (except_orm, ValueError):
+ location_id = False
+ return location_id
+
+ @api.model
+ def _default_picking_type_id(self):
+ """ Search for an internal picking type
+ """
+ type_obj = self.env['stock.picking.type']
+
+ types = type_obj.search([('code', '=', 'internal')], limit=1)
+ if types:
+ return types[0].id
+ return False
+
+ @api.model
+ def _default_location_id(self):
+ move_obj = self.env['stock.move']
+ picking_type_id = self._default_picking_type_id()
+ return (move_obj
+ .with_context(default_picking_type_id=picking_type_id)
+ ._default_location_source())
+
+ @api.model
+ def _default_location_dest_id(self):
+ ref = 'stock_reserve.stock_location_reservation'
+ return self.get_location_from_ref(ref)
+
+ _defaults = {
+ 'picking_type_id': _default_picking_type_id,
+ 'location_id': _default_location_id,
+ 'location_dest_id': _default_location_dest_id,
+ 'product_uom_qty': 1.0,
+ }
+
+ @api.multi
+ def reserve(self):
+ """ Confirm a reservation
+
+ The reservation is done using the default UOM of the product.
+ A date until which the product is reserved can be specified.
+ """
+ move_recs = self.move_id
+ move_recs.date_expected = fields.Datetime.now()
+ move_recs.action_confirm()
+ move_recs.force_assign()
+ return True
+
+ @api.multi
+ def release(self):
+ """
+ Release moves from reservation
+ """
+ move_recs = self.move_id
+ move_recs.action_cancel()
+ return True
+
+ @api.model
+ def release_validity_exceeded(self, ids=None):
+ """ Release all the reservation having an exceeded validity date """
+ domain = [('date_validity', '<', fields.date.today()),
+ ('state', '=', 'assigned')]
+ if ids:
+ domain.append(('id', 'in', ids))
+ reserv_ids = self.search(domain)
+ reserv_ids.release()
+ return True
+
+ @api.multi
+ def unlink(self):
+ """ Release the reservation before the unlink """
+ self.release()
+ return super(StockReservation, self).unlink()
+
+ @api.onchange('product_id')
+ def _onchange_product_id(self):
+ """ set product_uom and name from product onchange """
+ # save value before reading of self.move_id as this last one erase
+ # product_id value
+ product = self.product_id
+ # WARNING this gettattr erase self.product_id
+ move = self.move_id
+ result = move.onchange_product_id(
+ prod_id=product.id, loc_id=False, loc_dest_id=False,
+ partner_id=False)
+ if result.get('value'):
+ vals = result['value']
+ # only keep the existing fields on the view
+ self.name = vals.get('name')
+ self.product_uom = vals.get('product_uom')
+ # repeat assignation of product_id so we don't loose it
+ self.product_id = product.id
+
+ @api.onchange('product_uom_qty')
+ def _onchange_quantity(self):
+ """ On change of product quantity avoid negative quantities """
+ if not self.product_id or self.product_uom_qty <= 0.0:
+ self.product_uom_qty = 0.0
+
+ @api.multi
+ def open_move(self):
+ assert len(self.ids) == 1, "1 ID expected, got %r" % self.ids
+ reserv = self.move_id
+ IrModelData = self.env['ir.model.data']
+ ref_form2 = 'stock.action_move_form2'
+ action = IrModelData.xmlid_to_object(ref_form2)
+ action_dict = action.read()[0]
+ action_dict['name'] = _('Reservation Move')
+ # open directly in the form view
+ ref_form = 'stock.view_move_form'
+ view_id = IrModelData.xmlid_to_res_id(ref_form)
+ action_dict.update(
+ views=[(view_id, 'form')],
+ res_id=reserv.id,
+ )
+ return action_dict
diff --git a/__unported__/stock_reserve/security/ir.model.access.csv b/stock_reserve/security/ir.model.access.csv
similarity index 100%
rename from __unported__/stock_reserve/security/ir.model.access.csv
rename to stock_reserve/security/ir.model.access.csv
diff --git a/stock_reserve/test/stock_reserve.yml b/stock_reserve/test/stock_reserve.yml
new file mode 100644
index 000000000..e23ac9465
--- /dev/null
+++ b/stock_reserve/test/stock_reserve.yml
@@ -0,0 +1,114 @@
+-
+ I create a product to test the stock reservation
+-
+ !record {model: product.product, id: product_sorbet}:
+ default_code: 001SORBET
+ name: Sorbet
+ 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
+-
+ I create a stock orderpoint for the product
+-
+ !record {model: stock.warehouse.orderpoint, id: sorbet_orderpoint}:
+ warehouse_id: stock.warehouse0
+ location_id: stock.stock_location_stock
+ product_id: product_sorbet
+ product_uom: product.product_uom_kgm
+ product_min_qty: 4.0
+ product_max_qty: 15.0
+-
+ I update the current stock of the Sorbet with 10 kgm
+-
+ !record {model: stock.change.product.qty, id: change_qty}:
+ new_quantity: 10
+ product_id: product_sorbet
+-
+ !python {model: stock.change.product.qty}: |
+ context['active_id'] = ref('stock_reserve.product_sorbet')
+ self.change_product_qty(cr, uid, [ref('change_qty')], context=context)
+-
+ I check Virtual stock of Sorbet after update stock.
+-
+ !python {model: product.product}: |
+ product = self.browse(cr, uid, ref('stock_reserve.product_sorbet'), context=context)
+ assert product.virtual_available == 10, "Stock is not updated."
+-
+ I create a stock reservation for 6 kgm
+-
+ !record {model: stock.reservation, id: reserv_sorbet1}:
+ product_id: product_sorbet
+ product_uom_qty: 6.0
+ product_uom: product.product_uom_kgm
+ name: reserve 6 kg of sorbet for test
+-
+ I confirm the reservation
+-
+ !python {model: stock.reservation}: |
+ self.reserve(cr, uid, [ref('reserv_sorbet1')], context=context)
+-
+ I create a stock reservation for 500g
+-
+ !record {model: stock.reservation, id: reserv_sorbet2}:
+ product_id: product_sorbet
+ product_uom_qty: 500
+ product_uom: product.product_uom_gram
+ name: reserve 500g of sorbet for test
+-
+ I confirm the reservation
+-
+ !python {model: stock.reservation}: |
+ self.reserve(cr, uid, [ref('reserv_sorbet2')], context=context)
+-
+ I check Virtual stock of Sorbet after update reservation
+-
+ !python {model: product.product}: |
+ product = self.browse(cr, uid, ref('stock_reserve.product_sorbet'), context=context)
+ assert product.virtual_available == 3.5, "Stock is not updated."
+-
+ I run the scheduler
+-
+ !python {model: procurement.order}: |
+ self.run_scheduler(cr, uid)
+-
+ The procurement linked to the orderpoint must be in exception (no routes configured)
+-
+ !python {model: stock.warehouse.orderpoint}: |
+ orderpoint = self.browse(cr, uid, ref('sorbet_orderpoint'))
+ assert orderpoint.procurement_ids[0].state == 'exception', 'procurement must be in exception as there is no rule for procurement'
+ import math
+ assert orderpoint.procurement_ids[0].product_qty == math.ceil(15 - 3.5), 'wrong product qty ordered'
+-
+ I release the reservation
+-
+ !python {model: stock.reservation}: |
+ self.release(cr, uid, [ref('reserv_sorbet1')], context=context)
+-
+ I check Virtual stock of Sorbet after update reservation
+-
+ !python {model: product.product}: |
+ product = self.browse(cr, uid, ref('stock_reserve.product_sorbet'), context=context)
+ assert product.virtual_available == 9.5, "Stock is not updated."
+-
+ I set the validity of the second reservation to yesterday
+-
+ !python {model: stock.reservation}: |
+ import datetime
+ from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
+ yesterday = datetime.date.today() - datetime.timedelta(days=1)
+ yesterday = yesterday.strftime(DEFAULT_SERVER_DATE_FORMAT)
+ self.write(cr, uid, [ref('reserv_sorbet2')], {'date_validity': yesterday})
+-
+ I call the function releasing expired reservations
+-
+ !python {model: stock.reservation}: |
+ self.release_validity_exceeded(cr, uid, [])
+-
+ I check Virtual stock of Sorbet after update reservation
+-
+ !python {model: product.product}: |
+ product = self.browse(cr, uid, ref('stock_reserve.product_sorbet'), context=context)
+ assert product.virtual_available == 10.0, "Stock is not updated."
diff --git a/stock_reserve/view/product.xml b/stock_reserve/view/product.xml
new file mode 100644
index 000000000..e4e1dc3fb
--- /dev/null
+++ b/stock_reserve/view/product.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+ product.template.reservation.button
+ product.template
+
+
+
+
+
+
+
+
+
+ product.template.reservation.button
+ product.product
+
+
+
+
+
+
+
+
+
diff --git a/__unported__/stock_reserve/view/stock_reserve.xml b/stock_reserve/view/stock_reserve.xml
similarity index 90%
rename from __unported__/stock_reserve/view/stock_reserve.xml
rename to stock_reserve/view/stock_reserve.xml
index a15d08c8c..7ee9cbdd3 100644
--- a/__unported__/stock_reserve/view/stock_reserve.xml
+++ b/stock_reserve/view/stock_reserve.xml
@@ -23,14 +23,10 @@
-
-
+
+
-
+
@@ -64,7 +60,7 @@
-
+
@@ -112,11 +108,10 @@
-
+
Stock Reservations
stock.reservation
ir.actions.act_window
- form
{'search_default_draft': 1,
@@ -132,7 +127,7 @@
-