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

Proposed by Lionel Sausin - Initiatives/Numérigraphe
Status: Merged
Merged at revision: 38
Proposed branch: lp:~numerigraphe-team/stock-logistic-warehouse/7.0-inventory-location-ls
Merge into: lp:~numerigraphe-team/stock-logistic-warehouse/7.0-inventory-location
Diff against target: 1532 lines (+547/-540)
14 files modified
stock_inventory_location/__init__.py (+2/-0)
stock_inventory_location/__openerp__.py (+43/-28)
stock_inventory_location/exceptions.py (+26/-0)
stock_inventory_location/i18n/fr.po (+11/-31)
stock_inventory_location/stock_inventory_location.py (+238/-189)
stock_inventory_location/stock_inventory_location_demo.xml (+18/-17)
stock_inventory_location/stock_inventory_location_view.xml (+48/-48)
stock_inventory_location/test/inventory_exhaustive_test.yml (+65/-49)
stock_inventory_location/test/inventory_future_test.yml (+13/-0)
stock_inventory_location/test/inventory_standard_test.yml (+25/-34)
stock_inventory_location/wizard/stock_confirm_uninventoried_location.py (+22/-58)
stock_inventory_location/wizard/stock_confirm_uninventoried_location.xml (+12/-12)
stock_inventory_location/wizard/stock_fill_location_inventory.py (+23/-73)
stock_inventory_location/wizard/stock_fill_location_inventory_view.xml (+1/-1)
To merge this branch: bzr merge lp:~numerigraphe-team/stock-logistic-warehouse/7.0-inventory-location-ls
Reviewer Review Type Date Requested Status
Loïc Bellier - Numérigraphe Pending
Review via email: mp+222375@code.launchpad.net
To post a comment you must log in.
55. By Numérigraphe

[FIX] don't make the inventory location mandatory when it's invisible (ie. when the inventory is not exhaustive)

56. By Numérigraphe

[FIX] when starting the fill inventory wizard, get 'location_id' and 'exhaustive' from the inventory

57. By Numérigraphe

[IMP] better placement for the new fields in stock_inventory_location

58. By Numérigraphe

[ FIX] Fix onchange_location_id (it still looked for multiple locations in the inventory header), and get the default location for inventory lines from the inventory header if it's an exhaustive one, otherwise the on_change check does not let us enter anything.

59. By Numérigraphe

[REF] improve description, remove TODOs done, remove debugging "print" statements

60. By Numérigraphe

[IMP] don't show the confirmation wizard if all the locations are actually present in inventory lines

61. By Numérigraphe

[REF] pep8

62. By Numérigraphe

[IMP] update screenshots

63. By Laetitia Gangloff (Acsone)

