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

Proposed by Loïc Bellier - Numérigraphe
Status: Rejected
Rejected by: Pedro Manuel Baeza
Proposed branch: lp:~numerigraphe-team/stock-logistic-warehouse/7.0-inventory-hierarchical
Merge into: lp:stock-logistic-warehouse
Prerequisite: lp:~numerigraphe-team/stock-logistic-warehouse/7.0-inventory-location
Diff against target: 1935 lines (+1799/-0)
23 files modified
stock_inventory_hierarchical/__init__.py (+28/-0)
stock_inventory_hierarchical/__openerp__.py (+46/-0)
stock_inventory_hierarchical/exceptions.py (+26/-0)
stock_inventory_hierarchical/hierarchical_inventory.py (+202/-0)
stock_inventory_hierarchical/hierarchical_inventory_demo.xml (+17/-0)
stock_inventory_hierarchical/hierarchical_inventory_view.xml (+84/-0)
stock_inventory_hierarchical/i18n/fr.po (+116/-0)
stock_inventory_hierarchical/i18n/stock_inventory_hierarchical.pot (+116/-0)
stock_inventory_hierarchical/test/hierarchical_inventory_test.yml (+96/-0)
stock_inventory_hierarchical_location/__init__.py (+22/-0)
stock_inventory_hierarchical_location/__openerp__.py (+49/-0)
stock_inventory_hierarchical_location/i18n/fr.po (+164/-0)
stock_inventory_hierarchical_location/i18n/stock_inventory_hierarchical_location.pot (+166/-0)
stock_inventory_hierarchical_location/inventory_hierarchical_location.py (+102/-0)
stock_inventory_hierarchical_location/inventory_hierarchical_location_demo.xml (+28/-0)
stock_inventory_hierarchical_location/inventory_hierarchical_location_view.xml (+37/-0)
stock_inventory_hierarchical_location/tests/__init__.py (+39/-0)
stock_inventory_hierarchical_location/tests/fill_inventory_test.py (+118/-0)
stock_inventory_hierarchical_location/tests/inventory_hierarchical_location_test.yml (+56/-0)
stock_inventory_hierarchical_location/wizard/__init__.py (+22/-0)
stock_inventory_hierarchical_location/wizard/generate_inventory.py (+134/-0)
stock_inventory_hierarchical_location/wizard/generate_inventory_view.xml (+42/-0)
stock_inventory_hierarchical_location/wizard/stock_fill_location_inventory.py (+89/-0)
To merge this branch: bzr merge lp:~numerigraphe-team/stock-logistic-warehouse/7.0-inventory-hierarchical
Reviewer Review Type Date Requested Status
Alexandre Fayolle - camptocamp Needs Resubmitting
Lionel Sausin - Initiatives/Numérigraphe (community) co-author Abstain
Laetitia Gangloff (Acsone) Pending
Review via email: mp+223882@code.launchpad.net

This proposal supersedes a proposal from 2014-03-12.

Description of the change

Code cleanup and Bug fixes.

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).

Thanks to Laetitia GANGLOFF from Ascone for her contribution.

To post a comment you must log in.
Revision history for this message
Lionel Sausin - Initiatives/Numérigraphe (ls-initiatives) wrote : Posted in a previous version of this proposal

Loic and I are improving this proposal WRT the 7.0 coding style and latest v8 updates.

Revision history for this message
Lionel Sausin - Initiatives/Numérigraphe (ls-initiatives) wrote : Posted in a previous version of this proposal

Loïc and I are still working on this proposal. Good progress was made: we re-aligned the features with v8 in mind, and improved the code style and the views.
Acsone joined in and we're still making tests, and I hope Loïc can make a new proposal in the coming days.

