Merge lp:~acsone-openerp/stock-logistic-warehouse/7.0-inventory-hierarchical-location-fix-subinventory-locations into lp:stock-logistic-warehouse

Proposed by Laetitia Gangloff (Acsone)
Status: Merged
Merge reported by: Laetitia Gangloff (Acsone)
Merged at revision: not available
Proposed branch: lp:~acsone-openerp/stock-logistic-warehouse/7.0-inventory-hierarchical-location-fix-subinventory-locations
Merge into: lp:stock-logistic-warehouse
Diff against target: 3044 lines (+2811/-0)
38 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 (+78/-0)
stock_inventory_hierarchical/i18n/fr.po (+112/-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 (+127/-0)
stock_inventory_hierarchical_location/inventory_hierarchical_location.py (+100/-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)
stock_inventory_location/__init__.py (+24/-0)
stock_inventory_location/__openerp__.py (+77/-0)
stock_inventory_location/exceptions.py (+26/-0)
stock_inventory_location/i18n/fr.po (+241/-0)
stock_inventory_location/stock_inventory_location.py (+353/-0)
stock_inventory_location/stock_inventory_location_demo.xml (+26/-0)
stock_inventory_location/stock_inventory_location_view.xml (+81/-0)
stock_inventory_location/tests/__init__.py (+39/-0)
stock_inventory_location/tests/inventory_exhaustive_test.yml (+128/-0)
stock_inventory_location/tests/inventory_future_test.yml (+13/-0)
stock_inventory_location/tests/inventory_standard_test.yml (+100/-0)
stock_inventory_location/tests/stock_inventory_location_test.py (+47/-0)
stock_inventory_location/wizard/__init__.py (+22/-0)
stock_inventory_location/wizard/stock_confirm_uninventoried_location.py (+68/-0)
stock_inventory_location/wizard/stock_confirm_uninventoried_location.xml (+33/-0)
stock_inventory_location/wizard/stock_fill_location_inventory.py (+47/-0)
stock_inventory_location/wizard/stock_fill_location_inventory_view.xml (+18/-0)
To merge this branch: bzr merge lp:~acsone-openerp/stock-logistic-warehouse/7.0-inventory-hierarchical-location-fix-subinventory-locations
Reviewer Review Type Date Requested Status
Stock and Logistic Core Editors Pending
Review via email: mp+224774@code.launchpad.net

Description of the change

Add exhaustive parameter for fill inventory.
And encapsulate in try catch to confirm even if there is no product to add

To post a comment you must log in.

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-06-27 09:18:58 +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-06-27 09:18:58 +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-06-27 09:18:58 +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-06-27 09:18:58 +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', 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-06-27 09:18:58 +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-06-27 09:18:58 +0000
349@@ -0,0 +1,78 @@
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//field[@name='name']" position="after">
395+ <field name="parent_id"/>
396+ </xpath>
397+ <xpath expr="/form//field[@name='date']" position="attributes">
398+ <attribute name="attrs">{'readonly':[('parent_id', '!=', False)]}</attribute>
399+ </xpath>
400+ <xpath
401+ expr="//page[@string='General Information']"
402+ position="after">
403+ <page string="Sub-inventories">
404+ <field name="inventory_ids" nolabel="1" context="{'default_parent_id': active_id}">
405+ <tree>
406+ <field name="name" />
407+ <field name="state" />
408+ <field name="progress_rate" widget="progressbar" />
409+ </tree>
410+ </field>
411+ </page>
412+ </xpath>
413+ </field>
414+ </record>
415+
416+ <!-- Open the children of the current Inventory in a distinct list
417+ to let users work in a normal window instead of a popup -->
418+ <act_window id="action_view_sub_inventory"
419+ name="View Sub-inventories"
420+ res_model="stock.inventory"
421+ src_model="stock.inventory"
422+ view_mode="tree,form"
423+ view_type="form"
424+ domain="[('parent_id', 'child_of', active_id),('id', '!=', active_id)]"
425+ context="{'full':1, 'search_default_main_inventories':0}"/>
426+ </data>
427+</openerp>
428
429=== added directory 'stock_inventory_hierarchical/i18n'
430=== added file 'stock_inventory_hierarchical/i18n/fr.po'
431--- stock_inventory_hierarchical/i18n/fr.po 1970-01-01 00:00:00 +0000
432+++ stock_inventory_hierarchical/i18n/fr.po 2014-06-27 09:18:58 +0000
433@@ -0,0 +1,112 @@
434+# Translation of OpenERP Server.
435+# This file contains the translation of the following modules:
436+# * stock_inventory_hierarchical
437+#
438+msgid ""
439+msgstr ""
440+"Project-Id-Version: OpenERP Server 6.0.4\n"
441+"Report-Msgid-Bugs-To: support@openerp.com\n"
442+"POT-Creation-Date: 2013-09-25 13:43+0000\n"
443+"PO-Revision-Date: 2013-09-25 13:43+0000\n"
444+"Last-Translator: <>\n"
445+"Language-Team: \n"
446+"MIME-Version: 1.0\n"
447+"Content-Type: text/plain; charset=UTF-8\n"
448+"Content-Transfer-Encoding: \n"
449+"Plural-Forms: \n"
450+
451+#. module: stock_inventory_hierarchical
452+#: field:stock.inventory,complete_name:0
453+msgid "Complete reference"
454+msgstr "Réference complète"
455+
456+#. module: stock_inventory_hierarchical
457+#: field:stock.inventory,progress_rate:0
458+msgid "Done"
459+msgstr "Terminé"
460+
461+#. module: stock_inventory_hierarchical
462+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:108
463+#: constraint:stock.inventory:0
464+#, python-format
465+msgid "Error: You can not create recursive inventories."
466+msgstr "Erreur : Vous ne pouvez pas créer d'inventaire récursifs."
467+
468+#. module: stock_inventory_hierarchical
469+#: model:ir.model,name:stock_inventory_hierarchical.model_stock_inventory
470+msgid "Gestion des stocks"
471+msgstr "Gestion des stocks"
472+
473+#. module: stock_inventory_hierarchical
474+#: field:stock.inventory,inventory_ids:0
475+msgid "List of Sub-inventories"
476+msgstr "Liste des sous-inventaires"
477+
478+#. module: stock_inventory_hierarchical
479+#: view:stock.inventory:0
480+msgid "Main inventories"
481+msgstr "Inventaires principaux"
482+
483+#. module: stock_inventory_hierarchical
484+#: view:stock.inventory:0
485+msgid "Number of Sub-inventories"
486+msgstr "Nombre de sous-inventaires"
487+
488+#. module: stock_inventory_hierarchical
489+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:180
490+#, python-format
491+msgid "One of the parent Inventories is not canceled."
492+msgstr "Un des inventaires pères n'est pas annulé."
493+
494+#. module: stock_inventory_hierarchical
495+#: constraint:stock.inventory:0
496+msgid "Other Physical inventories are being conducted using the same Locations."
497+msgstr "Certains emplacements sont déjà dans un autre inventaire."
498+
499+#. module: stock_inventory_hierarchical
500+#: field:stock.inventory,parent_id:0
501+msgid "Parent"
502+msgstr "Parent"
503+
504+#. module: stock_inventory_hierarchical
505+#: field:stock.inventory,parent_left:0
506+msgid "Parent Left"
507+msgstr "Parent gauche"
508+
509+#. module: stock_inventory_hierarchical
510+#: field:stock.inventory,parent_right:0
511+msgid "Parent Right"
512+msgstr "Parent droit"
513+
514+#. module: stock_inventory_hierarchical
515+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:188
516+#, python-format
517+msgid "Some Sub-inventories are not confirmed."
518+msgstr "Certains sous-inventaires ne sont pas confirmés."
519+
520+#. module: stock_inventory_hierarchical
521+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:196
522+#, python-format
523+msgid "Some Sub-inventories are not validated."
524+msgstr "Certains sous-inventaires ne sont pas terminés."
525+
526+#. module: stock_inventory_hierarchical
527+#: model:ir.actions.act_window,name:stock_inventory_hierarchical.action_view_sub_inventory
528+#: view:stock.inventory:0
529+msgid "View Sub-inventories"
530+msgstr "Voir les sous-inventaires"
531+
532+#. module: stock_inventory_hierarchical
533+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:130
534+#, python-format
535+msgid "Sub-inventory: %s"
536+msgstr "Sous-inventaire : %s"
537+
538+#. module: stock_inventory_hierarchical
539+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:180
540+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:188
541+#: code:addons/stock_inventory_hierarchical/hierarchical_inventory.py:196
542+#, python-format
543+msgid "Warning"
544+msgstr "Attention"
545+
546
547=== added directory 'stock_inventory_hierarchical/images'
548=== added file 'stock_inventory_hierarchical/images/inventory_form.png'
549Binary files stock_inventory_hierarchical/images/inventory_form.png 1970-01-01 00:00:00 +0000 and stock_inventory_hierarchical/images/inventory_form.png 2014-06-27 09:18:58 +0000 differ
550=== added file 'stock_inventory_hierarchical/images/inventory_form_actions.png'
551Binary 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-06-27 09:18:58 +0000 differ
552=== added directory 'stock_inventory_hierarchical/static'
553=== added directory 'stock_inventory_hierarchical/static/src'
554=== added directory 'stock_inventory_hierarchical/static/src/img'
555=== added file 'stock_inventory_hierarchical/static/src/img/icon.png'
556Binary 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-06-27 09:18:58 +0000 differ
557=== added directory 'stock_inventory_hierarchical/test'
558=== added file 'stock_inventory_hierarchical/test/hierarchical_inventory_test.yml'
559--- stock_inventory_hierarchical/test/hierarchical_inventory_test.yml 1970-01-01 00:00:00 +0000
560+++ stock_inventory_hierarchical/test/hierarchical_inventory_test.yml 2014-06-27 09:18:58 +0000
561@@ -0,0 +1,96 @@
562+-
563+ In this file, i check rules about hierarchical inventories.
564+ Children date must be the same of parent date for each state,
565+ the state of parent and children can change only if conditions are correct.
566+-
567+ Check if date of children are the same as the parent's.
568+-
569+ !python {model: stock.inventory}: |
570+ parent_date = self.read(
571+ cr, uid, [ref('stock_inventory_parent0')], ['date'])[0]['date']
572+ child_1_date = self.read(
573+ cr, uid, [ref('child_1_id')], ['date'])[0]['date']
574+ assert child_1_date == parent_date, "Date are different: %s - %s" % (parent_date, child_1_date)
575+
576+ child_2_date = self.read(
577+ cr, uid, [ref('child_2_id')], ['date'])[0]['date']
578+ assert child_2_date == parent_date, "Date are different: %s - %s" % (parent_date, child_2_date)
579+
580+-
581+ Check if children cannot be canceled if the parent was not canceled.
582+ I'll try to cancel both children inventory while parent inventory having "draft" state.
583+ After, i'll verify the state of each inventory.
584+-
585+ !python {model: stock.inventory}: |
586+ from stock_inventory_hierarchical import HierarchicalInventoryException
587+ try:
588+ self.action_cancel_inventory(cr, uid, [ref('child_1_id')])
589+ except HierarchicalInventoryException as e:
590+ log("Good ! The Inventory could not be canceled: %s" % e)
591+ try:
592+ self.action_cancel_inventory(cr, uid, [ref('child_2_id')])
593+ except HierarchicalInventoryException as e:
594+ log("Good ! The Inventory could not be canceled: %s" % e)
595+ child_1_state = self.read(cr, uid, [ref('child_1_id')], ['state'])[0]['state']
596+ assert child_1_state == 'draft', "Child inventory 1 have '%s' state. It should be 'draft'" % child_1_state
597+ child_2_state = self.read(cr, uid, [ref('child_2_id')], ['state'])[0]['state']
598+ assert child_2_state == 'draft', "Child inventory 2 have '%s' state. It should be 'draft'" % child_2_state
599+
600+-
601+ Check if children inventory have confirm state before confirm parent inventory.
602+ To check this, i'll try to confirm parent inventory when children inventory having "draft" state,
603+ and i'll check if state is still 'draft'.
604+-
605+ !python {model: stock.inventory}: |
606+ from stock_inventory_hierarchical import HierarchicalInventoryException
607+ try:
608+ self.action_confirm(cr, uid, [ref('stock_inventory_parent0')])
609+ except HierarchicalInventoryException as e:
610+ log("Good, the inventory could not be confirmed: %s", e)
611+ parent_state = self.read(cr, uid, [ref('stock_inventory_parent0')], ['state'])[0]['state']
612+ assert parent_state == 'draft', "Parent inventory have '%s' state. It should be 'draft'" % parent_state
613+
614+-
615+ In order, i'll confirm the children inventories, and the parent inventory after.
616+-
617+ !python {model: stock.inventory}: |
618+ self.action_confirm(cr, uid, [ref('child_1_id')])
619+ child_1_state = self.read(cr, uid, [ref('child_1_id')], ['state'])[0]['state']
620+ assert child_1_state == 'confirm', "Child inventory 1 have '%s' state. It should be 'confirm'" % child_1_state
621+
622+ self.action_confirm(cr, uid, [ref('child_2_id')])
623+ child_2_state = self.read(cr, uid, [ref('child_2_id')], ['state'])[0]['state']
624+ assert child_2_state == 'confirm', "Child inventory 2 have '%s' state. It should be 'confirm'" % child_2_state
625+
626+ self.action_confirm(cr, uid, [ref('stock_inventory_parent0')])
627+ parent_state = self.read(cr, uid, [ref('stock_inventory_parent0')], ['state'])[0]['state']
628+ assert parent_state == 'confirm', "Parent inventory have '%s' state. It should be 'confirm'" % parent_state
629+
630+-
631+ Check if children inventory have done state before validate parent inventory.
632+ I'll try to validate parent inventory before children.
633+-
634+ !python {model: stock.inventory}: |
635+ from stock_inventory_hierarchical import HierarchicalInventoryException
636+ try:
637+ self.action_done(cr, uid, [ref('stock_inventory_parent0')])
638+ except HierarchicalInventoryException as e:
639+ log("Good, the inventory could not be validated: %s", e)
640+ parent_state = self.read(cr, uid, [ref('stock_inventory_parent0')], ['state'])[0]['state']
641+ assert parent_state == 'confirm', "Parent inventory have '%s' state. It should be 'confirm'" % parent_state
642+
643+-
644+ Now, i'll validate all children inventory before validate the parent.
645+-
646+ !python {model: stock.inventory}: |
647+ self.action_done(cr, uid, [ref('child_1_id')])
648+ child_1_state = self.read(cr, uid, [ref('child_1_id')], ['state'])[0]['state']
649+ assert child_1_state == 'done', "Child inventory 1 have '%s' state. It should be 'done'" % child_1_state
650+
651+ self.action_done(cr, uid, [ref('child_2_id')])
652+ child_2_state = self.read(cr, uid, [ref('child_2_id')], ['state'])[0]['state']
653+ assert child_2_state == 'done', "Child inventory 2 have '%s' state. It should be 'done'" % child_2_state
654+
655+ self.action_done(cr, uid, [ref('stock_inventory_parent0')])
656+ parent_state = self.read(cr, uid, [ref('stock_inventory_parent0')], ['state'])[0]['state']
657+ assert parent_state == 'done', "Parent inventory have '%s' state. It should be 'done'" % parent_state
658
659=== added directory 'stock_inventory_hierarchical_location'
660=== added file 'stock_inventory_hierarchical_location/__init__.py'
661--- stock_inventory_hierarchical_location/__init__.py 1970-01-01 00:00:00 +0000
662+++ stock_inventory_hierarchical_location/__init__.py 2014-06-27 09:18:58 +0000
663@@ -0,0 +1,22 @@
664+# -*- coding: utf-8 -*-
665+##############################################################################
666+#
667+# This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
668+#
669+# This program is free software: you can redistribute it and/or modify
670+# it under the terms of the GNU General Public License as published by
671+# the Free Software Foundation, either version 3 of the License, or
672+# (at your option) any later version.
673+#
674+# This program is distributed in the hope that it will be useful,
675+# but WITHOUT ANY WARRANTY; without even the implied warranty of
676+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
677+# GNU General Public License for more details.
678+#
679+# You should have received a copy of the GNU General Public License
680+# along with this program. If not, see <http://www.gnu.org/licenses/>.
681+#
682+##############################################################################
683+
684+from . import inventory_hierarchical_location
685+from . import wizard
686
687=== added file 'stock_inventory_hierarchical_location/__openerp__.py'
688--- stock_inventory_hierarchical_location/__openerp__.py 1970-01-01 00:00:00 +0000
689+++ stock_inventory_hierarchical_location/__openerp__.py 2014-06-27 09:18:58 +0000
690@@ -0,0 +1,49 @@
691+# -*- coding: utf-8 -*-
692+##############################################################################
693+#
694+# This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
695+#
696+# This program is free software: you can redistribute it and/or modify
697+# it under the terms of the GNU General Public License as published by
698+# the Free Software Foundation, either version 3 of the License, or
699+# (at your option) any later version.
700+#
701+# This program is distributed in the hope that it will be useful,
702+# but WITHOUT ANY WARRANTY; without even the implied warranty of
703+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
704+# GNU General Public License for more details.
705+#
706+# You should have received a copy of the GNU General Public License
707+# along with this program. If not, see <http://www.gnu.org/licenses/>.
708+#
709+##############################################################################
710+
711+{
712+ "name": "Exhaustive and hierarchical Stock Inventories",
713+ "version": "1.1",
714+ "depends": ["stock_inventory_hierarchical", "stock_inventory_location"],
715+ "auto_install": True,
716+ "author": u"Numérigraphe",
717+ "category": "Hidden",
718+ "description": """
719+Make exhaustive Inventories aware of their Sub-Inventories.
720+===========================================================
721+
722+This module allows an inventory to contain a general Location,
723+and it's sub-inventories to contain some of it's sub-Locations.
724+It will prevent you from setting the Inventories and sub-Inventories
725+in inconsistent status.
726+
727+This module will be installed automatically if the modules
728+"stock_inventory_location" and "stock_inventory_hierarchical" are both
729+installed.
730+You must keep this module installed to ensure proper functioning.
731+
732+ """,
733+ "data": [
734+ "inventory_hierarchical_location_view.xml",
735+ "wizard/generate_inventory_view.xml",
736+ ],
737+ "test": ["tests/inventory_hierarchical_location_test.yml"],
738+ "demo": ["inventory_hierarchical_location_demo.xml"],
739+}
740
741=== added directory 'stock_inventory_hierarchical_location/i18n'
742=== added file 'stock_inventory_hierarchical_location/i18n/fr.po'
743--- stock_inventory_hierarchical_location/i18n/fr.po 1970-01-01 00:00:00 +0000
744+++ stock_inventory_hierarchical_location/i18n/fr.po 2014-06-27 09:18:58 +0000
745@@ -0,0 +1,127 @@
746+# Translation of OpenERP Server.
747+# This file contains the translation of the following modules:
748+# * stock_inventory_hierarchical_location
749+#
750+msgid ""
751+msgstr ""
752+"Project-Id-Version: OpenERP Server 6.0.4\n"
753+"Report-Msgid-Bugs-To: support@openerp.com\n"
754+"POT-Creation-Date: 2013-09-25 13:55+0000\n"
755+"PO-Revision-Date: 2013-09-25 13:55+0000\n"
756+"Last-Translator: <>\n"
757+"Language-Team: \n"
758+"MIME-Version: 1.0\n"
759+"Content-Type: text/plain; charset=UTF-8\n"
760+"Content-Transfer-Encoding: \n"
761+"Plural-Forms: \n"
762+
763+#. module: stock_inventory_hierarchical_location
764+#: model:ir.actions.act_window,name:stock_inventory_hierarchical_location.action_view_stock_inventory_missing_location
765+msgid "Confirm missing location"
766+msgstr "Confirmer les emplacements manquants"
767+
768+#. module: stock_inventory_hierarchical_location
769+#: view:stock.inventory.missing.location:0
770+msgid "Confirm missing locations"
771+msgstr "Confirmer les emplacements manquants"
772+
773+#. module: stock_inventory_hierarchical_location
774+#: model:ir.model,name:stock_inventory_hierarchical_location.model_stock_inventory_uninventoried_locations
775+msgid "Confirm the uninventoried Locations."
776+msgstr "Confirmer les emplacements non inventoriés."
777+
778+#. module: stock_inventory_hierarchical_location
779+#: view:stock.inventory.missing.location:0
780+msgid "Do you want to continue ?"
781+msgstr "Voulez-vous continuer ?"
782+
783+#. module: stock_inventory_hierarchical_location
784+#: constraint:stock.inventory:0
785+msgid "Error! You can not create recursive inventories."
786+msgstr "Erreur! Vous ne pouvez pas créer un inventaire récursif."
787+
788+#. module: stock_inventory_hierarchical_location
789+#: model:ir.model,name:stock_inventory_hierarchical_location.model_stock_inventory
790+msgid "Gestion des stocks"
791+msgstr "Gestion des stocks"
792+
793+#. module: stock_inventory_hierarchical_location
794+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:70
795+#, python-format
796+msgid "Location missing for inventory \"%s\"."
797+msgstr "Emplacement manquants dans l'inventaire \"%s\"."
798+
799+#. module: stock_inventory_hierarchical_location
800+#: view:stock.inventory:0
801+msgid "Locations"
802+msgstr "Emplacements"
803+
804+#. module: stock_inventory_hierarchical_location
805+#: field:stock.inventory.missing.location,location_ids:0
806+msgid "Missing location"
807+msgstr "Emplacements manquants"
808+
809+#. module: stock_inventory_hierarchical_location
810+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:41
811+#, python-format
812+msgid "One of the parent inventories is not open."
813+msgstr "Un des inventaire parent n'est pas ouvert."
814+
815+#. module: stock_inventory_hierarchical_location
816+#: view:stock.inventory:0
817+msgid "Open Inventory"
818+msgstr "Ouvrir l'inventaire"
819+
820+#. module: stock_inventory_hierarchical_location
821+#: constraint:stock.inventory:0
822+msgid "Other Physical inventories are being conducted using the same Locations."
823+msgstr "Erreur: certains emplacements sont déjà dans un autre inventaire."
824+
825+#. module: stock_inventory_hierarchical_location
826+#: model:ir.model,name:stock_inventory_hierarchical_location.model_stock_inventory_missing_location
827+msgid "Search on inventory tree for missing declared locations."
828+msgstr "Recherche dans les inventaires les emplacements absents."
829+
830+#. module: stock_inventory_hierarchical_location
831+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:122
832+#, python-format
833+msgid "Some Sub-inventories are not confirmed."
834+msgstr "Au moins un sous-inventaire n'est pas confirmé."
835+
836+#. module: stock_inventory_hierarchical_location
837+#: view:stock.inventory.missing.location:0
838+msgid "This is the list of missing locations."
839+msgstr "Voici la liste des emplacements manquants."
840+
841+#. module: stock_inventory_hierarchical_location
842+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:58
843+#, python-format
844+msgid "This location is not declared on the parent inventory\n"
845+"It cannot be added."
846+msgstr "Cet emplacement n'est pas déclaré dans l'inventaire parent\n"
847+"Vous ne pouvez pas l'ajouter."
848+
849+#. module: stock_inventory_hierarchical_location
850+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:41
851+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:70
852+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:122
853+#, python-format
854+msgid "Warning !"
855+msgstr "Attention !"
856+
857+#. module: stock_inventory_hierarchical_location
858+#: code:addons/stock_inventory_hierarchical_location/inventory_hierarchical_location.py:57
859+#, python-format
860+msgid "Warning: Wrong location"
861+msgstr "Attention: mauvais emplacement"
862+
863+#. module: stock_inventory_hierarchical_location
864+#: view:stock.inventory.missing.location:0
865+msgid "_Cancel"
866+msgstr "_Annuler"
867+
868+#. module: stock_inventory_hierarchical_location
869+#: view:stock.inventory.missing.location:0
870+msgid "_Confirm missing locations"
871+msgstr "_Confirmer les emplacements manquants"
872+
873
874=== added file 'stock_inventory_hierarchical_location/inventory_hierarchical_location.py'
875--- stock_inventory_hierarchical_location/inventory_hierarchical_location.py 1970-01-01 00:00:00 +0000
876+++ stock_inventory_hierarchical_location/inventory_hierarchical_location.py 2014-06-27 09:18:58 +0000
877@@ -0,0 +1,100 @@
878+# -*- coding: utf-8 -*-
879+##############################################################################
880+#
881+# This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
882+#
883+# This program is free software: you can redistribute it and/or modify
884+# it under the terms of the GNU General Public License as published by
885+# the Free Software Foundation, either version 3 of the License, or
886+# (at your option) any later version.
887+#
888+# This program is distributed in the hope that it will be useful,
889+# but WITHOUT ANY WARRANTY; without even the implied warranty of
890+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
891+# GNU General Public License for more details.
892+#
893+# You should have received a copy of the GNU General Public License
894+# along with this program. If not, see <http://www.gnu.org/licenses/>.
895+#
896+##############################################################################
897+
898+from openerp.osv import orm
899+from openerp.tools.translate import _
900+
901+from stock_inventory_hierarchical import HierarchicalInventoryException
902+
903+# Add the date to the list of fields we must propagate to children inventories
904+from stock_inventory_hierarchical import PARENT_VALUES
905+PARENT_VALUES.append('exhaustive')
906+
907+
908+class HierarchicalExhInventory(orm.Model):
909+ """Add hierarchical structure features to exhaustive Inventories"""
910+ _inherit = 'stock.inventory'
911+
912+ def action_open(self, cr, uid, ids, context=None):
913+ """Open only if all the parents are Open."""
914+ for inventory in self.browse(cr, uid, ids, context=context):
915+ while inventory.parent_id:
916+ inventory = inventory.parent_id
917+ if inventory.state != 'open':
918+ raise HierarchicalInventoryException(
919+ _('Warning'),
920+ _('One of the parent inventories is not open.'))
921+ return super(HierarchicalExhInventory, self).action_open(
922+ cr, uid, ids, context=context)
923+
924+ def get_missing_locations(self, cr, uid, ids, context=None):
925+ """Extend the list of inventories with their children"""
926+ ids = self.search(
927+ cr, uid, [('parent_id', 'child_of', ids)], context=context)
928+ missing_ids = set(
929+ super(HierarchicalExhInventory, self).get_missing_locations(
930+ cr, uid, ids, context=context))
931+ # Find the locations already included in sub-inventories
932+ inventories = self.browse(cr, uid, ids, context=context)
933+ subinv_location_ids = [sub.location_id.id
934+ for i in inventories
935+ for sub in i.inventory_ids]
936+ # Extend to the children locations
937+ subinv_location_ids = set(self.pool['stock.location'].search(
938+ cr, uid, [
939+ ('location_id', 'child_of', subinv_location_ids),
940+ ('usage', '=', 'internal')], context=context))
941+ return list(missing_ids - subinv_location_ids)
942+
943+ # TODO v8: probably only keep the state "done"
944+ def confirm_missing_locations(self, cr, uid, ids, context=None):
945+ """Do something only if children state are confirm or done."""
946+ children_count = self.search(
947+ cr, uid, [('parent_id', 'child_of', ids),
948+ ('id', 'not in', ids),
949+ ('state', 'not in', ['confirm', 'done'])],
950+ context=context, count=True)
951+ if children_count > 0:
952+ raise HierarchicalInventoryException(
953+ _('Warning'),
954+ _('Some Sub-inventories are not confirmed.'))
955+ return super(HierarchicalExhInventory,
956+ self).confirm_missing_locations(
957+ cr, uid, ids, context=context)
958+
959+ def onchange_location_id(self, cr, uid, ids, location_id, context=None):
960+ """Check if location is a child of parent inventory location"""
961+ loc_obj = self.pool['stock.location']
962+ for inventory in self.browse(cr, uid, ids, context=context):
963+ if inventory.parent_id:
964+ allowed_location_ids = loc_obj.search(
965+ cr, uid, [('location_id', 'child_of',
966+ inventory.parent_id.location_id.id)],
967+ context=context)
968+ if location_id not in allowed_location_ids:
969+ return {
970+ 'location_id': False,
971+ 'warning': {
972+ 'title': _('Warning: Wrong location'),
973+ 'message': _("This location is not declared on "
974+ "the parent inventory\n"
975+ "It cannot be added.")}
976+ }
977+ return {}
978
979=== added file 'stock_inventory_hierarchical_location/inventory_hierarchical_location_demo.xml'
980--- stock_inventory_hierarchical_location/inventory_hierarchical_location_demo.xml 1970-01-01 00:00:00 +0000
981+++ stock_inventory_hierarchical_location/inventory_hierarchical_location_demo.xml 2014-06-27 09:18:58 +0000
982@@ -0,0 +1,28 @@
983+<?xml version="1.0" encoding="utf-8"?>
984+<openerp>
985+ <data noupdate="0">
986+
987+ <!-- Record inventories we can use in the tests. -->
988+ <!-- We need them in the demo data because test data is rolled back
989+ whenever an exception is raised. -->
990+
991+ <!-- Record a hierarchical exhaustive inventory -->
992+ <record id="parent_inventory" model="stock.inventory">
993+ <field name="name">Hierarchical exhaustive inventory</field>
994+ <field name="state">draft</field>
995+ <field name="date">2020-04-15 00:00:00</field>
996+ <field name="exhaustive">True</field>
997+ <field name="location_id" model="stock.location" ref="stock.stock_location_stock"/>
998+ </record>
999+ <record id="child_1_id" model="stock.inventory">
1000+ <field name="name">Team A</field>
1001+ <field name="parent_id" ref="parent_inventory"/>
1002+ <field name="location_id" model="stock.location" ref="stock.stock_location_14"/>
1003+ </record>
1004+ <record id="child_2_id" model="stock.inventory">
1005+ <field name="name">Team B</field>
1006+ <field name="parent_id" ref="parent_inventory"/>
1007+ <field name="location_id" model="stock.location" ref="stock.stock_location_components"/>
1008+ </record>
1009+ </data>
1010+</openerp>
1011
1012=== added file 'stock_inventory_hierarchical_location/inventory_hierarchical_location_view.xml'
1013--- stock_inventory_hierarchical_location/inventory_hierarchical_location_view.xml 1970-01-01 00:00:00 +0000
1014+++ stock_inventory_hierarchical_location/inventory_hierarchical_location_view.xml 2014-06-27 09:18:58 +0000
1015@@ -0,0 +1,37 @@
1016+<?xml version="1.0" encoding="utf-8"?>
1017+<openerp>
1018+ <data>
1019+ <record model="ir.ui.view" id="stock_inventory_hierarchical_location_form_view">
1020+ <field name="name">hierarchical.inventory.location.form</field>
1021+ <field name="model">stock.inventory</field>
1022+ <field name="inherit_id" ref="stock.view_inventory_form" />
1023+ <field name="arch" type="xml">
1024+
1025+ <xpath expr="/form//field[@name='exhaustive']" position="attributes">
1026+ <attribute name="attrs">{'readonly':[('parent_id', '!=', False)]}</attribute>
1027+ </xpath>
1028+
1029+ <xpath expr="/form//field[@name='location_id']" position="attributes">
1030+ <attribute name="on_change">onchange_location_id(location_id)</attribute>
1031+ </xpath>
1032+
1033+ </field>
1034+ </record>
1035+
1036+ <record model="ir.ui.view" id="stock_ihl_exhautive_form_view">
1037+ <field name="name">hierarchical.inventory.location.exhautive.form</field>
1038+ <field name="model">stock.inventory</field>
1039+ <field name="inherit_id" ref="stock_inventory_hierarchical.stock_inventory_hierarchical_form_view" />
1040+ <field name="arch" type="xml">
1041+ <xpath expr="//field[@name='inventory_ids']" position="attributes">
1042+ <attribute name="context">{'default_parent_id': active_id, 'default_exhaustive': exhaustive}</attribute>
1043+ </xpath>
1044+ </field>
1045+ </record>
1046+
1047+ <!-- Show hierarchical exhaustive inventories by default -->
1048+ <record id="stock.action_inventory_form" model="ir.actions.act_window">
1049+ <field name="context">{'full':'1', 'search_default_exhaustive':1, 'search_default_main_inventories':1}</field>
1050+ </record>
1051+ </data>
1052+</openerp>
1053
1054=== added directory 'stock_inventory_hierarchical_location/static'
1055=== added directory 'stock_inventory_hierarchical_location/static/src'
1056=== added directory 'stock_inventory_hierarchical_location/static/src/img'
1057=== added file 'stock_inventory_hierarchical_location/static/src/img/icon.png'
1058Binary 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-06-27 09:18:58 +0000 differ
1059=== added directory 'stock_inventory_hierarchical_location/tests'
1060=== added file 'stock_inventory_hierarchical_location/tests/__init__.py'
1061--- stock_inventory_hierarchical_location/tests/__init__.py 1970-01-01 00:00:00 +0000
1062+++ stock_inventory_hierarchical_location/tests/__init__.py 2014-06-27 09:18:58 +0000
1063@@ -0,0 +1,39 @@
1064+# -*- coding: utf-8 -*-
1065+#
1066+#
1067+# Authors: Laetitia Gangloff
1068+# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
1069+# All Rights Reserved
1070+#
1071+# WARNING: This program as such is intended to be used by professional
1072+# programmers who take the whole responsibility of assessing all potential
1073+# consequences resulting from its eventual inadequacies and bugs.
1074+# End users who are looking for a ready-to-use solution with commercial
1075+# guarantees and support are strongly advised to contact a Free Software
1076+# Service Company.
1077+#
1078+# This program is free software: you can redistribute it and/or modify
1079+# it under the terms of the GNU Affero General Public License as
1080+# published by the Free Software Foundation, either version 3 of the
1081+# License, or (at your option) any later version.
1082+#
1083+# This program is distributed in the hope that it will be useful,
1084+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1085+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1086+# GNU Affero General Public License for more details.
1087+#
1088+# You should have received a copy of the GNU Affero General Public License
1089+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1090+#
1091+#
1092+
1093+import fill_inventory_test
1094+
1095+fast_suite = [
1096+]
1097+
1098+checks = [
1099+ fill_inventory_test,
1100+]
1101+
1102+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
1103
1104=== added file 'stock_inventory_hierarchical_location/tests/fill_inventory_test.py'
1105--- stock_inventory_hierarchical_location/tests/fill_inventory_test.py 1970-01-01 00:00:00 +0000
1106+++ stock_inventory_hierarchical_location/tests/fill_inventory_test.py 2014-06-27 09:18:58 +0000
1107@@ -0,0 +1,118 @@
1108+# -*- coding: utf-8 -*-
1109+#
1110+#
1111+# Authors: Laetitia Gangloff
1112+# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
1113+# All Rights Reserved
1114+#
1115+# WARNING: This program as such is intended to be used by professional
1116+# programmers who take the whole responsibility of assessing all potential
1117+# consequences resulting from its eventual inadequacies and bugs.
1118+# End users who are looking for a ready-to-use solution with commercial
1119+# guarantees and support are strongly advised to contact a Free Software
1120+# Service Company.
1121+#
1122+# This program is free software: you can redistribute it and/or modify
1123+# it under the terms of the GNU Affero General Public License as
1124+# published by the Free Software Foundation, either version 3 of the
1125+# License, or (at your option) any later version.
1126+#
1127+# This program is distributed in the hope that it will be useful,
1128+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1129+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1130+# GNU Affero General Public License for more details.
1131+#
1132+# You should have received a copy of the GNU Affero General Public License
1133+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1134+#
1135+#
1136+
1137+import openerp.tests.common as common
1138+
1139+DB = common.DB
1140+ADMIN_USER_ID = common.ADMIN_USER_ID
1141+
1142+
1143+class fill_inventory_test(common.TransactionCase):
1144+
1145+ def setUp(self):
1146+ super(fill_inventory_test, self).setUp()
1147+
1148+ def test_missing_location(self):
1149+ """
1150+ Test that when confirm a parent inventory, the child location are not in the confirmation result
1151+ """
1152+ parent_inventory_id = self.ref('stock_inventory_hierarchical_location.parent_inventory')
1153+ self.registry('stock.inventory').action_open(self.cr, self.uid, [parent_inventory_id])
1154+ # confirm shelf 1 inventory
1155+ inventory_id = self.ref('stock_inventory_hierarchical_location.child_2_id')
1156+ self.registry('stock.inventory').action_open(self.cr, self.uid, [inventory_id])
1157+ missing_location = self.registry('stock.inventory').get_missing_locations(self.cr, self.uid, [inventory_id])
1158+ self.assertEqual(len(missing_location), 1, "1 missing location should be find, because the inventory is empty")
1159+ wizard_id = self.registry('stock.inventory.uninventoried.locations').create(self.cr, self.uid, {}, context={'active_ids': [inventory_id]})
1160+ self.registry('stock.inventory.uninventoried.locations').confirm_uninventoried_locations(self.cr, self.uid, wizard_id, context={'active_ids': [inventory_id]})
1161+ missing_location = self.registry('stock.inventory').get_missing_locations(self.cr, self.uid, [inventory_id])
1162+ self.assertEqual(len(missing_location), 0, "No missing location should be find, because the inventory is confirmed")
1163+ # confirm shelf 2 inventory
1164+ inventory_id = self.ref('stock_inventory_hierarchical_location.child_1_id')
1165+ self.registry('stock.inventory').action_open(self.cr, self.uid, [inventory_id])
1166+ missing_location = self.registry('stock.inventory').get_missing_locations(self.cr, self.uid, [inventory_id])
1167+ self.assertEqual(len(missing_location), 1, "1 missing location should be fine, because the inventory is empty")
1168+ self.registry('stock.inventory.line').create(self.cr, self.uid, {'product_id': self.ref('product.product_product_7'),
1169+ 'product_uom': self.ref('product.product_uom_unit'),
1170+ 'company_id': self.ref('base.main_company'),
1171+ 'inventory_id': inventory_id,
1172+ 'product_qty': 18.0,
1173+ 'location_id': self.ref('stock.stock_location_14')})
1174+ missing_location = self.registry('stock.inventory').get_missing_locations(self.cr, self.uid, [inventory_id])
1175+ self.assertEqual(len(missing_location), 0, "No missing location should be find, because the inventory is filled")
1176+ wizard_id = self.registry('stock.inventory.uninventoried.locations').create(self.cr, self.uid, {}, context={'active_ids': [inventory_id]})
1177+ wizard = self.registry('stock.inventory.uninventoried.locations').browse(self.cr, self.uid, wizard_id, context={'active_ids': [inventory_id]})
1178+ self.assertEqual(len(wizard.location_ids), 0, "The wizard should not contain any lines but contains %s." % wizard.location_ids)
1179+ self.registry('stock.inventory.uninventoried.locations').confirm_uninventoried_locations(self.cr, self.uid, wizard_id, context={'active_ids': [inventory_id]})
1180+ # confirm parent inventory
1181+ missing_location = self.registry('stock.inventory').get_missing_locations(self.cr, self.uid, [parent_inventory_id])
1182+ self.assertEqual(len(missing_location), 1, "Only 1 missing location should be find, because there is some location in child inventory")
1183+
1184+ def test_fill_inventory(self):
1185+ """
1186+ Test that when fill a parent inventory, the child location are not in the result
1187+ """
1188+ parent_inventory_id = self.ref('stock_inventory_hierarchical_location.parent_inventory')
1189+ self.registry('stock.inventory').action_open(self.cr, self.uid, [parent_inventory_id])
1190+ # confirm shelf 1 inventory
1191+ inventory_id = self.ref('stock_inventory_hierarchical_location.child_2_id')
1192+ self.registry('stock.inventory').action_open(self.cr, self.uid, [inventory_id])
1193+ wizard_id = self.registry('stock.fill.inventory').create(self.cr, self.uid, {'location_id': self.ref('stock.stock_location_components'),
1194+ 'recursive': True,
1195+ 'exhaustive': True,
1196+ 'set_stock_zero': True}, context={'active_ids': [inventory_id]})
1197+ self.registry('stock.fill.inventory').fill_inventory(self.cr, self.uid, [wizard_id], context={'active_ids': [inventory_id]})
1198+ inventory_line_ids = self.registry('stock.inventory.line').search(self.cr, self.uid, [('inventory_id', '=', inventory_id)])
1199+ self.assertEqual(len(inventory_line_ids), 12, "12 inventory line is fount after filling inventory")
1200+ # confirm shelf 2 inventory
1201+ inventory_id = self.ref('stock_inventory_hierarchical_location.child_1_id')
1202+ self.registry('stock.inventory').action_open(self.cr, self.uid, [inventory_id])
1203+ wizard_id = self.registry('stock.fill.inventory').create(self.cr, self.uid, {'location_id': self.ref('stock.stock_location_14'),
1204+ 'recursive': True,
1205+ 'exhaustive': True,
1206+ 'set_stock_zero': True}, context={'active_ids': [inventory_id]})
1207+ self.registry('stock.fill.inventory').fill_inventory(self.cr, self.uid, [wizard_id], context={'active_ids': [inventory_id]})
1208+ inventory_line_ids = self.registry('stock.inventory.line').search(self.cr, self.uid, [('inventory_id', '=', inventory_id)])
1209+ self.assertEqual(len(inventory_line_ids), 4, "1 inventory line is fount after filling inventory")
1210+ # confirm parent inventory
1211+ wizard_id = self.registry('stock.fill.inventory').create(self.cr, self.uid, {'location_id': self.ref('stock.stock_location_stock'),
1212+ 'recursive': True,
1213+ 'exhaustive': True,
1214+ 'set_stock_zero': True}, context={'active_ids': [parent_inventory_id]})
1215+ try:
1216+ self.registry('stock.fill.inventory').fill_inventory(self.cr, self.uid, [wizard_id], context={'active_ids': [parent_inventory_id]})
1217+ except Exception, e:
1218+ 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.''")
1219+ exception_happened = True
1220+ pass
1221+ self.assertTrue(exception_happened)
1222+ inventory_line_ids = self.registry('stock.inventory.line').search(self.cr, self.uid, [('inventory_id', '=', parent_inventory_id)])
1223+ self.assertEqual(len(inventory_line_ids), 0, "No inventory line is fount after filling inventory")
1224+
1225+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
1226
1227=== added file 'stock_inventory_hierarchical_location/tests/inventory_hierarchical_location_test.yml'
1228--- stock_inventory_hierarchical_location/tests/inventory_hierarchical_location_test.yml 1970-01-01 00:00:00 +0000
1229+++ stock_inventory_hierarchical_location/tests/inventory_hierarchical_location_test.yml 2014-06-27 09:18:58 +0000
1230@@ -0,0 +1,56 @@
1231+-
1232+ Check that the exhaustive field of parent inventory has been propagated to children.
1233+-
1234+ !python {model: stock.inventory}: |
1235+ exhaustive = self.read(cr, uid, [ref("child_1_id")], ['exhaustive'])[0]['exhaustive']
1236+ assert exhaustive, "Exhaustive field not propagated to child inventory"
1237+
1238+-
1239+ Check that I can't open child inventory while parent inventory is open.
1240+-
1241+ !python {model: stock.inventory}: |
1242+ from stock_inventory_hierarchical import HierarchicalInventoryException
1243+ parent_state = self.read(cr, uid, [ref("parent_inventory")], ['state'])[0]['state']
1244+ assert parent_state == 'draft', "Parent inventory in state '%s'. It should be 'draft'" % parent_state
1245+ try:
1246+ self.action_open(cr, uid, [ref("child_1_id")])
1247+ except HierarchicalInventoryException as e:
1248+ log("Good ! The Inventory could not be opened: %s" % e)
1249+ child_1_state = self.read(cr, uid, [ref("child_1_id")], ['state'])[0]['state']
1250+ assert child_1_state == 'draft', "Child inventory 1 have '%s' state. It should be 'draft'" % child_1_state
1251+
1252+-
1253+ I will check that the function get_missing_locations return some locations.
1254+-
1255+ !python {model: stock.inventory}: |
1256+ missing_loc_ids = self.get_missing_locations(cr, uid, [ref('parent_inventory')], context=context)
1257+ assert len(missing_loc_ids)==3, "get_missing_locations did not return any ID."
1258+
1259+-
1260+ I will fill the inventory and check that the function get_missing_locations return no locations.
1261+ Adding 17” LCD Monitor.
1262+-
1263+ !record {model: stock.inventory.line, id: lines_inventory_location_pc1}:
1264+ product_id: product.product_product_7
1265+ product_uom: product.product_uom_unit
1266+ company_id: base.main_company
1267+ inventory_id: child_1_id
1268+ product_qty: 18.0
1269+ location_id: stock.stock_location_14
1270+
1271+-
1272+ Adding USB Keyboard, QWERTY.
1273+-
1274+ !record {model: stock.inventory.line, id: lines_inventory_location_pc3}:
1275+ product_id: product.product_product_8
1276+ product_uom: product.product_uom_unit
1277+ company_id: base.main_company
1278+ inventory_id: child_2_id
1279+ product_qty: 5.0
1280+ location_id: stock.stock_location_components
1281+
1282+-
1283+ !python {model: stock.inventory}: |
1284+
1285+ missing_loc_ids = self.get_missing_locations(cr, uid, [ref('parent_inventory')], context=context)
1286+ 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)
1287
1288=== added directory 'stock_inventory_hierarchical_location/wizard'
1289=== added file 'stock_inventory_hierarchical_location/wizard/__init__.py'
1290--- stock_inventory_hierarchical_location/wizard/__init__.py 1970-01-01 00:00:00 +0000
1291+++ stock_inventory_hierarchical_location/wizard/__init__.py 2014-06-27 09:18:58 +0000
1292@@ -0,0 +1,22 @@
1293+# -*- coding: utf-8 -*-
1294+##############################################################################
1295+#
1296+# This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
1297+#
1298+# This program is free software: you can redistribute it and/or modify
1299+# it under the terms of the GNU General Public License as published by
1300+# the Free Software Foundation, either version 3 of the License, or
1301+# (at your option) any later version.
1302+#
1303+# This program is distributed in the hope that it will be useful,
1304+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1305+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1306+# GNU General Public License for more details.
1307+#
1308+# You should have received a copy of the GNU General Public License
1309+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1310+#
1311+##############################################################################
1312+
1313+from . import stock_fill_location_inventory
1314+from . import generate_inventory
1315
1316=== added file 'stock_inventory_hierarchical_location/wizard/generate_inventory.py'
1317--- stock_inventory_hierarchical_location/wizard/generate_inventory.py 1970-01-01 00:00:00 +0000
1318+++ stock_inventory_hierarchical_location/wizard/generate_inventory.py 2014-06-27 09:18:58 +0000
1319@@ -0,0 +1,134 @@
1320+# -*- coding: utf-8 -*-
1321+#
1322+#
1323+# Authors: Laetitia Gangloff
1324+# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
1325+# All Rights Reserved
1326+#
1327+# WARNING: This program as such is intended to be used by professional
1328+# programmers who take the whole responsibility of assessing all potential
1329+# consequences resulting from its eventual inadequacies and bugs.
1330+# End users who are looking for a ready-to-use solution with commercial
1331+# guarantees and support are strongly advised to contact a Free Software
1332+# Service Company.
1333+#
1334+# This program is free software: you can redistribute it and/or modify
1335+# it under the terms of the GNU Affero General Public License as
1336+# published by the Free Software Foundation, either version 3 of the
1337+# License, or (at your option) any later version.
1338+#
1339+# This program is distributed in the hope that it will be useful,
1340+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1341+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1342+# GNU Affero General Public License for more details.
1343+#
1344+# You should have received a copy of the GNU Affero General Public License
1345+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1346+#
1347+#
1348+
1349+from openerp.osv import fields, orm
1350+from openerp.tools.translate import _
1351+
1352+
1353+class GenerateInventoryWizard(orm.TransientModel):
1354+ """ This wizard generate an inventory and all related sub-inventories for the specified location and level
1355+ Example: location = Stock / level = 1 => 1 inventory on Stock (similar to create)
1356+ location = Stock / level = 2 => 1 inventory on Stock, 1 sub-inventory on Shelf1, 1 sub-inventory on Shelf2
1357+ """
1358+
1359+ _name = "stock.generate.inventory"
1360+ _description = "Generate Inventory"
1361+
1362+ _columns = {
1363+ 'prefix_inv_name': fields.char('Inventory prefix', help="Optional prefix for all created inventory"),
1364+ 'location_id': fields.many2one('stock.location', 'Location', required=True),
1365+ 'level': fields.integer("Level", help="number of level between inventory on location_id and sub-inventory"),
1366+ 'only_view': fields.boolean('Only view', help="If set, only inventory on view location can be created"),
1367+ }
1368+
1369+ def _default_location(self, cr, uid, ids, context=None):
1370+ """Default stock location
1371+
1372+ @return: id of the stock location of the first warehouse of the
1373+ default company"""
1374+ location_id = False
1375+ company_id = self.pool['res.company']._company_default_get(
1376+ cr, uid, 'stock.warehouse', context=context)
1377+ warehouse_id = self.pool['stock.warehouse'].search(
1378+ cr, uid, [('company_id', '=', company_id)], limit=1)
1379+ if warehouse_id:
1380+ location_id = self.pool['stock.warehouse'].read(
1381+ cr, uid, warehouse_id[0], ['lot_stock_id'])['lot_stock_id'][0]
1382+ return location_id
1383+
1384+ _defaults = {
1385+ 'location_id': _default_location,
1386+ 'level': 1,
1387+ 'only_view': True,
1388+ }
1389+
1390+ _sql_constraints = [
1391+ ('level', 'CHECK (level>0)', 'Level must be positive!'),
1392+ ]
1393+
1394+ def _create_subinventory(self, cr, uid, inventory_ids, prefix_inv_name, only_view, context):
1395+ new_inventory_ids = []
1396+ for inventory_id in inventory_ids:
1397+ location_id = self.pool['stock.inventory'].read(cr, uid, inventory_id, ['location_id'], context=context)['location_id'][0]
1398+ domain = [('location_id', '=', location_id)]
1399+ if only_view:
1400+ domain.append(('usage', '=', 'view'))
1401+ location_ids = self.pool['stock.location'].search(cr, uid, domain, context=context)
1402+ for location_id in location_ids:
1403+ location_name = self.pool['stock.location'].read(cr, uid, location_id, ['name'], context=context)['name']
1404+ new_inventory_ids.append(self.pool['stock.inventory'].create(cr, uid, {'name': prefix_inv_name + location_name,
1405+ 'exhaustive': True,
1406+ 'location_id': location_id,
1407+ 'parent_id': inventory_id}, context=context))
1408+ return new_inventory_ids
1409+
1410+ def generate_inventory(self, cr, uid, ids, context=None):
1411+ """ Generate inventory and sub-inventories for specified location and level
1412+
1413+ @param self: The object pointer.
1414+ @param cr: A database cursor
1415+ @param uid: ID of the user currently logged in
1416+ @param ids: the ID or list of IDs if we want more than one
1417+ @param context: A standard dictionary
1418+ @return:
1419+ """
1420+ if context is None:
1421+ context = {}
1422+
1423+ if ids and len(ids):
1424+ ids = ids[0]
1425+ else:
1426+ return {'type': 'ir.actions.act_window_close'}
1427+ generate_inventory = self.browse(cr, uid, ids, context=context)
1428+ # create first level inventory
1429+ prefix_inv_name = generate_inventory.prefix_inv_name or ''
1430+ location_id = generate_inventory.location_id.id
1431+ only_view = generate_inventory.only_view
1432+ parent_inventory_id = self.pool['stock.inventory'].create(cr, uid, {'name': prefix_inv_name + generate_inventory.location_id.name,
1433+ 'exhaustive': True,
1434+ 'location_id': location_id}, context=context)
1435+
1436+ inventory_ids = [parent_inventory_id]
1437+ for i in range(1, generate_inventory.level):
1438+ inventory_ids = self._create_subinventory(cr, uid, inventory_ids, prefix_inv_name, only_view, context)
1439+
1440+ mod_obj = self.pool['ir.model.data']
1441+ result = mod_obj.get_object_reference(cr, uid, 'stock', 'view_inventory_form')
1442+ view_id = result and result[1] or False
1443+ return {'name': _('Inventory generated'),
1444+ 'view_mode': 'form',
1445+ 'view_type': 'form',
1446+ 'res_model': 'stock.inventory',
1447+ 'type': 'ir.actions.act_window',
1448+ 'view_id': view_id,
1449+ 'res_id': int(parent_inventory_id),
1450+ }
1451+
1452+
1453+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
1454
1455=== added file 'stock_inventory_hierarchical_location/wizard/generate_inventory_view.xml'
1456--- stock_inventory_hierarchical_location/wizard/generate_inventory_view.xml 1970-01-01 00:00:00 +0000
1457+++ stock_inventory_hierarchical_location/wizard/generate_inventory_view.xml 2014-06-27 09:18:58 +0000
1458@@ -0,0 +1,42 @@
1459+<?xml version="1.0" encoding="utf-8"?>
1460+<openerp>
1461+ <data>
1462+ <record id="view_stock_generate_inventory" model="ir.ui.view">
1463+ <field name="name">Generate Inventory</field>
1464+ <field name="model">stock.generate.inventory</field>
1465+ <field name="arch" type="xml">
1466+ <form string="Generate Inventory" version="7.0">
1467+ <separator string="Generate inventory"/>
1468+ <group>
1469+ <field name="prefix_inv_name"/>
1470+ <field name="location_id"/>
1471+ <field name="only_view"/>
1472+ <field name="level"/>
1473+ </group>
1474+ <footer>
1475+ <button name="generate_inventory" string="Generate Inventory" type="object" class="oe_highlight"/>
1476+ or
1477+ <button string="Cancel" class="oe_link" special="cancel" />
1478+ </footer>
1479+ </form>
1480+ </field>
1481+ </record>
1482+
1483+ <record id="action_view_stock_generate_inventory" model="ir.actions.act_window">
1484+ <field name="name">Generate Inventory</field>
1485+ <field name="type">ir.actions.act_window</field>
1486+ <field name="res_model">stock.generate.inventory</field>
1487+ <field name="view_type">form</field>
1488+ <field name="view_mode">form</field>
1489+ <field name="view_id" ref="view_stock_generate_inventory"/>
1490+ <field name="target">new</field>
1491+ </record>
1492+
1493+ <menuitem action="action_view_stock_generate_inventory"
1494+ id="menu_action_stock_generate_inventory_form"
1495+ parent="stock.menu_stock_inventory_control"
1496+ sequence="20"
1497+ groups="stock.group_locations"/>
1498+
1499+ </data>
1500+</openerp>
1501
1502=== added file 'stock_inventory_hierarchical_location/wizard/stock_fill_location_inventory.py'
1503--- stock_inventory_hierarchical_location/wizard/stock_fill_location_inventory.py 1970-01-01 00:00:00 +0000
1504+++ stock_inventory_hierarchical_location/wizard/stock_fill_location_inventory.py 2014-06-27 09:18:58 +0000
1505@@ -0,0 +1,89 @@
1506+# -*- coding: utf-8 -*-
1507+#
1508+#
1509+# Authors: Laetitia Gangloff
1510+# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
1511+# All Rights Reserved
1512+#
1513+# WARNING: This program as such is intended to be used by professional
1514+# programmers who take the whole responsibility of assessing all potential
1515+# consequences resulting from its eventual inadequacies and bugs.
1516+# End users who are looking for a ready-to-use solution with commercial
1517+# guarantees and support are strongly advised to contact a Free Software
1518+# Service Company.
1519+#
1520+# This program is free software: you can redistribute it and/or modify
1521+# it under the terms of the GNU Affero General Public License as
1522+# published by the Free Software Foundation, either version 3 of the
1523+# License, or (at your option) any later version.
1524+#
1525+# This program is distributed in the hope that it will be useful,
1526+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1527+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1528+# GNU Affero General Public License for more details.
1529+#
1530+# You should have received a copy of the GNU Affero General Public License
1531+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1532+#
1533+#
1534+
1535+from openerp.osv import orm, osv
1536+from openerp.tools.translate import _
1537+
1538+
1539+class FillInventoryWizard(orm.TransientModel):
1540+ """If inventory as sub inventories, do not fill with sub inventories location"""
1541+ _inherit = 'stock.fill.inventory'
1542+
1543+ def fill_inventory(self, cr, uid, ids, context=None):
1544+ """ To Import stock inventory according to products available in the location and not already in a sub inventory
1545+
1546+ We split fill_inventory on many fill_inventory (one for each location)
1547+ @param self: The object pointer.
1548+ @param cr: A database cursor
1549+ @param uid: ID of the user currently logged in
1550+ @param ids: the ID or list of IDs if we want more than one
1551+ @param context: A standard dictionary
1552+ @return:
1553+ """
1554+ if context is None:
1555+ context = {}
1556+
1557+ if ids and len(ids):
1558+ ids = ids[0]
1559+ else:
1560+ return {'type': 'ir.actions.act_window_close'}
1561+ fill_inventory = self.browse(cr, uid, ids, context=context)
1562+ if fill_inventory.recursive and fill_inventory.exhaustive:
1563+ exclude_location_ids = []
1564+ for i in self.pool['stock.inventory'].browse(cr, uid, context['active_ids']):
1565+ for sub_inventory in i.inventory_ids:
1566+ # exclude these location
1567+ exclude_location_ids.append(sub_inventory.location_id.id)
1568+ domain = [('location_id', 'child_of', [fill_inventory.location_id.id])]
1569+ if exclude_location_ids:
1570+ domain.append('!')
1571+ domain.append(('location_id', 'child_of', exclude_location_ids))
1572+ location_ids = self.pool['stock.location'].search(cr, uid, domain,
1573+ order="id",
1574+ context=context)
1575+ all_in_exception = 0
1576+ for location_id in location_ids:
1577+ try:
1578+ super(FillInventoryWizard, self).fill_inventory(cr, uid,
1579+ [self.copy(cr, uid, ids, {'location_id': location_id,
1580+ 'recursive': False, }, context=context)],
1581+ context=context)
1582+ except osv.except_osv, e:
1583+ if e.value == _('No product in this location. Please select a location in the product form.'):
1584+ all_in_exception = all_in_exception + 1
1585+ pass
1586+ else:
1587+ raise e
1588+ if all_in_exception == len(location_ids):
1589+ raise orm.except_orm(_('Warning!'), _('No product in this location. Please select a location in the product form.'))
1590+ return {'type': 'ir.actions.act_window_close'}
1591+ else:
1592+ return super(FillInventoryWizard, self).fill_inventory(cr, uid, [ids], context=context)
1593+
1594+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
1595
1596=== added directory 'stock_inventory_location'
1597=== added file 'stock_inventory_location/__init__.py'
1598--- stock_inventory_location/__init__.py 1970-01-01 00:00:00 +0000
1599+++ stock_inventory_location/__init__.py 2014-06-27 09:18:58 +0000
1600@@ -0,0 +1,24 @@
1601+# -*- coding: utf-8 -*-
1602+##############################################################################
1603+#
1604+# This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
1605+#
1606+# This program is free software: you can redistribute it and/or modify
1607+# it under the terms of the GNU General Public License as published by
1608+# the Free Software Foundation, either version 3 of the License, or
1609+# (at your option) any later version.
1610+#
1611+# This program is distributed in the hope that it will be useful,
1612+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1613+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1614+# GNU General Public License for more details.
1615+#
1616+# You should have received a copy of the GNU General Public License
1617+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1618+#
1619+##############################################################################
1620+
1621+import stock_inventory_location
1622+import wizard
1623+# Bring the main exception into the package's scope for easier reuse
1624+from .exceptions import ExhaustiveInventoryException
1625
1626=== added file 'stock_inventory_location/__openerp__.py'
1627--- stock_inventory_location/__openerp__.py 1970-01-01 00:00:00 +0000
1628+++ stock_inventory_location/__openerp__.py 2014-06-27 09:18:58 +0000
1629@@ -0,0 +1,77 @@
1630+# -*- coding: utf-8 -*-
1631+##############################################################################
1632+#
1633+# This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
1634+#
1635+# This program is free software: you can redistribute it and/or modify
1636+# it under the terms of the GNU General Public License as published by
1637+# the Free Software Foundation, either version 3 of the License, or
1638+# (at your option) any later version.
1639+#
1640+# This program is distributed in the hope that it will be useful,
1641+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1642+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1643+# GNU General Public License for more details.
1644+#
1645+# You should have received a copy of the GNU General Public License
1646+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1647+#
1648+##############################################################################
1649+
1650+{
1651+ "name": "Exhaustive Stock Inventories",
1652+ "version": "1.1",
1653+ "depends": ["stock"],
1654+ "author": u"Numérigraphe",
1655+ "category": "Warehouse Management",
1656+ "description": """
1657+Let users make exhaustive Inventories
1658+=====================================
1659+
1660+Standard Physical Inventories in OpenERP only contain a generic list of
1661+products by locations, which is well suited to partial Inventories and simple
1662+warehouses. When the a standard Inventory is confirmed, only the products in
1663+the inventory are checked. If a Product is present in the computed stock and
1664+not in the recorded Inventory, OpenERP will consider that it remains unchanged.
1665+
1666+But for exhaustive inventories in complex warehouses, it is not practical:
1667+ - you want to avoid Stock Moves to/from these Locations while counting goods
1668+ - you must make sure all the locations you want have been counted
1669+ - you must make sure no other location has been counted by mistake
1670+ - you want the computed stock to perfectly match the inventory when you
1671+ confirm it.
1672+
1673+This module lets choose whether an Physical Inventory is exhaustive or
1674+standard.
1675+For an exhaustive Inventory:
1676+ - in the state "Draft" you define the Location where goods must be counted.
1677+ - the new Inventory status "Open" lets you indicate that the list of Locations
1678+ is final and you are now counting the goods.
1679+ In that status, no Stock Moves can be recorded in/out of the Inventory's
1680+ Locations.
1681+ - if the Location or some of it's children have not been entered in the
1682+ Inventory Lines, OpenERP warns you when you confirm the Inventory.
1683+ - only the Inventory's Location or its children can be entered in the
1684+ Inventory Lines.
1685+ - every good that is not in the Inventory Lines is considered lost, and gets
1686+ moved out of the stock when you confirm the Inventory.
1687+""",
1688+ "data": [
1689+ "wizard/stock_confirm_uninventoried_location.xml",
1690+ "stock_inventory_location_view.xml",
1691+ "wizard/stock_fill_location_inventory_view.xml",
1692+ ],
1693+ "test": [
1694+ "tests/inventory_standard_test.yml",
1695+ "tests/inventory_exhaustive_test.yml",
1696+ "tests/inventory_future_test.yml",
1697+ ],
1698+ "images": [
1699+ "images/inventory_form.png",
1700+ "inventory_empty_locations.png",
1701+ "images/move_error.png",
1702+ "images/location_locked.png",
1703+ "images/future_inventory.png",
1704+ ],
1705+ "demo": ["stock_inventory_location_demo.xml"]
1706+}
1707
1708=== added file 'stock_inventory_location/exceptions.py'
1709--- stock_inventory_location/exceptions.py 1970-01-01 00:00:00 +0000
1710+++ stock_inventory_location/exceptions.py 2014-06-27 09:18:58 +0000
1711@@ -0,0 +1,26 @@
1712+# -*- coding: utf-8 -*-
1713+##############################################################################
1714+#
1715+# This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
1716+#
1717+# This program is free software: you can redistribute it and/or modify
1718+# it under the terms of the GNU General Public License as published by
1719+# the Free Software Foundation, either version 3 of the License, or
1720+# (at your option) any later version.
1721+#
1722+# This program is distributed in the hope that it will be useful,
1723+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1724+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1725+# GNU General Public License for more details.
1726+#
1727+# You should have received a copy of the GNU General Public License
1728+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1729+#
1730+##############################################################################
1731+
1732+from openerp.osv import orm
1733+
1734+
1735+class ExhaustiveInventoryException(orm.except_orm):
1736+ """The operation is not possible for an exhaustive inventory"""
1737+ pass
1738
1739=== added directory 'stock_inventory_location/i18n'
1740=== added file 'stock_inventory_location/i18n/fr.po'
1741--- stock_inventory_location/i18n/fr.po 1970-01-01 00:00:00 +0000
1742+++ stock_inventory_location/i18n/fr.po 2014-06-27 09:18:58 +0000
1743@@ -0,0 +1,241 @@
1744+# Translation of OpenERP Server.
1745+# This file contains the translation of the following modules:
1746+# * stock_inventory_location
1747+#
1748+msgid ""
1749+msgstr ""
1750+"Project-Id-Version: OpenERP Server 6.0.4\n"
1751+"Report-Msgid-Bugs-To: support@openerp.com\n"
1752+"POT-Creation-Date: 2013-09-25 13:58+0000\n"
1753+"PO-Revision-Date: 2013-09-25 13:58+0000\n"
1754+"Last-Translator: <>\n"
1755+"Language-Team: \n"
1756+"MIME-Version: 1.0\n"
1757+"Content-Type: text/plain; charset=UTF-8\n"
1758+"Content-Transfer-Encoding: \n"
1759+"Plural-Forms: \n"
1760+
1761+#. module: stock_inventory_location
1762+#: code:addons/stock_inventory_location/stock_inventory_location.py:232
1763+#: constraint:stock.move:0
1764+#, python-format
1765+msgid "A Physical Inventory is being conducted at this location"
1766+msgstr "Un inventaire est en cours à cet emplacement"
1767+
1768+#. module: stock_inventory_location
1769+#: view:stock.inventory.uninventoried.locations:0
1770+msgid "Cancel"
1771+msgstr "Annuler"
1772+
1773+#. module: stock_inventory_location
1774+#: view:stock.inventory:0
1775+msgid "Cancel Inventory"
1776+msgstr "Annuler l'inventaire"
1777+
1778+#. module: stock_inventory_location
1779+#: help:stock.inventory,exhaustive:0
1780+msgid "Check the box if you are conducting an exhaustive Inventory.\n"
1781+"Leave the box unchecked if you are conducting a standard Inventory (partial inventory for example).\n"
1782+"For an exhaustive Inventory:\n"
1783+" - the status \"Draft\" lets you define the list of Locations where goods must be counted\n"
1784+" - the status \"Open\" indicates that the list of Locations is definitive and you are now counting the goods. In that status, no Stock Moves can be recorded in/out of the Inventory's Locations\n"
1785+" - only the Inventory's Locations can be entered in the Inventory Lines\n"
1786+" - if some of the Inventory's Locations have not been entered in the Inventory Lines, OpenERP warns you when you confirm the Inventory\n"
1787+" - every good that is not in the Inventory Lines is considered lost, and gets moved out of the stock when you confirm the Inventory"
1788+msgstr "Cochez la case si vous effectuez un inventaire exhaustif.\n"
1789+"Laissez la case non-dochée si vous effectuez un inventaire standard (par exmple un inventaire partiel).\n"
1790+"Pour les inventaires exhaustifs :\n"
1791+" - le statut \"Brouillon\" permet d'indiquer la liste des emplacements dont la marchandise doit être comptée\n"
1792+" - le statut \"Ouvert\" indiqueque la liste des emplacements est définitive, et que le comptage est en cours. Dans ce statut, aucun mouvement de stock ne peut être enregistré depuis/vers les emplacements de l'inventaire\n"
1793+" - seuls les emplacements de l'inventaire peuvent être saisis dans les lignes d'inventaire\n"
1794+" - si certains emplacements sont absent des lignes d'inventaire, OpenERP vous en avertit lors de la confirmation de l'inventaire\n"
1795+" - toutes les marchandises qui ne sont pas dans les lignes d'inventaire sont considérées comme perdues, et sont supprimées lors de la confirmation de l'inventaire"
1796+
1797+#. module: stock_inventory_location
1798+#: view:stock.inventory:0
1799+msgid "Confirm Inventory"
1800+msgstr "Confirmer l'inventaire"
1801+
1802+#. module: stock_inventory_location
1803+#: model:ir.model,name:stock_inventory_location.model_stock_inventory_uninventoried_locations
1804+msgid "Confirm the uninventoried Locations."
1805+msgstr "Confirmer les emplacements non inventoriés."
1806+
1807+#. module: stock_inventory_location
1808+#: view:stock.inventory.uninventoried.locations:0
1809+msgid "Confirm empty locations"
1810+msgstr "Confirmez les emplacements vides"
1811+
1812+#. module: stock_inventory_location
1813+#: model:ir.model,name:stock_inventory_location.model_stock_location
1814+msgid "Emplacement"
1815+msgstr "Emplacement"
1816+
1817+#. module: stock_inventory_location
1818+#: code:addons/stock_inventory_location/wizard/stock_fill_location_inventory.py:54
1819+#, python-format
1820+msgid "Error"
1821+msgstr "Erreur"
1822+
1823+#. module: stock_inventory_location
1824+#: code:addons/stock_inventory_location/wizard/stock_fill_location_inventory.py:75
1825+#, python-format
1826+msgid "Error: Empty location"
1827+msgstr "Erreur : Emplacement vide"
1828+
1829+#. module: stock_inventory_location
1830+#: code:addons/stock_inventory_location/stock_inventory_location.py:231
1831+#: code:addons/stock_inventory_location/stock_inventory_location.py:282
1832+#, python-format
1833+msgid "Error: Location locked down"
1834+msgstr "Erreur: emplacement en inventaire"
1835+
1836+#. module: stock_inventory_location
1837+#: constraint:stock.inventory:0
1838+msgid "Error: You can not create recursive inventories."
1839+msgstr "Erreur: Vous ne pouvez pas créer un inventaire récursif."
1840+
1841+#. module: stock_inventory_location
1842+#: constraint:stock.inventory.line:0
1843+msgid "Error: duplicates lines"
1844+msgstr "Erreur: lignes en double"
1845+
1846+#. module: stock_inventory_location
1847+#: view:stock.inventory:0
1848+#: field:stock.inventory,exhaustive:0
1849+msgid "Exhaustive"
1850+msgstr "Exhaustif"
1851+
1852+#. module: stock_inventory_location
1853+#: model:ir.model,name:stock_inventory_location.model_stock_inventory
1854+msgid "Gestion des stocks"
1855+msgstr "Gestion des stocks"
1856+
1857+#. module: stock_inventory_location
1858+#: view:stock.inventory.uninventoried.locations:0
1859+msgid "If you confirm the Inventory, these Locations will be considered empty and their content will be purged."
1860+msgstr "Si vous confirmez l'inventaire, ces emplacements seront considérés comme vides et leur contenu sera supprimé."
1861+
1862+#. module: stock_inventory_location
1863+#: model:ir.model,name:stock_inventory_location.model_stock_fill_inventory
1864+msgid "Importer un inventaire"
1865+msgstr "Importer un inventaire"
1866+
1867+#. module: stock_inventory_location
1868+#: view:stock.inventory.uninventoried.locations:0
1869+msgid "It could either mean that the Locations are empty, or that the Inventory is not yet complete."
1870+msgstr "Cela peut vouloir dire soit que l'inventaire n'est pas terminé, soit que ces emplacements sont effectivement vides."
1871+
1872+#. module: stock_inventory_location
1873+#: model:ir.model,name:stock_inventory_location.model_stock_inventory_line
1874+msgid "Ligne d'inventaire"
1875+msgstr "Ligne d'inventaire"
1876+
1877+#. module: stock_inventory_location
1878+#: code:addons/stock_inventory_location/stock_inventory_location.py:59
1879+#, python-format
1880+msgid "Location missing for this inventory."
1881+msgstr "Emplacement manquant pour cet inventaire."
1882+
1883+#. module: stock_inventory_location
1884+#: model:ir.model,name:stock_inventory_location.model_stock_move
1885+msgid "Mouvement de stock"
1886+msgstr "Mouvement de stock"
1887+
1888+#. module: stock_inventory_location
1889+#: code:addons/stock_inventory_location/wizard/stock_fill_location_inventory.py:75
1890+#, python-format
1891+msgid "No location to import.\n"
1892+"You must add a location on the locations list."
1893+msgstr "Pas d'emplacement à importer.\n"
1894+"Vous devez ajouter un emplacement dans la liste des emplacements."
1895+
1896+#. module: stock_inventory_location
1897+#: code:addons/stock_inventory_location/wizard/stock_fill_location_inventory.py:58
1898+#, python-format
1899+msgid "No locations found for the inventory."
1900+msgstr "Pas d'emplacement trouvé pour cet inventaire."
1901+
1902+#. module: stock_inventory_location
1903+#: code:addons/stock_inventory_location/stock_inventory_location.py:163
1904+#, python-format
1905+msgid "No product in this location."
1906+msgstr "Pas de produit à cet emplacement."
1907+
1908+#. module: stock_inventory_location
1909+#: code:addons/stock_inventory_location/stock_inventory_location.py:283
1910+#, python-format
1911+msgid "A Physical Inventory is being conducted at the following location(s):\n"
1912+"%s"
1913+msgstr "Les emplacements suivants sont en inventaire :\n"
1914+"%s"
1915+
1916+#. module: stock_inventory_location
1917+#: view:stock.inventory:0
1918+msgid "Open Inventory"
1919+msgstr "Ouvrir l'inventaire"
1920+
1921+#. module: stock_inventory_location
1922+#: constraint:stock.inventory:0
1923+msgid "Other Physical inventories are being conducted using the same Locations."
1924+msgstr "Erreur: certains emplacements sont déjà dans un autre inventaire."
1925+
1926+#. module: stock_inventory_location
1927+#: view:stock.inventory.uninventoried.locations:0
1928+msgid "Purge contents and confirm Inventory"
1929+msgstr "Supprimer le contenu et confirmer l'inventaire"
1930+
1931+#. module: stock_inventory_location
1932+#: view:stock.inventory.uninventoried.locations:0
1933+msgid "The following Stock Locations are part of the current Physical Inventory, but no Inventory Line has been recorded for them."
1934+msgstr "Les emplacements suivants font partie de l'inventaire en cours, mais aucune ligne d'inventaire les concernant n'a été enregistrée."
1935+
1936+#. module: stock_inventory_location
1937+#: field:stock.inventory.uninventoried.locations,location_ids:0
1938+msgid "Uninventoried location"
1939+msgstr "Emplacements non inventoriés"
1940+
1941+#. module: stock_inventory_location
1942+#: code:addons/stock_inventory_location/stock_inventory_location.py:59
1943+#: code:addons/stock_inventory_location/stock_inventory_location.py:163
1944+#: code:addons/stock_inventory_location/wizard/stock_fill_location_inventory.py:58
1945+#, python-format
1946+msgid "Warning"
1947+msgstr "Attention"
1948+
1949+#. module: stock_inventory_location
1950+#: code:addons/stock_inventory_location/stock_inventory_location.py:209
1951+#, python-format
1952+msgid "Warning: Wrong location"
1953+msgstr "Attention: emplacement incorrect"
1954+
1955+#. module: stock_inventory_location
1956+#: code:addons/stock_inventory_location/stock_inventory_location.py:210
1957+#, python-format
1958+msgid "You cannot record an Inventory Line for this Location.\n"
1959+"You must first add this Location to the list of affected Locations on the Inventory form."
1960+msgstr "Vous ne pouvez pas ajouter cet emplacement.\n"
1961+"Il ne fait pas partie de la liste des emplacements à inventorier"
1962+
1963+#. module: stock_inventory_location
1964+#: constraint:stock.move:0
1965+msgid "You must assign a production lot for this product"
1966+msgstr "Vous devez affecter un lot de fabrication à ce produit."
1967+
1968+#. module: stock_inventory_location
1969+#: constraint:stock.inventory.line:0
1970+msgid "You must not create same inventory line Product, Location, Lot on the same date"
1971+msgstr "Vous ne pouvez pas créer plusieurs lignes d'inventaires pour le même produit, lot, emplacement à la même date"
1972+
1973+#. module: stock_inventory_location
1974+#: constraint:stock.move:0
1975+msgid "You try to assign a lot which is not from the same product"
1976+msgstr "Vous essayez d'affecter un lot qui n'est pas pour ce produit."
1977+
1978+
1979+#. module: stock_inventory_location
1980+#: code:addons/stock_inventory_location/wizard/stock_fill_location_inventory.py:54
1981+#, python-format
1982+msgid "the inventory must be in \"Open\" state."
1983+msgstr "l'inventaire doit etre \"Ouvert\"."
1984+
1985
1986=== added directory 'stock_inventory_location/images'
1987=== added file 'stock_inventory_location/images/future_inventory.png'
1988Binary files stock_inventory_location/images/future_inventory.png 1970-01-01 00:00:00 +0000 and stock_inventory_location/images/future_inventory.png 2014-06-27 09:18:58 +0000 differ
1989=== added file 'stock_inventory_location/images/inventory_empty_locations.png'
1990Binary files stock_inventory_location/images/inventory_empty_locations.png 1970-01-01 00:00:00 +0000 and stock_inventory_location/images/inventory_empty_locations.png 2014-06-27 09:18:58 +0000 differ
1991=== added file 'stock_inventory_location/images/inventory_form.png'
1992Binary files stock_inventory_location/images/inventory_form.png 1970-01-01 00:00:00 +0000 and stock_inventory_location/images/inventory_form.png 2014-06-27 09:18:58 +0000 differ
1993=== added file 'stock_inventory_location/images/location_locked.png'
1994Binary files stock_inventory_location/images/location_locked.png 1970-01-01 00:00:00 +0000 and stock_inventory_location/images/location_locked.png 2014-06-27 09:18:58 +0000 differ
1995=== added file 'stock_inventory_location/images/move_error.png'
1996Binary files stock_inventory_location/images/move_error.png 1970-01-01 00:00:00 +0000 and stock_inventory_location/images/move_error.png 2014-06-27 09:18:58 +0000 differ
1997=== added directory 'stock_inventory_location/static'
1998=== added directory 'stock_inventory_location/static/src'
1999=== added directory 'stock_inventory_location/static/src/img'
2000=== added file 'stock_inventory_location/static/src/img/icon.png'
2001Binary files stock_inventory_location/static/src/img/icon.png 1970-01-01 00:00:00 +0000 and stock_inventory_location/static/src/img/icon.png 2014-06-27 09:18:58 +0000 differ
2002=== added file 'stock_inventory_location/stock_inventory_location.py'
2003--- stock_inventory_location/stock_inventory_location.py 1970-01-01 00:00:00 +0000
2004+++ stock_inventory_location/stock_inventory_location.py 2014-06-27 09:18:58 +0000
2005@@ -0,0 +1,353 @@
2006+# -*- coding: utf-8 -*-
2007+##############################################################################
2008+#
2009+# This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
2010+#
2011+# This program is free software: you can redistribute it and/or modify
2012+# it under the terms of the GNU General Public License as published by
2013+# the Free Software Foundation, either version 3 of the License, or
2014+# (at your option) any later version.
2015+#
2016+# This program is distributed in the hope that it will be useful,
2017+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2018+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2019+# GNU General Public License for more details.
2020+#
2021+# You should have received a copy of the GNU General Public License
2022+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2023+#
2024+##############################################################################
2025+
2026+import time
2027+from collections import Iterable
2028+
2029+from openerp.osv import orm, fields
2030+from openerp.tools.translate import _
2031+# The next 2 imports are only needed for a feature backported from trunk-wms
2032+# TODOv8! remove, feature is included upstream
2033+from openerp.osv import osv
2034+from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
2035+from openerp import SUPERUSER_ID
2036+
2037+from .exceptions import ExhaustiveInventoryException
2038+
2039+
2040+class StockInventory(orm.Model):
2041+ """Add locations to the inventories"""
2042+ _inherit = 'stock.inventory'
2043+
2044+ INVENTORY_STATE_SELECTION = [
2045+ ('draft', 'Draft'),
2046+ ('open', 'Open'),
2047+ ('done', 'Done'),
2048+ ('confirm', 'Confirmed'),
2049+ ('cancel', 'Cancelled')
2050+ ]
2051+
2052+ _columns = {
2053+ # TODO v8: should we use "confirm" instead of adding "open"?
2054+ 'state': fields.selection(
2055+ INVENTORY_STATE_SELECTION, 'State', readonly=True, select=True),
2056+ # TODO v8: remove this, should not be needed anymore
2057+ # Make the inventory lines read-only in all states except "Open",
2058+ # to ensure that no unwanted Location can be inserted
2059+ 'inventory_line_id': fields.one2many(
2060+ 'stock.inventory.line', 'inventory_id', 'Inventory lines',
2061+ readonly=True, states={'open': [('readonly', False)]}),
2062+ # TODO v8: remove this, it's backported from v8
2063+ 'location_id': fields.many2one(
2064+ 'stock.location', 'Inventoried Location',
2065+ readonly=True, states={'draft': [('readonly', False)]}),
2066+ 'exhaustive': fields.boolean(
2067+ 'Exhaustive', readonly=True,
2068+ states={'draft': [('readonly', False)]},
2069+ help="Check the box if you are conducting an exhaustive "
2070+ "Inventory.\n"
2071+ "Leave the box unchecked if you are conducting a standard "
2072+ "Inventory (partial inventory for example).\n"
2073+ "For an exhaustive Inventory:\n"
2074+ " - the status \"Draft\" lets you define the list of "
2075+ "Locations where goods must be counted\n"
2076+ " - the status \"Open\" indicates that the list of Locations "
2077+ "is definitive and you are now counting the goods. In that "
2078+ "status, no Stock Moves can be recorded in/out of the "
2079+ "Inventory's Locations\n"
2080+ " - only the Inventory's Locations can be entered in the "
2081+ "Inventory Lines\n"
2082+ " - if some of the Inventory's Locations have not been "
2083+ "entered in the Inventory Lines, OpenERP warns you "
2084+ "when you confirm the Inventory\n"
2085+ " - every good that is not in the Inventory Lines is "
2086+ "considered lost, and gets moved out of the stock when you "
2087+ "confirm the Inventory"),
2088+ }
2089+
2090+ def action_open(self, cr, uid, ids, context=None):
2091+ """Change the state of the inventory to 'open'"""
2092+ return self.write(cr, uid, ids, {'state': 'open'}, context=context)
2093+
2094+ # TODO v8: remove this method? the feature looks already done upstream
2095+ def action_done(self, cr, uid, ids, context=None):
2096+ """
2097+ Don't allow to make an inventory with a date in the future.
2098+
2099+ This makes sure no stock moves will be introduced between the
2100+ moment you finish the inventory and the date of the Stock Moves.
2101+ Backported from trunk-wms:
2102+ revid:qdp-launchpad@openerp.com-20140317090656-o7lo22tzm8yuv3r8
2103+
2104+ @raise osv.except_osv:
2105+ We raise this exception on purpose instead of
2106+ ExhaustiveInventoryException to ensure forward-compatibility
2107+ with v8.
2108+ """
2109+ for inventory in self.browse(cr, uid, ids, context=None):
2110+ if inventory.date > time.strftime(DEFAULT_SERVER_DATETIME_FORMAT):
2111+ raise osv.except_osv(
2112+ _('Error!'),
2113+ _('It\'s impossible to confirm an inventory in the '
2114+ 'future. Please change the inventory date to proceed '
2115+ 'further.'))
2116+ return super(StockInventory, self).action_done(cr, uid, ids,
2117+ context=context)
2118+
2119+ # TODO: remove this in v8
2120+ def _default_location(self, cr, uid, ids, context=None):
2121+ """Default stock location
2122+
2123+ @return: id of the stock location of the first warehouse of the
2124+ default company"""
2125+ location_id = False
2126+ company_id = self.pool['res.company']._company_default_get(
2127+ cr, uid, 'stock.warehouse', context=context)
2128+ warehouse_id = self.pool['stock.warehouse'].search(
2129+ cr, uid, [('company_id', '=', company_id)], limit=1)
2130+ if warehouse_id:
2131+ location_id = self.pool['stock.warehouse'].read(
2132+ cr, uid, warehouse_id[0], ['lot_stock_id'])['lot_stock_id'][0]
2133+ return location_id
2134+
2135+ _defaults = {
2136+ 'state': 'draft',
2137+ 'exhaustive': False,
2138+ # TODO: remove this in v8
2139+ 'location_id': _default_location,
2140+ }
2141+
2142+ def _check_location_free_from_inventories(self, cr, uid, ids):
2143+ """
2144+ Verify that no other Inventory is being conducted on the same locations
2145+
2146+ Open Inventories are matched using the exact Location IDs,
2147+ excluding children.
2148+ """
2149+ # We don't get a context because we're called from a _constraint
2150+ for inventory in self.browse(cr, uid, ids):
2151+ if not inventory.exhaustive:
2152+ # never block standard inventories
2153+ continue
2154+ if self.search(cr, uid,
2155+ [('location_id', '=', inventory.location_id.id),
2156+ ('id', '!=', inventory.id),
2157+ ('date', '=', inventory.date),
2158+ ('exhaustive', '=', True)]):
2159+ # Quit as soon as one offending inventory is found
2160+ return False
2161+ return True
2162+
2163+ _constraints = [
2164+ (_check_location_free_from_inventories,
2165+ 'Other Physical inventories are being conducted using the same '
2166+ 'Locations.',
2167+ ['location_id', 'date', 'exhaustive'])
2168+ ]
2169+
2170+ def _get_locations_open_inventories(self, cr, uid, context=None):
2171+ """IDs of location in open exhaustive inventories, with children"""
2172+ inv_ids = self.search(
2173+ cr, uid, [('state', '=', 'open'), ('exhaustive', '=', True)],
2174+ context=context)
2175+ if not inv_ids:
2176+ # Early exit if no match found
2177+ return []
2178+ # List the Locations - normally all exhaustive inventories have one
2179+ location_ids = [inventory.location_id.id
2180+ for inventory in self.browse(cr, uid, inv_ids,
2181+ context=context)]
2182+ # Extend to the children Locations
2183+ return self.pool['stock.location'].search(
2184+ cr, uid,
2185+ [('location_id', 'child_of', set(location_ids)),
2186+ ('usage', '=', 'internal')],
2187+ context=context)
2188+
2189+ def get_missing_locations(self, cr, uid, ids, context=None):
2190+ """Compute the list of location_ids which are missing from the lines
2191+
2192+ Here, "missing" means the location is the inventory's location or one
2193+ of it's children, and the inventory does not contain any line with
2194+ this location."""
2195+ inventories = self.browse(cr, uid, ids, context=context)
2196+ # Find the locations of the inventories
2197+ inv_location_ids = [i.location_id.id for i in inventories]
2198+ # Extend to the children locations
2199+ inv_location_ids = set(self.pool['stock.location'].search(
2200+ cr, uid, [
2201+ ('location_id', 'child_of', inv_location_ids),
2202+ ('usage', '=', 'internal')], context=context))
2203+ # Find the locations already recorded in inventory lines
2204+ line_locations_ids = set([l.location_id.id
2205+ for i in inventories
2206+ for l in i.inventory_line_id])
2207+ return list(inv_location_ids - line_locations_ids)
2208+
2209+ def confirm_missing_locations(self, cr, uid, ids, context=None):
2210+ """Open wizard to confirm empty locations on exhaustive inventories"""
2211+ for inventory in self.browse(cr, uid, ids, context=context):
2212+ if (self.get_missing_locations(cr, uid, ids, context=context)
2213+ and inventory.exhaustive):
2214+ return {
2215+ 'type': 'ir.actions.act_window',
2216+ 'view_type': 'form',
2217+ 'view_mode': 'form',
2218+ 'res_model': 'stock.inventory.uninventoried.locations',
2219+ 'target': 'new',
2220+ 'context': dict(context,
2221+ active_ids=ids,
2222+ active_id=ids[0]),
2223+ 'nodestroy': True,
2224+ }
2225+ return self.action_confirm(cr, uid, ids, context=context)
2226+
2227+
2228+class StockInventoryLine(orm.Model):
2229+ """Only allow the Inventory's Locations"""
2230+
2231+ _inherit = 'stock.inventory.line'
2232+
2233+ def _default_stock_location(self, cr, uid, context=None):
2234+ res = super(StockInventoryLine, self)._default_stock_location(
2235+ cr, uid, context=context)
2236+ if context is None or not context.get('location_id', False):
2237+ return res
2238+ else:
2239+ return context['location_id']
2240+
2241+ _defaults = {
2242+ 'location_id': _default_stock_location
2243+ }
2244+
2245+ def onchange_location_id(self, cr, uid, ids, inventory_location_id,
2246+ exhaustive, location_id, context=None):
2247+ """Warn if the new is not in the location list for this inventory."""
2248+ if not exhaustive:
2249+ # Don't check if partial inventory
2250+ return True
2251+
2252+ # search children of location
2253+ if location_id not in self.pool['stock.location'].search(
2254+ cr, uid, [('location_id', 'child_of', inventory_location_id)],
2255+ context=context):
2256+ return {
2257+ 'value': {'location_id': False},
2258+ 'warning': {
2259+ 'title': _('Warning: Wrong location'),
2260+ 'message': _(
2261+ "You cannot record an Inventory Line for this "
2262+ "Location.\n"
2263+ "You must first add this Location to the list of "
2264+ "affected Locations on the Inventory form.")}
2265+ }
2266+ return True
2267+
2268+
2269+class StockLocation(orm.Model):
2270+ """Refuse changes during exhaustive Inventories"""
2271+ _inherit = 'stock.location'
2272+ _order = 'name'
2273+
2274+ def _check_inventory(self, cr, uid, ids, context=None):
2275+ """Error if an exhaustive Inventory is being conducted here"""
2276+ inv_obj = self.pool['stock.inventory']
2277+ location_inventory_open_ids = inv_obj._get_locations_open_inventories(
2278+ cr, SUPERUSER_ID, context=context)
2279+ if not isinstance(ids, Iterable):
2280+ ids = [ids]
2281+ for inv_id in ids:
2282+ if inv_id in location_inventory_open_ids:
2283+ raise ExhaustiveInventoryException(
2284+ _('Error: Location locked down'),
2285+ _('A Physical Inventory is being conducted at this '
2286+ 'location'))
2287+ return True
2288+
2289+ def write(self, cr, uid, ids, vals, context=None):
2290+ """Refuse write if an inventory is being conducted"""
2291+ self._check_inventory(cr, uid, ids, context=context)
2292+ if not isinstance(ids, Iterable):
2293+ ids_to_check = [ids]
2294+ else:
2295+ # Copy the data to avoid changing 'ids', it would trigger an infinite recursion
2296+ ids_to_check = list(ids)
2297+ # If changing the parent, no inventory must conducted there either
2298+ if vals.get('location_id'):
2299+ ids_to_check.append(vals['location_id'])
2300+ self._check_inventory(cr, uid, ids_to_check, context=context)
2301+ return super(StockLocation, self).write(cr, uid, ids, vals,
2302+ context=context)
2303+
2304+ def create(self, cr, uid, vals, context=None):
2305+ """Refuse create if an inventory is being conducted at the parent"""
2306+ self._check_inventory(cr, uid, vals.get('location_id'),
2307+ context=context)
2308+ return super(StockLocation, self).create(cr, uid, vals,
2309+ context=context)
2310+
2311+ def unlink(self, cr, uid, ids, context=None):
2312+ """Refuse unlink if an inventory is being conducted"""
2313+ self._check_inventory(cr, uid, ids, context=context)
2314+ return super(StockLocation, self).unlink(cr, uid, ids, context=context)
2315+
2316+
2317+class StockMove(orm.Model):
2318+ """Refuse Moves during exhaustive Inventories"""
2319+
2320+ _inherit = 'stock.move'
2321+
2322+ # TODOv7: adapt this to match trunk-wms
2323+ def _check_open_inventory_location(self, cr, uid, ids, context=None):
2324+ """
2325+ Check that the locations are not locked by an open inventory
2326+
2327+ Standard inventories are not checked.
2328+
2329+ @raise ExhaustiveInventoryException: an error is raised if locations
2330+ are locked, instead of returning False, in order to pass an
2331+ extensive error message back to users.
2332+ """
2333+ message = ""
2334+ inv_obj = self.pool['stock.inventory']
2335+ locked_location_ids = inv_obj._get_locations_open_inventories(
2336+ cr, SUPERUSER_ID, context=context)
2337+ if not locked_location_ids:
2338+ # Nothing to verify
2339+ return True
2340+ for move in self.browse(cr, uid, ids, context=context):
2341+ if (move.location_id.usage != 'inventory'
2342+ and move.location_dest_id.id in locked_location_ids):
2343+ message += " - %s\n" % (move.location_dest_id.name)
2344+ if (move.location_dest_id.usage != 'inventory'
2345+ and move.location_id.id in locked_location_ids):
2346+ message += " - %s\n" % (move.location_id.name)
2347+ if message:
2348+ raise ExhaustiveInventoryException(
2349+ _('Error: Location locked down'),
2350+ _('A Physical Inventory is being conducted at the following '
2351+ 'location(s):\n%s') % message)
2352+ return True
2353+
2354+ _constraints = [
2355+ (_check_open_inventory_location,
2356+ "A Physical Inventory is being conducted at this location",
2357+ ['location_id', 'location_dest_id']),
2358+ ]
2359
2360=== added file 'stock_inventory_location/stock_inventory_location_demo.xml'
2361--- stock_inventory_location/stock_inventory_location_demo.xml 1970-01-01 00:00:00 +0000
2362+++ stock_inventory_location/stock_inventory_location_demo.xml 2014-06-27 09:18:58 +0000
2363@@ -0,0 +1,26 @@
2364+<?xml version="1.0" encoding="utf-8"?>
2365+<openerp>
2366+ <data noupdate="0">
2367+ <!-- Record a non exhaustive inventory -->
2368+ <record id="inventory_standard" model="stock.inventory">
2369+ <field name="name">Standard inventory</field>
2370+ <field name="state">draft</field>
2371+ </record>
2372+
2373+ <!-- Record an exhaustive inventory -->
2374+ <record id="inventory_exhaustive" model="stock.inventory">
2375+ <field name="name">Exhaustive inventory</field>
2376+ <field name="state">draft</field>
2377+ <field name="exhaustive">True</field>
2378+ <field name="location_id" model="stock.location" search="[('name', '=', 'Shelf 2')]" />
2379+ </record>
2380+
2381+ <!-- Record an inventory dated in the future -->
2382+ <!-- TODOv8: remove this entry, only useful for a test already in trunk-wms -->
2383+ <record id="inventory_future" model="stock.inventory">
2384+ <field name="name">Inventory in the future</field>
2385+ <field name="state">draft</field>
2386+ <field name="date">2020-03-01 00:00:00</field>
2387+ </record>
2388+ </data>
2389+</openerp>
2390
2391=== added file 'stock_inventory_location/stock_inventory_location_view.xml'
2392--- stock_inventory_location/stock_inventory_location_view.xml 1970-01-01 00:00:00 +0000
2393+++ stock_inventory_location/stock_inventory_location_view.xml 2014-06-27 09:18:58 +0000
2394@@ -0,0 +1,81 @@
2395+<?xml version="1.0" encoding="utf-8"?>
2396+<openerp>
2397+ <data>
2398+
2399+ <record model="ir.ui.view" id="stock_inventory_location_form_view">
2400+ <field name="name">stock.inventory.location.form</field>
2401+ <field name="model">stock.inventory</field>
2402+ <field name="inherit_id" ref="stock.view_inventory_form"/>
2403+ <field name="priority" eval="10"/>
2404+ <field name="arch" type="xml">
2405+ <!-- Show the state 'done' in the statusbar -->
2406+ <xpath expr="/form//field[@name='state']" position="attributes">
2407+ <attribute name="statusbar_visible">draft,open,confirm</attribute>
2408+ </xpath>
2409+
2410+ <!-- TODO v8 place "exhaustive" next to "location_id" -->
2411+ <!-- Add type of inventory: standard or exhaustive. -->
2412+ <xpath expr="/form//field[@name='name']" position="after">
2413+ <field name="exhaustive"/>
2414+ <field name="location_id" groups="stock.group_locations"
2415+ domain="[('usage','in',('view', 'internal'))]"
2416+ attrs="{'invisible':[('exhaustive','!=',True)], 'required':[('exhaustive','=',True)]}"/>
2417+ </xpath>
2418+
2419+ <!-- Enable Fill Inventory button when state is open -->
2420+ <xpath expr="//button[@string='Fill Inventory']" position="attributes">
2421+ <attribute name="states">open</attribute>
2422+ <attribute name="context">{'default_exhaustive': exhaustive}</attribute>
2423+ </xpath>
2424+
2425+ <!-- Control locations added by user on inventory line -->
2426+ <xpath expr="/form//field[@name='inventory_line_id']"
2427+ position="attributes">
2428+ <attribute name="context">{'location_id': location_id}</attribute>
2429+ </xpath>
2430+ <xpath expr="/form//field[@name='inventory_line_id']/tree//field[@name='location_id']"
2431+ position="attributes">
2432+ <attribute name="on_change">onchange_location_id(parent.location_id, parent.exhaustive, location_id)</attribute>
2433+ </xpath>
2434+ <xpath expr="/form//field[@name='inventory_line_id']/form//field[@name='location_id']"
2435+ position="attributes">
2436+ <attribute name="on_change">onchange_location_id(parent.location_id, parent.exhaustive, location_id)</attribute>
2437+ </xpath>
2438+
2439+ <!-- Add button to open an inventory. Call wizard on confirm inventory -->
2440+ <xpath expr="/form//button[@name='action_cancel_inventory']" position="before">
2441+ <button name="action_open" states="draft" string="Open Inventory" type="object" class="oe_highlight" groups="stock.group_stock_user"/>
2442+ </xpath>
2443+ <!-- Show the "cancel" button in state "open" -->
2444+ <xpath expr="/form//button[@name='action_cancel_inventory']" position="attributes">
2445+ <attribute name="states">draft,open,confirm,done</attribute>
2446+ </xpath>
2447+ <!-- hijack the "confirm" button -->
2448+ <xpath expr="/form//button[@name='action_confirm']" position="attributes">
2449+ <attribute name="states">open</attribute>
2450+ <attribute name="name">confirm_missing_locations</attribute>
2451+ </xpath>
2452+ </field>
2453+ </record>
2454+
2455+ <!-- Add filter for complete or partial inventory -->
2456+ <record model="ir.ui.view" id="view_inventory_complete_filter">
2457+ <field name="name">complete.inventory.filter</field>
2458+ <field name="model">stock.inventory</field>
2459+ <field name="inherit_id" ref="stock.view_inventory_filter"/>
2460+ <field name="arch" type="xml">
2461+ <xpath expr="//field[@name='name']" position="before">
2462+ <filter icon="terp-check" name="exhaustive" string="Exhaustive" domain="[('exhaustive', '=', True)]"
2463+ help="Only select exhaustive Inventories." />
2464+ <separator orientation="vertical"/>
2465+ </xpath>
2466+ </field>
2467+ </record>
2468+
2469+ <!-- Show exhaustive inventories by default -->
2470+ <record id="stock.action_inventory_form" model="ir.actions.act_window">
2471+ <field name="context">{'full':'1', 'search_default_exhaustive':1}</field>
2472+ </record>
2473+
2474+ </data>
2475+</openerp>
2476\ No newline at end of file
2477
2478=== added directory 'stock_inventory_location/tests'
2479=== added file 'stock_inventory_location/tests/__init__.py'
2480--- stock_inventory_location/tests/__init__.py 1970-01-01 00:00:00 +0000
2481+++ stock_inventory_location/tests/__init__.py 2014-06-27 09:18:58 +0000
2482@@ -0,0 +1,39 @@
2483+# -*- coding: utf-8 -*-
2484+#
2485+#
2486+# Authors: Laetitia Gangloff
2487+# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
2488+# All Rights Reserved
2489+#
2490+# WARNING: This program as such is intended to be used by professional
2491+# programmers who take the whole responsibility of assessing all potential
2492+# consequences resulting from its eventual inadequacies and bugs.
2493+# End users who are looking for a ready-to-use solution with commercial
2494+# guarantees and support are strongly advised to contact a Free Software
2495+# Service Company.
2496+#
2497+# This program is free software: you can redistribute it and/or modify
2498+# it under the terms of the GNU Affero General Public License as
2499+# published by the Free Software Foundation, either version 3 of the
2500+# License, or (at your option) any later version.
2501+#
2502+# This program is distributed in the hope that it will be useful,
2503+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2504+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2505+# GNU Affero General Public License for more details.
2506+#
2507+# You should have received a copy of the GNU Affero General Public License
2508+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2509+#
2510+#
2511+
2512+import stock_inventory_location_test
2513+
2514+fast_suite = [
2515+]
2516+
2517+checks = [
2518+ stock_inventory_location_test,
2519+]
2520+
2521+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
2522
2523=== added file 'stock_inventory_location/tests/inventory_exhaustive_test.yml'
2524--- stock_inventory_location/tests/inventory_exhaustive_test.yml 1970-01-01 00:00:00 +0000
2525+++ stock_inventory_location/tests/inventory_exhaustive_test.yml 2014-06-27 09:18:58 +0000
2526@@ -0,0 +1,128 @@
2527+-
2528+ This file will test an exhaustive inventory.
2529+ I will call open_action method and check if state of inventories are 'open'.
2530+-
2531+ !python {model: stock.inventory}: |
2532+ self.action_open(cr, uid, [ref('inventory_exhaustive')])
2533+ inventory_state = self.read(cr, uid, [ref('inventory_exhaustive')], ['state'])[0]['state']
2534+ assert inventory_state == 'open', "Inventory in state '%s'. It should be 'open'" % inventory_state
2535+-
2536+ I will check that the function get_missing_locations return some locations.
2537+-
2538+ !python {model: stock.inventory}: |
2539+ missing_loc_ids = self.get_missing_locations(cr, uid, [ref('inventory_exhaustive')], context=context)
2540+ assert len(missing_loc_ids), "get_missing_locations did not return any ID."
2541+-
2542+ I create a wizard record for stock_confirm_uninventoried_location to verify that it contains the uninventoried locations
2543+-
2544+ !python {model: stock.inventory.uninventoried.locations}: |
2545+ ctx = dict(context, active_ids=[ref('inventory_exhaustive')])
2546+ wizard_id = self.create(cr, uid, {}, context=ctx)
2547+ wizard = self.browse(cr, uid, wizard_id, context=ctx)
2548+ assert len(wizard.location_ids) > 0 , "The wizard does not contain any lines."
2549+-
2550+ I add products to exhaustive inventory.
2551+ Adding 17” LCD Monitor.
2552+-
2553+ !record {model: stock.inventory.line, id: lines_inventory_location_pc1}:
2554+ product_id: product.product_product_7
2555+ product_uom: product.product_uom_unit
2556+ company_id: base.main_company
2557+ inventory_id: inventory_exhaustive
2558+ product_qty: 18.0
2559+ location_id: stock.stock_location_14
2560+
2561+-
2562+ Adding USB Keyboard, QWERTY.
2563+-
2564+ !record {model: stock.inventory.line, id: lines_inventory_location_pc3}:
2565+ product_id: product.product_product_8
2566+ product_uom: product.product_uom_unit
2567+ company_id: base.main_company
2568+ inventory_id: inventory_exhaustive
2569+ product_qty: 5.0
2570+ location_id: stock.stock_location_14
2571+
2572+-
2573+ I will call the function _get_locations_open_inventories and check the result.
2574+ The function will return only the location_id of the exhaustive inventory.
2575+-
2576+ !python {model: stock.inventory}: |
2577+ locations = self._get_locations_open_inventories(cr, uid)
2578+ assert len(locations) == 1, "Function return wrong results: %s" % locations
2579+ assert locations[0] == ref('stock.stock_location_14'), "Function '_get_locations_open_inventories' return wrong location_id. Should be '%s': '%s'" % (stock.stock_location_14, locations[0])
2580+-
2581+ I will call the function onchange_location_id.
2582+ The function must return True in the first case, and return a warning dictionnary in the second test.
2583+-
2584+ !python {model: stock.inventory.line}: |
2585+ res = self.onchange_location_id(cr, uid, [], ref('stock.stock_location_14'), True, ref('stock.stock_location_14'))
2586+ assert res == True, "Exhaustive: The function 'onchange_location_id' should return True and return '%s'" % res
2587+ res = self.onchange_location_id(cr, uid, [], ref('stock.stock_location_14'), True, ref('stock.stock_location_components'))
2588+ assert res.get('warning', False) != False , "Function 'onchange_location_id': Warning not raise. ('%s)" % res
2589+
2590+-
2591+ I will check that the function get_missing_locations does not return any locations.
2592+-
2593+ !python {model: stock.inventory}: |
2594+ missing_loc_ids = self.get_missing_locations(cr, uid, [ref('inventory_exhaustive')], context=context)
2595+ assert not missing_loc_ids, "get_missing_locations should not return IDs but returned %s" % missing_loc_ids
2596+-
2597+ I create a wizard record for stock_confirm_uninventoried_location and validate it
2598+-
2599+ !python {model: stock.inventory.uninventoried.locations}: |
2600+ ctx = dict(context, active_ids=[ref('inventory_exhaustive')])
2601+ wizard_id = self.create(cr, uid, {}, context=ctx)
2602+ wizard = self.browse(cr, uid, wizard_id, context=ctx)
2603+ assert len(wizard.location_ids) == 0 , "The wizard should not contain any lines but contains %s." % wizard.location_ids
2604+ self.confirm_uninventoried_locations(cr, uid, wizard_id, context=ctx)
2605+-
2606+ Stock moves are not allowed in the locations during the inventory.
2607+-
2608+ !python {model: stock.move}: |
2609+ # TODOv8: remove this test, this is already part of trunk-wms
2610+ from stock_inventory_location import ExhaustiveInventoryException
2611+ try:
2612+ self.create(
2613+ cr,uid, {
2614+ 'name': 'Bad move',
2615+ 'location_id': ref('stock.stock_location_14'),
2616+ 'location_dest_id': ref('stock.stock_location_3'),
2617+ 'product_id': ref('product.product_product_8'),
2618+ 'product_uom': ref('product.product_uom_unit'),
2619+ 'date_expected': '2020-01-01 00:00:00'
2620+ })
2621+ except ExhaustiveInventoryException as e:
2622+ log("Good! The Stock Move was refused: %s" % e)
2623+-
2624+ I will confirm the exhaustive inventory
2625+-
2626+ !python {model: stock.inventory}: |
2627+ self.action_confirm(cr, uid, [ref('inventory_exhaustive')])
2628+ inventory_state = self.read(cr, uid, [ref('inventory_exhaustive')], ['state'])[0]['state']
2629+ assert inventory_state == 'confirm', "Exhaustive inventory is in state '%s'. It should be 'confirm'" % inventory_state
2630+
2631+-
2632+ I will validate the exhaustive inventory
2633+-
2634+ !python {model: stock.inventory}: |
2635+ self.action_done(cr, uid, [ref('inventory_exhaustive')])
2636+ inventory_state = self.read(cr, uid, [ref('inventory_exhaustive')], ['state'])[0]['state']
2637+ assert inventory_state == 'done', "Exhaustive inventory is in state '%s'. It should be 'done'" % inventory_state
2638+
2639+-
2640+ I will verify the quantity for each products.
2641+-
2642+ !python {model: product.product}: |
2643+ ctx = dict(context, location=[ref('stock.stock_location_14')])
2644+ prod_qty_avail = self.read(cr, uid, [ref('product.product_product_7')], ['qty_available'], context=ctx)[0]['qty_available']
2645+ assert prod_qty_avail == 18.0, "The stock of PC1 was not set to 18.0: %s" % prod_qty_avail
2646+
2647+ prod_qty_avail = self.read(cr, uid, [ref('product.product_product_8')], ['qty_available'], context=ctx)[0]['qty_available']
2648+ assert prod_qty_avail == 5.0, "The stock of PC3 was not set to 5.0: %s" % prod_qty_avail
2649+
2650+ prod_qty_avail = self.read(cr, uid, [ref('product.product_product_24')], ['qty_available'], context=ctx)[0]['qty_available']
2651+ assert prod_qty_avail == 0.0, "The stock of KEYA was not set to 0: %s" % prod_qty_avail
2652+
2653+ prod_qty_avail = self.read(cr, uid, [ref('product.product_product_25')], ['qty_available'], context=ctx)[0]['qty_available']
2654+ assert prod_qty_avail == 0.0, "The stock of MOU was not set to 0: %s" % prod_qty_avail
2655
2656=== added file 'stock_inventory_location/tests/inventory_future_test.yml'
2657--- stock_inventory_location/tests/inventory_future_test.yml 1970-01-01 00:00:00 +0000
2658+++ stock_inventory_location/tests/inventory_future_test.yml 2014-06-27 09:18:58 +0000
2659@@ -0,0 +1,13 @@
2660+-
2661+ Check that an inventory with a date in the future cannot be opened.
2662+-
2663+ !python {model: stock.inventory}: |
2664+ # TODO v8: maybe this is already part of the new WMS
2665+ from osv.osv import except_osv
2666+ self.action_open(cr, uid, [ref('inventory_future')])
2667+ try:
2668+ self.action_done(cr, uid, [ref('inventory_future')])
2669+ except except_osv as e:
2670+ log("Good! The Inventory could not be opened: %s" % e)
2671+ inventory_state = self.read(cr, uid, [ref('inventory_future')], ['state'])[0]['state']
2672+ assert inventory_state != 'done', "Future inventory is done."
2673
2674=== added file 'stock_inventory_location/tests/inventory_standard_test.yml'
2675--- stock_inventory_location/tests/inventory_standard_test.yml 1970-01-01 00:00:00 +0000
2676+++ stock_inventory_location/tests/inventory_standard_test.yml 2014-06-27 09:18:58 +0000
2677@@ -0,0 +1,100 @@
2678+-
2679+ This file will test a non exhaustive inventory.
2680+ I will call open_action method and check if state of inventories are 'open'.
2681+-
2682+ !python {model: stock.inventory}: |
2683+ self.action_open(cr, uid, [ref('inventory_standard')])
2684+ inventory_state = self.read(cr, uid, [ref('inventory_standard')], ['state'])[0]['state']
2685+ assert inventory_state == 'open', "Partial inventory have '%s' state. It should be 'open'" % inventory_state
2686+
2687+-
2688+ In order, I add products to inventory.
2689+ Adding Azerty Keyboard.
2690+-
2691+ !record {model: stock.inventory.line, id: lines_inventory_location_kbaz}:
2692+ product_id: product.product_product_9
2693+ product_uom: product.product_uom_unit
2694+ company_id: base.main_company
2695+ inventory_id: inventory_standard
2696+ product_qty: 18.0
2697+ location_id: stock.stock_location_components
2698+
2699+-
2700+ Adding Optical Mouse.
2701+-
2702+ !record {model: stock.inventory.line, id: lines_inventory_location_optm}:
2703+ product_id: product.product_product_10
2704+ product_uom: product.product_uom_unit
2705+ company_id: base.main_company
2706+ inventory_id: inventory_standard
2707+ product_qty: 12.0
2708+ location_id: stock.stock_location_components
2709+
2710+-
2711+ Adding Multimedia Speakers.
2712+-
2713+ !record {model: stock.inventory.line, id: lines_inventory_location_grca}:
2714+ product_id: product.product_template_31
2715+ product_uom: product.product_uom_unit
2716+ company_id: base.main_company
2717+ inventory_id: inventory_standard
2718+ product_qty: 32.0
2719+ location_id: stock.stock_location_components
2720+
2721+-
2722+ I will call the function _get_locations_open_inventories and check the result.
2723+ The function will return no locations because it's not an exhaustive inventory.
2724+-
2725+ !python {model: stock.inventory}: |
2726+ locations = self._get_locations_open_inventories(cr, uid)
2727+ assert len(locations) == 0, "Function return wrong results: %s" % locations
2728+
2729+-
2730+ I will call the function onchange_location_id.
2731+ The function must to return true in all test case.
2732+-
2733+ !python {model: stock.inventory.line}: |
2734+ res = self.onchange_location_id(cr, uid, [], [(6,0,[ref('stock.stock_location_components')])], False, ref('stock.stock_location_components'))
2735+ assert res == True, "The function 'onchange_location_id' should return True and return '%s'" % res
2736+ res = self.onchange_location_id(cr, uid, [], [(6,0,[ref('stock.stock_location_components')])], False, ref('stock.stock_location_14'))
2737+ assert res == True, "The function 'onchange_location_id' should return True and return '%s'" % res
2738+
2739+-
2740+ I will call the function _check_inventory.
2741+ The function must return True in all test cases.
2742+-
2743+ !python {model: stock.location}: |
2744+ res = self._check_inventory(cr, uid, ref('stock.stock_location_components'))
2745+ assert res == True, "The function '_check_inventory' should return True and return '%s'" % res
2746+ res = self._check_inventory(cr, uid, ref('stock.stock_location_14'))
2747+ assert res == True, "The function '_check_inventory' should return True and return '%s'" % res
2748+
2749+-
2750+ I will confirm inventory.
2751+-
2752+ !python {model: stock.inventory}: |
2753+ self.action_confirm(cr, uid, [ref('inventory_standard')])
2754+ inventory_state = self.read(cr, uid, [ref('inventory_standard')], ['state'])[0]['state']
2755+ assert inventory_state == 'confirm', "Partial inventory have '%s' state. It should be 'confirm'" % inventory_state
2756+
2757+-
2758+ I will validate inventory
2759+-
2760+ !python {model: stock.inventory}: |
2761+ self.action_done(cr, uid, [ref('inventory_standard')])
2762+ inventory_state = self.read(cr, uid, [ref('inventory_standard')], ['state'])[0]['state']
2763+ assert inventory_state == 'done', "Partial inventory have '%s' state. It should be 'done'" % inventory_state
2764+
2765+-
2766+ I will verify the quantity for each products.
2767+-
2768+ !python {model: product.product}: |
2769+ ctx={'location': [ref('stock.stock_location_components')]}
2770+ prod_qty_avail = self.read(cr, uid, [ref('product.product_product_9')], ['qty_available'], context=ctx)[0]['qty_available']
2771+ assert prod_qty_avail == 18.0, "The stock of CPU1 was not set to 18.0: %s" % prod_qty_avail
2772+
2773+ prod_qty_avail = self.read(cr, uid, [ref('product.product_product_10')], ['qty_available'], context=ctx)[0]['qty_available']
2774+ assert prod_qty_avail == 12.0, "The stock of CPU3 was not set to 12.0: %s" % prod_qty_avail
2775+
2776+ prod_qty_avail = self.read(cr, uid, [ref('product.product_template_31')], ['qty_available'], context=ctx)[0]['qty_available']
2777+ assert prod_qty_avail == 32.0, "The stock of FAN was not set to 32.0: %s" % prod_qty_avail
2778
2779=== added file 'stock_inventory_location/tests/stock_inventory_location_test.py'
2780--- stock_inventory_location/tests/stock_inventory_location_test.py 1970-01-01 00:00:00 +0000
2781+++ stock_inventory_location/tests/stock_inventory_location_test.py 2014-06-27 09:18:58 +0000
2782@@ -0,0 +1,47 @@
2783+# -*- coding: utf-8 -*-
2784+#
2785+#
2786+# Authors: Laetitia Gangloff
2787+# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
2788+# All Rights Reserved
2789+#
2790+# WARNING: This program as such is intended to be used by professional
2791+# programmers who take the whole responsibility of assessing all potential
2792+# consequences resulting from its eventual inadequacies and bugs.
2793+# End users who are looking for a ready-to-use solution with commercial
2794+# guarantees and support are strongly advised to contact a Free Software
2795+# Service Company.
2796+#
2797+# This program is free software: you can redistribute it and/or modify
2798+# it under the terms of the GNU Affero General Public License as
2799+# published by the Free Software Foundation, either version 3 of the
2800+# License, or (at your option) any later version.
2801+#
2802+# This program is distributed in the hope that it will be useful,
2803+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2804+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2805+# GNU Affero General Public License for more details.
2806+#
2807+# You should have received a copy of the GNU Affero General Public License
2808+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2809+#
2810+#
2811+
2812+import openerp.tests.common as common
2813+
2814+DB = common.DB
2815+ADMIN_USER_ID = common.ADMIN_USER_ID
2816+
2817+
2818+class stock_inventory_location_test(common.TransactionCase):
2819+
2820+ def setUp(self):
2821+ super(stock_inventory_location_test, self).setUp()
2822+
2823+ def test_update_parent_location(self):
2824+ """
2825+ Test the update of the parent of a location (no inventory in progress
2826+ """
2827+ self.registry('stock.location').write(self.cr, self.uid, self.ref('stock.stock_location_5'), {'location_id': self.ref('stock.stock_location_4')})
2828+
2829+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
2830
2831=== added directory 'stock_inventory_location/wizard'
2832=== added file 'stock_inventory_location/wizard/__init__.py'
2833--- stock_inventory_location/wizard/__init__.py 1970-01-01 00:00:00 +0000
2834+++ stock_inventory_location/wizard/__init__.py 2014-06-27 09:18:58 +0000
2835@@ -0,0 +1,22 @@
2836+# -*- coding: utf-8 -*-1
2837+##############################################################################
2838+#
2839+# This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
2840+#
2841+# This program is free software: you can redistribute it and/or modify
2842+# it under the terms of the GNU General Public License as published by
2843+# the Free Software Foundation, either version 3 of the License, or
2844+# (at your option) any later version.
2845+#
2846+# This program is distributed in the hope that it will be useful,
2847+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2848+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2849+# GNU General Public License for more details.
2850+#
2851+# You should have received a copy of the GNU General Public License
2852+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2853+#
2854+##############################################################################
2855+
2856+import stock_fill_location_inventory
2857+import stock_confirm_uninventoried_location
2858
2859=== added file 'stock_inventory_location/wizard/stock_confirm_uninventoried_location.py'
2860--- stock_inventory_location/wizard/stock_confirm_uninventoried_location.py 1970-01-01 00:00:00 +0000
2861+++ stock_inventory_location/wizard/stock_confirm_uninventoried_location.py 2014-06-27 09:18:58 +0000
2862@@ -0,0 +1,68 @@
2863+# -*- encoding: utf-8 -*-
2864+##############################################################################
2865+#
2866+# This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
2867+#
2868+# This program is free software: you can redistribute it and/or modify
2869+# it under the terms of the GNU General Public License as published by
2870+# the Free Software Foundation, either version 3 of the License, or
2871+# (at your option) any later version.
2872+#
2873+# This program is distributed in the hope that it will be useful,
2874+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2875+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2876+# GNU General Public License for more details.
2877+#
2878+# You should have received a copy of the GNU General Public License
2879+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2880+#
2881+##############################################################################
2882+
2883+from openerp.osv import fields, orm, osv
2884+
2885+
2886+class stock_inventory_uninventoried_location(orm.TransientModel):
2887+ _name = 'stock.inventory.uninventoried.locations'
2888+ _description = 'Confirm the uninventoried Locations.'
2889+
2890+ _columns = {
2891+ 'location_ids': fields.many2many(
2892+ 'stock.location',
2893+ 'stock_inventory_uninventoried_location_rel',
2894+ 'location_id', 'wizard_id',
2895+ 'Uninventoried location', readonly=True),
2896+ }
2897+
2898+ def default_locations(self, cr, uid, context=None):
2899+ """Initialize view with the list of uninventoried locations."""
2900+ return self.pool['stock.inventory'].get_missing_locations(
2901+ cr, uid, context['active_ids'], context=context)
2902+
2903+ _defaults = {
2904+ 'location_ids': default_locations,
2905+ }
2906+
2907+ def confirm_uninventoried_locations(self, cr, uid, ids, context=None):
2908+ """Add the missing inventory lines with qty=0 and confirm inventory"""
2909+ inventory_ids = context['active_ids']
2910+ inventory_obj = self.pool['stock.inventory']
2911+ wizard_obj = self.pool['stock.fill.inventory']
2912+ for inventory in inventory_obj.browse(cr, uid, inventory_ids,
2913+ context=context):
2914+ if inventory.exhaustive:
2915+ # silently run the import wizard with qty=0
2916+ try:
2917+ # on parent inventory it is possible that fill inventory fail with no product
2918+ wizard_id = wizard_obj.create(
2919+ cr, uid, {'location_id': inventory.location_id.id,
2920+ 'recursive': True,
2921+ 'set_stock_zero': True,
2922+ 'exhaustive': True}, context=context)
2923+ wizard_obj.fill_inventory(cr, uid, [wizard_id],
2924+ context=context)
2925+ except osv.except_osv, e:
2926+ if e.value == _('No product in this location. Please select a location in the product form.'):
2927+ pass
2928+
2929+ inventory_obj.action_confirm(cr, uid, inventory_ids, context=context)
2930+ return {'type': 'ir.actions.act_window_close'}
2931
2932=== added file 'stock_inventory_location/wizard/stock_confirm_uninventoried_location.xml'
2933--- stock_inventory_location/wizard/stock_confirm_uninventoried_location.xml 1970-01-01 00:00:00 +0000
2934+++ stock_inventory_location/wizard/stock_confirm_uninventoried_location.xml 2014-06-27 09:18:58 +0000
2935@@ -0,0 +1,33 @@
2936+<?xml version="1.0" encoding="utf-8"?>
2937+<openerp>
2938+ <data>
2939+ <!-- The view definition is similar with stock_inventory_hierarchical_location/wizard/stock_inventory_missing_locations_view.xml,
2940+ but the code is different.
2941+ This wizard compare declared locations with locations on inventory lines to present the uninventoried (empty) locations to user. -->
2942+ <record id="view_confirm_uninventoried_location" model="ir.ui.view">
2943+ <field name="name">stock.inventory.uninventoried.locations.form</field>
2944+ <field name="model">stock.inventory.uninventoried.locations</field>
2945+ <field name="arch" type="xml">
2946+ <form string="Confirm empty locations" version="7.0">
2947+ <group colspan="4" col="1">
2948+ <separator string="The following Locations are empty"/>
2949+ <label string="The following Stock Locations are part of the current Physical Inventory, but no Inventory Line has been recorded for them."/>
2950+ <label string="It could either mean that the Locations are empty, or that the Inventory is not yet complete."/>
2951+ <label string="If you confirm the Inventory, these Locations will be considered empty and their content will be purged."/>
2952+ <field name="location_ids" nolabel="1">
2953+ <tree>
2954+ <field name="name"/>
2955+ </tree>
2956+ </field>
2957+ <separator string=""/>
2958+ </group>
2959+ <footer>
2960+ <button name="confirm_uninventoried_locations" string="Purge contents and confirm Inventory" type="object" class="oe_highlight"/>
2961+ or
2962+ <button string="Cancel" class="oe_link" special="cancel" default_focus="1"/>
2963+ </footer>
2964+ </form>
2965+ </field>
2966+ </record>
2967+ </data>
2968+</openerp>
2969\ No newline at end of file
2970
2971=== added file 'stock_inventory_location/wizard/stock_fill_location_inventory.py'
2972--- stock_inventory_location/wizard/stock_fill_location_inventory.py 1970-01-01 00:00:00 +0000
2973+++ stock_inventory_location/wizard/stock_fill_location_inventory.py 2014-06-27 09:18:58 +0000
2974@@ -0,0 +1,47 @@
2975+# -*- coding: utf-8 -*-
2976+##############################################################################
2977+#
2978+# This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
2979+#
2980+# This program is free software: you can redistribute it and/or modify
2981+# it under the terms of the GNU General Public License as published by
2982+# the Free Software Foundation, either version 3 of the License, or
2983+# (at your option) any later version.
2984+#
2985+# This program is distributed in the hope that it will be useful,
2986+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2987+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2988+# GNU General Public License for more details.
2989+#
2990+# You should have received a copy of the GNU General Public License
2991+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2992+#
2993+##############################################################################
2994+
2995+from openerp.osv import fields, orm
2996+
2997+
2998+class FillInventoryWizard(orm.TransientModel):
2999+ """Add a field that lets the client make the location read-only"""
3000+ _inherit = 'stock.fill.inventory'
3001+
3002+ _columns = {
3003+ 'exhaustive': fields.boolean('Exhaustive', readonly=True)
3004+ }
3005+
3006+ def default_get(self, cr, uid, fields, context=None):
3007+ """Get 'location_id' and 'exhaustive' from the inventory"""
3008+ if context is None:
3009+ context = {}
3010+ inv_id = context.get('active_id')
3011+
3012+ res = super(FillInventoryWizard, self).default_get(
3013+ cr, uid, fields, context=context)
3014+ if (context.get('active_model') == 'stock.inventory'
3015+ and inv_id
3016+ and 'location_id' in fields):
3017+ inventory = self.pool['stock.inventory'].browse(
3018+ cr, uid, context['active_id'], context=context)
3019+ res.update({'location_id': inventory.location_id.id,
3020+ 'exhaustive': inventory.exhaustive})
3021+ return res
3022
3023=== added file 'stock_inventory_location/wizard/stock_fill_location_inventory_view.xml'
3024--- stock_inventory_location/wizard/stock_fill_location_inventory_view.xml 1970-01-01 00:00:00 +0000
3025+++ stock_inventory_location/wizard/stock_fill_location_inventory_view.xml 2014-06-27 09:18:58 +0000
3026@@ -0,0 +1,18 @@
3027+<?xml version="1.0" encoding="utf-8"?>
3028+<openerp>
3029+ <data>
3030+ <record id="view_stock_fill_inventory_location" model="ir.ui.view">
3031+ <field name="name">Import Inventory</field>
3032+ <field name="model">stock.fill.inventory</field>
3033+ <field name="inherit_id" ref="stock.view_stock_fill_inventory" />
3034+ <field name="arch" type="xml">
3035+ <xpath expr="//field[@name='location_id']" position="after">
3036+ <field name="exhaustive" invisible="1" />
3037+ </xpath>
3038+ <xpath expr="//field[@name='location_id']" position="attributes">
3039+ <attribute name="attrs">{'readonly':[('exhaustive','=',True)]}</attribute>
3040+ </xpath>
3041+ </field>
3042+ </record>
3043+ </data>
3044+</openerp>

Subscribers

People subscribed via source and target branches