From fa6e8448d647aee86d609962a6d77fc54a51ca16 Mon Sep 17 00:00:00 2001 From: qsm-odoo Date: Mon, 26 Oct 2015 11:35:28 +0100 Subject: [PATCH 01/13] [IMP] base: description pages containers width Containers inside description pages must be displayed as a container-fluid since they are inside a box which has a smaller width than a container. --- openerp/addons/base/static/src/css/description.css | 3 +++ openerp/addons/base/static/src/css/description.sass | 3 +++ 2 files changed, 6 insertions(+) diff --git a/openerp/addons/base/static/src/css/description.css b/openerp/addons/base/static/src/css/description.css index 33e631bb46e..044cf8db266 100644 --- a/openerp/addons/base/static/src/css/description.css +++ b/openerp/addons/base/static/src/css/description.css @@ -21,6 +21,9 @@ background: white; font-size: 16px; } +.openerp .oe_form .oe_styling_v8 .container { + width: 100%; +} .openerp .oe_form .oe_styling_v8 .oe_websiteonly { display: none; } diff --git a/openerp/addons/base/static/src/css/description.sass b/openerp/addons/base/static/src/css/description.sass index 3bb218a84aa..a70f8f073f3 100644 --- a/openerp/addons/base/static/src/css/description.sass +++ b/openerp/addons/base/static/src/css/description.sass @@ -37,6 +37,9 @@ $v8_anchor_visited_color: rgb(91, 40, 79) background: $v8_bg_color font-size: 16px + .container + width: 100% + .oe_websiteonly display: none From 81566103c0b8e496b54521afa9f77cd2857d6442 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Mon, 26 Oct 2015 11:41:49 +0100 Subject: [PATCH 02/13] [FIX] res_lang: prevent modification of language code --- openerp/addons/base/res/res_lang.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openerp/addons/base/res/res_lang.py b/openerp/addons/base/res/res_lang.py index b9dacf1c2d8..2729d8e1f84 100644 --- a/openerp/addons/base/res/res_lang.py +++ b/openerp/addons/base/res/res_lang.py @@ -194,6 +194,10 @@ class lang(osv.osv): return grouping, thousands_sep, decimal_point def write(self, cr, uid, ids, vals, context=None): + if 'code' in vals: + for lang in self.browse(cr, uid, ids, context): + if lang.code != vals['code']: + raise UserError(_("Language code cannot be modified.")) self._lang_get.clear_cache(self) self._lang_data_get.clear_cache(self) return super(lang, self).write(cr, uid, ids, vals, context) From d4b91ccd3402853a4e5011271072698a74132b92 Mon Sep 17 00:00:00 2001 From: qsm-odoo Date: Mon, 26 Oct 2015 11:35:28 +0100 Subject: [PATCH 03/13] [IMP] base: description pages containers width Containers inside description pages must be displayed as a container-fluid since they are inside a box which has a smaller width than a container. --- openerp/addons/base/static/src/css/description.css | 3 +++ openerp/addons/base/static/src/css/description.sass | 3 +++ 2 files changed, 6 insertions(+) diff --git a/openerp/addons/base/static/src/css/description.css b/openerp/addons/base/static/src/css/description.css index 44f7b5c068b..2b1c60db35e 100644 --- a/openerp/addons/base/static/src/css/description.css +++ b/openerp/addons/base/static/src/css/description.css @@ -21,6 +21,9 @@ background: white; font-size: 16px; } +.openerp .oe_form .oe_styling_v8 .container { + width: 100%; +} .openerp .oe_form .oe_styling_v8 .oe_websiteonly { display: none; } diff --git a/openerp/addons/base/static/src/css/description.sass b/openerp/addons/base/static/src/css/description.sass index 3bb218a84aa..a70f8f073f3 100644 --- a/openerp/addons/base/static/src/css/description.sass +++ b/openerp/addons/base/static/src/css/description.sass @@ -37,6 +37,9 @@ $v8_anchor_visited_color: rgb(91, 40, 79) background: $v8_bg_color font-size: 16px + .container + width: 100% + .oe_websiteonly display: none From 780cfba3c68d8183dc338f48e95c65b95a3c79e3 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 23 Oct 2015 13:46:53 +0200 Subject: [PATCH 04/13] [FIX] fields: make overriding a property field by a function/computed field work Contribution by Adrien Peiffer (ACSONE). --- .../addons/test_inherit/ir.model.access.csv | 1 + openerp/addons/test_inherit/models.py | 32 ++++++++++++++++++- .../addons/test_inherit/tests/test_inherit.py | 16 +++++++++- openerp/osv/fields.py | 1 + 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/openerp/addons/test_inherit/ir.model.access.csv b/openerp/addons/test_inherit/ir.model.access.csv index 9118525cff3..fcf5366a425 100644 --- a/openerp/addons/test_inherit/ir.model.access.csv +++ b/openerp/addons/test_inherit/ir.model.access.csv @@ -1,3 +1,4 @@ "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" access_test_inherit_mother,access_test_inherit_mother,model_test_inherit_mother,,1,1,1,1 access_test_inherit_daughter,access_test_inherit_daughter,model_test_inherit_daughter,,1,1,1,1 +access_test_inherit_property,access_test_inherit_property,model_test_inherit_property,,1,1,1,1 diff --git a/openerp/addons/test_inherit/models.py b/openerp/addons/test_inherit/models.py index a6d5aa96e9c..0875c4fed5a 100644 --- a/openerp/addons/test_inherit/models.py +++ b/openerp/addons/test_inherit/models.py @@ -79,4 +79,34 @@ class res_partner(models.Model): # define a one2many field based on the inherited field partner_id daughter_ids = fields.One2many('test.inherit.daughter', 'partner_id') -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: + +# Check the overriding of property fields by non-property fields. +# Contribution by Adrien Peiffer (ACSONE). +class test_inherit_property(models.Model): + _name = 'test.inherit.property' + + _columns = { + 'name': osv.fields.char('Name', required=True), + 'property_foo': osv.fields.property(string='Foo', type='integer'), + 'property_bar': osv.fields.property(string='Bar', type='integer'), + } + +class test_inherit_property(models.Model): + _inherit = 'test.inherit.property' + + @api.multi + def _get_foo(self, name, arg): + return dict.fromkeys(self.ids, 42) + + _columns = { + # override property_foo with an old-api function field + 'property_foo': osv.fields.function(_get_foo, type='integer', string='Foo'), + } + + # override property_bar with a new-api computed field + property_bar = fields.Integer(compute='_compute_bar', company_dependent=False) + + @api.multi + def _compute_bar(self): + for record in self: + record.property_bar = 42 diff --git a/openerp/addons/test_inherit/tests/test_inherit.py b/openerp/addons/test_inherit/tests/test_inherit.py index cda98193473..ddf5d1424a0 100644 --- a/openerp/addons/test_inherit/tests/test_inherit.py +++ b/openerp/addons/test_inherit/tests/test_inherit.py @@ -78,4 +78,18 @@ class test_inherits(common.TransactionCase): self.assertIn(partner_demo, partners) -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +class test_override_property(common.TransactionCase): + + def test_override_with_function_field(self): + """ test overriding a property field by a function field """ + record = self.env['test.inherit.property'].create({'name': "Stuff"}) + # record.property_foo is not a property field + self.assertEqual(record.property_foo, 42) + self.assertFalse(type(record).property_foo.company_dependent) + + def test_override_with_computed_field(self): + """ test overriding a property field by a computed field """ + record = self.env['test.inherit.property'].create({'name': "Stuff"}) + # record.property_bar is not a property field + self.assertEqual(record.property_bar, 42) + self.assertFalse(type(record).property_bar.company_dependent) diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index fd417d2b7b7..8e6e7bc51a6 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -1399,6 +1399,7 @@ class function(_column): def to_field_args(self): args = super(function, self).to_field_args() args['store'] = bool(self.store) + args['company_dependent'] = False if self._type in ('float',): args['digits'] = self._digits_compute or self._digits elif self._type in ('selection', 'reference'): From 16bf2e94028024b29fed44cef788168c77c47544 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Mon, 26 Oct 2015 17:28:21 +0100 Subject: [PATCH 05/13] [FIX] res_lang: fix confusion between class variable and local variable --- openerp/addons/base/res/res_lang.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openerp/addons/base/res/res_lang.py b/openerp/addons/base/res/res_lang.py index 2729d8e1f84..0c53124263d 100644 --- a/openerp/addons/base/res/res_lang.py +++ b/openerp/addons/base/res/res_lang.py @@ -195,8 +195,8 @@ class lang(osv.osv): def write(self, cr, uid, ids, vals, context=None): if 'code' in vals: - for lang in self.browse(cr, uid, ids, context): - if lang.code != vals['code']: + for rec in self.browse(cr, uid, ids, context): + if rec.code != vals['code']: raise UserError(_("Language code cannot be modified.")) self._lang_get.clear_cache(self) self._lang_data_get.clear_cache(self) From e6a3852c1fb64fd8f1295f99e7933cdec9b7efb7 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Tue, 27 Oct 2015 13:47:08 +0100 Subject: [PATCH 06/13] [FIX] website: child menus without url. This is possible to have a child menu without URL. The condition checking if the `active` class must be added or not must take that into account. opw-653132 --- addons/website/views/website_templates.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml index 2c24ed98d31..5c71266af9d 100644 --- a/addons/website/views/website_templates.xml +++ b/addons/website/views/website_templates.xml @@ -40,7 +40,7 @@
  • From 4513092733f1268342032474636292902cc7c4d4 Mon Sep 17 00:00:00 2001 From: Nicolas Lempereur Date: Wed, 14 Oct 2015 15:29:50 +0200 Subject: [PATCH 07/13] [FIX] web: restoring action from breadcrumb nav When clicking on a breadcrumb taking us to a previous view, we should restore that view action and not keep the current one. closes #9075 opw-652039 note: no forward-port needed --- addons/web/static/src/js/views.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index ec1f720d9b9..49207cd4341 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -128,6 +128,8 @@ instance.web.ActionManager = instance.web.Widget.extend({ if (this.webclient.has_uncommitted_changes()) { return $.Deferred().reject(); } + + self.inner_action = widget.action; var widget_index = this.widgets.indexOf(widget), def = $.when(widget.select_view && widget.select_view(index)); From 35e9fa5c410db78fbad9492afc57b5e7c05a9cb4 Mon Sep 17 00:00:00 2001 From: Joren Van Onder Date: Thu, 22 Oct 2015 10:19:44 +0200 Subject: [PATCH 08/13] [FIX] point_of_sale: handle POS clients with a closed session People sometimes have an open POS client (/pos/web) which is associated to a closed POS session. This causes multiple issues. The most important problem occurs when a user closes the session and opens a new session without refreshing the POS client. When doing this new POS orders will become part of the old, already closed session and no new accounting entries will be generated. In order to fix this we make sure to check that the session that's associated with the order that we get from the client is still open. If it isn't we'll try to find a new, compatible session and add the order there. If we cannot find a compatible session we'll create a new one based on the old, closed one. When creating this new session we bypass the opening_control phase which normally takes care of opening cash control. opw-652356 --- addons/point_of_sale/point_of_sale.py | 33 ++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py index 023b031cc90..3609960dfb0 100644 --- a/addons/point_of_sale/point_of_sale.py +++ b/addons/point_of_sale/point_of_sale.py @@ -586,14 +586,45 @@ class pos_order(osv.osv): 'journal': ui_paymentline['journal_id'], } + # This deals with orders that belong to a closed session. In order + # to recover from this we: + # - assign the order to another compatible open session + # - if that doesn't exist, create a new one + def _get_valid_session(self, cr, uid, order, context=None): + session = self.pool.get('pos.session') + closed_session = session.browse(cr, uid, order['pos_session_id'], context=context) + open_sessions = session.search(cr, uid, [('state', '=', 'opened'), + ('config_id', '=', closed_session.config_id.id), + ('user_id', '=', closed_session.user_id.id)], + limit=1, order="start_at DESC", context=context) + + if open_sessions: + return open_sessions[0] + else: + new_session_id = session.create(cr, uid, { + 'config_id': closed_session.config_id.id, + }, context=context) + new_session = session.browse(cr, uid, new_session_id, context=context) + + # bypass opening_control (necessary when using cash control) + new_session.signal_workflow('open') + + return new_session_id + def _process_order(self, cr, uid, order, context=None): + session = self.pool.get('pos.session').browse(cr, uid, order['pos_session_id'], context=context) + + if session.state == 'closing_control' or session.state == 'closed': + session_id = self._get_valid_session(cr, uid, order, context=context) + session = self.pool.get('pos.session').browse(cr, uid, session_id, context=context) + order['pos_session_id'] = session_id + order_id = self.create(cr, uid, self._order_fields(cr, uid, order, context=context),context) journal_ids = set() for payments in order['statement_ids']: self.add_payment(cr, uid, order_id, self._payment_fields(cr, uid, payments[2], context=context), context=context) journal_ids.add(payments[2]['journal_id']) - session = self.pool.get('pos.session').browse(cr, uid, order['pos_session_id'], context=context) if session.sequence_number <= order['sequence_number']: session.write({'sequence_number': order['sequence_number'] + 1}) session.refresh() From cdc795b2008975bfd7d1b4f408bf92f72812b3ae Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Wed, 28 Oct 2015 16:24:48 +0100 Subject: [PATCH 09/13] [FIX] sale: Taxes mis-aligned with their column label Regression introduced with 71c624163c98cbc1bb24ecb6fa407c4622e7d349. If the discount column is not added in the `tbody` when not part of the group `sale.group_discount_per_so_line`, the head column should not be added either in the `thead` opw-653242 --- addons/sale/views/report_saleorder.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sale/views/report_saleorder.xml b/addons/sale/views/report_saleorder.xml index 58ac0fb52ba..0b1bd08b76e 100644 --- a/addons/sale/views/report_saleorder.xml +++ b/addons/sale/views/report_saleorder.xml @@ -58,7 +58,7 @@ Description Quantity Unit Price - Disc.(%) + Disc.(%) Taxes Price From 3dafb19c96cbc75e99bf199931214189d70a7a1f Mon Sep 17 00:00:00 2001 From: Ludovic Laffineur lla Date: Wed, 28 Oct 2015 15:19:12 +0100 Subject: [PATCH 10/13] [IMP] payment_adyen: add support HPP notifications from adyen It is mandatory to switch to a production account --- addons/payment_adyen/controllers/main.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/addons/payment_adyen/controllers/main.py b/addons/payment_adyen/controllers/main.py index 63b8c9a8fb5..d3a369df369 100644 --- a/addons/payment_adyen/controllers/main.py +++ b/addons/payment_adyen/controllers/main.py @@ -18,7 +18,7 @@ class AdyenController(http.Controller): _return_url = '/payment/adyen/return/' @http.route([ - '/payment/adyen/return/', + '/payment/adyen/return', ], type='http', auth='none') def adyen_return(self, **post): _logger.info('Beginning Adyen form_feedback with post data %s', pprint.pformat(post)) # debug @@ -29,3 +29,17 @@ class AdyenController(http.Controller): custom = json.loads(post.pop('merchantReturnData', '{}')) return_url = custom.pop('return_url', '/') return werkzeug.utils.redirect(return_url) + + @http.route([ + '/payment/adyen/notification', + ], type='http', auth='none', methods=['POST']) + def adyen_notification(self, **post): + tx_id = post.get('merchantReference') and request.registry['payment.transaction'].search(request.cr, SUPERUSER_ID, [('reference', 'in', [post.get('merchantReference')])], limit=1, context=request.context) + if post.get('eventCode') in ['AUTHORISATION'] and tx_id: + tx = request.registry['payment.transaction'].browse(request.cr, SUPERUSER_ID, tx_id, context=request.context) + states = (post.get('merchantReference'), post.get('success'), tx.state) + if (post.get('success') == 'true' and tx.state == 'done') or (post.get('success') == 'false' and tx.state in ['cancel', 'error']): + _logger.info('Notification from Adyen for the reference %s: received %s, state is %s', states) + else: + _logger.warning('Notification from Adyen for the reference %s: received %s but state is %s', states) + return '[accepted]' From ee2b550f3c4be92b42b7a941958eb3aa2b54df66 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Tue, 13 Oct 2015 17:22:01 +0200 Subject: [PATCH 11/13] [FIX] web: radio button display empty value when change record When a selection field with widget="radio" is False, the form view display the previews value for this field. 1 - Create a selection field (ex: [("1", "1"), ("2", "2")]), and display it on a form vue. 2 - Go to the form view (all the record have False value for this field). 3 - Change the value of this field for one record. 4 - In readonly mode, all other records now wrongly display this value (still False in DB or when "edit"). #opw-652002 --- addons/web/static/src/js/view_form.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index b4d43f26b6d..2ed32ab15c2 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -3300,10 +3300,8 @@ instance.web.form.FieldRadio = instance.web.form.AbstractField.extend(instance.w var self = this; this.$el.toggleClass("oe_readonly", this.get('effective_readonly')); this.$("input:checked").prop("checked", false); - if (this.get_value()) { - this.$("input").filter(function () {return this.value == self.get_value();}).prop("checked", true); - this.$(".oe_radio_readonly").text(this.get('value') ? this.get('value')[1] : ""); - } + this.$("input").filter(function () {return this.value == self.get_value();}).prop("checked", true); + this.$(".oe_radio_readonly").text(this.get('value') ? this.get('value')[1] : ""); } }); From 7ffc2916ee8c5a6c97ebb66d1aa59da05f71838c Mon Sep 17 00:00:00 2001 From: Nicolas Lempereur Date: Wed, 28 Oct 2015 14:36:31 +0100 Subject: [PATCH 12/13] [FIX] website: slide position gallery snippet Changing position of a slide from the gallery snippet was not working. saas-6 part of #9284 opw-653061 opw-653167 --- addons/website/static/src/js/website.snippets.gallery.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/website/static/src/js/website.snippets.gallery.js b/addons/website/static/src/js/website.snippets.gallery.js index dede9e07969..2d57211b51c 100644 --- a/addons/website/static/src/js/website.snippets.gallery.js +++ b/addons/website/static/src/js/website.snippets.gallery.js @@ -220,7 +220,7 @@ var $container = this.$target.find(".container:first"); var editor = new website.editor.MediaDialog(this.$target.closest('.o_editable'), null, {select_images: true}); editor.appendTo(document.body); - var index = Math.max(_.map(this.$target.find("img").get(), function (img) { return img.dataset.index | 0; })) + 1; + var index = Math.max(0, _.max(_.map(this.$target.find("img").get(), function (img) { return img.dataset.index | 0; })) + 1); editor.on('saved', this, function (attachments) { for (var i = 0 ; i < attachments.length; i++) { var img = $('') @@ -327,7 +327,6 @@ if (type !== "click") return; var $parent = this.$target.closest("section"); - this.BuildingBlock.create_overlay($parent); var editor = $parent.data('snippet-editor').styles.gallery; var imgs = $parent.find('img').get(); imgs.sort(function (a,b) { return $(a).data('index')-$(b).data('index'); }); From 46266ee11eef53c3590e31467275851e44bc80ad Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Wed, 28 Oct 2015 12:32:50 +0100 Subject: [PATCH 13/13] [FIX] website: _view_obj in multi websites First 'key' and 'website_id' fields must be checked to retrieve the right view. Then if no view is found, it falls back on xml_id. To choose the right ir_ui_view, _view_obj must take into account the 'website_id' set in the context. Inspired from function "get_view_id" in addons/website/models/ir_ui_view.py --- addons/website/models/ir_ui_view.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/addons/website/models/ir_ui_view.py b/addons/website/models/ir_ui_view.py index 75d03986438..fb3f4f6cad9 100644 --- a/addons/website/models/ir_ui_view.py +++ b/addons/website/models/ir_ui_view.py @@ -35,18 +35,16 @@ class view(osv.osv): def _view_obj(self, cr, uid, view_id, context=None): if isinstance(view_id, basestring): - try: - return self.pool['ir.model.data'].xmlid_to_object( - cr, uid, view_id, raise_if_not_found=True, context=context - ) - except: - # Try to fallback on key instead of xml_id + if 'website_id' in context: + domain = [('key', '=', view_id), '|', ('website_id', '=', False), ('website_id', '=', context.get('website_id'))] + rec_id = self.search(cr, uid, domain, order='website_id', context=context) + else: rec_id = self.search(cr, uid, [('key', '=', view_id)], context=context) - if rec_id: - _logger.info("Could not find view with `xml_id' '%s', fallback on `key'" % (view_id)) - return self.browse(cr, uid, rec_id, context=context)[0] - else: - raise + if rec_id: + return self.browse(cr, uid, rec_id, context=context)[0] + else: + return self.pool['ir.model.data'].xmlid_to_object( + cr, uid, view_id, raise_if_not_found=True, context=context) elif isinstance(view_id, (int, long)): return self.browse(cr, uid, view_id, context=context)