From a1d6c2d6317fd3cbc9af0060c14e4281ce63e11f Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Tue, 27 Sep 2016 09:41:05 +0200 Subject: [PATCH 1/6] [FIX] account: check exchange rate An issue occurs in the following situation: - Define a currency exchange rate at day 1 and day 2 - Create an invoice at day 1, and calculate the taxes. Do not set an invoice date! - Validate the invoice at day 2 The exchange rate for taxes is the rate at day 1, while the exchange rate for other amounts is the rate at day 2. There is actually no way to know what was the rate applied for the taxes at invoice validation. There are two solutions: - recompute the taxes at validation - force the user to set an invoice date and recompute manually the taxes The first solution might have unexpected effects, therefore the second solution is applied. Fixes #13473 opw-688517 --- addons/account/account_invoice.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index 97c4685ea52..580e033e8f3 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -807,11 +807,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 From 4ca06e02558b202183592f4d7c67f76f302bc0d1 Mon Sep 17 00:00:00 2001 From: Jeremy Kersten Date: Mon, 10 Oct 2016 17:21:50 +0200 Subject: [PATCH 2/6] [FIX] fields: add group_operator to be availble in get_description. Web client use fields_get (which one call get_description) to know if a group operator exists. But until now, group_operator are never returned. Without it, the web client cannot display the sub-total except for sum (the fallback in the web client). This commit closes #13713 Todo: do the same on Class Monetary in next branch --- openerp/fields.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openerp/fields.py b/openerp/fields.py index c1982a0e131..cc856351051 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 From 633780a00c33089a5bd6fbbd9388ed37d46d59b9 Mon Sep 17 00:00:00 2001 From: Jeremy Kersten Date: Tue, 11 Oct 2016 11:28:28 +0200 Subject: [PATCH 3/6] [FIX] web,crm: view_list respect group operator defined in the view. Remove useless avg defined on crm view. If you define avg='Average Values' in a view, now we see in the footer of list view the average. --- addons/crm/crm_lead_view.xml | 2 +- addons/web/static/src/js/view_list.js | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index 66e89457aeb..1980a9fe074 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -518,7 +518,7 @@ - + diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index 9fe5426f2cc..a7686f8eebc 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -2182,7 +2182,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); @@ -2209,10 +2208,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; From dd312e0da204ef88134622f71cbe16ed1f22c95e Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Tue, 11 Oct 2016 15:14:05 +0200 Subject: [PATCH 4/6] [FIX] fields: make [(5,)] with computed domain work (#13480) * Failing test for one2many [(5,)] action, when domain is callable. The problem is that `self` inside a callable domain becomes the comodel when at [(5,)]. * [FIX][fields] Make [(5,)] with computed domain work. To reproduce this failure, declare a field like: ``` child_ids = fields.One2many( comodel_name="other.model", domain=lambda self: [("id", "in", self._ids_to_find())], ) ``` Now set some value to it. Now unset them. Impossible because ``self`` becomes ``other.model`` in domain evaluation. --- openerp/addons/test_new_api/models.py | 7 +++++++ openerp/addons/test_new_api/tests/test_new_fields.py | 6 ++++++ openerp/osv/fields.py | 4 +++- 3 files changed, 16 insertions(+), 1 deletion(-) 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 a38949def49..5ff00bfeb40 100644 --- a/openerp/addons/test_new_api/tests/test_new_fields.py +++ b/openerp/addons/test_new_api/tests/test_new_fields.py @@ -470,6 +470,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/osv/fields.py b/openerp/osv/fields.py index 54b39418b9c..e09ad6bb477 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, From 789566a0d05d3832e8f9bc5670049afc636ae785 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Tue, 11 Oct 2016 10:37:34 +0200 Subject: [PATCH 5/6] [FIX] website_livechat: make it auto-install Most people install the im_livechat module and expect it to be enabled automatically on their website. When it doesn't work they try to use the integration JS code and add it to their website layout, which does not work as expected. Setting it as auto-installed solves the issue. --- addons/website_livechat/__openerp__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/website_livechat/__openerp__.py b/addons/website_livechat/__openerp__.py index 1e87f362778..e476e472253 100644 --- a/addons/website_livechat/__openerp__.py +++ b/addons/website_livechat/__openerp__.py @@ -11,6 +11,7 @@ For website built with Odoo CMS, this module include a chat button on your Websi 'author': 'OpenERP SA', 'depends': ['website', 'im_livechat'], 'installable': True, + 'auto_install': True, 'data': [ 'views/website_livechat.xml', 'views/res_config.xml', From ecb6055b0f208001894ae5b05aef01ee367a9099 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Tue, 11 Oct 2016 16:42:54 +0200 Subject: [PATCH 6/6] [FIX] tools/config: avoid sharing init/demo config Theses config options should not share the same dict as `init` dict may be modified by migration scripts to handle new module dependencies. --- openerp/tools/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/tools/config.py b/openerp/tools/config.py index 2a96ed764bb..d6affc6a407 100644 --- a/openerp/tools/config.py +++ b/openerp/tools/config.py @@ -480,7 +480,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()