[IMP] pricelist: performance improvement: created a 'price_get_multi' method to retrieve prices for several products at once

bzr revid: olt@tinyerp.com-20100927142150-4yf58feypg0sxn3r
This commit is contained in:
olt@tinyerp.com 2010-09-27 16:21:50 +02:00
parent 7f5e2da3d4
commit 2bdd3062d6
2 changed files with 222 additions and 8 deletions

View File

@ -1022,15 +1022,16 @@ class pos_order_line(osv.osv):
def _amount_line_ttc(self, cr, uid, ids, field_name, arg, context):
res = dict.fromkeys(ids, 0.0)
account_tax_obj = self.pool.get('account.tax')
prices = self.price_by_product_multi(cr, uid, ids)
for line in self.browse(cr, uid, ids):
tax_amount = 0.0
taxes = [t for t in line.product_id.taxes_id]
if line.qty == 0.0:
continue
if line.qty == 0.0:
continue
computed_taxes = account_tax_obj.compute_all(cr, uid, taxes, line.price_unit, line.qty)['taxes']
for tax in computed_taxes:
tax_amount += tax['amount']
price = self.price_by_product(cr, uid, ids, line.order_id.pricelist_id.id, line.product_id.id, line.qty, line.order_id.partner_id.id)
price = prices[line.id]
if line.discount!=0.0:
res[line.id] = line.price_unit * line.qty * (1 - (line.discount or 0.0) / 100.0)
else:
@ -1041,14 +1042,70 @@ class pos_order_line(osv.osv):
def _amount_line(self, cr, uid, ids, field_name, arg, context):
res = {}
prices = self.price_by_product_multi(cr, uid, ids)
for line in self.browse(cr, uid, ids):
price = self.price_by_product(cr, uid, ids, line.order_id.pricelist_id.id, line.product_id.id, line.qty, line.order_id.partner_id.id)
price = prices[line.id]
if line.discount!=0.0:
res[line.id] = line.price_unit * line.qty * (1 - (line.discount or 0.0) / 100.0)
else:
res[line.id]=line.price_unit*line.qty
return res
def price_by_product_multi(self, cr, uid, ids, context=None):
if context is None:
context = {}
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)
for line in lines:
pricelist = line.order_id.pricelist_id.id
product_id = line.product_id
qty = line.qty or 0
partner_id = line.order_id.partner_id.id or False
if not product_id:
res[line.id] = 0.0
continue
if not pricelist:
raise osv.except_osv(_('No Pricelist !'),
_('You have to select a pricelist in the sale form !\n' \
'Please set one before choosing a product.'))
uom_id = product_id.uom_po_id.id
#old_price = self.pool.get('product.pricelist').price_get(cr, uid, [pricelist], product_id.id, qty or 1.0, partner_id, {'uom': uom_id})[pricelist]
#print "prod_id: %s, pricelist: %s, price: %s" % (product_id.id, pricelist, price)
price = price_get_multi_res[line.product_id.id][pricelist]
#print "prod_id: %s, pricelist: %s, price2: %s" % (product_id.id, pricelist, price2)
#if old_price != price:
# raise Exception('old_price != price')
unit_price = price or product_id.list_price
res[line.id] = unit_price
if unit_price is False:
raise osv.except_osv(_('No valid pricelist line found !'),
_("Couldn't find a pricelist line matching this product" \
" and quantity.\nYou have to change either the product," \
" the quantity or the pricelist."))
return res
def price_by_product(self, cr, uid, ids, pricelist, product_id, qty=0, partner_id=False):
if not product_id:
return 0.0
@ -1057,7 +1114,7 @@ class pos_order_line(osv.osv):
_('You have to select a pricelist in the sale form !\n' \
'Please set one before choosing a product.'))
p_obj = self.pool.get('product.product').browse(cr,uid,[product_id])[0]
uom_id=p_obj.uom_po_id.id
uom_id = p_obj.uom_po_id.id
price = self.pool.get('product.pricelist').price_get(cr, uid,
[pricelist], product_id, qty or 1.0, partner_id,{'uom': uom_id})[pricelist]
unit_price=price or p_obj.list_price

View File

@ -126,6 +126,162 @@ class product_pricelist(osv.osv):
"currency_id": _get_currency
}
def price_get_multi(self, cr, uid, product_map, context=None):
"""multi products 'price_get'
@param context: {
'date': Date of the pricelist (%Y-%m-%d),
@return: a dict with product_id as key and the product price 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
context = context or {}
# XXX
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')
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:
pricelist_version_ids = list(set([k[1] for k in product_map.keys()]))
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 = 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'])])
# 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, pricelist_id), line in product_map.items():
price = False
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
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 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(self, cr, uid, ids, prod_id, qty, partner=None, context=None):
'''
context = {
@ -134,8 +290,8 @@ class product_pricelist(osv.osv):
'date': Date of the pricelist (%Y-%m-%d),
}
'''
price=False
item_id=0
price = False
item_id = 0
context = context or {}
currency_obj = self.pool.get('res.currency')
product_obj = self.pool.get('product.product')
@ -219,7 +375,7 @@ class product_pricelist(osv.osv):
elif res['base'] == -2:
where = []
if partner:
where = [('name', '=', partner) ]
where = [('name', '=', partner) ]
sinfo = supplierinfo_obj.search(cr, uid,
[('product_id', '=', tmpl_id)] + where)
price = 0.0
@ -264,6 +420,7 @@ class product_pricelist(osv.osv):
uom = product.uos_id or product.uom_id
result[id] = self.pool.get('product.uom')._compute_price(cr,
uid, uom.id, result[id], context['uom'])
return result
product_pricelist()