from openerp import SUPERUSER_ID
from openerp import tools
-from openerp.osv import osv, orm, fields
+from openerp.osv import osv, orm, fields, expression
from openerp.tools.translate import _
import openerp.addons.decimal_precision as dp
+from openerp.tools.float_utils import float_round
def ean_checksum(eancode):
"""returns the checksum of an ean string of length 13, returns -1 if the string has the wrong length"""
'name': fields.char('Unit of Measure', required=True, translate=True),
'category_id': fields.many2one('product.uom.categ', 'Category', required=True, ondelete='cascade',
help="Conversion between Units of Measure can only occur if they belong to the same category. The conversion will be made based on the ratios."),
- 'factor': fields.float('Ratio', required=True,digits=(12, 12),
+ 'factor': fields.float('Ratio', required=True, digits=0, # force NUMERIC with unlimited precision
help='How much bigger or smaller this unit is compared to the reference Unit of Measure for this category:\n'\
'1 * (reference unit) = ratio * (this unit)'),
- 'factor_inv': fields.function(_factor_inv, digits=(12,12),
+ 'factor_inv': fields.function(_factor_inv, digits=0, # force NUMERIC with unlimited precision
fnct_inv=_factor_inv_write,
string='Bigger Ratio',
help='How many times this Unit of Measure is bigger than the reference Unit of Measure in this category:\n'\
raise osv.except_osv(_('Error!'), _('Conversion from Product UoM %s to Default UoM %s is not possible as they both belong to different Category!.') % (from_unit.name,to_unit.name,))
else:
return qty
- amount = qty / from_unit.factor
+ # First round to the precision of the original unit, so that
+ # float representation errors do not bias the following ceil()
+ # e.g. with 1 / (1/12) we could get 12.0000048, ceiling to 13!
+ amount = float_round(qty/from_unit.factor, precision_rounding=from_unit.rounding)
if to_unit:
amount = ceiling(amount * to_unit.factor, to_unit.rounding)
return amount
unlink_ids = []
unlink_product_tmpl_ids = []
for product in self.browse(cr, uid, ids, context=context):
+ # Check if product still exists, in case it has been unlinked by unlinking its template
+ if not product.exists():
+ continue
tmpl_id = product.product_tmpl_id.id
# Check if the product is last product of this template
other_product_ids = self.search(cr, uid, [('product_tmpl_id', '=', tmpl_id), ('id', '!=', product.id)], context=context)
return (d['id'], name)
partner_id = context.get('partner_id', False)
+ if partner_id:
+ partner_ids = [partner_id, self.pool['res.partner'].browse(cr, user, partner_id, context=context).commercial_partner_id.id]
+ else:
+ partner_ids = []
# all user don't have access to seller and partner
# check access and use superuser
result = []
for product in self.browse(cr, SUPERUSER_ID, ids, context=context):
- sellers = filter(lambda x: x.name.id == partner_id, product.seller_ids)
+ sellers = partner_ids and filter(lambda x: x.name.id in partner_ids, product.seller_ids) or []
if sellers:
for s in sellers:
mydict = {
if not args:
args = []
if name:
- ids = self.search(cr, user, [('default_code','=',name)]+ args, limit=limit, context=context)
- if not ids:
- ids = self.search(cr, user, [('ean13','=',name)]+ args, limit=limit, context=context)
- if not ids:
+ positive_operators = ['=', 'ilike', '=ilike', 'like', '=like']
+ ids = []
+ if operator in positive_operators:
+ ids = self.search(cr, user, [('default_code','=',name)]+ args, limit=limit, context=context)
+ if not ids:
+ ids = self.search(cr, user, [('ean13','=',name)]+ args, limit=limit, context=context)
+ if not ids and operator not in expression.NEGATIVE_TERM_OPERATORS:
# Do not merge the 2 next lines into one single search, SQL search performance would be abysmal
# on a database with thousands of matching products, due to the huge merge+unique needed for the
# OR operator (and given the fact that the 'name' lookup results come from the ir.translation table
limit2 = (limit - len(ids)) if limit else False
ids.update(self.search(cr, user, args + [('name', operator, name)], limit=limit2, context=context))
ids = list(ids)
- if not ids:
+ elif not ids and operator in expression.NEGATIVE_TERM_OPERATORS:
+ ids = self.search(cr, user, args + ['&', ('default_code', operator, name), ('name', operator, name)], limit=limit, context=context)
+ if not ids and operator in positive_operators:
ptrn = re.compile('(\[(.*?)\])')
res = ptrn.search(name)
if res:
price_type_currency_id = pricetype_obj.browse(cr,uid,price_type_id).currency_id.id
res = {}
+ company_id = self.pool['res.users'].read(cr, uid, uid, ['company_id'], context=context)['company_id'][0]
# standard_price field can only be seen by users in base.group_user
# Thus, in order to compute the sale price from the cost price 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:
- res[product.id] = self.read(cr, SUPERUSER_ID, product.id, [ptype], context=context)[ptype] or 0.0
+ res[product.id] = self.read(cr, SUPERUSER_ID, product.id, [ptype], context=dict(context, force_company=company_id))[ptype] or 0.0
product_uom_obj = self.pool.get('product.uom')
for product in products:
return res
def copy(self, cr, uid, id, default=None, context=None):
- context = context or {}
- default = dict(default or {})
-
- # Craft our own `<name> (copy)` in en_US (self.copy_translation()
- # will do the other languages).
- context_wo_lang = dict(context or {})
- context_wo_lang.pop('lang', None)
- product = self.browse(cr, uid, id, context_wo_lang)
+ if context is None:
+ context={}
+
+ product = self.browse(cr, uid, id, context)
if context.get('variant'):
# if we copy a variant or create one, we keep the same template
default['product_tmpl_id'] = product.product_tmpl_id.id
'res_id': product.product_tmpl_id.id,
'target': 'new'}
+ def _compute_uos_qty(self, cr, uid, ids, uom, qty, uos, context=None):
+ '''
+ Computes product's invoicing quantity in UoS from quantity in UoM.
+ Takes into account the
+ :param uom: Source unit
+ :param qty: Source quantity
+ :param uos: Target UoS unit.
+ '''
+ if not uom or not qty or not uos:
+ return qty
+ uom_obj = self.pool['product.uom']
+ product_id = ids[0] if isinstance(ids, (list, tuple)) else ids
+ product = self.browse(cr, uid, product_id, context=context)
+ if isinstance(uos, (int, long)):
+ uos = uom_obj.browse(cr, uid, uos, context=context)
+ if isinstance(uom, (int, long)):
+ uom = uom_obj.browse(cr, uid, uom, context=context)
+ if product.uos_id: # Product has UoS defined
+ # We cannot convert directly between units even if the units are of the same category
+ # as we need to apply the conversion coefficient which is valid only between quantities
+ # in product's default UoM/UoS
+ qty_default_uom = uom_obj._compute_qty_obj(cr, uid, uom, qty, product.uom_id) # qty in product's default UoM
+ qty_default_uos = qty_default_uom * product.uos_coeff
+ return uom_obj._compute_qty_obj(cr, uid, product.uos_id, qty_default_uos, uos)
+ else:
+ return uom_obj._compute_qty_obj(cr, uid, uom, qty, uos)
+
+
class product_packaging(osv.osv):
_name = "product.packaging"