diff --git a/addons/delivery/models/stock_picking.py b/addons/delivery/models/stock_picking.py index 75b14aa7a7b..ad2bf8a8e15 100644 --- a/addons/delivery/models/stock_picking.py +++ b/addons/delivery/models/stock_picking.py @@ -152,6 +152,7 @@ class StockPicking(models.Model): 'context': dict( self.env.context, current_package_carrier_type=self.carrier_id.delivery_type, + default_picking_id=self.id, # DO NOT FORWARD PORT default_stock_quant_package_id=res.id ), } diff --git a/addons/delivery/wizard/choose_delivery_package.py b/addons/delivery/wizard/choose_delivery_package.py index e7ad1d75292..422e3a1893e 100644 --- a/addons/delivery/wizard/choose_delivery_package.py +++ b/addons/delivery/wizard/choose_delivery_package.py @@ -37,14 +37,15 @@ class ChooseDeliveryPackage(models.TransientModel): return res def _default_shipping_weight(self): - if self.env.context.get('default_stock_quant_package_id'): - stock_quant_package = self.env['stock.quant.package'].browse(self.env.context['default_stock_quant_package_id']) - return stock_quant_package.shipping_weight - else: - picking_id = self.env['stock.picking'].browse(self.env.context['active_id']) - move_line_ids = [po for po in picking_id.move_line_ids if po.qty_done > 0 and not po.result_package_id] + package_id = self.env.context.get('default_stock_quant_package_id') + picking_id = self.env.context.get('default_picking_id') + if package_id and picking_id: # DO NOT FORWARD PORT + picking = self.env['stock.picking'].browse(picking_id) + move_line_ids = [po for po in picking.move_line_ids if po.qty_done > 0 and po.result_package_id.id == package_id] total_weight = sum([po.qty_done * po.product_id.weight for po in move_line_ids]) return total_weight + else: + return 0 @api.depends('stock_quant_package_id', 'delivery_packaging_id') def _compute_weight_uom_name(self): diff --git a/addons/google_calendar/models/google_calendar.py b/addons/google_calendar/models/google_calendar.py index 9155cf9a0fa..bf9010f3023 100644 --- a/addons/google_calendar/models/google_calendar.py +++ b/addons/google_calendar/models/google_calendar.py @@ -81,7 +81,7 @@ class SyncEvent(object): self.OP = Delete((self.OE.status and "OE") or (self.GG.status and "GG"), 'The event has been deleted from one side, we delete on other side !') #If event is not deleted ! - elif self.OE.status and (self.GG.status or not is_owner): + elif self.OE.status and (self.GG.status or (not is_owner and self.GG.update)): if abs(self.OE.update - self.GG.update) > timedelta(seconds=1): if self.OE.update < self.GG.update: tmpSrc = 'GG' diff --git a/addons/google_spreadsheet/models/google_drive.py b/addons/google_spreadsheet/models/google_drive.py index 32402ffe626..f3c04608e72 100644 --- a/addons/google_spreadsheet/models/google_drive.py +++ b/addons/google_spreadsheet/models/google_drive.py @@ -35,7 +35,7 @@ class GoogleDrive(models.Model): if not modifiers.get('invisible') and not modifiers.get('column_invisible'): display_fields.append(node.get('name')) fields = " ".join(display_fields) - domain = domain.replace("'", r"\'").replace('"', "'") + domain = domain.replace("'", r"\'").replace('"', "'").replace('True', 'true').replace('False', 'false') if groupbys: fields = "%s %s" % (groupbys, fields) formula = '=oe_read_group("%s";"%s";"%s";"%s")' % (model, fields, groupbys, domain) diff --git a/addons/mail/models/mail_mail.py b/addons/mail/models/mail_mail.py index a2ba820656f..5875ab42ee2 100644 --- a/addons/mail/models/mail_mail.py +++ b/addons/mail/models/mail_mail.py @@ -172,6 +172,8 @@ class MailMail(models.Model): messages._notify_failure_update() # notify user that we have a failure (notifications - failed).sudo().write({ 'email_status': 'sent', + 'failure_type': '', + 'failure_reason': '', }) if not failure_type or failure_type == 'RECIPIENT': # if we have another error, we want to keep the mail. mail_to_delete_ids = [mail.id for mail in self if mail.auto_delete] diff --git a/addons/mail/static/src/js/composers/basic_composer.js b/addons/mail/static/src/js/composers/basic_composer.js index 9def912612c..1beeea18644 100644 --- a/addons/mail/static/src/js/composers/basic_composer.js +++ b/addons/mail/static/src/js/composers/basic_composer.js @@ -536,6 +536,7 @@ var BasicComposer = Widget.extend({ files: ev.currentTarget.files, submitForm: true }); + ev.target.value = ""; }, /** * @private diff --git a/addons/mrp/models/mrp_workorder.py b/addons/mrp/models/mrp_workorder.py index 861faf383be..59e2599f2ff 100644 --- a/addons/mrp/models/mrp_workorder.py +++ b/addons/mrp/models/mrp_workorder.py @@ -249,7 +249,7 @@ class MrpWorkorder(models.Model): self.qty_producing = float_round(self.production_id.product_qty - self.qty_produced, precision_rounding=rounding) self.generate_wo_lines() - if self.next_work_order_id and self.production_id.product_id.tracking != 'none': + if self.next_work_order_id and self.next_work_order_id.state not in ['done', 'cancel'] and self.production_id.product_id.tracking != 'none': self.next_work_order_id._assign_default_final_lot_id() if float_compare(self.qty_produced, self.production_id.product_qty, precision_rounding=rounding) >= 0: diff --git a/addons/payment/views/payment_templates.xml b/addons/payment/views/payment_templates.xml index 550939e21b7..64afb0b016f 100644 --- a/addons/payment/views/payment_templates.xml +++ b/addons/payment/views/payment_templates.xml @@ -87,6 +87,9 @@ Save my payment data + + + diff --git a/addons/project/views/project_portal_templates.xml b/addons/project/views/project_portal_templates.xml index 14498944913..1bb202f8fd8 100644 --- a/addons/project/views/project_portal_templates.xml +++ b/addons/project/views/project_portal_templates.xml @@ -238,7 +238,7 @@
    - +
    diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py index ec6729064ae..745419c98a0 100644 --- a/addons/sale/models/sale.py +++ b/addons/sale/models/sale.py @@ -1242,6 +1242,7 @@ class SaleOrderLine(models.Model): :param additional_domain: domain to restrict AAL to include in computation (required since timesheet is an AAL with a project ...) """ result = {} + # avoid recomputation if no SO lines concerned if not self: return result diff --git a/addons/snailmail/models/ir_actions_report.py b/addons/snailmail/models/ir_actions_report.py index 2a1ad2f9496..b441d55c0fb 100644 --- a/addons/snailmail/models/ir_actions_report.py +++ b/addons/snailmail/models/ir_actions_report.py @@ -6,6 +6,14 @@ from odoo import models, fields, api, _ class IrActionsReport(models.Model): _inherit = 'ir.actions.report' + @api.multi + def retrieve_attachment(self, record): + # Override this method in order to force to re-render the pdf in case of + # using snailmail + if self.env.context.get('snailmail_layout'): + return False + return super(IrActionsReport, self).retrieve_attachment(record) + @api.model def get_paperformat(self): # force the right format (euro/A4) when sending letters, only if we are not using the l10n_DE layout diff --git a/addons/stock/data/stock_data.xml b/addons/stock/data/stock_data.xml index d522f76d9c6..b38a917f891 100644 --- a/addons/stock/data/stock_data.xml +++ b/addons/stock/data/stock_data.xml @@ -14,6 +14,8 @@ + + Physical Locations view diff --git a/addons/stock/models/stock_quant.py b/addons/stock/models/stock_quant.py index 66a7aa90b8c..f43374dad61 100644 --- a/addons/stock/models/stock_quant.py +++ b/addons/stock/models/stock_quant.py @@ -433,5 +433,5 @@ class QuantPackage(models.Model): for quant in self._get_contained_quants(): if quant.product_id not in res: res[quant.product_id] = 0 - res[quant.product_id] += quant.qty + res[quant.product_id] += quant.quantity return res diff --git a/addons/uom/models/uom_uom.py b/addons/uom/models/uom_uom.py index f98b0dc67bb..04368b3624c 100644 --- a/addons/uom/models/uom_uom.py +++ b/addons/uom/models/uom_uom.py @@ -83,9 +83,8 @@ class UoM(models.Model): self._cr.execute(""" SELECT C.id AS category_id, count(U.id) AS uom_count FROM uom_category C - LEFT JOIN uom_uom U ON C.id = U.category_id AND uom_type = 'reference' + LEFT JOIN uom_uom U ON C.id = U.category_id AND uom_type = 'reference' AND U.active = 't' WHERE C.id IN %s - AND U.active = 't' GROUP BY C.id """, (tuple(category_ids),)) for uom_data in self._cr.dictfetchall(): diff --git a/addons/uom/tests/test_uom.py b/addons/uom/tests/test_uom.py index 66207639f77..c6fe98dd81e 100644 --- a/addons/uom/tests/test_uom.py +++ b/addons/uom/tests/test_uom.py @@ -68,6 +68,16 @@ class TestUom(TransactionCase): 'name': 'Custom UoM category', }) + # at first we can not create a non reference in custom category + with self.assertRaises(ValidationError): + self.env['uom.uom'].create({ + 'name': 'Bigger UoM of my category', + 'factor_inv': 42, + 'uom_type': 'bigger', + 'rounding': 0.5, + 'category_id': category.id + }) + # create the reference self.env['uom.uom'].create({ 'name': 'Reference UoM of my category', diff --git a/addons/web/static/src/scss/layout_boxed.scss b/addons/web/static/src/scss/layout_boxed.scss index 2129f583b21..bc4f5a6c2fb 100644 --- a/addons/web/static/src/scss/layout_boxed.scss +++ b/addons/web/static/src/scss/layout_boxed.scss @@ -1,5 +1,5 @@ .o_boxed_footer, .o_boxed_header, .o_report_layout_boxed { - font-family: 'Lato' 'Noto'; + font-family: 'Lato', 'Noto'; color: gray('700'); font-size: 12px; } diff --git a/addons/website/models/website.py b/addons/website/models/website.py index 0c63ee55f41..82e4f36107f 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -141,7 +141,7 @@ class Website(models.Model): public_user_to_change_websites = self.env['website'] self._get_languages.clear_cache(self) if 'company_id' in values and 'user_id' not in values: - public_user_to_change_websites = self.filtered(lambda w: w.user_id.company_id.id != values['company_id']) + public_user_to_change_websites = self.filtered(lambda w: w.sudo().user_id.company_id.id != values['company_id']) if public_user_to_change_websites: company = self.env['res.company'].browse(values['company_id']) super(Website, public_user_to_change_websites).write(dict(values, user_id=company._get_public_user().id)) diff --git a/addons/website_sale/static/src/js/website_sale_options.js b/addons/website_sale/static/src/js/website_sale_options.js index 619ea92f6b5..f98aad44a5f 100644 --- a/addons/website_sale/static/src/js/website_sale_options.js +++ b/addons/website_sale/static/src/js/website_sale_options.js @@ -129,11 +129,20 @@ sAnimations.registry.WebsiteSaleOptions = sAnimations.Class.extend(ProductConfig * @param {integer} quantity */ _onOptionsUpdateQuantity: function (quantity) { - this.$form + var $qtyInput = this.$form .find('.js_main_product input[name="add_qty"]') - .first() - .val(quantity) - .trigger('change'); + .first(); + + if ($qtyInput.length) { + $qtyInput.val(quantity).trigger('change'); + } else { + // This handles the case when the "Select Quantity" customize show + // is disabled, and therefore the above selector does not find an + // element. + // To avoid duplicating all RPC, only trigger the variant change if + // it is not already done from the above trigger. + this.optionalProductsModal.triggerVariantChange(this.optionalProductsModal.$el); + } }, /** diff --git a/addons/website_sale_wishlist/models/product_wishlist.py b/addons/website_sale_wishlist/models/product_wishlist.py index 7467991f3a7..1bcbe57baef 100644 --- a/addons/website_sale_wishlist/models/product_wishlist.py +++ b/addons/website_sale_wishlist/models/product_wishlist.py @@ -7,7 +7,7 @@ from odoo.http import request class ProductWishlist(models.Model): _name = 'product.wishlist' _description = 'Product Wishlist' - _sql_constrains = [ + _sql_constraints = [ ("product_unique_partner_id", "UNIQUE(product_id, partner_id)", "Duplicated wishlisted product for this partner."), diff --git a/odoo/addons/test_new_api/__manifest__.py b/odoo/addons/test_new_api/__manifest__.py index e238f37f779..ec4bdae82bc 100644 --- a/odoo/addons/test_new_api/__manifest__.py +++ b/odoo/addons/test_new_api/__manifest__.py @@ -10,7 +10,9 @@ 'data': [ 'ir.model.access.csv', 'views.xml', - 'demo_data.xml', 'template.xml', ], + 'demo': [ + 'demo_data.xml', + ], } diff --git a/odoo/http.py b/odoo/http.py index 886fecc48ba..217720f6a72 100644 --- a/odoo/http.py +++ b/odoo/http.py @@ -280,13 +280,15 @@ class WebRequest(object): _request_stack.pop() if self._cr: - if exc_type is None and not self._failed: - self._cr.commit() - if self.registry: - self.registry.signal_changes() - elif self.registry: - self.registry.reset_changes() - self._cr.close() + try: + if exc_type is None and not self._failed: + self._cr.commit() + if self.registry: + self.registry.signal_changes() + elif self.registry: + self.registry.reset_changes() + finally: + self._cr.close() # just to be sure no one tries to re-use the request self.disable_db = True self.uid = None