diff --git a/addons/account/data/mail_template_data.xml b/addons/account/data/mail_template_data.xml
index f461a27145232043a0ff7899d3e94bddc22d2393..4bb340a4f5c38f2ba6b356cf25a8f0e3a2824d15 100644
--- a/addons/account/data/mail_template_data.xml
+++ b/addons/account/data/mail_template_data.xml
@@ -7,7 +7,7 @@
         <!--Email template -->
         <record id="email_template_edi_invoice" model="mail.template">
             <field name="name">Invoicing: Invoice email</field>
-            <field name="email_from">${(object.user_id.email and '%s &lt;%s&gt;' % (object.user_id.name, object.user_id.email) or '')|safe}</field>
+            <field name="email_from">${(object.user_id.email and '&quot;%s&quot; &lt;%s&gt;' % (object.user_id.name, object.user_id.email) or '')|safe}</field>
             <field name="subject">${object.company_id.name} Invoice (Ref ${object.number or 'n/a'})</field>
             <field name="partner_to">${object.partner_id.id}</field>
             <field name="model_id" ref="account.model_account_invoice"/>
diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py
index bc809c577f72c8a27bad738b6ce14379736ef356..94c1fa485c70a804f44c9d4f3820feb328f3e1ac 100644
--- a/addons/account/models/account_invoice.py
+++ b/addons/account/models/account_invoice.py
@@ -1370,8 +1370,6 @@ class AccountInvoice(models.Model):
         else:
             payment_method = self.env.ref('account.account_payment_method_manual_out')
             journal_payment_methods = pay_journal.outbound_payment_method_ids
-        if payment_method not in journal_payment_methods:
-            raise UserError(_('No appropriate payment method enabled on journal %s') % pay_journal.name)
 
         communication = self.type in ('in_invoice', 'in_refund') and self.reference or self.number
         if self.origin:
diff --git a/addons/account/models/company.py b/addons/account/models/company.py
index 8ae4230a7b20ee03037630df9410555b26c91ec3..0209ea5c849ea2193d89b00264fa1c15bfceba07 100644
--- a/addons/account/models/company.py
+++ b/addons/account/models/company.py
@@ -2,10 +2,12 @@
 
 from datetime import timedelta, datetime
 import calendar
+import time
+from dateutil.relativedelta import relativedelta
 
 from odoo import fields, models, api, _
 from odoo.exceptions import ValidationError, UserError
-from odoo.exceptions import UserError
+from odoo.tools.misc import DEFAULT_SERVER_DATE_FORMAT
 from odoo.tools.float_utils import float_round, float_is_zero
 
 