review: Needs Resubmitting (test pending)
Revision history for this message
Lionel Sausin - Initiatives/Numérigraphe (ls-initiatives) :
review: Abstain (i'm a contributor)
Revision history for this message
Lionel Sausin - Initiatives/Numérigraphe (ls-initiatives) wrote :

Loïc just noticed a bug when clearing locations in exhaustive and structured inventories, related to Bug #1197467.
When confirming the parent inventory, the children inventories' locations are not excluded from the exhaustivity check, and lines with qty=0 are unduly added to the parent inventory.
I'm fixing this as fast as I can.

review: Needs Fixing (functionnal bug)
Revision history for this message
Lionel Sausin - Initiatives/Numérigraphe (ls-initiatives) wrote :
review: Abstain (co-author)
47. By Numérigraphe

[IMP] add button to open subinventories and progresbar on inventory header.

48. By Numérigraphe

[IMP] French translation

Revision history for this message
Alexandre Fayolle - camptocamp (alexandre-fayolle-c2c) wrote :

The source code management for this project has been moved to https://github.com/OCA/stock-logistics-warehouse

Could you resubmit this MP on the new site?

review: Needs Resubmitting
Revision history for this message
Lionel Sausin - Initiatives/Numérigraphe (ls-initiatives) wrote :

I've finally resubmitted this as part of the pull request at https://github.com/OCA/stock-logistics-warehouse/pull/17.
Please reject the merge proposal here to make it clear it won't be processed on Launchpad.

Unmerged revisions

48. By Numérigraphe

[IMP] French translation

47. By Numérigraphe

[IMP] add button to open subinventories and progresbar on inventory header.

46. By Laetitia Gangloff (Acsone)

[MERGE] raise the same exception as the standard so the other parts of the code can catch it properly

45. By Numérigraphe

[MERGE] no-change commit to close acsone's MP

44. By Numérigraphe

[MERGE] take subinventories into account when validating an exhaustive structured inventory

43. By Numérigraphe

[MERGE] fixes from 7.0-inventory-location

42. By Loïc Bellier - Numérigraphe

[MERGE]: Exception fault fixed by ACSONE.

41. By Loïc Bellier - Numérigraphe

[MERGE]: Fix some bugs. Add Merge Proposal of Laetitia Gangloff from ASCONE and thanks to her.

40. By Loïc Bellier - Numérigraphe

[MERGE]: merge from dev branch

39. By Loïc Bellier - Numérigraphe

[MERGE]: From dev branch

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'stock_inventory_hierarchical'
2=== added file 'stock_inventory_hierarchical/__init__.py'
3--- stock_inventory_hierarchical/__init__.py 1970-01-01 00:00:00 +0000
4+++ stock_inventory_hierarchical/__init__.py 2014-07-01 11:47:18 +0000
5@@ -0,0 +1,28 @@
6+# -*- coding: utf-8 -*-
7+##############################################################################
8+#
9+# This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
10+#
11+# This program is free software: you can redistribute it and/or modify
12+# it under the terms of the GNU General Public License as published by
13+# the Free Software Foundation, either version 3 of the License, or
14+# (at your option) any later version.
15+#
16+# This program is distributed in the hope that it will be useful,
17+# but WITHOUT ANY WARRANTY; without even the implied warranty of
18+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+# GNU General Public License for more details.
20+#
21+# You should have received a copy of the GNU General Public License
22+# along with this program. If not, see <http://www.gnu.org/licenses/>.
23+#
24+##############################################################################
25+
26+# This package-wide list keeps the names of the field that must be
27+# propagated from root inventories to their children.
28+# Add field names in the Model's definition.
29+PARENT_VALUES = []
30+
31+import hierarchical_inventory
32+# Bring the main exception into the package's scope for easier reuse
33+from .exceptions import HierarchicalInventoryException
34
35=== added file 'stock_inventory_hierarchical/__openerp__.py'
36--- stock_inventory_hierarchical/__openerp__.py 1970-01-01 00:00:00 +0000
37+++ stock_inventory_hierarchical/__openerp__.py 2014-07-01 11:47:18 +0000
38@@ -0,0 +1,46 @@
39+# -*- coding: utf-8 -*-
40+##############################################################################
41+#
42+# This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
43+#
44+# This program is free software: you can redistribute it and/or modify
45+# it under the terms of the GNU General Public License as published by
46+# the Free Software Foundation, either version 3 of the License, or
47+# (at your option) any later version.
48+#
49+# This program is distributed in the hope that it will be useful,
50+# but WITHOUT ANY WARRANTY; without even the implied warranty of
51+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
52+# GNU General Public License for more details.
53+#
54+# You should have received a copy of the GNU General Public License
55+# along with this program. If not, see <http://www.gnu.org/licenses/>.
56+#
57+##############################################################################
58+
59+{
60+ "name": "Hierarchical Physical Inventory",
61+ "version": "1.1",
62+ "depends": ["stock"],
63+ "author": "Numérigraphe",
64+ "category": "Warehouse Management",
65+ "description": """
66+Hierarchical structure for Physical Inventories and sub-Inventories
67+===================================================================
68+
69+This module adds a parent-child relationship between Physical Inventories, to
70+help users manage complex inventories.
71+Using several inventories, you can distribute the counting to several persons
72+and still keep a clear overview of global Inventory's status.
73+
74+OpenERP will make sure the status of the Inventory and it's Sub-Inventories are
75+consistent.
76+""",
77+ "data": ["hierarchical_inventory_view.xml"],
78+ "test": ["test/hierarchical_inventory_test.yml"],
79+ "demo": ["hierarchical_inventory_demo.xml"],
80+ "images": [
81+ "inventory_form.png",
82+ "inventory_form_actions.png",
83+ ],
84+}
85
86=== added file 'stock_inventory_hierarchical/exceptions.py'
87--- stock_inventory_hierarchical/exceptions.py 1970-01-01 00:00:00 +0000
88+++ stock_inventory_hierarchical/exceptions.py 2014-07-01 11:47:18 +0000
89@@ -0,0 +1,26 @@
90+# -*- coding: utf-8 -*-
91+##############################################################################
92+#
93+# This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
94+#
95+# This program is free software: you can redistribute it and/or modify
96+# it under the terms of the GNU General Public License as published by
97+# the Free Software Foundation, either version 3 of the License, or
98+# (at your option) any later version.
99+#
100+# This program is distributed in the hope that it will be useful,
101+# but WITHOUT ANY WARRANTY; without even the implied warranty of
102+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
103+# GNU General Public License for more details.
104+#
105+# You should have received a copy of the GNU General Public License
106+# along with this program. If not, see <http://www.gnu.org/licenses/>.
107+#
108+##############################################################################
109+
110+from openerp.osv import orm
111+
112+
113+class HierarchicalInventoryException(orm.except_orm):
114+ """The operation is not possible for a hierarchical inventory"""
115+ pass
116
117=== added file 'stock_inventory_hierarchical/hierarchical_inventory.py'
118--- stock_inventory_hierarchical/hierarchical_inventory.py 1970-01-01 00:00:00 +0000
119+++ stock_inventory_hierarchical/hierarchical_inventory.py 2014-07-01 11:47:18 +0000
120@@ -0,0 +1,202 @@
121+# -*- coding: utf-8 -*-
122+##############################################################################
123+#
124+# This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
125+#
126+# This program is free software: you can redistribute it and/or modify
127+# it under the terms of the GNU General Public License as published by
128+# the Free Software Foundation, either version 3 of the License, or
129+# (at your option) any later version.
130+#
131+# This program is distributed in the hope that it will be useful,
132+# but WITHOUT ANY WARRANTY; without even the implied warranty of
133+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
134+# GNU General Public License for more details.
135+#
136+# You should have received a copy of the GNU General Public License
137+# along with this program. If not, see <http://www.gnu.org/licenses/>.
138+#
139+##############################################################################
140+
141+from openerp.osv import orm, fields
142+from openerp.tools.translate import _
143+
144+from .exceptions import HierarchicalInventoryException
145+
146+# Add the date to the list of fields we must propagate to children inventories
147+from . import PARENT_VALUES
148+PARENT_VALUES.append('date')
149+
150+
151+class HierarchicalInventory(orm.Model):
152+ _inherit = 'stock.inventory'
153+
154+ _parent_store = True
155+ _parent_order = 'date, name'
156+ _order = 'parent_left'
157+
158+ def name_get(self, cr, uid, ids, context=None):
159+ """Show the parent inventory's name in the name of the children
160+
161+ :param dict context: the ``inventory_display`` key can be
162+ used to select the short version of the
163+ inventory name (without the direct parent),
164+ when set to ``'short'``. The default is
165+ the long version."""
166+ if context is None:
167+ context = {}
168+ if context.get('inventory_display') == 'short':
169+ # Short name context: just do the usual stuff
170+ return super(HierarchicalInventory, self).name_get(
171+ cr, uid, ids, context=context)
172+ if isinstance(ids, (list, tuple)) and not len(ids):
173+ return []
174+ if isinstance(ids, (long, int)):
175+ ids = [ids]
176+ reads = self.read(cr, uid, ids, ['name', 'parent_id'], context=context)
177+ res = []
178+ for record in reads:
179+ name = record['name']
180+ if record['parent_id']:
181+ name = record['parent_id'][1] + ' / ' + name
182+ res.append((record['id'], name))
183+ return res
184+
185+ def name_search(self, cr, uid, name='', args=None, operator='ilike',
186+ context=None, limit=100):
187+ """Enable search on value returned by name_get ("parent / child")"""
188+ if not args:
189+ args = []
190+ if not context:
191+ context = {}
192+ if name:
193+ # Make sure name_search is symmetric to name_get
194+ name = name.split(' / ')[-1]
195+ ids = self.search(cr, uid, [('name', operator, name)] + args,
196+ limit=limit, context=context)
197+ else:
198+ ids = self.search(cr, uid, args, limit=limit, context=context)
199+ return self.name_get(cr, uid, ids, context=context)
200+
201+ def _complete_name(self, cr, uid, ids, field_name, arg, context=None):
202+ """Function-field wrapper to get the complete name from name_get"""
203+ res = self.name_get(cr, uid, ids, context=context)
204+ return dict(res)
205+
206+ def _progress_rate(self, cr, uid, ids, field_name, arg, context=None):
207+ """Rate of (sub)inventories done/total"""
208+ rates = {}
209+ for current_id in ids:
210+ nb = self.search(
211+ cr, uid, [('parent_id', 'child_of', current_id)],
212+ context=context, count=True)
213+ if not nb:
214+ # No inventory, consider it's 0% done
215+ rates[current_id] = 0
216+ continue
217+ nb_done = self.search(
218+ cr, uid, [('parent_id', 'child_of', current_id),
219+ ('state', '=', 'done')],
220+ context=context, count=True)
221+ rates[current_id] = 100 * nb_done / nb
222+ return rates
223+
224+ _columns = {
225+ # name_get() only changes the default name of the record, not the
226+ # content of the field "name" so we add another field for that
227+ 'complete_name': fields.function(
228+ _complete_name, type="char",
229+ string='Complete reference'),
230+ 'parent_id': fields.many2one(
231+ 'stock.inventory', 'Parent Inventory', ondelete='cascade', readonly=True,
232+ states={'draft': [('readonly', False)]}),
233+ 'inventory_ids': fields.one2many(
234+ 'stock.inventory', 'parent_id', 'List of Sub-inventories',
235+ readonly=True, states={'draft': [('readonly', False)]}),
236+ 'parent_left': fields.integer('Parent Left', select=1),
237+ 'parent_right': fields.integer('Parent Right', select=1),
238+ 'progress_rate': fields.function(
239+ _progress_rate, string='Progress', type='float'),
240+ }
241+
242+ _constraints = [
243+ (orm.Model._check_recursion,
244+ 'Error: You can not create recursive inventories.',
245+ ['parent_id']),
246+ ]
247+
248+ def create(self, cr, uid, vals, context=None):
249+ """Copy selected values from parent to child"""
250+ if vals and vals.get('parent_id'):
251+ existing_fields = self.fields_get_keys(cr, uid, context=context)
252+ parent_values = self.read(cr, uid, [vals['parent_id']],
253+ PARENT_VALUES, context=context)
254+ vals = vals.copy()
255+ vals.update({field: parent_values[0][field]
256+ for field in PARENT_VALUES
257+ if field in existing_fields})
258+ return super(HierarchicalInventory, self).create(
259+ cr, uid, vals, context=context)
260+
261+ def write(self, cr, uid, ids, vals, context=None):
262+ """Copy selected values from parent to children"""
263+ if context is None:
264+ context = {}
265+
266+ values = super(HierarchicalInventory, self).write(
267+ cr, uid, ids, vals, context=context)
268+ if not vals or context.get('norecurse', False):
269+ return values
270+
271+ # filter the fields we want to propagate
272+ children_values = {
273+ field: vals[field] for field in PARENT_VALUES if field in vals
274+ }
275+ if not children_values:
276+ return values
277+
278+ if not isinstance(ids, list):
279+ ids = [ids]
280+ # The context disables recursion - children are already included
281+ return self.write(
282+ cr, uid, self.search(cr, uid, [('parent_id', 'child_of', ids)]),
283+ children_values, context=dict(context, norecurse=True))
284+
285+ def action_cancel_inventory(self, cr, uid, ids, context=None):
286+ """Cancel inventory only if all the parents are canceled"""
287+ inventories = self.browse(cr, uid, ids, context=context)
288+ for inventory in inventories:
289+ while inventory.parent_id:
290+ inventory = inventory.parent_id
291+ if inventory.state != 'cancel':
292+ raise HierarchicalInventoryException(
293+ _('Warning'),
294+ _('One of the parent Inventories is not canceled.'))
295+ return super(HierarchicalInventory,
296+ self).action_cancel_inventory(cr, uid, ids,
297+ context=context)
298+
299+ def action_confirm(self, cr, uid, ids, context=None):
300+ """Confirm inventory only if all the children are confirmed"""
301+ children_count = self.search(
302+ cr, uid, [('parent_id', 'child_of', ids),
303+ ('state', 'not in', ['confirm', 'done'])],
304+ context=context, count=True)
305+ if children_count > 1:
306+ raise HierarchicalInventoryException(
307+ _('Warning'),
308+ _('Some Sub-inventories are not confirmed.'))
309+ return super(HierarchicalInventory, self).action_confirm(
310+ cr, uid, ids, context=context)
311+
312+ def action_done(self, cr, uid, ids, context=None):
313+ """Perform validation only if all the children states are 'done'."""
314+ children_count = self.search(cr, uid, [('parent_id', 'child_of', ids),
315+ ('state', '!=', 'done')],
316+ context=context, count=True)
317+ if children_count > 1:
318+ raise HierarchicalInventoryException(
319+ _('Warning'),
320+ _('Some Sub-inventories are not validated.'))
321+ return super(HierarchicalInventory, self).action_done(
322+ cr, uid, ids, context=context)
323
324=== added file 'stock_inventory_hierarchical/hierarchical_inventory_demo.xml'
325--- stock_inventory_hierarchical/hierarchical_inventory_demo.xml 1970-01-01 00:00:00 +0000
326+++ stock_inventory_hierarchical/hierarchical_inventory_demo.xml 2014-07-01 11:47:18 +0000
327@@ -0,0 +1,17 @@
328+<?xml version="1.0" encoding="utf-8"?>
329+<openerp>
330+ <data noupdate="0">
331+ <!-- Example Inventory with Sub-Inventories. -->
332+ <record id="stock_inventory_parent0" model="stock.inventory">
333+ <field name="name">Main Inventory</field>
334+ </record>
335+ <record id="child_1_id" model="stock.inventory">
336+ <field name="name">Sub-Inventory 1</field>
337+ <field name="parent_id" ref="stock_inventory_parent0" />
338+ </record>
339+ <record id="child_2_id" model="stock.inventory">
340+ <field name="name">Sub-Inventory 2</field>
341+ <field name="parent_id" ref="stock_inventory_parent0" />
342+ </record>
343+ </data>
344+</openerp>
345
346=== added file 'stock_inventory_hierarchical/hierarchical_inventory_view.xml'
347--- stock_inventory_hierarchical/hierarchical_inventory_view.xml 1970-01-01 00:00:00 +0000
348+++ stock_inventory_hierarchical/hierarchical_inventory_view.xml 2014-07-01 11:47:18 +0000
349@@ -0,0 +1,84 @@
350+<?xml version="1.0" encoding="utf-8"?>
351+<openerp>
352+ <data>
353+ <!-- Add parent_id and number of Sub-inventories to form view -->
354+ <record model="ir.ui.view" id="stock_inventory_hierarchical_tree_view">
355+ <field name="name">hierarchical.inventory.tree</field>
356+ <field name="model">stock.inventory</field>
357+ <field name="inherit_id" ref="stock.view_inventory_tree" />
358+ <field name="field_parent">inventory_ids</field>
359+ <field name="arch" type="xml">
360+ <xpath expr="//field[@name='name']" position="replace">
361+ <field name="complete_name" string="Reference"/>
362+ </xpath>
363+ <xpath expr="//field[@name='state']" position="after">
364+ <field name="progress_rate" widget="progressbar" />
365+ </xpath>
366+ </field>
367+ </record>
368+
369+ <!-- Add the parent_id filter to search view -->
370+ <record model="ir.ui.view" id="view_inventory_subinventories_filter">
371+ <field name="name">hierarchical.inventory.filter</field>
372+ <field name="model">stock.inventory</field>
373+ <field name="inherit_id" ref="stock.view_inventory_filter" />
374+ <field name="arch" type="xml">
375+ <xpath expr="//field[@name='name']" position="before">
376+ <filter icon="terp-check" name="main_inventories" string="Main inventories" domain="[('parent_id', '=', False)]" help="Only select inventories that have no parents." />
377+ <separator orientation="vertical"/>
378+ </xpath>
379+ <xpath expr="//field[@name='date']" position="after">
380+ <field name="parent_id" />
381+ </xpath>
382+ </field>
383+ </record>
384+ <!-- Show main inventories by default -->
385+ <record id="stock.action_inventory_form" model="ir.actions.act_window">
386+ <field name="context">{'full':'1', 'search_default_main_inventories':1}</field>
387+ </record>
388+
389+ <record model="ir.ui.view" id="stock_inventory_hierarchical_form_view">
390+ <field name="name">hierarchical.inventory.form</field>
391+ <field name="model">stock.inventory</field>
392+ <field name="inherit_id" ref="stock.view_inventory_form" />
393+ <field name="arch" type="xml">
394+ <xpath expr="/form//div[@class='oe_right oe_button_box']" position="inside">
395+ <button name="%(action_view_sub_inventory)d" string="View Sub-inventories" type="action" />
396+ </xpath>
397+ <xpath expr="/form//field[@name='name']" position="after">
398+ <field name="parent_id"/>
399+ </xpath>
400+ <xpath expr="/form//field[@name='date']" position="attributes">
401+ <attribute name="attrs">{'readonly':[('parent_id', '!=', False)]}</attribute>
402+ </xpath>
403+ <xpath expr="/form//field[@name='date']" position="after">
404+ <field name="progress_rate" widget="progressbar" />
405+ </xpath>
406+ <xpath
407+ expr="//page[@string='General Information']"
408+ position="after">
409+ <page string="Sub-inventories">
410+ <field name="inventory_ids" nolabel="1" context="{'default_parent_id': active_id}">
411+ <tree>
412+ <field name="name" />
413+ <field name="state" />
414+ <field name="progress_rate" widget="progressbar" />
415+ </tree>
416+ </field>
417+ </page>
418+ </xpath>
419+ </field>
420+ </record>
421+
422+ <!-- Open the children of the current Inventory in a distinct list
423+ to let users work in a normal window instead of a popup -->
424+ <act_window id="action_view_sub_inventory"
425+ name="View Sub-inventories"
426+ res_model="stock.inventory"
427+ src_model="stock.inventory"
428+ view_mode="tree,form"
429+ view_type="form"
430+ domain="[('parent_id', 'child_of', active_id),('id', '!=', active_id)]"
431+ context="{'full':1, 'search_default_main_inventories':0}"/>
432+ </data>
433+</openerp>
434
435=== added directory 'stock_inventory_hierarchical/i18n'
436=== added file 'stock_inventory_hierarchical/i18n/fr.po'
437--- stock_inventory_hierarchical/i18n/fr.po 1970-01-01 00:00:00 +0000
438+++ stock_inventory_hierarchical/i18n/fr.po 2014-07-01 11:47:18 +0000
439@@ -0,0 +1,116 @@
440+# Translation of OpenERP Server.
441+# This file contains the translation of the following modules:
442+# * stock_inventory_hierarchical
443+#
444+msgid ""
445+msgstr ""
446+"Project-Id-Version: OpenERP Server 7.0\n"
447+"Report-Msgid-Bugs-To: \n"
448+"POT-Creation-Date: 2014-07-01 11:15+0000\n"
449+"PO-Revision-Date: 2014-07-01 11:15+0000\n"
450+"Last-Translator: <>\n"
451+"Language-Team: \n"
452+"MIME-Version: 1.0\n"
453+"Content-Type: text/plain; charset=UTF-8\n"
454+"Content-Transfer-Encoding: \n"
455+"Plural-Forms: \n"
456+
457+#. module: stock_inventory_hierarchical
458+#: field:stock.inventory,complete_name:0
459+msgid "Complete reference"
460+msgstr "Réference complète"
461+
462+#. module: stock_inventory_hierarchical
463+#: constraint:stock.inventory:0
464+msgid "Error: You can not create recursive inventories."
465+msgstr "Erreur : Vous ne pouvez pas créer d'inventaire récursifs."
466+
467+#. module: stock_inventory_hierarchical
468+#: code:_description:0
469+#: model:ir.model,name:stock_inventory_hierarchical.model_stock_inventory
470+#, python-format
471+msgid "Inventory"
472+msgstr "Inventaire"
473+
474+#. module: stock_inventory_hierarchical
475+#: field:stock.inventory,inventory_ids:0
476+msgid "List of Sub-inventories"
477+msgstr "Liste des sous-inventaires"
478+
479+#. module: stock_inventory_hierarchical
480+#: view:stock.inventory:0
481+msgid "Main inventories"
482+msgstr "Inventaires principaux"
483+
484+#. module: stock_inventory_hierarchical
485+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:174
486+#, python-format
487+msgid "One of the parent Inventories is not canceled."
488+msgstr "Un des inventaires pères n'est pas annulé."
489+
490+#. module: stock_inventory_hierarchical
491+#: view:stock.inventory:0
492+msgid "Only select inventories that have no parents."
493+msgstr "Sélectionne uniquement les inventaires qui n'ont pas de père."
494+
495+#. module: stock_inventory_hierarchical
496+#: field:stock.inventory,parent_id:0
497+msgid "Parent Inventory"
498+msgstr "Inventaire père"
499+
500+#. module: stock_inventory_hierarchical
501+#: field:stock.inventory,parent_left:0
502+msgid "Parent Left"
503+msgstr "Parent gauche"
504+
505+#. module: stock_inventory_hierarchical
506+#: field:stock.inventory,parent_right:0
507+msgid "Parent Right"
508+msgstr "Parent droit"
509+
510+#. module: stock_inventory_hierarchical
511+#: field:stock.inventory,progress_rate:0
512+msgid "Progress"
513+msgstr "Avancement"
514+
515+#. module: stock_inventory_hierarchical
516+#: view:stock.inventory:0
517+msgid "Reference"
518+msgstr "Référence"
519+
520+#. module: stock_inventory_hierarchical
521+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:188
522+#, python-format
523+msgid "Some Sub-inventories are not confirmed."
524+msgstr "Certains sous-inventaires ne sont pas confirmés."
525+
526+#. module: stock_inventory_hierarchical
527+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:200
528+#, python-format
529+msgid "Some Sub-inventories are not validated."
530+msgstr "Certains sous-inventaires ne sont pas terminés."
531+
532+#. module: stock_inventory_hierarchical
533+#: view:stock.inventory:0
534+msgid "Sub-inventories"
535+msgstr "Sous-inventaires"
536+
537+#. module: stock_inventory_hierarchical
538+#: model:ir.actions.act_window,name:stock_inventory_hierarchical.action_view_sub_inventory
539+#: view:stock.inventory:0
540+msgid "View Sub-inventories"
541+msgstr "Voir les sous-inventaires"
542+
543+#. module: stock_inventory_hierarchical
544+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:173
545+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:187
546+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:199
547+#, python-format
548+msgid "Warning"
549+msgstr "Attention"
550+
551+#. module: stock_inventory_hierarchical
552+#: view:stock.inventory:0
553+msgid "{'readonly':[('parent_id', '!=', False)]}"
554+msgstr "{'readonly':[('parent_id', '!=', False)]}"
555+
556
557=== added file 'stock_inventory_hierarchical/i18n/stock_inventory_hierarchical.pot'
558--- stock_inventory_hierarchical/i18n/stock_inventory_hierarchical.pot 1970-01-01 00:00:00 +0000
559+++ stock_inventory_hierarchical/i18n/stock_inventory_hierarchical.pot 2014-07-01 11:47:18 +0000
560@@ -0,0 +1,116 @@
561+# Translation of OpenERP Server.
562+# This file contains the translation of the following modules:
563+# * stock_inventory_hierarchical
564+#
565+msgid ""
566+msgstr ""
567+"Project-Id-Version: OpenERP Server 7.0\n"
568+"Report-Msgid-Bugs-To: \n"
569+"POT-Creation-Date: 2014-07-01 11:26+0000\n"
570+"PO-Revision-Date: 2014-07-01 11:26+0000\n"
571+"Last-Translator: <>\n"
572+"Language-Team: \n"
573+"MIME-Version: 1.0\n"
574+"Content-Type: text/plain; charset=UTF-8\n"
575+"Content-Transfer-Encoding: \n"
576+"Plural-Forms: \n"
577+
578+#. module: stock_inventory_hierarchical
579+#: field:stock.inventory,complete_name:0
580+msgid "Complete reference"
581+msgstr ""
582+
583+#. module: stock_inventory_hierarchical
584+#: constraint:stock.inventory:0
585+msgid "Error: You can not create recursive inventories."
586+msgstr ""
587+
588+#. module: stock_inventory_hierarchical
589+#: code:_description:0
590+#: model:ir.model,name:stock_inventory_hierarchical.model_stock_inventory
591+#, python-format
592+msgid "Inventory"
593+msgstr ""
594+
595+#. module: stock_inventory_hierarchical
596+#: field:stock.inventory,inventory_ids:0
597+msgid "List of Sub-inventories"
598+msgstr ""
599+
600+#. module: stock_inventory_hierarchical
601+#: view:stock.inventory:0
602+msgid "Main inventories"
603+msgstr ""
604+
605+#. module: stock_inventory_hierarchical
606+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:174
607+#, python-format
608+msgid "One of the parent Inventories is not canceled."
609+msgstr ""
610+
611+#. module: stock_inventory_hierarchical
612+#: view:stock.inventory:0
613+msgid "Only select inventories that have no parents."
614+msgstr ""
615+
616+#. module: stock_inventory_hierarchical
617+#: field:stock.inventory,parent_id:0
618+msgid "Parent Inventory"
619+msgstr ""
620+
621+#. module: stock_inventory_hierarchical
622+#: field:stock.inventory,parent_left:0
623+msgid "Parent Left"
624+msgstr ""
625+
626+#. module: stock_inventory_hierarchical
627+#: field:stock.inventory,parent_right:0
628+msgid "Parent Right"
629+msgstr ""
630+
631+#. module: stock_inventory_hierarchical
632+#: field:stock.inventory,progress_rate:0
633+msgid "Progress"
634+msgstr ""
635+
636+#. module: stock_inventory_hierarchical
637+#: view:stock.inventory:0
638+msgid "Reference"
639+msgstr ""
640+
641+#. module: stock_inventory_hierarchical
642+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:188
643+#, python-format
644+msgid "Some Sub-inventories are not confirmed."
645+msgstr ""
646+
647+#. module: stock_inventory_hierarchical
648+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:200
649+#, python-format
650+msgid "Some Sub-inventories are not validated."
651+msgstr ""
652+
653+#. module: stock_inventory_hierarchical
654+#: view:stock.inventory:0
655+msgid "Sub-inventories"
656+msgstr ""
657+
658+#. module: stock_inventory_hierarchical
659+#: model:ir.actions.act_window,name:stock_inventory_hierarchical.action_view_sub_inventory
660+#: view:stock.inventory:0
661+msgid "View Sub-inventories"
662+msgstr ""
663+
664+#. module: stock_inventory_hierarchical
665+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:173
666+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:187
667+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:199
668+#, python-format
669+msgid "Warning"
670+msgstr ""
671+
672+#. module: stock_inventory_hierarchical
673+#: view:stock.inventory:0
674+msgid "{'readonly':[('parent_id', '!=', False)]}"
675+msgstr ""
676+
677
678=== added directory 'stock_inventory_hierarchical/images'
679=== added file 'stock_inventory_hierarchical/images/inventory_form.png'
680Binary files stock_inventory_hierarchical/images/inventory_form.png 1970-01-01 00:00:00 +0000 and stock_inventory_hierarchical/images/inventory_form.png 2014-07-01 11:47:18 +0000 differ
681=== added file 'stock_inventory_hierarchical/images/inventory_form_actions.png'
682Binary files stock_inventory_hierarchical/images/inventory_form_actions.png 1970-01-01 00:00:00 +0000 and stock_inventory_hierarchical/images/inventory_form_actions.png 2014-07-01 11:47:18 +0000 differ
683=== added directory 'stock_inventory_hierarchical/static'
684=== added directory 'stock_inventory_hierarchical/static/src'
685=== added directory 'stock_inventory_hierarchical/static/src/img'
686=== added file 'stock_inventory_hierarchical/static/src/img/icon.png'
687Binary files stock_inventory_hierarchical/static/src/img/icon.png 1970-01-01 00:00:00 +0000 and stock_inventory_hierarchical/static/src/img/icon.png 2014-07-01 11:47:18 +0000 differ
688=== added directory 'stock_inventory_hierarchical/test'
689=== added file 'stock_inventory_hierarchical/test/hierarchical_inventory_test.yml'
690--- stock_inventory_hierarchical/test/hierarchical_inventory_test.yml 1970-01-01 00:00:00 +0000
691+++ stock_inventory_hierarchical/test/hierarchical_inventory_test.yml 2014-07-01 11:47:18 +0000
692@@ -0,0 +1,96 @@
693+-
694+ In this file, i check rules about hierarchical inventories.
695+ Children date must be the same of parent date for each state,
696+ the state of parent and children can change only if conditions are correct.
697+-
698+ Check if date of children are the same as the parent's.
699+-
700+ !python {model: stock.inventory}: |
701+ parent_date = self.read(
702+ cr, uid, [ref('stock_inventory_parent0')], ['date'])[0]['date']
703+ child_1_date = self.read(
704+ cr, uid, [ref('child_1_id')], ['date'])[0]['date']
705+ assert child_1_date == parent_date, "Date are different: %s - %s" % (parent_date, child_1_date)
706+
707+ child_2_date = self.read(
708+ cr, uid, [ref('child_2_id')], ['date'])[0]['date']
709+ assert child_2_date == parent_date, "Date are different: %s - %s" % (parent_date, child_2_date)
710+
711+-
712+ Check if children cannot be canceled if the parent was not canceled.
713+ I'll try to cancel both children inventory while parent inventory having "draft" state.
714+ After, i'll verify the state of each inventory.
715+-
716+ !python {model: stock.inventory}: |
717+ from stock_inventory_hierarchical import HierarchicalInventoryException
718+ try:
719+ self.action_cancel_inventory(cr, uid, [ref('child_1_id')])
720+ except HierarchicalInventoryException as e:
721+ log("Good ! The Inventory could not be canceled: %s" % e)
722+ try:
723+ self.action_cancel_inventory(cr, uid, [ref('child_2_id')])
724+ except HierarchicalInventoryException as e:
725+ log("Good ! The Inventory could not be canceled: %s" % e)
726+ child_1_state = self.read(cr, uid, [ref('child_1_id')], ['state'])[0]['state']
727+ assert child_1_state == 'draft', "Child inventory 1 have '%s' state. It should be 'draft'" % child_1_state
728+ child_2_state = self.read(cr, uid, [ref('child_2_id')], ['state'])[0]['state']
729+ assert child_2_state == 'draft', "Child inventory 2 have '%s' state. It should be 'draft'" % child_2_state
730+
731+-
732+ Check if children inventory have confirm state before confirm parent inventory.
733+ To check this, i'll try to confirm parent inventory when children inventory having "draft" state,
734+ and i'll check if state is still 'draft'.
735+-
736+ !python {model: stock.inventory}: |
737+ from stock_inventory_hierarchical import HierarchicalInventoryException
738+ try:
739+ self.action_confirm(cr, uid, [ref('stock_inventory_parent0')])
740+ except HierarchicalInventoryException as e:
741+ log("Good, the inventory could not be confirmed: %s", e)
742+ parent_state = self.read(cr, uid, [ref('stock_inventory_parent0')], ['state'])[0]['state']
743+ assert parent_state == 'draft', "Parent inventory have '%s' state. It should be 'draft'" % parent_state
744+
745+-
746+ In order, i'll confirm the children inventories, and the parent inventory after.
747+-
748+ !python {model: stock.inventory}: |
749+ self.action_confirm(cr, uid, [ref('child_1_id')])
750+ child_1_state = self.read(cr, uid, [ref('child_1_id')], ['state'])[0]['state']
751+ assert child_1_state == 'confirm', "Child inventory 1 have '%s' state. It should be 'confirm'" % child_1_state
752+
753+ self.action_confirm(cr, uid, [ref('child_2_id')])
754+ child_2_state = self.read(cr, uid, [ref('child_2_id')], ['state'])[0]['state']
755+ assert child_2_state == 'confirm', "Child inventory 2 have '%s' state. It should be 'confirm'" % child_2_state
756+
757+ self.action_confirm(cr, uid, [ref('stock_inventory_parent0')])
758+ parent_state = self.read(cr, uid, [ref('stock_inventory_parent0')], ['state'])[0]['state']
759+ assert parent_state == 'confirm', "Parent inventory have '%s' state. It should be 'confirm'" % parent_state
760+
761+-
762+ Check if children inventory have done state before validate parent inventory.
763+ I'll try to validate parent inventory before children.
764+-
765+ !python {model: stock.inventory}: |
766+ from stock_inventory_hierarchical import HierarchicalInventoryException
767+ try:
768+ self.action_done(cr, uid, [ref('stock_inventory_parent0')])
769+ except HierarchicalInventoryException as e:
770+ log("Good, the inventory could not be validated: %s", e)
771+ parent_state = self.read(cr, uid, [ref('stock_inventory_parent0')], ['state'])[0]['state']
772+ assert parent_state == 'confirm', "Parent inventory have '%s' state. It should be 'confirm'" % parent_state
773+
774+-
775+ Now, i'll validate all children inventory before validate the parent.
776+-
777+ !python {model: stock.inventory}: |
778+ self.action_done(cr, uid, [ref('child_1_id')])
779+ child_1_state = self.read(cr, uid, [ref('child_1_id')], ['state'])[0]['state']
780+ assert child_1_state == 'done', "Child inventory 1 have '%s' state. It should be 'done'" % child_1_state
781+
782+ self.action_done(cr, uid, [ref('child_2_id')])
783+ child_2_state = self.read(cr, uid, [ref('child_2_id')], ['state'])[0]['state']
784+ assert child_2_state == 'done', "Child inventory 2 have '%s' state. It should be 'done'" % child_2_state
785+
786+ self.action_done(cr, uid, [ref('stock_inventory_parent0')])
787+ parent_state = self.read(cr, uid, [ref('stock_inventory_parent0')], ['state'])[0]['state']
788+ assert parent_state == 'done', "Parent inventory have '%s' state. It should be 'done'" % parent_state
789
790=== added directory 'stock_inventory_hierarchical_location'
791=== added file 'stock_inventory_hierarchical_location/__init__.py'
792--- stock_inventory_hierarchical_location/__init__.py 1970-01-01 00:00:00 +0000
793+++ stock_inventory_hierarchical_location/__init__.py 2014-07-01 11:47:18 +0000
794@@ -0,0 +1,22 @@
795+# -*- coding: utf-8 -*-
796+##############################################################################
797+#
798+# This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
799+#
800+# This program is free software: you can redistribute it and/or modify
801+# it under the terms of the GNU General Public License as published by
802+# the Free Software Foundation, either version 3 of the License, or
803+# (at your option) any later version.
804+#
805+# This program is distributed in the hope that it will be useful,
806+# but WITHOUT ANY WARRANTY; without even the implied warranty of
807+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
808+# GNU General Public License for more details.
809+#
810+# You should have received a copy of the GNU General Public License
811+# along with this program. If not, see <http://www.gnu.org/licenses/>.
812+#
813+##############################################################################
814+
815+from . import inventory_hierarchical_location
816+from . import wizard
817
818=== added file 'stock_inventory_hierarchical_location/__openerp__.py'
819--- stock_inventory_hierarchical_location/__openerp__.py 1970-01-01 00:00:00 +0000
820+++ stock_inventory_hierarchical_location/__openerp__.py 2014-07-01 11:47:18 +0000
821@@ -0,0 +1,49 @@
822+# -*- coding: utf-8 -*-
823+##############################################################################
824+#
825+# This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
826+#
827+# This program is free software: you can redistribute it and/or modify
828+# it under the terms of the GNU General Public License as published by
829+# the Free Software Foundation, either version 3 of the License, or
830+# (at your option) any later version.
831+#
832+# This program is distributed in the hope that it will be useful,
833+# but WITHOUT ANY WARRANTY; without even the implied warranty of
834+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
835+# GNU General Public License for more details.
836+#
837+# You should have received a copy of the GNU General Public License
838+# along with this program. If not, see <http://www.gnu.org/licenses/>.
839+#
840+##############################################################################
841+
842+{
843+ "name": "Exhaustive and hierarchical Stock Inventories",
844+ "version": "1.1",
845+ "depends": ["stock_inventory_hierarchical", "stock_inventory_location"],
846+ "auto_install": True,
847+ "author": u"Numérigraphe",
848+ "category": "Hidden",
849+ "description": """
850+Make exhaustive Inventories aware of their Sub-Inventories.
851+===========================================================
852+
853+This module allows an inventory to contain a general Location,
854+and it's sub-inventories to contain some of it's sub-Locations.
855+It will prevent you from setting the Inventories and sub-Inventories
856+in inconsistent status.
857+
858+This module will be installed automatically if the modules
859+"stock_inventory_location" and "stock_inventory_hierarchical" are both
860+installed.
861+You must keep this module installed to ensure proper functioning.
862+
863+ """,
864+ "data": [
865+ "inventory_hierarchical_location_view.xml",
866+ "wizard/generate_inventory_view.xml",
867+ ],
868+ "test": ["tests/inventory_hierarchical_location_test.yml"],
869+ "demo": ["inventory_hierarchical_location_demo.xml"],
870+}
871
872=== added directory 'stock_inventory_hierarchical_location/i18n'
873=== added file 'stock_inventory_hierarchical_location/i18n/fr.po'
874--- stock_inventory_hierarchical_location/i18n/fr.po 1970-01-01 00:00:00 +0000
875+++ stock_inventory_hierarchical_location/i18n/fr.po 2014-07-01 11:47:18 +0000
876@@ -0,0 +1,164 @@
877+# Translation of OpenERP Server.
878+# This file contains the translation of the following modules:
879+# * stock_inventory_hierarchical_location
880+#
881+msgid ""
882+msgstr ""
883+"Project-Id-Version: OpenERP Server 7.0\n"
884+"Report-Msgid-Bugs-To: \n"
885+"POT-Creation-Date: 2014-07-01 11:35+0000\n"
886+"PO-Revision-Date: 2014-07-01 11:35+0000\n"
887+"Last-Translator: <>\n"
888+"Language-Team: \n"
889+"MIME-Version: 1.0\n"
890+"Content-Type: text/plain; charset=UTF-8\n"
891+"Content-Transfer-Encoding: \n"
892+"Plural-Forms: \n"
893+
894+#. module: stock_inventory_hierarchical_location
895+#: view:stock.generate.inventory:0
896+msgid "Cancel"
897+msgstr "Cancel"
898+
899+#. module: stock_inventory_hierarchical_location
900+#: code:_description:0
901+#: model:ir.actions.act_window,name:stock_inventory_hierarchical_location.action_view_stock_generate_inventory
902+#: model:ir.model,name:stock_inventory_hierarchical_location.model_stock_generate_inventory
903+#: model:ir.ui.menu,name:stock_inventory_hierarchical_location.menu_action_stock_generate_inventory_form
904+#: view:stock.generate.inventory:0
905+#, python-format
906+msgid "Generate Inventory"
907+msgstr "Générer l'inventaire"
908+
909+#. module: stock_inventory_hierarchical_location
910+#: help:stock.generate.inventory,only_view:0
911+msgid "If set, only inventory on view location can be created"
912+msgstr "Si coché, seuls les inventaires des emplacements vues seront créés."
913+
914+#. module: stock_inventory_hierarchical_location
915+#: code:_description:0
916+#: model:ir.model,name:stock_inventory_hierarchical_location.model_stock_fill_inventory
917+#, python-format
918+msgid "Import Inventory"
919+msgstr "Importer un inventaire"
920+
921+#. module: stock_inventory_hierarchical_location
922+#: code:_description:0
923+#: model:ir.model,name:stock_inventory_hierarchical_location.model_stock_inventory
924+#, python-format
925+msgid "Inventory"
926+msgstr "Inventaire"
927+
928+#. module: stock_inventory_hierarchical_location
929+#: code:addons/stock_inventory_hierarchical_location/wizard/generate_inventory.py:124
930+#, python-format
931+msgid "Inventory generated"
932+msgstr "Inventaire généré"
933+
934+#. module: stock_inventory_hierarchical_location
935+#: field:stock.generate.inventory,prefix_inv_name:0
936+msgid "Inventory prefix"
937+msgstr "Préfixe de l'inventaire"
938+
939+#. module: stock_inventory_hierarchical_location
940+#: field:stock.generate.inventory,level:0
941+msgid "Level"
942+msgstr "Profondeur"
943+
944+#. module: stock_inventory_hierarchical_location
945+#: sql_constraint:stock.generate.inventory:0
946+msgid "Level must be positive!"
947+msgstr "La profondeur doit être positive!"
948+
949+#. module: stock_inventory_hierarchical_location
950+#: field:stock.generate.inventory,location_id:0
951+msgid "Location"
952+msgstr "Emplacement"
953+
954+#. module: stock_inventory_hierarchical_location
955+#: code:addons/stock_inventory_hierarchical_location/wizard/stock_fill_location_inventory.py:78
956+#: code:addons/stock_inventory_hierarchical_location/wizard/stock_fill_location_inventory.py:84
957+#, python-format
958+msgid "No product in this location. Please select a location in the product form."
959+msgstr ""
960+"Aucun article dans cet emplacement. Veuillez choisir un emplacement dans le "
961+"formulaire produit."
962+
963+#. module: stock_inventory_hierarchical_location
964+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:43
965+#, python-format
966+msgid "One of the parent inventories is not open."
967+msgstr "Un des inventaires pères n'est pas ouvert."
968+
969+#. module: stock_inventory_hierarchical_location
970+#: field:stock.generate.inventory,only_view:0
971+msgid "Only view"
972+msgstr "Vues seulement"
973+
974+#. module: stock_inventory_hierarchical_location
975+#: help:stock.generate.inventory,prefix_inv_name:0
976+msgid "Optional prefix for all created inventory"
977+msgstr "Préfixe facultatif ajouté devant les noms des inventaires créés"
978+
979+#. module: stock_inventory_hierarchical_location
980+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:65
981+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:79
982+#, python-format
983+msgid "Some Sub-inventories are not confirmed."
984+msgstr "Au moins un sous-inventaire n'est pas confirmé."
985+
986+#. module: stock_inventory_hierarchical_location
987+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:84
988+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:98
989+#, python-format
990+msgid "This location is not declared on the parent inventory\n"
991+"It cannot be added."
992+msgstr "Cet emplacement n'est pas déclaré dans l'inventaire parent\n"
993+"Vous ne pouvez pas l'ajouter."
994+
995+#. module: stock_inventory_hierarchical_location
996+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:42
997+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:64
998+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:78
999+#, python-format
1000+msgid "Warning"
1001+msgstr "Attention"
1002+
1003+#. module: stock_inventory_hierarchical_location
1004+#: code:addons/stock_inventory_hierarchical_location/wizard/stock_fill_location_inventory.py:84
1005+#, python-format
1006+msgid "Warning!"
1007+msgstr "Attention!"
1008+
1009+#. module: stock_inventory_hierarchical_location
1010+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:83
1011+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:97
1012+#, python-format
1013+msgid "Warning: Wrong location"
1014+msgstr "Attention: emplacement incorrect"
1015+
1016+#. module: stock_inventory_hierarchical_location
1017+#: help:stock.generate.inventory,level:0
1018+msgid "Maximum number of intermediate sub-inventories between the main inventory and the smallest sub-inventory."
1019+msgstr "Nombre maximum de niveaux entre l'inventaire principal et le plus petit sous-inventaire."
1020+
1021+#. module: stock_inventory_hierarchical_location
1022+#: view:stock.inventory:0
1023+msgid "onchange_location_id(location_id)"
1024+msgstr "onchange_location_id(location_id)"
1025+
1026+#. module: stock_inventory_hierarchical_location
1027+#: view:stock.generate.inventory:0
1028+msgid "or"
1029+msgstr "ou"
1030+
1031+#. module: stock_inventory_hierarchical_location
1032+#: view:stock.inventory:0
1033+msgid "{'default_parent_id': active_id, 'default_exhaustive': exhaustive}"
1034+msgstr "{'default_parent_id': active_id, 'default_exhaustive': exhaustive}"
1035+
1036+#. module: stock_inventory_hierarchical_location
1037+#: view:stock.inventory:0
1038+msgid "{'readonly':[('parent_id', '!=', False)]}"
1039+msgstr "{'readonly':[('parent_id', '!=', False)]}"
1040+
1041
1042=== added file 'stock_inventory_hierarchical_location/i18n/stock_inventory_hierarchical_location.pot'
1043--- stock_inventory_hierarchical_location/i18n/stock_inventory_hierarchical_location.pot 1970-01-01 00:00:00 +0000
1044+++ stock_inventory_hierarchical_location/i18n/stock_inventory_hierarchical_location.pot 2014-07-01 11:47:18 +0000
1045@@ -0,0 +1,166 @@
1046+# Translation of OpenERP Server.
1047+# This file contains the translation of the following modules:
1048+# * stock_inventory_hierarchical_location
1049+#
1050+msgid ""
1051+msgstr ""
1052+"Project-Id-Version: OpenERP Server 7.0\n"
1053+"Report-Msgid-Bugs-To: \n"
1054+"POT-Creation-Date: 2014-07-01 11:32+0000\n"
1055+"PO-Revision-Date: 2014-07-01 11:32+0000\n"
1056+"Last-Translator: <>\n"
1057+"Language-Team: \n"
1058+"MIME-Version: 1.0\n"
1059+"Content-Type: text/plain; charset=UTF-8\n"
1060+"Content-Transfer-Encoding: \n"
1061+"Plural-Forms: \n"
1062+
1063+#. module: stock_inventory_hierarchical_location
1064+#: view:stock.generate.inventory:0
1065+msgid "Cancel"
1066+msgstr ""
1067+
1068+#. module: stock_inventory_hierarchical_location
1069+#: code:_description:0
1070+#: model:ir.actions.act_window,name:stock_inventory_hierarchical_location.action_view_stock_generate_inventory
1071+#: model:ir.model,name:stock_inventory_hierarchical_location.model_stock_generate_inventory
1072+#: model:ir.ui.menu,name:stock_inventory_hierarchical_location.menu_action_stock_generate_inventory_form
1073+#: view:stock.generate.inventory:0
1074+#, python-format
1075+msgid "Generate Inventory"
1076+msgstr ""
1077+
1078+#. module: stock_inventory_hierarchical_location
1079+#: view:stock.generate.inventory:0
1080+msgid "Generate inventory"
1081+msgstr ""
1082+
1083+#. module: stock_inventory_hierarchical_location
1084+#: help:stock.generate.inventory,only_view:0
1085+msgid "If set, only inventory on view location can be created"
1086+msgstr ""
1087+
1088+#. module: stock_inventory_hierarchical_location
1089+#: code:_description:0
1090+#: model:ir.model,name:stock_inventory_hierarchical_location.model_stock_fill_inventory
1091+#, python-format
1092+msgid "Import Inventory"
1093+msgstr ""
1094+
1095+#. module: stock_inventory_hierarchical_location
1096+#: code:_description:0
1097+#: model:ir.model,name:stock_inventory_hierarchical_location.model_stock_inventory
1098+#, python-format
1099+msgid "Inventory"
1100+msgstr ""
1101+
1102+#. module: stock_inventory_hierarchical_location
1103+#: code:addons/stock_inventory_hierarchical_location/wizard/generate_inventory.py:124
1104+#, python-format
1105+msgid "Inventory generated"
1106+msgstr ""
1107+
1108+#. module: stock_inventory_hierarchical_location
1109+#: field:stock.generate.inventory,prefix_inv_name:0
1110+msgid "Inventory prefix"
1111+msgstr ""
1112+
1113+#. module: stock_inventory_hierarchical_location
1114+#: field:stock.generate.inventory,level:0
1115+msgid "Level"
1116+msgstr ""
1117+
1118+#. module: stock_inventory_hierarchical_location
1119+#: sql_constraint:stock.generate.inventory:0
1120+msgid "Level must be positive!"
1121+msgstr ""
1122+
1123+#. module: stock_inventory_hierarchical_location
1124+#: field:stock.generate.inventory,location_id:0
1125+msgid "Location"
1126+msgstr ""
1127+
1128+#. module: stock_inventory_hierarchical_location
1129+#: code:addons/stock_inventory_hierarchical_location/wizard/stock_fill_location_inventory.py:78
1130+#: code:addons/stock_inventory_hierarchical_location/wizard/stock_fill_location_inventory.py:84
1131+#, python-format
1132+msgid "No product in this location. Please select a location in the product form."
1133+msgstr ""
1134+
1135+#. module: stock_inventory_hierarchical_location
1136+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:43
1137+#, python-format
1138+msgid "One of the parent inventories is not open."
1139+msgstr ""
1140+
1141+#. module: stock_inventory_hierarchical_location
1142+#: field:stock.generate.inventory,only_view:0
1143+msgid "Only view"
1144+msgstr ""
1145+
1146+#. module: stock_inventory_hierarchical_location
1147+#: help:stock.generate.inventory,prefix_inv_name:0
1148+msgid "Optional prefix for all created inventory"
1149+msgstr ""
1150+
1151+#. module: stock_inventory_hierarchical_location
1152+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:65
1153+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:79
1154+#, python-format
1155+msgid "Some Sub-inventories are not confirmed."
1156+msgstr ""
1157+
1158+#. module: stock_inventory_hierarchical_location
1159+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:84
1160+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:98
1161+#, python-format
1162+msgid "This location is not declared on the parent inventory\n"
1163+"It cannot be added."
1164+msgstr ""
1165+
1166+#. module: stock_inventory_hierarchical_location
1167+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:42
1168+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:64
1169+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:78
1170+#, python-format
1171+msgid "Warning"
1172+msgstr ""
1173+
1174+#. module: stock_inventory_hierarchical_location
1175+#: code:addons/stock_inventory_hierarchical_location/wizard/stock_fill_location_inventory.py:84
1176+#, python-format
1177+msgid "Warning!"
1178+msgstr ""
1179+
1180+#. module: stock_inventory_hierarchical_location
1181+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:83
1182+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:97
1183+#, python-format
1184+msgid "Warning: Wrong location"
1185+msgstr ""
1186+
1187+#. module: stock_inventory_hierarchical_location
1188+#: help:stock.generate.inventory,level:0
1189+msgid "Maximum number of intermediate sub-inventories between the main inventory and the smallest sub-inventory."
1190+msgstr ""
1191+
1192+#. module: stock_inventory_hierarchical_location
1193+#: view:stock.inventory:0
1194+msgid "onchange_location_id(location_id)"
1195+msgstr ""
1196+
1197+#. module: stock_inventory_hierarchical_location
1198+#: view:stock.generate.inventory:0
1199+msgid "or"
1200+msgstr ""
1201+
1202+#. module: stock_inventory_hierarchical_location
1203+#: view:stock.inventory:0
1204+msgid "{'default_parent_id': active_id, 'default_exhaustive': exhaustive}"
1205+msgstr ""
1206+
1207+#. module: stock_inventory_hierarchical_location
1208+#: view:stock.inventory:0
1209+msgid "{'readonly':[('parent_id', '!=', False)]}"
1210+msgstr ""
1211+
1212
1213=== added file 'stock_inventory_hierarchical_location/inventory_hierarchical_location.py'
1214--- stock_inventory_hierarchical_location/inventory_hierarchical_location.py 1970-01-01 00:00:00 +0000
1215+++ stock_inventory_hierarchical_location/inventory_hierarchical_location.py 2014-07-01 11:47:18 +0000
1216@@ -0,0 +1,102 @@
1217+# -*- coding: utf-8 -*-
1218+##############################################################################
1219+#
1220+# This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
1221+#
1222+# This program is free software: you can redistribute it and/or modify
1223+# it under the terms of the GNU General Public License as published by
1224+# the Free Software Foundation, either version 3 of the License, or
1225+# (at your option) any later version.
1226+#
1227+# This program is distributed in the hope that it will be useful,
1228+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1229+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1230+# GNU General Public License for more details.
1231+#
1232+# You should have received a copy of the GNU General Public License
1233+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1234+#
1235+##############################################################################
1236+
1237+from openerp.osv import orm
1238+from openerp.tools.translate import _
1239+
1240+from stock_inventory_hierarchical import HierarchicalInventoryException
1241+
1242+# Add the date to the list of fields we must propagate to children inventories
1243+from stock_inventory_hierarchical import PARENT_VALUES
1244+PARENT_VALUES.append('exhaustive')
1245+
1246+
1247+class HierarchicalExhInventory(orm.Model):
1248+ """Add hierarchical structure features to exhaustive Inventories"""
1249+ _inherit = 'stock.inventory'
1250+
1251+ def action_open(self, cr, uid, ids, context=None):
1252+ """Open only if all the parents are Open."""
1253+ for inventory in self.browse(cr, uid, ids, context=context):
1254+ while inventory.parent_id:
1255+ inventory = inventory.parent_id
1256+ if inventory.state != 'open':
1257+ raise HierarchicalInventoryException(
1258+ _('Warning'),
1259+ _('One of the parent inventories is not open.'))
1260+ return super(HierarchicalExhInventory, self).action_open(
1261+ cr, uid, ids, context=context)
1262+
1263+ def get_missing_locations(self, cr, uid, ids, context=None):
1264+ """Extend the list of inventories with their children"""
1265+ ids = self.search(
1266+ cr, uid, [('parent_id', 'child_of', ids)], context=context)
1267+ missing_ids = super(HierarchicalExhInventory,
1268+ self).get_missing_locations(
1269+ cr, uid, ids, context=context)
1270+ # Find the locations already included in sub-inventories
1271+ inventories = self.browse(cr, uid, ids, context=context)
1272+ subinv_location_ids = [sub.location_id.id
1273+ for i in inventories
1274+ for sub in i.inventory_ids]
1275+ if not subinv_location_ids:
1276+ return missing_ids
1277+ # Extend to the children locations
1278+ subinv_location_ids = set(self.pool['stock.location'].search(
1279+ cr, uid, [
1280+ ('location_id', 'child_of', subinv_location_ids),
1281+ ('usage', '=', 'internal')], context=context))
1282+ return list(set(missing_ids) - subinv_location_ids)
1283+
1284+ # TODO v8: probably only keep the state "done"
1285+ def confirm_missing_locations(self, cr, uid, ids, context=None):
1286+ """Do something only if children state are confirm or done."""
1287+ children_count = self.search(
1288+ cr, uid, [('parent_id', 'child_of', ids),
1289+ ('id', 'not in', ids),
1290+ ('state', 'not in', ['confirm', 'done'])],
1291+ context=context, count=True)
1292+ if children_count > 0:
1293+ raise HierarchicalInventoryException(
1294+ _('Warning'),
1295+ _('Some Sub-inventories are not confirmed.'))
1296+ return super(HierarchicalExhInventory,
1297+ self).confirm_missing_locations(
1298+ cr, uid, ids, context=context)
1299+
1300+ def onchange_location_id(self, cr, uid, ids, location_id, context=None):
1301+ """Check if location is a child of parent inventory location"""
1302+ loc_obj = self.pool['stock.location']
1303+ for inventory in self.browse(cr, uid, ids, context=context):
1304+ if inventory.parent_id:
1305+ allowed_location_ids = loc_obj.search(
1306+ cr, uid, [('location_id', 'child_of',
1307+ inventory.parent_id.location_id.id)],
1308+ context=context)
1309+ if location_id not in allowed_location_ids:
1310+ return {
1311+ 'location_id': False,
1312+ 'warning': {
1313+ 'title': _('Warning: Wrong location'),
1314+ 'message': _("This location is not declared on "
1315+ "the parent inventory\n"
1316+ "It cannot be added.")}
1317+ }
1318+ return {}
1319
1320=== added file 'stock_inventory_hierarchical_location/inventory_hierarchical_location_demo.xml'
1321--- stock_inventory_hierarchical_location/inventory_hierarchical_location_demo.xml 1970-01-01 00:00:00 +0000
1322+++ stock_inventory_hierarchical_location/inventory_hierarchical_location_demo.xml 2014-07-01 11:47:18 +0000
1323@@ -0,0 +1,28 @@
1324+<?xml version="1.0" encoding="utf-8"?>
1325+<openerp>
1326+ <data noupdate="0">
1327+
1328+ <!-- Record inventories we can use in the tests. -->
1329+ <!-- We need them in the demo data because test data is rolled back
1330+ whenever an exception is raised. -->
1331+
1332+ <!-- Record a hierarchical exhaustive inventory -->
1333+ <record id="parent_inventory" model="stock.inventory">
1334+ <field name="name">Hierarchical exhaustive inventory</field>
1335+ <field name="state">draft</field>
1336+ <field name="date">2020-04-15 00:00:00</field>
1337+ <field name="exhaustive">True</field>
1338+ <field name="location_id" model="stock.location" ref="stock.stock_location_stock"/>
1339+ </record>
1340+ <record id="child_1_id" model="stock.inventory">
1341+ <field name="name">Team A</field>
1342+ <field name="parent_id" ref="parent_inventory"/>
1343+ <field name="location_id" model="stock.location" ref="stock.stock_location_14"/>
1344+ </record>
1345+ <record id="child_2_id" model="stock.inventory">
1346+ <field name="name">Team B</field>
1347+ <field name="parent_id" ref="parent_inventory"/>
1348+ <field name="location_id" model="stock.location" ref="stock.stock_location_components"/>
1349+ </record>
1350+ </data>
1351+</openerp>
1352
1353=== added file 'stock_inventory_hierarchical_location/inventory_hierarchical_location_view.xml'
1354--- stock_inventory_hierarchical_location/inventory_hierarchical_location_view.xml 1970-01-01 00:00:00 +0000
1355+++ stock_inventory_hierarchical_location/inventory_hierarchical_location_view.xml 2014-07-01 11:47:18 +0000
1356@@ -0,0 +1,37 @@
1357+<?xml version="1.0" encoding="utf-8"?>
1358+<openerp>
1359+ <data>
1360+ <record model="ir.ui.view" id="stock_inventory_hierarchical_location_form_view">
1361+ <field name="name">hierarchical.inventory.location.form</field>
1362+ <field name="model">stock.inventory</field>
1363+ <field name="inherit_id" ref="stock.view_inventory_form" />
1364+ <field name="arch" type="xml">
1365+
1366+ <xpath expr="/form//field[@name='exhaustive']" position="attributes">
1367+ <attribute name="attrs">{'readonly':[('parent_id', '!=', False)]}</attribute>
1368+ </xpath>
1369+
1370+ <xpath expr="/form//field[@name='location_id']" position="attributes">
1371+ <attribute name="on_change">onchange_location_id(location_id)</attribute>
1372+ </xpath>
1373+
1374+ </field>
1375+ </record>
1376+
1377+ <record model="ir.ui.view" id="stock_ihl_exhautive_form_view">
1378+ <field name="name">hierarchical.inventory.location.exhautive.form</field>
1379+ <field name="model">stock.inventory</field>
1380+ <field name="inherit_id" ref="stock_inventory_hierarchical.stock_inventory_hierarchical_form_view" />
1381+ <field name="arch" type="xml">
1382+ <xpath expr="//field[@name='inventory_ids']" position="attributes">
1383+ <attribute name="context">{'default_parent_id': active_id, 'default_exhaustive': exhaustive}</attribute>
1384+ </xpath>
1385+ </field>
1386+ </record>
1387+
1388+ <!-- Show hierarchical exhaustive inventories by default -->
1389+ <record id="stock.action_inventory_form" model="ir.actions.act_window">
1390+ <field name="context">{'full':'1', 'search_default_exhaustive':1, 'search_default_main_inventories':1}</field>
1391+ </record>
1392+ </data>
1393+</openerp>
1394
1395=== added directory 'stock_inventory_hierarchical_location/static'
1396=== added directory 'stock_inventory_hierarchical_location/static/src'
1397=== added directory 'stock_inventory_hierarchical_location/static/src/img'
1398=== added file 'stock_inventory_hierarchical_location/static/src/img/icon.png'
1399Binary files stock_inventory_hierarchical_location/static/src/img/icon.png 1970-01-01 00:00:00 +0000 and stock_inventory_hierarchical_location/static/src/img/icon.png 2014-07-01 11:47:18 +0000 differ
1400=== added directory 'stock_inventory_hierarchical_location/tests'
1401=== added file 'stock_inventory_hierarchical_location/tests/__init__.py'
1402--- stock_inventory_hierarchical_location/tests/__init__.py 1970-01-01 00:00:00 +0000
1403+++ stock_inventory_hierarchical_location/tests/__init__.py 2014-07-01 11:47:18 +0000
1404@@ -0,0 +1,39 @@
1405+# -*- coding: utf-8 -*-
1406+#
1407+#
1408+# Authors: Laetitia Gangloff
1409+# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
1410+# All Rights Reserved
1411+#
1412+# WARNING: This program as such is intended to be used by professional
1413+# programmers who take the whole responsibility of assessing all potential
1414+# consequences resulting from its eventual inadequacies and bugs.
1415+# End users who are looking for a ready-to-use solution with commercial
1416+# guarantees and support are strongly advised to contact a Free Software
1417+# Service Company.
1418+#
1419+# This program is free software: you can redistribute it and/or modify
1420+# it under the terms of the GNU Affero General Public License as
1421+# published by the Free Software Foundation, either version 3 of the
1422+# License, or (at your option) any later version.
1423+#
1424+# This program is distributed in the hope that it will be useful,
1425+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1426+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1427+# GNU Affero General Public License for more details.
1428+#
1429+# You should have received a copy of the GNU Affero General Public License
1430+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1431+#
1432+#
1433+
1434+import fill_inventory_test
1435+
1436+fast_suite = [
1437+]
1438+
1439+checks = [
1440+ fill_inventory_test,
1441+]
1442+
1443+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
1444
1445=== added file 'stock_inventory_hierarchical_location/tests/fill_inventory_test.py'
1446--- stock_inventory_hierarchical_location/tests/fill_inventory_test.py 1970-01-01 00:00:00 +0000
1447+++ stock_inventory_hierarchical_location/tests/fill_inventory_test.py 2014-07-01 11:47:18 +0000
1448@@ -0,0 +1,118 @@
1449+# -*- coding: utf-8 -*-
1450+#
1451+#
1452+# Authors: Laetitia Gangloff
1453+# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
1454+# All Rights Reserved
1455+#
1456+# WARNING: This program as such is intended to be used by professional
1457+# programmers who take the whole responsibility of assessing all potential
1458+# consequences resulting from its eventual inadequacies and bugs.
1459+# End users who are looking for a ready-to-use solution with commercial
1460+# guarantees and support are strongly advised to contact a Free Software
1461+# Service Company.
1462+#
1463+# This program is free software: you can redistribute it and/or modify
1464+# it under the terms of the GNU Affero General Public License as
1465+# published by the Free Software Foundation, either version 3 of the
1466+# License, or (at your option) any later version.
1467+#
1468+# This program is distributed in the hope that it will be useful,
1469+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1470+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1471+# GNU Affero General Public License for more details.
1472+#
1473+# You should have received a copy of the GNU Affero General Public License
1474+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1475+#
1476+#
1477+
1478+import openerp.tests.common as common
1479+
1480+DB = common.DB
1481+ADMIN_USER_ID = common.ADMIN_USER_ID
1482+
1483+
1484+class fill_inventory_test(common.TransactionCase):
1485+
1486+ def setUp(self):
1487+ super(fill_inventory_test, self).setUp()
1488+
1489+ def test_missing_location(self):
1490+ """
1491+ Test that when confirm a parent inventory, the child location are not in the confirmation result
1492+ """
1493+ parent_inventory_id = self.ref('stock_inventory_hierarchical_location.parent_inventory')
1494+ self.registry('stock.inventory').action_open(self.cr, self.uid, [parent_inventory_id])
1495+ # confirm shelf 1 inventory
1496+ inventory_id = self.ref('stock_inventory_hierarchical_location.child_2_id')
1497+ self.registry('stock.inventory').action_open(self.cr, self.uid, [inventory_id])
1498+ missing_location = self.registry('stock.inventory').get_missing_locations(self.cr, self.uid, [inventory_id])
1499+ self.assertEqual(len(missing_location), 1, "1 missing location should be find, because the inventory is empty")
1500+ wizard_id = self.registry('stock.inventory.uninventoried.locations').create(self.cr, self.uid, {}, context={'active_ids': [inventory_id]})
1501+ self.registry('stock.inventory.uninventoried.locations').confirm_uninventoried_locations(self.cr, self.uid, wizard_id, context={'active_ids': [inventory_id]})
1502+ missing_location = self.registry('stock.inventory').get_missing_locations(self.cr, self.uid, [inventory_id])
1503+ self.assertEqual(len(missing_location), 0, "No missing location should be find, because the inventory is confirmed")
1504+ # confirm shelf 2 inventory
1505+ inventory_id = self.ref('stock_inventory_hierarchical_location.child_1_id')
1506+ self.registry('stock.inventory').action_open(self.cr, self.uid, [inventory_id])
1507+ missing_location = self.registry('stock.inventory').get_missing_locations(self.cr, self.uid, [inventory_id])
1508+ self.assertEqual(len(missing_location), 1, "1 missing location should be fine, because the inventory is empty")
1509+ self.registry('stock.inventory.line').create(self.cr, self.uid, {'product_id': self.ref('product.product_product_7'),
1510+ 'product_uom': self.ref('product.product_uom_unit'),
1511+ 'company_id': self.ref('base.main_company'),
1512+ 'inventory_id': inventory_id,
1513+ 'product_qty': 18.0,
1514+ 'location_id': self.ref('stock.stock_location_14')})
1515+ missing_location = self.registry('stock.inventory').get_missing_locations(self.cr, self.uid, [inventory_id])
1516+ self.assertEqual(len(missing_location), 0, "No missing location should be find, because the inventory is filled")
1517+ wizard_id = self.registry('stock.inventory.uninventoried.locations').create(self.cr, self.uid, {}, context={'active_ids': [inventory_id]})
1518+ wizard = self.registry('stock.inventory.uninventoried.locations').browse(self.cr, self.uid, wizard_id, context={'active_ids': [inventory_id]})
1519+ self.assertEqual(len(wizard.location_ids), 0, "The wizard should not contain any lines but contains %s." % wizard.location_ids)
1520+ self.registry('stock.inventory.uninventoried.locations').confirm_uninventoried_locations(self.cr, self.uid, wizard_id, context={'active_ids': [inventory_id]})
1521+ # confirm parent inventory
1522+ missing_location = self.registry('stock.inventory').get_missing_locations(self.cr, self.uid, [parent_inventory_id])
1523+ self.assertEqual(len(missing_location), 1, "Only 1 missing location should be find, because there is some location in child inventory")
1524+
1525+ def test_fill_inventory(self):
1526+ """
1527+ Test that when fill a parent inventory, the child location are not in the result
1528+ """
1529+ parent_inventory_id = self.ref('stock_inventory_hierarchical_location.parent_inventory')
1530+ self.registry('stock.inventory').action_open(self.cr, self.uid, [parent_inventory_id])
1531+ # confirm shelf 1 inventory
1532+ inventory_id = self.ref('stock_inventory_hierarchical_location.child_2_id')
1533+ self.registry('stock.inventory').action_open(self.cr, self.uid, [inventory_id])
1534+ wizard_id = self.registry('stock.fill.inventory').create(self.cr, self.uid, {'location_id': self.ref('stock.stock_location_components'),
1535+ 'recursive': True,
1536+ 'exhaustive': True,
1537+ 'set_stock_zero': True}, context={'active_ids': [inventory_id]})
1538+ self.registry('stock.fill.inventory').fill_inventory(self.cr, self.uid, [wizard_id], context={'active_ids': [inventory_id]})
1539+ inventory_line_ids = self.registry('stock.inventory.line').search(self.cr, self.uid, [('inventory_id', '=', inventory_id)])
1540+ self.assertEqual(len(inventory_line_ids), 12, "12 inventory line is fount after filling inventory")
1541+ # confirm shelf 2 inventory
1542+ inventory_id = self.ref('stock_inventory_hierarchical_location.child_1_id')
1543+ self.registry('stock.inventory').action_open(self.cr, self.uid, [inventory_id])
1544+ wizard_id = self.registry('stock.fill.inventory').create(self.cr, self.uid, {'location_id': self.ref('stock.stock_location_14'),
1545+ 'recursive': True,
1546+ 'exhaustive': True,
1547+ 'set_stock_zero': True}, context={'active_ids': [inventory_id]})
1548+ self.registry('stock.fill.inventory').fill_inventory(self.cr, self.uid, [wizard_id], context={'active_ids': [inventory_id]})
1549+ inventory_line_ids = self.registry('stock.inventory.line').search(self.cr, self.uid, [('inventory_id', '=', inventory_id)])
1550+ self.assertEqual(len(inventory_line_ids), 4, "1 inventory line is fount after filling inventory")
1551+ # confirm parent inventory
1552+ wizard_id = self.registry('stock.fill.inventory').create(self.cr, self.uid, {'location_id': self.ref('stock.stock_location_stock'),
1553+ 'recursive': True,
1554+ 'exhaustive': True,
1555+ 'set_stock_zero': True}, context={'active_ids': [parent_inventory_id]})
1556+ try:
1557+ self.registry('stock.fill.inventory').fill_inventory(self.cr, self.uid, [wizard_id], context={'active_ids': [parent_inventory_id]})
1558+ except Exception, e:
1559+ self.assertEqual(e.value, 'No product in this location. Please select a location in the product form.', "The message should be ''No product in this location. Please select a location in the product form.''")
1560+ exception_happened = True
1561+ pass
1562+ self.assertTrue(exception_happened)
1563+ inventory_line_ids = self.registry('stock.inventory.line').search(self.cr, self.uid, [('inventory_id', '=', parent_inventory_id)])
1564+ self.assertEqual(len(inventory_line_ids), 0, "No inventory line is fount after filling inventory")
1565+
1566+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
1567
1568=== added file 'stock_inventory_hierarchical_location/tests/inventory_hierarchical_location_test.yml'
1569--- stock_inventory_hierarchical_location/tests/inventory_hierarchical_location_test.yml 1970-01-01 00:00:00 +0000
1570+++ stock_inventory_hierarchical_location/tests/inventory_hierarchical_location_test.yml 2014-07-01 11:47:18 +0000
1571@@ -0,0 +1,56 @@
1572+-
1573+ Check that the exhaustive field of parent inventory has been propagated to children.
1574+-
1575+ !python {model: stock.inventory}: |
1576+ exhaustive = self.read(cr, uid, [ref("child_1_id")], ['exhaustive'])[0]['exhaustive']
1577+ assert exhaustive, "Exhaustive field not propagated to child inventory"
1578+
1579+-
1580+ Check that I can't open child inventory while parent inventory is open.
1581+-
1582+ !python {model: stock.inventory}: |
1583+ from stock_inventory_hierarchical import HierarchicalInventoryException
1584+ parent_state = self.read(cr, uid, [ref("parent_inventory")], ['state'])[0]['state']
1585+ assert parent_state == 'draft', "Parent inventory in state '%s'. It should be 'draft'" % parent_state
1586+ try:
1587+ self.action_open(cr, uid, [ref("child_1_id")])
1588+ except HierarchicalInventoryException as e:
1589+ log("Good ! The Inventory could not be opened: %s" % e)
1590+ child_1_state = self.read(cr, uid, [ref("child_1_id")], ['state'])[0]['state']
1591+ assert child_1_state == 'draft', "Child inventory 1 have '%s' state. It should be 'draft'" % child_1_state
1592+
1593+-
1594+ I will check that the function get_missing_locations return some locations.
1595+-
1596+ !python {model: stock.inventory}: |
1597+ missing_loc_ids = self.get_missing_locations(cr, uid, [ref('parent_inventory')], context=context)
1598+ assert len(missing_loc_ids)==3, "get_missing_locations did not return any ID."
1599+
1600+-
1601+ I will fill the inventory and check that the function get_missing_locations return no locations.
1602+ Adding 17” LCD Monitor.
1603+-
1604+ !record {model: stock.inventory.line, id: lines_inventory_location_pc1}:
1605+ product_id: product.product_product_7
1606+ product_uom: product.product_uom_unit
1607+ company_id: base.main_company
1608+ inventory_id: child_1_id
1609+ product_qty: 18.0
1610+ location_id: stock.stock_location_14
1611+
1612+-
1613+ Adding USB Keyboard, QWERTY.
1614+-
1615+ !record {model: stock.inventory.line, id: lines_inventory_location_pc3}:
1616+ product_id: product.product_product_8
1617+ product_uom: product.product_uom_unit
1618+ company_id: base.main_company
1619+ inventory_id: child_2_id
1620+ product_qty: 5.0
1621+ location_id: stock.stock_location_components
1622+
1623+-
1624+ !python {model: stock.inventory}: |
1625+
1626+ missing_loc_ids = self.get_missing_locations(cr, uid, [ref('parent_inventory')], context=context)
1627+ assert set(missing_loc_ids)==set([ref('stock.stock_location_stock')]), "get_missing_locations should return only %s but returned %s" % ([ref('stock.stock_location_stock')], missing_loc_ids)
1628
1629=== added directory 'stock_inventory_hierarchical_location/wizard'
1630=== added file 'stock_inventory_hierarchical_location/wizard/__init__.py'
1631--- stock_inventory_hierarchical_location/wizard/__init__.py 1970-01-01 00:00:00 +0000
1632+++ stock_inventory_hierarchical_location/wizard/__init__.py 2014-07-01 11:47:18 +0000
1633@@ -0,0 +1,22 @@
1634+# -*- coding: utf-8 -*-
1635+##############################################################################
1636+#
1637+# This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
1638+#
1639+# This program is free software: you can redistribute it and/or modify
1640+# it under the terms of the GNU General Public License as published by
1641+# the Free Software Foundation, either version 3 of the License, or
1642+# (at your option) any later version.
1643+#
1644+# This program is distributed in the hope that it will be useful,
1645+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1646+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1647+# GNU General Public License for more details.
1648+#
1649+# You should have received a copy of the GNU General Public License
1650+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1651+#
1652+##############################################################################
1653+
1654+from . import stock_fill_location_inventory
1655+from . import generate_inventory
1656
1657=== added file 'stock_inventory_hierarchical_location/wizard/generate_inventory.py'
1658--- stock_inventory_hierarchical_location/wizard/generate_inventory.py 1970-01-01 00:00:00 +0000
1659+++ stock_inventory_hierarchical_location/wizard/generate_inventory.py 2014-07-01 11:47:18 +0000
1660@@ -0,0 +1,134 @@
1661+# -*- coding: utf-8 -*-
1662+#
1663+#
1664+# Authors: Laetitia Gangloff
1665+# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
1666+# All Rights Reserved
1667+#
1668+# WARNING: This program as such is intended to be used by professional
1669+# programmers who take the whole responsibility of assessing all potential
1670+# consequences resulting from its eventual inadequacies and bugs.
1671+# End users who are looking for a ready-to-use solution with commercial
1672+# guarantees and support are strongly advised to contact a Free Software
1673+# Service Company.
1674+#
1675+# This program is free software: you can redistribute it and/or modify
1676+# it under the terms of the GNU Affero General Public License as
1677+# published by the Free Software Foundation, either version 3 of the
1678+# License, or (at your option) any later version.
1679+#
1680+# This program is distributed in the hope that it will be useful,
1681+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1682+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1683+# GNU Affero General Public License for more details.
1684+#
1685+# You should have received a copy of the GNU Affero General Public License
1686+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1687+#
1688+#
1689+
1690+from openerp.osv import fields, orm
1691+from openerp.tools.translate import _
1692+
1693+
1694+class GenerateInventoryWizard(orm.TransientModel):
1695+ """ This wizard generate an inventory and all related sub-inventories for the specified location and level
1696+ Example: location = Stock / level = 1 => 1 inventory on Stock (similar to create)
1697+ location = Stock / level = 2 => 1 inventory on Stock, 1 sub-inventory on Shelf1, 1 sub-inventory on Shelf2
1698+ """
1699+
1700+ _name = "stock.generate.inventory"
1701+ _description = "Generate Inventory"
1702+
1703+ _columns = {
1704+ 'prefix_inv_name': fields.char('Inventory prefix', help="Optional prefix for all created inventory"),
1705+ 'location_id': fields.many2one('stock.location', 'Location', required=True),
1706+ 'level': fields.integer("Level", help="Maximum number of intermediate sub-inventories between the main inventory and the smallest sub-inventory."),
1707+ 'only_view': fields.boolean('Only view', help="If set, only inventory on view location can be created"),
1708+ }
1709+
1710+ def _default_location(self, cr, uid, ids, context=None):
1711+ """Default stock location
1712+
1713+ @return: id of the stock location of the first warehouse of the
1714+ default company"""
1715+ location_id = False
1716+ company_id = self.pool['res.company']._company_default_get(
1717+ cr, uid, 'stock.warehouse', context=context)
1718+ warehouse_id = self.pool['stock.warehouse'].search(
1719+ cr, uid, [('company_id', '=', company_id)], limit=1)
1720+ if warehouse_id:
1721+ location_id = self.pool['stock.warehouse'].read(
1722+ cr, uid, warehouse_id[0], ['lot_stock_id'])['lot_stock_id'][0]
1723+ return location_id
1724+
1725+ _defaults = {
1726+ 'location_id': _default_location,
1727+ 'level': 1,
1728+ 'only_view': True,
1729+ }
1730+
1731+ _sql_constraints = [
1732+ ('level', 'CHECK (level>0)', 'Level must be positive!'),
1733+ ]
1734+
1735+ def _create_subinventory(self, cr, uid, inventory_ids, prefix_inv_name, only_view, context):
1736+ new_inventory_ids = []
1737+ for inventory_id in inventory_ids:
1738+ location_id = self.pool['stock.inventory'].read(cr, uid, inventory_id, ['location_id'], context=context)['location_id'][0]
1739+ domain = [('location_id', '=', location_id)]
1740+ if only_view:
1741+ domain.append(('usage', '=', 'view'))
1742+ location_ids = self.pool['stock.location'].search(cr, uid, domain, context=context)
1743+ for location_id in location_ids:
1744+ location_name = self.pool['stock.location'].read(cr, uid, location_id, ['name'], context=context)['name']
1745+ new_inventory_ids.append(self.pool['stock.inventory'].create(cr, uid, {'name': prefix_inv_name + location_name,
1746+ 'exhaustive': True,
1747+ 'location_id': location_id,
1748+ 'parent_id': inventory_id}, context=context))
1749+ return new_inventory_ids
1750+
1751+ def generate_inventory(self, cr, uid, ids, context=None):
1752+ """ Generate inventory and sub-inventories for specified location and level
1753+
1754+ @param self: The object pointer.
1755+ @param cr: A database cursor
1756+ @param uid: ID of the user currently logged in
1757+ @param ids: the ID or list of IDs if we want more than one
1758+ @param context: A standard dictionary
1759+ @return:
1760+ """
1761+ if context is None:
1762+ context = {}
1763+
1764+ if ids and len(ids):
1765+ ids = ids[0]
1766+ else:
1767+ return {'type': 'ir.actions.act_window_close'}
1768+ generate_inventory = self.browse(cr, uid, ids, context=context)
1769+ # create first level inventory
1770+ prefix_inv_name = generate_inventory.prefix_inv_name or ''
1771+ location_id = generate_inventory.location_id.id
1772+ only_view = generate_inventory.only_view
1773+ parent_inventory_id = self.pool['stock.inventory'].create(cr, uid, {'name': prefix_inv_name + generate_inventory.location_id.name,
1774+ 'exhaustive': True,
1775+ 'location_id': location_id}, context=context)
1776+
1777+ inventory_ids = [parent_inventory_id]
1778+ for i in range(1, generate_inventory.level):
1779+ inventory_ids = self._create_subinventory(cr, uid, inventory_ids, prefix_inv_name, only_view, context)
1780+
1781+ mod_obj = self.pool['ir.model.data']
1782+ result = mod_obj.get_object_reference(cr, uid, 'stock', 'view_inventory_form')
1783+ view_id = result and result[1] or False
1784+ return {'name': _('Inventory generated'),
1785+ 'view_mode': 'form',
1786+ 'view_type': 'form',
1787+ 'res_model': 'stock.inventory',
1788+ 'type': 'ir.actions.act_window',
1789+ 'view_id': view_id,
1790+ 'res_id': int(parent_inventory_id),
1791+ }
1792+
1793+
1794+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
1795
1796=== added file 'stock_inventory_hierarchical_location/wizard/generate_inventory_view.xml'
1797--- stock_inventory_hierarchical_location/wizard/generate_inventory_view.xml 1970-01-01 00:00:00 +0000
1798+++ stock_inventory_hierarchical_location/wizard/generate_inventory_view.xml 2014-07-01 11:47:18 +0000
1799@@ -0,0 +1,42 @@
1800+<?xml version="1.0" encoding="utf-8"?>
1801+<openerp>
1802+ <data>
1803+ <record id="view_stock_generate_inventory" model="ir.ui.view">
1804+ <field name="name">Generate Inventory</field>
1805+ <field name="model">stock.generate.inventory</field>
1806+ <field name="arch" type="xml">
1807+ <form string="Generate Inventory" version="7.0">
1808+ <separator string="Generate Inventory"/>
1809+ <group>
1810+ <field name="prefix_inv_name"/>
1811+ <field name="location_id"/>
1812+ <field name="only_view"/>
1813+ <field name="level"/>
1814+ </group>
1815+ <footer>
1816+ <button name="generate_inventory" string="Generate Inventory" type="object" class="oe_highlight"/>
1817+ or
1818+ <button string="Cancel" class="oe_link" special="cancel" />
1819+ </footer>
1820+ </form>
1821+ </field>
1822+ </record>
1823+
1824+ <record id="action_view_stock_generate_inventory" model="ir.actions.act_window">
1825+ <field name="name">Generate Inventory</field>
1826+ <field name="type">ir.actions.act_window</field>
1827+ <field name="res_model">stock.generate.inventory</field>
1828+ <field name="view_type">form</field>
1829+ <field name="view_mode">form</field>
1830+ <field name="view_id" ref="view_stock_generate_inventory"/>
1831+ <field name="target">new</field>
1832+ </record>
1833+
1834+ <menuitem action="action_view_stock_generate_inventory"
1835+ id="menu_action_stock_generate_inventory_form"
1836+ parent="stock.menu_stock_inventory_control"
1837+ sequence="20"
1838+ groups="stock.group_locations"/>
1839+
1840+ </data>
1841+</openerp>
1842
1843=== added file 'stock_inventory_hierarchical_location/wizard/stock_fill_location_inventory.py'
1844--- stock_inventory_hierarchical_location/wizard/stock_fill_location_inventory.py 1970-01-01 00:00:00 +0000
1845+++ stock_inventory_hierarchical_location/wizard/stock_fill_location_inventory.py 2014-07-01 11:47:18 +0000
1846@@ -0,0 +1,89 @@
1847+# -*- coding: utf-8 -*-
1848+#
1849+#
1850+# Authors: Laetitia Gangloff
1851+# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
1852+# All Rights Reserved
1853+#
1854+# WARNING: This program as such is intended to be used by professional
1855+# programmers who take the whole responsibility of assessing all potential
1856+# consequences resulting from its eventual inadequacies and bugs.
1857+# End users who are looking for a ready-to-use solution with commercial
1858+# guarantees and support are strongly advised to contact a Free Software
1859+# Service Company.
1860+#
1861+# This program is free software: you can redistribute it and/or modify
1862+# it under the terms of the GNU Affero General Public License as
1863+# published by the Free Software Foundation, either version 3 of the
1864+# License, or (at your option) any later version.
1865+#
1866+# This program is distributed in the hope that it will be useful,
1867+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1868+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1869+# GNU Affero General Public License for more details.
1870+#
1871+# You should have received a copy of the GNU Affero General Public License
1872+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1873+#
1874+#
1875+
1876+from openerp.osv import orm, osv
1877+from openerp.tools.translate import _
1878+
1879+
1880+class FillInventoryWizard(orm.TransientModel):
1881+ """If inventory as sub inventories, do not fill with sub inventories location"""
1882+ _inherit = 'stock.fill.inventory'
1883+
1884+ def fill_inventory(self, cr, uid, ids, context=None):
1885+ """ To Import stock inventory according to products available in the location and not already in a sub inventory
1886+
1887+ We split fill_inventory on many fill_inventory (one for each location)
1888+ @param self: The object pointer.
1889+ @param cr: A database cursor
1890+ @param uid: ID of the user currently logged in
1891+ @param ids: the ID or list of IDs if we want more than one
1892+ @param context: A standard dictionary
1893+ @return:
1894+ """
1895+ if context is None:
1896+ context = {}
1897+
1898+ if ids and len(ids):
1899+ ids = ids[0]
1900+ else:
1901+ return {'type': 'ir.actions.act_window_close'}
1902+ fill_inventory = self.browse(cr, uid, ids, context=context)
1903+ if fill_inventory.recursive and fill_inventory.exhaustive:
1904+ exclude_location_ids = []
1905+ for i in self.pool['stock.inventory'].browse(cr, uid, context['active_ids']):
1906+ for sub_inventory in i.inventory_ids:
1907+ # exclude these location
1908+ exclude_location_ids.append(sub_inventory.location_id.id)
1909+ domain = [('location_id', 'child_of', [fill_inventory.location_id.id])]
1910+ if exclude_location_ids:
1911+ domain.append('!')
1912+ domain.append(('location_id', 'child_of', exclude_location_ids))
1913+ location_ids = self.pool['stock.location'].search(cr, uid, domain,
1914+ order="id",
1915+ context=context)
1916+ all_in_exception = 0
1917+ for location_id in location_ids:
1918+ try:
1919+ super(FillInventoryWizard, self).fill_inventory(cr, uid,
1920+ [self.copy(cr, uid, ids, {'location_id': location_id,
1921+ 'recursive': False, }, context=context)],
1922+ context=context)
1923+ except osv.except_osv, e:
1924+ if e.value == _('No product in this location. Please select a location in the product form.'):
1925+ all_in_exception = all_in_exception + 1
1926+ pass
1927+ else:
1928+ raise e
1929+ if all_in_exception == len(location_ids):
1930+ raise osv.except_osv(_('Warning!'), _('No product in this location. Please select a location in the product form.'))
1931+ return {'type': 'ir.actions.act_window_close'}
1932+ else:
1933+ return super(FillInventoryWizard, self).fill_inventory(cr, uid, [ids], context=context)
1934+
1935+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: