Odoo Help

Welcome!

This community is for beginners and experts willing to share their Odoo knowledge. It's not a forum to discuss ideas, but a knowledge base of questions and their answers.

0

infinite loop with on_change?

By
vgze
on 7/16/13, 1:57 PM 3,142 views

Hello, I try add a on_change on two fields (fields A and fields B). I wand that:

  • if user change the fields A that aumaticaly change the fileds B.
  • if user change the fields B that aumaticaly change the fileds A.

I take the 2 events and they work. But when I launch theim together, infinite loop.

It is possible to take what I want to do?

edit:

sale_perso.py:

def price_unit_change(self, cr, uid, ids, price_unit, product):
    result = {}
    if product:
        product_obj = self.pool.get('product.product')
        product_obj = product_obj.browse(cr, uid, product)
        result['coeff'] = price_unit / product_obj.standard_price 
    return {'value': result}

def coeff_change(self, cr, uid, ids, coeff, standard_price):
    result = {}
    result['price_unit'] = coeff * standard_price
    return {'value': result}

sale_perso_view.xml: <openerp> <data>

    <record model="ir.ui.view" id="sale_order_line_type_price">
        <field name="name">sale.order.line.type.price</field>
        <field name="model">sale.order</field>
        <field name="inherit_id" ref="sale.view_order_form"/>
        <field name="arch" type="xml">
           <xpath expr="//field[@name='order_line']/tree//field[@name='price_unit']" position="replace">
                <field name="price_unit"  on_change="price_unit_change(price_unit, product_id)"/>
           </xpath>
           <xpath expr="//field[@name='order_line']/tree//field[@name='price_unit']" position="before">
                <field name="standard_price"/>
                <field name="coeff"  on_change="coeff_change(coeff, standard_price)"/>
            </xpath>
        </field>
    </record>
</data>
</openerp>

fields A and B will always have the same value.
on_change event is triggered when focused loses, pressing TAB or ENTER.
Please post your on_change method and XML on_change field. thanks

KAM, ainur rofiq
on 7/16/13, 3:10 PM

When you change A then on_change(A) is triggered to change B = A. Now B is changed, then on_changed(B) is triggered to change A. At this point there is no change for A, so on_change(A) is not triggered. So there is no infinite loop.

KAM, ainur rofiq
on 7/16/13, 3:50 PM
1
Adrian Merrall
On 7/17/13, 12:56 AM

Hi,

If you have an on_change on field A that returns

{'value': {'B': new_value,},}

This will then trigger an on_change on B and if this returns

{'value': {'A': new_value,},}

which will trigger the on_change on A and around we go.

A couple of thoughts here:

  1. 6.1 or older, add a hidden field to the form and pass/return that into the on_change events so your on_change event terminates if the hidden field is set.
  2. Version 7 - use the context. In on_change A, set a flag in the context to say it has just run. In the on_change for B, if the flag is in the context, remote it and terminate rather than re-setting A.

In Version 7, just pass the context in your XML like this:

<field name="A" on_change="a_change(A, context) />

HTH,

Adrian, Auckland, NZ

enter code here
0
Christophe Combelles
On 7/20/13, 3:45 PM

Hi, I was able to reproduce the issue an found no obvious way to do so. So I entered a new bug report #1203343 (sorry, no "karma" to post the full link :-/)

0
vgze
On 7/17/13, 6:45 AM

I never used the context dictionnary. I try but, the problem is still here.

sale_perso.py:

def price_unit_change(self, cr, uid, ids, price_unit, product, context):
    result = {}
    lang = context.get('lang',False)
    if context.get('cancel_event',False):
        context = {'lang': lang, 'cancel_event': False}
    else:
        context = {'lang': lang, 'cancel_event': True}
        if product:
            product_obj = self.pool.get('product.product')
            product_obj = product_obj.browse(cr, uid, product)
            result['coeff'] = price_unit / product_obj.standard_price 
    return {'value': result}

def coeff_change(self, cr, uid, ids, coeff, product, context):
    result = {}
    lang = context.get('lang',False)
    if context.get('cancel_event',False):
        context = {'lang': lang, 'cancel_event': False}
    else:
        context = {'lang': lang, 'cancel_event': True}
        if product:
            product_obj = self.pool.get('product.product')
            product_obj = product_obj.browse(cr, uid, product)
            result['price_unit'] = coeff * product_obj.standard_price 
    return {'value': result}

sale_perso_view.xml:

  <?xml version="1.0"?>
    <openerp>
    <data>
       <record model="ir.ui.view" id="sale_order_line_type_price">
        <field name="name">sale.order.line.type.price</field>
        <field name="model">sale.order</field>
        <field name="inherit_id" ref="sale.view_order_form"/>
        <field name="arch" type="xml">
           <xpath expr="//field[@name='order_line']/tree//field[@name='price_unit']" position="replace">
                <field name="price_unit"  on_change="price_unit_change(price_unit, product_id, context)"/>
            </xpath>
           <xpath expr="//field[@name='order_line']/tree//field[@name='price_unit']" position="after">
                <field name="type_price"/>
            </xpath>
           <xpath expr="//field[@name='order_line']/tree//field[@name='price_unit']" position="before">
                <field name="list_price"/>
                <field name="prixmat"/>
                <field name="standard_price"/>
                <field name="coeff"  on_change="coeff_change(coeff, product_id, context)"/>
            </xpath>
           <xpath expr="//field[@name='order_line']/tree//field[@name='name']" position="after">
                <field name="marque"/>
            </xpath>
        </field>
    </record>
    </data>
    </openerp>
0
Edison Ibañez
On 8/26/13, 2:45 PM

I had the same problem, solved it updating the context before checking if the previous value was modified

