diff --git a/addons/point_of_sale/pos.py b/addons/point_of_sale/pos.py index 769d3a0460e..2e3e9c1445f 100644 --- a/addons/point_of_sale/pos.py +++ b/addons/point_of_sale/pos.py @@ -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 diff --git a/addons/product/pricelist.py b/addons/product/pricelist.py index aae62228748..0dbc9cadcef 100644 --- a/addons/product/pricelist.py +++ b/addons/product/pricelist.py @@ -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()