← Back to team overview

openerp-community-reviewer team mailing list archive

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

 

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

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-hierarchical/+merge/210631

This proposal lets users compose inventories with sub-inventories in 
a hierarchical tree. 

This is useful in medium/large warehouses where 
several teams must work in parallel to make inventories.

The module stock_inventory_hierarchical_location depends of stock_inventory_hierarchical and
stock_inventory_location (lp:~numerigraphe-team/stock-logistic-warehouse/7.0-inventory-location).


-- 
https://code.launchpad.net/~numerigraphe-team/stock-logistic-warehouse/7.0-inventory-hierarchical/+merge/210631
Your team Stock and Logistic Core Editors is requested to review the proposed merge of lp:~numerigraphe-team/stock-logistic-warehouse/7.0-inventory-hierarchical into lp:stock-logistic-warehouse.
=== added directory 'stock_inventory_hierarchical'
=== added file 'stock_inventory_hierarchical/__init__.py'
--- stock_inventory_hierarchical/__init__.py	1970-01-01 00:00:00 +0000
+++ stock_inventory_hierarchical/__init__.py	2014-03-12 15:27:02 +0000
@@ -0,0 +1,21 @@
+# -*- 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 hierarchical_inventory

=== added file 'stock_inventory_hierarchical/__openerp__.py'
--- stock_inventory_hierarchical/__openerp__.py	1970-01-01 00:00:00 +0000
+++ stock_inventory_hierarchical/__openerp__.py	2014-03-12 15:27:02 +0000
@@ -0,0 +1,41 @@
+# -*- 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": "Hierarchical Physical Inventory",
+    "version": "1.0",
+    "depends": ["stock"],
+    "author": "Numérigraphe",
+    "category": "stock inventory",
+    "description": """
+Hierarchical structure for Physical Inventories and sub-Inventories
+===================================================================
+
+This module adds a parent-child relationship between Physical Inventories, to help users
+manage complex inventories.
+Using several inventories, you can distribute the counting to several persons
+and still keep a clear overview of global Inventory's status.
+
+OpenERP will make sure the status of the Inventory and it's sub-Inventories are consistent.
+""",
+    "update_xml": ["hierarchical_inventory_view.xml"],
+     "test": ["test/hierarchical_inventory_test.yml"],
+     "demo": ["hierarchical_inventory_demo.xml"]
+}

