[REF] product, *: clean price computation methods

Old price_get method is now replaced by price_compute and ready for migration.
Explicit parameters for unit of measure and currency have been added to ease
code understanding.
This commit is contained in:
Thibault Delavallée
2016-08-01 15:05:32 +02:00
parent 62d300d889
commit 429cd5970e
7 changed files with 72 additions and 43 deletions
@@ -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
+1 -1
View File
@@ -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']
@@ -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')
+35 -5
View File
@@ -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:
+5 -4
View File
@@ -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,
+28 -24
View File
@@ -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]
+1 -1
View File
@@ -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)