diff --git a/addons/account/models/account_analytic_line.py b/addons/account/models/account_analytic_line.py index 83faccaa15c..b7120d0c907 100644 --- a/addons/account/models/account_analytic_line.py +++ b/addons/account/models/account_analytic_line.py @@ -35,14 +35,8 @@ class AccountAnalyticLine(models.Model): if not unit or self.product_id.uom_po_id.category_id.id != unit.category_id.id: unit = self.product_id.uom_po_id - ctx = dict(self._context or {}) - if unit: - # price_get() will respect a 'uom' in its context, in order - # to return a default price for those units - ctx['uom'] = unit.id - # Compute based on pricetype - amount_unit = self.product_id.with_context(ctx).price_get('standard_price')[self.product_id.id] + amount_unit = self.product_id.price_compute('standard_price', uom=unit)[self.product_id.id] amount = amount_unit * self.unit_amount or 0.0 result = round(amount, self.currency_id.decimal_places) * -1 self.amount = result diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py index 6e61084afd9..3f081d4f031 100644 --- a/addons/hr_expense/models/hr_expense.py +++ b/addons/hr_expense/models/hr_expense.py @@ -80,7 +80,7 @@ class HrExpense(models.Model): if self.product_id: if not self.name: self.name = self.product_id.display_name or '' - self.unit_amount = self.env['product.template']._price_get(self.product_id, 'standard_price')[self.product_id.id] + self.unit_amount = self.product_id.price_compute('standard_price')[self.product_id.id] self.product_uom_id = self.product_id.uom_id self.tax_ids = self.product_id.supplier_taxes_id account = self.product_id.product_tmpl_id._get_product_accounts()['expense'] diff --git a/addons/membership/wizard/membership_invoice.py b/addons/membership/wizard/membership_invoice.py index 0ec06bad0fc..987ce3078d1 100644 --- a/addons/membership/wizard/membership_invoice.py +++ b/addons/membership/wizard/membership_invoice.py @@ -18,7 +18,7 @@ class membership_invoice(osv.osv_memory): """ if not product_id: return {'value': {'member_price': False}} - return {'value': {'member_price': self.pool.get('product.product').price_get(cr, uid, [product_id])[product_id]}} + return {'value': {'member_price': self.pool.get('product.product').price_compute(cr, uid, [product_id], 'list_price')[product_id]}} def membership_invoice(self, cr, uid, ids, context=None): mod_obj = self.pool.get('ir.model.data') diff --git a/addons/product/models/product.py b/addons/product/models/product.py index 94573e776fc..fa4556e336b 100644 --- a/addons/product/models/product.py +++ b/addons/product/models/product.py @@ -495,12 +495,42 @@ class product_product(osv.osv): result = self.name_get(cr, uid, ids, context=context) return result - # - # Could be overrided for variants matrices prices - # + @api.multi + def price_compute(self, price_type, uom=False, currency=False, company=False): + # TDE FIXME: delegate to template or not ? fields are reencoded here ... + # compatibility about context keys used a bit everywhere in the code + if not uom and self._context.get('uom'): + uom = self.env['product.uom'].browse(self._context['uom']) + if not currency and self._context.get('currency'): + currency = self.env['res.currency'].browse(self._context['currency']) + + products = self + if price_type == 'standard_price': + # standard_price field can only be seen by users in base.group_user + # Thus, in order to compute the sale price from the cost for users not in this group + # We fetch the standard price as the superuser + products = self.with_context(force_company=company and company.id or self._context.get('force_company', self.env.user.company_id.id)).sudo() + + prices = dict.fromkeys(self.ids, 0.0) + for product in products: + prices[product.id] = product[price_type] or 0.0 + if price_type == 'list_price': + prices[product.id] += product.price_extra + + if uom: + prices[product.id] = product.uom_id._compute_price(product.uom_id.id, prices[product.id], uom.id) + + # Convert from current user company currency to asked one + # This is right cause a field cannot be in more than one currency + if currency: + prices[product.id] = product.currency_id.compute(prices[product.id], currency) + + return prices + + + # compatibility to remove after v10 - DEPRECATED def price_get(self, cr, uid, ids, ptype='list_price', context=None): - products = self.browse(cr, uid, ids, context=context) - return self.pool.get("product.template")._price_get(cr, uid, products, ptype=ptype, context=context) + return self.browse(cr, uid, ids, context=context).price_compute(ptype) def copy(self, cr, uid, id, default=None, context=None): if context is None: diff --git a/addons/product/models/product_pricelist.py b/addons/product/models/product_pricelist.py index f634f8f4026..cf2439f3ed2 100644 --- a/addons/product/models/product_pricelist.py +++ b/addons/product/models/product_pricelist.py @@ -183,8 +183,9 @@ class product_pricelist(osv.osv): # Ignored - incompatible UoM in context, use default product UoM pass - # if Public user try to access standard price from website sale, need to call _price_get. - price = self.pool['product.template']._price_get(cr, uid, [product], 'list_price', context=context)[product.id] + # if Public user try to access standard price from website sale, need to call price_compute. + # TDE SURPRISE: product can actually be a template + price = product.price_compute('list_price')[product.id] price_uom_id = qty_uom_id for rule in items: @@ -217,8 +218,8 @@ class product_pricelist(osv.osv): price = self.pool['res.currency'].compute(cr, uid, ptype_src, pricelist.currency_id.id, price_tmp, round=False, context=context) else: # if base option is public price take sale price else cost price of product - # price_get returns the price in the context UoM, i.e. qty_uom_id - price = self.pool['product.template']._price_get(cr, uid, [product], rule.base, context=context)[product.id] + # price_compute returns the price in the context UoM, i.e. qty_uom_id + price = product.price_compute(rule.base)[product.id] convert_to_price_uom = (lambda price: product_uom_obj._compute_price( cr, uid, product.uom_id.id, diff --git a/addons/product/models/product_template.py b/addons/product/models/product_template.py index 67a10ad9002..4bf45d0bbd1 100644 --- a/addons/product/models/product_template.py +++ b/addons/product/models/product_template.py @@ -191,35 +191,39 @@ class product_template(osv.osv): "resized as a 64x64px image, with aspect ratio preserved. "\ "Use this field anywhere a small image is required.") - def _price_get(self, cr, uid, products, ptype='list_price', context=None): - if context is None: - context = {} + @api.multi + def price_compute(self, price_type, uom=False, currency=False, company=False): + # TDE FIXME: delegate to template or not ? fields are reencoded here ... + # compatibility about context keys used a bit everywhere in the code + if not uom and self._context.get('uom'): + uom = self.env['product.uom'].browse(self._context['uom']) + if not currency and self._context.get('currency'): + currency = self.env['res.currency'].browse(self._context['currency']) - res = {} - product_uom_obj = self.pool.get('product.uom') - for product in products: + templates = self + if price_type == 'standard_price': # standard_price field can only be seen by users in base.group_user # Thus, in order to compute the sale price from the cost for users not in this group # We fetch the standard price as the superuser - if ptype != 'standard_price': - res[product.id] = product[ptype] or 0.0 - else: - company_id = context.get('force_company') or product.env.user.company_id.id - product = product.with_context(force_company=company_id) - res[product.id] = res[product.id] = product.sudo()[ptype] - if ptype == 'list_price': - res[product.id] += product._name == "product.product" and product.price_extra or 0.0 - if 'uom' in context: - uom = product.uom_id - res[product.id] = product_uom_obj._compute_price(cr, uid, - uom.id, res[product.id], context['uom']) + templates = self.with_context(force_company=company and company.id or self._context.get('force_company', self.env.user.company_id.id)).sudo() + + prices = dict.fromkeys(self.ids, 0.0) + for template in templates: + prices[template.id] = template[price_type] or 0.0 + + if uom: + prices[template.id] = template.uom_id._compute_price(template.uom_id.id, prices[template.id], uom.id) + # Convert from current user company currency to asked one - if 'currency_id' in context: - # Take current user company currency. - # This is right cause a field cannot be in more than one currency - res[product.id] = self.pool.get('res.currency').compute(cr, uid, product.currency_id.id, - context['currency_id'], res[product.id], context=context) - return res + # This is right cause a field cannot be in more than one currency + if currency: + prices[template.id] = template.currency_id.compute(prices[template.id], currency) + + return prices + + # compatibility to remove after v10 - DEPRECATED + def _price_get(self, cr, uid, products, ptype='list_price', context=None): + return products.price_compute(ptype) def _get_uom_id(self, cr, uid, *args): return self.pool["product.uom"].search(cr, uid, [], limit=1, order='id')[0] diff --git a/addons/product/tests/test_pricelist.py b/addons/product/tests/test_pricelist.py index 1aa3c6d8c9e..063b71124b6 100644 --- a/addons/product/tests/test_pricelist.py +++ b/addons/product/tests/test_pricelist.py @@ -101,7 +101,7 @@ class TestPricelist(TransactionCase): unit_price = self.registry('product.pricelist').price_get(cr, uid, [pricelist_id], spam_id, qty, context={'uom': uom})[pricelist_id] - self.assertAlmostEqual(unit_price, expected_unit_price, msg='Computed unit price is wrong') + self.assertAlmostEqual(unit_price, expected_unit_price) # Test prices - they are *per unit*, the quantity is only here to match the pricelist rules! test_unit_price(2, kg, tonne_price / 1000.0)