=== added file 'stock_inventory_hierarchical/hierarchical_inventory.py'
--- stock_inventory_hierarchical/hierarchical_inventory.py	1970-01-01 00:00:00 +0000
+++ stock_inventory_hierarchical/hierarchical_inventory.py	2014-03-12 15:27:02 +0000
@@ -0,0 +1,200 @@
+# -*- 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 osv, fields
+from openerp.tools.translate import _
+
+
+class stock_inventory_hierarchical(osv.osv):
+    _inherit = 'stock.inventory'
+
+    _parent_store = True
+    _parent_order = 'date, name'
+    _order = 'parent_left'
+
+    def name_get(self, cr, uid, ids, context=None):
+        """Show the parent inventory's name in the name of the children"""
+        if isinstance(ids, (list, tuple)) and not len(ids):
+            return []
+        if isinstance(ids, (long, int)):
+            ids = [ids]
+        reads = self.read(cr, uid, ids, ['name', 'parent_id'], context=context)
+        res = []
+        for record in reads:
+            name = record['name']
+            if record['parent_id']:
+                name = record['parent_id'][1] + ' / ' + name
+            res.append((record['id'], name))
+        return res
+
+    def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=100):
+        """Allow search on value returned by name_get ("parent/ child")"""
+        if not args:
+            args = []
+        if not context:
+            context = {}
+        if name:
+            # Be sure name_search is symetric to name_get
+            name = name.split(' / ')[-1]
+            ids = self.search(cr, uid, [('name', operator, name)] + args, limit=limit, context=context)
+        else:
+            ids = self.search(cr, uid, args, limit=limit, context=context)
+        return self.name_get(cr, uid, ids, context=context)
+
+    def _name_get_fnc(self, cr, uid, ids, field_name, arg, context=None):
+        """Function field containing the long name"""
+        res = self.name_get(cr, uid, ids, context=context)
+        return dict(res)
+
+    def _confirmed_rate(self, cr, uid, ids, field_name, arg, context=None):
+        """Number of (sub)inventories confirmed/total"""
+        rates = {}
+        for id in ids:
+            nb = self.search(cr, uid, [('parent_id', 'child_of', id)], context=context, count=True)
+            nb_confirmed = self.search(cr, uid, [('parent_id', 'child_of', id),
+                                                 ('state', 'in', ('confirm', 'done'))], context=context, count=True)
+            rates[id] = 100 * nb_confirmed / nb
+        return rates
+
+    _columns = {
+        # XXX remove "method=True" in v7 ?
+        'complete_name': fields.function(_name_get_fnc, method=True, type="char", string='Complete reference'),
+        'parent_id': fields.many2one('stock.inventory', 'Parent', ondelete='cascade', readonly=True, states={'draft': [('readonly', False)]}),
+        'inventory_ids': fields.one2many('stock.inventory', 'parent_id', 'List of Sub-inventories', readonly=True, states={'draft': [('readonly', False)]}),
+        'parent_left': fields.integer('Parent Left', select=1),
+        'parent_right': fields.integer('Parent Right', select=1),
+        'confirmed_rate': fields.function(_confirmed_rate, method=True, string='Confirmed', type='float'),
+        }
+
+    # XXX: drop this in v7
+    def _check_recursion(self, cr, uid, ids, context=None, parent=None):
+        """Backport of osv.osv._check_recursion from v7.0, to allow writing parents and children in the same write()"""
+        if not parent:
+            parent = self._parent_name
+
+        # must ignore 'active' flag, ir.rules, etc. => direct SQL query
+        query = 'SELECT "%s" FROM "%s" WHERE id = %%s' % (parent, self._table)
+        for id in ids:
+            current_id = id
+            while current_id is not None:
+                cr.execute(query, (current_id,))
+                result = cr.fetchone()
+                current_id = result[0] if result else None
+                if current_id == id:
+                    return False
+        return True
+
+    # XXX: use this in v7
+    # _constraints = [(osv.osv._check_recursion, 'Error! You can not create recursive inventories.', ['parent_id']), ]
+    _constraints = [
+        (_check_recursion,
+         _('Error! You can not create recursive inventories.'), ['parent_id']),
+    ]
+
+    # This is the list of fields that must be forced from Inventories to Sub-Inventories 
+    # TODO: propose this as a new feature of the ORM's API using (using a field named _parent_values for example)
+    PARENT_VALUES = ['date']
+
+# XXX: Ideally we would have liked to have a button to open Sub-inventories,
+# but unfortunately the v6.0 GTK client crashes, and the 6.0 web client opens a windows without action buttons.
+# Maybe we may try that again with the new web client one day... 
+#     def open_sub_inventory(self, cr, uid, id, context=None):
+#         """Method to open Sub-inventory from one2many list on new tab, with specific view."""
+#         # Find out the form view id
+#         if not isinstance(id, list):
+#             id = [id]
+#         id = id[0]
+#         res = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'stock', 'view_inventory_form')
+#         view_id = res and res[1] or False
+#         inv = self.browse(cr, uid, id, context=context)
+#         return {
+#             'type': 'ir.actions.act_window',
+#             'name': _("Sub-inventory : %s") % inv.name,
+#             'view_type': 'form',
+#             'view_mode': 'form',
+#             'view_id': [view_id],
+#             'res_model': 'stock.inventory',
+#             'res_id': id,
+#         }
+#         return True
+
+    # TODO: propose this as a new feature of the ORM's API using (using a field named _parent_values for example)
+    def create(self, cr, user, vals, context=None):
+        """Copy selected values from parent to child"""
+        if vals and vals.get('parent_id'):
+            for f in self.PARENT_VALUES:
+                parent_field = self.read(cr, user, [vals['parent_id']], [f], context=context)
+                vals = vals.copy()
+                vals[f] = parent_field[0][f]
+        return super(stock_inventory_hierarchical, self).create(cr, user, vals, context=context)
+
+    # TODO: propose this as a new feature of the ORM's API using (using a field named _parent_values for example)
+    def write(self, cr, uid, ids, vals, context=None):
+        """Copy selected values from parent to children"""
+        if context is None:
+            context = {}
+
+        values = super(stock_inventory_hierarchical, self).write(cr, uid, ids, vals, context=context)
+        if not vals or context.get('norecurs'):
+            return values
+
+        record = {}
+        for f in self.PARENT_VALUES:
+            if f in vals:
+                record[f] = vals[f]
+        if not record:
+            return values
+
+        if not isinstance(ids, list):
+            ids = [ids]
+        children_ids = self.search(cr, uid, [('parent_id', 'child_of', ids)])
+        ctx = context.copy()
+        ctx['norecurs'] = True  # needed to write children once.
+        return self.write(cr, uid, children_ids, record, context=ctx)
+
+    def action_cancel_inventory(self, cr, uid, ids, context=None):
+        """Cancel inventory only if all the parents are canceled"""
+        inventories = self.browse(cr, uid, ids, context=context)
+        for inventory in inventories:
+            while inventory.parent_id:
+                inventory = inventory.parent_id
+                if inventory.state != 'cancel':
+                    raise osv.except_osv(_('Warning !'), 
+                                         _('One of the parent Inventories is not canceled.'))
+        return super(stock_inventory_hierarchical, self).action_cancel_inventory(cr, uid, ids, context=context)
+
+    def action_confirm(self, cr, uid, ids, context=None):
+        """Confirm inventory only if all the children are confirmed"""
+        children_count = self.search(cr, uid, [('parent_id', 'child_of', ids),
+                                             ('state', 'not in', ['confirm', 'done'])], context=context, count=True)
+        if children_count > 1:
+            raise osv.except_osv(_('Warning !'),
+                                 _('Some Sub-inventories are not confirmed.'))
+        return super(stock_inventory_hierarchical, self).action_confirm(cr, uid, ids, context=context)
+
+    def action_done(self, cr, uid, ids, context=None):
+        """Perform validation only if all the children states are 'done'."""
+        children_count = self.search(cr, uid, [('parent_id', 'child_of', ids),
+                                               ('state', '!=', 'done')],
+                                               context=context, count=True)
+        if children_count > 1:
+            raise osv.except_osv(_('Warning !'),
+                                 _('Some Sub-inventories are not done.'))
+        return super(stock_inventory_hierarchical, self).action_done(cr, uid, ids, context=context)

=== added file 'stock_inventory_hierarchical/hierarchical_inventory_demo.xml'
--- stock_inventory_hierarchical/hierarchical_inventory_demo.xml	1970-01-01 00:00:00 +0000
+++ stock_inventory_hierarchical/hierarchical_inventory_demo.xml	2014-03-12 15:27:02 +0000
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data noupdate="0">
+
+        <!-- Record a production lot we can use in the tests. -->
+        <record id="lot_test0" model="stock.production.lot">
+            <field name="product_id" ref="product.product_product_10" />
+        </record>
+
+        <!-- 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 inventory to make sure the next one will have stock moves posted. -->
+        <record id="stock_inventory_parent0" model="stock.inventory">
+            <field name="name">Parent test inventory</field>
+            <field name="state">draft</field>
+            <field name="date">2020-01-01 00:00:00</field>
+        </record>        
+        <record id="stock_inventory_line" model="stock.inventory.line">
+            <field name="inventory_id" ref="stock_inventory_parent0" />
+            <field name="product_id" ref="product.product_product_10" />
+            <field name="prod_lot_id" ref="lot_test0"/>
+            <field name="product_uom" ref="product.product_uom_unit" />
+            <field name="product_qty">27.0</field>
+            <field name="location_id" ref="stock.stock_location_components" />
+        </record>        
+
+    </data>
+</openerp>

