UnboundLocalError: local variable 'diff' referenced before assignment

Asked by sambasivarao

My company currency "INR", i ahve created a price list with currency "USD" . when i try to create a sales order with that price list i am getting "UnboundLocalError: local variable 'diff' referenced before assignment" error

Question information

Language:
English Edit question
Status:
Answered
For:
Odoo Addons (MOVED TO GITHUB) Edit question
Assignee:
No assignee Edit question
Last query:
Last reply:
Revision history for this message
Hamish McIntyre-Bhatty (hamishmb) said :
#1

I could help you, as this looks like a python problem (is it?), but I'd need to see the source code to help you, and if you're a company, I'm not sure you'd want me to see that. When I have issues like this, what I normally do is follow the code's patgh of execution, and see (in this case) if there any ways depending on options chosen, that this variable could be used before it's assigned to. If you can, assign to it as early on in the code as possible.

Hope this helps,
Hamish

Revision history for this message
sambasivarao (gs9783) said :
#2

Thank you Hamish,
I am using openerp version 7.0
I did not change any code in base module.
My company currency "INR", i ahve created a price list with currency "USD" . when i try to create a sales order with that price list i am getting "UnboundLocalError: local variable 'diff' referenced before assignment" error.
This error showing in file base/res/res_currency file

Revision history for this message
Hamish McIntyre-Bhatty (hamishmb) said :
#3

So is this a python problem? If so, python 2 or 3? What is openerp? I can't really fix this unless I see your code, could you deliberately cause the error and show me the last 30 lines of output please?

Thanks,
Hamish

Revision history for this message
sambasivarao (gs9783) said :
#4

Thank you Hamish,
This is the total error i am getting.

OpenERP

OpenERP Server Error

Client Traceback (most recent call last):
  File "/home/kptuser/openerp/web-client/addons/web/http.py", line 204, in dispatch
    response["result"] = method(self, **self.params)
  File "/home/kptuser/openerp/web-client/addons/web/controllers/main.py", line 1139, in exec_workflow
    return req.session.exec_workflow(model, id, signal)
  File "/home/kptuser/openerp/web-client/addons/web/session.py", line 147, in exec_workflow
    r = self.proxy('object').exec_workflow(self._db, self._uid, self._password, model, signal, id)
  File "/home/kptuser/openerp/web-client/addons/web/session.py", line 30, in proxy_method
    result = self.session.send(self.service_name, method, *args)
  File "/home/kptuser/openerp/web-client/addons/web/session.py", line 103, in send
    raise xmlrpclib.Fault(openerp.tools.ustr(e), formatted_info)

Server Traceback (most recent call last):
  File "/home/kptuser/openerp/web-client/addons/web/session.py", line 89, in send
    return openerp.netsvc.dispatch_rpc(service_name, method, args)
  File "/home/kptuser/openerp/server/openerp/netsvc.py", line 292, in dispatch_rpc
    result = ExportService.getService(service_name).dispatch(method, params)
  File "/home/kptuser/openerp/server/openerp/service/web_services.py", line 626, in dispatch
    res = fn(db, uid, *params)
  File "/home/kptuser/openerp/server/openerp/osv/osv.py", line 132, in wrapper
    return f(self, dbname, *args, **kwargs)
  File "/home/kptuser/openerp/server/openerp/osv/osv.py", line 222, in exec_workflow
    res = self.exec_workflow_cr(cr, uid, obj, signal, *args)
  File "/home/kptuser/openerp/server/openerp/osv/osv.py", line 215, in exec_workflow_cr
    return object._workflow_signal(cr, uid, [res_id], signal)[res_id]
  File "/home/kptuser/openerp/server/openerp/osv/orm.py", line 3999, in _workflow_signal
    result[res_id] = wf_service.trg_validate(uid, self._name, res_id, signal, cr)
  File "/home/kptuser/openerp/server/openerp/workflow/wkf_service.py", line 119, in trg_validate
    res2 = instance.validate(cr, id, ident, signal)
  File "/home/kptuser/openerp/server/openerp/workflow/instance.py", line 43, in validate
    workitem.process(cr, witem, ident, signal, force_running, stack=stack)
  File "/home/kptuser/openerp/server/openerp/workflow/workitem.py", line 60, in process
    ok = _split_test(cr, workitem, activity['split_mode'], ident, signal, stack)
  File "/home/kptuser/openerp/server/openerp/workflow/workitem.py", line 175, in _split_test
    _join_test(cr, t[0], t[1], ident, stack)
  File "/home/kptuser/openerp/server/openerp/workflow/workitem.py", line 183, in _join_test
    create(cr,[activity], inst_id, ident, stack)
  File "/home/kptuser/openerp/server/openerp/workflow/workitem.py", line 40, in create
    process(cr, res, ident, stack=stack)
  File "/home/kptuser/openerp/server/openerp/workflow/workitem.py", line 52, in process
    result = _execute(cr, workitem, activity, ident, stack)
  File "/home/kptuser/openerp/server/openerp/workflow/workitem.py", line 106, in _execute
    returned_action = wkf_expr.execute(cr, ident, workitem, activity)
  File "/home/kptuser/openerp/server/openerp/workflow/wkf_expr.py", line 67, in execute
    return _eval_expr(cr, ident, workitem, activity['action'])
  File "/home/kptuser/openerp/server/openerp/workflow/wkf_expr.py", line 57, in _eval_expr
    ret = eval(line, env, nocopy=True)
  File "/home/kptuser/openerp/server/openerp/tools/safe_eval.py", line 241, in safe_eval
    return eval(test_expr(expr, _SAFE_OPCODES, mode=mode), globals_dict, locals_dict)
  File "", line 1, in <module>
  File "/home/kptuser/openerp/server/openerp/osv/orm.py", line 377, in function_proxy
    return attr(self._cr, self._uid, [self._id], *args, **kwargs)
  File "/home/kptuser/openerp/server/openerp/addons/account/account_invoice.py", line 958, in action_move_create
    total, total_currency, iml = self.compute_invoice_totals(cr, uid, inv, company_currency, ref, iml, context=ctx)
  File "/home/kptuser/openerp/server/openerp/addons/account/account_invoice.py", line 845, in compute_invoice_totals
    context=context)
  File "/home/kptuser/openerp/server/openerp/addons/base/res/res_currency.py", line 234, in compute
    rate = self._get_conversion_rate(cr, uid, from_currency, to_currency, context=context)
  File "/home/kptuser/openerp/server/openerp/addons/account/res_currency.py", line 31, in _get_conversion_rate
    rate = super(res_currency_account, self)._get_conversion_rate(cr, uid, from_currency, to_currency, context=context)
  File "/home/kptuser/openerp/server/openerp/addons/base/res/res_currency.py", line 214, in _get_conversion_rate
    return diff
UnboundLocalError: local variable 'diff' referenced before assignment

Revision history for this message
sambasivarao (gs9783) said :
#5

python 2.7.3 version

Revision history for this message
Hamish McIntyre-Bhatty (hamishmb) said :
#6

Good, I use python 2 as well :) Okay, now we're really getting somewhere. Could you now show me the contents of /home/kptuser/openerp/server/openerp/addons/base/res/res_currency.py please? This is more complicated than any of my errors, but at least python's tracebacks are really helpful :) Is there a log or anything like that as well?

Thanks,
Hamish

Revision history for this message
sambasivarao (gs9783) said :
#7

Thank you very much helping us Hamish,
This is code of res_currency.py

# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

import re
import time

from openerp import tools
from openerp.osv import fields, osv
from openerp.tools import float_round, float_is_zero, float_compare
from openerp.tools.translate import _

CURRENCY_DISPLAY_PATTERN = re.compile(r'(\w+)\s*(?:\((.*)\))?')

class res_currency(osv.osv):
    def _current_rate(self, cr, uid, ids, name, arg, context=None):
        return self._current_rate_computation(cr, uid, ids, name, arg, True, context=context)

    def _current_rate_silent(self, cr, uid, ids, name, arg, context=None):
        return self._current_rate_computation(cr, uid, ids, name, arg, False, context=context)

    def _current_rate_computation(self, cr, uid, ids, name, arg, raise_on_no_rate, context=None):
        if context is None:
            context = {}
        res = {}
        if 'date' in context:
            date = context['date']
        else:
            date = time.strftime('%Y-%m-%d')
        date = date or time.strftime('%Y-%m-%d')
        # Convert False values to None ...
        currency_rate_type = context.get('currency_rate_type_id') or None
        # ... and use 'is NULL' instead of '= some-id'.
        operator = '=' if currency_rate_type else 'is'
        for id in ids:
            cr.execute("SELECT currency_id, rate FROM res_currency_rate WHERE currency_id = %s AND name <= %s AND currency_rate_type_id " + operator +" %s ORDER BY name desc LIMIT 1" ,(id, date, currency_rate_type))
            if cr.rowcount:
                id, rate = cr.fetchall()[0]
                res[id] = rate
            elif not raise_on_no_rate:
                res[id] = 0
            else:
                raise osv.except_osv(_('Error!'),_("No currency rate associated for currency %d for the given period" % (id)))
        return res

    _name = "res.currency"
    _description = "Currency"
    _columns = {
        # Note: 'code' column was removed as of v6.0, the 'name' should now hold the ISO code.
        'name': fields.char('Currency', size=32, required=True, help="Currency Code (ISO 4217)"),
        'symbol': fields.char('Symbol', size=4, help="Currency sign, to be used when printing amounts."),
        'rate': fields.function(_current_rate, string='Current Rate', digits=(12,6),
            help='The rate of the currency to the currency of rate 1.'),

        # Do not use for computation ! Same as rate field with silent failing
        'rate_silent': fields.function(_current_rate_silent, string='Current Rate', digits=(12,6),
            help='The rate of the currency to the currency of rate 1 (0 if no rate defined).'),
        'rate_ids': fields.one2many('res.currency.rate', 'currency_id', 'Rates'),
        'accuracy': fields.integer('Computational Accuracy'),
        'rounding': fields.float('Rounding Factor', digits=(12,6)),
        'active': fields.boolean('Active'),
        'company_id':fields.many2one('res.company', 'Company'),
        'date': fields.date('Date'),
        'base': fields.boolean('Base'),
        'position': fields.selection([('after','After Amount'),('before','Before Amount')], 'Symbol Position', help="Determines where the currency symbol should be placed after or before the amount.")
    }
    _defaults = {
        'active': 1,
        'position' : 'after',
        'rounding': 0.01,
        'accuracy': 4,
        'company_id': False,
    }
    _sql_constraints = [
        # this constraint does not cover all cases due to SQL NULL handling for company_id,
        # so it is complemented with a unique index (see below). The constraint and index
        # share the same prefix so that IntegrityError triggered by the index will be caught
        # and reported to the user with the constraint's error message.
        ('unique_name_company_id', 'unique (name, company_id)', 'The currency code must be unique per company!'),
    ]
    _order = "name"

    def init(self, cr):
        # CONSTRAINT/UNIQUE INDEX on (name,company_id)
        # /!\ The unique constraint 'unique_name_company_id' is not sufficient, because SQL92
        # only support field names in constraint definitions, and we need a function here:
        # we need to special-case company_id to treat all NULL company_id as equal, otherwise
        # we would allow duplicate "global" currencies (all having company_id == NULL)
        cr.execute("""SELECT indexname FROM pg_indexes WHERE indexname = 'res_currency_unique_name_company_id_idx'""")
        if not cr.fetchone():
            cr.execute("""CREATE UNIQUE INDEX res_currency_unique_name_company_id_idx
                          ON res_currency
                          (name, (COALESCE(company_id,-1)))""")

    def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
        res = super(res_currency, self).read(cr, user, ids, fields, context, load)
        currency_rate_obj = self.pool.get('res.currency.rate')
        values = res
        if not isinstance(values, list):
            values = [values]
        for r in values:
            if r.__contains__('rate_ids'):
                rates=r['rate_ids']
                if rates:
                    currency_date = currency_rate_obj.read(cr, user, rates[0], ['name'])['name']
                    r['date'] = currency_date
        return res

    def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100):
        if not args:
            args = []
        results = super(res_currency,self)\
            .name_search(cr, user, name, args, operator=operator, context=context, limit=limit)
        if not results:
            name_match = CURRENCY_DISPLAY_PATTERN.match(name)
            if name_match:
                results = super(res_currency,self)\
                    .name_search(cr, user, name_match.group(1), args, operator=operator, context=context, limit=limit)
        return results

    def name_get(self, cr, uid, ids, context=None):
        if not ids:
            return []
        if isinstance(ids, (int, long)):
            ids = [ids]
        reads = self.read(cr, uid, ids, ['name','symbol'], context=context, load='_classic_write')
        return [(x['id'], tools.ustr(x['name'])) for x in reads]

    def round(self, cr, uid, currency, amount):
        """Return ``amount`` rounded according to ``currency``'s
           rounding rules.

           :param browse_record currency: currency for which we are rounding
           :param float amount: the amount to round
           :return: rounded float
        """
        return float_round(amount, precision_rounding=currency.rounding)

    def compare_amounts(self, cr, uid, currency, amount1, amount2):
        """Compare ``amount1`` and ``amount2`` after rounding them according to the
           given currency's precision..
           An amount is considered lower/greater than another amount if their rounded
           value is different. This is not the same as having a non-zero difference!

           For example 1.432 and 1.431 are equal at 2 digits precision,
           so this method would return 0.
           However 0.006 and 0.002 are considered different (returns 1) because
           they respectively round to 0.01 and 0.0, even though
           0.006-0.002 = 0.004 which would be considered zero at 2 digits precision.

           :param browse_record currency: currency for which we are rounding
           :param float amount1: first amount to compare
           :param float amount2: second amount to compare
           :return: (resp.) -1, 0 or 1, if ``amount1`` is (resp.) lower than,
                    equal to, or greater than ``amount2``, according to
                    ``currency``'s rounding.
        """
        return float_compare(amount1, amount2, precision_rounding=currency.rounding)

    def is_zero(self, cr, uid, currency, amount):
        """Returns true if ``amount`` is small enough to be treated as
           zero according to ``currency``'s rounding rules.

           Warning: ``is_zero(amount1-amount2)`` is not always equivalent to
           ``compare_amounts(amount1,amount2) == 0``, as the former will round after
           computing the difference, while the latter will round before, giving
           different results for e.g. 0.006 and 0.002 at 2 digits precision.

           :param browse_record currency: currency for which we are rounding
           :param float amount: amount to compare with currency's zero
        """
        return float_is_zero(amount, precision_rounding=currency.rounding)

    def _get_conversion_rate(self, cr, uid, from_currency, to_currency, context=None):
        if context is None:
            context = {}
        ctx = context.copy()
        ctx.update({'currency_rate_type_id': ctx.get('currency_rate_type_from')})
        from_currency = self.browse(cr, uid, from_currency.id, context=ctx)

        ctx.update({'currency_rate_type_id': ctx.get('currency_rate_type_to')})
        to_currency = self.browse(cr, uid, to_currency.id, context=ctx)

        if from_currency.rate == 0 or to_currency.rate == 0:
            date = context.get('date', time.strftime('%Y-%m-%d'))
            if from_currency.rate == 0:
                currency_symbol = from_currency.symbol
            else:
                currency_symbol = to_currency.symbol
            raise osv.except_osv(_('Error'), _('No rate found \n' \
                    'for the currency: %s \n' \
                    'at the date: %s') % (currency_symbol, date))
        return to_currency.rate/from_currency.rate

    def compute(self, cr, uid, from_currency_id, to_currency_id, from_amount,
                round=True, currency_rate_type_from=False, currency_rate_type_to=False, context=None):
        if not context:
            context = {}
        if not from_currency_id:
            from_currency_id = to_currency_id
        if not to_currency_id:
            to_currency_id = from_currency_id
        xc = self.browse(cr, uid, [from_currency_id,to_currency_id], context=context)
        from_currency = (xc[0].id == from_currency_id and xc[0]) or xc[1]
        to_currency = (xc[0].id == to_currency_id and xc[0]) or xc[1]
        if (to_currency_id == from_currency_id) and (currency_rate_type_from == currency_rate_type_to):
            if round:
                return self.round(cr, uid, to_currency, from_amount)
            else:
                return from_amount
        else:
            context.update({'currency_rate_type_from': currency_rate_type_from, 'currency_rate_type_to': currency_rate_type_to})
            rate = self._get_conversion_rate(cr, uid, from_currency, to_currency, context=context)
            if round:
                return self.round(cr, uid, to_currency, from_amount * rate)
            else:
                return from_amount * rate

