← Back to team overview

openerp-community-reviewer team mailing list archive

[Merge] lp:~numerigraphe-team/stock-logistic-warehouse/7.0-inventory-location into lp:stock-logistic-warehouse

 

Loïc Bellier - Numérigraphe has proposed merging lp:~numerigraphe-team/stock-logistic-warehouse/7.0-inventory-location into lp:stock-logistic-warehouse.

Requested reviews:
  Stock and Logistic Core Editors (stock-logistic-core-editors)

For more details, see:
https://code.launchpad.net/~numerigraphe-team/stock-logistic-warehouse/7.0-inventory-location/+merge/210630

This proposal feature obsoletes Tiny's "stock_alternative_inventory", and 
implements exhaustive inventory in a much more practical way.

It lets you explicitly state which locations are being inventoried, and makes 
sure the stock levels exactly reflect the physical inventory for those 
locations.

It also checks that all locations been entered in the inventory lines, 
and no other locations.

Finally it will block Stock Moves during the inventory, to keep 
warehouse workers from changing the stock while the inventory is not 
finished.
-- 
https://code.launchpad.net/~numerigraphe-team/stock-logistic-warehouse/7.0-inventory-location/+merge/210630
Your team Stock and Logistic Core Editors is requested to review the proposed merge of lp:~numerigraphe-team/stock-logistic-warehouse/7.0-inventory-location into lp:stock-logistic-warehouse.
=== added directory 'stock_inventory_location'
=== added file 'stock_inventory_location/__init__.py'
--- stock_inventory_location/__init__.py	1970-01-01 00:00:00 +0000
+++ stock_inventory_location/__init__.py	2014-03-12 15:23:02 +0000
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU 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 General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import stock_inventory_location
+import wizard

=== added file 'stock_inventory_location/__openerp__.py'
--- stock_inventory_location/__openerp__.py	1970-01-01 00:00:00 +0000
+++ stock_inventory_location/__openerp__.py	2014-03-12 15:23:02 +0000
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU 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 General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+
+{
+    "name": "Exhaustive Stock Inventories",
+    "version": "1.0",
+    "depends": ["stock"],
+    "author": u"Numérigraphe",
+    "category": "Inventory",
+    "description": """
+Let users choose between standard and exhaustive Inventories
+============================================================
+
+Standard Physical Inventories in OpenERP only contain a generic list of products by locations,
+which is well suited to partial Inventories and simple warehouses.
+When the a standard Inventory is confirmed, only the products in the inventory are checked.
+If a Product is present in the computed stock and not in the recorded Inventory, OpenERP will
+consider that it remains unchanged.
+
+But for exhaustive inventories in complex warehouses, it is not practical:
+ - you want to avoid Stock Moves in/out of these Locations while you count the goods
+ - you must make sure all the locations you want have been counted
+ - you must make sure no other location has been counted by mistake
+ - you want the computed stock to perfectly match the inventory when you confirm it.
+
+This module lets choose whether an Physical Inventory is exhaustive or standard.
+For an exhaustive Inventory, in the state "Draft" you define the list of Locations where goods must be counted.
+ - a new Inventory status ("Open") lets you indicate that the list of Locations is definitive and you are now counting the goods.
+    In that status, no Stock Moves can be recorded in/out of the Inventory's Locations.
+ - if some of the Inventory's Locations have not been entered in the Inventory Lines, OpenERP warns you when you confirm the Inventory.
+ - only the Inventory's Locations can be entered in the Inventory Lines.
+ - every good that is not in the Inventory Lines is considered lost, and gets moved out of the stock when you confirm the Inventory.avec openerp
+""",
+    "update_xml": [
+                   "wizard/stock_confirm_uninventoried_location.xml",
+                   "stock_inventory_location_view.xml",
+                   "wizard/stock_fill_location_inventory_view.xml",
+                   ],
+     "test": ["test/location_inventory_test.yml",
+              "test/location_exhaustive_inventory_test.yml",
+              ],
+     "demo": ["stock_inventory_location_demo.xml"]
+
+}

