From c63bc7d2970b981f9ce01a9be0f701cde940b018 Mon Sep 17 00:00:00 2001 From: Bertrand Chenal Date: Wed, 20 Aug 2008 19:41:32 +0200 Subject: [PATCH] Some refectoring: more efficient code and bugfixes --- inventory.py | 25 ++++++------ location.py | 16 ++------ product.py | 113 ++++++++++++++++++++++++++++++--------------------- 3 files changed, 84 insertions(+), 70 deletions(-) diff --git a/inventory.py b/inventory.py index 54d28d252a..d8b9e2ae3c 100644 --- a/inventory.py +++ b/inventory.py @@ -216,26 +216,27 @@ class CompleteInventory(Wizard): else: pbl = product_obj.products_by_location( cursor, user, [inventory.location.id], context=context) + # Index some data - uom_by_id = {} - for uom in uom_obj.browse( - cursor, user, [line['uom'] for line in pbl], context=context): - uom_by_id[uom.id] = uom + product2uom = {} + for product in product_obj.read(cursor, user, + [line[1] for line in pbl], + ['default_uom'], context=context): + product2uom[product['id']] = product['default_uom'] product_qty = {} - for line in pbl: - product_qty[line['product']] = (line['quantity'], - uom_by_id[line['uom']]) + for (location, product), quantity in pbl.iteritems(): + product_qty[product] = (quantity, product2uom[product]) # Update existing lines for line in inventory.lines: if line.product.id in product_qty: - quantity, uom = product_qty[line.product.id] + quantity, uom_id = product_qty[line.product.id] del product_qty[line.product.id] # if nothing as changed, continue if line.quantity == line.expected_quantity == quantity \ - and line.uom.id == uom.id: + and line.uom.id == uom_id: continue values = {'expected_quantity': quantity, - 'uom': uom.id} + 'uom': uom_id} # update also quantity field if not edited if line.quantity == line.expected_quantity: values['quantity'] = quantity @@ -250,12 +251,12 @@ class CompleteInventory(Wizard): # Create lines if needed for product in product_qty: - quantity, uom = product_qty[product] + quantity, uom_id = product_qty[product] values = { 'product': product, 'expected_quantity': quantity, 'quantity': quantity, - 'uom': uom.id, + 'uom': uom_id, 'inventory': inventory.id, } line_obj.create( diff --git a/location.py b/location.py index e683b2c512..2f8e397402 100644 --- a/location.py +++ b/location.py @@ -103,18 +103,10 @@ class Location(OSV): context = context.copy() del context['stock_date'] pbl = product_obj.products_by_location(cursor, user, location_ids=ids, - product_ids=[context['product']], with_childs=True, - context=context) - qty_by_ltn = dict([(i['location'], i['quantity']) for i in pbl]) - res = {} - for location_id in ids: - child_ids = self.search(cursor, user, [ - ('parent', 'child_of', [location_id]), - ], context=context) - res[location_id] = 0.0 - for child_id in {}.fromkeys(child_ids): - res[location_id] += qty_by_ltn.get(child_id, 0.0) - return res + product_ids=[context['product']], with_childs=True, skip_zero=False, + context=context).iteritems() + + return dict( [(loc,qty) for (loc,prod), qty in pbl] ) def view_header_get(self, cursor, user, value, view_type='form', context=None): diff --git a/product.py b/product.py index 35c43a7856..d6b00c4afc 100644 --- a/product.py +++ b/product.py @@ -24,11 +24,10 @@ class Product(OSV): pbl = self.products_by_location(cursor, user, location_ids=context['locations'], product_ids=ids, with_childs=True, context=context) - res = {} - for product_id in ids: - res[product_id] = 0.0 - for line in pbl: - res[line['product']] += line['quantity'] + + res = {}.fromkeys(ids, 0.0) + for (location, product) in zip(context['locations'], ids): + res[product] += pbl.get((location, product), 0.0) return res def _search_quantity_eval_domain(self, line, domain): @@ -47,28 +46,22 @@ class Product(OSV): def search_quantity(self, cursor, user, name, domain=[], context=None): if not (context and context.get('locations')): return [] + if (name != 'forecast_quantity') and context.get('stock_date'): + if context['stock_date'] != datetime.date.today(): + context = context.copy() + del context['stock_date'] - pbl = self.products_by_location(cursor, user, - location_ids=context['locations'], - forecast=(name == 'forecast_quantity'), with_childs=True, - context=context) + pbl = self.products_by_location( + cursor, user, location_ids=context['locations'], with_childs=True, + skip_zero=False, context=context).iteritems() - if name == 'forecast_quantity': - pbl = self.products_by_location(cursor, user, - location_ids=context['locations'], with_childs=True, - skip_zero=False, context=context) - for line in pbl: - line['forecast_quantity'] = line['quantity'] - del line['quantity'] - else: - if context.get('stock_date'): - if context['stock_date'] != datetime.date.today(): - context = context.copy() - del context['stock_date'] - pbl = self.products_by_location(cursor, user, - location_ids=context['locations'], with_childs=True, - skip_zero=False, context=context) - res= [line['product'] for line in pbl \ + processed_lines = [] + for (location, product), quantity in pbl: + processed_lines.append({'location': location, #XXX useful ? + 'product': product, + name: quantity}) + + res= [line['product'] for line in processed_lines \ if self._search_quantity_eval_domain(line, domain)] return [('id', 'in', res)] @@ -145,13 +138,14 @@ class Product(OSV): where_date = [context['stock_date'], context['stock_date']] else: where_date = [datetime.date.today(), datetime.date.today()] + cursor.execute(select_clause % ( ",".join(["%s" for i in in_states]), where_clause, ",".join(["%s" for i in out_states]), where_clause), in_states + where_vals + where_date + \ out_states + where_vals + where_date) - - return cursor.fetchall() + res = cursor.fetchall() + return res def products_by_location(self, cursor, user, location_ids, product_ids=None, with_childs=False, skip_zero=True, context=None): @@ -163,9 +157,11 @@ class Product(OSV): date than today. If with_childs, childs locations are also computed. If skip_zero, list item with quantity equal to zero are not returned. + If no product_ids are given the computation is done on all products. """ uom_obj = self.pool.get("product.uom") product_obj = self.pool.get("product.product") + location_obj = self.pool.get("stock.location") if not location_ids: return [] @@ -174,12 +170,18 @@ class Product(OSV): raw_lines = self.raw_products_by_location(cursor, user, location_ids, product_ids, with_childs=with_childs, context=context) + res_location_ids = [] uom_ids = [] - product_ids = [] + res_product_ids = [] for line in raw_lines: - uom_ids.append(line[2]) - product_ids.append(line[1]) + for id_list, position in ((res_location_ids, 0), (uom_ids, 1), + (res_product_ids, 1)): + if line[position] not in id_list: + id_list.append(line[position]) + if not product_ids: + product_ids = self.pool.get("product.product").search( + cursor, user, [], context=context) uom_by_id = dict([(x.id, x) for x in uom_obj.browse( cursor, user, uom_ids, context=context)]) default_uom = dict((x.id, x.default_uom) for x in product_obj.browse( @@ -187,32 +189,51 @@ class Product(OSV): for line in raw_lines: location, product, uom, quantity = line - key = (location, product, default_uom[product].id) + key = (location, product) res.setdefault(key, 0.0) res[key] += uom_obj.compute_qty(cursor, user, uom_by_id[uom], quantity, default_uom[product], context=context) - # Remove seen products from product_ids - if key[1] in product_ids: - product_ids.remove(key[1]) - res = [{'location': key[0], - 'product': key[1], - 'uom': key[2], - 'quantity': val} for key, val in res.iteritems()] + # Propagate quantities on from child locations to their parents + if with_childs: + # Fetch all child locations + all_location_ids = location_obj.search( + cursor, user, [('parent', 'child_of', location_ids)], + context=context) + locations = location_obj.browse(cursor, user, all_location_ids, + context=context) + # Generate a set of locations without childs and a dict + # giving the parent of each location. + leafs = set(all_location_ids) + parent = {} + for location in locations: + if not location.parent: continue + if location.parent.id in leafs: + leafs.remove(location.parent.id) + parent[location.id] = location.parent.id + + while leafs: + next_leafs = set() + for l in leafs: + if l not in parent: + continue + next_leafs.add(parent[l]) + for product in res_product_ids: + res.setdefault((parent[l], product), 0) + res[(parent[l], product)] += res.get((l,product), 0) + leafs = next_leafs # Complete result with missing products if asked if not skip_zero: - for location_id in location_ids: - for product_id in product_ids: - res.append({ - 'location': location_id, - 'product': product_id, - 'uom': default_uom[product_id].id, - 'quantity': 0.0, - }) + keys = ((l,p) for l in location_ids for p in product_ids) + for location_id, product_id in keys: + if (location_id, product_id) not in res: + res[(location_id, product_id)] = 0.0 return res + + def view_header_get(self, cursor, user, value, view_type='form', context=None): if not context.get('locations'):