From 52f3a5ecc4fea63609ba105f6b504eebf5e52377 Mon Sep 17 00:00:00 2001 From: Ward Marissen Date: Tue, 6 Feb 2018 13:25:04 +0100 Subject: [PATCH 01/13] [CLA] signature for Ward Marissen Closes #22841 --- doc/cla/individual/wardm95.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 doc/cla/individual/wardm95.md diff --git a/doc/cla/individual/wardm95.md b/doc/cla/individual/wardm95.md new file mode 100644 index 00000000000..46396fd4e05 --- /dev/null +++ b/doc/cla/individual/wardm95.md @@ -0,0 +1,11 @@ +Belgium, 06/02/2018 + +I hereby agree to the terms of the Odoo Individual Contributor License +Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this +declaration. + +Signed, + +Ward Marissen ward.marissen@gmail.com https://github.com/wardm95 From a0f8ee92c3692f7d12652ec38a5e62f2cbddf0a8 Mon Sep 17 00:00:00 2001 From: "Lucas Perais (lpe)" Date: Wed, 14 Feb 2018 10:52:13 +0100 Subject: [PATCH 02/13] [FIX] website_quote: require payment field should be given by the template When having a default template for quotation Doing a sale order through the website Before this commit: The field require_payment on the SO was not matching the one on the template This leads to confusion at config time when on the backend something is set on the template as default while not matching what actually happens After this commit: The field require_payment on the SO is computed from the template OPW 807946 --- addons/website_quote/models/sale_order.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/addons/website_quote/models/sale_order.py b/addons/website_quote/models/sale_order.py index 88a2015566a..b66e09f87c4 100644 --- a/addons/website_quote/models/sale_order.py +++ b/addons/website_quote/models/sale_order.py @@ -68,7 +68,14 @@ class SaleOrder(models.Model): (0, 'Not mandatory on website quote validation'), (1, 'Immediate after website order validation'), (2, 'Immediate after website order validation and save a token'), - ], 'Payment', help="Require immediate payment by the customer when validating the order from the website quote") + ], 'Payment', help="Require immediate payment by the customer when validating the order from the website quote", compute='_compute_require_payment', store=True) + + @api.one + @api.depends('template_id') + def _compute_require_payment(self): + # Do not overwrite if the field is already set + if self.template_id and (self.require_payment is None or self.require_payment is False): + self.require_payment = self.template_id.require_payment @api.multi def copy(self, default=None): From bcc088c348abb15d8daae18d6431e5367869fb47 Mon Sep 17 00:00:00 2001 From: Romain Derie Date: Thu, 8 Feb 2018 15:48:50 +0100 Subject: [PATCH 03/13] [FIX] web, web_editor: Better email validation with regex (JS) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit improves link creation in web editor. Before this commit, anything with a @​ would be considered as valid email which is obviously not the case. It would then be prefixed by 'mailto:'. This was causing 2 issues: - URL with @​ symbol in it would have 'mailto:' added as prefix, like google maps coordinates. eg: mailto:http://www.google.com/maps/whatever/@​312211.22,321321 - Incorrect emails would go through (eg: @​@​@​, ko@​k@​k@​, mail@​mail) Now, we will try to detect and identify valid emails from invalid emails from URLs. It will also handle 'mailto:' directly typed in the input. This commit comes with it's counterpart in enterprise https://github.com/odoo/enterprise/pull/1859 This closes #11984, closes #22936 Note that there is a WIP in master to use this new is_email function everywhere we try to validate emails. It will aim to uniformize email validation in odoo's javascript that are often performed with different mean here and there, some more permissive than others, some only checking for @​. --- addons/web/static/src/js/framework/utils.js | 11 +++++++++++ addons/web_editor/static/src/js/widgets.js | 13 ++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/addons/web/static/src/js/framework/utils.js b/addons/web/static/src/js/framework/utils.js index 3e9bd87f8de..cfb9d16af22 100644 --- a/addons/web/static/src/js/framework/utils.js +++ b/addons/web/static/src/js/framework/utils.js @@ -418,6 +418,16 @@ function swap(array, elem1, elem2) { array[i1] = elem2; } +function is_email(value, allow_mailto) { + // http://stackoverflow.com/questions/46155/validate-email-address-in-javascript + var re; + if (allow_mailto) { + re = /^(mailto:)?(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i; + } else { + re = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i; + } + return re.test(value); +} return { divmod: divmod, @@ -445,6 +455,7 @@ return { xor: xor, DropMisordered: DropMisordered, swap: swap, + is_email: is_email, }; }); diff --git a/addons/web_editor/static/src/js/widgets.js b/addons/web_editor/static/src/js/widgets.js index bf72f25bc74..f23e196997a 100644 --- a/addons/web_editor/static/src/js/widgets.js +++ b/addons/web_editor/static/src/js/widgets.js @@ -4,6 +4,7 @@ odoo.define('web_editor.widget', function (require) { var core = require('web.core'); var ajax = require('web.ajax'); var Widget = require('web.Widget'); +var utils = require('web.utils'); var base = require('web_editor.base'); var rte = require('web_editor.rte'); @@ -1050,6 +1051,7 @@ var LinkDialog = Dialog.extend({ if (!$e.length) { $e = this.$('input.url-source:first'); } + $e.closest('.form-group').removeClass('has-error'); var val = $e.val(); var label = this.$('#link-text').val() || val; @@ -1070,8 +1072,7 @@ var LinkDialog = Dialog.extend({ var size = this.$("input[name='link-style-size']:checked").val() || ''; var classes = (this.data.className || "") + (style && style.length ? " btn " : "") + style + " " + size; var isNewWindow = this.$('input.window-new').prop('checked'); - - if ($e.hasClass('email-address') && $e.val().indexOf("@") !== -1) { + if ($e.hasClass('email-address') && (_.str.startsWith(val, 'mailto:') || (val.indexOf("@") !== -1 && !_.str.startsWith(val, 'http') && !_.str.startsWith(val, 'www')))) { self.get_data_buy_mail(def, $e, isNewWindow, label, classes, test); } else { self.get_data_buy_url(def, $e, isNewWindow, label, classes, test); @@ -1080,7 +1081,13 @@ var LinkDialog = Dialog.extend({ }, get_data_buy_mail: function (def, $e, isNewWindow, label, classes, test) { var val = $e.val(); - def.resolve(val.indexOf("mailto:") === 0 ? val : 'mailto:' + val, isNewWindow, label, classes); + if (utils.is_email(val, true)) { + def.resolve(val.indexOf("mailto:") === 0 ? val : 'mailto:' + val, isNewWindow, label, classes); + } else { + $e.closest('.form-group').addClass('has-error'); + $e.focus(); + def.reject(); + } }, get_data_buy_url: function (def, $e, isNewWindow, label, classes, test) { def.resolve($e.val(), isNewWindow, label, classes); From c0f15c57fe54c3ab9ad3278c92be92f9e6552d79 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Tue, 13 Feb 2018 15:05:14 +0100 Subject: [PATCH 04/13] [FIX] mrp: Changing the routing on a bom When changing the routing on a bom, "Consumed in Operation" indicated in some lines of the BoM did not correspond to the routing used. So the system did not link it to a workorder and it was never consumed when a MO was processed. Now the "Consumed in operation" are reset when the routing is changed. opw:807560 --- addons/mrp/models/mrp_bom.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/addons/mrp/models/mrp_bom.py b/addons/mrp/models/mrp_bom.py index d113b8d0f52..ec90c55d020 100644 --- a/addons/mrp/models/mrp_bom.py +++ b/addons/mrp/models/mrp_bom.py @@ -82,6 +82,11 @@ class MrpBom(models.Model): if self.product_tmpl_id: self.product_uom_id = self.product_tmpl_id.uom_id.id + @api.onchange('routing_id') + def onchange_routing_id(self): + for line in self.bom_line_ids: + line.operation_id = False + @api.multi def name_get(self): return [(bom.id, '%s%s' % (bom.code and '%s: ' % bom.code or '', bom.product_tmpl_id.display_name)) for bom in self] From 50ccc1e3dd08c9201024be6210236fb60af92836 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 24 Mar 2017 11:32:09 +0100 Subject: [PATCH 05/13] [FIX] mail: know who added somebody to the channel When X is adding Y in a discuss channel, the recieved notification was: - if read in discuss: from Y, "joined the channel" (no information who added him) - if read in email: from X, "joined the channel" (misleading, think X was the one joining the channel) The problem comes from the author_id in the first case and the email_from in the second, both containing a different value. Also correct a bad api.multi parameter --- addons/mail/i18n/mail.pot | 6 ++++++ addons/mail/models/mail_channel.py | 10 +++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/addons/mail/i18n/mail.pot b/addons/mail/i18n/mail.pot index 977702bd921..6698c5a0515 100644 --- a/addons/mail/i18n/mail.pot +++ b/addons/mail/i18n/mail.pot @@ -167,6 +167,12 @@ msgid "

\n" " You can insert canned responses in your message by typing :shortcut.
" msgstr "" +#. module: mail +#: code:addons/mail/models/mail_channel.py:475 +#, python-format +msgid "
%(author)s invited %(new_partner)s to #%(channel_name)s
" +msgstr "" + #. module: mail #: code:addons/mail/models/mail_channel.py:550 #, python-format diff --git a/addons/mail/models/mail_channel.py b/addons/mail/models/mail_channel.py index 7012f621849..06a272948ea 100644 --- a/addons/mail/models/mail_channel.py +++ b/addons/mail/models/mail_channel.py @@ -471,7 +471,15 @@ class Channel(models.Model): partners_to_add = partners - channel.channel_partner_ids channel.write({'channel_last_seen_partner_ids': [(0, 0, {'partner_id': partner_id}) for partner_id in partners_to_add.ids]}) for partner in partners_to_add: - notification = _('
joined #%s
') % (self.id, self.name,) + if partner.id != self.env.user.partner_id.id: + notification = _('
%(author)s invited %(new_partner)s to #%(channel_name)s
') % { + 'author': self.env.user.display_name, + 'new_partner': partner.display_name, + 'channel_id': channel.id, + 'channel_name': channel.name, + } + else: + notification = _('
joined #%s
') % (channel.id, channel.name,) self.message_post(body=notification, message_type="notification", subtype="mail.mt_comment", author_id=partner.id) # broadcast the channel header to the added partner From eb7602c4ea6e62c8c57ccc518e71bd45f24d5631 Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Tue, 20 Feb 2018 14:54:51 +0100 Subject: [PATCH 06/13] [FIX] sale: Confirmation date duplicated when duplicating SO When duplicating a SO, the confirmation date is duplicated and the confirmation date was older than the creation date. opw:816866 --- addons/sale/models/sale.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py index 3c60e7cb0d9..1dcd6ca3c22 100644 --- a/addons/sale/models/sale.py +++ b/addons/sale/models/sale.py @@ -122,7 +122,7 @@ class SaleOrder(models.Model): validity_date = fields.Date(string='Expiration Date', readonly=True, copy=False, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Manually set the expiration date of your quotation (offer), or it will set the date automatically based on the template if online quotation is installed.") create_date = fields.Datetime(string='Creation Date', readonly=True, index=True, help="Date on which sales order is created.") - confirmation_date = fields.Datetime(string='Confirmation Date', readonly=True, index=True, help="Date on which the sale order is confirmed.", oldname="date_confirm") + confirmation_date = fields.Datetime(string='Confirmation Date', readonly=True, index=True, help="Date on which the sale order is confirmed.", oldname="date_confirm", copy=False) user_id = fields.Many2one('res.users', string='Salesperson', index=True, track_visibility='onchange', default=lambda self: self.env.user) partner_id = fields.Many2one('res.partner', string='Customer', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, required=True, change_default=True, index=True, track_visibility='always') partner_invoice_id = fields.Many2one('res.partner', string='Invoice Address', readonly=True, required=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Invoice address for current sales order.") From e544ec8df7028e6a9ffb2c9b2a3ec64035c33bf9 Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Tue, 20 Feb 2018 15:23:40 +0100 Subject: [PATCH 07/13] [FIX] stock_account: method expect a record, not an id Backport of 6cedc90f69d8c4fb82da19a6ed8a1ed3460101bc opw-817262 --- addons/stock_account/stock_account.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/stock_account/stock_account.py b/addons/stock_account/stock_account.py index 88c1c1edaf2..18c07c0fbed 100644 --- a/addons/stock_account/stock_account.py +++ b/addons/stock_account/stock_account.py @@ -136,13 +136,13 @@ class account_invoice(osv.osv): company_currency = inv.company_id.currency_id price_unit = i_line._get_anglo_saxon_price_unit() if inv.currency_id != company_currency: - currency_id = inv.currency_id.id + currency = inv.currency_id amount_currency = i_line._get_price(inv, company_currency.id, i_line, price_unit) else: - currency_id = False + currency = False amount_currency = False - return self.env['product.product']._anglo_saxon_sale_move_lines(i_line.name, i_line.product_id, i_line.uom_id, i_line.quantity, price_unit, currency=currency_id, amount_currency=amount_currency, fiscal_position=inv.fiscal_position_id, account_analytic=i_line.account_analytic_id) + return self.env['product.product']._anglo_saxon_sale_move_lines(i_line.name, i_line.product_id, i_line.uom_id, i_line.quantity, price_unit, currency=currency, amount_currency=amount_currency, fiscal_position=inv.fiscal_position_id, account_analytic=i_line.account_analytic_id) #---------------------------------------------------------- From 2120e667b9098a6a74788d8311bd2833a65c6ac8 Mon Sep 17 00:00:00 2001 From: Romain Derie Date: Fri, 26 Jan 2018 14:04:13 +0100 Subject: [PATCH 08/13] [FIX] website_sale: variants view, currency price and price diff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this commit: 1. When enabling 'List View of Variants' on a product page, it would not replace the variant normal view as it should since the code being replaced is included in the code replacing it. 2.The badge specifying the price difference between the variance and the product base price would be calculated by taking the price_extra of the variance attribute and converting it in the user currency which was not correct if a pricelist was set on the product variant because the price diff would then be (the variant price set on the pricelist) - (product base price) Note: this is about the 'List View of Variants' mode, not the normal mode which list variant attribute and not variant itself 3. The strikethrough amount was not in the user currency Step to reproduce: - Enable variants & pricelists - Enable multi-currency and activate 'COP' as second currency (you may want to change the currency symbol on edit mode since it is also '$') - Go to iPad Retina Display product page in shop - Enable 'List View of Variants' in 'Customize' - Both Variants selection mode are visible (bug 1) - The third variant shows '+$ 50.40' which is the diff between the strikethrough prices (in fact the sum of the price_extra of the product variant attributes, +50,40 on the 32GO attribute in this case) It should show '+$ 40.32' since the price change from $600 to $640.32. This is bug n°2 - Change Public Pricelist currency from USD to COP - Back on iPad page, change between the 3 product variants, you will see the $ price strikedthrough instead of the COP one: Strikedthrough: 750 (it should be 1 714 491,47) Price: 1 371 593,17 This is bug 3 - You can notice the bug n°2 being more visible since it shows '+$ 115 213,83' instead of '+ 92 171.07' - Remove the iPad Retina from Public Pricelist and add iPad Retina 32Go variant to it with a fix price of $1 000 000. - '32Gb, 2.4GHz, White' variant cost $1 000 000 but shows '+ 115 213.83' instead of '- 714 491.47' since the base price of Retina is '1 714 491.47'. - You can play with the pricelist price for this variant up and down, the diff price shown in the badge will always be 115 213.83 since it will always calculate it by summing all the variant's attribute price_extra and then converting it to the user currency. This closes #14094, closes #22553 --- addons/website_sale/views/templates.xml | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml index b260d26af7e..7e45ae5d56c 100644 --- a/addons/website_sale/views/templates.xml +++ b/addons/website_sale/views/templates.xml @@ -449,19 +449,13 @@