'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
"""
+ if not pricelist_ids:
+ pricelist_ids = self.pool.get('product.pricelist').search(cr, uid, [], context=context)
+ results = {}
+ for pricelist in self.browse(cr, uid, pricelist_ids, context=context):
+ subres = self._price_get_multi(cr, uid, pricelist, products_by_qty_by_partner, context=context)
+ for product_id,price in subres.items():
+ results.setdefault(product_id, {})
+ results[product_id][pricelist.id] = price
+ return results
- 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 = {}
-
+ def _price_get_multi(self, cr, uid, pricelist, products_by_qty_by_partner, context=None):
+ context = context or {}
date = context.get('date') or time.strftime('%Y-%m-%d')
+ products = map(lambda x: x[0], products_by_qty_by_partner)
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:
- if not pricelist_ids:
- pricelist_ids = self.pool.get('product.pricelist').search(cr, uid, [], context=context)
-
- pricelist_version_ids = self.pool.get('product.pricelist.version').search(cr, uid, [
- ('pricelist_id', 'in', pricelist_ids),
- '|',
- ('date_start', '=', False),
- ('date_start', '<=', date),
- '|',
- ('date_end', '=', False),
- ('date_end', '>=', date),
- ])
- if len(pricelist_ids) != len(pricelist_version_ids):
+ version = False
+ for v in pricelist.version_id:
+ if ((v.date_start is False) or (v.date_start <= date)) and ((v.date_end is False) or (v.date_end >= date)):
+ version = v
+ break
+ if not version:
raise osv.except_osv(_('Warning!'), _("At least one pricelist has no active version !\nPlease create or activate one."))
-
- # 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']])
+ categ_ids = {}
+ for p in products:
+ categ = p.categ_id
+ while categ:
+ categ_ids[categ.id] = True
+ categ = categ.parent_id
+ categ_ids = categ_ids.keys()
+
+ prod_ids = [x.id for x in products]
+ prod_tmpl_ids = [x.product_tmpl_id.id for x in products]
+
+ # Load all rules
+ cr.execute(
+ 'SELECT i.id '
+ 'FROM product_pricelist_item AS i '
+ 'WHERE (product_tmpl_id IS NULL OR product_tmpl_id = any(%s)) '
+ 'AND (product_id IS NULL OR (product_id = any(%s))) '
+ 'AND ((categ_id IS NULL) OR (categ_id = any(%s))) '
+ 'AND (price_version_id = %s) '
+ 'ORDER BY sequence, min_quantity desc',
+ (prod_tmpl_ids, prod_ids, categ_ids, version.id))
+
+ item_ids = [x[0] for x in cr.fetchall()]
+ items = self.pool.get('product.pricelist.item').browse(cr, uid, item_ids, context=context)
+
+ price_types = {}
results = {}
- for product_id, qty, partner in products_by_qty_by_partner:
- for pricelist_id in pricelist_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)'
-
- if partner:
- partner_where = 'base <> -2 OR %s IN (SELECT name FROM product_supplierinfo WHERE product_id = %s) '
- partner_args = (partner, tmpl_id)
- else:
- partner_where = 'base <> -2 '
- partner_args = ()
-
- 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 (' + partner_where + ') '
- '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) + partner_args + (pricelist_version_ids[0], qty))
- res1 = cr.dictfetchall()
- uom_price_already_computed = False
- 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']], product_id,
- qty, context=context)[res['base_pricelist_id']]
- ptype_src = self.browse(cr, uid, res['base_pricelist_id']).currency_id.id
- uom_price_already_computed = True
- price = currency_obj.compute(cr, uid,
- ptype_src, res['currency_id'],
- price_tmp, round=False,
- context=context)
- 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:
- qty_in_product_uom = qty
- from_uom = context.get('uom') or product_obj.read(cr, uid, [product_id], ['uom_id'])[0]['uom_id'][0]
- supplier = supplierinfo_obj.browse(cr, uid, sinfo, context=context)[0]
- seller_uom = supplier.product_uom and supplier.product_uom.id or False
- if seller_uom and from_uom and from_uom != seller_uom:
- qty_in_product_uom = product_uom_obj._compute_qty(cr, uid, from_uom, qty, to_uom_id=seller_uom)
- else:
- uom_price_already_computed = True
- 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_in_product_uom,))
- res2 = cr.dictfetchone()
- if res2:
- price = res2['price']
+ for product, qty, partner in products_by_qty_by_partner:
+ uom_price_already_computed = False
+ results[product.id] = 0.0
+ price = False
+ for rule in items:
+ if rule.min_quantity and qty<rule.min_quantity:
+ continue
+ if rule.product_tmpl_id and product.product_tmpl_id.id<>rule.product_tmpl_id.id:
+ continue
+ if rule.product_id and product.id<>rule.product_id.id:
+ continue
+ if rule.categ_id:
+ cat = product.categ_id
+ while cat:
+ if cat.id == rule.categ_id.id:
+ break
+ cat = cat.parent_id
+ if not cat:
+ continue
+
+ if rule.base == -1:
+ if rule.base_pricelist_id:
+ price_tmp = self._price_get_multi(cr, uid,
+ rule.base_pricelist_id, [(product,
+ qty, False)], context=context)[product.id]
+ ptype_src = rule.base_pricelist_id.currency_id.id
+ uom_price_already_computed = True
+ price = currency_obj.compute(cr, uid,
+ ptype_src, pricelist.currency_id.id,
+ price_tmp, round=False,
+ context=context)
+ elif rule.base == -2:
+ for seller in product.seller_ids:
+ if (not partner) or (seller.name.id<>partner):
+ continue
+ qty_in_seller_uom = qty
+ from_uom = context.get('uom') or product.uom_id.id
+ seller_uom = seller.product_uom and seller.product_uom.id or False
+ if seller_uom and from_uom and from_uom != seller_uom:
+ qty_in_seller_uom = product_uom_obj._compute_qty(cr, uid, from_uom, qty, to_uom_id=seller_uom)
else:
- price_type = price_type_obj.browse(cr, uid, int(res['base']))
uom_price_already_computed = True
- 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, context=context)[product_id], round=False, context=context)
-
- if price is not False:
- price_limit = price
- price = price * (1.0+(res['price_discount'] or 0.0))
- if res['price_round']:
- price = tools.float_round(price, precision_rounding=res['price_round'])
- if context.get('uom'):
- # compute price_surcharge based on reference uom
- factor = product_uom_obj.browse(cr, uid, context.get('uom'), context=context).factor
- else:
- factor = 1.0
- price += (res['price_surcharge'] or 0.0) / factor
- 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
+ for line in seller.pricelist_ids:
+ if line.min_quantity <= qty_in_seller_uom:
+ price = line.price
- else:
- # False means no valid line found ! But we may not raise an
- # exception here because it breaks the search
- price = False
-
- if price:
- results['item_id'] = res['id']
- if 'uom' in context and not uom_price_already_computed:
- product = products_dict[product_id]
- uom = product.uos_id or product.uom_id
- price = product_uom_obj._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}
-
+ if rule.base not in price_types:
+ price_types[rule.base] = price_type_obj.browse(cr, uid, int(rule.base))
+ price_type = price_types[rule.base]
+
+ uom_price_already_computed = True
+ price = currency_obj.compute(cr, uid,
+ price_type.currency_id.id, pricelist.currency_id.id,
+ product_obj._price_get(cr, uid, [product],
+ price_type.field, context=context)[product.id], round=False, context=context)
+
+ if price is not False:
+ price_limit = price
+ price = price * (1.0+(rule.price_discount or 0.0))
+ if rule.price_round:
+ price = tools.float_round(price, precision_rounding=rule.price_round)
- price += (rule.price_surcharge or 0.0)
++ if context.get('uom'):
++ # compute price_surcharge based on reference uom
++ factor = product_uom_obj.browse(cr, uid, context.get('uom'), context=context).factor
++ else:
++ factor = 1.0
++ price += (rule.price_surcharge or 0.0) / factor
+ if rule.price_min_margin:
+ price = max(price, price_limit+rule.price_min_margin)
+ if rule.price_max_margin:
+ price = min(price, price_limit+rule.price_max_margin)
+ break
+
+ if price:
+ if 'uom' in context and not uom_price_already_computed:
+ uom = product.uos_id or product.uom_id
+ price = product_uom_obj._compute_price(cr, uid, uom.id, price, context['uom'])
+
+ results[product.id] = price
return results
def price_get(self, cr, uid, ids, prod_id, qty, partner=None, context=None):