diff --git a/addons/calendar/calendar.py b/addons/calendar/calendar.py index 9c8ec4d60a2..e4d0a99c9ec 100644 --- a/addons/calendar/calendar.py +++ b/addons/calendar/calendar.py @@ -1544,75 +1544,64 @@ class calendar_event(osv.Model): return super(calendar_event, self)._name_search(cr, user, name=name, args=args, operator=operator, context=context, limit=limit, name_get_uid=name_get_uid) def write(self, cr, uid, ids, values, context=None): - def _only_changes_to_apply_on_real_ids(field_names): - ''' return True if changes are only to be made on the real ids''' - for field in field_names: - if field in ['start', 'start_date', 'start_datetime', 'stop', 'stop_date', 'stop_datetime', 'active']: - return True - return False - + context = context or {} if not isinstance(ids, (tuple, list)): ids = [ids] - context = context or {} - self._set_date(cr, uid, values, id=ids[0], context=context) + values0 = values - for one_ids in ids: - if isinstance(one_ids, (basestring, int, long)): - if len(str(one_ids).split('-')) == 1: - ids = [int(one_ids)] - else: - ids = [one_ids] + # process events one by one + for event_id in ids: + # make a copy, since _set_date() modifies values depending on event + values = dict(values0) + self._set_date(cr, uid, values, event_id, context=context) - res = False - new_id = False - - # Special write of complex IDS - for event_id in list(ids): - if len(str(event_id).split('-')) == 1: - continue - - ids.remove(event_id) - real_event_id = calendar_id2real_id(event_id) - - # if we are setting the recurrency flag to False or if we are only changing fields that - # should be only updated on the real ID and not on the virtual (like message_follower_ids): - # then set real ids to be updated. - if not values.get('recurrency', True) or not _only_changes_to_apply_on_real_ids(values.keys()): - ids.append(real_event_id) - continue + # special write of complex IDS + real_ids = [] + new_ids = [] + if '-' not in str(event_id): + real_ids = [int(event_id)] else: - data = self.read(cr, uid, event_id, ['start', 'stop', 'rrule', 'duration']) - if data.get('rrule'): - new_id = self._detach_one_event(cr, uid, event_id, values, context=None) + real_event_id = calendar_id2real_id(event_id) - res = super(calendar_event, self).write(cr, uid, [int(event_id) for event_id in ids], values, context=context) - - # set end_date for calendar searching - if values.get('recurrency', True) and values.get('end_type', 'count') in ('count', unicode('count')) and \ - (values.get('rrule_type') or values.get('count') or values.get('start') or values.get('stop')): - for id in ids: - final_date = self._get_recurrency_end_date(cr, uid, id, context=context) - super(calendar_event, self).write(cr, uid, [id], {'final_date': final_date}, context=context) - - attendees_create = False - if values.get('partner_ids', False): - attendees_create = self.create_attendees(cr, uid, ids, context) - - if (values.get('start_date') or values.get('start_datetime', False)) and values.get('active', True): - the_id = new_id or (ids and int(ids[0])) - if the_id: - if attendees_create: - attendees_create = attendees_create[the_id] - mail_to_ids = list(set(attendees_create['old_attendee_ids']) - set(attendees_create['removed_attendee_ids'])) + # if we are setting the recurrency flag to False or if we are only changing fields that + # should be only updated on the real ID and not on the virtual (like message_follower_ids): + # then set real ids to be updated. + blacklisted = any(key in values for key in ('start', 'stop', 'active')) + if not values.get('recurrency', True) or not blacklisted: + real_ids = [real_event_id] else: - mail_to_ids = [att.id for att in self.browse(cr, uid, the_id, context=context).attendee_ids] + data = self.read(cr, uid, event_id, ['start', 'stop', 'rrule', 'duration']) + if data.get('rrule'): + new_ids = [self._detach_one_event(cr, uid, event_id, values, context=None)] - if mail_to_ids: - current_user = self.pool['res.users'].browse(cr, uid, uid, context=context) - if self.pool['calendar.attendee']._send_mail_to_attendees(cr, uid, mail_to_ids, template_xmlid='calendar_template_meeting_changedate', email_from=current_user.email, context=context): - self.message_post(cr, uid, the_id, body=_("A email has been send to specify that the date has been changed !"), subtype="calendar.subtype_invitation", context=context) - return res or True and False + super(calendar_event, self).write(cr, uid, real_ids, values, context=context) + + # set end_date for calendar searching + if values.get('recurrency') and values.get('end_type', 'count') in ('count', unicode('count')) and \ + (values.get('rrule_type') or values.get('count') or values.get('start') or values.get('stop')): + for id in real_ids: + final_date = self._get_recurrency_end_date(cr, uid, id, context=context) + super(calendar_event, self).write(cr, uid, [id], {'final_date': final_date}, context=context) + + attendees_create = False + if values.get('partner_ids', False): + attendees_create = self.create_attendees(cr, uid, real_ids + new_ids, context) + + if (values.get('start_date') or values.get('start_datetime')) and values.get('active', True): + for the_id in real_ids + new_ids: + if attendees_create: + attendees_create = attendees_create[the_id] + mail_to_ids = list(set(attendees_create['old_attendee_ids']) - set(attendees_create['removed_attendee_ids'])) + else: + mail_to_ids = [att.id for att in self.browse(cr, uid, the_id, context=context).attendee_ids] + + if mail_to_ids: + current_user = self.pool['res.users'].browse(cr, uid, uid, context=context) + if self.pool['calendar.attendee']._send_mail_to_attendees(cr, uid, mail_to_ids, template_xmlid='calendar_template_meeting_changedate', email_from=current_user.email, context=context): + self.message_post(cr, uid, the_id, body=_("A email has been send to specify that the date has been changed !"), subtype="calendar.subtype_invitation", context=context) + + return True def create(self, cr, uid, vals, context=None): if context is None: diff --git a/addons/calendar/test/test_calendar_recurrent_event_case2.yml b/addons/calendar/test/test_calendar_recurrent_event_case2.yml index bd5faac0086..0908edfb2a8 100644 --- a/addons/calendar/test/test_calendar_recurrent_event_case2.yml +++ b/addons/calendar/test/test_calendar_recurrent_event_case2.yml @@ -91,5 +91,12 @@ meetings = self.browse(cr, uid, meeting_ids, context) for meeting in meetings: assert meeting.description == 'Review code of the module: sync_google_calendar.', 'Description not changed for id: %s' %meeting.id - - +- + I update the description of two meetings, and check that both have been updated +- + !python {model: calendar.event}: | + DESCRIPTION = "Some description" + ids = [ref('calendar_event_testmeeting0'), ref('calendar_event_sprintreview0')] + self.write(cr, uid, ids, {'description': DESCRIPTION}, context=context) + for i, event in enumerate(self.browse(cr, uid, ids, context)): + assert event.description == DESCRIPTION, "Event %d has not been updated" % i diff --git a/addons/google_drive/google_drive_demo.xml b/addons/google_drive/google_drive_demo.xml index ebe73f40ec2..d222da057fa 100644 --- a/addons/google_drive/google_drive_demo.xml +++ b/addons/google_drive/google_drive_demo.xml @@ -15,7 +15,7 @@ Partner Review - https://docs.google.com/spreadsheet/ccc?key=0Ah2qnrLAoZmUdGRvdVdmS1VoSDctWk1kd18taGZ4ckE#gid=0 + https://docs.google.com/spreadsheet/ccc?key=1eIM-q7PX9wp1vEUUdUvrmAe8V3Do60vXDETulNmI9mA#gid=0 Partner Review %(name)s diff --git a/addons/hr_payroll_account/hr_payroll_account.py b/addons/hr_payroll_account/hr_payroll_account.py index 7a24fb27459..0951d8b2a94 100644 --- a/addons/hr_payroll_account/hr_payroll_account.py +++ b/addons/hr_payroll_account/hr_payroll_account.py @@ -66,7 +66,7 @@ class hr_payslip(osv.osv): def onchange_contract_id(self, cr, uid, ids, date_from, date_to, employee_id=False, contract_id=False, context=None): contract_obj = self.pool.get('hr.contract') res = super(hr_payslip, self).onchange_contract_id(cr, uid, ids, date_from=date_from, date_to=date_to, employee_id=employee_id, contract_id=contract_id, context=context) - journal_id = contract_id and contract_obj.browse(cr, uid, contract_id, context=context).journal_id.id or False + journal_id = contract_id and contract_obj.browse(cr, uid, contract_id, context=context).journal_id.id or (not contract_id and self._get_default_journal(cr, uid, context=None)) res['value'].update({'journal_id': journal_id}) return res diff --git a/addons/mass_mailing/models/mass_mailing_stats.py b/addons/mass_mailing/models/mass_mailing_stats.py index 58ab233c4ae..db5b588649a 100644 --- a/addons/mass_mailing/models/mass_mailing_stats.py +++ b/addons/mass_mailing/models/mass_mailing_stats.py @@ -38,6 +38,8 @@ class MailMailStats(osv.Model): if not self.pool.get(stat.model): continue target = self.pool[stat.model].browse(cr, uid, stat.res_id, context=context) + if not target or not target.exists(): + continue email = '' for email_field in ('email', 'email_from'): if email_field in target and target[email_field]: diff --git a/addons/point_of_sale/static/src/js/chrome.js b/addons/point_of_sale/static/src/js/chrome.js index d2beb9525e9..0c7d90612ca 100644 --- a/addons/point_of_sale/static/src/js/chrome.js +++ b/addons/point_of_sale/static/src/js/chrome.js @@ -443,6 +443,8 @@ var Chrome = PosBaseWidget.extend({ this.logo_click_time = 0; this.logo_click_count = 0; + this.previous_touch_y_coordinate = -1; + this.widget = {}; // contains references to subwidgets instances this.cleanup_dom(); @@ -537,12 +539,40 @@ var Chrome = PosBaseWidget.extend({ } }, + _scrollable: function(element, scrolling_down){ + var $element = $(element); + var scrollable = true; + + if (! scrolling_down && $element.scrollTop() <= 0) { + scrollable = false; + } else if (scrolling_down && $element.scrollTop() + $element.height() >= element.scrollHeight) { + scrollable = false; + } + + return scrollable; + }, + disable_rubberbanding: function(){ + var self = this; + + document.body.addEventListener('touchstart', function(event){ + self.previous_touch_y_coordinate = event.touches[0].clientY; + }); + // prevent the pos body from being scrollable. document.body.addEventListener('touchmove',function(event){ var node = event.target; + var current_touch_y_coordinate = event.touches[0].clientY; + var scrolling_down; + + if (current_touch_y_coordinate < self.previous_touch_y_coordinate) { + scrolling_down = true; + } else { + scrolling_down = false; + } + while(node){ - if(node.classList && node.classList.contains('touch-scrollable')){ + if(node.classList && node.classList.contains('touch-scrollable') && self._scrollable(node, scrolling_down)){ return; } node = node.parentNode; diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 526f6fd31c9..d344019137e 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -757,6 +757,9 @@ class ProcurementOrder(models.Model): taxes = self.product_id.supplier_taxes_id fpos = po.fiscal_position_id taxes_id = fpos.map_tax(taxes).ids if fpos else [] + if taxes_id: + companies = self.env['res.company'].search([('id', 'child_of', self.company_id.id)]) + taxes_id = taxes_id.filtered(lambda x: x.company_id.id in companies).ids price_unit = self.env['account.tax']._fix_tax_included_price(seller.price, self.product_id.supplier_taxes_id, taxes_id) if seller else 0.0 if price_unit and seller and po.currency_id and seller.currency_id != po.currency_id: @@ -802,6 +805,7 @@ class ProcurementOrder(models.Model): 'partner_id': partner.id, 'picking_type_id': self.rule_id.picking_type_id.id, 'company_id': self.company_id.id, + 'currency_id': partner.property_purchase_currency_id.id or self.env.user.company_id.currency_id.id, 'dest_address_id': self.partner_dest_id.id, 'origin': self.origin, 'payment_term_id': partner.property_supplier_payment_term_id.id, diff --git a/addons/stock/stock.py b/addons/stock/stock.py index e36b0a1cf08..1a456288be6 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -1636,7 +1636,7 @@ class stock_picking(models.Model): """ if not context: context = {} - + notrack_context = dict(context, mail_notrack=True) stock_move_obj = self.pool.get('stock.move') self.create_lots_for_picking(cr, uid, ids, context=context) for picking in self.browse(cr, uid, ids, context=context): @@ -1664,7 +1664,7 @@ class stock_picking(models.Model): todo_move_ids.append(move.id) elif float_compare(remaining_qty,0, precision_rounding = move.product_id.uom_id.rounding) > 0 and \ float_compare(remaining_qty, move.product_qty, precision_rounding = move.product_id.uom_id.rounding) < 0: - new_move = stock_move_obj.split(cr, uid, move, remaining_qty, context=context) + new_move = stock_move_obj.split(cr, uid, move, remaining_qty, context=notrack_context) todo_move_ids.append(move.id) #Assign move as it was assigned before toassign_move_ids.append(new_move) @@ -1674,7 +1674,7 @@ class stock_picking(models.Model): self.rereserve_quants(cr, uid, picking, move_ids=todo_move_ids, context=context) self.do_recompute_remaining_quantities(cr, uid, [picking.id], context=context) if todo_move_ids and not context.get('do_only_split'): - self.pool.get('stock.move').action_done(cr, uid, todo_move_ids, context=context) + self.pool.get('stock.move').action_done(cr, uid, todo_move_ids, context=notrack_context) elif context.get('do_only_split'): context = dict(context, split=todo_move_ids) self._create_backorder(cr, uid, picking, context=context) @@ -2103,14 +2103,29 @@ class stock_move(osv.osv): self.pool['procurement.order'].run(cr, uid, res, context=context) return res + def create(self, cr, uid, vals, context=None): + if context is None: + context = {} + picking_obj = self.pool['stock.picking'] + track = not context.get('mail_notrack') and vals.get('picking_id') + if track: + picking = picking_obj.browse(cr, uid, vals['picking_id'], context=context) + initial_values = {picking.id: {'state': picking.state}} + res = super(stock_move, self).create(cr, uid, vals, context=context) + if track: + picking_obj.message_track(cr, uid, [vals['picking_id']], picking_obj.fields_get(cr, uid, ['state'], context=context), initial_values, context=context) + return res + def write(self, cr, uid, ids, vals, context=None): if context is None: context = {} if isinstance(ids, (int, long)): ids = [ids] + picking_obj = self.pool['stock.picking'] # Check that we do not modify a stock.move which is done frozen_fields = set(['product_qty', 'product_uom', 'location_id', 'location_dest_id', 'product_id']) - for move in self.browse(cr, uid, ids, context=context): + moves = self.browse(cr, uid, ids, context=context) + for move in moves: if move.state == 'done': if frozen_fields.intersection(vals): raise UserError(_('Quantities, Units of Measure, Products and Locations cannot be modified on stock moves that have already been processed (except by the Administrator).')) @@ -2147,7 +2162,18 @@ class stock_move(osv.osv): #Note that, for pulled moves we intentionally don't propagate on the procurement. if propagated_changes_dict: self.write(cr, uid, [move.move_dest_id.id], propagated_changes_dict, context=context) - return super(stock_move, self).write(cr, uid, ids, vals, context=context) + track_pickings = not context.get('mail_notrack') and any(field in vals for field in ['state', 'picking_id', 'partially_available']) + if track_pickings: + to_track_picking_ids = set([move.picking_id.id for move in moves if move.picking_id]) + if vals.get('picking_id'): + to_track_picking_ids.add(vals['picking_id']) + to_track_picking_ids = list(to_track_picking_ids) + pickings = picking_obj.browse(cr, uid, to_track_picking_ids, context=context) + initial_values = dict((picking.id, {'state': picking.state}) for picking in pickings) + res = super(stock_move, self).write(cr, uid, ids, vals, context=context) + if track_pickings: + picking_obj.message_track(cr, uid, to_track_picking_ids, picking_obj.fields_get(cr, uid, ['state'], context=context), initial_values, context=context) + return res def onchange_quantity(self, cr, uid, ids, product_id, product_qty, product_uom): """ On change of product quantity finds UoM @@ -2306,7 +2332,7 @@ class stock_move(osv.osv): for state, write_ids in states.items(): if len(write_ids): - self.write(cr, uid, write_ids, {'state': state}) + self.write(cr, uid, write_ids, {'state': state}, context=context) #assign picking in batch for all confirmed move that share the same details for key, move_ids in to_assign.items(): self._picking_assign(cr, uid, move_ids, context=context) @@ -2443,7 +2469,7 @@ class stock_move(osv.osv): """ procurement_obj = self.pool.get('procurement.order') context = context or {} - procs_to_check = [] + procs_to_check = set() for move in self.browse(cr, uid, ids, context=context): if move.state == 'done': raise UserError(_('You cannot cancel a stock move that has been set to \'Done\'.')) @@ -2462,11 +2488,11 @@ class stock_move(osv.osv): self.write(cr, uid, [move.move_dest_id.id], {'state': 'confirmed'}, context=context) if move.procurement_id: # Does the same as procurement check, only eliminating a refresh - procs_to_check.append(move.procurement_id.id) + procs_to_check.add(move.procurement_id.id) res = self.write(cr, uid, ids, {'state': 'cancel', 'move_dest_id': False}, context=context) if procs_to_check: - procurement_obj.check(cr, uid, procs_to_check, context=context) + procurement_obj.check(cr, uid, list(procs_to_check), context=context) return res def _check_package_from_moves(self, cr, uid, ids, context=None): diff --git a/addons/stock_account/wizard/stock_valuation_history.py b/addons/stock_account/wizard/stock_valuation_history.py index 46a72f01ad4..922598e9456 100644 --- a/addons/stock_account/wizard/stock_valuation_history.py +++ b/addons/stock_account/wizard/stock_valuation_history.py @@ -50,7 +50,7 @@ class stock_history(osv.osv): if 'inventory_value' in fields: group_lines = {} for line in res: - domain = line.get('__domain', []) + domain = line.get('__domain', domain) group_lines.setdefault(str(domain), self.search(cr, uid, domain, context=context)) line_ids = set() for ids in group_lines.values(): @@ -75,7 +75,7 @@ class stock_history(osv.osv): histories_dict[(history['product_id'], history['company_id'])] = history['cost'] for line in res: inv_value = 0.0 - lines = group_lines.get(str(line.get('__domain', []))) + lines = group_lines.get(str(line.get('__domain', domain))) for line_id in lines: line_rec = lines_dict[line_id] product = products_dict[line_rec['product_id']] diff --git a/addons/survey/survey.py b/addons/survey/survey.py index 44caa437dc0..7802bf03d7a 100644 --- a/addons/survey/survey.py +++ b/addons/survey/survey.py @@ -355,7 +355,7 @@ class survey_survey(osv.Model): for cell in product(rows.keys(), answers.keys()): res[cell] = 0 for input_line in question.user_input_line_ids: - if input_line.answer_type == 'suggestion' and (not(current_filters) or input_line.user_input_id.id in current_filters): + if input_line.answer_type == 'suggestion' and (not(current_filters) or input_line.user_input_id.id in current_filters) and input_line.value_suggested_row: res[(input_line.value_suggested_row.id, input_line.value_suggested.id)] += 1 if input_line.answer_type == 'text' and (not(current_filters) or input_line.user_input_id.id in current_filters): comments.append(input_line) diff --git a/addons/web/static/src/js/views/pivot_view.js b/addons/web/static/src/js/views/pivot_view.js index 7c74103f211..6d0a75da569 100644 --- a/addons/web/static/src/js/views/pivot_view.js +++ b/addons/web/static/src/js/views/pivot_view.js @@ -799,6 +799,10 @@ var PivotView = View.extend({ nbr_measures: nbr_measures, title: this.title, }; + if(table.measure_row.length + 1 > 256) { + c.show_message(_t("For Excel compatibility, data cannot be exported if there is more than 256 columns.\n\nTip: try to flip axis, filter further or reduce the number of measures.")); + return; + } session.get_file({ url: '/web/pivot/export_xls', data: {data: JSON.stringify(table)}, diff --git a/openerp/addons/base/res/res_company.py b/openerp/addons/base/res/res_company.py index c5bd670140e..8372acc55d4 100644 --- a/openerp/addons/base/res/res_company.py +++ b/openerp/addons/base/res/res_company.py @@ -198,7 +198,7 @@ class res_company(osv.osv): self.cache_restart(cr) return super(res_company, self).create(cr, uid, vals, context=context) obj_partner = self.pool.get('res.partner') - partner_id = obj_partner.create(cr, uid, {'name': vals['name'], 'is_company':True, 'image': vals.get('logo', False)}, context=context) + partner_id = obj_partner.create(cr, uid, {'name': vals['name'], 'is_company':True, 'image': vals.get('logo', False), 'customer': False}, context=context) vals.update({'partner_id': partner_id}) self.cache_restart(cr) company_id = super(res_company, self).create(cr, uid, vals, context=context) diff --git a/openerp/addons/test_new_api/models.py b/openerp/addons/test_new_api/models.py index 9bb88a8dc03..2ebc7f52655 100644 --- a/openerp/addons/test_new_api/models.py +++ b/openerp/addons/test_new_api/models.py @@ -149,6 +149,9 @@ class Message(models.Model): size = fields.Integer(compute='_compute_size', search='_search_size') double_size = fields.Integer(compute='_compute_double_size') discussion_name = fields.Char(related='discussion.name') + author_partner = fields.Many2one( + 'res.partner', compute='_compute_author_partner', + search='_search_author_partner') @api.one @api.constrains('author', 'discussion') @@ -194,6 +197,15 @@ class Message(models.Model): size = self.size self.double_size = self.double_size + size + @api.one + @api.depends('author', 'author.partner_id') + def _compute_author_partner(self): + self.author_partner = author.partner_id + + @api.model + def _search_author_partner(self, operator, value): + return [('author.partner_id', operator, value)] + class MixedModel(models.Model): _name = 'test_new_api.mixed' diff --git a/openerp/addons/test_new_api/tests/test_new_fields.py b/openerp/addons/test_new_api/tests/test_new_fields.py index e3e39ea67c3..ba8cb0516c3 100644 --- a/openerp/addons/test_new_api/tests/test_new_fields.py +++ b/openerp/addons/test_new_api/tests/test_new_fields.py @@ -5,6 +5,7 @@ from datetime import date, datetime from openerp.exceptions import AccessError from openerp.tests import common +from openerp.tools import mute_logger class TestNewFields(common.TransactionCase): @@ -419,7 +420,31 @@ class TestNewFields(common.TransactionCase): self.assertEqual(message.name, "[%s] %s" % (discussion.name, '')) self.assertEqual(message.size, len(BODY)) - def test_41_defaults(self): + @mute_logger('openerp.addons.base.ir.ir_model') + def test_41_new_related(self): + """ test the behavior of related fields on new records. """ + discussion = self.env.ref('test_new_api.discussion_0') + access = self.env.ref('test_new_api.access_discussion') + + # make discussions unreadable for demo user + access.write({'perm_read': False}) + + # create an environment for demo user + demo_env = self.env(user=self.env.ref('base.user_demo')) + self.assertEqual(demo_env.user.login, "demo") + + # create a new message as demo user + values = {'discussion': discussion.id} + message = demo_env['test_new_api.message'].new(values) + self.assertEqual(message.discussion, discussion) + + # read the related field discussion_name + self.assertEqual(message.discussion.env, demo_env) + self.assertEqual(message.discussion_name, discussion.name) + with self.assertRaises(AccessError): + message.discussion.name + + def test_50_defaults(self): """ test default values. """ fields = ['discussion', 'body', 'author', 'size'] defaults = self.env['test_new_api.message'].default_get(fields) @@ -428,6 +453,13 @@ class TestNewFields(common.TransactionCase): defaults = self.env['test_new_api.mixed'].default_get(['number']) self.assertEqual(defaults, {'number': 3.14}) + def test_50_search_many2one(self): + """ test search through a path of computed fields""" + messages = self.env['test_new_api.message'].search( + [('author_partner.name', '=', 'Demo User')]) + self.assertEqual(messages, self.env.ref('test_new_api.message_0_1')) + + class TestMagicFields(common.TransactionCase): diff --git a/openerp/fields.py b/openerp/fields.py index 15ab96d29a4..09c474d5b50 100644 --- a/openerp/fields.py +++ b/openerp/fields.py @@ -560,7 +560,10 @@ class Field(object): # when related_sudo, bypass access rights checks when reading values others = records.sudo() if self.related_sudo else records for record, other in zip(records, others): - # do not switch to another environment if record is a draft one + if not record.id: + # draft records: copy record's cache to other's cache first + for name, value in record._cache.iteritems(): + other[name] = value other, field = self.traverse_related(other if record.id else record) record[self.name] = other[field.name] diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index 7c961760ec4..d30b6a943cd 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -852,12 +852,12 @@ class expression(object): # as after transforming the column, it will go through this loop once again # ---------------------------------------- - elif len(path) > 1 and column._type == 'many2one' and column._auto_join: + elif len(path) > 1 and column and column._type == 'many2one' and column._auto_join: # res_partner.state_id = res_partner__state_id.id leaf.add_join_context(comodel, path[0], 'id', path[0]) push(create_substitution_leaf(leaf, (path[1], operator, right), comodel)) - elif len(path) > 1 and column._type == 'one2many' and column._auto_join: + elif len(path) > 1 and column and column._type == 'one2many' and column._auto_join: # res_partner.id = res_partner__bank_ids.partner_id leaf.add_join_context(comodel, 'id', column._fields_id, path[0]) domain = column._domain(model) if callable(column._domain) else column._domain @@ -868,16 +868,16 @@ class expression(object): push(create_substitution_leaf(leaf, elem, comodel)) push(create_substitution_leaf(leaf, AND_OPERATOR, comodel)) - elif len(path) > 1 and column._auto_join: + elif len(path) > 1 and column and column._auto_join: raise NotImplementedError('_auto_join attribute not supported on many2many column %s' % left) - elif len(path) > 1 and column._type == 'many2one': + elif len(path) > 1 and column and column._type == 'many2one': right_ids = comodel.search(cr, uid, [(path[1], operator, right)], context=dict(context, active_test=False)) leaf.leaf = (path[0], 'in', right_ids) push(leaf) # Making search easier when there is a left operand as column.o2m or column.m2m - elif len(path) > 1 and column._type in ['many2many', 'one2many']: + elif len(path) > 1 and column and column._type in ['many2many', 'one2many']: right_ids = comodel.search(cr, uid, [(path[1], operator, right)], context=context) table_ids = model.search(cr, uid, [(path[0], 'in', right_ids)], context=dict(context, active_test=False)) leaf.leaf = ('id', 'in', table_ids) @@ -894,7 +894,12 @@ class expression(object): domain = [] else: # Let the field generate a domain. - recs = model.browse(cr, uid, [], context) + if len(path) > 1: + right = comodel.search( + cr, uid, [(path[1], operator, right)], + context=context) + operator = 'in' + recs = model.browse(cr, uid, [], context=context) domain = field.determine_domain(recs, operator, right) if not domain: