From d6fbbf0d963c18569b71ae6d9ac959b1f570529f Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Thu, 5 Sep 2013 14:06:46 +0200 Subject: [PATCH 01/49] [ADD] started a generic stock reservation module (stock_reserve), it will serve as a basis for the sale pre-book --- stock_reserve/__init__.py | 22 +++ stock_reserve/__openerp__.py | 58 +++++++ stock_reserve/data/stock_data.xml | 26 +++ stock_reserve/model/__init__.py | 23 +++ stock_reserve/model/product.py | 40 +++++ stock_reserve/model/stock_reserve.py | 177 +++++++++++++++++++++ stock_reserve/security/ir.model.access.csv | 3 + stock_reserve/test/stock_reserve.yml | 63 ++++++++ stock_reserve/view/product.xml | 19 +++ stock_reserve/view/stock_reserve.xml | 140 ++++++++++++++++ 10 files changed, 571 insertions(+) create mode 100644 stock_reserve/__init__.py create mode 100644 stock_reserve/__openerp__.py create mode 100644 stock_reserve/data/stock_data.xml create mode 100644 stock_reserve/model/__init__.py create mode 100644 stock_reserve/model/product.py create mode 100644 stock_reserve/model/stock_reserve.py create mode 100644 stock_reserve/security/ir.model.access.csv create mode 100644 stock_reserve/test/stock_reserve.yml create mode 100644 stock_reserve/view/product.xml create mode 100644 stock_reserve/view/stock_reserve.xml diff --git a/stock_reserve/__init__.py b/stock_reserve/__init__.py new file mode 100644 index 000000000..643bee7ab --- /dev/null +++ b/stock_reserve/__init__.py @@ -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 . +# +############################################################################## + +from . import model diff --git a/stock_reserve/__openerp__.py b/stock_reserve/__openerp__.py new file mode 100644 index 000000000..61684f3c8 --- /dev/null +++ b/stock_reserve/__openerp__.py @@ -0,0 +1,58 @@ +# -*- 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 . +# +############################################################################## + +{'name': 'Stock Reserve', + 'version': '0.1', + 'author': 'Camptocamp', + 'category': 'Warehouse', + 'license': 'AGPL-3', + 'complexity': 'normal', + 'images': [], + 'website': "http://www.camptocamp.com", + 'description': """ +Stock Reserve +============= + +Allows to create stock reservations on products. + +Each reservation can have a validity date, once passed, the reservation +is automatically lifted. + +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. + +""", + 'depends': ['stock', + ], + 'demo': [], + 'data': ['view/stock_reserve.xml', + 'view/product.xml', + 'data/stock_data.xml', + 'security/ir.model.access.csv', + ], + 'auto_install': False, + 'test': ['test/stock_reserve.yml', + ], + 'installable': True, + } diff --git a/stock_reserve/data/stock_data.xml b/stock_reserve/data/stock_data.xml new file mode 100644 index 000000000..be6e44224 --- /dev/null +++ b/stock_reserve/data/stock_data.xml @@ -0,0 +1,26 @@ + + + + + Reservation Stock + + + + + + + Release the stock reservation having a passed validity date + + + 1 + days + -1 + + stock.reservation + release_validity_exceeded + () + + + + diff --git a/stock_reserve/model/__init__.py b/stock_reserve/model/__init__.py new file mode 100644 index 000000000..9adf1d54b --- /dev/null +++ b/stock_reserve/model/__init__.py @@ -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 . +# +############################################################################## + +from . import stock_reserve +from . import product diff --git a/stock_reserve/model/product.py b/stock_reserve/model/product.py new file mode 100644 index 000000000..45b7f28aa --- /dev/null +++ b/stock_reserve/model/product.py @@ -0,0 +1,40 @@ +# -*- 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 + + +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/stock_reserve/model/stock_reserve.py b/stock_reserve/model/stock_reserve.py new file mode 100644 index 000000000..6466ac233 --- /dev/null +++ b/stock_reserve/model/stock_reserve.py @@ -0,0 +1,177 @@ +# -*- 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'): + vals = result['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/stock_reserve/security/ir.model.access.csv b/stock_reserve/security/ir.model.access.csv new file mode 100644 index 000000000..c48a8eefa --- /dev/null +++ b/stock_reserve/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_stock_reservation_manager,stock.reservation manager,model_stock_reservation,stock.group_stock_manager,1,1,1,1 +access_stock_reservation_user,stock.reservation user,model_stock_reservation,stock.group_stock_user,1,1,1,0 diff --git a/stock_reserve/test/stock_reserve.yml b/stock_reserve/test/stock_reserve.yml new file mode 100644 index 000000000..5cd92e29f --- /dev/null +++ b/stock_reserve/test/stock_reserve.yml @@ -0,0 +1,63 @@ +- + 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/stock_reserve/view/product.xml b/stock_reserve/view/product.xml new file mode 100644 index 000000000..9ff624dc1 --- /dev/null +++ b/stock_reserve/view/product.xml @@ -0,0 +1,19 @@ + + + + + + product.product.form.reserve + product.product + + + + + + + + + + product.template.reservation.button + product.product + + + + + + + + + diff --git a/stock_reserve/view/stock_reserve.xml b/stock_reserve/view/stock_reserve.xml new file mode 100644 index 000000000..7ee9cbdd3 --- /dev/null +++ b/stock_reserve/view/stock_reserve.xml @@ -0,0 +1,135 @@ + + + + + stock.reservation.form + stock.reservation + +
+
+
+ + + + + + + + + + + + + + +
+
+
+ + + stock.reservation.tree + stock.reservation + + + + + + + + + + - - - - product.template.reservation.button product.product - + + + + + + + + + product.template.reservation.button product.product From e53d3fe800c3c8a922488a0e16e7bec6183269d9 Mon Sep 17 00:00:00 2001 From: gfcapalbo Date: Wed, 25 Feb 2015 10:31:23 +0100 Subject: [PATCH 10/49] [FIX] Stock_reserve change revert --- stock_reserve/view/product.xml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/stock_reserve/view/product.xml b/stock_reserve/view/product.xml index 76c979362..ca427c8e1 100644 --- a/stock_reserve/view/product.xml +++ b/stock_reserve/view/product.xml @@ -1,19 +1,19 @@ - - product.template.reservation.button - product.template - - - - - - - + + product.template.reservation.button + product.template + + + + + + + From 981b47dcf94e15340f561ed99afdec882df56079 Mon Sep 17 00:00:00 2001 From: "Giovanni Capalbo (Therp)" Date: Wed, 25 Feb 2015 11:35:18 +0100 Subject: [PATCH 11/49] Update product.xml --- stock_reserve/view/product.xml | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/stock_reserve/view/product.xml b/stock_reserve/view/product.xml index ca427c8e1..66a679665 100644 --- a/stock_reserve/view/product.xml +++ b/stock_reserve/view/product.xml @@ -1,23 +1,20 @@ - - product.template.reservation.button - product.template - - - - - - - - - - - + + + product.template.reservation.button + product.template + + + + + + + product.template.reservation.button From c3c758c3d9ce04f87b9df8aba42f7386d7b1afc7 Mon Sep 17 00:00:00 2001 From: Alexandre Fayolle Date: Mon, 2 Mar 2015 17:28:35 +0100 Subject: [PATCH 12/49] Add OCA as author of OCA addons In order to get visibility on https://www.odoo.com/apps the OCA board has decided to add the OCA as author of all the addons maintained as part of the association. --- stock_reserve/__openerp__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stock_reserve/__openerp__.py b/stock_reserve/__openerp__.py index d1114f1fc..23386630f 100644 --- a/stock_reserve/__openerp__.py +++ b/stock_reserve/__openerp__.py @@ -22,7 +22,7 @@ {'name': 'Stock Reservation', 'summary': 'Stock reservations on products', 'version': '0.1', - 'author': 'Camptocamp', + 'author': "Camptocamp,Odoo Community Association (OCA)", 'category': 'Warehouse', 'license': 'AGPL-3', 'complexity': 'normal', From 224cd3e181bf69bf81984e3c68f77b153cd44590 Mon Sep 17 00:00:00 2001 From: Leonardo Pistone Date: Tue, 17 Mar 2015 17:30:57 +0100 Subject: [PATCH 13/49] fix #42: reserve location should be outside WH This has always been wrong on v8, but because of the very obscure odoo/odoo#5797, this seemed to work every time we had single-step reception. Please note that this block of XML is noupdate, so for existing installations you need to change the parent of the reservation location to something outside your warehouses manually. Incidentally, this makes the branch green independently of odoo/odoo#5797. --- stock_reserve/__openerp__.py | 6 ++- stock_reserve/data/stock_data.xml | 2 +- .../migrations/0.2/post-migration.py | 42 +++++++++++++++++++ stock_reserve/model/product.py | 25 ++++------- stock_reserve/test/stock_reserve.yml | 17 +++++--- stock_reserve/view/stock_reserve.xml | 3 ++ 6 files changed, 72 insertions(+), 23 deletions(-) create mode 100644 stock_reserve/migrations/0.2/post-migration.py diff --git a/stock_reserve/__openerp__.py b/stock_reserve/__openerp__.py index 23386630f..1acf2312b 100644 --- a/stock_reserve/__openerp__.py +++ b/stock_reserve/__openerp__.py @@ -21,7 +21,7 @@ {'name': 'Stock Reservation', 'summary': 'Stock reservations on products', - 'version': '0.1', + 'version': '0.2', 'author': "Camptocamp,Odoo Community Association (OCA)", 'category': 'Warehouse', 'license': 'AGPL-3', @@ -43,11 +43,15 @@ 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 ownership of stock is active in the stock settings, you can specify the +owner on the reservation. + Contributors ------------ * Guewen Baconnier * Yannick Vaucher +* Leonardo Pistone """, 'depends': ['stock', diff --git a/stock_reserve/data/stock_data.xml b/stock_reserve/data/stock_data.xml index be6e44224..d2dc81a49 100644 --- a/stock_reserve/data/stock_data.xml +++ b/stock_reserve/data/stock_data.xml @@ -3,7 +3,7 @@ Reservation Stock - + diff --git a/stock_reserve/migrations/0.2/post-migration.py b/stock_reserve/migrations/0.2/post-migration.py new file mode 100644 index 000000000..40e439352 --- /dev/null +++ b/stock_reserve/migrations/0.2/post-migration.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Author: Leonardo Pistone +# Copyright 2015 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 . + + +def migrate(cr, installed_version): + """Update a wrong location that is no_update in XML.""" + if installed_version == '8.0.0.1': + cr.execute(''' + UPDATE stock_location + SET location_id = ( + SELECT res_id + FROM ir_model_data + WHERE name = 'stock_location_locations' + AND module = 'stock' + ) + WHERE id = ( + SELECT res_id + FROM ir_model_data + WHERE name = 'stock_location_reservation' + AND module = 'stock_reserve' + ) + AND location_id = ( + SELECT res_id + FROM ir_model_data + WHERE name = 'stock_location_company' + AND module = 'stock' + ); + ''') diff --git a/stock_reserve/model/product.py b/stock_reserve/model/product.py index fd9ea385b..1f6c42333 100644 --- a/stock_reserve/model/product.py +++ b/stock_reserve/model/product.py @@ -25,19 +25,14 @@ from openerp import models, fields, api class ProductTemplate(models.Model): _inherit = 'product.template' - reservation_count = fields.Integer( + reservation_count = fields.Float( compute='_reservation_count', string='# Sales') - @api.multi + @api.one 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) + self.reservation_count = sum(variant.reservation_count + for variant in self.product_variant_ids) @api.multi def action_view_reservations(self): @@ -56,18 +51,16 @@ class ProductTemplate(models.Model): class ProductProduct(models.Model): _inherit = 'product.product' - reservation_count = fields.Integer( + reservation_count = fields.Float( compute='_reservation_count', string='# Sales') - @api.multi + @api.one def _reservation_count(self): - StockReservation = self.env['stock.reservation'] - product_id = self._ids[0] - domain = [('product_id', '=', product_id), + domain = [('product_id', '=', self.id), ('state', 'in', ['draft', 'assigned'])] - reservations = StockReservation.search(domain) - self.reservation_count = sum(reserv.product_uom_qty + reservations = self.env['stock.reservation'].search(domain) + self.reservation_count = sum(reserv.product_qty for reserv in reservations) @api.multi diff --git a/stock_reserve/test/stock_reserve.yml b/stock_reserve/test/stock_reserve.yml index 66b6f412e..369b1563f 100644 --- a/stock_reserve/test/stock_reserve.yml +++ b/stock_reserve/test/stock_reserve.yml @@ -60,8 +60,15 @@ - I confirm the reservation - - !python {model: stock.reservation}: | - self.reserve(cr, uid, [ref('reserv_sorbet2')], context=context) + !python {model: stock.reservation, id: reserv_sorbet2}: | + self.reserve() +- + I check the reserved amount of the product and the template +- + !python {model: product.product, id: product_sorbet}: | + from nose.tools import * + assert_almost_equal(6.5, self.reservation_count) + assert_almost_equal(6.5, self.product_tmpl_id.reservation_count) - Then the reservation should be assigned and have reserved a quant - @@ -73,9 +80,9 @@ - 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." + !python {model: product.product, id: product_sorbet}: | + from nose.tools import * + assert_almost_equal(3.5, self.virtual_available) - I run the scheduler - diff --git a/stock_reserve/view/stock_reserve.xml b/stock_reserve/view/stock_reserve.xml index 7ee9cbdd3..cbeaa8abf 100644 --- a/stock_reserve/view/stock_reserve.xml +++ b/stock_reserve/view/stock_reserve.xml @@ -36,6 +36,7 @@ + @@ -63,6 +64,7 @@ + - - - + + product.template.reservation.button + product.template + + + + + + + - - product.template.reservation.button - product.product - - - - - - - - - + + product.template.reservation.button + product.product + + + + + + + + diff --git a/stock_reserve/view/stock_reserve.xml b/stock_reserve/view/stock_reserve.xml index 1a1dee05c..760ac9dd9 100644 --- a/stock_reserve/view/stock_reserve.xml +++ b/stock_reserve/view/stock_reserve.xml @@ -1,142 +1,140 @@ - - - - stock.reservation.form - stock.reservation - -
-
-
- - - - - - - - - - - - - - -
-
-
- - - stock.reservation.tree - stock.reservation - - - - - - - - - - - - - - - - - - product.template.reservation.button - product.product - - - - - - - + + + + +
+ + product.template.reservation.button + product.product + + + + + + + diff --git a/stock_reserve/view/stock_reserve.xml b/stock_reserve/view/stock_reserve.xml index 760ac9dd9..8ff8c4910 100644 --- a/stock_reserve/view/stock_reserve.xml +++ b/stock_reserve/view/stock_reserve.xml @@ -1,140 +1,192 @@ - + - - stock.reservation.form - stock.reservation - -
-
-
+ + + + + + + + + + + + + + +
+
+
+ + stock.reservation.tree + stock.reservation + + + + + + + + + + + +