res_currency()

class res_currency_rate_type(osv.osv):
    _name = "res.currency.rate.type"
    _description = "Currency Rate Type"
    _columns = {
        'name': fields.char('Name', size=64, required=True, translate=True),
    }

res_currency_rate_type()

class res_currency_rate(osv.osv):
    _name = "res.currency.rate"
    _description = "Currency Rate"

    _columns = {
        'name': fields.date('Date', required=True, select=True),
        'rate': fields.float('Rate', digits=(12,6), help='The rate of the currency to the currency of rate 1'),
        'currency_id': fields.many2one('res.currency', 'Currency', readonly=True),
        'currency_rate_type_id': fields.many2one('res.currency.rate.type', 'Currency Rate Type', help="Allow you to define your own currency rate types, like 'Average' or 'Year to Date'. Leave empty if you simply want to use the normal 'spot' rate type"),
    }
    _defaults = {
        'name': lambda *a: time.strftime('%Y-%m-%d'),
    }
    _order = "name desc"

res_currency_rate()

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

Thank you,
sincerely,
Samba.

Revision history for this message
Hamish McIntyre-Bhatty (hamishmb) said :
#8

Okay, thanks, and you're welcome :) By the looks of it, this calculates conversion rates, but the way they've written is is hard to understand. Just so I can understand what it should do, could you add:

