diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index eedc3b0592d..97c54c0aca1 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -805,11 +805,20 @@ class account_invoice(models.Model): ctx = dict(self._context, lang=inv.partner_id.lang) + company_currency = inv.company_id.currency_id if not inv.date_invoice: + # FORWARD-PORT UP TO SAAS-6 + if inv.currency_id != company_currency and inv.tax_line: + raise except_orm( + _('Warning!'), + _('No invoice date!' + '\nThe invoice currency is not the same than the company currency.' + ' An invoice date is required to determine the exchange rate to apply. Do not forget to update the taxes!' + ) + ) inv.with_context(ctx).write({'date_invoice': fields.Date.context_today(self)}) date_invoice = inv.date_invoice - company_currency = inv.company_id.currency_id # create the analytical lines, one move line per invoice line iml = inv._get_analytic_lines() # check if taxes are all computed diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index 84f8b1d1363..22245ce922f 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -561,7 +561,7 @@ - + diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index 2a1ded90d5a..bfbdf4751e7 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -2190,7 +2190,6 @@ instance.web.list.Column = instance.web.Class.extend({ id: id, tag: tag }); - this.modifiers = attrs.modifiers ? JSON.parse(attrs.modifiers) : {}; delete attrs.modifiers; _.extend(this, attrs); @@ -2217,10 +2216,14 @@ instance.web.list.Column = instance.web.Class.extend({ if (this.type !== 'integer' && this.type !== 'float') { return {}; } - var aggregation_func = this['group_operator'] || 'sum'; - if (!(aggregation_func in this)) { + + var aggregation_func = (this.sum && 'sum') || (this.avg && 'avg') || + (this.max && 'max') || (this.min && 'min') || this.group_operator; + + if (!aggregation_func) { return {}; } + var C = function (fn, label) { this['function'] = fn; this.label = label; diff --git a/addons/website_livechat/__openerp__.py b/addons/website_livechat/__openerp__.py index 2ef1660950c..1d7d814af7f 100644 --- a/addons/website_livechat/__openerp__.py +++ b/addons/website_livechat/__openerp__.py @@ -12,6 +12,7 @@ It also will include the feedback tool for the livechat, and web pages to displa 'author': 'OpenERP SA', 'depends': ['website', 'im_livechat'], 'installable': True, + 'auto_install': True, 'data': [ 'views/website_livechat.xml', 'views/res_config.xml', diff --git a/openerp/addons/test_new_api/models.py b/openerp/addons/test_new_api/models.py index 80af95ab0e4..add22daca2c 100644 --- a/openerp/addons/test_new_api/models.py +++ b/openerp/addons/test_new_api/models.py @@ -145,10 +145,17 @@ class Discussion(models.Model): message_changes = fields.Integer(string='Message changes') important_messages = fields.One2many('test_new_api.message', 'discussion', domain=[('important', '=', True)]) + very_important_messages = fields.One2many( + 'test_new_api.message', 'discussion', + domain=lambda self: self._domain_very_important()) emails = fields.One2many('test_new_api.emailmessage', 'discussion') important_emails = fields.One2many('test_new_api.emailmessage', 'discussion', domain=[('important', '=', True)]) + def _domain_very_important(self): + """Ensure computed O2M domains work as expected.""" + return [("important", "=", True)] + @api.onchange('moderator') def _onchange_moderator(self): self.participants |= self.moderator 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 35cfc30989c..3a731b913a9 100644 --- a/openerp/addons/test_new_api/tests/test_new_fields.py +++ b/openerp/addons/test_new_api/tests/test_new_fields.py @@ -469,6 +469,12 @@ class TestNewFields(common.TransactionCase): message.important = True self.assertIn(message, discussion.important_messages) + # writing on very_important_messages should call its domain method + self.assertIn(message, discussion.very_important_messages) + discussion.write({'very_important_messages': [(5,)]}) + self.assertFalse(discussion.very_important_messages) + self.assertFalse(message.exists()) + class TestMagicFields(common.TransactionCase): diff --git a/openerp/fields.py b/openerp/fields.py index c00bea008dc..f960cd365a4 100644 --- a/openerp/fields.py +++ b/openerp/fields.py @@ -1041,6 +1041,7 @@ class Integer(Field): } _related_group_operator = property(attrgetter('group_operator')) + _description_group_operator = property(attrgetter('group_operator')) _column_group_operator = property(attrgetter('group_operator')) def convert_to_cache(self, value, record, validate=True): @@ -1101,6 +1102,7 @@ class Float(Field): _related_group_operator = property(attrgetter('group_operator')) _description_digits = property(attrgetter('digits')) + _description_group_operator = property(attrgetter('group_operator')) _column_digits = property(lambda self: not callable(self._digits) and self._digits) _column_digits_compute = property(lambda self: callable(self._digits) and self._digits) @@ -1129,7 +1131,7 @@ class _String(Field): _column_translate = property(attrgetter('translate')) _related_translate = property(attrgetter('translate')) _description_translate = property(attrgetter('translate')) - + class Char(_String): """ Basic string field, can be length-limited, usually displayed as a diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index 53a2a6aace0..acc810332d9 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -788,6 +788,7 @@ class one2many(_column): context.update(self._context) if not values: return + original_obj = obj obj = obj.pool[self._obj] rec = obj.browse(cr, user, [], context=context) with rec.env.norecompute(): @@ -819,7 +820,8 @@ class one2many(_column): inverse_field = obj._fields.get(self._fields_id) assert inverse_field, 'Trying to unlink the content of a o2m but the pointed model does not have a m2o' # if the o2m has a static domain we must respect it when unlinking - domain = self._domain(obj) if callable(self._domain) else self._domain + domain = (self._domain(original_obj) + if callable(self._domain) else self._domain) extra_domain = domain or [] ids_to_unlink = obj.search(cr, user, [(self._fields_id,'=',id)] + extra_domain, context=context) # If the model has cascade deletion, we delete the rows because it is the intended behavior, diff --git a/openerp/tools/config.py b/openerp/tools/config.py index 027ef49ffa2..adc51629365 100644 --- a/openerp/tools/config.py +++ b/openerp/tools/config.py @@ -446,7 +446,7 @@ class configmanager(object): for x in self.options['addons_path'].split(',')) self.options['init'] = opt.init and dict.fromkeys(opt.init.split(','), 1) or {} - self.options["demo"] = not opt.without_demo and self.options['init'] or {} + self.options['demo'] = not opt.without_demo and dict(self.options['init']) or {} self.options['update'] = opt.update and dict.fromkeys(opt.update.split(','), 1) or {} self.options['translate_modules'] = opt.translate_modules and map(lambda m: m.strip(), opt.translate_modules.split(',')) or ['all'] self.options['translate_modules'].sort()