[IMP] make the default location company-sensitive (take the location of the 1st warehouse of the default company - courtesy of Laetitia Gangloff (Acsone)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'stock_inventory_location/__init__.py'
2--- stock_inventory_location/__init__.py 2014-03-12 14:55:39 +0000
3+++ stock_inventory_location/__init__.py 2014-06-11 15:01:48 +0000
4@@ -20,3 +20,5 @@
5
6 import stock_inventory_location
7 import wizard
8+# Bring the main exception into the package's scope for easier reuse
9+from .exceptions import ExhaustiveInventoryException
10
11=== modified file 'stock_inventory_location/__openerp__.py'
12--- stock_inventory_location/__openerp__.py 2014-03-28 16:13:01 +0000
13+++ stock_inventory_location/__openerp__.py 2014-06-11 15:01:48 +0000
14@@ -18,45 +18,60 @@
15 #
16 ##############################################################################
17
18-
19 {
20 "name": "Exhaustive Stock Inventories",
21- "version": "1.0",
22+ "version": "1.1",
23 "depends": ["stock"],
24 "author": u"Numérigraphe",
25 "category": "Warehouse Management",
26 "description": """
27-Let users choose between standard and exhaustive Inventories
28-============================================================
29+Let users make exhaustive Inventories
30+=====================================
31
32-Standard Physical Inventories in OpenERP only contain a generic list of products by locations,
33-which is well suited to partial Inventories and simple warehouses.
34-When the a standard Inventory is confirmed, only the products in the inventory are checked.
35-If a Product is present in the computed stock and not in the recorded Inventory, OpenERP will
36-consider that it remains unchanged.
37+Standard Physical Inventories in OpenERP only contain a generic list of
38+products by locations, which is well suited to partial Inventories and simple
39+warehouses. When the a standard Inventory is confirmed, only the products in
40+the inventory are checked. If a Product is present in the computed stock and
41+not in the recorded Inventory, OpenERP will consider that it remains unchanged.
42
43 But for exhaustive inventories in complex warehouses, it is not practical:
44- - you want to avoid Stock Moves in/out of these Locations while you count the goods
45+ - you want to avoid Stock Moves to/from these Locations while counting goods
46 - you must make sure all the locations you want have been counted
47 - you must make sure no other location has been counted by mistake
48- - you want the computed stock to perfectly match the inventory when you confirm it.
49+ - you want the computed stock to perfectly match the inventory when you
50+ confirm it.
51
52-This module lets choose whether an Physical Inventory is exhaustive or standard.
53-For an exhaustive Inventory, in the state "Draft" you define the list of Locations where goods must be counted.
54- - a new Inventory status ("Open") lets you indicate that the list of Locations is definitive and you are now counting the goods.
55- In that status, no Stock Moves can be recorded in/out of the Inventory's Locations.
56- - if some of the Inventory's Locations have not been entered in the Inventory Lines, OpenERP warns you when you confirm the Inventory.
57- - only the Inventory's Locations can be entered in the Inventory Lines.
58- - every good that is not in the Inventory Lines is considered lost, and gets moved out of the stock when you confirm the Inventory.avec openerp
59+This module lets choose whether an Physical Inventory is exhaustive or
60+standard.
61+For an exhaustive Inventory:
62+ - in the state "Draft" you define the Location where goods must be counted.
63+ - the new Inventory status "Open" lets you indicate that the list of Locations
64+ is final and you are now counting the goods.
65+ In that status, no Stock Moves can be recorded in/out of the Inventory's
66+ Locations.
67+ - if the Location or some of it's children have not been entered in the
68+ Inventory Lines, OpenERP warns you when you confirm the Inventory.
69+ - only the Inventory's Location or its children can be entered in the
70+ Inventory Lines.
71+ - every good that is not in the Inventory Lines is considered lost, and gets
72+ moved out of the stock when you confirm the Inventory.
73 """,
74- "update_xml": [
75- "wizard/stock_confirm_uninventoried_location.xml",
76- "stock_inventory_location_view.xml",
77- "wizard/stock_fill_location_inventory_view.xml",
78- ],
79- "test": ["test/location_inventory_test.yml",
80- "test/location_exhaustive_inventory_test.yml",
81- ],
82- "demo": ["stock_inventory_location_demo.xml"]
83-
84+ "data": [
85+ "wizard/stock_confirm_uninventoried_location.xml",
86+ "stock_inventory_location_view.xml",
87+ "wizard/stock_fill_location_inventory_view.xml",
88+ ],
89+ "test": [
90+ "test/inventory_standard_test.yml",
91+ "test/inventory_exhaustive_test.yml",
92+ "test/inventory_future_test.yml",
93+ ],
94+ "images": [
95+ "images/inventory_form.png",
96+ "inventory_empty_locations.png",
97+ "images/move_error.png",
98+ "images/location_locked.png",
99+ "images/future_inventory.png",
100+ ],
101+ "demo": ["stock_inventory_location_demo.xml"]
102 }
103
104=== added file 'stock_inventory_location/exceptions.py'
105--- stock_inventory_location/exceptions.py 1970-01-01 00:00:00 +0000
106+++ stock_inventory_location/exceptions.py 2014-06-11 15:01:48 +0000
107@@ -0,0 +1,26 @@
108+# -*- coding: utf-8 -*-
109+##############################################################################
110+#
111+# This module is copyright (C) 2013 Numérigraphe SARL. All Rights Reserved.
112+#
113+# This program is free software: you can redistribute it and/or modify
114+# it under the terms of the GNU General Public License as published by
115+# the Free Software Foundation, either version 3 of the License, or
116+# (at your option) any later version.
117+#
118+# This program is distributed in the hope that it will be useful,
119+# but WITHOUT ANY WARRANTY; without even the implied warranty of
120+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
121+# GNU General Public License for more details.
122+#
123+# You should have received a copy of the GNU General Public License
124+# along with this program. If not, see <http://www.gnu.org/licenses/>.
125+#
126+##############################################################################
127+
128+from openerp.osv import orm
129+
130+
131+class ExhaustiveInventoryException(orm.except_orm):
132+ """The operation is not possible for an exhaustive inventory"""
133+ pass
134
135=== modified file 'stock_inventory_location/i18n/fr.po'
136--- stock_inventory_location/i18n/fr.po 2014-03-28 16:13:01 +0000
137+++ stock_inventory_location/i18n/fr.po 2014-06-11 15:01:48 +0000
138@@ -20,7 +20,7 @@
139 #: constraint:stock.move:0
140 #, python-format
141 msgid "A Physical Inventory is being conducted at this location"
142-msgstr "Un inventaire est déjà en cours à cet emplacement"
143+msgstr "Un inventaire est en cours à cet emplacement"
144
145 #. module: stock_inventory_location
146 #: view:stock.inventory.uninventoried.locations:0
147@@ -63,8 +63,8 @@
148
149 #. module: stock_inventory_location
150 #: view:stock.inventory.uninventoried.locations:0
151-msgid "Confirm uninventoried locations"
152-msgstr "Confirmez les emplacements non-inventoriés"
153+msgid "Confirm empty locations"
154+msgstr "Confirmez les emplacements vides"
155
156 #. module: stock_inventory_location
157 #: model:ir.model,name:stock_inventory_location.model_stock_location
158@@ -87,7 +87,7 @@
159 #: code:addons/stock_inventory_location/stock_inventory_location.py:231
160 #: code:addons/stock_inventory_location/stock_inventory_location.py:282
161 #, python-format
162-msgid "Error: Location on inventory"
163+msgid "Error: Location locked down"
164 msgstr "Erreur: emplacement en inventaire"
165
166 #. module: stock_inventory_location
167@@ -138,12 +138,6 @@
168 msgstr "Emplacement manquant pour cet inventaire."
169
170 #. module: stock_inventory_location
171-#: view:stock.inventory:0
172-#: field:stock.inventory,location_ids:0
173-msgid "Locations"
174-msgstr "Emplacements"
175-
176-#. module: stock_inventory_location
177 #: model:ir.model,name:stock_inventory_location.model_stock_move
178 msgid "Mouvement de stock"
179 msgstr "Mouvement de stock"
180@@ -171,15 +165,10 @@
181 #. module: stock_inventory_location
182 #: code:addons/stock_inventory_location/stock_inventory_location.py:283
183 #, python-format
184-msgid "One or more locations are inventoried:\n"
185-"%s"
186-msgstr "Un ou plusieurs emplacements sont en inventaire :\n"
187-"%s"
188-
189-#. module: stock_inventory_location
190-#: constraint:stock.move:0
191-msgid "One or more lots are awaiting quality control and cannot be moved."
192-msgstr "Un ou plusieurs lots sont en attente de contrôle qualité, et ne peuvent pas être déplacés."
193+msgid "A Physical Inventory is being conducted at the following location(s):\n"
194+"%s"
195+msgstr "Les emplacements suivants sont en inventaire :\n"
196+"%s"
197
198 #. module: stock_inventory_location
199 #: view:stock.inventory:0
200@@ -198,19 +187,10 @@
201
202 #. module: stock_inventory_location
203 #: view:stock.inventory.uninventoried.locations:0
204-msgid "The following Stock Locations are part of the current Physical Inventory, but not Inventory Line has been recorded for them."
205+msgid "The following Stock Locations are part of the current Physical Inventory, but no Inventory Line has been recorded for them."
206 msgstr "Les emplacements suivants font partie de l'inventaire en cours, mais aucune ligne d'inventaire les concernant n'a été enregistrée."
207
208 #. module: stock_inventory_location
209-#: help:stock.inventory,location_ids:0
210-msgid "This is the list of the Stock Locations that you want to count the goods in.\n"
211-"Only these Locations can be entered in the Inventory Lines.\n"
212-"If some of them have not been entered in the Inventory Lines, OpenERP will warn you when you confirm the Inventory."
213-msgstr "Ceci est la liste des emplacements dont vous voulez compter la marchandise.\n"
214-"Seuls ces emplacements peuvent être enregistrés dans les lignes d'inventaire.\n"
215-"Si certains ne sont enregistrés sur aucune ligne d'inventaire, OpenERP vous avertit lors de la confirmation de l'inventaire."
216-
217-#. module: stock_inventory_location
218 #: field:stock.inventory.uninventoried.locations,location_ids:0
219 msgid "Uninventoried location"
220 msgstr "Emplacements non inventoriés"
221@@ -232,8 +212,8 @@
222 #. module: stock_inventory_location
223 #: code:addons/stock_inventory_location/stock_inventory_location.py:210
224 #, python-format
225-msgid "You cannot add this location to inventory line.\n"
226-"You must add this location on the locations list"
227+msgid "You cannot record an Inventory Line for this Location.\n"
228+"You must first add this Location to the list of affected Locations on the Inventory form."
229 msgstr "Vous ne pouvez pas ajouter cet emplacement.\n"
230 "Il ne fait pas partie de la liste des emplacements à inventorier"
231
232
233=== added directory 'stock_inventory_location/images'
234=== added file 'stock_inventory_location/images/future_inventory.png'
235Binary 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-11 15:01:48 +0000 differ
236=== added file 'stock_inventory_location/images/inventory_empty_locations.png'
237Binary 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-11 15:01:48 +0000 differ
238=== added file 'stock_inventory_location/images/inventory_form.png'
239Binary 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-11 15:01:48 +0000 differ
240=== added file 'stock_inventory_location/images/location_locked.png'
241Binary 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-11 15:01:48 +0000 differ
242=== added file 'stock_inventory_location/images/move_error.png'
243Binary 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-11 15:01:48 +0000 differ
244=== added directory 'stock_inventory_location/static'
245=== added directory 'stock_inventory_location/static/src'
246=== added directory 'stock_inventory_location/static/src/img'
247=== added file 'stock_inventory_location/static/src/img/icon.png'
248Binary 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-11 15:01:48 +0000 differ
249=== modified file 'stock_inventory_location/stock_inventory_location.py'
250--- stock_inventory_location/stock_inventory_location.py 2014-03-28 16:13:01 +0000
251+++ stock_inventory_location/stock_inventory_location.py 2014-06-11 15:01:48 +0000
252@@ -18,192 +18,205 @@
253 #
254 ##############################################################################
255
256+import time
257 from collections import Iterable
258
259 from openerp.osv import orm, fields
260 from openerp.tools.translate import _
261-
262-
263-class EmptyLocationException(orm.except_orm):
264- def __init__(self, name, value):
265- self.name = name
266- self.value = value
267- self.args = (name, value)
268+# The next 2 imports are only needed for a feature backported from trunk-wms
269+# TODOv8! remove, feature is included upstream
270+from openerp.osv import osv
271+from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
272+
273+from .exceptions import ExhaustiveInventoryException
274
275
276 class StockInventory(orm.Model):
277 """Add locations to the inventories"""
278 _inherit = 'stock.inventory'
279+
280+ INVENTORY_STATE_SELECTION = [
281+ ('draft', 'Draft'),
282+ ('open', 'Open'),
283+ ('done', 'Done'),
284+ ('confirm', 'Confirmed'),
285+ ('cancel', 'Cancelled')
286+ ]
287+
288 _columns = {
289- # XXX refactor if ever lp:~numerigraphe/openobject-addons/7.0-inventory-states is accepted upstream
290- 'state': fields.selection((('draft', 'Draft'),
291- ('open', 'Open'),
292- ('done', 'Done'),
293- ('confirm', 'Confirmed'),
294- ('cancel', 'Cancelled')), 'State', readonly=True, select=True),
295- # Make the inventory lines read-only in all states except "Open", to ensure that no unwanted Location can be inserted
296- 'inventory_line_id': fields.one2many('stock.inventory.line', 'inventory_id', 'Inventory lines', readonly=True, states={'open': [('readonly', False)]}),
297- 'location_ids': fields.many2many('stock.location', 'stock_inventory_location_rel',
298- 'location_id', 'inventory_id',
299- 'Locations',
300- readonly=True, states={'draft': [('readonly', False)]},
301- help="""This is the list of the Stock Locations that you want to count the goods in.
302-Only these Locations can be entered in the Inventory Lines.
303-If some of them have not been entered in the Inventory Lines, OpenERP will warn you when you confirm the Inventory."""),
304- 'exhaustive': fields.boolean('Exhaustive', readonly=True, states={'draft': [('readonly', False)]},
305- help="""Check the box if you are conducting an exhaustive Inventory.
306-Leave the box unchecked if you are conducting a standard Inventory (partial inventory for example).
307-For an exhaustive Inventory:
308- - the status "Draft" lets you define the list of Locations where goods must be counted
309- - 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
310- - only the Inventory's Locations can be entered in the Inventory Lines
311- - if some of the Inventory's Locations have not been entered in the Inventory Lines, OpenERP warns you when you confirm the Inventory
312- - every good that is not in the Inventory Lines is considered lost, and gets moved out of the stock when you confirm the Inventory"""),
313+ # TODO v8: should we use "confirm" instead of adding "open"?
314+ 'state': fields.selection(
315+ INVENTORY_STATE_SELECTION, 'State', readonly=True, select=True),
316+ # TODO v8: remove this, should not be needed anymore
317+ # Make the inventory lines read-only in all states except "Open",
318+ # to ensure that no unwanted Location can be inserted
319+ 'inventory_line_id': fields.one2many(
320+ 'stock.inventory.line', 'inventory_id', 'Inventory lines',
321+ readonly=True, states={'open': [('readonly', False)]}),
322+ # TODO v8: remove this, it's backported from v8
323+ 'location_id': fields.many2one(
324+ 'stock.location', 'Inventoried Location',
325+ readonly=True, states={'draft': [('readonly', False)]}),
326+ 'exhaustive': fields.boolean(
327+ 'Exhaustive', readonly=True,
328+ states={'draft': [('readonly', False)]},
329+ help="Check the box if you are conducting an exhaustive "
330+ "Inventory.\n"
331+ "Leave the box unchecked if you are conducting a standard "
332+ "Inventory (partial inventory for example).\n"
333+ "For an exhaustive Inventory:\n"
334+ " - the status \"Draft\" lets you define the list of "
335+ "Locations where goods must be counted\n"
336+ " - the status \"Open\" indicates that the list of Locations "
337+ "is definitive and you are now counting the goods. In that "
338+ "status, no Stock Moves can be recorded in/out of the "
339+ "Inventory's Locations\n"
340+ " - only the Inventory's Locations can be entered in the "
341+ "Inventory Lines\n"
342+ " - if some of the Inventory's Locations have not been "
343+ "entered in the Inventory Lines, OpenERP warns you "
344+ "when you confirm the Inventory\n"
345+ " - every good that is not in the Inventory Lines is "
346+ "considered lost, and gets moved out of the stock when you "
347+ "confirm the Inventory"),
348 }
349
350 def action_open(self, cr, uid, ids, context=None):
351- """Open the inventory: open all locations, import and print inventory sheet become possible"""
352- # verify if exhaustive inventory have locations before open inventory
353- for inventory in self.browse(cr, uid, ids, context=None):
354- if inventory.exhaustive:
355- if not inventory.location_ids:
356- raise orm.except_orm(_('Warning'), _('Location missing for this inventory.'))
357+ """Change the state of the inventory to 'open'"""
358 return self.write(cr, uid, ids, {'state': 'open'}, context=context)
359
360- # XXX refactor if ever lp:~numerigraphe/openobject-addons/7.0-inventory-states is accepted upstream
361+ # TODO v8: remove this method? the feature looks already done upstream
362+ def action_done(self, cr, uid, ids, context=None):
363+ """
364+ Don't allow to make an inventory with a date in the future.
365+
366+ This makes sure no stock moves will be introduced between the
367+ moment you finish the inventory and the date of the Stock Moves.
368+ Backported from trunk-wms:
369+ revid:qdp-launchpad@openerp.com-20140317090656-o7lo22tzm8yuv3r8
370+
371+ @raise osv.except_osv:
372+ We raise this exception on purpose instead of
373+ ExhaustiveInventoryException to ensure forward-compatibility
374+ with v8.
375+ """
376+ for inventory in self.browse(cr, uid, ids, context=None):
377+ if inventory.date > time.strftime(DEFAULT_SERVER_DATETIME_FORMAT):
378+ raise osv.except_osv(
379+ _('Error!'),
380+ _('It\'s impossible to confirm an inventory in the '
381+ 'future. Please change the inventory date to proceed '
382+ 'further.'))
383+ return super(StockInventory, self).action_done(cr, uid, ids,
384+ context=context)
385+
386+ # TODO: remove this in v8
387+ def _default_location(self, cr, uid, ids, context=None):
388+ """Default stock location
389+
390+ @return: id of the stock location of the first warehouse of the
391+ default company"""
392+ location_id = False
393+ company_id = self.pool['res.company']._company_default_get(
394+ cr, uid, 'stock.warehouse', context=context)
395+ warehouse_id = self.pool['stock.warehouse'].search(
396+ cr, uid, [('company_id', '=', company_id)], limit=1)
397+ if warehouse_id:
398+ location_id = self.pool['stock.warehouse'].read(
399+ cr, uid, warehouse_id[0], ['lot_stock_id'])['lot_stock_id'][0]
400+ return location_id
401+
402 _defaults = {
403 'state': 'draft',
404 'exhaustive': False,
405+ # TODO: remove this in v8
406+ 'location_id': _default_location,
407 }
408
409 def _check_location_free_from_inventories(self, cr, uid, ids):
410- """Verify that no other Inventory is being conducted on the location (exact id, not children)."""
411- for inventory in self.browse(cr, uid, ids, context=None):
412+ """
413+ Verify that no other Inventory is being conducted on the same locations
414+
415+ Open Inventories are matched using the exact Location IDs,
416+ excluding children.
417+ """
418+ # We don't get a context because we're called from a _constraint
419+ for inventory in self.browse(cr, uid, ids):
420 if not inventory.exhaustive:
421- continue # always accepted on partial inventories
422- location_ids = [location.id for location in inventory.location_ids]
423- inv_ids = self.search(cr, uid, [('location_ids', 'in', location_ids),
424- ('id', '!=', inventory.id),
425- ('date', '=', inventory.date),
426- ('exhaustive', '=', True), ])
427- if inv_ids:
428+ # never block standard inventories
429+ continue
430+ if self.search(cr, uid,
431+ [('location_id', '=', inventory.location_id.id),
432+ ('id', '!=', inventory.id),
433+ ('date', '=', inventory.date),
434+ ('exhaustive', '=', True)]):
435+ # Quit as soon as one offending inventory is found
436 return False
437 return True
438
439- _constraints = [(_check_location_free_from_inventories,
440- 'Other Physical inventories are being conducted using the same Locations.',
441- ['location_ids', 'date', 'exhaustive'])]
442+ _constraints = [
443+ (_check_location_free_from_inventories,
444+ 'Other Physical inventories are being conducted using the same '
445+ 'Locations.',
446+ ['location_id', 'date', 'exhaustive'])
447+ ]
448
449 def _get_locations_open_inventories(self, cr, uid, context=None):
450- """Search for locations on open inventories (exhaustive inventory only), and their children """
451- open_inventories_ids = self.search(cr, uid, [('state', '=', 'open'), ], context=context)
452- location_ids = set()
453- for open_inventory in self.browse(cr, uid, open_inventories_ids, context=context):
454- location_ids.update([location.id for location in open_inventory.location_ids])
455+ """IDs of location in open exhaustive inventories, with children"""
456+ inv_ids = self.search(
457+ cr, uid, [('state', '=', 'open'), ('exhaustive', '=', True)],
458+ context=context)
459+ if not inv_ids:
460+ # Early exit if no match found
461+ return []
462+ # List the Locations - normally all exhaustive inventories have one
463+ location_ids = [inventory.location_id.id
464+ for inventory in self.browse(cr, uid, inv_ids,
465+ context=context)]
466 # Extend to the children Locations
467- if location_ids: # XXX probably works even otherwise
468- location_ids = self.pool.get('stock.location').search(cr, uid,
469- [('location_id', 'child_of', location_ids), ('usage', '=', 'internal')],
470- context=context)
471- return location_ids
472-
473- def confirm_uninventoried_location_wizard(self, cr, uid, ids, context=None):
474- """ Open wizard if inventory is exhautive """
475+ return self.pool['stock.location'].search(
476+ cr, uid,
477+ [('location_id', 'child_of', set(location_ids)),
478+ ('usage', '=', 'internal')],
479+ context=context)
480+
481+ def get_missing_locations(self, cr, uid, ids, context=None):
482+ """Compute the list of location_ids which are missing from the lines
483+
484+ Here, "missing" means the location is the inventory's location or one
485+ of it's children, and the inventory does not contain any line with
486+ this location."""
487+ inventories = self.browse(cr, uid, ids, context=context)
488+ # Find the locations of the inventories
489+ inv_location_ids = [i.location_id.id for i in inventories]
490+ # Extend to the children locations
491+ inv_location_ids = set(self.pool['stock.location'].search(
492+ cr, uid, [
493+ ('location_id', 'child_of', inv_location_ids),
494+ ('usage', '=', 'internal')], context=context))
495+ # Find the locations already recorded in inventory lines
496+ line_locations_ids = set([l.location_id.id
497+ for l in i.inventory_line_id
498+ for i in inventories])
499+ return list(inv_location_ids - line_locations_ids)
500+
501+ def confirm_missing_locations(self, cr, uid, ids, context=None):
502+ """Open wizard to confirm empty locations on exhaustive inventories"""
503 for inventory in self.browse(cr, uid, ids, context=context):
504- if not inventory.exhaustive:
505- return self.action_confirm(cr, uid, ids, context=context)
506- else:
507- context['active_ids'] = ids
508- context['active_id'] = ids[0]
509+ if (self.get_missing_locations(cr, uid, ids, context=context)
510+ and inventory.exhaustive):
511 return {
512 'type': 'ir.actions.act_window',
513 'view_type': 'form',
514 'view_mode': 'form',
515 'res_model': 'stock.inventory.uninventoried.locations',
516 'target': 'new',
517- 'context': context,
518+ 'context': dict(context,
519+ active_ids=ids,
520+ active_id=ids[0]),
521 'nodestroy': True,
522- }
523-
524- # XXX: get from stock.py v6.0 patch. Waiting for integration on standard by openerp ...
525- def _fill_location_lines(self, cr, uid, inventory_id, location_ids, set_stock_zero, context=None):
526- """ To Import stock inventory according to products available in the selected locations.
527- @param self: The object pointer.
528- @param cr: A database cursor
529- @param uid: ID of the user currently logged in
530- @param location_ids: the location ID or list of location IDs if we want more than one
531- @param inventory_id: the inventory ID
532- @param set_stock_zero: indicate if all the lines will be imported with zero quantity
533- @param context: A standard dictionary
534- @return:
535- """
536- inventory_line_obj = self.pool.get('stock.inventory.line')
537- move_obj = self.pool.get('stock.move')
538- uom_obj = self.pool.get('product.uom')
539- res = []
540- flag = False
541- for location in location_ids:
542- datas = {}
543- move_ids = move_obj.search(cr, uid, ['|', ('location_dest_id', '=', location),
544- ('location_id', '=', location),
545- ('state', '=', 'done')], context=context)
546- for move in move_obj.browse(cr, uid, move_ids, context=context):
547- if move.location_dest_id.id == move.location_id.id:
548- continue
549- lot_id = move.prodlot_id.id
550- prod_id = move.product_id.id
551- if move.location_dest_id.id == location:
552- qty = uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_qty, move.product_id.uom_id.id)
553- else:
554- qty = -uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_qty, move.product_id.uom_id.id)
555- if datas.get((prod_id, lot_id)):
556- qty = qty + datas[(prod_id, lot_id)]['product_qty']
557-
558- datas[(prod_id, lot_id)] = {'product_id': prod_id,
559- 'location_id': location,
560- # Floating point sum could introduce some tiny rounding errors.
561- # The uom are the same on input and output to use api for rounding.
562- 'product_qty': uom_obj._compute_qty_obj(cr, uid, move.product_id.uom_id, qty, move.product_id.uom_id),
563- 'product_uom': move.product_id.uom_id.id,
564- 'prod_lot_id': lot_id,
565- 'default_code': move.product_id.default_code,
566- 'prodlot_name': move.prodlot_id.name}
567- if datas:
568- flag = True
569- res.append(datas)
570- if not flag:
571- raise EmptyLocationException(_('Warning'), _('No product in this location.'))
572-
573- stock_moves = []
574- for stock_move in res:
575- prod_lots = sorted(stock_move, key=lambda k: (stock_move[k]['default_code'], stock_move[k]['prodlot_name']))
576- for prod_lot in prod_lots:
577- stock_move_details = stock_move.get(prod_lot)
578-
579- if stock_move_details['product_qty'] == 0:
580- continue # ignore product if stock equal 0
581-
582- stock_move_details.update({'inventory_id': inventory_id})
583-
584- if set_stock_zero:
585- stock_move_details.update({'product_qty': 0})
586-
587- domain = [(field, '=', stock_move_details[field])
588- for field in ['location_id',
589- 'product_id',
590- 'prod_lot_id']
591- ]
592- # children_inventory_ids is present if stock_inventory_hierarchical_location module has been installed
593- inventory_ids = context.get('children_inventory_ids', False)
594- if inventory_ids:
595- domain.append(('inventory_id', 'child_of', inventory_ids))
596- else:
597- domain.append(('inventory_id', '=', stock_move_details['inventory_id']))
598-
599- line_ids = inventory_line_obj.search(cr, uid, domain, context=context)
600- if not line_ids:
601- stock_moves.append(stock_move_details)
602- return stock_moves
603+ }
604+ return self.action_confirm(cr, uid, ids, context=context)
605
606
607 class StockInventoryLine(orm.Model):
608@@ -211,21 +224,39 @@
609
610 _inherit = 'stock.inventory.line'
611
612- def onchange_location_id(self, cr, uid, ids, location_ids, exhaustive, location_id, context=None):
613- """ Raise exception if Location is not internal, or location_id not in locations list for this inventory """
614- location_ids = location_ids[0][2]
615- if not exhaustive or not location_ids:
616- return True # don't check if partial inventory
617+ def _default_stock_location(self, cr, uid, context=None):
618+ res = super(StockInventoryLine, self)._default_stock_location(
619+ cr, uid, context=context)
620+ if context is None or not context.get('location_id', False):
621+ return res
622+ else:
623+ return context['location_id']
624+
625+ _defaults = {
626+ 'location_id': _default_stock_location
627+ }
628+
629+ def onchange_location_id(self, cr, uid, ids, inventory_location_id,
630+ exhaustive, location_id, context=None):
631+ """Warn if the new is not in the location list for this inventory."""
632+ if not exhaustive:
633+ # Don't check if partial inventory
634+ return True
635
636 # search children of location
637- location_ids = self.pool.get('stock.location').search(cr, uid,
638- [('location_id', 'child_of', location_ids)], context=context)
639- if location_id not in location_ids:
640- return {'value': {'location_id': False},
641- 'warning': {'title': _('Warning: Wrong location'),
642- 'message': _("You cannot add this location to inventory line.\n"
643- "You must add this location on the locations list")}
644- }
645+ if location_id not in self.pool['stock.location'].search(
646+ cr, uid, [('location_id', 'child_of', inventory_location_id)],
647+ context=context):
648+ return {
649+ 'value': {'location_id': False},
650+ 'warning': {
651+ 'title': _('Warning: Wrong location'),
652+ 'message': _(
653+ "You cannot record an Inventory Line for this "
654+ "Location.\n"
655+ "You must first add this Location to the list of "
656+ "affected Locations on the Inventory form.")}
657+ }
658 return True
659
660
661@@ -234,16 +265,20 @@
662 _inherit = 'stock.location'
663 _order = 'name'
664
665+ # TODOv7: why not put this in an ORM "_constraint" instead?
666 def _check_inventory(self, cr, uid, ids, context=None):
667- """Raise an error if an exhaustive Inventory is being conducted on this Location"""
668- inventory_obj = self.pool.get('stock.inventory')
669- location_inventory_open_ids = inventory_obj._get_locations_open_inventories(cr, uid, context=context)
670+ """Error if an exhaustive Inventory is being conducted here"""
671+ inv_obj = self.pool['stock.inventory']
672+ location_inventory_open_ids = inv_obj._get_locations_open_inventories(
673+ cr, uid, context=context)
674 if not isinstance(ids, Iterable):
675 ids = [ids]
676- for id in ids:
677- if id in location_inventory_open_ids:
678- raise orm.except_orm(_('Error: Location on inventory'),
679- _('A Physical Inventory is being conducted at this location'))
680+ for inv_id in ids:
681+ if inv_id in location_inventory_open_ids:
682+ raise ExhaustiveInventoryException(
683+ _('Error: Location locked down'),
684+ _('A Physical Inventory is being conducted at this '
685+ 'location'))
686 return True
687
688 def write(self, cr, uid, ids, vals, context=None):
689@@ -252,16 +287,19 @@
690 if not isinstance(ids, Iterable):
691 ids = [ids]
692 ids_to_check = ids
693- # If we are changing the parent, there must be no inventory must conducted there either
694- if vals.get('location_id'):
695+ # If changing the parent, no inventory must conducted there either
696+ if vals.get('location_id'):
697 ids_to_check.append(vals['location_id'])
698 self._check_inventory(cr, uid, ids_to_check, context=context)
699- return super(StockLocation, self).write(cr, uid, ids, vals, context=context)
700+ return super(StockLocation, self).write(cr, uid, ids, vals,
701+ context=context)
702
703 def create(self, cr, uid, vals, context=None):
704 """Refuse create if an inventory is being conducted at the parent"""
705- self._check_inventory(cr, uid, vals.get('location_id'), context=context)
706- return super(StockLocation, self).create(cr, uid, vals, context=context)
707+ self._check_inventory(cr, uid, vals.get('location_id'),
708+ context=context)
709+ return super(StockLocation, self).create(cr, uid, vals,
710+ context=context)
711
712 def unlink(self, cr, uid, ids, context=None):
713 """Refuse unlink if an inventory is being conducted"""
714@@ -274,29 +312,40 @@
715
716 _inherit = 'stock.move'
717
718+ # TODOv7: adapt this to match trunk-wms
719 def _check_open_inventory_location(self, cr, uid, ids, context=None):
720- """ Check if location is not in opened inventory
721- Don't check on partial inventory (checkbox "Exhaustive" not checked).
722+ """
723+ Check that the locations are not locked by an open inventory
724+
725+ Standard inventories are not checked.
726+
727+ @raise ExhaustiveInventoryException: an error is raised if locations
728+ are locked, instead of returning False, in order to pass an
729+ extensive error message back to users.
730 """
731 message = ""
732- inventory_obj = self.pool.get('stock.inventory')
733- location_inventory_open_ids = inventory_obj._get_locations_open_inventories(cr, uid, context=context)
734- if not location_inventory_open_ids:
735- return True # Nothing to verify
736+ inv_obj = self.pool['stock.inventory']
737+ locked_location_ids = inv_obj._get_locations_open_inventories(
738+ cr, uid, context=context)
739+ if not locked_location_ids:
740+ # Nothing to verify
741+ return True
742 for move in self.browse(cr, uid, ids, context=context):
743 if (move.location_id.usage != 'inventory'
744- and move.location_dest_id.id in location_inventory_open_ids):
745+ and move.location_dest_id.id in locked_location_ids):
746 message += " - %s\n" % (move.location_dest_id.name)
747-
748 if (move.location_dest_id.usage != 'inventory'
749- and move.location_id.id in location_inventory_open_ids):
750+ and move.location_id.id in locked_location_ids):
751 message += " - %s\n" % (move.location_id.name)
752 if message:
753- raise orm.except_orm(_('Error: Location on inventory'),
754- _('One or more locations are inventoried:\n%s') % message)
755+ raise ExhaustiveInventoryException(
756+ _('Error: Location locked down'),
757+ _('A Physical Inventory is being conducted at the following '
758+ 'location(s):\n%s') % message)
759 return True
760
761 _constraints = [
762- (_check_open_inventory_location,
763- "A Physical Inventory is being conducted at this location", ['location_id', 'location_dest_id']),
764- ]
765+ (_check_open_inventory_location,
766+ "A Physical Inventory is being conducted at this location",
767+ ['location_id', 'location_dest_id']),
768+ ]
769
770=== modified file 'stock_inventory_location/stock_inventory_location_demo.xml'
771--- stock_inventory_location/stock_inventory_location_demo.xml 2014-03-12 14:55:39 +0000
772+++ stock_inventory_location/stock_inventory_location_demo.xml 2014-06-11 15:01:48 +0000
773@@ -1,25 +1,26 @@
774 <?xml version="1.0" encoding="utf-8"?>
775 <openerp>
776 <data noupdate="0">
777+ <!-- Record a non exhaustive inventory -->
778+ <record id="inventory_standard" model="stock.inventory">
779+ <field name="name">Standard inventory</field>
780+ <field name="state">draft</field>
781+ </record>
782
783- <!-- Record inventories we can use in the tests. -->
784- <!-- We need them in the demo data because test data is rolled back
785- whenever an exception is raised. -->
786-
787- <!-- Record an non exhaustive inventory -->
788- <record id="stock_inventory_location_0" model="stock.inventory">
789- <field name="name">Location test inventory</field>
790- <field name="state">draft</field>
791- <field name="date">2020-03-01 00:00:00</field>
792- </record>
793-
794 <!-- Record an exhaustive inventory -->
795- <record id="stock_inventory_location_1" model="stock.inventory">
796- <field name="name">Location test exhaustive inventory</field>
797- <field name="state">draft</field>
798- <field name="date">2020-03-15 00:00:00</field>
799- <field name="exhaustive">True</field>
800- <field name="location_ids" model="stock.location" search="[('name', '=', 'Shelf 2')]" />
801+ <record id="inventory_exhaustive" model="stock.inventory">
802+ <field name="name">Exhaustive inventory</field>
803+ <field name="state">draft</field>
804+ <field name="exhaustive">True</field>
805+ <field name="location_id" model="stock.location" search="[('name', '=', 'Shelf 2')]" />
806+ </record>
807+
808+ <!-- Record an inventory dated in the future -->
809+ <!-- TODOv8: remove this entry, only useful for a test already in trunk-wms -->
810+ <record id="inventory_future" model="stock.inventory">
811+ <field name="name">Inventory in the future</field>
812+ <field name="state">draft</field>
813+ <field name="date">2020-03-01 00:00:00</field>
814 </record>
815 </data>
816 </openerp>
817
818=== modified file 'stock_inventory_location/stock_inventory_location_view.xml'
819--- stock_inventory_location/stock_inventory_location_view.xml 2014-03-28 16:13:01 +0000
820+++ stock_inventory_location/stock_inventory_location_view.xml 2014-06-11 15:01:48 +0000
821@@ -1,59 +1,59 @@
822 <?xml version="1.0" encoding="utf-8"?>
823 <openerp>
824 <data>
825-
826+
827 <record model="ir.ui.view" id="stock_inventory_location_form_view">
828 <field name="name">stock.inventory.location.form</field>
829 <field name="model">stock.inventory</field>
830 <field name="inherit_id" ref="stock.view_inventory_form"/>
831 <field name="priority" eval="10"/>
832 <field name="arch" type="xml">
833- <!-- Add type of inventory: partial or complete. -->
834- <xpath expr="/form//field[@name='date']" position="after">
835+ <!-- Show the state 'done' in the statusbar -->
836+ <xpath expr="/form//field[@name='state']" position="attributes">
837+ <attribute name="statusbar_visible">draft,open,confirm</attribute>
838+ </xpath>
839+
840+ <!-- TODO v8 place "exhaustive" next to "location_id" -->
841+ <!-- Add type of inventory: standard or exhaustive. -->
842+ <xpath expr="/form//field[@name='name']" position="after">
843 <field name="exhaustive"/>
844- </xpath>
845-
846- <!-- Add the list of Locations on exhaustive inventories -->
847- <xpath expr="/form//field[@name='inventory_line_id']" position="before">
848- <!-- Add the locations list for inventory -->
849- <field colspan="1" nolabel="1" name="location_ids" domain="[('usage','in',('view', 'internal'))]" attrs="{'invisible':[('exhaustive','!=',True)]}">
850- <tree string="Locations" editable="bottom">
851- <field name="name"/>
852- </tree>
853- </field>
854- </xpath>
855-
856- <!-- Add Fill Inventory button when state is open -->
857- <xpath expr="//button[@string='Fill Inventory']" position="attributes">
858- <attribute name="states">draft,open,confirm</attribute>
859- </xpath>
860-
861- <!-- Reduce the colspan of the lines to make room for the Locations-->
862- <xpath expr="/form//field[@name='inventory_line_id']" position="attributes">
863- <attribute name="colspan">3</attribute>
864- </xpath>
865-
866- <!-- Control locations added by user on inventory line -->
867- <xpath expr="/form//field[@name='inventory_line_id']/tree//field[@name='location_id']" position="attributes">
868- <attribute name="on_change">onchange_location_id(parent.location_ids, parent.exhaustive, location_id)</attribute>
869- </xpath>
870- <xpath expr="/form//field[@name='inventory_line_id']/form//field[@name='location_id']" position="attributes">
871- <attribute name="on_change">onchange_location_id(parent.location_ids, parent.exhaustive, location_id)</attribute>
872- </xpath>
873-
874- <!-- #XXX change the attributes instead and add the button -->
875- <!-- #XXX enlarge the group's colspan? -->
876+ <field name="location_id" groups="stock.group_locations"
877+ domain="[('usage','in',('view', 'internal'))]"
878+ attrs="{'invisible':[('exhaustive','!=',True)], 'required':[('exhaustive','=',True)]}"/>
879+ </xpath>
880+
881+ <!-- Enable Fill Inventory button when state is open -->
882+ <xpath expr="//button[@string='Fill Inventory']" position="attributes">
883+ <attribute name="states">open</attribute>
884+ <attribute name="context">{'default_exhaustive': exhaustive}</attribute>
885+ </xpath>
886+
887+ <!-- Control locations added by user on inventory line -->
888+ <xpath expr="/form//field[@name='inventory_line_id']"
889+ position="attributes">
890+ <attribute name="context">{'location_id': location_id}</attribute>
891+ </xpath>
892+ <xpath expr="/form//field[@name='inventory_line_id']/tree//field[@name='location_id']"
893+ position="attributes">
894+ <attribute name="on_change">onchange_location_id(parent.location_id, parent.exhaustive, location_id)</attribute>
895+ </xpath>
896+ <xpath expr="/form//field[@name='inventory_line_id']/form//field[@name='location_id']"
897+ position="attributes">
898+ <attribute name="on_change">onchange_location_id(parent.location_id, parent.exhaustive, location_id)</attribute>
899+ </xpath>
900+
901 <!-- Add button to open an inventory. Call wizard on confirm inventory -->
902- <xpath expr="/form//button[@name='action_cancel_inventory']" position="replace">
903- <button name="action_cancel_inventory" states="draft,open,confirm,done" string="Cancel Inventory" type="object" icon="gtk-cancel"/>
904- <button name="action_open" states="draft" string="Open Inventory" type="object" icon="gtk-go-forward"/>
905- </xpath>
906-
907- <!-- replace action_confirm button -->
908- <!-- #XXX change the attributes instead -->
909- <xpath expr="/form//button[@name='action_confirm']" position="replace">
910- <button name="confirm_uninventoried_location_wizard"
911- string="Confirm Inventory" type="object" states="open" icon="gtk-apply"/>
912+ <xpath expr="/form//button[@name='action_cancel_inventory']" position="before">
913+ <button name="action_open" states="draft" string="Open Inventory" type="object" class="oe_highlight" groups="stock.group_stock_user"/>
914+ </xpath>
915+ <!-- Show the "cancel" button in state "open" -->
916+ <xpath expr="/form//button[@name='action_cancel_inventory']" position="attributes">
917+ <attribute name="states">draft,open,confirm,done</attribute>
918+ </xpath>
919+ <!-- hijack the "confirm" button -->
920+ <xpath expr="/form//button[@name='action_confirm']" position="attributes">
921+ <attribute name="states">open</attribute>
922+ <attribute name="name">confirm_missing_locations</attribute>
923 </xpath>
924 </field>
925 </record>
926@@ -62,16 +62,16 @@
927 <record model="ir.ui.view" id="view_inventory_complete_filter">
928 <field name="name">complete.inventory.filter</field>
929 <field name="model">stock.inventory</field>
930- <field name="type">search</field>
931 <field name="inherit_id" ref="stock.view_inventory_filter"/>
932 <field name="arch" type="xml">
933 <xpath expr="//field[@name='name']" position="before">
934- <filter icon="terp-check" name="exhaustive" string="Exhaustive" domain="[('exhaustive', '=', True)]" help="Only select inventories that have no parents." />
935+ <filter icon="terp-check" name="exhaustive" string="Exhaustive" domain="[('exhaustive', '=', True)]"
936+ help="Only select exhaustive Inventories." />
937 <separator orientation="vertical"/>
938 </xpath>
939 </field>
940 </record>
941-
942+
943 <!-- Show exhaustive inventories by default -->
944 <record id="stock.action_inventory_form" model="ir.actions.act_window">
945 <field name="context">{'full':'1', 'search_default_exhaustive':1}</field>
946
947=== renamed file 'stock_inventory_location/test/location_exhaustive_inventory_test.yml' => 'stock_inventory_location/test/inventory_exhaustive_test.yml'
948--- stock_inventory_location/test/location_exhaustive_inventory_test.yml 2014-03-31 13:19:48 +0000
949+++ stock_inventory_location/test/inventory_exhaustive_test.yml 2014-06-11 15:01:48 +0000
950@@ -3,93 +3,109 @@
951 I will call open_action method and check if state of inventories are 'open'.
952 -
953 !python {model: stock.inventory}: |
954- from osv import orm, osv
955- self.action_open(cr, uid, [ref('stock_inventory_location_1')])
956- inventory_state = self.read(cr, uid, [ref('stock_inventory_location_1')], ['state'])[0]['state']
957- assert inventory_state == 'open', "Parent inventory have '%s' state. It should be 'open'" % inventory_state
958-
959--
960- In order, i add products to exhaustive inventory.
961+ self.action_open(cr, uid, [ref('inventory_exhaustive')])
962+ inventory_state = self.read(cr, uid, [ref('inventory_exhaustive')], ['state'])[0]['state']
963+ assert inventory_state == 'open', "Inventory in state '%s'. It should be 'open'" % inventory_state
964+-
965+ I create a wizard record for stock_confirm_uninventoried_location to verify that it contains the uninventoried locations
966+-
967+ !python {model: stock.inventory.uninventoried.locations}: |
968+ ctx = dict(context, active_ids=[ref('inventory_exhaustive')])
969+ wizard_id = self.create(cr, uid, {}, context=ctx)
970+ wizard = self.browse(cr, uid, wizard_id, context=ctx)
971+ assert len(wizard.location_ids) > 0 , "The wizard does not contain any lines."
972+-
973+ I add products to exhaustive inventory.
974 Adding 17” LCD Monitor.
975--
976+-
977 !record {model: stock.inventory.line, id: lines_inventory_location_pc1}:
978 product_id: product.product_product_7
979 product_uom: product.product_uom_unit
980 company_id: base.main_company
981- inventory_id: stock_inventory_location_1
982+ inventory_id: inventory_exhaustive
983 product_qty: 18.0
984 location_id: stock.stock_location_14
985
986 -
987 Adding USB Keyboard, QWERTY.
988--
989+-
990 !record {model: stock.inventory.line, id: lines_inventory_location_pc3}:
991 product_id: product.product_product_8
992 product_uom: product.product_uom_unit
993 company_id: base.main_company
994- inventory_id: stock_inventory_location_1
995+ inventory_id: inventory_exhaustive
996 product_qty: 5.0
997- location_id: stock.stock_location_14
998+ location_id: stock.stock_location_14
999
1000 -
1001 I will call the function _get_locations_open_inventories and check the result.
1002- The function will return only the location_ids of the exhaustive inventory.
1003+ The function will return only the location_id of the exhaustive inventory.
1004 -
1005 !python {model: stock.inventory}: |
1006- from osv import orm, osv
1007- locations = self._get_locations_open_inventories(cr, uid)
1008+ locations = self._get_locations_open_inventories(cr, uid)
1009 assert len(locations) == 1, "Function return wrong results: %s" % locations
1010- 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])
1011-
1012--
1013+ 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])
1014+-
1015 I will call the function onchange_location_id.
1016- The function must to return true in the first case, and return a warning dictionnary in the second test.
1017+ The function must return True in the first case, and return a warning dictionnary in the second test.
1018 -
1019 !python {model: stock.inventory.line}: |
1020- from osv import orm, osv
1021 res = self.onchange_location_id(cr, uid, [], [(6,0,[ref('stock.stock_location_14')])], True, ref('stock.stock_location_14'))
1022- assert res == True, "Exhaustive: The function 'onchange_location_id' should return True and return '%s'" % res
1023+ assert res == True, "Exhaustive: The function 'onchange_location_id' should return True and return '%s'" % res
1024 res = self.onchange_location_id(cr, uid, [], [(6,0,[ref('stock.stock_location_14')])], True, ref('stock.stock_location_components'))
1025- assert res.get('warning', False) != False , "Function 'onchange_location_id': Warning not raise. ('%s)" % res
1026+ assert res.get('warning', False) != False , "Function 'onchange_location_id': Warning not raise. ('%s)" % res
1027
1028 -
1029- I will call _fill_location_lines to simulate confirmation for stock_confirm_uninventoried_location,
1030- and create stock_moves
1031--
1032- !python {model: stock.inventory}: |
1033- from osv import orm, osv
1034- ctx={'location': [ref('stock.stock_location_14')]}
1035- lines = self._fill_location_lines(cr, uid, ref('stock_inventory_location_1'), [ref('stock.stock_location_14')], True, context=ctx)
1036- for line in lines:
1037- self.pool.get('stock.inventory.line').create(cr, uid, line, context=context)
1038-
1039--
1040- I will confirm exhaustive inventory
1041--
1042- !python {model: stock.inventory}: |
1043- from osv import orm, osv
1044- self.action_confirm(cr, uid, [ref('stock_inventory_location_1')])
1045- inventory_state = self.read(cr, uid, [ref('stock_inventory_location_1')], ['state'])[0]['state']
1046+ I create a wizard record for stock_confirm_uninventoried_location and validate it
1047+-
1048+ !python {model: stock.inventory.uninventoried.locations}: |
1049+ ctx = dict(context, active_ids=[ref('inventory_exhaustive')])
1050+ wizard_id = self.create(cr, uid, {}, context=ctx)
1051+ wizard = self.browse(cr, uid, wizard_id, context=ctx)
1052+ assert len(wizard.location_ids) == 0 , "The wizard should not contain any lines but contains %s." % wizard.location_ids
1053+ self.confirm_uninventoried_locations(cr, uid, wizard_id, context=ctx)
1054+-
1055+ Stock moves are not allowed in the locations during the inventory.
1056+-
1057+ !python {model: stock.move}: |
1058+ # TODOv8: remove this test, this is already part of trunk-wms
1059+ from stock_inventory_location import ExhaustiveInventoryException
1060+ try:
1061+ self.create(
1062+ cr,uid, {
1063+ 'name': 'Bad move',
1064+ 'location_id': ref('stock.stock_location_14'),
1065+ 'location_dest_id': ref('stock.stock_location_3'),
1066+ 'product_id': ref('product.product_product_8'),
1067+ 'product_uom': ref('product.product_uom_unit'),
1068+ 'date_expected': '2020-01-01 00:00:00'
1069+ })
1070+ except ExhaustiveInventoryException as e:
1071+ log("Good! The Stock Move was refused: %s" % e)
1072+-
1073+ I will confirm the exhaustive inventory
1074+-
1075+ !python {model: stock.inventory}: |
1076+ self.action_confirm(cr, uid, [ref('inventory_exhaustive')])
1077+ inventory_state = self.read(cr, uid, [ref('inventory_exhaustive')], ['state'])[0]['state']
1078 assert inventory_state == 'confirm', "Exhaustive inventory have '%s' state. It should be 'confirm'" % inventory_state
1079-
1080+
1081 -
1082- I will validate exhaustive inventory
1083+ I will validate the exhaustive inventory
1084 -
1085 !python {model: stock.inventory}: |
1086- from osv import orm, osv
1087- self.action_done(cr, uid, [ref('stock_inventory_location_1')])
1088- inventory_state = self.read(cr, uid, [ref('stock_inventory_location_1')], ['state'])[0]['state']
1089+ self.action_done(cr, uid, [ref('inventory_exhaustive')])
1090+ inventory_state = self.read(cr, uid, [ref('inventory_exhaustive')], ['state'])[0]['state']
1091 assert inventory_state == 'done', "Exhaustive inventory have '%s' state. It should be 'done'" % inventory_state
1092-
1093+
1094 -
1095 I will verify the quantity for each products.
1096--
1097+-
1098 !python {model: product.product}: |
1099- from osv import orm, osv
1100- ctx={'location': [ref('stock.stock_location_14')], 'to_date': '2020-12-31 23:59:59'}
1101+ ctx = dict(context, location=[ref('stock.stock_location_14')])
1102 prod_qty_avail = self.read(cr, uid, [ref('product.product_product_7')], ['qty_available'], context=ctx)[0]['qty_available']
1103 assert prod_qty_avail == 18.0, "The stock of PC1 was not set to 18.0: %s" % prod_qty_avail
1104-
1105+
1106 prod_qty_avail = self.read(cr, uid, [ref('product.product_product_8')], ['qty_available'], context=ctx)[0]['qty_available']
1107 assert prod_qty_avail == 5.0, "The stock of PC3 was not set to 5.0: %s" % prod_qty_avail
1108
1109
1110=== added file 'stock_inventory_location/test/inventory_future_test.yml'
1111--- stock_inventory_location/test/inventory_future_test.yml 1970-01-01 00:00:00 +0000
1112+++ stock_inventory_location/test/inventory_future_test.yml 2014-06-11 15:01:48 +0000
1113@@ -0,0 +1,13 @@
1114+-
1115+ Check that an inventory with a date in the future cannot be opened.
1116+-
1117+ !python {model: stock.inventory}: |
1118+ # TODO v8: maybe this is already part of the new WMS
1119+ from osv.osv import except_osv
1120+ self.action_open(cr, uid, [ref('inventory_future')])
1121+ try:
1122+ self.action_done(cr, uid, [ref('inventory_future')])
1123+ except except_osv as e:
1124+ log("Good! The Inventory could not be opened: %s" % e)
1125+ inventory_state = self.read(cr, uid, [ref('inventory_future')], ['state'])[0]['state']
1126+ assert inventory_state != 'done', "Future inventory is done."
1127
1128=== renamed file 'stock_inventory_location/test/location_inventory_test.yml' => 'stock_inventory_location/test/inventory_standard_test.yml'
1129--- stock_inventory_location/test/location_inventory_test.yml 2014-03-31 13:19:48 +0000
1130+++ stock_inventory_location/test/inventory_standard_test.yml 2014-06-11 15:01:48 +0000
1131@@ -3,11 +3,10 @@
1132 I will call open_action method and check if state of inventories are 'open'.
1133 -
1134 !python {model: stock.inventory}: |
1135- from osv import orm, osv
1136- self.action_open(cr, uid, [ref('stock_inventory_location_0')])
1137- inventory_state = self.read(cr, uid, [ref('stock_inventory_location_0')], ['state'])[0]['state']
1138- assert inventory_state == 'open', "Partial inventory have '%s' state. It should be 'open'" % inventory_state
1139-
1140+ self.action_open(cr, uid, [ref('inventory_standard')])
1141+ inventory_state = self.read(cr, uid, [ref('inventory_standard')], ['state'])[0]['state']
1142+ assert inventory_state == 'open', "Partial inventory have '%s' state. It should be 'open'" % inventory_state
1143+
1144 -
1145 In order, I add products to inventory.
1146 Adding Azerty Keyboard.
1147@@ -16,7 +15,7 @@
1148 product_id: product.product_product_9
1149 product_uom: product.product_uom_unit
1150 company_id: base.main_company
1151- inventory_id: stock_inventory_location_0
1152+ inventory_id: inventory_standard
1153 product_qty: 18.0
1154 location_id: stock.stock_location_components
1155
1156@@ -27,7 +26,7 @@
1157 product_id: product.product_product_10
1158 product_uom: product.product_uom_unit
1159 company_id: base.main_company
1160- inventory_id: stock_inventory_location_0
1161+ inventory_id: inventory_standard
1162 product_qty: 12.0
1163 location_id: stock.stock_location_components
1164
1165@@ -38,26 +37,23 @@
1166 product_id: product.product_template_31
1167 product_uom: product.product_uom_unit
1168 company_id: base.main_company
1169- inventory_id: stock_inventory_location_0
1170+ inventory_id: inventory_standard
1171 product_qty: 32.0
1172 location_id: stock.stock_location_components
1173
1174 -
1175 I will call the function _get_locations_open_inventories and check the result.
1176- The function will return no locations.
1177+ The function will return no locations because it's not an exhaustive inventory.
1178 -
1179 !python {model: stock.inventory}: |
1180- from osv import orm, osv
1181- locations = self._get_locations_open_inventories(cr, uid)
1182+ locations = self._get_locations_open_inventories(cr, uid)
1183 assert len(locations) == 0, "Function return wrong results: %s" % locations
1184-
1185+
1186 -
1187 I will call the function onchange_location_id.
1188- The function must to return true in all test case.
1189+ The function must to return true in all test case.
1190 -
1191 !python {model: stock.inventory.line}: |
1192- from osv import orm, osv
1193- pass
1194 res = self.onchange_location_id(cr, uid, [], [(6,0,[ref('stock.stock_location_components')])], False, ref('stock.stock_location_components'))
1195 assert res == True, "The function 'onchange_location_id' should return True and return '%s'" % res
1196 res = self.onchange_location_id(cr, uid, [], [(6,0,[ref('stock.stock_location_components')])], False, ref('stock.stock_location_14'))
1197@@ -68,42 +64,37 @@
1198 The function must return True in all test cases.
1199 -
1200 !python {model: stock.location}: |
1201- from osv import orm, osv
1202 res = self._check_inventory(cr, uid, ref('stock.stock_location_components'))
1203 assert res == True, "The function '_check_inventory' should return True and return '%s'" % res
1204 res = self._check_inventory(cr, uid, ref('stock.stock_location_14'))
1205 assert res == True, "The function '_check_inventory' should return True and return '%s'" % res
1206-
1207--
1208- I will confirm inventory.
1209--
1210+
1211+-
1212+ I will confirm inventory.
1213+-
1214 !python {model: stock.inventory}: |
1215- from osv import orm, osv
1216- self.action_confirm(cr, uid, [ref('stock_inventory_location_0')])
1217- inventory_state = self.read(cr, uid, [ref('stock_inventory_location_0')], ['state'])[0]['state']
1218+ self.action_confirm(cr, uid, [ref('inventory_standard')])
1219+ inventory_state = self.read(cr, uid, [ref('inventory_standard')], ['state'])[0]['state']
1220 assert inventory_state == 'confirm', "Partial inventory have '%s' state. It should be 'confirm'" % inventory_state
1221
1222 -
1223 I will validate inventory
1224 -
1225 !python {model: stock.inventory}: |
1226- from osv import orm, osv
1227- self.action_done(cr, uid, [ref('stock_inventory_location_0')])
1228- inventory_state = self.read(cr, uid, [ref('stock_inventory_location_0')], ['state'])[0]['state']
1229- assert inventory_state == 'done', "Partial inventory have '%s' state. It should be 'done'" % inventory_state
1230-
1231+ self.action_done(cr, uid, [ref('inventory_standard')])
1232+ inventory_state = self.read(cr, uid, [ref('inventory_standard')], ['state'])[0]['state']
1233+ assert inventory_state == 'done', "Partial inventory have '%s' state. It should be 'done'" % inventory_state
1234+
1235 -
1236 I will verify the quantity for each products.
1237--
1238+-
1239 !python {model: product.product}: |
1240- from osv import orm, osv
1241- ctx={'location': [ref('stock.stock_location_components')], 'to_date': '2020-12-31 23:59:59'}
1242+ ctx={'location': [ref('stock.stock_location_components')]}
1243 prod_qty_avail = self.read(cr, uid, [ref('product.product_product_9')], ['qty_available'], context=ctx)[0]['qty_available']
1244 assert prod_qty_avail == 18.0, "The stock of CPU1 was not set to 18.0: %s" % prod_qty_avail
1245-
1246+
1247 prod_qty_avail = self.read(cr, uid, [ref('product.product_product_10')], ['qty_available'], context=ctx)[0]['qty_available']
1248 assert prod_qty_avail == 12.0, "The stock of CPU3 was not set to 12.0: %s" % prod_qty_avail
1249-
1250+
1251 prod_qty_avail = self.read(cr, uid, [ref('product.product_template_31')], ['qty_available'], context=ctx)[0]['qty_available']
1252 assert prod_qty_avail == 32.0, "The stock of FAN was not set to 32.0: %s" % prod_qty_avail
1253-
1254\ No newline at end of file
1255
1256=== modified file 'stock_inventory_location/wizard/stock_confirm_uninventoried_location.py'
1257--- stock_inventory_location/wizard/stock_confirm_uninventoried_location.py 2014-03-28 16:13:01 +0000
1258+++ stock_inventory_location/wizard/stock_confirm_uninventoried_location.py 2014-06-11 15:01:48 +0000
1259@@ -19,8 +19,6 @@
1260 ##############################################################################
1261
1262 from openerp.osv import fields, orm
1263-from openerp.tools.translate import _
1264-from stock_inventory_location.stock_inventory_location import EmptyLocationException
1265
1266
1267 class stock_inventory_uninventoried_location(orm.TransientModel):
1268@@ -28,71 +26,37 @@
1269 _description = 'Confirm the uninventoried Locations.'
1270
1271 _columns = {
1272- 'location_ids': fields.many2many('stock.location',
1273- 'stock_inventory_uninventoried_location_rel',
1274- 'location_id',
1275- 'wizard_id',
1276- 'Uninventoried location', readonly=True),
1277- }
1278-
1279- def get_locations(self, cr, uid, inventory_id, context=None):
1280- """ Get all locations from inventory. """
1281- location_ids = self.pool.get('stock.inventory').read(cr, uid, [inventory_id], ['location_ids'], context=context)[0]
1282- return self.pool.get('stock.location').search(cr, uid, [
1283- ('location_id', 'child_of', location_ids['location_ids']),
1284- ('usage', '=', 'internal')], context=context)
1285-
1286- def get_locations_inventoried(self, cr, uid, inventory_id, location_ids, context=None):
1287- """ Get all locations on inventory lines. """
1288- inventory_line_obj = self.pool.get('stock.inventory.line')
1289- inventory_line_ids = inventory_line_obj.search(cr, uid, [('location_id', 'in', location_ids),
1290- ('inventory_id', '=', inventory_id)], context=context)
1291- inventory_line_locations_ids = inventory_line_obj.read(cr, uid, inventory_line_ids, ['location_id'], context=context)
1292- return list(set([_id['location_id'][0] for _id in inventory_line_locations_ids]))
1293+ 'location_ids': fields.many2many(
1294+ 'stock.location',
1295+ 'stock_inventory_uninventoried_location_rel',
1296+ 'location_id', 'wizard_id',
1297+ 'Uninventoried location', readonly=True),
1298+ }
1299
1300 def default_locations(self, cr, uid, context=None):
1301- """ Initialize view with the list of uninventoried locations.
1302- Search for children of the location if exists.
1303- """
1304- if context is None:
1305- context = {}
1306- location_ids = self.get_locations(cr, uid, context['active_id'])
1307- inventory_line_locations_ids = self.get_locations_inventoried(cr, uid, context['active_id'], location_ids)
1308- return [_id for _id in location_ids if _id not in inventory_line_locations_ids]
1309+ """Initialize view with the list of uninventoried locations."""
1310+ return self.pool['stock.inventory'].get_missing_locations(
1311+ cr, uid, context['active_ids'], context=context)
1312
1313 _defaults = {
1314 'location_ids': default_locations,
1315- }
1316+ }
1317
1318 def confirm_uninventoried_locations(self, cr, uid, ids, context=None):
1319- """ Call action confirm method from stock.inventory """
1320- if context is None or 'active_ids' not in context:
1321- return False
1322+ """Add the missing inventory lines with qty=0 and confirm inventory"""
1323 inventory_ids = context['active_ids']
1324- # call the wizard to add lines for uninventoried locations with zero quantity
1325- inventory_obj = self.pool.get('stock.inventory')
1326- if not isinstance(inventory_ids, list):
1327- inventory_ids = [inventory_ids]
1328-
1329- for inventory in inventory_obj.browse(cr, uid, inventory_ids, context=context):
1330+ inventory_obj = self.pool['stock.inventory']
1331+ wizard_obj = self.pool['stock.fill.inventory']
1332+ for inventory in inventory_obj.browse(cr, uid, inventory_ids,
1333+ context=context):
1334 if inventory.exhaustive:
1335- location_ids = self.get_locations(cr, uid, inventory.id, context=context)
1336- # get stock inventory lines
1337- lines = []
1338- try:
1339- # create inventory lines with zero qty
1340- lines = inventory_obj._fill_location_lines(cr, uid,
1341- inventory.id,
1342- location_ids,
1343- True,
1344- context=context)
1345- except EmptyLocationException:
1346- pass # Don't care about empty location exception
1347-
1348- for line in lines:
1349- self.pool.get('stock.inventory.line').create(cr, uid, line, context=context)
1350+ # silently run the import wizard with qty=0
1351+ wizard_id = wizard_obj.create(
1352+ cr, uid, {'location_id': inventory.location_id.id,
1353+ 'recursive': True,
1354+ 'set_stock_zero': True}, context=context)
1355+ wizard_obj.fill_inventory(cr, uid, [wizard_id],
1356+ context=context)
1357
1358 inventory_obj.action_confirm(cr, uid, inventory_ids, context=context)
1359 return {'type': 'ir.actions.act_window_close'}
1360-
1361-
1362
1363=== modified file 'stock_inventory_location/wizard/stock_confirm_uninventoried_location.xml'
1364--- stock_inventory_location/wizard/stock_confirm_uninventoried_location.xml 2014-03-12 14:55:39 +0000
1365+++ stock_inventory_location/wizard/stock_confirm_uninventoried_location.xml 2014-06-11 15:01:48 +0000
1366@@ -1,33 +1,33 @@
1367 <?xml version="1.0" encoding="utf-8"?>
1368 <openerp>
1369 <data>
1370- <!-- The view definition is similar with stock_inventory_hierarchical_location/wizard/stock_inventory_missing_locations_view.xml,
1371- but the code is different.
1372+ <!-- The view definition is similar with stock_inventory_hierarchical_location/wizard/stock_inventory_missing_locations_view.xml,
1373+ but the code is different.
1374 This wizard compare declared locations with locations on inventory lines to present the uninventoried (empty) locations to user. -->
1375 <record id="view_confirm_uninventoried_location" model="ir.ui.view">
1376 <field name="name">stock.inventory.uninventoried.locations.form</field>
1377 <field name="model">stock.inventory.uninventoried.locations</field>
1378- <field name="type">form</field>
1379 <field name="arch" type="xml">
1380- <form string="Confirm uninventoried locations">
1381+ <form string="Confirm empty locations" version="7.0">
1382 <group colspan="4" col="1">
1383- <label string="The following Stock Locations are part of the current Physical Inventory, but not Inventory Line has been recorded for them."/>
1384+ <separator string="The following Locations are empty"/>
1385+ <label string="The following Stock Locations are part of the current Physical Inventory, but no Inventory Line has been recorded for them."/>
1386+ <label string="It could either mean that the Locations are empty, or that the Inventory is not yet complete."/>
1387+ <label string="If you confirm the Inventory, these Locations will be considered empty and their content will be purged."/>
1388 <field name="location_ids" nolabel="1">
1389 <tree>
1390 <field name="name"/>
1391 </tree>
1392 </field>
1393- <label string="It could either mean that the Locations are empty, or that the Inventory is not yet complete."/>
1394- <label string="If you confirm the Inventory, these Locations will be considered empty and their content will be purged."/>
1395 <separator string=""/>
1396 </group>
1397- <group colspan="4" col="2">
1398- <button special="cancel" string="Cancel" icon="gtk-cancel" default_focus="1" />
1399- <button name="confirm_uninventoried_locations" string="Purge contents and confirm Inventory" type="object" icon="gtk-ok"/>
1400- </group>
1401+ <footer>
1402+ <button name="confirm_uninventoried_locations" string="Purge contents and confirm Inventory" type="object" class="oe_highlight"/>
1403+ or
1404+ <button string="Cancel" class="oe_link" special="cancel" default_focus="1"/>
1405+ </footer>
1406 </form>
1407 </field>
1408 </record>
1409-
1410 </data>
1411 </openerp>
1412\ No newline at end of file
1413
1414=== modified file 'stock_inventory_location/wizard/stock_fill_location_inventory.py'
1415--- stock_inventory_location/wizard/stock_fill_location_inventory.py 2014-03-28 16:13:01 +0000
1416+++ stock_inventory_location/wizard/stock_fill_location_inventory.py 2014-06-11 15:01:48 +0000
1417@@ -19,79 +19,29 @@
1418 ##############################################################################
1419
1420 from openerp.osv import fields, orm
1421-from openerp.tools.translate import _
1422-from collections import OrderedDict
1423-
1424-
1425-class stock_fill_location_inventory(orm.TransientModel):
1426+
1427+
1428+class FillInventoryWizard(orm.TransientModel):
1429+ """Add a field that lets the client make the location read-only"""
1430 _inherit = 'stock.fill.inventory'
1431
1432 _columns = {
1433- 'location_id': fields.many2one('stock.location', 'Location'),
1434- #'exhaustive': fields.boolean('stock.inventory', 'Type'),
1435- 'exhaustive': fields.boolean('stock.inventory'),
1436- }
1437-
1438- def get_inventory_type(self, cr, uid, context=None):
1439- if context.get('active_id', False):
1440- inventory_obj = self.pool.get('stock.inventory')
1441- exhaustive = inventory_obj.read(cr, uid, [context.get('active_id')], ['exhaustive'], context=context)[0]['exhaustive']
1442- return exhaustive
1443- return False
1444-
1445- _defaults = {
1446- 'exhaustive': get_inventory_type,
1447- }
1448-
1449- def view_init(self, cr, uid, fields_list, context=None):
1450- """ inherit from original to add multiple selection of location
1451- and exclude from location list the locations already choosen by another inventory """
1452- if context is None:
1453- context = {}
1454-
1455- inventory_obj = self.pool.get('stock.inventory')
1456- inventory_state = inventory_obj.read(cr, uid, [context.get('active_id')], ['state'], context=context)[0]
1457- if inventory_state['state'] != 'open':
1458- raise orm.except_orm(_('Error'),
1459- _('the inventory must be in "Open" state.'))
1460-
1461- nb_inventory = inventory_obj.search(cr, uid, [('id', '=', context.get('active_id'))], count=True, context=context)
1462- if nb_inventory == 0:
1463- raise orm.except_orm(_('Warning'),
1464- _('No locations found for the inventory.'))
1465-
1466- return super(stock_fill_location_inventory, self).view_init(cr, uid, fields_list, context=context)
1467-
1468- def fill_inventory(self, cr, uid, ids, context=None):
1469- """ Fill the inventory only with open locations on the inventory.
1470- """
1471- if context is None:
1472- context = {}
1473-
1474- fill_inventory = self.browse(cr, uid, ids[0], context=context)
1475- if not fill_inventory.exhaustive:
1476- return super(stock_fill_location_inventory, self).fill_inventory(cr, uid, ids, context=context) # call standard wizard
1477-
1478- location_ids = self.pool.get('stock.inventory').read(cr, uid, [context.get('active_id')], ['location_ids'])[0]
1479-
1480- if not location_ids['location_ids']:
1481- raise orm.except_orm(_('Error: Empty location !'), _('No location to import.\nYou must add a location on the locations list.'))
1482-
1483- if fill_inventory.recursive:
1484- location_ids = self.pool.get('stock.location').search(cr, uid, [('location_id', 'child_of', location_ids['location_ids']),
1485- ('usage', '=', 'internal')], context=context)
1486- else:
1487- location_ids = location_ids['location_ids']
1488-
1489- location_ids = list(OrderedDict.fromkeys(location_ids))
1490-
1491- lines = self.pool.get('stock.inventory')._fill_location_lines(cr, uid,
1492- context['active_ids'][0],
1493- location_ids,
1494- fill_inventory.set_stock_zero,
1495- context=context)
1496-
1497- inventory_lines_obj = self.pool.get('stock.inventory.line')
1498- for line in lines:
1499- inventory_lines_obj.create(cr, uid, line, context=context)
1500- return {'type': 'ir.actions.act_window_close'}
1501+ 'exhaustive': fields.boolean('Exhaustive', readonly=True)
1502+ }
1503+
1504+ def default_get(self, cr, uid, fields, context=None):
1505+ """Get 'location_id' and 'exhaustive' from the inventory"""
1506+ if context is None:
1507+ context = {}
1508+ inv_id = context.get('active_id')
1509+
1510+ res = super(FillInventoryWizard, self).default_get(
1511+ cr, uid, fields, context=context)
1512+ if (context.get('active_model') == 'stock.inventory'
1513+ and inv_id
1514+ and 'location_id' in fields):
1515+ inventory = self.pool['stock.inventory'].browse(
1516+ cr, uid, context['active_id'], context=context)
1517+ res.update({'location_id': inventory.location_id.id,
1518+ 'exhaustive': inventory.exhaustive})
1519+ return res
1520
1521=== modified file 'stock_inventory_location/wizard/stock_fill_location_inventory_view.xml'
1522--- stock_inventory_location/wizard/stock_fill_location_inventory_view.xml 2014-03-12 14:55:39 +0000
1523+++ stock_inventory_location/wizard/stock_fill_location_inventory_view.xml 2014-06-11 15:01:48 +0000
1524@@ -10,7 +10,7 @@
1525 <field name="exhaustive" invisible="1" />
1526 </xpath>
1527 <xpath expr="//field[@name='location_id']" position="attributes">
1528- <attribute name="attrs">{'invisible':[('exhaustive','=',True)]}</attribute>
1529+ <attribute name="attrs">{'readonly':[('exhaustive','=',True)]}</attribute>
1530 </xpath>
1531 </field>
1532 </record>

Subscribers

People subscribed via source and target branches