=== added directory 'stock_inventory_location/i18n'
=== added file 'stock_inventory_location/i18n/fr.po'
--- stock_inventory_location/i18n/fr.po	1970-01-01 00:00:00 +0000
+++ stock_inventory_location/i18n/fr.po	2014-03-12 15:23:02 +0000
@@ -0,0 +1,261 @@
+# Translation of OpenERP Server.
+# This file contains the translation of the following modules:
+#	* stock_inventory_location
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: OpenERP Server 6.0.4\n"
+"Report-Msgid-Bugs-To: support@xxxxxxxxxxx\n"
+"POT-Creation-Date: 2013-09-25 13:58+0000\n"
+"PO-Revision-Date: 2013-09-25 13:58+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: stock_inventory_location
+#: code:addons/stock_inventory_location/stock_inventory_location.py:232
+#: constraint:stock.move:0
+#, python-format
+msgid "A Physical Inventory is being conducted at this location"
+msgstr "Un inventaire est déjà en cours à cet emplacement"
+
+#. module: stock_inventory_location
+#: view:stock.inventory.uninventoried.locations:0
+msgid "Cancel"
+msgstr "Annuler"
+
+#. module: stock_inventory_location
+#: view:stock.inventory:0
+msgid "Cancel Inventory"
+msgstr "Annuler l'inventaire"
+
+#. module: stock_inventory_location
+#: help:stock.inventory,exhaustive:0
+msgid "Check the box if you are conducting an exhaustive Inventory.\n"
+"Leave the box unchecked if you are conducting a standard Inventory (partial inventory for example).\n"
+"For an exhaustive Inventory:\n"
+" - the status \"Draft\" lets you define the list of Locations where goods must be counted\n"
+" - the status \"Open\" indicates that the list of Locations is definitive and you are now counting the goods. In that status, no Stock Moves can be recorded in/out of the Inventory's Locations\n"
+" - only the Inventory's Locations can be entered in the Inventory Lines\n"
+" - if some of the Inventory's Locations have not been entered in the Inventory Lines, OpenERP warns you when you confirm the Inventory\n"
+" - every good that is not in the Inventory Lines is considered lost, and gets moved out of the stock when you confirm the Inventory"
+msgstr "Cochez la case si vous effectuez un inventaire exhaustif.\n"
+"Laissez la case non-dochée si vous effectuez un inventaire standard (par exmple un inventaire partiel).\n"
+"Pour les inventaires exhaustifs :\n"
+" - le statut \"Brouillon\" permet d'indiquer la liste des emplacements dont la marchandise doit être comptée\n"
+" - le statut \"Ouvert\" indiqueque la liste des emplacements est définitive, et que le comptage est en cours. Dans ce statut, aucun mouvement de stock ne peut être enregistré depuis/vers les emplacements de l'inventaire\n"
+" - seuls les emplacements de l'inventaire peuvent être saisis dans les lignes d'inventaire\n"
+" - si certains emplacements sont absent des lignes d'inventaire, OpenERP vous en avertit lors de la confirmation de l'inventaire\n"
+" - toutes les marchandises qui ne sont pas dans les lignes d'inventaire sont considérées comme perdues, et sont supprimées lors de la confirmation de l'inventaire"
+
+#. module: stock_inventory_location
+#: view:stock.inventory:0
+msgid "Confirm Inventory"
+msgstr "Confirmer l'inventaire"
+
+#. module: stock_inventory_location
+#: model:ir.model,name:stock_inventory_location.model_stock_inventory_uninventoried_locations
+msgid "Confirm the uninventoried Locations."
+msgstr "Confirmer les emplacements non inventoriés."
+
+#. module: stock_inventory_location
+#: view:stock.inventory.uninventoried.locations:0
+msgid "Confirm uninventoried locations"
+msgstr "Confirmez les emplacements non-inventoriés"
+
+#. module: stock_inventory_location
+#: model:ir.model,name:stock_inventory_location.model_stock_location
+msgid "Emplacement"
+msgstr "Emplacement"
+
+#. module: stock_inventory_location
+#: code:addons/stock_inventory_location/wizard/stock_fill_location_inventory.py:54
+#, python-format
+msgid "Error !"
+msgstr "Erreur !"
+
+#. module: stock_inventory_location
+#: code:addons/stock_inventory_location/wizard/stock_fill_location_inventory.py:75
+#, python-format
+msgid "Error : Empty location !"
+msgstr "Erreur : Emplacement vide !"
+
+#. module: stock_inventory_location
+#: code:addons/stock_inventory_location/stock_inventory_location.py:231
+#: code:addons/stock_inventory_location/stock_inventory_location.py:282
+#, python-format
+msgid "Error! Location on inventory"
+msgstr "Erreur! emplacement en inventaire"
+
+#. module: stock_inventory_location
+#: constraint:stock.inventory:0
+msgid "Error! You can not create recursive inventories."
+msgstr "Erreur! Vous ne pouvez pas créer un inventaire récursif."
+
+#. module: stock_inventory_location
+#: constraint:stock.inventory.line:0
+msgid "Error: duplicates lines"
+msgstr "Erreur: lignes en double"
+
+#. module: stock_inventory_location
+#: view:stock.inventory:0
+#: field:stock.inventory,exhaustive:0
+msgid "Exhaustive"
+msgstr "Exhaustif"
+
+#. module: stock_inventory_location
+#: model:ir.model,name:stock_inventory_location.model_stock_inventory
+msgid "Gestion des stocks"
+msgstr "Gestion des stocks"
+
+#. module: stock_inventory_location
+#: view:stock.inventory.uninventoried.locations:0
+msgid "If you confirm the Inventory, these Locations will be considered empty and their content will be purged."
+msgstr "Si vous confirmez l'inventaire, ces emplacements seront considérés comme vides et leur contenu sera supprimé."
+
+#. module: stock_inventory_location
+#: model:ir.model,name:stock_inventory_location.model_stock_fill_inventory
+msgid "Importer un inventaire"
+msgstr "Importer un inventaire"
+
+#. module: stock_inventory_location
+#: view:stock.inventory.uninventoried.locations:0
+msgid "It could either mean that the Locations are empty, or that the Inventory is not yet complete."
+msgstr "Cela peut vouloir dire soit que l'inventaire n'est pas terminé, soit que ces emplacements sont effectivement vides."
+
+#. module: stock_inventory_location
+#: model:ir.model,name:stock_inventory_location.model_stock_inventory_line
+msgid "Ligne d'inventaire"
+msgstr "Ligne d'inventaire"
+
+#. module: stock_inventory_location
+#: code:addons/stock_inventory_location/stock_inventory_location.py:59
+#, python-format
+msgid "Location missing for this inventory."
+msgstr "Emplacement manquant pour cet inventaire."
+
+#. module: stock_inventory_location
+#: view:stock.inventory:0
+#: field:stock.inventory,location_ids:0
+msgid "Locations"
+msgstr "Emplacements"
+
+#. module: stock_inventory_location
+#: model:ir.model,name:stock_inventory_location.model_stock_move
+msgid "Mouvement de stock"
+msgstr "Mouvement de stock"
+
+#. module: stock_inventory_location
+#: code:addons/stock_inventory_location/wizard/stock_fill_location_inventory.py:75
+#, python-format
+msgid "No location to import.\n"
+"You must add a location on the locations list."
+msgstr "Pas d'emplacement à importer.\n"
+"Vous devez ajouter un emplacement dans la liste des emplacements."
+
+#. module: stock_inventory_location
+#: code:addons/stock_inventory_location/wizard/stock_fill_location_inventory.py:58
+#, python-format
+msgid "No locations found for the inventory."
+msgstr "Pas d'emplacement trouvé pour cet inventaire."
+
+#. module: stock_inventory_location
+#: code:addons/stock_inventory_location/stock_inventory_location.py:163
+#, python-format
+msgid "No product in this location."
+msgstr "Pas de produit à cet emplacement."
+
+#. module: stock_inventory_location
+#: code:addons/stock_inventory_location/stock_inventory_location.py:283
+#, python-format
+msgid "One or more locations are inventoried :\n"
+"%s"
+msgstr "Un ou plusieurs emplacements sont en inventaire :\n"
+"%s"
+
+#. module: stock_inventory_location
+#: constraint:stock.move:0
+msgid "One or more lots are awaiting quality control and cannot be moved."
+msgstr "Un ou plusieurs lots sont en attente de contrôle qualité, et ne peuvent pas être déplacés."
+
+#. module: stock_inventory_location
+#: view:stock.inventory:0
+msgid "Open Inventory"
+msgstr "Ouvrir l'inventaire"
+
+#. module: stock_inventory_location
+#: constraint:stock.inventory:0
+msgid "Other Physical inventories are being conducted using the same Locations."
+msgstr "Erreur: certains emplacements sont déjà dans un autre inventaire."
+
+#. module: stock_inventory_location
+#: view:stock.inventory.uninventoried.locations:0
+msgid "Purge contents and confirm Inventory"
+msgstr "Supprimer le contenu et confirmer l'inventaire"
+
+#. module: stock_inventory_location
+#: view:stock.inventory.uninventoried.locations:0
+msgid "The following Stock Locations are part of the current Physical Inventory, but not Inventory Line has been recorded for them."
+msgstr "Les emplacements suivants font partie de l'inventaire en cours, mais aucune ligne d'inventaire les concernant n'a été enregistrée."
+
+#. module: stock_inventory_location
+#: help:stock.inventory,location_ids:0
+msgid "This is the list of the Stock Locations that you want to count the goods in.\n"
+"Only these Locations can be entered in the Inventory Lines.\n"
+"If some of them have not been entered in the Inventory Lines, OpenERP will warn you when you confirm the Inventory."
+msgstr "Ceci est la liste des emplacements dont vous voulez compter la marchandise.\n"
+"Seuls ces emplacements peuvent être enregistrés dans les lignes d'inventaire.\n"
+"Si certains ne sont enregistrés sur aucune ligne d'inventaire, OpenERP vous avertit lors de la confirmation de l'inventaire."
+
+#. module: stock_inventory_location
+#: field:stock.inventory.uninventoried.locations,location_ids:0
+msgid "Uninventoried location"
+msgstr "Emplacements non inventoriés"
+
+#. module: stock_inventory_location
+#: code:addons/stock_inventory_location/stock_inventory_location.py:59
+#: code:addons/stock_inventory_location/stock_inventory_location.py:163
+#: code:addons/stock_inventory_location/wizard/stock_fill_location_inventory.py:58
+#, python-format
+msgid "Warning !"
+msgstr "Attention !"
+
+#. module: stock_inventory_location
+#: code:addons/stock_inventory_location/stock_inventory_location.py:209
+#, python-format
+msgid "Warning: Wrong location"
+msgstr "Attention: emplacement incorrect"
+
+#. module: stock_inventory_location
+#: code:addons/stock_inventory_location/stock_inventory_location.py:210
+#, python-format
+msgid "You cannot add this location to inventory line.\n"
+"You must add this location on the locations list"
+msgstr "Vous ne pouvez pas ajouter cet emplacement.\n"
+"Il ne fait pas partie de la liste des emplacements à inventorier"
+
+#. module: stock_inventory_location
+#: constraint:stock.move:0
+msgid "You must assign a production lot for this product"
+msgstr "Vous devez affecter un lot de fabrication à ce produit."
+
+#. module: stock_inventory_location
+#: constraint:stock.inventory.line:0
+msgid "You must not create same inventory line Product, Location, Lot on the same date"
+msgstr "Vous ne pouvez pas créer plusieurs lignes d'inventaires pour le même produit, lot, emplacement à la même date"
+
+#. module: stock_inventory_location
+#: constraint:stock.move:0
+msgid "You try to assign a lot which is not from the same product"
+msgstr "Vous essayez d'affecter un lot qui n'est pas pour ce produit."
+
+
+#. module: stock_inventory_location
+#: code:addons/stock_inventory_location/wizard/stock_fill_location_inventory.py:54
+#, python-format
+msgid "the inventory must be in \"Open\" state."
+msgstr "l'inventaire doit etre \"Ouvert\"."
+

=== added file 'stock_inventory_location/stock_inventory_location.py'
--- stock_inventory_location/stock_inventory_location.py	1970-01-01 00:00:00 +0000
+++ stock_inventory_location/stock_inventory_location.py	2014-03-12 15:23:02 +0000
@@ -0,0 +1,295 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU 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 General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from collections import Iterable
+
+from openerp.osv import osv, orm, fields
+from openerp.tools.translate import _
+
+
+class StockInventory(osv.osv):
+    """Add locations to the inventories"""
+    _inherit = 'stock.inventory'
+    _columns = {
+        # XXX refactor if ever lp:~numerigraphe/openobject-addons/7.0-inventory-states is accepted upstream
+        'state': fields.selection((('draft', 'Draft'),
+                                   ('open', 'Open'),
+                                   ('done', 'Done'),
+                                   ('confirm', 'Confirmed'),
+                                   ('cancel', 'Cancelled')), 'State', readonly=True, select=True),
+        # Make the inventory lines read-only in all states except "Open", to ensure that no unwanted Location can be inserted
+        'inventory_line_id': fields.one2many('stock.inventory.line', 'inventory_id', 'Inventory lines', readonly=True, states={'open': [('readonly', False)]}),
+        'location_ids': fields.many2many('stock.location', 'stock_inventory_location_rel',
+                                         'location_id', 'inventory_id',
+                                         'Locations',
+                                         readonly=True, states={'draft': [('readonly', False)]},
+                                         help="""This is the list of the Stock Locations that you want to count the goods in.
+Only these Locations can be entered in the Inventory Lines.
+If some of them have not been entered in the Inventory Lines, OpenERP will warn you when you confirm the Inventory."""),
+        'exhaustive': fields.boolean('Exhaustive', readonly=True, states={'draft': [('readonly', False)]},
+                                         help="""Check the box if you are conducting an exhaustive Inventory.
+Leave the box unchecked if you are conducting a standard Inventory (partial inventory for example).
+For an exhaustive Inventory:
+ - the status "Draft" lets you define the list of Locations where goods must be counted
+ - the status "Open" indicates that the list of Locations is definitive and you are now counting the goods. In that status, no Stock Moves can be recorded in/out of the Inventory's Locations
+ - only the Inventory's Locations can be entered in the Inventory Lines
+ - if some of the Inventory's Locations have not been entered in the Inventory Lines, OpenERP warns you when you confirm the Inventory
+ - every good that is not in the Inventory Lines is considered lost, and gets moved out of the stock when you confirm the Inventory"""),
+        }
+
+    def action_open(self, cr, uid, ids, context=None):
+        """Open the inventory: open all locations, import and print inventory sheet become possible"""
+        # verify if exhaustive inventory have locations before open inventory
+        for inventory in self.browse(cr, uid, ids, context=None):
+            if inventory.exhaustive:
+                if not inventory.location_ids:
+                    raise osv.except_osv(_('Warning !'), _('Location missing for this inventory.'))
+        return self.write(cr, uid, ids, {'state': 'open'}, context=context)
+
+    # XXX refactor if ever lp:~numerigraphe/openobject-addons/7.0-inventory-states is accepted upstream
+    _defaults = {
+        'state': lambda *a: 'draft',
+        'exhaustive': lambda *a: False,
+        }
+
+    def _check_location_free_from_inventories(self, cr, uid, ids):
+        """Verify that no other Inventory is being conducted on the location (exact id, not children)."""
+        for inventory in self.browse(cr, uid, ids, context=None):
+            if not inventory.exhaustive:
+                continue  # always accepted on partial inventories
+            location_ids = [location.id for location in inventory.location_ids]
+            inv_ids = self.search(cr, uid, [('location_ids', 'in', location_ids),
+                                            ('id', '!=', inventory.id),
+                                            ('date', '=', inventory.date),
+                                            ('exhaustive', '=', True), ])
+            if inv_ids:
+                return False
+        return True
+
+    _constraints = [(_check_location_free_from_inventories,
+                     'Other Physical inventories are being conducted using the same Locations.',
+                     ['location_ids', 'date', 'exhaustive'])]
+
+    def _get_locations_open_inventories(self, cr, uid, context=None):
+        """Search for locations on open inventories (exhaustive inventory only), and their children """
+        open_inventories_ids = self.search(cr, uid, [('state', '=', 'open'), ], context=context)
+        location_ids = set()
+        for open_inventory in self.browse(cr, uid, open_inventories_ids, context=context):
+            location_ids.update([location.id for location in open_inventory.location_ids])
+        # Extend to the children Locations
+        if location_ids:  # XXX probably works even otherwise
+            location_ids = self.pool.get('stock.location').search(cr, uid,
+                [('location_id', 'child_of', location_ids), ('usage', '=', 'internal')],
+                context=context)
+        return location_ids
+
+    def confirm_uninventoried_location_wizard(self, cr, uid, ids, context=None):
+        """ Open wizard if inventory is exhautive """
+        for inventory in self.browse(cr, uid, ids, context=context):
+            if not inventory.exhaustive:
+                return self.action_confirm(cr, uid, ids, context=context)
+            else:
+                context['active_ids'] = ids
+                context['active_id'] = ids[0]
+                return {
+                    'type': 'ir.actions.act_window',
+                    'view_type': 'form',
+                    'view_mode': 'form',
+                    'res_model': 'stock.inventory.uninventoried.locations',
+                    'target': 'new',
+                    'context': context,
+                    'nodestroy': True,
+                    }
+
+    # XXX : get from stock.py v6.0 patch. Waiting for integration on standard by openerp ... 
+    def _fill_location_lines(self, cr, uid, inventory_id, location_ids, set_stock_zero, context=None):
+        """ To Import stock inventory according to products available in the selected locations.
+        @param self: The object pointer.
+        @param cr: A database cursor
+        @param uid: ID of the user currently logged in
+        @param location_ids: the location ID or list of location IDs if we want more than one
+        @param inventory_id: the inventory ID
+        @param set_stock_zero: indicate if all the lines will be imported with zero quantity
+        @param context: A standard dictionary
+        @return:
+        """
+        inventory_line_obj = self.pool.get('stock.inventory.line')
+        move_obj = self.pool.get('stock.move')
+        uom_obj = self.pool.get('product.uom')
+        res = []
+        flag = False
+        for location in location_ids:
+            datas = {}
+            move_ids = move_obj.search(cr, uid, ['|', ('location_dest_id', '=', location),
+                                                      ('location_id', '=', location),
+                                                      ('state', '=', 'done')], context=context)
+            for move in move_obj.browse(cr, uid, move_ids, context=context):
+                if move.location_dest_id.id == move.location_id.id:
+                    continue
+                lot_id = move.prodlot_id.id
+                prod_id = move.product_id.id
+                if move.location_dest_id.id == location:
+                    qty = uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_qty, move.product_id.uom_id.id)
+                else:
+                    qty = -uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_qty, move.product_id.uom_id.id)
+                if datas.get((prod_id, lot_id)):
+                    qty = qty + datas[(prod_id, lot_id)]['product_qty']
+
+                datas[(prod_id, lot_id)] = {'product_id': prod_id,
+                                            'location_id': location,
+                                            # Floating point sum could introduce some tiny rounding errors. 
+                                            # The uom are the same on input and output to use api for rounding.
+                                            'product_qty': uom_obj._compute_qty_obj(cr, uid, move.product_id.uom_id, qty, move.product_id.uom_id),
+                                            'product_uom': move.product_id.uom_id.id,
+                                            'prod_lot_id': lot_id,
+                                            'default_code': move.product_id.default_code,
+                                            'prodlot_name': move.prodlot_id.name}
+            if datas:
+                flag = True
+                res.append(datas)
+        if not flag:
+            raise osv.except_osv(_('Warning !'), _('No product in this location.'))
+
+        stock_moves = []
+        for stock_move in res:
+            prod_lots = sorted(stock_move, key=lambda k: (stock_move[k]['default_code'], stock_move[k]['prodlot_name']))
+            for prod_lot in prod_lots:
+                stock_move_details = stock_move.get(prod_lot)
+
+                if stock_move_details['product_qty'] == 0:
+                    continue  # ignore product if stock equal 0
+
+                stock_move_details.update({'inventory_id': inventory_id})
+
+                if set_stock_zero:
+                    stock_move_details.update({'product_qty': 0})
+
+                domain = [(field, '=', stock_move_details[field])
+                           for field in ['location_id',
+                                         'product_id',
+                                         'prod_lot_id']
+                          ]
+                # children_inventory_ids is present if stock_inventory_hierarchical_location module has been installed
+                inventory_ids = context.get('children_inventory_ids', False)
+                if inventory_ids:
+                    domain.append(('inventory_id', 'child_of', inventory_ids))
+                else:
+                    domain.append(('inventory_id', '=', stock_move_details['inventory_id']))
+
+                line_ids = inventory_line_obj.search(cr, uid, domain, context=context)
+                if not line_ids:
+                    stock_moves.append(stock_move_details)
+        return stock_moves
+
+
+class StockInventoryLine(osv.osv):
+    """Only allow the Inventory's Locations"""
+
+    _inherit = 'stock.inventory.line'
+
+    def onchange_location_id(self, cr, uid, ids, location_ids, exhaustive, location_id, context=None):
+        """ Raise exception if Location is not internal, or location_id not in locations list for this inventory """
+        location_ids = location_ids[0][2]
+        if not exhaustive or not location_ids:
+            return True  # don't check if partial inventory
+
+        # search children of location
+        location_ids = self.pool.get('stock.location').search(cr, uid,
+                        [('location_id', 'child_of', location_ids)], context=context)
+        if location_id not in location_ids:
+            return {'value': {'location_id': False},
+                    'warning': {'title': _('Warning: Wrong location'),
+                                'message': _("You cannot add this location to inventory line.\n"
+                                             "You must add this location on the locations list")}
+                    }
+        return True
+
+
+class StockLocation(osv.osv):
+    """Refuse changes during exhaustive Inventories"""
+    _inherit = 'stock.location'
+    _order = 'name'
+
+    def _check_inventory(self, cr, uid, ids, context=None):
+        """Raise an error if an exhaustive Inventory is being conducted on this Location"""
+        inventory_obj = self.pool.get('stock.inventory')
+        location_inventory_open_ids = inventory_obj._get_locations_open_inventories(cr, uid, context=context)
+        if not isinstance(ids, Iterable):
+            ids = [ids]
+        for id in ids:
+            if id in location_inventory_open_ids:
+                raise osv.except_osv(_('Error! Location on inventory'),
+                                     _('A Physical Inventory is being conducted at this location'))
+        return True
+
+    def write(self, cr, uid, ids, vals, context=None):
+        """Refuse write if an inventory is being conducted"""
+        self._check_inventory(cr, uid, ids, context=context)
+        if not isinstance(ids, Iterable):
+            ids = [ids]
+        ids_to_check = ids
+        # If we are changing the parent, there must be no inventory must conducted there either
+        if  vals.get('location_id'):
+            ids_to_check.append(vals['location_id'])
+        self._check_inventory(cr, uid, ids_to_check, context=context)
+        return super(StockLocation, self).write(cr, uid, ids, vals, context=context)
+
+    def create(self, cr, uid, vals, context=None):
+        """Refuse create if an inventory is being conducted at the parent"""
+        self._check_inventory(cr, uid, vals.get('location_id'), context=context)
+        return super(StockLocation, self).create(cr, uid, vals, context=context)
+
+    def unlink(self, cr, uid, ids, context=None):
+        """Refuse unlink if an inventory is being conducted"""
+        self._check_inventory(cr, uid, ids, context=context)
+        return super(StockLocation, self).unlink(cr, uid, ids, context=context)
+
+
+class StockMove(osv.osv):
+    """Refuse Moves during exhaustive Inventories"""
+
+    _inherit = 'stock.move'
+
+    def _check_open_inventory_location(self, cr, uid, ids, context=None):
+        """ Check if location is not in opened inventory
+        Don't check on partial inventory (checkbox "Exhaustive" not checked).
+        """
+        message = ""
+        inventory_obj = self.pool.get('stock.inventory')
+        location_inventory_open_ids = inventory_obj._get_locations_open_inventories(cr, uid, context=context)
+        if not location_inventory_open_ids:
+            return True  # Nothing to verify
+        for move in self.browse(cr, uid, ids, context=context):
+            if (move.location_id.usage != 'inventory'
+                and move.location_dest_id.id in location_inventory_open_ids):
+                message += " - %s\n" % (move.location_dest_id.name)
+
+            if (move.location_dest_id.usage != 'inventory'
+                and move.location_id.id in location_inventory_open_ids):
+                message += " - %s\n" % (move.location_id.name)
+        if message:
+            raise osv.except_osv(_('Error! Location on inventory'),
+                                 _('One or more locations are inventoried :\n%s') % message)
+        return True
+
+    _constraints = [
+                    (_check_open_inventory_location,
+                     "A Physical Inventory is being conducted at this location", ['location_id', 'location_dest_id']),
+                   ]

=== added file 'stock_inventory_location/stock_inventory_location_demo.xml'
--- stock_inventory_location/stock_inventory_location_demo.xml	1970-01-01 00:00:00 +0000
+++ stock_inventory_location/stock_inventory_location_demo.xml	2014-03-12 15:23:02 +0000
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data noupdate="0">
+
+        <!-- Record inventories we can use in the tests. -->
+        <!-- We need them in the demo data because test data is rolled back 
+            whenever an exception is raised. -->
+        
+        <!-- Record an non exhaustive inventory -->
+        <record id="stock_inventory_location_0" model="stock.inventory">
+            <field name="name">Location test inventory</field>
+            <field name="state">draft</field>
+            <field name="date">2020-03-01 00:00:00</field>          
+        </record>        
+        
+        <!-- Record an exhaustive inventory -->
+        <record id="stock_inventory_location_1" model="stock.inventory">
+            <field name="name">Location test exhaustive inventory</field>
+            <field name="state">draft</field>
+            <field name="date">2020-03-15 00:00:00</field>
+            <field name="exhaustive">True</field>          
+            <field name="location_ids"  model="stock.location" search="[('name', '=', 'Shelf 2')]" />
+        </record>
+    </data>
+</openerp>

=== added file 'stock_inventory_location/stock_inventory_location_view.xml'
--- stock_inventory_location/stock_inventory_location_view.xml	1970-01-01 00:00:00 +0000
+++ stock_inventory_location/stock_inventory_location_view.xml	2014-03-12 15:23:02 +0000
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+           
+        <record model="ir.ui.view" id="stock_inventory_location_form_view">
+            <field name="name">stock.inventory.location.form</field>
+            <field name="model">stock.inventory</field>
+            <field name="inherit_id" ref="stock.view_inventory_form"/>
+            <field name="priority" eval="10"/>
+            <field name="arch" type="xml">
+                <!-- Add type of inventory : partial or complete. -->
+                <xpath expr="/form//field[@name='date']" position="after">
+                    <field name="exhaustive"/>
+                </xpath>
+
+                <!-- Add the list of Locations on exhaustive inventories --> 
+                <xpath expr="/form//field[@name='inventory_line_id']" position="before">
+                    <!-- Add the locations list for inventory -->
+                    <field colspan="1" nolabel="1" name="location_ids" domain="[('usage','in',('view', 'internal'))]" attrs="{'invisible':[('exhaustive','!=',True)]}">
+                        <tree string="Locations" editable="bottom">
+                            <field name="name"/>
+                        </tree>
+                    </field>
+                </xpath>                    
+
+				<!-- Add Fill Inventory button when state is open -->
+				<xpath expr="//button[@string='Fill Inventory']" position="attributes">
+					<attribute name="states">draft,open,confirm</attribute>
+				</xpath>
+
+                <!-- Reduce the colspan of the lines to make room for the Locations-->
+                <xpath expr="/form//field[@name='inventory_line_id']" position="attributes">
+                    <attribute name="colspan">3</attribute>
+                </xpath>                
+
+                <!-- Control locations added by user on inventory line -->  
+                <xpath expr="/form//field[@name='inventory_line_id']/tree//field[@name='location_id']" position="attributes">
+                    <attribute name="on_change">onchange_location_id(parent.location_ids, parent.exhaustive, location_id)</attribute>
+                </xpath>
+                <xpath expr="/form//field[@name='inventory_line_id']/form//field[@name='location_id']" position="attributes">
+                    <attribute name="on_change">onchange_location_id(parent.location_ids, parent.exhaustive, location_id)</attribute>
+                </xpath>
+                                
+                <!-- #XXX change the attributes  instead and add the button -->
+                <!-- #XXX enlarge the group's colspan? -->
+                <!-- Add button to open an inventory. Call wizard on confirm inventory -->
+                <xpath expr="/form//button[@name='action_cancel_inventory']" position="replace">
+                    <button name="action_cancel_inventory" states="draft,open,confirm,done" string="Cancel Inventory" type="object" icon="gtk-cancel"/>
+                    <button name="action_open" states="draft" string="Open Inventory" type="object" icon="gtk-go-forward"/>
+                </xpath>
+                
+                <!-- replace action_confirm button  -->
+                <!-- #XXX change the attributes instead -->
+                <xpath expr="/form//button[@name='action_confirm']" position="replace">
+                      <button name="confirm_uninventoried_location_wizard" 
+                      string="Confirm Inventory" type="object" states="open" icon="gtk-apply"/>                          
+                </xpath>
+            </field>
+        </record>
+
+        <!-- Add filter for complete or partial inventory -->
+        <record model="ir.ui.view" id="view_inventory_complete_filter">
+            <field name="name">complete.inventory.filter</field>
+            <field name="model">stock.inventory</field>
+            <field name="type">search</field>
+            <field name="inherit_id" ref="stock.view_inventory_filter"/>
+            <field name="arch" type="xml">
+                <xpath expr="//field[@name='name']" position="before">
+                    <filter icon="terp-check" name="exhaustive" string="Exhaustive" domain="[('exhaustive', '=', True)]" help="Only select inventories that have no parents." />
+                    <separator orientation="vertical"/>
+                </xpath>
+            </field>
+        </record>
+        
+        <!-- Show exhaustive inventories by default -->
+        <record id="stock.action_inventory_form" model="ir.actions.act_window">
+            <field name="context">{'full':'1', 'search_default_exhaustive':1}</field>
+        </record>
+
+	</data>
+</openerp>
\ No newline at end of file

=== added directory 'stock_inventory_location/test'
=== added file 'stock_inventory_location/test/location_exhaustive_inventory_test.yml'
--- stock_inventory_location/test/location_exhaustive_inventory_test.yml	1970-01-01 00:00:00 +0000
+++ stock_inventory_location/test/location_exhaustive_inventory_test.yml	2014-03-12 15:23:02 +0000
@@ -0,0 +1,101 @@
+-
+  This file will test an exhaustive inventory.
+  I will call open_action method and check if state of inventories are 'open'.
+-
+  !python {model: stock.inventory}: |
+    from osv import orm, osv     
+    self.action_open(cr, uid, [ref('stock_inventory_location_1')])    
+    inventory_state = self.read(cr, uid, [ref('stock_inventory_location_1')], ['state'])[0]['state']
+    assert inventory_state == 'open', "Parent inventory have '%s' state. It should be 'open'" % inventory_state
+
+-
+  In order, i add products to exhaustive inventory.
+  Don't add mou(product.product_product_25) and keya (product.product_product_24) products.
+  Adding pc1.
+-    
+  !record {model: stock.inventory.line, id: lines_inventory_location_pc1}:
+    product_id: product.product_product_10
+    product_uom: product.product_uom_unit
+    company_id: base.main_company
+    inventory_id: stock_inventory_location_1    
+    product_qty: 18.0
+    location_id: stock.stock_location_14
+
+-
+  Adding pc3.
+-      
+  !record {model: stock.inventory.line, id: lines_inventory_location_pc3}:
+    product_id: product.product_product_pc3
+    product_uom: product.product_uom_unit
+    company_id: base.main_company
+    inventory_id: stock_inventory_location_1    
+    product_qty: 5.0
+    location_id: stock.stock_location_14    
+
+-
+  I will call the function _get_locations_open_inventories and check the result.
+  The function will return only the location_ids of the exhaustive inventory.
+-
+  !python {model: stock.inventory}: |
+    from osv import orm, osv
+    locations = self._get_locations_open_inventories(cr, uid)    
+    assert len(locations) == 1, "Function return wrong results : %s" % locations
+    assert locations[0] == ref('stock.stock_location_14'), "Function '_get_locations_open_inventories' return wrong location_id. Should be '%s' : '%s'" % (stock.stock_location_14, locations[0]) 
+  
+-  
+  I will call the function onchange_location_id.
+  The function must to return true in the first case, and return a warning dictionnary in the second test.  
+-
+  !python {model: stock.inventory.line}: |
+    from osv import orm, osv
+    res = self.onchange_location_id(cr, uid, [], [(6,0,[ref('stock.stock_location_14')])], True, ref('stock.stock_location_14'))
+    assert res == True, "Exhaustive : The function 'onchange_location_id' should return True and return '%s'" % res    
+    res = self.onchange_location_id(cr, uid, [], [(6,0,[ref('stock.stock_location_14')])], True, ref('stock.stock_location_components'))
+    assert res.get('warning', False) != False , "Function 'onchange_location_id' : Warning not raise. ('%s)" % res    
+
+-
+  I will call _fill_location_lines to simulate confirmation for stock_confirm_uninventoried_location,
+  and create stock_moves
+-
+  !python {model: stock.inventory}: |
+    from osv import orm, osv
+    ctx={'location': [ref('stock.stock_location_14')]}      
+    lines = self._fill_location_lines(cr, uid, ref('stock_inventory_location_1'), [ref('stock.stock_location_14')], True, context=ctx)
+    for line in lines:
+      self.pool.get('stock.inventory.line').create(cr, uid, line, context=context)    
+    
+-
+  I will confirm exhaustive inventory 
+-    
+  !python {model: stock.inventory}: |
+    from osv import orm, osv     
+    self.action_confirm(cr, uid, [ref('stock_inventory_location_1')])    
+    inventory_state = self.read(cr, uid, [ref('stock_inventory_location_1')], ['state'])[0]['state']
+    assert inventory_state == 'confirm', "Exhaustive inventory have '%s' state. It should be 'confirm'" % inventory_state
+              
+-
+  I will validate exhaustive inventory
+-
+  !python {model: stock.inventory}: |
+    from osv import orm, osv   
+    self.action_done(cr, uid, [ref('stock_inventory_location_1')])
+    inventory_state = self.read(cr, uid, [ref('stock_inventory_location_1')], ['state'])[0]['state']
+    assert inventory_state == 'done', "Exhaustive inventory have '%s' state. It should be 'done'" % inventory_state
+       
+-
+  I will verify the quantity for each products.
+-    
+  !python {model: product.product}: |
+    from osv import orm, osv 
+    ctx={'location': [ref('stock.stock_location_14')], 'to_date': '2020-12-31 23:59:59'} 
+    prod_qty_avail = self.read(cr, uid, [ref('product.product_product_10')], ['qty_available'], context=ctx)[0]['qty_available']
+    assert prod_qty_avail == 18.0, "The stock of PC1 was not set to 18.0 : %s" % prod_qty_avail
+    
+    prod_qty_avail = self.read(cr, uid, [ref('product.product_product_pc3')], ['qty_available'], context=ctx)[0]['qty_available']
+    assert prod_qty_avail == 5.0, "The stock of PC3 was not set to 5.0 : %s" % prod_qty_avail
+
+    prod_qty_avail = self.read(cr, uid, [ref('product.product_product_24')], ['qty_available'], context=ctx)[0]['qty_available']
+    assert prod_qty_avail == 0.0, "The stock of KEYA was not set to 0 : %s" % prod_qty_avail
+
+    prod_qty_avail = self.read(cr, uid, [ref('product.product_product_25')], ['qty_available'], context=ctx)[0]['qty_available']
+    assert prod_qty_avail == 0.0, "The stock of MOU was not set to 0 : %s" % prod_qty_avail

=== added file 'stock_inventory_location/test/location_inventory_test.yml'
--- stock_inventory_location/test/location_inventory_test.yml	1970-01-01 00:00:00 +0000
+++ stock_inventory_location/test/location_inventory_test.yml	2014-03-12 15:23:02 +0000
@@ -0,0 +1,109 @@
+-
+  This file will test a non exhaustive inventory.
+  I will call open_action method and check if state of inventories are 'open'.
+-
+  !python {model: stock.inventory}: |
+    from osv import orm, osv     
+    self.action_open(cr, uid, [ref('stock_inventory_location_0')])    
+    inventory_state = self.read(cr, uid, [ref('stock_inventory_location_0')], ['state'])[0]['state']
+    assert inventory_state == 'open', "Partial inventory have '%s' state. It should be 'open'" % inventory_state    
+   
+-
+  In order, I add products to inventory.
+  Adding cpu1.
+-
+  !record {model: stock.inventory.line, id: lines_inventory_location_cpu1}:
+    product_id: product.product_product_cpu1
+    product_uom: product.product_uom_unit
+    company_id: base.main_company
+    inventory_id: stock_inventory_location_0    
+    product_qty: 18.0
+    location_id: stock.stock_location_components
+
+-
+  Adding cpu3.
+-
+  !record {model: stock.inventory.line, id: lines_inventory_location_cpu3}:
+    product_id: product.product_product_cpu3
+    product_uom: product.product_uom_unit
+    company_id: base.main_company
+    inventory_id: stock_inventory_location_0    
+    product_qty: 12.0
+    location_id: stock.stock_location_components
+
+-
+  Adding fan.
+-
+  !record {model: stock.inventory.line, id: lines_inventory_location_fan}:
+    product_id: product.product_product_fan
+    product_uom: product.product_uom_unit
+    company_id: base.main_company
+    inventory_id: stock_inventory_location_0    
+    product_qty: 32.0
+    location_id: stock.stock_location_components
+
+-
+  I will call the function _get_locations_open_inventories and check the result.
+  The function will return no locations.
+-
+  !python {model: stock.inventory}: |
+    from osv import orm, osv
+    locations = self._get_locations_open_inventories(cr, uid)    
+    assert len(locations) == 0, "Function return wrong results : %s" % locations
+     
+-
+  I will call the function onchange_location_id.
+  The function must to return true in all test case.  
+-
+  !python {model: stock.inventory.line}: |
+    from osv import orm, osv
+    pass
+    res = self.onchange_location_id(cr, uid, [], [(6,0,[ref('stock.stock_location_components')])], False, ref('stock.stock_location_components'))
+    assert res == True, "The function 'onchange_location_id' should return True and return '%s'" % res
+    res = self.onchange_location_id(cr, uid, [], [(6,0,[ref('stock.stock_location_components')])], False, ref('stock.stock_location_14'))
+    assert res == True, "The function 'onchange_location_id' should return True and return '%s'" % res
+
+-
+  I will call the function _check_inventory.
+  The function must return True in all test cases.
+-
+  !python {model: stock.location}: |
+    from osv import orm, osv    
+    res = self._check_inventory(cr, uid, ref('stock.stock_location_components'))
+    assert res == True, "The function '_check_inventory' should return True and return '%s'" % res
+    res = self._check_inventory(cr, uid, ref('stock.stock_location_14'))
+    assert res == True, "The function '_check_inventory' should return True and return '%s'" % res
+        
+-
+  I will confirm inventory. 
+-    
+  !python {model: stock.inventory}: |
+    from osv import orm, osv     
+    self.action_confirm(cr, uid, [ref('stock_inventory_location_0')])    
+    inventory_state = self.read(cr, uid, [ref('stock_inventory_location_0')], ['state'])[0]['state']
+    assert inventory_state == 'confirm', "Partial inventory have '%s' state. It should be 'confirm'" % inventory_state
+
+-
+  I will validate inventory
+-
+  !python {model: stock.inventory}: |
+    from osv import orm, osv 
+    self.action_done(cr, uid, [ref('stock_inventory_location_0')])    
+    inventory_state = self.read(cr, uid, [ref('stock_inventory_location_0')], ['state'])[0]['state']
+    assert inventory_state == 'done', "Partial inventory have '%s' state. It should be 'done'" % inventory_state    
+       
+-
+  I will verify the quantity for each products.
+-    
+  !python {model: product.product}: |
+    from osv import orm, osv 
+    ctx={'location': [ref('stock.stock_location_components')], 'to_date': '2020-12-31 23:59:59'} 
+    prod_qty_avail = self.read(cr, uid, [ref('product.product_product_cpu1')], ['qty_available'], context=ctx)[0]['qty_available']
+    assert prod_qty_avail == 18.0, "The stock of CPU1 was not set to 18.0 : %s" % prod_qty_avail
+    
+    prod_qty_avail = self.read(cr, uid, [ref('product.product_product_cpu3')], ['qty_available'], context=ctx)[0]['qty_available']
+    assert prod_qty_avail == 12.0, "The stock of CPU3 was not set to 12.0 : %s" % prod_qty_avail
+    
+    prod_qty_avail = self.read(cr, uid, [ref('product.product_product_fan')], ['qty_available'], context=ctx)[0]['qty_available']
+    assert prod_qty_avail == 32.0, "The stock of FAN was not set to 32.0 : %s" % prod_qty_avail
+    
\ No newline at end of file

=== added directory 'stock_inventory_location/wizard'
=== added file 'stock_inventory_location/wizard/__init__.py'
--- stock_inventory_location/wizard/__init__.py	1970-01-01 00:00:00 +0000
+++ stock_inventory_location/wizard/__init__.py	2014-03-12 15:23:02 +0000
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-1
+##############################################################################
+#
+#    This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU 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 General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import stock_fill_location_inventory
+import stock_confirm_uninventoried_location

=== added file 'stock_inventory_location/wizard/stock_confirm_uninventoried_location.py'
--- stock_inventory_location/wizard/stock_confirm_uninventoried_location.py	1970-01-01 00:00:00 +0000
+++ stock_inventory_location/wizard/stock_confirm_uninventoried_location.py	2014-03-12 15:23:02 +0000
@@ -0,0 +1,95 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+#    This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU 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 General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from openerp.osv import fields, osv
+from openerp.tools.translate import _
+
+
+class stock_inventory_uninventoried_location(osv.osv_memory):
+    _name = 'stock.inventory.uninventoried.locations'
+    _description = 'Confirm the uninventoried Locations.'
+
+    _columns = {
+                'location_ids': fields.many2many('stock.location',
+                                                 'stock_inventory_uninventoried_location_rel',
+                                                 'location_id',
+                                                 'wizard_id',
+                                                 'Uninventoried location', readonly=True),
+                }
+
+    def get_locations(self, cr, uid, inventory_id, context=None):
+        """ Get all locations from inventory. """
+        location_ids = self.pool.get('stock.inventory').read(cr, uid, [inventory_id], ['location_ids'], context=context)[0]
+        return self.pool.get('stock.location').search(cr, uid, [
+                        ('location_id', 'child_of', location_ids['location_ids']),
+                        ('usage', '=', 'internal')], context=context)
+
+    def get_locations_inventoried(self, cr, uid, inventory_id, location_ids, context=None):
+        """ Get all locations on inventory lines. """
+        inventory_line_obj = self.pool.get('stock.inventory.line')
+        inventory_line_ids = inventory_line_obj.search(cr, uid, [('location_id', 'in', location_ids),
+                                                                 ('inventory_id', '=', inventory_id)], context=context)
+        inventory_line_locations_ids = inventory_line_obj.read(cr, uid, inventory_line_ids, ['location_id'], context=context)
+        return list(set([_id['location_id'][0] for _id in inventory_line_locations_ids]))
+
+    def default_locations(self, cr, uid, context=None):
+        """ Initialize view with the list of uninventoried locations.
+            Search for children of the location if exists.
+        """
+        if context is None:
+            context = {}
+        location_ids = self.get_locations(cr, uid, context['active_id'])
+        inventory_line_locations_ids = self.get_locations_inventoried(cr, uid, context['active_id'], location_ids)
+        return  [_id for _id in location_ids if _id not in inventory_line_locations_ids]
+
+    _defaults = {
+        'location_ids': default_locations,
+        }
+
+    def confirm_uninventoried_locations(self, cr, uid, ids, context=None):
+        """ Call action confirm method from stock.inventory """
+        inventory_ids = context['active_ids']
+        # call the wizard to add lines for uninventoried locations with zero quantity
+        inventory_obj = self.pool.get('stock.inventory')
+        if not isinstance(inventory_ids, list):
+            inventory_ids = [inventory_ids]
+
+        for inventory in inventory_obj.browse(cr, uid, inventory_ids, context=context):
+            if inventory.exhaustive:
+                location_ids = self.get_locations(cr, uid, inventory.id, context=context)
+                # get stock inventory lines
+                lines = []
+                try:
+                    # create inventory lines with zero qty
+                    lines = inventory_obj._fill_location_lines(cr, uid,
+                                                               inventory.id,
+                                                               location_ids,
+                                                               True,
+                                                               context=context)
+                except osv.except_osv as e:
+                    pass
+
+                for line in lines:
+                    self.pool.get('stock.inventory.line').create(cr, uid, line, context=context)
+
+        inventory_obj.action_confirm(cr, uid, inventory_ids, context=context)
+        return {'type': 'ir.actions.act_window_close'}
+
+

