'Perform an onchange on the given field.
:param values: dictionary mapping field names to values, giving the
current state of modification
:param field_name: name of the modified field, or list of field
names (in view order), or False
:param field_onchange: dictionary mapping field names to their
on_change attribute'

@api.multi
def onchange(self, values, field_name, field_onchange):

    env = self.env
    if isinstance(field_name, list):
        names = field_name
    elif field_name:
        names = [field_name]
    else:
        names = []
    if (not all(((name in self._fields) for name in names))):
        return {}
    secondary = []
    subfields = defaultdict(set)
    for dotname in field_onchange:
        if ('.' in dotname):
            secondary.append(dotname)
            (name, subname) = dotname.split('.')
            subfields[name].add(subname)
    with env.do_in_onchange():
        record = self.new(values)
        values = {name: record[name] for name in record._cache}
        record._origin = self.with_context(__onchange=True)
    with env.do_in_onchange():
        for field_seq in secondary:
            record.mapped(field_seq)
    todo = (list(names) or list(values))
    done = set()
    for name in todo:
        if (name == 'id'):
            continue
        value = record[name]
        field = self._fields[name]
        if ((field.type == 'many2one') and field.delegate and (not value)):
            continue
        record[name] = value
    result = {}
    dirty = set()
    while todo:
        name = todo.pop(0)
        if (name in done):
            continue
        done.add(name)
        with env.do_in_onchange():
            if field_onchange.get(name):
                record._onchange_eval(name, field_onchange[name], result)
            for field_seq in secondary:
                record.mapped(field_seq)
            for (name, oldval) in values.iteritems():
                field = self._fields[name]
                newval = record[name]
                if ((newval != oldval) or ((field.type in ('one2many', 'many2many')) and newval._is_dirty())):
                    todo.append(name)
                    dirty.add(name)
    if (isinstance(field_name, basestring) and (self._fields[field_name].type in ('one2many', 'many2many'))):
        dirty.discard(field_name)
    result['value'] = {name: self._fields[name].convert_to_onchange(record[name], record, subfields[name]) for name in dirty}
    return result
