From 47867b8d71cefca520be4b245076d77d74e370d9 Mon Sep 17 00:00:00 2001 From: "olt@tinyerp.com" <> Date: Wed, 29 Sep 2010 11:57:42 +0200 Subject: [PATCH] [IMP] point_of_sale: performance improvement in pricelist (price_get) bzr revid: olt@tinyerp.com-20100929095742-yi2ldls09prywt48 --- addons/point_of_sale/pos.py | 19 +--- addons/product/pricelist.py | 188 ++++++++++++++++++++++++++++++++++-- addons/product/product.py | 13 ++- 3 files changed, 193 insertions(+), 27 deletions(-) diff --git a/addons/point_of_sale/pos.py b/addons/point_of_sale/pos.py index b5b17d54d34..0d6ab5f2b33 100644 --- a/addons/point_of_sale/pos.py +++ b/addons/point_of_sale/pos.py @@ -1079,8 +1079,6 @@ class pos_order_line(osv.osv): else: res[line.id][f] = line.price_unit * line.qty res[line.id][f] += tax_amount - print ids - print res return res def price_by_product_multi(self, cr, uid, ids, context=None): @@ -1090,20 +1088,11 @@ class pos_order_line(osv.osv): res = {}.fromkeys(ids, 0.0) lines = self.browse(cr, uid, ids, context=context) - # product_map: - # {10, 1): {'qty': 5, 'uom_id': 1, 'partner_id': 512}, - # (22, 3): {'qty': 10, 'uom_id': 2, 'partner_id', 35}, - # } - # key = (product_id, pricelist_id) - product_map = dict([((line.product_id.id, line.order_id.pricelist_id.id), { - 'qty': line.qty, - 'uom_id': line.product_id.uom_po_id.id, - 'partner_id': line.order_id.partner_id.id, - }) - for line - in lines]) - price_get_multi_res = self.pool.get('product.pricelist').price_get_multi(cr, uid, product_map) + pricelist_ids = [line.order_id.pricelist_id.id for line in lines] + products_by_qty_by_partner = [(line.product_id.id, line.qty, line.order_id.partner_id.id) for line in lines] + + price_get_multi_res = self.pool.get('product.pricelist').price_get_multi(cr, uid, pricelist_ids, products_by_qty_by_partner, context=context) for line in lines: pricelist = line.order_id.pricelist_id.id diff --git a/addons/product/pricelist.py b/addons/product/pricelist.py index 0dbc9cadcef..a4b93827660 100644 --- a/addons/product/pricelist.py +++ b/addons/product/pricelist.py @@ -126,7 +126,7 @@ class product_pricelist(osv.osv): "currency_id": _get_currency } - def price_get_multi(self, cr, uid, product_map, context=None): + def price_get_multi_old(self, cr, uid, product_map, context=None): """multi products 'price_get' @param context: { 'date': Date of the pricelist (%Y-%m-%d), @@ -155,6 +155,7 @@ class product_pricelist(osv.osv): currency_obj = self.pool.get('res.currency') product_obj = self.pool.get('product.product') product_category_obj = self.pool.get('product.category') + product_uom_obj = self.pool.get('product.uom') supplierinfo_obj = self.pool.get('product.supplierinfo') price_type_obj = self.pool.get('product.price.type') product_pricelist_version_obj = self.pool.get('product.pricelist.version') @@ -178,7 +179,7 @@ class product_pricelist(osv.osv): # product.product: product_ids = list(set([k[0] for k in product_map.keys()])) - products = dict([(item['id'], item) for item in product_obj.read(cr, uid, product_ids, ['categ_id', 'product_tmpl_id'])]) + products = dict([(item['id'], item) for item in product_obj.read(cr, uid, product_ids, ['categ_id', 'product_tmpl_id', 'uos_id', 'uom_id'])]) # product.category: product_category_ids = product_category_obj.search(cr, uid, []) @@ -191,7 +192,6 @@ class product_pricelist(osv.osv): partner = line['partner_id'] qty = line['qty'] - uom_id = line['uom_id'] tmpl_id = products[product_id]['product_tmpl_id'] and products[product_id]['product_tmpl_id'][0] or False categ_id = products[product_id]['categ_id'] and products[product_id]['categ_id'][0] or False @@ -269,20 +269,192 @@ class product_pricelist(osv.osv): # exception here because it breaks the search price = False + if price: + p_uom_id = products[product_id]['uos_id'] and products[product_id]['uos_id'][0] or products[product_id]['uom_id'] and products[product_id]['uom_id'][0] or False + if p_uom_id: + price = product_uom_obj._compute_price(cr, uid, p_uom_id, price, context.get('uom', False)) + if results.get(product_id): results[product_id][pricelist_id] = price else: results[product_id] = {pricelist_id: price} - # XXX - #if 'uom' in context: - # product = product_obj.browse(cr, uid, product_id) - # uom = product.uos_id or product.uom_id - # results[product_id] = self.pool.get('product.uom')._compute_price(cr, uid, uom.id, result[id], context['uom']) + return results + + #def price_get_multi(self, cr, uid, product_ids, context=None): + def price_get_multi(self, cr, uid, pricelist_ids, products_by_qty_by_partner, context=None): + """multi products 'price_get'. + @param pricelist_ids: + @param products_by_qty: + @param partner: + @param context: { + 'date': Date of the pricelist (%Y-%m-%d),} + @return: a dict of dict with product_id as key and a dict 'price by pricelist' as value + """ + + def _create_parent_category_list(id, lst): + if not id: + return [] + parent = product_category_tree.get(id) + if parent: + lst.append(parent) + return _create_parent_category_list(parent, lst) + else: + return lst + # _create_parent_category_list + + if context is None: + context = {} + + date = time.strftime('%Y-%m-%d') + if 'date' in context: + date = context['date'] + + currency_obj = self.pool.get('res.currency') + product_obj = self.pool.get('product.product') + product_category_obj = self.pool.get('product.category') + product_uom_obj = self.pool.get('product.uom') + supplierinfo_obj = self.pool.get('product.supplierinfo') + price_type_obj = self.pool.get('product.price.type') + product_pricelist_version_obj = self.pool.get('product.pricelist.version') + + # product.pricelist.version: + if pricelist_ids: + pricelist_version_ids = pricelist_ids + else: + # all pricelists: + pricelist_version_ids = product_pricelist_version_obj.search(cr, uid, []) + + pricelist_version_ids = list(set(pricelist_version_ids)) + + plversions_search_args = [ + ('pricelist_id', 'in', pricelist_version_ids), + '|', + ('date_start', '=', False), + ('date_start', '<=', date), + '|', + ('date_end', '=', False), + ('date_end', '>=', date), + ] + + plversion_ids = product_pricelist_version_obj.search(cr, uid, plversions_search_args) + if len(pricelist_version_ids) != len(plversion_ids): + msg = "At least one pricelist has no active version !\nPlease create or activate one." + raise osv.except_osv(_('Warning !'), _(msg)) + + # product.product: + product_ids = [i[0] for i in products_by_qty_by_partner] + #products = dict([(item['id'], item) for item in product_obj.read(cr, uid, product_ids, ['categ_id', 'product_tmpl_id', 'uos_id', 'uom_id'])]) + products = product_obj.browse(cr, uid, product_ids, context=context) + products_dict = dict([(item.id, item) for item in products]) + + # product.category: + product_category_ids = product_category_obj.search(cr, uid, []) + product_categories = product_category_obj.read(cr, uid, product_category_ids, ['parent_id']) + product_category_tree = dict([(item['id'], item['parent_id'][0]) for item in product_categories if item['parent_id']]) + + results = {} + for product_id, qty, partner in products_by_qty_by_partner: + for pricelist_id in pricelist_version_ids: + price = False + + tmpl_id = products_dict[product_id].product_tmpl_id and products_dict[product_id].product_tmpl_id.id or False + + categ_id = products_dict[product_id].categ_id and products_dict[product_id].categ_id.id or False + categ_ids = _create_parent_category_list(categ_id, [categ_id]) + if categ_ids: + categ_where = '(categ_id IN (' + ','.join(map(str, categ_ids)) + '))' + else: + categ_where = '(categ_id IS NULL)' + + cr.execute( + 'SELECT i.*, pl.currency_id ' + 'FROM product_pricelist_item AS i, ' + 'product_pricelist_version AS v, product_pricelist AS pl ' + 'WHERE (product_tmpl_id IS NULL OR product_tmpl_id = %s) ' + 'AND (product_id IS NULL OR product_id = %s) ' + 'AND (' + categ_where + ' OR (categ_id IS NULL)) ' + 'AND price_version_id = %s ' + 'AND (min_quantity IS NULL OR min_quantity <= %s) ' + 'AND i.price_version_id = v.id AND v.pricelist_id = pl.id ' + 'ORDER BY sequence', + (tmpl_id, product_id, pricelist_id, qty)) + res1 = cr.dictfetchall() + + for res in res1: + if res: + if res['base'] == -1: + if not res['base_pricelist_id']: + price = 0.0 + else: + price_tmp = self.price_get(cr, uid, + [res['base_pricelist_id']], prod_id, + qty)[res['base_pricelist_id']] + ptype_src = self.browse(cr, uid, res['base_pricelist_id']).currency_id.id + price = currency_obj.compute(cr, uid, ptype_src, res['currency_id'], price_tmp, round=False) + break + elif res['base'] == -2: + # this section could be improved by moving the queries outside the loop: + where = [] + if partner: + where = [('name', '=', partner) ] + sinfo = supplierinfo_obj.search(cr, uid, + [('product_id', '=', tmpl_id)] + where) + price = 0.0 + if sinfo: + cr.execute('SELECT * ' \ + 'FROM pricelist_partnerinfo ' \ + 'WHERE suppinfo_id IN %s' \ + 'AND min_quantity <= %s ' \ + 'ORDER BY min_quantity DESC LIMIT 1', (tuple(sinfo),qty,)) + res2 = cr.dictfetchone() + if res2: + price = res2['price'] + break + else: + price_type = price_type_obj.browse(cr, uid, int(res['base'])) + price = currency_obj.compute(cr, uid, + price_type.currency_id.id, res['currency_id'], + product_obj.price_get(cr, uid, [product_id], + price_type.field)[product_id], round=False, context=context) + + if price: + price_limit = price + + price = price * (1.0+(res['price_discount'] or 0.0)) + price = rounding(price, res['price_round']) + price += (res['price_surcharge'] or 0.0) + if res['price_min_margin']: + price = max(price, price_limit+res['price_min_margin']) + if res['price_max_margin']: + price = min(price, price_limit+res['price_max_margin']) + break + + else: + # False means no valid line found ! But we may not raise an + # exception here because it breaks the search + price = False + + if price: + if 'uom' in context: + product = products_dict[product_id] + uom = product.uos_id or product.uom_id + price = self.pool.get('product.uom')._compute_price(cr, uid, uom.id, price, context['uom']) + + if results.get(product_id): + results[product_id][pricelist_id] = price + else: + results[product_id] = {pricelist_id: price} return results def price_get(self, cr, uid, ids, prod_id, qty, partner=None, context=None): + res_multi = self.price_get_multi(cr, uid, pricelist_ids=ids, products_by_qty_by_partner=[(prod_id, qty, partner)], context=context) + res = res_multi[prod_id] + res.update({'item_id': {ids[-1]: ids[-1]}}) + return res + + def price_get_old(self, cr, uid, ids, prod_id, qty, partner=None, context=None): ''' context = { 'uom': Unit of Measure (int), diff --git a/addons/product/product.py b/addons/product/product.py index 1b74bef87f9..6c1125268a9 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -503,7 +503,15 @@ class product_product(osv.osv): # # Could be overrided for variants matrices prices # - def price_get(self, cr, uid, ids, ptype='list_price', context={}): + def price_get(self, cr, uid, ids, ptype='list_price', context=None): + if context is None: + context = {} + + if 'currency_id' in context: + pricetype_obj = self.pool.get('product.price.type') + price_type_id = pricetype_obj.search(cr, uid, [('field','=',ptype)])[0] + price_type_currency_id = pricetype_obj.browse(cr,uid,price_type_id).currency_id.id + res = {} product_uom_obj = self.pool.get('product.uom') for product in self.browse(cr, uid, ids, context=context): @@ -517,11 +525,8 @@ class product_product(osv.osv): uom.id, res[product.id], context['uom']) # Convert from price_type currency to asked one if 'currency_id' in context: - pricetype_obj = self.pool.get('product.price.type') # Take the price_type currency from the product field # This is right cause a field cannot be in more than one currency - price_type_id = pricetype_obj.search(cr, uid, [('field','=',ptype)])[0] - price_type_currency_id = pricetype_obj.browse(cr,uid,price_type_id).currency_id.id res[product.id] = self.pool.get('res.currency').compute(cr, uid, price_type_currency_id, context['currency_id'], res[product.id],context=context)