=== added file 'stock_inventory_location/wizard/stock_confirm_uninventoried_location.xml'
--- stock_inventory_location/wizard/stock_confirm_uninventoried_location.xml	1970-01-01 00:00:00 +0000
+++ stock_inventory_location/wizard/stock_confirm_uninventoried_location.xml	2014-03-12 15:23:02 +0000
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        <!-- The view definition is similar with stock_inventory_hierarchical_location/wizard/stock_inventory_missing_locations_view.xml, 
+        but the code is different. 
+        This wizard compare declared locations with locations on inventory lines to present the uninventoried (empty) locations to user. -->
+        <record id="view_confirm_uninventoried_location" model="ir.ui.view">
+            <field name="name">stock.inventory.uninventoried.locations.form</field>
+            <field name="model">stock.inventory.uninventoried.locations</field>
+            <field name="type">form</field>
+            <field name="arch" type="xml">
+                <form string="Confirm uninventoried locations">
+                    <group colspan="4" col="1">
+                        <label string="The following Stock Locations are part of the current Physical Inventory, but not Inventory Line has been recorded for them."/>
+                        <field name="location_ids" nolabel="1">
+                            <tree>
+                                <field name="name"/>
+                            </tree>
+                        </field>
+                        <label string="It could either mean that the Locations are empty, or that the Inventory is not yet complete."/>
+                        <label string="If you confirm the Inventory, these Locations will be considered empty and their content will be purged."/>
+                        <separator string=""/>
+                    </group>
+                    <group colspan="4" col="2">
+                        <button special="cancel" string="Cancel" icon="gtk-cancel" default_focus="1" />
+                        <button name="confirm_uninventoried_locations" string="Purge contents and confirm Inventory" type="object" icon="gtk-ok"/>                        
+                    </group>
+                </form>
+            </field>
+        </record>
+
+	</data>
+</openerp>
\ No newline at end of file

=== added file 'stock_inventory_location/wizard/stock_fill_location_inventory.py'
--- stock_inventory_location/wizard/stock_fill_location_inventory.py	1970-01-01 00:00:00 +0000
+++ stock_inventory_location/wizard/stock_fill_location_inventory.py	2014-03-12 15:23:02 +0000
@@ -0,0 +1,97 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU 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 General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from openerp.osv import fields, osv
+from openerp.tools.translate import _
+from collections import OrderedDict
+
+
+class stock_fill_location_inventory(osv.osv_memory):
+    _inherit = 'stock.fill.inventory'
+
+    _columns = {
+         'location_id': fields.many2one('stock.location', 'Location'),
+         #'exhaustive': fields.boolean('stock.inventory', 'Type'),
+         'exhaustive': fields.boolean('stock.inventory'),
+         }
+
+    def get_inventory_type(self, cr, uid, context=None):
+        if context.get('active_id', False):
+            inventory_obj = self.pool.get('stock.inventory')
+            exhaustive = inventory_obj.read(cr, uid, [context.get('active_id')], ['exhaustive'], context=context)[0]['exhaustive']
+            return exhaustive
+        return False
+
+    _defaults = {
+        'exhaustive': get_inventory_type,
+        }
+
+    def view_init(self, cr, uid, fields_list, context=None):
+        """ inherit from original to add multiple selection of location
+        and exclude from location list the locations already choosen by another inventory """
+        if context is None:
+            context = {}
+
+        inventory_obj = self.pool.get('stock.inventory')
+        inventory_state = inventory_obj.read(cr, uid, [context.get('active_id')], ['state'], context=context)[0]
+        if inventory_state['state'] != 'open':
+            raise osv.except_osv(_('Error !'),
+                                 _('the inventory must be in "Open" state.'))
+
+        nb_inventory = inventory_obj.search(cr, uid, [('id', '=', context.get('active_id'))], count=True, context=context)
+        if nb_inventory == 0:
+            raise osv.except_osv(_('Warning !'),
+                                 _('No locations found for the inventory.'))
+
+        return super(stock_fill_location_inventory, self).view_init(cr, uid, fields_list, context=context)
+
+    def fill_inventory(self, cr, uid, ids, context=None):
+        """ Fill the inventory only with open locations on the inventory.
+        """
+        if context is None:
+            context = {}
+
+        fill_inventory = self.browse(cr, uid, ids[0], context=context)
+        if not fill_inventory.exhaustive:
+            return super(stock_fill_location_inventory, self).fill_inventory(cr, uid, ids, context=context)  # call standard wizard
+
+        location_ids = self.pool.get('stock.inventory').read(cr, uid, [context.get('active_id')], ['location_ids'])[0]
+
+        if not location_ids['location_ids']:
+            raise osv.except_osv(_('Error : Empty location !'), _('No location to import.\nYou must add a location on the locations list.'))
+
+        if fill_inventory.recursive:
+            location_ids = self.pool.get('stock.location').search(cr, uid, [('location_id', 'child_of', location_ids['location_ids']),
+                                                                            ('usage', '=', 'internal')], context=context)
+        else:
+            location_ids = location_ids['location_ids']
+
+        location_ids = list(OrderedDict.fromkeys(location_ids))
+
+        lines = self.pool.get('stock.inventory')._fill_location_lines(cr, uid,
+                                                   context['active_ids'][0],
+                                                   location_ids,
+                                                   fill_inventory.set_stock_zero,
+                                                   context=context)
+
+        inventory_lines_obj = self.pool.get('stock.inventory.line')
+        for line in lines:
+            inventory_lines_obj.create(cr, uid, line, context=context)
+        return {'type': 'ir.actions.act_window_close'}

=== added file 'stock_inventory_location/wizard/stock_fill_location_inventory_view.xml'
--- stock_inventory_location/wizard/stock_fill_location_inventory_view.xml	1970-01-01 00:00:00 +0000
+++ stock_inventory_location/wizard/stock_fill_location_inventory_view.xml	2014-03-12 15:23:02 +0000
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        <record id="view_stock_fill_inventory_location" model="ir.ui.view">
+            <field name="name">Import Inventory</field>
+            <field name="model">stock.fill.inventory</field>
+            <field name="inherit_id" ref="stock.view_stock_fill_inventory" />
+            <field name="arch" type="xml">
+                <xpath expr="//field[@name='location_id']" position="after">
+                    <field name="exhaustive" invisible="1" />
+                </xpath>
+                <xpath expr="//field[@name='location_id']" position="attributes">
+                    <attribute name="attrs">{'invisible':[('exhaustive','=',True)]}</attribute>
+                </xpath>
+            </field>
+        </record>
+    </data>
+</openerp>


Follow ups