@@ -63,6 +65,61 @@ Best Regards,'''))
     account_setup_coa_done = fields.Boolean(string='Chart of Account Checked', help="Technical field holding the status of the chart of account setup step.")
     account_setup_bar_closed = fields.Boolean(string='Setup Bar Closed', help="Technical field set to True when setup bar has been closed by the user.")
 
+    @api.multi
+    def _check_lock_dates(self, vals):
+        '''Check the lock dates for the current companies. This can't be done in a api.constrains because we need
+        to perform some comparison between new/old values. This method forces the lock dates to be irreversible.
+
+        * You cannot define stricter conditions on advisors than on users. Then, the lock date on advisor must be set
+        after the lock date for users.
+        * You cannot lock a period that is not finished yet. Then, the lock date for advisors must be set after the
+        last day of the previous month.
+        * The new lock date for advisors must be set after the previous lock date.
+
+        :param vals: The values passed to the write method.
+        '''
+        period_lock_date = vals.get('period_lock_date') and\
+            time.strptime(vals['period_lock_date'], DEFAULT_SERVER_DATE_FORMAT)
+        fiscalyear_lock_date = vals.get('fiscalyear_lock_date') and\
+            time.strptime(vals['fiscalyear_lock_date'], DEFAULT_SERVER_DATE_FORMAT)
+
+        previous_month = datetime.strptime(fields.Date.today(), DEFAULT_SERVER_DATE_FORMAT) + relativedelta(months=-1)
+        days_previous_month = calendar.monthrange(previous_month.year, previous_month.month)
+        previous_month = previous_month.replace(day=days_previous_month[1]).timetuple()
+        for company in self:
+            old_fiscalyear_lock_date = company.fiscalyear_lock_date and\
+                time.strptime(company.fiscalyear_lock_date, DEFAULT_SERVER_DATE_FORMAT)
+
+            # The user attempts to remove the lock date for advisors
+            if old_fiscalyear_lock_date and not fiscalyear_lock_date and 'fiscalyear_lock_date' in vals:
+                raise ValidationError(_('The lock date for advisors is irreversible and can\'t be removed.'))
+
+            # The user attempts to set a lock date for advisors prior to the previous one
+            if old_fiscalyear_lock_date and fiscalyear_lock_date and fiscalyear_lock_date < old_fiscalyear_lock_date:
+                raise ValidationError(_('The new lock date for advisors must be set after the previous lock date.'))
+
+            # In case of no new fiscal year in vals, fallback to the oldest
+            if not fiscalyear_lock_date:
+                if old_fiscalyear_lock_date:
+                    fiscalyear_lock_date = old_fiscalyear_lock_date
+                else:
+                    continue
+
+            # The user attempts to set a lock date for advisors prior to the last day of previous month
+            if fiscalyear_lock_date > previous_month:
+                raise ValidationError(_('You cannot lock a period that is not finished yet. Please make sure that the lock date for advisors is not set after the last day of the previous month.'))
+
+            # In case of no new period lock date in vals, fallback to the one defined in the company
+            if not period_lock_date:
+                if company.period_lock_date:
+                    period_lock_date = time.strptime(company.period_lock_date, DEFAULT_SERVER_DATE_FORMAT)
+                else:
+                    continue
+
+            # The user attempts to set a lock date for advisors prior to the lock date for users
+            if period_lock_date < fiscalyear_lock_date:
+                raise ValidationError(_('You cannot define stricter conditions on advisors than on users. Please make sure that the lock date on advisor is set before the lock date for users.'))
+
     @api.model
     def _verify_fiscalyear_last_day(self, company_id, last_day, last_month):
         company = self.browse(company_id)
diff --git a/addons/account/tests/test_account_move_closed_period.py b/addons/account/tests/test_account_move_closed_period.py
index 298738a0d6c54ebbbd7c86f133ae0ff59e8a0576..5de3e97ac4fe632b990b4dd7429ce7679cccfad8 100644
--- a/addons/account/tests/test_account_move_closed_period.py
+++ b/addons/account/tests/test_account_move_closed_period.py
@@ -1,6 +1,8 @@
 from odoo.addons.account.tests.account_test_classes import AccountingTestCase
 from odoo.osv.orm import except_orm
-from datetime import datetime, timedelta
+from datetime import datetime
+from dateutil.relativedelta import relativedelta
+from calendar import monthrange
 from odoo.tools import DEFAULT_SERVER_DATE_FORMAT
 from odoo.tests import tagged
 
@@ -14,14 +16,16 @@ class TestPeriodState(AccountingTestCase):
     def setUp(self):
         super(TestPeriodState, self).setUp()
         self.user_id = self.env.user
-        self.day_before_yesterday = datetime.now() - timedelta(2)
-        self.yesterday = datetime.now() - timedelta(1)
-        self.yesterday_str = self.yesterday.strftime(DEFAULT_SERVER_DATE_FORMAT)
+
+        last_day_month = datetime.now() - relativedelta(months=1)
+        last_day_month = last_day_month.replace(day=monthrange(last_day_month.year, last_day_month.month)[1])
+        self.last_day_month_str = last_day_month.strftime(DEFAULT_SERVER_DATE_FORMAT)
+
         #make sure there is no unposted entry
-        draft_entries = self.env['account.move'].search([('date', '<=', self.yesterday_str), ('state', '=', 'draft')])
+        draft_entries = self.env['account.move'].search([('date', '<=', self.last_day_month_str), ('state', '=', 'draft')])
         if draft_entries:
             draft_entries.post()
-        self.user_id.company_id.write({'fiscalyear_lock_date': self.yesterday_str})
+        self.user_id.company_id.fiscalyear_lock_date = self.last_day_month_str
         self.sale_journal_id = self.env['account.journal'].search([('type', '=', 'sale')])[0]
         self.account_id = self.env['account.account'].search([('internal_type', '=', 'receivable')])[0]
 
@@ -30,7 +34,7 @@ class TestPeriodState(AccountingTestCase):
             move = self.env['account.move'].create({
                 'name': '/',
                 'journal_id': self.sale_journal_id.id,
-                'date': self.day_before_yesterday.strftime(DEFAULT_SERVER_DATE_FORMAT),
+                'date': self.last_day_month_str,
                 'line_ids': [(0, 0, {
                         'name': 'foo',
                         'debit': 10,
diff --git a/addons/account/tests/test_reconciliation.py b/addons/account/tests/test_reconciliation.py
index 4a50c812620f7e6fdeacf9fd9110fb9db534cf83..1fce897b602243105e93eecc3bc72588537b6a9d 100644
--- a/addons/account/tests/test_reconciliation.py
+++ b/addons/account/tests/test_reconciliation.py
@@ -42,6 +42,12 @@ class TestReconciliation(AccountingTestCase):
         self.diff_income_account = self.env['res.users'].browse(self.env.uid).company_id.income_currency_exchange_account_id
         self.diff_expense_account = self.env['res.users'].browse(self.env.uid).company_id.expense_currency_exchange_account_id
 
+        self.inbound_payment_method = self.env['account.payment.method'].create({
+            'name': 'inbound',
+            'code': 'IN',
+            'payment_type': 'inbound',
+        })
+
     def create_invoice(self, type='out_invoice', invoice_amount=50, currency_id=None):
         #we create an invoice in given currency
         invoice = self.account_invoice_model.create({'partner_id': self.partner_agrolait_id,
@@ -709,6 +715,49 @@ class TestReconciliation(AccountingTestCase):
         credit_aml.with_context(invoice_id=inv.id).remove_move_reconcile()
         self.assertAlmostEquals(inv.residual, 111)
 
+    def test_revert_payment_and_reconcile(self):
+        payment = self.env['account.payment'].create({
+            'payment_method_id': self.inbound_payment_method.id,
+            'payment_type': 'inbound',
+            'partner_type': 'customer',
+            'partner_id': self.partner_agrolait_id,
+            'journal_id': self.bank_journal_usd.id,
+            'payment_date': '2018-06-04',
+            'amount': 666,
+        })
+        payment.post()
+
+        self.assertEqual(len(payment.move_line_ids), 2)
+
+        bank_line = payment.move_line_ids.filtered(lambda l: l.account_id.id == self.bank_journal_usd.default_debit_account_id.id)
+        customer_line = payment.move_line_ids - bank_line
+
+        self.assertEqual(len(bank_line), 1)
+        self.assertEqual(len(customer_line), 1)
+        self.assertNotEqual(bank_line.id, customer_line.id)
+
+        self.assertEqual(bank_line.move_id.id, customer_line.move_id.id)
+        move = bank_line.move_id
+
+        # Reversing the payment's move
+        reversed_move_list = move.reverse_moves('2018-06-04')
+        self.assertEqual(len(reversed_move_list), 1)
+        reversed_move = self.env['account.move'].browse(reversed_move_list[0])
+
+        self.assertEqual(len(reversed_move.line_ids), 2)
+
+        # Testing the reconciliation matching between the move lines and their reversed counterparts
+        reversed_bank_line = reversed_move.line_ids.filtered(lambda l: l.account_id.id == self.bank_journal_usd.default_debit_account_id.id)
+        reversed_customer_line = reversed_move.line_ids - reversed_bank_line
+
+        self.assertEqual(len(reversed_bank_line), 1)
+        self.assertEqual(len(reversed_customer_line), 1)
+        self.assertNotEqual(reversed_bank_line.id, reversed_customer_line.id)
+        self.assertEqual(reversed_bank_line.move_id.id, reversed_customer_line.move_id.id)
+
+        self.assertEqual(reversed_bank_line.full_reconcile_id.id, bank_line.full_reconcile_id.id)
+        self.assertEqual(reversed_customer_line.full_reconcile_id.id, customer_line.full_reconcile_id.id)
+
     def test_partial_reconcile_currencies_02(self):
         ####
         # Day 1: Invoice Cust/001 to customer (expressed in USD)
diff --git a/addons/account/views/account_view.xml b/addons/account/views/account_view.xml
index 712bfe7b1f94aa238d38e751f3cecec86a9593f8..8a925dbdf8cf2a7f901cd63abbdb47fea3637fda 100644
--- a/addons/account/views/account_view.xml
+++ b/addons/account/views/account_view.xml
@@ -1141,7 +1141,8 @@
                         <group>
                             <field name="name"/>
                             <field name="partner_id"
-                                domain="['|', ('parent_id', '=', False), ('is_company', '=', True)]"/>
+                                domain="['|', ('parent_id', '=', False), ('is_company', '=', True)]"
+                                attrs="{'readonly': [('parent_state', '=', 'posted')]}"/>
                         </group>
                         <notebook colspan="4">
                             <page string="Information">
diff --git a/addons/account_lock/__init__.py b/addons/account_lock/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..cde864bae21a11c0e4f50067aa46b4c497549b4c
--- /dev/null
+++ b/addons/account_lock/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from . import models
diff --git a/addons/account_lock/__manifest__.py b/addons/account_lock/__manifest__.py
new file mode 100644
index 0000000000000000000000000000000000000000..67e76b49098bc2eaeeb9eeec4d28e2f64621e5d4
--- /dev/null
+++ b/addons/account_lock/__manifest__.py
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+{
+    'name' : 'Irreversible Lock Date',
+    'version' : '1.0',
+    'category': 'Accounting',
+    'description': """
+    Make the lock date irreversible:
+
+    * You cannot define stricter conditions on advisors than on users. Then, the lock date on advisor must be set before the lock date for users.
+    * You cannot lock a period that is not finished yet. Then, the lock date for advisors must be set before the last day of the previous month.
+    * The new lock date for advisors must be set after the previous lock date.
+    """,
+    'depends' : ['account'],
+    'data': [],
+}
diff --git a/addons/account_lock/models/__init__.py b/addons/account_lock/models/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e759f74fc8dba3b0da63684c75f0ac24dc55fc99
--- /dev/null
+++ b/addons/account_lock/models/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from . import res_company
diff --git a/addons/account_lock/models/res_company.py b/addons/account_lock/models/res_company.py
new file mode 100644
index 0000000000000000000000000000000000000000..5a01046f36bbb0a09533840ad9a304bb1205fcb0
--- /dev/null
+++ b/addons/account_lock/models/res_company.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+
+from odoo import models, api
+
+
+class ResCompany(models.Model):
+    _inherit = 'res.company'
+
+    @api.multi
+    def write(self, vals):
+        # fiscalyear_lock_date can't be set to a prior date
+        if 'fiscalyear_lock_date' in vals or 'period_lock_date' in vals:
+            self._check_lock_dates(vals)
+        return super(ResCompany, self).write(vals)
diff --git a/addons/barcodes/static/src/js/barcode_events.js b/addons/barcodes/static/src/js/barcode_events.js
index a2e5808f8be3ff6ffec34d5d0ad678be6fbc637d..2d602a36b9701a302cdce4faf5ab50275bbdbd14 100644
--- a/addons/barcodes/static/src/js/barcode_events.js
+++ b/addons/barcodes/static/src/js/barcode_events.js
@@ -64,12 +64,14 @@ var BarcodeEvents = core.Class.extend(mixins.PropertiesMixin, {
                     'position': 'fixed',
                     'top': '50%',
                     'transform': 'translateY(-50%)',
-                    'opacity': 0,
+                    'z-index': '-1',
                 },
             });
+            // Avoid to show autocomplete for a non appearing input
+            this.$barcodeInput.attr('autocomplete', 'off');
         }
 
-        this.__removeBarcodeField = _.debounce(this._removeBarcodeField, this.inputTimeOut);
+        this.__blurBarcodeInput = _.debounce(this._blurBarcodeInput, this.inputTimeOut);
     },
 
     handle_buffered_keys: function() {
@@ -232,7 +234,7 @@ var BarcodeEvents = core.Class.extend(mixins.PropertiesMixin, {
                     this.max_time_between_keys_in_ms);
             }
             // if the barcode input doesn't receive keydown for a while, remove it.
-            this.__removeBarcodeField();
+            this.__blurBarcodeInput();
         }
     },
 
@@ -247,21 +249,22 @@ var BarcodeEvents = core.Class.extend(mixins.PropertiesMixin, {
         var barcodeValue = this.$barcodeInput.val();
         if (barcodeValue.match(this.regexp)) {
             core.bus.trigger('barcode_scanned', barcodeValue, $(e.target).parent()[0]);
-            this.$barcodeInput.val('');
+            this._blurBarcodeInput();
         }
     },
 
     /**
-     * Remove the temporary input created to store the barcode value.
-     * If nothing happens, this input will be removed, so the focus will be lost
-     * and the virtual keyboard on mobile devices will be closed.
+     * Removes the value and focus from the barcode input.
+     * If nothing happens, the focus will be lost and
+     * the virtual keyboard on mobile devices will be closed.
      *
      * @private
      */
-    _removeBarcodeField: function () {
+    _blurBarcodeInput: function () {
         if (this.$barcodeInput) {
-            // Reset the value and remove from the DOM.
-            this.$barcodeInput.val('').remove();
+            // Close the virtual keyboard on mobile browsers
+            // FIXME: actually we can't prevent keyboard from opening
+            this.$barcodeInput.val('').blur();
         }
     },
 
diff --git a/addons/delivery/models/stock_picking.py b/addons/delivery/models/stock_picking.py
index c071f23c5b26f1241b0d17a6c31d9e710bff9e11..c57799109a70bac2749d8c25f378654274a042e0 100644
--- a/addons/delivery/models/stock_picking.py
+++ b/addons/delivery/models/stock_picking.py
@@ -162,7 +162,8 @@ class StockPicking(models.Model):
         if self.carrier_id.free_over and self.sale_id and self.sale_id._compute_amount_total_without_delivery() >= self.carrier_id.amount:
             res['exact_price'] = 0.0
         self.carrier_price = res['exact_price']
-        self.carrier_tracking_ref = res['tracking_number']
+        if res['tracking_number']:
+            self.carrier_tracking_ref = res['tracking_number']
         order_currency = self.sale_id.currency_id or self.company_id.currency_id
         msg = _("Shipment sent to carrier %s for shipping with tracking number %s<br/>Cost: %.2f %s") % (self.carrier_id.name, self.carrier_tracking_ref, self.carrier_price, order_currency.name)
         self.message_post(body=msg)
diff --git a/addons/google_calendar/models/google_calendar.py b/addons/google_calendar/models/google_calendar.py
index df0df9e3388c231c1d9e3c6974c8dc455275c032..b9392bec08b0d67b014ddedf1df705b3c844d31d 100644
--- a/addons/google_calendar/models/google_calendar.py
+++ b/addons/google_calendar/models/google_calendar.py
@@ -842,7 +842,7 @@ class GoogleCalendar(models.AbstractModel):
                         try:
                             # if already deleted from gmail or never created
                             recs.delete_an_event(current_event[0])
-                        except Exception as e:
+                        except requests.exceptions.HTTPError as e:
                             if e.response.status_code in (401, 410,):
                                 pass
                             else:
diff --git a/addons/hw_escpos/escpos/escpos.py b/addons/hw_escpos/escpos/escpos.py
index 7159ac3b31e5f28e4e093187665bb3eeac6f02ab..f31b16198943dd21ee0b4dfc3ec63f2dbc109fe8 100644
--- a/addons/hw_escpos/escpos/escpos.py
+++ b/addons/hw_escpos/escpos/escpos.py
@@ -522,6 +522,10 @@ class Escpos:
         # Print Code
         if code:
             self._raw(code)
+            # We are using type A commands
+            # So we need to add the 'NULL' character
+            # https://github.com/python-escpos/python-escpos/pull/98/files#diff-a0b1df12c7c67e38915adbe469051e2dR444
+            self._raw('\x00')
         else:
             raise exception.BarcodeCodeError()
 
diff --git a/addons/l10n_fr_certification/models/res_company.py b/addons/l10n_fr_certification/models/res_company.py
index 97aa993aa7e6b3d670265a44ca01e8c4e7680a89..f5f962f6fa326a4c4c65e8cf41ade14e43ba43b7 100644
--- a/addons/l10n_fr_certification/models/res_company.py
+++ b/addons/l10n_fr_certification/models/res_company.py
@@ -30,6 +30,9 @@ class ResCompany(models.Model):
             if company._is_accounting_unalterable():
                 sequence_fields = ['l10n_fr_secure_sequence_id']
                 company._create_secure_sequence(sequence_fields)
+        # fiscalyear_lock_date can't be set to a prior date
+        if 'fiscalyear_lock_date' in vals or 'period_lock_date' in vals:
+            self._check_lock_dates(vals)
         return res
 
     def _create_secure_sequence(self, sequence_fields):
diff --git a/addons/l10n_generic_coa/__init__.py b/addons/l10n_generic_coa/__init__.py
index 67dee8c60dbf8317b263fbc3279f0823b2eb4b35..d417dc6807ce40bce885a2985d34ab3a70b396be 100644
--- a/addons/l10n_generic_coa/__init__.py
+++ b/addons/l10n_generic_coa/__init__.py
@@ -1,2 +1,8 @@
 # -*- coding: utf-8 -*-
 # Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+
+def uninstall_hook(cr, registry):
+    cr.execute(
+        "DELETE FROM ir_model_data WHERE module = 'l10n_generic_coa'"
+    )
diff --git a/addons/l10n_generic_coa/__manifest__.py b/addons/l10n_generic_coa/__manifest__.py
index 314e13f44ff61c86f7727153fc826e0c2787afac..2f04d8df53012803e700d23b4eebc23b7d019f9d 100644
--- a/addons/l10n_generic_coa/__manifest__.py
+++ b/addons/l10n_generic_coa/__manifest__.py
@@ -24,4 +24,5 @@ Install some generic chart of accounts.
         'data/account_invoice_demo.xml',
     ],
     'website': 'https://www.odoo.com/page/accounting',
+    'uninstall_hook': 'uninstall_hook',
 }
diff --git a/addons/mass_mailing/models/mail_mail.py b/addons/mass_mailing/models/mail_mail.py
index 5889bbeede1be417645b937d42eba71cccf51dd3..537d333cd7b3a04686adb47d8d8dba4439e565c0 100644
--- a/addons/mass_mailing/models/mail_mail.py
+++ b/addons/mass_mailing/models/mail_mail.py
@@ -84,7 +84,7 @@ class MailMail(models.Model):
     def send_get_email_dict(self, partner=None):
         # TDE: temporary addition (mail was parameter) due to semi-new-API
         res = super(MailMail, self).send_get_email_dict(partner)
-        base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
+        base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url').rstrip('/')
         if self.mailing_id and res.get('body') and res.get('email_to'):
             emails = tools.email_split(res.get('email_to')[0])
             email_to = emails and emails[0] or False
diff --git a/addons/point_of_sale/models/pos_config.py b/addons/point_of_sale/models/pos_config.py
index 1b41565401db7dafcd720492d485dfd0377ad0ef..2ef110a6dae79d45a4dba0cd8f3be3828b0e974a 100644
--- a/addons/point_of_sale/models/pos_config.py
+++ b/addons/point_of_sale/models/pos_config.py
@@ -364,11 +364,12 @@ class PosConfig(models.Model):
 
     @api.multi
     def write(self, vals):
-        if (self.is_posbox or vals.get('is_posbox')) and (self.iface_customer_facing_display or vals.get('iface_customer_facing_display')):
-            facing_display = (self.customer_facing_display_html or vals.get('customer_facing_display_html') or '').strip()
-            if not facing_display:
-                vals['customer_facing_display_html'] = self._compute_default_customer_html()
         result = super(PosConfig, self).write(vals)
+
+        config_display = self.filtered(lambda c: c.is_posbox and c.iface_customer_facing_display and not (c.customer_facing_display_html or '').strip())
+        if config_display:
+            super(PosConfig, config_display).write({'customer_facing_display_html': self._compute_default_customer_html()})
+
         self.sudo()._set_fiscal_position()
         self.sudo()._check_modules_to_install()
         self.sudo()._check_groups_implied()
diff --git a/addons/purchase/data/mail_template_data.xml b/addons/purchase/data/mail_template_data.xml
index d3c075c3ff05c7f8e026f5d16394790d432a08f3..072342afc871c8374d5fcf57adcd9f40a7ef74df 100644
--- a/addons/purchase/data/mail_template_data.xml
+++ b/addons/purchase/data/mail_template_data.xml
@@ -5,7 +5,7 @@
         <!--Email template -->
         <record id="email_template_edi_purchase" model="mail.template">
             <field name="name">RFQ - Send by Email</field>
-            <field name="email_from">${(object.create_uid.email and '%s &lt;%s&gt;' % (object.create_uid.name, object.create_uid.email) or '')|safe}</field>
+            <field name="email_from">${(object.create_uid.email and '&quot;%s&quot; &lt;%s&gt;' % (object.create_uid.name, object.create_uid.email) or '')|safe}</field>
             <field name="subject">${object.company_id.name} Order (Ref ${object.name or 'n/a' })</field>
             <field name="partner_to">${object.partner_id.id}</field>
             <field name="model_id" ref="purchase.model_purchase_order"/>
@@ -46,7 +46,7 @@ from ${object.company_id.name}.
         <!--Email template -->
         <record id="email_template_edi_purchase_done" model="mail.template">
             <field name="name">Purchase Order - Send by Email</field>
-            <field name="email_from">${(object.create_uid.email and '%s &lt;%s&gt;' % (object.create_uid.name, object.create_uid.email) or '')|safe}</field>
+            <field name="email_from">${(object.create_uid.email and '&quot;%s&quot; &lt;%s&gt;' % (object.create_uid.name, object.create_uid.email) or '')|safe}</field>
             <field name="subject">${object.company_id.name} Order (Ref ${object.name or 'n/a' })</field>
             <field name="partner_to">${object.partner_id.id}</field>
             <field name="model_id" ref="purchase.model_purchase_order"/>
diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py
index 35ff379f620b8547bd2bb66fa24ac67930759ce9..1bdf4b3bf94353ed616bfc6df83882abd0452d0f 100644
--- a/addons/purchase/models/purchase.py
+++ b/addons/purchase/models/purchase.py
@@ -942,12 +942,12 @@ class ProcurementRule(models.Model):
         if domain in cache:
             po = cache[domain]
         else:
-            po = self.env['purchase.order'].search([dom for dom in domain])
+            po = self.env['purchase.order'].sudo().search([dom for dom in domain])
             po = po[0] if po else False
             cache[domain] = po
         if not po:
             vals = self._prepare_purchase_order(product_id, product_qty, product_uom, origin, values, partner)
-            po = self.env['purchase.order'].create(vals)
+            po = self.env['purchase.order'].sudo().create(vals)
             cache[domain] = po
         elif not po.origin or origin not in po.origin.split(', '):
             if po.origin:
@@ -968,7 +968,7 @@ class ProcurementRule(models.Model):
                     break
         if not po_line:
             vals = self._prepare_purchase_order_line(product_id, product_qty, product_uom, values, po, supplier)
-            self.env['purchase.order.line'].create(vals)
+            self.env['purchase.order.line'].sudo().create(vals)
 
     def _get_purchase_schedule_date(self, values):
         """Return the datetime value to use as Schedule Date (``date_planned``) for the
diff --git a/addons/sale/data/mail_template_data.xml b/addons/sale/data/mail_template_data.xml
index b1ee2412b94141cec469597580291ae5144d8022..57d66f904dba498a5a34ea12aedcc479f19ea219 100644
--- a/addons/sale/data/mail_template_data.xml
+++ b/addons/sale/data/mail_template_data.xml
@@ -5,7 +5,7 @@
         <!--Email template -->
         <record id="email_template_edi_sale" model="mail.template">
             <field name="name">Sales Order - Send by Email</field>
-            <field name="email_from">${(object.user_id.email and '%s &lt;%s&gt;' % (object.user_id.name, object.user_id.email) or '')|safe}</field>
+            <field name="email_from">${(object.user_id.email and '&quot;%s&quot; &lt;%s&gt;' % (object.user_id.name, object.user_id.email) or '')|safe}</field>
             <field name="subject">${object.company_id.name} ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'} (Ref ${object.name or 'n/a' })</field>
             <field name="partner_to">${object.partner_id.id}</field>
             <field name="model_id" ref="sale.model_sale_order"/>
diff --git a/addons/sale/models/product_template.py b/addons/sale/models/product_template.py
index e1ffda478abf948695a5b74ed41a9be18d69c35d..79dffdd719bbea33c2bcb4ae0db8477afe54820e 100644
--- a/addons/sale/models/product_template.py
+++ b/addons/sale/models/product_template.py
@@ -24,7 +24,7 @@ class ProductTemplate(models.Model):
     @api.depends('product_variant_ids.sales_count')
     def _sales_count(self):
         for product in self:
-            product.sales_count = sum([p.sales_count for p in product.product_variant_ids])
+            product.sales_count = sum([p.sales_count for p in product.with_context(active_test=False).product_variant_ids])
 
     @api.multi
     def action_view_sales(self):
diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py
index e59af341990a5f15c7af7eb4581e937f4e22434c..d88fbca0ceb40b5be2175b376d581fc2fdb265ea 100644
--- a/addons/stock/models/stock_move.py
+++ b/addons/stock/models/stock_move.py
@@ -925,11 +925,15 @@ class StockMove(models.Model):
                 if not move.move_orig_ids:
                     if move.procure_method == 'make_to_order':
                         continue
+                    # If we don't need any quantity, consider the move assigned.
+                    need = move.product_qty - move.reserved_availability
+                    if float_is_zero(need, precision_rounding=move.product_id.uom_id.rounding):
+                        assigned_moves |= move
+                        continue
                     # Reserve new quants and create move lines accordingly.
                     available_quantity = self.env['stock.quant']._get_available_quantity(move.product_id, move.location_id)
                     if available_quantity <= 0:
                         continue
-                    need = move.product_qty - move.reserved_availability
                     taken_quantity = move._update_reserved_quantity(need, available_quantity, move.location_id, strict=False)
                     if float_is_zero(taken_quantity, precision_rounding=move.product_id.uom_id.rounding):
                         continue
diff --git a/addons/stock/report/report_stock_forecast.py b/addons/stock/report/report_stock_forecast.py
index 700aa5455de80563897188e3cc37091b5d74c91b..1ca3b1195b5b4a7fd804e7eaee34ff438ed79c22 100644
--- a/addons/stock/report/report_stock_forecast.py
+++ b/addons/stock/report/report_stock_forecast.py
@@ -66,7 +66,7 @@ class ReportStockForecat(models.Model):
             LEFT JOIN
             stock_location source_location ON sm.location_id = source_location.id
             WHERE
-            sm.state IN ('confirmed','assigned','waiting') and
+            sm.state IN ('confirmed','partially_available','assigned','waiting') and
             source_location.usage != 'internal' and dest_location.usage = 'internal'
             GROUP BY sm.date_expected,sm.product_id, sm.company_id
             UNION ALL
@@ -88,7 +88,7 @@ class ReportStockForecat(models.Model):
             LEFT JOIN
                stock_location dest_location ON sm.location_dest_id = dest_location.id
             WHERE
-                sm.state IN ('confirmed','assigned','waiting') and
+                sm.state IN ('confirmed','partially_available','assigned','waiting') and
             source_location.usage = 'internal' and dest_location.usage != 'internal'
             GROUP BY sm.date_expected,sm.product_id, sm.company_id)
          as MAIN
diff --git a/addons/stock/tests/test_move.py b/addons/stock/tests/test_move.py
index df57065b8d7e3f87bc28c7b8caed2438fab76bfb..8326328bc958ee25e98b75ad6c2662d6e5a056b3 100644
--- a/addons/stock/tests/test_move.py
+++ b/addons/stock/tests/test_move.py
@@ -925,6 +925,31 @@ class StockMove(TransactionCase):
         self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product2, self.customer_location), 12.0)
         self.assertEqual(len(self.env['stock.quant']._gather(self.product2, self.customer_location)), 12)
 
+    def test_availability_8(self):
+        """ Test the assignment mechanism when the product quantity is decreased on a partially
+            reserved stock move.
+        """
+        # make some stock
+        self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 3.0)
+        self.assertAlmostEqual(self.product1.qty_available, 3.0)
+
+        move_partial = self.env['stock.move'].create({
+            'name': 'test_partial',
+            'location_id': self.stock_location.id,
+            'location_dest_id': self.customer_location.id,
+            'product_id': self.product1.id,
+            'product_uom': self.uom_unit.id,
+            'product_uom_qty': 5.0,
+        })
+
+        move_partial._action_confirm()
+        move_partial._action_assign()
+        self.assertAlmostEqual(self.product1.virtual_available, -2.0)
+        self.assertEqual(move_partial.state, 'partially_available')
+        move_partial.product_uom_qty = 3.0
+        move_partial._action_assign()
+        self.assertEqual(move_partial.state, 'assigned')
+
     def test_unreserve_1(self):
         """ Check that unreserving a stock move sets the products reserved as available and
         set the state back to confirmed.
@@ -3767,4 +3792,3 @@ class StockMove(TransactionCase):
         picking.button_validate()
         self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 0)
         self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.customer_location), 2)
-
diff --git a/addons/stock_account/views/stock_account_views.xml b/addons/stock_account/views/stock_account_views.xml
index 656d6ea7364f6cfa125292eab41ce8075dea84a4..77747cdea5ce388f75402745300b1027ef335bb4 100644
--- a/addons/stock_account/views/stock_account_views.xml
+++ b/addons/stock_account/views/stock_account_views.xml
@@ -44,7 +44,7 @@
             <field name="model">stock.return.picking</field>
             <field name="arch" type="xml">
                 <xpath expr="//field[@name='product_return_moves']/tree" position="inside">
-                    <field name="to_refund" widget="boolean_toggle"/>
+                    <field name="to_refund"/>
                 </xpath>
             </field>
         </record>
diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py
index 62d862afe35a6ca90cd9b397fd075eec67eea76e..ead68c28274df05dbc63c1ff5590c4d088d69428 100644
--- a/addons/website/controllers/main.py
+++ b/addons/website/controllers/main.py
@@ -18,8 +18,9 @@ from odoo import http, models, fields, _
 from odoo.http import request
 from odoo.tools import pycompat, OrderedSet
 from odoo.addons.http_routing.models.ir_http import slug, _guess_mimetype
-from odoo.addons.web.controllers.main import WebClient, Binary, Home
+from odoo.addons.web.controllers.main import WebClient, Binary
 from odoo.addons.portal.controllers.portal import pager as portal_pager
+from odoo.addons.portal.controllers.web import Home
 
 logger = logging.getLogger(__name__)
 
diff --git a/addons/website_event/static/src/js/website_event.js b/addons/website_event/static/src/js/website_event.js
index 3aa8691fca74524a30e70fb694b3d4956ac946cc..ebe38118559e399e53f2bf8f54421b82b81a2ab6 100644
--- a/addons/website_event/static/src/js/website_event.js
+++ b/addons/website_event/static/src/js/website_event.js
@@ -20,8 +20,11 @@ return instance.appendTo($form).then(function () {
 odoo.define('website_event.website_event', function (require) {
 
 var ajax = require('web.ajax');
+var core = require('web.core');
 var Widget = require('web.Widget');
 
+var _t = core._t;
+
 // Catch registration form event, because of JS for attendee details
 var EventRegistrationForm = Widget.extend({
     start: function () {
@@ -31,7 +34,6 @@ var EventRegistrationForm = Widget.extend({
                 .off('click')
                 .removeClass('a-submit')
                 .click(function (ev) {
-                    $(this).attr('disabled', true);
                     self.on_click(ev);
                 });
         });
@@ -43,15 +45,18 @@ var EventRegistrationForm = Widget.extend({
         var $form = $(ev.currentTarget).closest('form');
         var $button = $(ev.currentTarget).closest('[type="submit"]');
         var post = {};
+        $('#registration_form table').siblings('.alert').remove();
         $('#registration_form select').each(function () {
             post[$(this).attr('name')] = $(this).val();
         });
         var tickets_ordered = _.some(_.map(post, function (value, key) { return parseInt(value); }));
         if (!tickets_ordered) {
-            return $('#registration_form table').after(
-                '<div class="alert alert-info">Please select at least one ticket.</div>'
-            );
+            $('<div class="alert alert-info"/>')
+                .text(_t('Please select at least one ticket.'))
+                .insertAfter('#registration_form table');
+            return $.Deferred();
         } else {
+            $button.attr('disabled', true);
             return ajax.jsonRpc($form.attr('action'), 'call', post).then(function (modal) {
                 var $modal = $(modal);
                 $modal.find('.modal-body > div').removeClass('container'); // retrocompatibility - REMOVE ME in master / saas-19
diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml
index 385234ad9ea47fdfa90f1a6a3aa8c8c97f35b65c..03bb0252b466f37a2e23035226f5e38d46f12bfa 100644
--- a/addons/website_sale/views/templates.xml
+++ b/addons/website_sale/views/templates.xml
@@ -420,7 +420,7 @@
                                 <div id="o-carousel-product" class="carousel slide" data-ride="carousel" data-interval="0">
                                   <div class="carousel-outer">
                                     <div class="carousel-inner">
-                                        <div t-if="variant_img" class="item active" itemprop="image" t-field="product.product_variant_id.image" t-options="{'widget': 'image', 'class': 'product_detail_img js_variant_img', 'alt-field': 'name', 'zoom': 'image', 'unique': product['__last_update'] + (product.product_variant_id['__last_update'] or '')}"/>
+                                        <div t-if="variant_img" class="item active" itemprop="image" t-field="product[:1].product_variant_id.image" t-options="{'widget': 'image', 'class': 'product_detail_img js_variant_img', 'alt-field': 'name', 'zoom': 'image', 'unique': product['__last_update'] + (product.product_variant_id['__last_update'] or '')}"/>
                                         <div t-attf-class="item#{'' if variant_img else ' active'}" itemprop="image" t-field="product.image" t-options="{'widget': 'image', 'class': 'product_detail_img', 'alt-field': 'name', 'zoom': 'image', 'unique': product['__last_update']}"/>
                                         <t t-if="len(image_ids)" t-foreach="image_ids" t-as="pimg">
                                             <div class="item" t-field="pimg.image" t-options='{"widget": "image", "class": "product_detail_img", "alt-field": "name", "zoom": "image" }'/>
diff --git a/doc/setup/deploy.rst b/doc/setup/deploy.rst
index 13d4198cceb3ae2ebe3a695f5c6e378686fd6887..dadd1484d460d02832f114964e52fabb13a4f411 100644
--- a/doc/setup/deploy.rst
+++ b/doc/setup/deploy.rst
@@ -534,10 +534,14 @@ Supported Browsers
 Odoo is supported by multiple browsers for each of its versions. No 
 distinction is made according to the browser version in order to be
 up-to-date. Odoo is supported on the current browser version. The list 
-of the supported browsers by Odoo version is the following:
+of the supported browsers is the following:
+
+- IE11,
+- Mozilla Firefox,
+- Google Chrome,
+- Safari,
+- Microsoft Edge
 
-- **Odoo 9:** IE11, Mozilla Firefox, Google Chrome, Safari, Microsoft Edge
-- **Odoo 10+:** Mozilla Firefox, Google Chrome, Safari, Microsoft Edge
 
 .. [#different-machines]
     to have multiple Odoo installations use the same PostgreSQL database,
diff --git a/odoo/addons/base/views/res_config_settings_views.xml b/odoo/addons/base/views/res_config_settings_views.xml
index 0f130d3ce1078acb55a88e3200ff1d8bbdb0419c..74ce361e96b72ee2028e726665d6b09ababd906e 100644
--- a/odoo/addons/base/views/res_config_settings_views.xml
+++ b/odoo/addons/base/views/res_config_settings_views.xml
@@ -16,7 +16,7 @@
                         </div>
                         <header>
                             <button string="Save" type="object" name="execute" class="oe_highlight" />
-                            <button string="Discard" type="object" name="cancel" />
+                            <button string="Discard" type="object" name="cancel" special="cancel" />
                         </header>
                     </div>
                     <div class="o_setting_container">
diff --git a/odoo/addons/base/views/res_partner_views.xml b/odoo/addons/base/views/res_partner_views.xml
index 3eb69146c4c78d48e64d9d6b9942a1b47d08208d..2037a45f65bba8bc0e4b50b29235e48613607dd5 100644
--- a/odoo/addons/base/views/res_partner_views.xml
+++ b/odoo/addons/base/views/res_partner_views.xml
@@ -171,7 +171,7 @@
                                 <field name="country_id" placeholder="Country" class="o_address_country" options='{"no_open": True, "no_create": True}'
                                     attrs="{'readonly': [('type', '=', 'contact'),('parent_id', '!=', False)]}"/>
                             </div>
-                            <field name="vat" placeholder="e.g. BE0477472701"/>
+                            <field name="vat" placeholder="e.g. BE0477472701" attrs="{'readonly': [('parent_id','!=',False)]}"/>
                         </group>
                         <group>
                             <field name="function" placeholder="e.g. Sales Director"
@@ -244,7 +244,7 @@
                                 <field name="country_id" placeholder="Country" class="o_address_country" options='{"no_open": True, "no_create": True}'
                                     attrs="{'readonly': [('type', '=', 'contact'),('parent_id', '!=', False)]}"/>
                             </div>
-                            <field name="vat" placeholder="e.g. BE0477472701"/>
+                            <field name="vat" placeholder="e.g. BE0477472701" attrs="{'readonly': [('parent_id','!=',False)]}"/>
                             <field name="category_id" widget="many2many_tags" options="{'color_field': 'color', 'no_create_edit': True}" placeholder="Tags..."/>
                         </group>
                         <group>
diff --git a/odoo/modules/loading.py b/odoo/modules/loading.py
index 4dda572fb1340dc70e27175943b2579333bb60c5..b9f4e2fdc7bfe0ec5a737624a85ca4d766a07fb1 100644
--- a/odoo/modules/loading.py
+++ b/odoo/modules/loading.py
@@ -25,7 +25,8 @@ _logger = logging.getLogger(__name__)
 _test_logger = logging.getLogger('odoo.tests')
 
 
-def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=None, report=None):
+def load_module_graph(cr, graph, status=None, perform_checks=True,
+                      skip_modules=None, report=None, models_to_check=None):
     """Migrates+Updates or Installs all module nodes from ``graph``
        :param graph: graph of module nodes to load
        :param status: deprecated parameter, unused, left to avoid changing signature in 8.0
@@ -93,6 +94,9 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
             if kind in ('demo', 'test'):
                 threading.currentThread().testing = False
 
+    if models_to_check is None:
+        models_to_check = set()
+
     processed_modules = []
     loaded_modules = []
     registry = odoo.registry(cr.dbname)
@@ -106,6 +110,8 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
     t0 = time.time()
     t0_sql = odoo.sql_db.sql_counter
 
+    models_updated = set()
+
     for index, package in enumerate(graph, 1):
         module_name = package.name
         module_id = package.id
@@ -127,10 +133,20 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
         model_names = registry.load(cr, package)
 
         loaded_modules.append(package.name)
-        if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'):
+        if (hasattr(package, 'init') or hasattr(package, 'update')
+                or package.state in ('to install', 'to upgrade')):
+            models_updated |= set(model_names)
+            models_to_check -= set(model_names)
             registry.setup_models(cr)
             registry.init_models(cr, model_names, {'module': package.name})
             cr.commit()
+        elif package.state != 'to remove':
+            # The current module has simply been loaded. The models extended by this module
+            # and for which we updated the schema, must have their schema checked again.
+            # This is because the extension may have changed the model,
+            # e.g. adding required=True to an existing field, but the schema has not been
+            # updated by this module because it's not marked as 'to upgrade/to install'.
+            models_to_check |= set(model_names) & models_updated
 
         idref = {}
 
@@ -225,9 +241,14 @@ def _check_module_names(cr, module_names):
             incorrect_names = mod_names.difference([x['name'] for x in cr.dictfetchall()])
             _logger.warning('invalid module names, ignored: %s', ", ".join(incorrect_names))
 
-def load_marked_modules(cr, graph, states, force, progressdict, report, loaded_modules, perform_checks):
+def load_marked_modules(cr, graph, states, force, progressdict, report,
+                        loaded_modules, perform_checks, models_to_check=None):
     """Loads modules marked with ``states``, adding them to ``graph`` and
        ``loaded_modules`` and returns a list of installed/upgraded modules."""
+
+    if models_to_check is None:
+        models_to_check = set()
+
     processed_modules = []
     while True:
         cr.execute("SELECT name from ir_module_module WHERE state IN %s" ,(tuple(states),))
@@ -236,7 +257,10 @@ def load_marked_modules(cr, graph, states, force, progressdict, report, loaded_m
             break
         graph.add_modules(cr, module_list, force)
         _logger.debug('Updating graph with %d more modules', len(module_list))
-        loaded, processed = load_module_graph(cr, graph, progressdict, report=report, skip_modules=loaded_modules, perform_checks=perform_checks)
+        loaded, processed = load_module_graph(
+            cr, graph, progressdict, report=report, skip_modules=loaded_modules,
+            perform_checks=perform_checks, models_to_check=models_to_check
+        )
         processed_modules.extend(processed)
         loaded_modules.extend(loaded)
         if not processed:
@@ -250,6 +274,8 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
     if force_demo:
         force.append('demo')
 
+    models_to_check = set()
+
     cr = db.cursor()
     try:
         if not odoo.modules.db.is_initialized(cr):
@@ -278,7 +304,9 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
         # processed_modules: for cleanup step after install
         # loaded_modules: to avoid double loading
         report = registry._assertion_report
-        loaded_modules, processed_modules = load_module_graph(cr, graph, status, perform_checks=update_module, report=report)
+        loaded_modules, processed_modules = load_module_graph(
+            cr, graph, status, perform_checks=update_module,
+            report=report, models_to_check=models_to_check)
 
         load_lang = tools.config.pop('load_language')
         if load_lang or update_module:
@@ -333,11 +361,11 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
             previously_processed = len(processed_modules)
             processed_modules += load_marked_modules(cr, graph,
                 ['installed', 'to upgrade', 'to remove'],
-                force, status, report, loaded_modules, update_module)
+                force, status, report, loaded_modules, update_module, models_to_check)
             if update_module:
                 processed_modules += load_marked_modules(cr, graph,
                     ['to install'], force, status, report,
-                    loaded_modules, update_module)
+                    loaded_modules, update_module, models_to_check)
 
         registry.loaded = True
         registry.setup_models(cr)
@@ -407,6 +435,16 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
                 cr.commit()
                 return registry
 
+        # STEP 5.5: Verify extended fields on every model
+        # This will fix the schema of all models in a situation such as:
+        #   - module A is loaded and defines model M;
+        #   - module B is installed/upgraded and extends model M;
+        #   - module C is loaded and extends model M;
+        #   - module B and C depend on A but not on each other;
+        # The changes introduced by module C are not taken into account by the upgrade of B.
+        if models_to_check:
+            registry.init_models(cr, list(models_to_check), {'models_to_check': True})
+
         # STEP 6: verify custom views on every model
         if update_module:
             env = api.Environment(cr, SUPERUSER_ID, {})
diff --git a/odoo/modules/registry.py b/odoo/modules/registry.py
index c7d8711287b6f5841adcc80702fb3077f9c7034c..3115470adeb24d4c048fc57c37a0353f9904145b 100644
--- a/odoo/modules/registry.py
+++ b/odoo/modules/registry.py
@@ -298,6 +298,8 @@ class Registry(Mapping):
         """
         if 'module' in context:
             _logger.info('module %s: creating or updating database tables', context['module'])
+        elif context.get('models_to_check', False):
+            _logger.info("verifying fields for every extended model")
 
         env = odoo.api.Environment(cr, SUPERUSER_ID, context)
         models = [env[model_name] for model_name in model_names]