=== added file 'stock_inventory_hierarchical/hierarchical_inventory_view.xml'
--- stock_inventory_hierarchical/hierarchical_inventory_view.xml	1970-01-01 00:00:00 +0000
+++ stock_inventory_hierarchical/hierarchical_inventory_view.xml	2014-03-12 15:27:02 +0000
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        <!-- Add parent_id and number of Sub-inventories to form view -->
+        <record model="ir.ui.view" id="stock_inventory_hierarchical_tree_view">
+            <field name="name">hierarchical.inventory.tree</field>
+            <field name="model">stock.inventory</field>
+            <field name="type">tree</field>
+            <field name="inherit_id" ref="stock.view_inventory_tree" />
+            <field name="field_parent">inventory_ids</field>
+            <field name="arch" type="xml">
+                <xpath expr="//field[@name='name']" position="replace">
+                    <field name="complete_name"/>
+                </xpath>
+                <xpath expr="//field[@name='state']" position="after">
+                    <field name="confirmed_rate" widget="progressbar" />
+                    <field name="inventory_ids" string="Number of Sub-inventories" />
+                </xpath>
+            </field>
+        </record>
+
+        <!-- Add the parent_id filter to search view  -->
+        <record model="ir.ui.view" id="view_inventory_subinventories_filter">
+            <field name="name">hierarchical.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="main_inventories" string="Main inventories" domain="[('parent_id', '=', False)]" help="Only select inventories that have no parents." />
+                    <separator orientation="vertical"/>
+                </xpath>
+                <xpath expr="//field[@name='date']" position="after">
+                    <field name="parent_id" />
+                </xpath>
+            </field>
+        </record>
+        <!-- Show main inventories by default -->
+        <record id="stock.action_inventory_form" model="ir.actions.act_window">
+            <field name="context">{'full':'1', 'search_default_main_inventories':1}</field>
+        </record>
+
+        <record model="ir.ui.view" id="stock_inventory_hierarchical_form_view">
+            <field name="name">hierarchical.inventory.form</field>
+            <field name="model">stock.inventory</field>
+            <field name="type">form</field>
+            <field name="inherit_id" ref="stock.view_inventory_form" />
+            <field name="arch" type="xml">
+                <xpath expr="/form//field[@name='date']" position="attributes">
+                    <attribute name="attrs">{'readonly':[('parent_id', '!=', False)]}</attribute>
+                </xpath>
+                <xpath
+                    expr="//page[@string='General Information']"
+                    position="after">
+                    <page string="Sub-inventories">
+                        <field name="parent_id" invisible="1" />
+                        <field name="inventory_ids" nolabel="1" 
+                            context="{'form_view_ref' : 'stock_inventory_hierarchical.stock_inventory_hierarchical_subinventory_form_view', 'tree_view_ref' : 'stock_inventory_hierarchical.stock_inventory_hierarchical_subinventory_tree_view'}" />
+                    </page>
+                </xpath>
+            </field>
+        </record>
+
+        <record model="ir.ui.view"
+            id="stock_inventory_hierarchical_subinventory_tree_view">
+            <field name="name">hierarchical.inventory.subinventory.tree</field>
+            <field name="model">stock.inventory</field>
+            <field name="type">tree</field>
+            <field name="priority" eval="99" />
+            <field name="arch" type="xml">
+                <tree string="Sub-inventories" version="7.0">
+                    <field name="name" />
+                    <field name="state" />
+                    <field name="confirmed_rate" widget="progressbar" />
+                    <field name="inventory_ids" string="Number of Sub-inventories"/>
+                    <!-- XXX: Ideally we would have liked to have a button to open Sub-inventories,
+                    but unfortunately the v6.0 GTK client crashes, and the 6.0 web client opens a windows without action buttons.
+                    Maybe we may try that again with the new web client one day... 
+                    <button string="View this inventory" type="object" name="open_sub_inventory"/>
+                    -->
+                </tree>
+            </field>
+        </record>
+
+        <record model="ir.ui.view"
+            id="stock_inventory_hierarchical_subinventory_form_view">
+            <field name="name">hierarchical.inventory.subinventory.form</field>
+            <field name="model">stock.inventory</field>
+            <field name="type">form</field>
+            <field name="priority" eval="99" />
+            <field name="arch" type="xml">
+                <form string="Sub-inventories" version="7.0">
+                    <group colspan="4" col="2">
+                        <field name="name" default_focus="1"/>
+                    </group>
+                    <field name="inventory_ids" nolabel="1" colspan="4"
+                        context="{'form_view_ref' : 'stock_inventory_hierarchical.stock_inventory_hierarchical_subinventory_form_view', 'tree_view_ref' : 'stock_inventory_hierarchical.stock_inventory_hierarchical_subinventory_tree_view'}" />
+                    <group colspan="4" col="3">
+                        <field name="state" />
+                        <!-- XXX: Ideally we would have liked to have a button to open Sub-inventories,
+                            but unfortunately the v6.0 GTK client crashes, and the 6.0 web client opens a windows without action buttons.
+                            Maybe we may try that again with the new web client one day...                             
+                            <button string="View this inventory" type="object" name="open_sub_inventory"/>
+                        -->
+                    </group>
+                </form>
+            </field>
+        </record>
+        
+         <act_window id="action_view_sub_inventory"
+            name="Sub-inventories"
+            res_model="stock.inventory"
+            src_model="stock.inventory"
+            view_mode="tree,form"
+            view_type="form"
+            domain="[('parent_id', 'child_of', active_id),('id', '!=', active_id)]"
+            context="{'full':1, 'search_default_main_inventories':0}"/>
+    </data>
+</openerp>

