openerp-community-reviewer team mailing list archive
-
openerp-community-reviewer team
-
Mailing list archive
-
Message #04741
[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