Posts

Avoiding Infinite OnChange Loop

OnChange loop happens when 2 fields with onchange() method which return result with some decimal point residue to another field, which trigger another call and another call.

The solution is to round() method before return value, ensure there is not decimal point residue to trigger next call.

    def onchange_amount(self, cr, uid, ids, field, src_amount, dst_amount, exchange_rate):
        res = {'value':{}}
        if field == 'src_amount':
            res['value']['src_amount'] = src_amount
            res['value']['dst_amount'] = round(src_amount * exchange_rate, 2) # Round to avoid infinite looping
            res['value']['exchange_rate'] = exchange_rate
            res['value']['exchange_inv'] = exchange_rate and 1.0 / exchange_rate or 0.0
        elif field == 'dst_amount':
            res['value']['src_amount'] = round(exchange_rate and dst_amount / exchange_rate or 0.0, 2)  # Round to avoid infinite looping
            res['value']['dst_amount'] = dst_amount
            res['value']['exchange_rate'] = exchange_rate
            res['value']['exchange_inv'] = exchange_rate and 1.0 / exchange_rate or 0.0
        elif field == 'exchange_rate':
            res['value']['src_amount'] = src_amount
            res['value']['dst_amount'] = round(src_amount * exchange_rate, 2)  # Round to avoid infinite looping
            res['value']['exchange_rate'] = exchange_rate
            res['value']['exchange_inv'] = exchange_rate and 1.0 / exchange_rate or 0.0
        return res

Coding Tips (OpenERP / Python)

Solving problem inheriting stock.picking.out

Normally, we would want to either inherit stock.picking.out directly or inherit from stock.picking and hoping it will propagate fetures to stock.picking.out.

But there seem to be a framework bug regarding this issue here

In short, the workaround is to inherit fields from stock.picking first, and then do it repeat it again in stock.picking.out.

Code sample from addons/delivery/stock.py

# Overloaded stock_picking to manage carriers :
class stock_picking(osv.osv):
    _inherit = 'stock.picking'

    def _cal_weight(self, cr, uid, ids, name, args, context=None):
        res = {}
        # Code goes here
        return res

    _columns = {
        'carrier_id':fields.many2one("delivery.carrier","Carrier"),
        }

stock_picking()

# Redefinition of the new fields in order to update the model stock.picking.out in the orm
# FIXME: this is a temporary workaround because of a framework bug (ref: lp996816). It should be removed as soon as
#        the bug is fixed
class stock_picking_out(osv.osv):
    _inherit = 'stock.picking.out'

    def _cal_weight(self, cr, uid, ids, name, args, context=None):
        return self.pool.get('stock.picking')._cal_weight(cr, uid, ids, name, args, context=context)

    _columns = {
        'carrier_id':fields.many2one("delivery.carrier","Carrier"),
        }
stock_picking_out()

Dynamic Domain Filter

Domain filter is available both in Object and View layer. But sometime it is not dynamic enough. Here is a tip.

For exmample, on_change event of product field we want to filter partner list with some complex function. While domain do not support using function we can eject domain forcefully when writing on_change method.

def onchange_product_id()

        # Dynamic domain filter for partner_id
        dom = {'partner_id':  [('id', 'in', get_product_supplier_list(cr, uid, product_id))]}
        res['domain'].update(dom)

        return res

While get_product_supplier_list() is the function that return ids.