print to_currency.rate/from_currency.rate # ** TEMPORARY CODE **

just before:

return to_currency.rate/from_currency.rate

and show me the ouput from a successful operation?

It seems that the calculation to_currency.rate/from_currency.rate sometimes returns "diff", but it'll help to know what its supposed to result in. I'm having some difficulty understanding the code so far. Sometimes I think mine's messy, but it's nothing like this! Anyhow, it doesn't look like it's too complicated. Also, could you show me the changes you made in any files to add support for USD?

Thanks,
Hamish

Revision history for this message
Hamish McIntyre-Bhatty (hamishmb) said :
#9

Could you also show me the output of lsb_release -a?

STATUS=NEEDSINFO

Thanks,
Hamish

On 12 June 2014 00:41:38 GMT+08:00, Hamish McIntyre-Bhatty <email address hidden> wrote:
>Question #249967 on Ubuntu changed:
>https://answers.launchpad.net/ubuntu/+question/249967
>
>Hamish McIntyre-Bhatty requested more information:
>Okay, thanks, and you're welcome :) By the looks of it, this calculates
>conversion rates, but the way they've written is is hard to understand.
>Just so I can understand what it should do, could you add:
>
>print to_currency.rate/from_currency.rate # ** TEMPORARY CODE **
>
>just before:
>
>return to_currency.rate/from_currency.rate
>
>and show me the ouput from a successful operation?
>
>It seems that the calculation to_currency.rate/from_currency.rate
>sometimes returns "diff", but it'll help to know what its supposed to
>result in. I'm having some difficulty understanding the code so far.
>Sometimes I think mine's messy, but it's nothing like this! Anyhow, it
>doesn't look like it's too complicated. Also, could you show me the
>changes you made in any files to add support for USD?
>
>Thanks,
>Hamish
>
>--
>You received this question notification because you are a direct
>subscriber of the question.

--
Sent from my Android device with K-9 Mail. Please excuse my brevity.

Can you help with this problem?

Provide an answer of your own, or ask sambasivarao for more information if necessary.

To post a message you must log in.