=== added directory 'stock_inventory_hierarchical/i18n'
=== added file 'stock_inventory_hierarchical/i18n/fr.po'
--- stock_inventory_hierarchical/i18n/fr.po	1970-01-01 00:00:00 +0000
+++ stock_inventory_hierarchical/i18n/fr.po	2014-03-12 15:27:02 +0000
@@ -0,0 +1,112 @@
+# Translation of OpenERP Server.
+# This file contains the translation of the following modules:
+#	* stock_inventory_hierarchical
+#
+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:43+0000\n"
+"PO-Revision-Date: 2013-09-25 13:43+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_hierarchical
+#: field:stock.inventory,complete_name:0
+msgid "Complete reference"
+msgstr "Réference complète"
+
+#. module: stock_inventory_hierarchical
+#: field:stock.inventory,confirmed_rate:0
+msgid "Confirmed"
+msgstr "Confirmé"
+
+#. module: stock_inventory_hierarchical
+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:108
+#: constraint:stock.inventory:0
+#, python-format
+msgid "Error! You can not create recursive inventories."
+msgstr "Erreur! Vous ne pouvez pas créer d'inventaire récursifs."
+
+#. module: stock_inventory_hierarchical
+#: model:ir.model,name:stock_inventory_hierarchical.model_stock_inventory
+msgid "Gestion des stocks"
+msgstr "Gestion des stocks"
+
+#. module: stock_inventory_hierarchical
+#: field:stock.inventory,inventory_ids:0
+msgid "List of Sub-inventories"
+msgstr "Liste des sous-inventaires"
+
+#. module: stock_inventory_hierarchical
+#: view:stock.inventory:0
+msgid "Main inventories"
+msgstr "Inventaires principaux"
+
+#. module: stock_inventory_hierarchical
+#: view:stock.inventory:0
+msgid "Number of Sub-inventories"
+msgstr "Nombre de sous-inventaires"
+
+#. module: stock_inventory_hierarchical
+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:180
+#, python-format
+msgid "One of the parent Inventories is not canceled."
+msgstr "Un des inventaires pères n'est pas annulé."
+
+#. module: stock_inventory_hierarchical
+#: 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_hierarchical
+#: field:stock.inventory,parent_id:0
+msgid "Parent"
+msgstr "Parent"
+
+#. module: stock_inventory_hierarchical
+#: field:stock.inventory,parent_left:0
+msgid "Parent Left"
+msgstr "Parent gauche"
+
+#. module: stock_inventory_hierarchical
+#: field:stock.inventory,parent_right:0
+msgid "Parent Right"
+msgstr "Parent droit"
+
+#. module: stock_inventory_hierarchical
+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:188
+#, python-format
+msgid "Some Sub-inventories are not confirmed."
+msgstr "Certains sous-inventaires ne sont pas confirmés."
+
+#. module: stock_inventory_hierarchical
+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:196
+#, python-format
+msgid "Some Sub-inventories are not done."
+msgstr "Certains sous-inventaires ne sont pas terminés."
+
+#. module: stock_inventory_hierarchical
+#: model:ir.actions.act_window,name:stock_inventory_hierarchical.action_view_sub_inventory
+#: view:stock.inventory:0
+msgid "Sub-inventories"
+msgstr "Sous-inventaires"
+
+#. module: stock_inventory_hierarchical
+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:130
+#, python-format
+msgid "Sub-inventory : %s"
+msgstr "Sous-inventaire : %s"
+
+#. module: stock_inventory_hierarchical
+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:180
+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:188
+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:196
+#, python-format
+msgid "Warning !"
+msgstr "Attention !"
+

=== added directory 'stock_inventory_hierarchical/test'
=== added file 'stock_inventory_hierarchical/test/hierarchical_inventory_test.yml'
--- stock_inventory_hierarchical/test/hierarchical_inventory_test.yml	1970-01-01 00:00:00 +0000
+++ stock_inventory_hierarchical/test/hierarchical_inventory_test.yml	2014-03-12 15:27:02 +0000
@@ -0,0 +1,107 @@
+-
+  In this file, i check rules about hierarchical inventories.
+  children date must be the same of parent date for each state,
+  the state of parent and children can change only if conditions are true.
+  First, i'll create two children inventory of stock_inventory_parent inventory.
+-
+  !python {model: stock.inventory}: |
+    from osv import orm                                          
+    self.child_1_id = self.create(cr, uid, {'parent_id': ref('stock_inventory_parent0'), 
+                                       'inventory_ids': [], 
+                                       'name': 'Children 1 test inventory'})
+    self.child_2_id = self.create(cr, uid, {'parent_id': ref('stock_inventory_parent0'), 
+                                       'inventory_ids': [], 
+                                       'name': 'Children 2 test inventory'})
+-
+  Check if date of children are the same than parent date.
+  I'll read the date for 2 children and compare them with parent inventory date.  
+-
+  !python {model: stock.inventory}: |
+    from osv import orm
+    parent_date = self.read(cr, uid, [ref('stock_inventory_parent0')], ['date'])[0]['date']
+    child_1_date = self.read(cr, uid, [self.child_1_id], ['date'])[0]['date']
+    assert child_1_date == parent_date, "Date are not equals : %s - %s" % (parent_date, child_1_date)
+           
+    child_2_date = self.read(cr, uid, [self.child_2_id], ['date'])[0]['date']    
+    assert child_2_date == parent_date, "Date are not equals : %s - %s" % (parent_date, child_2_date)
+
+-
+  Check if children cannot be canceled if the parent was not canceled.
+  I'll try to cancel both children inventory while parent inventory having "draft" state.
+  After, i'll verify the state of each inventory. 
+-
+  !python {model: stock.inventory}: |  
+    from osv import orm, osv    
+    try:
+      self.action_cancel_inventary(cr, uid, [self.child_1_id])      
+    except osv.except_osv as e:
+      log("Good ! The Inventory could not be canceled : %s" % e)
+    try:
+      self.action_cancel_inventary(cr, uid, [self.child_2_id])      
+    except osv.except_osv as e:
+      log("Good ! The Inventory could not be canceled : %s" % e)      
+    child_1_state = self.read(cr, uid, [self.child_1_id], ['state'])[0]['state']
+    assert child_1_state == 'draft', "Child inventory 1 have '%s' state. It should be 'draft'" % child_1_state
+    child_2_state = self.read(cr, uid, [self.child_2_id], ['state'])[0]['state']
+    assert child_2_state == 'draft', "Child inventory 2 have '%s' state. It should be 'draft'" % child_2_state
+
+-
+  Check if children inventory have confirm state before confirm parent inventory.
+  To check this, i'll try to confirm parent inventory when children inventory having "draft" state,
+  and i'll check if state is still 'draft'.
+-
+  !python {model: stock.inventory}: |  
+    from osv import orm, osv          
+    try:
+      self.action_confirm(cr, uid, [ref('stock_inventory_parent0')])    
+    except osv.except_osv as e:
+      log("Good, the inventory could not be confirm : %s", e)
+    parent_state = self.read(cr, uid, [ref('stock_inventory_parent0')], ['state'])[0]['state']
+    assert parent_state == 'draft', "Parent inventory have '%s' state. It should be 'draft'" % parent_state
+
+-
+  In order, i'll confirm the children inventories, and the parent inventory after.
+-
+  !python {model: stock.inventory}: |  
+    from osv import orm, osv
+    self.action_confirm(cr, uid, [self.child_1_id])
+    child_1_state = self.read(cr, uid, [self.child_1_id], ['state'])[0]['state']
+    assert child_1_state == 'confirm', "Child inventory 1 have '%s' state. It should be 'confirm'" % child_1_state
+    
+    self.action_confirm(cr, uid, [self.child_2_id])
+    child_2_state = self.read(cr, uid, [self.child_2_id], ['state'])[0]['state']
+    assert child_2_state == 'confirm', "Child inventory 2 have '%s' state. It should be 'confirm'" % child_2_state
+        
+    self.action_confirm(cr, uid, [ref('stock_inventory_parent0')])
+    parent_state = self.read(cr, uid, [ref('stock_inventory_parent0')], ['state'])[0]['state']
+    assert parent_state == 'confirm', "Parent inventory have '%s' state. It should be 'confirm'" % parent_state
+    
+-
+  Check if children inventory have done state before validate parent inventory.
+  I'll try to validate parent inventory before children. 
+-
+  !python {model: stock.inventory}: |  
+    from osv import orm, osv
+    try:
+      self.action_done(cr, uid, [ref('stock_inventory_parent0')])
+    except osv.except_osv as e:
+      log("Good, the inventory could not be validate : %s", e)
+    parent_state = self.read(cr, uid, [ref('stock_inventory_parent0')], ['state'])[0]['state']
+    assert parent_state == 'confirm', "Parent inventory have '%s' state. It should be 'confirm'" % parent_state
+
+-
+  Now, i'll validate all children inventory before validate the parent.
+-
+  !python {model: stock.inventory}: |  
+    from osv import orm, osv
+    self.action_done(cr, uid, [self.child_1_id])
+    child_1_state = self.read(cr, uid, [self.child_1_id], ['state'])[0]['state']
+    assert child_1_state == 'done', "Child inventory 1 have '%s' state. It should be 'done'" % child_1_state
+    
+    self.action_done(cr, uid, [self.child_2_id])
+    child_2_state = self.read(cr, uid, [self.child_2_id], ['state'])[0]['state']
+    assert child_2_state == 'done', "Child inventory 2 have '%s' state. It should be 'done'" % child_2_state
+        
+    self.action_done(cr, uid, [ref('stock_inventory_parent0')])
+    parent_state = self.read(cr, uid, [ref('stock_inventory_parent0')], ['state'])[0]['state']
+    assert parent_state == 'done', "Parent inventory have '%s' state. It should be 'done'" % parent_state    