def onchange_comision_value(self, cr, uid, ids, comision, context):
    res = {}
    ctx = dict(context)
    ctx.update
    if context.get('cancel_event', False):
        ctx.update({'cancel_event': False,})
    else:
        ctx.update({'cancel_event': True,})
        for i in self.browse(cr,uid,ids):
            aux_percent = ( comision * 100 ) / i.amount_untaxed
            res['comision_percent'] = aux_percent
    return {'value': res}

def onchange_comision_percent(self, cr, uid, ids, comision_percent, context):
    res = {}
    ctx = dict(context)
    ctx.update
    if context.get('cancel_event', False):
        ctx.update({'cancel_event': False,})
    else:
        ctx.update({'cancel_event': True,})
        for i in self.browse(cr,uid,ids):
            aux_comision = ( i.amount_untaxed * comision_percent ) / 100
            res['comision'] = aux_comision
    return {'value': res}

<field name='comision_percent' class="oe_custom_input  oe_inline" nolabel="1" on_change="onchange_comision_percent(comision_percent, context)" /> %% 
 <field name='comision' string='Comision' class="oe_inline" widget='monetary' options="{'currency_field': 'currency_id'}" on_change="onchange_comision_value(comision, context)" />

I hope that even you need

Greetings.

0
Edison Ibañez
On 8/28/13, 8:30 PM

the solution for your problem, here in the module fleet epecificamente in part:

    def on_change_liter(self, cr, uid, ids, liter, price_per_liter, amount, context=None):
    #need to cast in float because the value receveid from web client maybe an integer (Javascript and JSON do not
    #make any difference between 3.0 and 3). This cause a problem if you encode, for example, 2 liters at 1.5 per
    #liter => total is computed as 3.0, then trigger an onchange that recomputes price_per_liter as 3/2=1 (instead
    #of 3.0/2=1.5)
    #If there is no change in the result, we return an empty dict to prevent an infinite loop due to the 3 intertwine
    #onchange. And in order to verify that there is no change in the result, we have to limit the precision of the 
    #computation to 2 decimal
    liter = float(liter)
    price_per_liter = float(price_per_liter)
    amount = float(amount)
    if liter > 0 and price_per_liter > 0 and round(liter*price_per_liter,2) != amount:
        return {'value' : {'amount' : round(liter * price_per_liter,2),}}
    elif amount > 0 and liter > 0 and round(amount/liter,2) != price_per_liter:
        return {'value' : {'price_per_liter' : round(amount / liter,2),}}
    elif amount > 0 and price_per_liter > 0 and round(amount/price_per_liter,2) != liter:
        return {'value' : {'liter' : round(amount / price_per_liter,2),}}
    else :
        return {}

def on_change_price_per_liter(self, cr, uid, ids, liter, price_per_liter, amount, context=None):
    #need to cast in float because the value receveid from web client maybe an integer (Javascript and JSON do not
    #make any difference between 3.0 and 3). This cause a problem if you encode, for example, 2 liters at 1.5 per
    #liter => total is computed as 3.0, then trigger an onchange that recomputes price_per_liter as 3/2=1 (instead
    #of 3.0/2=1.5)
    #If there is no change in the result, we return an empty dict to prevent an infinite loop due to the 3 intertwine
    #onchange. And in order to verify that there is no change in the result, we have to limit the precision of the 
    #computation to 2 decimal
    liter = float(liter)
    price_per_liter = float(price_per_liter)
    amount = float(amount)
    if liter > 0 and price_per_liter > 0 and round(liter*price_per_liter,2) != amount:
        return {'value' : {'amount' : round(liter * price_per_liter,2),}}
    elif amount > 0 and price_per_liter > 0 and round(amount/price_per_liter,2) != liter:
        return {'value' : {'liter' : round(amount / price_per_liter,2),}}
    elif amount > 0 and liter > 0 and round(amount/liter,2) != price_per_liter:
        return {'value' : {'price_per_liter' : round(amount / liter,2),}}
    else :
        return {}

def on_change_amount(self, cr, uid, ids, liter, price_per_liter, amount, context=None):
    #need to cast in float because the value receveid from web client maybe an integer (Javascript and JSON do not
    #make any difference between 3.0 and 3). This cause a problem if you encode, for example, 2 liters at 1.5 per
    #liter => total is computed as 3.0, then trigger an onchange that recomputes price_per_liter as 3/2=1 (instead
    #of 3.0/2=1.5)
    #If there is no change in the result, we return an empty dict to prevent an infinite loop due to the 3 intertwine
    #onchange. And in order to verify that there is no change in the result, we have to limit the precision of the 
    #computation to 2 decimal
    liter = float(liter)
    price_per_liter = float(price_per_liter)
    amount = float(amount)
    if amount > 0 and liter > 0 and round(amount/liter,2) != price_per_liter:
        return {'value': {'price_per_liter': round(amount / liter,2),}}
    elif amount > 0 and price_per_liter > 0 and round(amount/price_per_liter,2) != liter:
        return {'value': {'liter': round(amount / price_per_liter,2),}}
    elif liter > 0 and price_per_liter > 0 and round(liter*price_per_liter,2) != amount:
        return {'value': {'amount': round(liter * price_per_liter,2),}}
    else :
        return {}

there is this doing the same thing you wantthe solution for your problem, here in the fleet module

Your Answer

Please try to give a substantial answer. If you wanted to comment on the question or answer, just use the commenting tool. Please remember that you can always revise your answers - no need to answer the same question twice. Also, please don't forget to vote - it really helps to select the best questions and answers!

About This Community

This community is for professionals and enthusiasts of our products and services. Read Guidelines

Question tools

0 follower(s)

Stats

Asked: 7/16/13, 1:57 PM
Seen: 3142 times
Last updated: 3/16/15, 8:10 AM