Merge lp:~unifield-team/unifield-server/grouped_pi_v7_jfb into lp:~unifield-team/unifield-server/grouped_pi_v7

Proposed by jftempo
Status: Needs review
Proposed branch: lp:~unifield-team/unifield-server/grouped_pi_v7_jfb
Merge into: lp:~unifield-team/unifield-server/grouped_pi_v7
Diff against target: 501 lines (+125/-140)
5 files modified
bin/addons/msf_profile/i18n/fr_MF.po (+9/-7)
bin/addons/stock/physical_inventory.py (+49/-43)
bin/addons/stock/wizard/physical_inventory_generate_counting_sheet.py (+61/-90)
bin/osv/orm.py (+4/-0)
bin/tools/translate.py (+2/-0)
To merge this branch: bzr merge lp:~unifield-team/unifield-server/grouped_pi_v7_jfb
Reviewer Review Type Date Requested Status
UniField Dev Team Pending
Review via email: mp+337846@code.launchpad.net
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=== modified file 'bin/addons/msf_profile/i18n/fr_MF.po'
2--- bin/addons/msf_profile/i18n/fr_MF.po 2018-02-15 17:15:17 +0000
3+++ bin/addons/msf_profile/i18n/fr_MF.po 2018-02-19 10:31:32 +0000
4@@ -73157,8 +73157,8 @@
5 #. module: stock
6 #: code:addons/stock/physical_inventory.py:759
7 #, python-format
8-msgid "Duplicate line (same product, batch number and expiry date)"
9-msgstr "Ligne dupliquée (même produit, numéro de lot et date d'expiration)"
10+msgid "Product %s, Duplicate line (same product, batch number and expiry date)"
11+msgstr "Produit %s, Ligne dupliquée (même produit, numéro de lot et date d'expiration)"
12
13 #. module: msf_budget
14 #: field:wizard.local.expenses,start_period_id:0
15@@ -85702,7 +85702,7 @@
16 #. module: stock
17 #: view:physical.inventory.import.wizard:0
18 msgid "Count all as 0"
19-msgstr "Les calculer tous comme à 0"
20+msgstr "Compter les lignes comme étant à 0"
21
22 #. module: base
23 #: help:res.config.users,menu_tips:0
24@@ -100851,7 +100851,7 @@
25 #: view:physical.inventory:0
26 #: field:physical.inventory.counting,is_ed:0
27 msgid "ED"
28-msgstr "DE"
29+msgstr "EXP"
30
31 #. modules: purchase, purchase_followup
32 #: field:purchase.order,partner_ref:0
33@@ -106718,7 +106718,7 @@
34 #. module: stock
35 #: field:physical.inventory.generate.counting.sheet,only_with_stock_level:0
36 msgid "Only count lines with stock different than 0"
37-msgstr "Commpter uniquement les produits avec un niveau de stock différent de 0."
38+msgstr "Compter uniquement les produits avec un niveau de stock différent de 0."
39
40 #. module: stock
41 #: code:addons/stock/physical_inventory.py:784
42@@ -106762,5 +106762,7 @@
43 msgid "You cannot confirm an inventory which is %s"
44 msgstr "Vous ne pouvez pas confirmé un inventaire qui est %s"
45
46-
47-
48+#. module: stock
49+#: view:physical.inventory:0
50+msgid "[('Hide ignored', ('ignored', '!=', True)), ('Show all', ''), ('Show ignored only', ('ignored', '=', True))]"
51+msgstr "[('Cacher lignes ignorées', ('ignored', '!=', True)), ('Tout afficher', ''), ('Afficher ignorées seulement', ('ignored', '=', True))]"
52
53=== modified file 'bin/addons/stock/physical_inventory.py'
54--- bin/addons/stock/physical_inventory.py 2018-02-15 17:16:59 +0000
55+++ bin/addons/stock/physical_inventory.py 2018-02-19 10:31:32 +0000
56@@ -32,6 +32,13 @@
57 _name = 'physical.inventory'
58 _description = 'Physical Inventory'
59
60+ def write(self, cr, uid, ids, vals, context=None):
61+ if context is None:
62+ context = {}
63+ if context.get('button') in ('import_xls_discrepancy_report', 'import_counting_sheet') and '__last_update' in context:
64+ del context['__last_update']
65+ return super(PhysicalInventory, self).write(cr, uid, ids, vals, context)
66+
67 def _inventory_totals(self, cr, uid, ids, field_names, arg, context=None):
68 context = context is None and {} or context
69 def read_many(model, ids, columns):
70@@ -236,12 +243,6 @@
71 Choose to include batch numbers / expiry date or not
72 """
73 context = context is None and {} or context
74- def read_single(model, id_, column):
75- return self.pool.get(model).read(cr, uid, [id_], [column], context=context)[0][column]
76- def create(model, vals):
77- return self.pool.get(model).create(cr, uid, vals, context=context)
78- def view(module, view):
79- return self.pool.get('ir.model.data').get_object_reference(cr, uid, module, view)[1]
80
81 # Prepare values to feed the wizard with
82 assert len(ids) == 1
83@@ -249,12 +250,11 @@
84
85 # Create the wizard
86 wiz_model = 'physical.inventory.generate.counting.sheet'
87- wiz_values = {"inventory_id": inventory_id}
88- wiz_id = create(wiz_model, wiz_values)
89+ wiz_id = self.pool.get(wiz_model).create(cr, uid, {'inventory_id': inventory_id}, context=context)
90 context['wizard_id'] = wiz_id
91
92 # Get the view reference
93- view_id = view('stock', 'physical_inventory_generate_counting_sheet')
94+ view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'stock', 'physical_inventory_generate_counting_sheet')[1]
95
96 # Return a description of the wizard view
97 return {'type': 'ir.actions.act_window',
98@@ -319,6 +319,7 @@
99 # Create a similar dictionnary for counted quantities
100 counting_lines_per_product_batch_expirtydate = {}
101 counted_quantities = {}
102+ max_line_no = 0
103 for line in counting_lines:
104
105 product_batch_expirydate = (line["product_id"][0],
106@@ -332,6 +333,8 @@
107 "line_id": line["id"],
108 "line_no": line["line_no"]
109 }
110+ if line["line_no"] > max_line_no:
111+ max_line_no = line["line_no"]
112
113 # Create a similar dictionnary for existing discrepancies
114 previous_discrepancy_line_ids = inventory["discrepancy_line_ids"]
115@@ -361,11 +364,15 @@
116 all_product_batch_expirydate = set().union(theoretical_quantities,
117 counted_quantities)
118
119+ bn_ed_prod_ids = [x[0] for x in all_product_batch_expirydate if x[1] or x[2]]
120+ prod_info = {}
121+ for prod in self.pool.get('product.product').read(cr, uid, bn_ed_prod_ids, ['batch_management', 'perishable'], context=context):
122+ prod_info[prod['id']] = prod
123+
124 # filter the case we had an entry with BN when product is not (anymore) BN mandatory:
125 filtered_all_product_batch_expirydate = set()
126 for prod_id, batch_n, exp_date in all_product_batch_expirydate:
127- prod_data = self.pool.get('product.product').read(cr, uid, prod_id, ['batch_management', 'perishable'])
128- if batch_n and exp_date and not prod_data['batch_management'] and not prod_data['perishable']:
129+ if batch_n and not prod_info[prod_id]['batch_management'] or exp_date and not prod_info[prod_id]['perishable']:
130 continue
131 else:
132 filtered_all_product_batch_expirydate.add((prod_id, batch_n, exp_date))
133@@ -375,7 +382,6 @@
134 update_discrepancies = {}
135 counting_lines_with_no_discrepancy = []
136
137- new_product_line_no = 0
138 # For each of them, compare the theoretical and counted qty
139 for product_batch_expirydate in filtered_all_product_batch_expirydate:
140 # If the key is not known, assume 0
141@@ -384,7 +390,7 @@
142
143 # If no discrepancy, nothing to do
144 # (Use a continue to save 1 indentation level..)
145- if counted_qty == theoretical_qty:
146+ if counted_qty == theoretical_qty or theoretical_qty == 0 and counted_qty == -1:
147 if product_batch_expirydate in counting_lines_per_product_batch_expirtydate:
148 counting_line_id = counting_lines_per_product_batch_expirtydate[product_batch_expirydate]["line_id"]
149 counting_lines_with_no_discrepancy.append(counting_line_id)
150@@ -395,14 +401,8 @@
151 this_product_batch_expirydate = counting_lines_per_product_batch_expirtydate[product_batch_expirydate]
152 line_no = this_product_batch_expirydate["line_no"]
153 else: # Otherwise, create additional line numbers starting from the total of existing lines
154- new_product_line_no += 1
155- if len(counting_lines_per_product_batch_expirtydate) > 0:
156- # get highest line_no
157- line_no = counting_lines_per_product_batch_expirtydate[
158- sorted(counting_lines_per_product_batch_expirtydate)[-1]
159- ]['line_no'] + new_product_line_no
160- else:
161- line_no = new_product_line_no
162+ max_line_no += 1
163+ line_no = max_line_no
164
165 if product_batch_expirydate in previous_discrepancies:
166 previous_discrepancies[product_batch_expirydate]["todelete"] = False
167@@ -754,19 +754,22 @@
168
169 # Check quantity
170 quantity = row.cells[4].data
171- # if quantity is integer then convert to string otherwise it will not be imported:
172- if isinstance(quantity, int) and quantity == 0:
173- quantity = '0'
174- try:
175- quantity = counting_obj.quantity_validate(cr, quantity)
176- except NegativeValueError:
177- add_error(_('Quantity %s is negative') % quantity, row_index, 4)
178- quantity = 0.0
179- except ValueError:
180- quantity = 0.0
181- add_error(_('Quantity %s is not valid') % quantity, row_index, 4)
182+ if quantity is not None:
183+ if isinstance(quantity, int) and quantity == 0:
184+ quantity = '0'
185+ try:
186+ quantity = counting_obj.quantity_validate(cr, quantity)
187+ except NegativeValueError:
188+ add_error(_('Quantity %s is negative') % quantity, row_index, 4)
189+ quantity = 0.0
190+ except ValueError:
191+ quantity = 0.0
192+ add_error(_('Quantity %s is not valid') % quantity, row_index, 4)
193
194- product_info = product_obj.read(cr, uid, product_id, ['batch_management', 'perishable', 'default_code', 'uom_id'])
195+ if product_id:
196+ product_info = product_obj.read(cr, uid, product_id, ['batch_management', 'perishable', 'default_code', 'uom_id'])
197+ else:
198+ product_info = {'batch_management': False, 'perishable': False, 'default_code': product_code, 'uom_id': False}
199
200 if product_info['uom_id'] and product_uom_id and product_info['uom_id'][0] != product_uom_id:
201 add_error(_("""Product %s, UoM %s does not conform to that of product in stock""") % (product_info['default_code'], product_uom), row_index, 3)
202@@ -774,7 +777,7 @@
203
204 # Check batch number
205 batch_name = row.cells[5].data
206- if not batch_name and product_info['batch_management'] and float(quantity or 0) > 0:
207+ if not batch_name and product_info['batch_management'] and quantity is not None:
208 add_error(_('Batch number is required'), row_index, 5)
209
210 if batch_name and not product_info['batch_management']:
211@@ -797,14 +800,14 @@
212 raise ValueError()
213 except ValueError:
214 add_error(_("""Expiry date %s is not valid""") % expiry_date, row_index, 6)
215- if not expiry_date and product_info['perishable'] and float(quantity or 0) > 0:
216+ if not expiry_date and product_info['perishable'] and quantity is not None:
217 add_error(_('Expiry date is required'), row_index, 6)
218
219 # Check duplicate line (Same product_id, batch_number, expirty_date)
220 item = '%d-%s-%s' % (product_id or -1, batch_name or '', expiry_date or '')
221- if item in line_items and (batch_name or expiry_date):
222- add_error(_("""Duplicate line (same product, batch number and expiry date)"""), row_index)
223- else:
224+ if item in line_items:
225+ add_error(_("""Product %s, Duplicate line (same product, batch number and expiry date)""") % product_info['default_code'], row_index)
226+ elif quantity is not None:
227 line_items.append(item)
228
229 data = {
230@@ -812,10 +815,12 @@
231 'product_id': product_id,
232 'batch_number': batch_name,
233 'expiry_date': expiry_date,
234- 'quantity': quantity,
235+ 'quantity': False,
236 'product_uom_id': product_uom_id,
237 }
238
239+ if quantity is not None:
240+ data['quantity'] = quantity
241 # Check if line exist
242 if line_no:
243 line_ids = counting_obj.search(cr, uid, [('inventory_id', '=', inventory_rec.id), ('line_no', '=', line_no)])
244@@ -829,7 +834,7 @@
245
246 if len(line_ids) > 0:
247 counting_sheet_lines.append((1, line_ids[0], data))
248- else:
249+ elif quantity is not None:
250 counting_sheet_lines.append((0, 0, data))
251
252 # endfor
253@@ -838,7 +843,8 @@
254 wizard_obj = self.pool.get('physical.inventory.import.wizard')
255 if counting_sheet_errors:
256 # Errors found, open message box for exlain
257- self.write(cr, uid, ids, {'file_to_import': False}, context=context)
258+ #self.write(cr, uid, ids, {'file_to_import': False}, context=context)
259+ cr.execute('update physical_inventory set file_to_import=NULL where id=%s', (ids[0], ))
260 if counting_sheet_warnings:
261 counting_sheet_errors.append("\n%s" % _("Warning"))
262 counting_sheet_errors += counting_sheet_warnings
263@@ -922,7 +928,8 @@
264 wizard_obj = self.pool.get('physical.inventory.import.wizard')
265 if discrepancy_report_errors:
266 # Errors found, open message box for exlain
267- self.write(cr, uid, ids, {'file_to_import2': False}, context=context)
268+ #self.write(cr, uid, ids, {'file_to_import2': False}, context=context)
269+ cr.execute('update physical_inventory set file_to_import2=NULL where id=%s', (ids[0], ))
270 result = wizard_obj.message_box(cr, uid, title=_('Importation errors'),
271 message='\n'.join(discrepancy_report_errors))
272 else:
273@@ -1170,7 +1177,6 @@
274 self.infolog(cr, uid, _("The Physical inventory id:%s (%s) has been cancelled") % (inv.id, inv.name))
275 return {}
276
277-
278 PhysicalInventory()
279
280
281
282=== modified file 'bin/addons/stock/wizard/physical_inventory_generate_counting_sheet.py'
283--- bin/addons/stock/wizard/physical_inventory_generate_counting_sheet.py 2018-02-15 17:06:51 +0000
284+++ bin/addons/stock/wizard/physical_inventory_generate_counting_sheet.py 2018-02-19 10:31:32 +0000
285@@ -59,46 +59,51 @@
286 location_id = inventory["location_id"][0]
287 product_ids = inventory["product_ids"]
288
289- bn_and_eds = self.get_BN_and_ED_for_products_at_location(cr, uid, location_id, product_ids, only_with_stock_level, context=context)
290+ bn_and_eds = self.get_BN_and_ED_for_products_at_location(cr, uid, location_id, product_ids, context=context)
291
292 # Prepare the inventory lines to be created
293
294 inventory_counting_lines_to_create = []
295- current_prodlot_id = False
296- for product in self.pool.get('product.product').browse(cr, uid, product_ids, context=context):
297- bn_and_eds_for_this_product = bn_and_eds[product.id]
298+ for product_id in bn_and_eds.keys():
299+ bn_and_eds_for_this_product = bn_and_eds[product_id]
300 # If no bn / ed related to this product, create a single inventory
301 # line
302- if not product.batch_management and not product.perishable:
303- if only_with_stock_level and not self.not_zero_stock_on_location(cr, uid, location_id, product.id,
304- current_prodlot_id, context=context):
305+ if bn_and_eds[product_id] == (False, False, False):
306+ if only_with_stock_level and not self.not_zero_stock_on_location(cr, uid, location_id, product_id, False, context=context):
307 continue
308 else:
309 values = {
310 "line_no": len(inventory_counting_lines_to_create) + 1,
311 "inventory_id": inventory_id,
312- "product_id": product.id,
313- "batch_number": False,
314- "expiry_date": False
315- }
316- inventory_counting_lines_to_create.append(values)
317- # Otherwise, create an inventory line for this product ~and~ for
318- # each BN/ED
319+ "product_id": product_id,
320+ "batch_number": False,
321+ "expiry_date": False
322+ }
323+ inventory_counting_lines_to_create.append(values)
324+ elif not bn_and_eds[product_id]:
325+ # BN/ED product with no stock move in this location
326+ if not only_with_stock_level:
327+ values = {
328+ "line_no": len(inventory_counting_lines_to_create) + 1,
329+ "inventory_id": inventory_id,
330+ "product_id": product_id,
331+ "batch_number": False,
332+ "expiry_date": False
333+ }
334+ inventory_counting_lines_to_create.append(values)
335 else:
336+ # Otherwise, create an inventory line for this product ~and~ for
337+ # each BN/ED
338 for bn_and_ed in bn_and_eds_for_this_product:
339- current_prodlot = bn_and_ed[0] if prefill_bn else False
340- current_prodlot_id = \
341- self.pool.get('stock.production.lot').search(cr, uid, [('name', '=', current_prodlot)],
342- context=context)[0] if prefill_bn else False
343- if only_with_stock_level and not self.not_zero_stock_on_location(cr, uid, location_id, product.id,
344- current_prodlot_id, context=context):
345+ if only_with_stock_level and not self.not_zero_stock_on_location(cr, uid, location_id, product_id,
346+ bn_and_ed[2], context=context):
347 continue
348 else:
349 values = {
350 "line_no": len(inventory_counting_lines_to_create) + 1,
351 "inventory_id": inventory_id,
352- "product_id": product.id,
353- "batch_number": current_prodlot,
354+ "product_id": product_id,
355+ "batch_number": bn_and_ed[0] if prefill_bn else False,
356 "expiry_date": bn_and_ed[1] if prefill_ed else False
357 }
358 inventory_counting_lines_to_create.append(values)
359@@ -125,80 +130,46 @@
360
361 return {'type': 'ir.actions.act_window_close'}
362
363- def get_BN_and_ED_for_products_at_location(self, cr, uid, location_id, product_ids, only_with_stock_level, context=None):
364- context = context if context else {}
365- def read_many(model, ids, columns):
366- return self.pool.get(model).read(cr, uid, ids, columns, context=context)
367-
368- # Add location to the copied context
369- ctx = context.copy()
370- ctx['location'] = location_id
371- ctx['compute_child'] = True
372-
373- # Get the moves at location, related to these products
374- moves_at_location = self.get_moves_at_location(cr, uid, location_id, context=ctx)
375-
376- moves_at_location_for_products = [ m for m in moves_at_location
377- if m["product_id"][0] in product_ids ]
378-
379- # Get the production lot associated to these moves
380- moves_at_location_for_products = read_many("stock.move",
381- moves_at_location_for_products,
382- ["product_id",
383- "prodlot_id",
384- "expired_date"])
385-
386- # Init a dict with an empty set for each products
387- BN_and_ED = { product_id:set() for product_id in product_ids }
388-
389- for move in moves_at_location_for_products:
390-
391+ def get_BN_and_ED_for_products_at_location(self, cr, uid, location_id, product_ids, context=None):
392+ if context is None:
393+ context = {}
394+
395+
396+ prod_info = {}
397+ move_obj = self.pool.get('stock.move')
398+ prod_obj = self.pool.get('product.product')
399+
400+ BN_and_ED = {}
401+ for prod in prod_obj.read(cr, uid, product_ids, ['batch_management', 'perishable'], context=context):
402+ if not prod['batch_management'] and not prod['perishable']:
403+ BN_and_ED[prod['id']] = (False, False, False)
404+ else:
405+ prod_info[prod['id']] = prod
406+ BN_and_ED[prod['id']] = set()
407+
408+ domain = ['&', '&', '|',
409+ ('location_id', 'in', [location_id]),
410+ ('location_dest_id', 'in', [location_id]),
411+ ('state', '=', 'done'),
412+ ('product_id', 'in', prod_info.keys()),
413+ ('prodlot_id', '!=', False)
414+ ]
415+
416+ move_ids = move_obj.search(cr, uid, domain, context=context)
417+
418+ for move in move_obj.read(cr, uid, move_ids, ['product_id', 'prodlot_id', 'expired_date']):
419 product_id = move["product_id"][0]
420- product_qty = self.pool.get('product.product').browse(cr, uid, product_id, fields_to_fetch=['qty_available'],
421- context=ctx).qty_available
422-
423- batch_number = move["prodlot_id"][1] if isinstance(move["prodlot_id"], tuple) else False
424- expired_date = move["expired_date"]
425-
426- # Dirty hack to ignore/hide internal batch numbers ("MSFBN")
427- if batch_number and batch_number.startswith("MSFBN"):
428+
429+
430+ if move['prodlot_id'] and move["prodlot_id"][1].startswith("MSFBN"):
431 batch_number = False
432+ else:
433+ batch_number = move["prodlot_id"][1]
434
435- if not only_with_stock_level or (only_with_stock_level and product_qty != 0):
436- BN_and_ED[product_id].add((batch_number, expired_date))
437+ BN_and_ED[product_id].add((batch_number, move["expired_date"], move["prodlot_id"][0]))
438
439 return BN_and_ED
440
441- # FIXME : this is copy/pasta from the other wizard ...
442- # Should be factorized, probably in physical inventory, or stock somewhere.
443- def get_moves_at_location(self, cr, uid, location_id, context=None):
444- context = context if context else {}
445-
446- def read_many(model, ids, columns):
447- return self.pool.get(model).read(cr, uid, ids, columns, context=context)
448-
449- def search(model, domain):
450- return self.pool.get(model).search(cr, uid, domain, context=context)
451-
452- assert isinstance(location_id, int)
453-
454- # Get all the moves for in/out of that location
455- from_or_to_location = ['&', '|',
456- ('location_id', 'in', [location_id]),
457- ('location_dest_id', 'in', [location_id]),
458- ('state', '=', 'done')]
459-
460- moves_at_location_ids = search("stock.move", from_or_to_location)
461- moves_at_location = read_many("stock.move",
462- moves_at_location_ids,
463- ["product_id",
464- "date",
465- "product_qty",
466- "location_id",
467- "location_dest_id"])
468-
469- return moves_at_location
470-
471 def not_zero_stock_on_location(self, cr, uid, location_id, product_id, prodlot_id, context=False):
472 '''
473 Check if the product's stock on the inventory's location is != 0
474
475=== modified file 'bin/osv/orm.py'
476--- bin/osv/orm.py 2018-01-18 18:35:04 +0000
477+++ bin/osv/orm.py 2018-02-19 10:31:32 +0000
478@@ -1472,6 +1472,10 @@
479 trans = translation_obj._get_source(cr, user, context['base_model_name'], 'view', context['lang'], node.get('string'))
480 if trans:
481 node.set('string', trans)
482+ if node.get('filter_selector'):
483+ trans = translation_obj._get_source(cr, user, self._name, 'view', context['lang'], node.get('filter_selector'))
484+ if trans:
485+ node.set('filter_selector', trans)
486 if node.tag == 'translate':
487 parent = node.getparent()
488 source = node.text
489
490=== modified file 'bin/tools/translate.py'
491--- bin/tools/translate.py 2017-02-16 10:55:56 +0000
492+++ bin/tools/translate.py 2018-02-19 10:31:32 +0000
493@@ -561,6 +561,8 @@
494 res.append(de.get('confirm').encode("utf8"))
495 if de.get("help"):
496 res.append(de.get('help').encode("utf8"))
497+ if de.get("filter_selector"):
498+ res.append(de.get('filter_selector').encode("utf8"))
499 if de.tag == 'translate':
500 text_to_translate = ''
501 if de.text:

Subscribers

People subscribed via source and target branches

to all changes: