Test message
", + id: 1, + model: 'mail.channel', + res_id: 2, + channel_ids: [2], + }; + var notification = [[false, 'mail.channel', 2], messageData]; + parent.call('bus_service', 'trigger', 'notification', [notification]); + + $visibleThreadWindows = $('.o_thread_window:not(.o_thread_window_dropdown, .o_hidden)'); + assert.strictEqual($visibleThreadWindows.filter('[data-thread-id="1"]').length, 1, + "the thread window with ID 1 should stay visible"); + assert.strictEqual($('.o_thread_window.o_hidden[data-thread-id="2"]').length, 1, + "the thread window with ID 2 should stay hidden"); + assert.strictEqual( + $('.o_thread_window_dropdown .o_total_unread_counter').text().trim(), + "1", + "should have unread counter of 1 on hidden dropup menu"); + + parent.destroy(); + testUtils.unpatch(this.services.mail_service); +}); + }); }); }); diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index 739ce795fe7..ac0c9c13882 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -9,7 +9,7 @@ import math from odoo import api, fields, models, _ from odoo.addons import decimal_precision as dp from odoo.exceptions import AccessError, UserError -from odoo.tools import float_compare, float_round, DEFAULT_SERVER_DATETIME_FORMAT +from odoo.tools import float_compare, float_round, float_is_zero, DEFAULT_SERVER_DATETIME_FORMAT class MrpProduction(models.Model): """ Manufacturing Orders """ @@ -820,6 +820,8 @@ class MrpProduction(models.Model): moves_to_do = order.move_raw_ids.filtered(lambda x: x.state not in ('done', 'cancel')) for move in moves_to_do.filtered(lambda m: m.product_qty == 0.0 and m.quantity_done > 0): move.product_uom_qty = move.quantity_done + for move in moves_to_do.filtered(lambda m: float_is_zero(m.quantity_done, precision_rounding=m.product_uom.rounding)): + move._action_cancel() moves_to_do._action_done() moves_to_do = order.move_raw_ids.filtered(lambda x: x.state == 'done') - moves_not_to_do order._cal_price(moves_to_do) diff --git a/addons/mrp/report/mrp_report_bom_structure.py b/addons/mrp/report/mrp_report_bom_structure.py index 8e709a7df67..1c93de90212 100644 --- a/addons/mrp/report/mrp_report_bom_structure.py +++ b/addons/mrp/report/mrp_report_bom_structure.py @@ -14,8 +14,7 @@ class ReportBomStructure(models.AbstractModel): docs = [] for bom_id in docids: bom = self.env['mrp.bom'].browse(bom_id) - variant = data and data.get('variant') - candidates = variant and self.env['product.product'].browse(variant) or bom.product_tmpl_id.product_variant_ids + candidates = bom.product_id or bom.product_tmpl_id.product_variant_ids for product_variant_id in candidates: if data and data.get('childs'): doc = self._get_pdf_line(bom_id, product_id=product_variant_id, qty=float(data.get('quantity')), child_bom_ids=json.loads(data.get('childs'))) diff --git a/addons/payment_stripe/i18n/payment_stripe.pot b/addons/payment_stripe/i18n/payment_stripe.pot index 16b83b04500..6f2e50dc7ec 100644 --- a/addons/payment_stripe/i18n/payment_stripe.pot +++ b/addons/payment_stripe/i18n/payment_stripe.pot @@ -184,6 +184,12 @@ msgstr "" msgid "Stripe: no order found for reference %s" msgstr "" +#. module: payment_stripe +#: code:addons/payment_stripe/models/payment.py:260 +#, python-format +msgid "Stripe: no payment token was provided or the token creation failed." +msgstr "" + #. module: payment_stripe #: code:addons/payment_stripe/models/payment.py:170 #, python-format diff --git a/addons/payment_stripe/models/payment.py b/addons/payment_stripe/models/payment.py index 78920e93580..f35a44165e7 100644 --- a/addons/payment_stripe/models/payment.py +++ b/addons/payment_stripe/models/payment.py @@ -257,7 +257,7 @@ class PaymentTokenStripe(models.Model): description = 'Partner: %s (id: %s)' % (partner_id.name, partner_id.id) if not token: - raise Exception('stripe_create: No token provided!') + raise UserError(_("Stripe: no payment token was provided or the token creation failed.")) res = self._stripe_create_customer(token, description, payment_acquirer.id) diff --git a/addons/payment_stripe/static/src/js/stripe.js b/addons/payment_stripe/static/src/js/stripe.js index 7d8d20a0590..ec60ec4fbc5 100644 --- a/addons/payment_stripe/static/src/js/stripe.js +++ b/addons/payment_stripe/static/src/js/stripe.js @@ -105,9 +105,17 @@ odoo.define('payment_stripe.stripe', function(require) { var invoice_num = get_input_value("invoice_num"); var merchant = get_input_value("merchant"); + // Search if the user wants to save the credit card information + var form_save_token = false; + var acquirer_form = $('#o_payment_form_acq_' + acquirer_id); + if (acquirer_form.length) { + form_save_token = acquirer_form.find('input[name="o_payment_form_save_token"]').prop('checked'); + } + ajax.jsonRpc(payment_tx_url, 'call', { acquirer_id: acquirer_id, access_token: access_token, + save_token: form_save_token, }).then(function(data) { var $pay_stripe = $('#pay_stripe').detach(); try { provider_form[0].innerHTML = data; } catch (e) {} diff --git a/addons/pos_discount/static/src/js/discount.js b/addons/pos_discount/static/src/js/discount.js index 87b97f88e16..fc41e55bc05 100644 --- a/addons/pos_discount/static/src/js/discount.js +++ b/addons/pos_discount/static/src/js/discount.js @@ -42,10 +42,16 @@ var DiscountButton = screens.ActionButtonWidget.extend({ } // Add discount + // We add the price as manually set to avoid recomputation when changing customer. var discount = - pc / 100.0 * order.get_total_with_tax(); if( discount < 0 ){ - order.add_product(product, { price: discount }); + order.add_product(product, { + price: discount, + extras: { + price_manually_set: true, + }, + }); } }, }); diff --git a/addons/product/models/product_template.py b/addons/product/models/product_template.py index 4d20b5afbeb..164d499085d 100644 --- a/addons/product/models/product_template.py +++ b/addons/product/models/product_template.py @@ -552,6 +552,16 @@ class ProductTemplate(models.Model): if variants_to_create: Product.create(variants_to_create) + # Avoid access errors in case the products is shared amongst companies but the underlying + # objects are not. If unlink fails because of an AccessError (e.g. while recomputing + # fields), the 'write' call will fail as well for the same reason since the field has + # been set to recompute. + if variants_to_unlink: + variants_to_unlink.check_access_rights('unlink') + variants_to_unlink.check_access_rule('unlink') + variants_to_unlink.check_access_rights('write') + variants_to_unlink.check_access_rule('write') + variants_to_unlink = variants_to_unlink.sudo() # unlink or inactive product # try in batch first because it is much faster try: diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py index fb76644adbf..b28e8f26318 100644 --- a/addons/sale/models/sale.py +++ b/addons/sale/models/sale.py @@ -1441,7 +1441,7 @@ class SaleOrderLine(models.Model): if currency != self.order_id.pricelist_id.currency_id: base_price = currency._convert( base_price, self.order_id.pricelist_id.currency_id, - self.order_id.company_id, self.order_id.date_order or fields.Date.today()) + self.order_id.company_id or self.env.user.company_id, self.order_id.date_order or fields.Date.today()) # negative discounts (= surcharge) are included in the display price return max(base_price, final_price) @@ -1624,7 +1624,7 @@ class SaleOrderLine(models.Model): # we need new_list_price in the same currency as price, which is in the SO's pricelist's currency new_list_price = currency._convert( new_list_price, self.order_id.pricelist_id.currency_id, - self.order_id.company_id, self.order_id.date_order or fields.Date.today()) + self.order_id.company_id or self.env.user.company_id, self.order_id.date_order or fields.Date.today()) discount = (new_list_price - price) / new_list_price * 100 if (discount > 0 and new_list_price > 0) or (discount < 0 and new_list_price < 0): self.discount = discount diff --git a/addons/sale/models/sales_team.py b/addons/sale/models/sales_team.py index aae3b6c6e31..6d297bb0ee5 100644 --- a/addons/sale/models/sales_team.py +++ b/addons/sale/models/sales_team.py @@ -57,7 +57,7 @@ class CrmTeam(models.Model): def _compute_sales_to_invoice(self): sale_order_data = self.env['sale.order'].read_group([ ('team_id', 'in', self.ids), - ('order_line.qty_to_invoice', '>', 0), + ('invoice_status','=','to invoice'), ], ['team_id'], ['team_id']) data_map = {datum['team_id'][0]: datum['team_id_count'] for datum in sale_order_data } for team in self: diff --git a/addons/sale/views/sale_views.xml b/addons/sale/views/sale_views.xml index d791f3cc768..6c2b18a1f41 100644 --- a/addons/sale/views/sale_views.xml +++ b/addons/sale/views/sale_views.xml @@ -1186,7 +1186,7 @@