=== added directory 'stock_inventory_hierarchical_location'
=== added file 'stock_inventory_hierarchical_location/__init__.py'
--- stock_inventory_hierarchical_location/__init__.py	1970-01-01 00:00:00 +0000
+++ stock_inventory_hierarchical_location/__init__.py	2014-03-12 15:27:02 +0000
@@ -0,0 +1,22 @@
+# -*- 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/>.
+#
+##############################################################################
+
+import inventory_hierarchical_location
+import wizard

=== added file 'stock_inventory_hierarchical_location/__openerp__.py'
--- stock_inventory_hierarchical_location/__openerp__.py	1970-01-01 00:00:00 +0000
+++ stock_inventory_hierarchical_location/__openerp__.py	2014-03-12 15:27:02 +0000
@@ -0,0 +1,52 @@
+# -*- 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/>.
+#
+##############################################################################
+
+{
+    "name": "Exhaustive and hierachical Stock Inventories",
+    "version": "1.0",
+    "depends": ["stock_inventory_hierarchical", "stock_inventory_location"],
+    "author": u"Numérigraphe",
+    "category": "stock inventory",
+    "description": """
+This module makes exhaustive Inventories aware of their Sub-Inventories.
+========================================================================
+
+It should be installed if both modules "stock_inventory_location"
+and "stock_inventory_hierarchical" are installed.
+
+This module allows an inventory to contain a general Location,
+and it's sub-inventories to contain some of it's sub-Locations.
+When you open an inventory, OpenERP will warn you if some of the sub-Locations
+of an inventory are absent from the sub-Inventories.
+It will also prevent you from setting the Inventories and sub-Inventories 
+in inconsistent status.
+    """,
+    "init_xml": [],
+    "update_xml": [
+                   "wizard/stock_inventory_missing_locations_view.xml",
+                   "inventory_hierarchical_location_view.xml",
+                   ],
+
+     "test": ["test/inventory_hierarchical_location_test.yml"],
+     "demo": ["inventory_hierarchical_location_demo.xml"],
+
+    # Will work with v6.1 and later
+    "auto_install": True,
+}

=== added directory 'stock_inventory_hierarchical_location/i18n'
=== added file 'stock_inventory_hierarchical_location/i18n/fr.po'
--- stock_inventory_hierarchical_location/i18n/fr.po	1970-01-01 00:00:00 +0000
+++ stock_inventory_hierarchical_location/i18n/fr.po	2014-03-12 15:27:02 +0000
@@ -0,0 +1,127 @@
+# Translation of OpenERP Server.
+# This file contains the translation of the following modules:
+#	* stock_inventory_hierarchical_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:55+0000\n"
+"PO-Revision-Date: 2013-09-25 13:55+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_hierarchical_location
+#: model:ir.actions.act_window,name:stock_inventory_hierarchical_location.action_view_stock_inventory_missing_location
+msgid "Confirm missing location"
+msgstr "Confirmer les emplacements manquants"
+
+#. module: stock_inventory_hierarchical_location
+#: view:stock.inventory.missing.location:0
+msgid "Confirm missing locations"
+msgstr "Confirmer les emplacements manquants"
+
+#. module: stock_inventory_hierarchical_location
+#: model:ir.model,name:stock_inventory_hierarchical_location.model_stock_inventory_uninventoried_locations
+msgid "Confirm the uninventoried Locations."
+msgstr "Confirmer les emplacements non inventoriés."
+
+#. module: stock_inventory_hierarchical_location
+#: view:stock.inventory.missing.location:0
+msgid "Do you want to continue ?"
+msgstr "Voulez-vous continuer ?"
+
+#. module: stock_inventory_hierarchical_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_hierarchical_location
+#: model:ir.model,name:stock_inventory_hierarchical_location.model_stock_inventory
+msgid "Gestion des stocks"
+msgstr "Gestion des stocks"
+
+#. module: stock_inventory_hierarchical_location
+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:70
+#, python-format
+msgid "Location missing for inventory \"%s\"."
+msgstr "Emplacement manquants dans l'inventaire \"%s\"."
+
+#. module: stock_inventory_hierarchical_location
+#: view:stock.inventory:0
+msgid "Locations"
+msgstr "Emplacements"
+
+#. module: stock_inventory_hierarchical_location
+#: field:stock.inventory.missing.location,location_ids:0
+msgid "Missing location"
+msgstr "Emplacements manquants"
+
+#. module: stock_inventory_hierarchical_location
+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:41
+#, python-format
+msgid "One of the parent inventories are not open."
+msgstr "Un inventaire parent n'est pas ouvert."
+
+#. module: stock_inventory_hierarchical_location
+#: view:stock.inventory:0
+msgid "Open Inventory"
+msgstr "Ouvrir l'inventaire"
+
+#. module: stock_inventory_hierarchical_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_hierarchical_location
+#: model:ir.model,name:stock_inventory_hierarchical_location.model_stock_inventory_missing_location
+msgid "Search on inventory tree for missing declared locations."
+msgstr "Recherche dans les inventaires les emplacements absents."
+
+#. module: stock_inventory_hierarchical_location
+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:122
+#, python-format
+msgid "Some Sub-inventories are not confirmed."
+msgstr "Au moins un sous-inventaire n'est pas confirmé."
+
+#. module: stock_inventory_hierarchical_location
+#: view:stock.inventory.missing.location:0
+msgid "This is the list of missing locations."
+msgstr "Voici la liste des emplacements manquants."
+
+#. module: stock_inventory_hierarchical_location
+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:58
+#, python-format
+msgid "This location is not declared on the parent inventory\n"
+"You cannot add it !"
+msgstr "Cet emplacement n'est pas déclaré dans l'inventaire parent\n"
+"Vous ne pouvez pas l'ajouter !"
+
+#. module: stock_inventory_hierarchical_location
+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:41
+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:70
+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:122
+#, python-format
+msgid "Warning !"
+msgstr "Attention !"
+
+#. module: stock_inventory_hierarchical_location
+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:57
+#, python-format
+msgid "Warning: Wrong location"
+msgstr "Attention: mauvais emplacement"
+
+#. module: stock_inventory_hierarchical_location
+#: view:stock.inventory.missing.location:0
+msgid "_Cancel"
+msgstr "_Annuler"
+
+#. module: stock_inventory_hierarchical_location
+#: view:stock.inventory.missing.location:0
+msgid "_Confirm missing locations"
+msgstr "_Confirmer les emplacements manquants"
+

=== added file 'stock_inventory_hierarchical_location/inventory_hierarchical_location.py'
--- stock_inventory_hierarchical_location/inventory_hierarchical_location.py	1970-01-01 00:00:00 +0000
+++ stock_inventory_hierarchical_location/inventory_hierarchical_location.py	2014-03-12 15:27:02 +0000
@@ -0,0 +1,137 @@
+# -*- 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 osv
+from openerp.tools.translate import _
+
+
+class StockInventory(osv.osv):
+    _inherit = 'stock.inventory'
+
+    def __init__(self, pool, cr):
+        """Propagate the "exhaustive" field from inventories to sub-inventories"""
+        s = super(StockInventory, self)
+        s.PARENT_VALUES.append('exhaustive')
+        return s.__init__(pool, cr)
+
+    def action_open(self, cr, uid, ids, context=None):
+        """Open only if all the parents are Open."""
+        #XXX the dosctring used to say this but it's not implemented, normal?
+        # --> "Before opening, if locations are  missing, ask the user 
+        # to validate the opening without these locations."
+        for inventory in self.browse(cr, uid, ids, context=context):
+            while inventory.parent_id:
+                inventory = inventory.parent_id
+                if inventory.state != 'open':
+                    raise osv.except_osv(_('Warning !'),
+                                         _('One of the parent inventories are not open.'))
+        return super(StockInventory, self).action_open(cr, uid, ids, context=context)
+
+    def check_location(self, cr, uid, ids, location_ids, name, context=None):
+        """Check if location is a child of parent inventory location"""
+        res_location_ids = location_ids[0][2]
+        nbr_location_ids = len(res_location_ids)
+        for inventory in self.browse(cr, uid, ids, context=None):
+            if inventory.parent_id.id:
+                parent_locations = self.read(cr, uid, [inventory.parent_id.id], ['location_ids'])
+                parent_children_locations = self.pool.get('stock.location').search(cr, uid, [('location_id', 'child_of', parent_locations[0]['location_ids'])])
+                for location_id in res_location_ids:
+                    if location_id not in parent_children_locations:
+                        res_location_ids.remove(location_id)
+        res = {}
+        if nbr_location_ids != len(res_location_ids):
+            res['warning'] = {'title': _('Warning: Wrong location'),
+                              'message': _("This location is not declared on the parent inventory\n"
+                                           "You cannot add it !")}
+        res['value'] = {'location_ids': res_location_ids, }
+        return res
+
+    def _fill_location_lines(self, cr, uid, inventory_id, location_ids, set_stock_zero, context=None):
+        """Add ids of children inventory into list """
+        children_inventory_ids = self.search(cr, uid, [('parent_id', 'child_of', inventory_id)])
+        context['children_inventory_ids'] = children_inventory_ids
+        return super(StockInventory, self)._fill_location_lines(cr, uid, inventory_id, location_ids, set_stock_zero, context=context)
+
+    def open_missing_location_wizard(self, cr, uid, ids, context=None):
+        """Open wizard if inventory have children.
+        Before, verify if all children of exhaustive inventory have at least one location."""
+        children_ids = self.search(cr, uid, [('parent_id', 'child_of', ids)], context=context)
+        for inventory in self.browse(cr, uid, children_ids, context=context):
+            if inventory.exhaustive:
+                if not inventory.location_ids:
+                    raise osv.except_osv(_('Warning !'),
+                                         _('Location missing for inventory "%s".') % inventory.name)
+        children_count = self.pool.get('stock.inventory').search(cr, uid, [('parent_id', 'child_of', ids)], count=True)
+        if children_count == 1:
+            return self.action_open(cr, uid, ids, 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.missing.location',
+                'target': 'new',
+                'context': context,
+                'nodestroy': True,
+                }
+
+
+# XXX: move to /wizard
+class StockInventoryUninventoriedLocation(osv.osv_memory):
+    _inherit = 'stock.inventory.uninventoried.locations'
+
+    def inventories(self, cr, uid, inventory_parent_id):
+        """Iterator of children inventories.
+        return inventory_id;
+       """
+        children_ids = self.pool.get('stock.inventory').search(cr, uid, [('parent_id', 'child_of', inventory_parent_id)])
+        for inventory_id in children_ids:
+            yield inventory_id
+
+    def get_locations(self, cr, uid, inventory_id, context=None):
+        """Get all locations through inventory tree."""
+        list_inventories_locations_ids = []
+        for i_id in self.inventories(cr, uid, inventory_id):
+            location_ids = super(StockInventoryUninventoriedLocation, self).get_locations(cr, uid, i_id, context=context)
+            list_inventories_locations_ids = list(set(list_inventories_locations_ids + location_ids))
+        return list_inventories_locations_ids
+
+    def get_locations_inventoried(self, cr, uid, inventory_id, location_ids, context=None):
+        """Get all locations on inventory lines through inventory tree."""
+        list_all_inventoried_location_ids = []
+        for i_id in self.inventories(cr, uid, inventory_id):
+            list_loc_ids = super(StockInventoryUninventoriedLocation, self).get_locations_inventoried(cr, uid, i_id, location_ids, context=context)
+            list_all_inventoried_location_ids = list(set(list_all_inventoried_location_ids + list_loc_ids))
+        return list_all_inventoried_location_ids
+
+    def default_locations(self, cr, uid, context=None):
+        """Do something only if children state are confirm or done."""
+        children_count = self.pool.get('stock.inventory').search(cr, uid, [('parent_id', 'child_of', context['active_id']),
+                                             ('state', 'not in', ['confirm', 'done'])], context=context, count=True)
+        if children_count > 1:
+            raise osv.except_osv(_('Warning !'), _('Some Sub-inventories are not confirmed.'))
+        return super(StockInventoryUninventoriedLocation, self).default_locations(cr, uid, context=context)
+
+    _defaults = {
+        'location_ids': default_locations,
+        }
+

=== added file 'stock_inventory_hierarchical_location/inventory_hierarchical_location_demo.xml'
--- stock_inventory_hierarchical_location/inventory_hierarchical_location_demo.xml	1970-01-01 00:00:00 +0000
+++ stock_inventory_hierarchical_location/inventory_hierarchical_location_demo.xml	2014-03-12 15:27:02 +0000
@@ -0,0 +1,18 @@
+<?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 exhaustive inventory -->
+        <record id="parent_inventory_location" model="stock.inventory">
+            <field name="name">Hierarchical location exhaustive inventory</field>
+            <field name="state">draft</field>
+            <field name="date">2020-04-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_hierarchical_location/inventory_hierarchical_location_view.xml'
--- stock_inventory_hierarchical_location/inventory_hierarchical_location_view.xml	1970-01-01 00:00:00 +0000
+++ stock_inventory_hierarchical_location/inventory_hierarchical_location_view.xml	2014-03-12 15:27:02 +0000
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+	<data>
+	
+        <record model="ir.ui.view" id="stock_inventory_hierarchical_location_form_view">
+            <field name="name">hierarchical.inventory.location.form</field>
+            <field name="model">stock.inventory</field>
+            <field name="type">form</field>
+            <field name="inherit_id" ref="stock.view_inventory_form" />
+            <field name="arch" type="xml">
+
+                 <xpath expr="//form[@string='Physical Inventory']//field[@name='exhaustive']" position="attributes">
+                    <attribute name="attrs">{'readonly':[('parent_id', '!=', False)]}</attribute>                    
+                </xpath>
+ 
+                <xpath expr="/form[@string='Physical Inventory']//page[@string='General Information']/field[@name='location_ids']" position="replace">           
+                    <field colspan="1" nolabel="1" name="location_ids" domain="[('usage','in',('view', 'internal'))]" 
+                            attrs="{'invisible':[('exhaustive','!=',True)]}"
+                            on_change="check_location(location_ids, name)">
+                        <tree string="Locations" editable="bottom">                                                                                                       
+                            <field name="name"/> 
+                        </tree>                                    
+                    </field>           
+                </xpath>
+                
+                <!-- replace action_open button to open wizard missing locations -->                
+                <xpath expr="/form//button[@name='action_open']" position="replace">
+                     <button name="open_missing_location_wizard"
+                      string="Open Inventory" type="object" states="draft" icon="gtk-apply"/>                    
+                </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, 'search_default_main_inventories':1}</field>
+        </record>
+	</data>
+</openerp>

=== added directory 'stock_inventory_hierarchical_location/test'
=== added file 'stock_inventory_hierarchical_location/test/inventory_hierarchical_location_test.yml'
--- stock_inventory_hierarchical_location/test/inventory_hierarchical_location_test.yml	1970-01-01 00:00:00 +0000
+++ stock_inventory_hierarchical_location/test/inventory_hierarchical_location_test.yml	2014-03-12 15:27:02 +0000
@@ -0,0 +1,31 @@
+-
+  I will create children inventory.
+-
+  !python {model: stock.inventory}: |                                          
+    self.child_1_id = self.create(cr, uid, {'parent_id': ref('parent_inventory_location'), 
+                                       'inventory_ids': [], 
+                                       'name': 'Children 1 test inventory'})
+
+-
+  I will check if exhaustive information of parent inventory has been added to children.
+-
+  !python {model: stock.inventory}: |
+    exhaustive = self.read(cr, uid, [self.child_1_id], ['exhaustive'])[0]['exhaustive']
+    assert exhaustive == True, "Exhaustive information not added to children inventory"
+    
+-
+  I will check if i can't open children inventory while parent inventory is open. 
+-     
+  !python {model: stock.inventory}: |
+    from osv import orm, osv
+    try:
+      self.action_open(cr, uid, [self.child_1_id])
+    except osv.except_osv as e:
+      log("Good ! The Inventory could not be opened : %s" % e)        
+    child_1_state = self.read(cr, uid, [self.child_1_id], ['state'])[0]['state']
+    assert child_1_state == 'draft', "Child inventory 1 have '%s' state. It should be 'draft'" % child_1_state
+    
+    
+    
+    
+    
\ No newline at end of file

=== added directory 'stock_inventory_hierarchical_location/wizard'
=== added file 'stock_inventory_hierarchical_location/wizard/__init__.py'
--- stock_inventory_hierarchical_location/wizard/__init__.py	1970-01-01 00:00:00 +0000
+++ stock_inventory_hierarchical_location/wizard/__init__.py	2014-03-12 15:27:02 +0000
@@ -0,0 +1,21 @@
+# -*- 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/>.
+#
+##############################################################################
+
+import stock_inventory_missing_locations

=== added file 'stock_inventory_hierarchical_location/wizard/stock_inventory_missing_locations.py'
--- stock_inventory_hierarchical_location/wizard/stock_inventory_missing_locations.py	1970-01-01 00:00:00 +0000
+++ stock_inventory_hierarchical_location/wizard/stock_inventory_missing_locations.py	2014-03-12 15:27:02 +0000
@@ -0,0 +1,82 @@
+# -*- 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 osv, fields
+from openerp.tools.translate import _
+
+
+class inventory_missing_location(osv.osv_memory):
+
+    _name = 'stock.inventory.missing.location'
+    _description = 'Search on inventory tree for missing declared locations.'
+
+    _columns = {
+                'location_ids': fields.many2many('stock.location',
+                                                 'stock_inventory_missing_location_rel',
+                                                 'location_id',
+                                                 'wizard_id',
+                                                 'Missing location', readonly=True),
+                }
+
+    def confirm_missing_locations(self, cr, uid, ids, context=None):
+        """ Call action open method from stock.inventory """
+        ids = context['active_ids']
+        self.pool.get('stock.inventory').action_open(cr, uid, ids, context=context)
+        return {'type': 'ir.actions.act_window_close'}
+
+    def inventories(self, cr, uid, inventory_parent_id):
+        """ Iterator of children inventories.
+        """
+        children_ids = self.pool.get('stock.inventory').search(cr, uid, [('parent_id', 'child_of', inventory_parent_id)])
+        for inventory_id in children_ids:
+            if inventory_id == inventory_parent_id:
+                continue  # pass the parent inventory
+            yield inventory_id
+
+    def get_locations_from_children(self, cr, uid, inventory_id, context=None):
+        """ Get all locations through inventory tree. """
+        list_inventories_locations_ids = []
+        for i_id in self.inventories(cr, uid, inventory_id):
+            location_ids = self.pool.get('stock.inventory').read(cr, uid, [i_id], ['location_ids'], context=context)[0]
+            location_ids = self.pool.get('stock.location').search(cr, uid, [
+                                            ('location_id', 'child_of', location_ids['location_ids']),
+                                            ('usage', '=', 'internal')], context=context)
+            list_inventories_locations_ids = list(set(list_inventories_locations_ids + location_ids))
+        return list_inventories_locations_ids
+
+    def default_missing_locations(self, cr, uid, context=None):
+        """ Initialize view with the list of missing locations on inventory tree.
+        """
+        if context is None:
+            context = {}
+
+        # get children locations for parent/current inventory
+        parent_location_ids = self.pool.get('stock.inventory').read(cr, uid, [context['active_id']], ['location_ids'], context=context)[0]
+        parent_location_ids = self.pool.get('stock.location').search(cr, uid, [
+                                        ('location_id', 'child_of', parent_location_ids['location_ids']),
+                                        ('usage', '=', 'internal')], context=context)
+        # get locations for each sub-inventory
+        location_ids = self.get_locations_from_children(cr, uid, context['active_id'])
+        list_missing_ids = [_id for _id in parent_location_ids if _id not in location_ids]
+        return list_missing_ids
+
+    _defaults = {
+        'location_ids': default_missing_locations,
+        }

=== added file 'stock_inventory_hierarchical_location/wizard/stock_inventory_missing_locations_view.xml'
--- stock_inventory_hierarchical_location/wizard/stock_inventory_missing_locations_view.xml	1970-01-01 00:00:00 +0000
+++ stock_inventory_hierarchical_location/wizard/stock_inventory_missing_locations_view.xml	2014-03-12 15:27:02 +0000
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+ 
+        <record id="action_view_stock_inventory_missing_location" model="ir.actions.act_window">
+            <field name="name">Confirm missing location</field>
+            <field name="type">ir.actions.act_window</field>
+            <field name="res_model">stock.inventory.missing.location</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">form</field>
+            <field name="target">new</field>
+        </record>
+
+        <!-- The view definition is similar with stock_inventory_location/wizard/stock_confirm_uninventoried_location.xml, 
+        but the code is different. 
+        This wizard compare the locations of inventory with locations declared on parent inventory to 
+        to present to user the missing locations. -->
+        <record id="view_confirm_missing_location" model="ir.ui.view">
+            <field name="name">Confirm missing location</field>
+            <field name="model">stock.inventory.missing.location</field>
+            <field name="type">form</field>
+            <field name="arch" type="xml">
+                <form string="Confirm missing locations">
+                    <group colspan="4" col="1">
+                        <field name="location_ids" nolabel="1">
+                            <tree>
+                                <field name="name"/>
+                            </tree>
+                        </field>                        
+                    </group>
+                    <group colspan="4" col="2">
+                        <label string="This is the list of missing locations."/>
+                        <label string="Do you want to continue ?"/>
+                        <separator string="" colspan="4" />
+                        <button special="cancel" string="_Cancel" icon='gtk-cancel'/>
+                        <button name="confirm_missing_locations" string="_Confirm missing locations" type="object" icon="gtk-ok"/>
+                    </group>
+                </form>
+            </field>
+        </record>
+
+    </data>
+</openerp>
\ No newline at end of file


Follow ups