diff --git a/addons/bus/models/res_partner.py b/addons/bus/models/res_partner.py
index b0b4c23d74ffcf4b74a975e64ddd11eedc640848..a476e0c08dcceccf414d6f716183248ab4b44adf 100644
--- a/addons/bus/models/res_partner.py
+++ b/addons/bus/models/res_partner.py
@@ -15,13 +15,14 @@ class ResPartner(models.Model):
         self.env.cr.execute("""
             SELECT
                 U.partner_id as id,
-                CASE WHEN age(now() AT TIME ZONE 'UTC', B.last_poll) > interval %s THEN 'offline'
-                     WHEN age(now() AT TIME ZONE 'UTC', B.last_presence) > interval %s THEN 'away'
+                CASE WHEN age(now() AT TIME ZONE 'UTC', max(B.last_poll)) > interval %s THEN 'offline'
+                     WHEN age(now() AT TIME ZONE 'UTC', max(B.last_presence)) > interval %s THEN 'away'
                      ELSE 'online'
                 END as status
             FROM bus_presence B
                 JOIN res_users U ON B.user_id = U.id
             WHERE U.partner_id IN %s AND U.active = 't'
+         GROUP BY U.partner_id
         """, ("%s seconds" % DISCONNECTION_TIMER, "%s seconds" % AWAY_TIMER, tuple(self.ids)))
         res = dict(((status['id'], status['status']) for status in self.env.cr.dictfetchall()))
         for partner in self:
diff --git a/addons/l10n_be_intrastat/wizard/xml_decl.py b/addons/l10n_be_intrastat/wizard/xml_decl.py
index aaf67485a112a15dc47eaca5a9898d82b1778fc4..988f70716d2cbd8e3b711f9f565e2e1f0afdf1e1 100644
--- a/addons/l10n_be_intrastat/wizard/xml_decl.py
+++ b/addons/l10n_be_intrastat/wizard/xml_decl.py
@@ -102,35 +102,140 @@ class XmlDeclaration(models.TransientModel):
             'res_id': self.id,
         }
 
+    def _build_intrastat_line(self, numlgn, item, linekey, amounts, dispatchmode, extendedmode):
+        self._set_Dim(item, 'EXSEQCODE', unicode(numlgn))
+        self._set_Dim(item, 'EXTRF', unicode(linekey.EXTRF))
+        self._set_Dim(item, 'EXCNT', unicode(linekey.EXCNT))
+        self._set_Dim(item, 'EXTTA', unicode(linekey.EXTTA))
+        self._set_Dim(item, 'EXREG', unicode(linekey.EXREG))
+        self._set_Dim(item, 'EXTGO', unicode(linekey.EXGO))
+        if extendedmode:
+            self._set_Dim(item, 'EXTPC', unicode(linekey.EXTPC))
+            self._set_Dim(item, 'EXDELTRM', unicode(linekey.EXDELTRM))
+        self._set_Dim(item, 'EXTXVAL', unicode(round(amounts[0], 0)).replace(".", ","))
+        self._set_Dim(item, 'EXWEIGHT', unicode(round(amounts[1], 0)).replace(".", ","))
+        self._set_Dim(item, 'EXUNITS', unicode(round(amounts[2], 0)).replace(".", ","))
+
+    def _get_intrastat_linekey(self, declcode, inv_line, dispatchmode, extendedmode):
+        IntrastatRegion = self.env['l10n_be_intrastat.region']
+        company = self.company_id
+
+        #Check type of transaction
+        if inv_line.intrastat_transaction_id:
+            extta = inv_line.intrastat_transaction_id.code
+        else:
+            extta = "1"
+        #Check country
+        if inv_line.invoice_id.intrastat_country_id:
+            excnt = inv_line.invoice_id.intrastat_country_id.code
+        else:
+            excnt = inv_line.invoice_id.partner_shipping_id.country_id.code or inv_line.invoice_id.partner_id.country_id.code
+
+        #Check region
+        #If purchase, comes from purchase order, linked to a location,
+        #which is linked to the warehouse
+        #if sales, the sales order is linked to the warehouse
+        #if sales, from a delivery order, linked to a location,
+        #which is linked to the warehouse
+        #If none found, get the company one.
+        exreg = None
+        if inv_line.invoice_id.type in ('in_invoice', 'in_refund'):
+            #comes from purchase
+            po_lines = self.env['purchase.order.line'].search([('invoice_lines', 'in', inv_line.id)], limit=1)
+            if po_lines:
+                if self._is_situation_triangular(company, po_line=po_lines):
+                    return
+                location = self.env['stock.location'].browse(po_lines.order_id._get_destination_location())
+                region_id = self.env['stock.warehouse'].get_regionid_from_locationid(location)
+                if region_id:
+                    exreg = IntrastatRegion.browse(region_id).code
+        elif inv_line.invoice_id.type in ('out_invoice', 'out_refund'):
+            #comes from sales
+            so_lines = self.env['sale.order.line'].search([('invoice_lines', 'in', inv_line.id)], limit=1)
+            if so_lines:
+                if self._is_situation_triangular(company, so_line=so_lines):
+                    return
+                saleorder = so_lines.order_id
+                if saleorder and saleorder.warehouse_id and saleorder.warehouse_id.region_id:
+                    exreg = IntrastatRegion.browse(saleorder.warehouse_id.region_id.id).code
+
+        if not exreg:
+            if company.region_id:
+                exreg = company.region_id.code
+            else:
+                self._company_warning(_('The Intrastat Region of the selected company is not set, '
+                      'please make sure to configure it first.'))
+
+        #Check commodity codes
+        intrastat_id = inv_line.product_id.get_intrastat_recursively()
+        if intrastat_id:
+            exgo = self.env['report.intrastat.code'].browse(intrastat_id).name
+        else:
+            raise exceptions.Warning(
+                _('Product "%s" has no intrastat code, please configure it') % inv_line.product_id.display_name)
+
+        #In extended mode, 2 more fields required
+        if extendedmode:
+            #Check means of transport
+            if inv_line.invoice_id.transport_mode_id:
+                extpc = inv_line.invoice_id.transport_mode_id.code
+            elif company.transport_mode_id:
+                extpc = company.transport_mode_id.code
+            else:
+                self._company_warning(_('The default Intrastat transport mode of your company '
+                      'is not set, please make sure to configure it first.'))
+
+            #Check incoterm
+            if inv_line.invoice_id.incoterm_id:
+                exdeltrm = inv_line.invoice_id.incoterm_id.code
+            elif company.incoterm_id:
+                exdeltrm = company.incoterm_id.code
+            else:
+                self._company_warning(_('The default Incoterm of your company is not set, '
+                      'please make sure to configure it first.'))
+        else:
+            extpc = ""
+            exdeltrm = ""
+        intrastatkey = namedtuple("intrastatkey",
+                                  ['EXTRF', 'EXCNT', 'EXTTA', 'EXREG',
+                                   'EXGO', 'EXTPC', 'EXDELTRM'])
+        return intrastatkey(EXTRF=declcode, EXCNT=excnt,
+                            EXTTA=extta, EXREG=exreg, EXGO=exgo,
+                            EXTPC=extpc, EXDELTRM=exdeltrm)
+
+    def _get_reception_code(self, extended):
+        return 'EX19E' if extended else 'EX19S'
+
+    def _get_reception_form(self, extended):
+        return 'EXF19E' if extended else 'EXF19S'
+
+    def _get_expedition_code(self, extended):
+        return 'EX29E' if extended else 'EX29S'
+
+    def _get_expedition_form(self, extended):
+        return 'EXF29E' if extended else 'EXF29S'
+
     @api.multi
     def _get_lines(self, dispatchmode=False, extendedmode=False):
         company = self.company_id
-        IntrastatRegion = self.env['l10n_be_intrastat.region']
 
         if dispatchmode:
             mode1 = 'out_invoice'
             mode2 = 'in_refund'
-            declcode = "29"
+            declcode = self._get_expedition_code(extendedmode)
+            declform = self._get_expedition_form(extendedmode)
         else:
             mode1 = 'in_invoice'
             mode2 = 'out_refund'
-            declcode = "19"
+            declcode = self._get_reception_code(extendedmode)
+            declform = self._get_reception_form(extendedmode)
 
         decl = ET.Element('Report')
-        if not extendedmode:
-            decl.set('code', 'EX%sS' % declcode)
-        else:
-            decl.set('code', 'EX%sE' % declcode)
+        decl.set('code', declcode)
         decl.set('date', '%s-%s' % (self.year, self.month))
         datas = ET.SubElement(decl, 'Data')
-        if not extendedmode:
-            datas.set('form', 'EXF%sS' % declcode)
-        else:
-            datas.set('form', 'EXF%sE' % declcode)
+        datas.set('form', declform)
         datas.set('close', 'true')
-        intrastatkey = namedtuple("intrastatkey",
-                                  ['EXTRF', 'EXCNT', 'EXTTA', 'EXREG',
-                                   'EXGO', 'EXTPC', 'EXDELTRM'])
         entries = {}
 
         query = """
@@ -166,86 +271,10 @@ class XmlDeclaration(models.TransientModel):
         invoicelines = self.env['account.invoice.line'].browse(invoicelines_ids)
 
         for inv_line in invoicelines:
+            linekey = self._get_intrastat_linekey(declcode, inv_line, dispatchmode, extendedmode)
+            if linekey is None:
+                continue
 
-            #Check type of transaction
-            if inv_line.intrastat_transaction_id:
-                extta = inv_line.intrastat_transaction_id.code
-            else:
-                extta = "1"
-            #Check country
-            if inv_line.invoice_id.intrastat_country_id:
-                excnt = inv_line.invoice_id.intrastat_country_id.code
-            else:
-                excnt = inv_line.invoice_id.partner_shipping_id.country_id.code or inv_line.invoice_id.partner_id.country_id.code
-
-            #Check region
-            #If purchase, comes from purchase order, linked to a location,
-            #which is linked to the warehouse
-            #if sales, the sales order is linked to the warehouse
-            #if sales, from a delivery order, linked to a location,
-            #which is linked to the warehouse
-            #If none found, get the company one.
-            exreg = None
-            if inv_line.invoice_id.type in ('in_invoice', 'in_refund'):
-                #comes from purchase
-                po_lines = self.env['purchase.order.line'].search([('invoice_lines', 'in', inv_line.id)], limit=1)
-                if po_lines:
-                    if self._is_situation_triangular(company, po_line=po_lines):
-                        continue
-                    location = self.env['stock.location'].browse(po_lines.order_id._get_destination_location())
-                    region_id = self.env['stock.warehouse'].get_regionid_from_locationid(location)
-                    if region_id:
-                        exreg = IntrastatRegion.browse(region_id).code
-            elif inv_line.invoice_id.type in ('out_invoice', 'out_refund'):
-                #comes from sales
-                so_lines = self.env['sale.order.line'].search([('invoice_lines', 'in', inv_line.id)], limit=1)
-                if so_lines:
-                    if self._is_situation_triangular(company, so_line=so_lines):
-                        continue
-                    saleorder = so_lines.order_id
-                    if saleorder and saleorder.warehouse_id and saleorder.warehouse_id.region_id:
-                        exreg = IntrastatRegion.browse(saleorder.warehouse_id.region_id.id).code
-
-            if not exreg:
-                if company.region_id:
-                    exreg = company.region_id.code
-                else:
-                    self._company_warning(_('The Intrastat Region of the selected company is not set, '
-                          'please make sure to configure it first.'))
-
-            #Check commodity codes
-            intrastat_id = inv_line.product_id.get_intrastat_recursively()
-            if intrastat_id:
-                exgo = self.env['report.intrastat.code'].browse(intrastat_id).name
-            else:
-                raise exceptions.Warning(
-                    _('Product "%s" has no intrastat code, please configure it') % inv_line.product_id.display_name)
-
-            #In extended mode, 2 more fields required
-            if extendedmode:
-                #Check means of transport
-                if inv_line.invoice_id.transport_mode_id:
-                    extpc = inv_line.invoice_id.transport_mode_id.code
-                elif company.transport_mode_id:
-                    extpc = company.transport_mode_id.code
-                else:
-                    self._company_warning(_('The default Intrastat transport mode of your company '
-                          'is not set, please make sure to configure it first.'))
-
-                #Check incoterm
-                if inv_line.invoice_id.incoterm_id:
-                    exdeltrm = inv_line.invoice_id.incoterm_id.code
-                elif company.incoterm_id:
-                    exdeltrm = company.incoterm_id.code
-                else:
-                    self._company_warning(_('The default Incoterm of your company is not set, '
-                          'please make sure to configure it first.'))
-            else:
-                extpc = ""
-                exdeltrm = ""
-            linekey = intrastatkey(EXTRF=declcode, EXCNT=excnt,
-                                   EXTTA=extta, EXREG=exreg, EXGO=exgo,
-                                   EXTPC=extpc, EXDELTRM=exdeltrm)
             #We have the key
             #calculate amounts
             if inv_line.price_unit and inv_line.quantity:
@@ -269,18 +298,7 @@ class XmlDeclaration(models.TransientModel):
                 continue
             numlgn += 1
             item = ET.SubElement(datas, 'Item')
-            self._set_Dim(item, 'EXSEQCODE', unicode(numlgn))
-            self._set_Dim(item, 'EXTRF', unicode(linekey.EXTRF))
-            self._set_Dim(item, 'EXCNT', unicode(linekey.EXCNT))
-            self._set_Dim(item, 'EXTTA', unicode(linekey.EXTTA))
-            self._set_Dim(item, 'EXREG', unicode(linekey.EXREG))
-            self._set_Dim(item, 'EXTGO', unicode(linekey.EXGO))
-            if extendedmode:
-                self._set_Dim(item, 'EXTPC', unicode(linekey.EXTPC))
-                self._set_Dim(item, 'EXDELTRM', unicode(linekey.EXDELTRM))
-            self._set_Dim(item, 'EXTXVAL', unicode(round(amounts[0], 0)).replace(".", ","))
-            self._set_Dim(item, 'EXWEIGHT', unicode(round(amounts[1], 0)).replace(".", ","))
-            self._set_Dim(item, 'EXUNITS', unicode(round(amounts[2], 0)).replace(".", ","))
+            self._build_intrastat_line(numlgn, item, linekey, amounts, dispatchmode, extendedmode)
 
         if numlgn == 0:
             #no datas
diff --git a/addons/l10n_be_intrastat_2019/__init__.py b/addons/l10n_be_intrastat_2019/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..dc5e6b693d19dcacd224b7ab27b26f75e66cb7b2
--- /dev/null
+++ b/addons/l10n_be_intrastat_2019/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import models
diff --git a/addons/l10n_be_intrastat_2019/__manifest__.py b/addons/l10n_be_intrastat_2019/__manifest__.py
new file mode 100644
index 0000000000000000000000000000000000000000..b3de01cd46b00521e89ee81c6da88280f1362d52
--- /dev/null
+++ b/addons/l10n_be_intrastat_2019/__manifest__.py
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+{
+    'name': 'Belgian Intrastat Declaration - Complement for 2019',
+    'category': 'Accounting',
+    'description': """
+Adds the possibility to specify the origin country of goods and the partner VAT in the Intrastat XML report.
+    """,
+    'depends': ['l10n_be_intrastat'],
+    'data': [
+        'views/account_invoice_line_view.xml',
+    ],
+    'auto_install': True,
+}
diff --git a/addons/l10n_be_intrastat_2019/i18n/l10n_be_intrastat_2019.pot b/addons/l10n_be_intrastat_2019/i18n/l10n_be_intrastat_2019.pot
new file mode 100644
index 0000000000000000000000000000000000000000..5953a7be43c00b435f68a2bb5c8570bb2448f5b0
--- /dev/null
+++ b/addons/l10n_be_intrastat_2019/i18n/l10n_be_intrastat_2019.pot
@@ -0,0 +1,32 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+#	* l10n_be_intrastat_2019
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 10.0+e\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2019-01-21 11:04+0000\n"
+"PO-Revision-Date: 2019-01-21 11:04+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: l10n_be_intrastat_2019
+#: model:ir.model,name:l10n_be_intrastat_2019.model_l10n_be_intrastat_xml_xml_decl
+msgid "Intrastat XML Declaration"
+msgstr ""
+
+#. module: l10n_be_intrastat_2019
+#: model:ir.model,name:l10n_be_intrastat_2019.model_account_invoice_line
+msgid "Invoice Line"
+msgstr ""
+
+#. module: l10n_be_intrastat_2019
+#: model:ir.model.fields,field_description:l10n_be_intrastat_2019.field_account_invoice_line_intrastat_product_origin_country_id
+msgid "Origin Country of Product"
+msgstr ""
+
diff --git a/addons/l10n_be_intrastat_2019/models/__init__.py b/addons/l10n_be_intrastat_2019/models/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..9115b4d66b62291065062392be8677c6e8b76114
--- /dev/null
+++ b/addons/l10n_be_intrastat_2019/models/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+
+from . import account_intrastat_report
+from . import account_invoice_line
diff --git a/addons/l10n_be_intrastat_2019/models/account_intrastat_report.py b/addons/l10n_be_intrastat_2019/models/account_intrastat_report.py
new file mode 100644
index 0000000000000000000000000000000000000000..2a091901a26b6cc92fc35769eaf2792ca8c901fb
--- /dev/null
+++ b/addons/l10n_be_intrastat_2019/models/account_intrastat_report.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import models
+from collections import namedtuple
+
+
+class XmlDeclaration(models.TransientModel):
+    """
+    Intrastat XML Declaration
+    """
+    _inherit = "l10n_be_intrastat_xml.xml_decl"
+
+    def _build_intrastat_line(self, numlgn, item, linekey, amounts, dispatchmode, extendedmode):
+        super(XmlDeclaration, self)._build_intrastat_line(numlgn, item, linekey, amounts, dispatchmode, extendedmode)
+        if dispatchmode:
+            self._set_Dim(item, 'EXCNTORI', unicode(linekey.EXCNTORI))
+            self._set_Dim(item, 'PARTNERID', unicode(linekey.PARTNERID))
+
+    def _get_intrastat_linekey(self, declcode, inv_line, dispatchmode, extendedmode):
+        res = super(XmlDeclaration, self)._get_intrastat_linekey(declcode, inv_line, dispatchmode, extendedmode)
+        if res and dispatchmode:
+            res_dict = res._asdict()
+            res_dict['EXCNTORI'] = inv_line.intrastat_product_origin_country_id.code or 'QU'
+            res_dict['PARTNERID'] = inv_line.invoice_id.partner_id.vat or 'QV999999999999'
+            return namedtuple('intrastatkey', res_dict.keys())(**res_dict)
+        return res
+
+    def _get_expedition_code(self, extended):
+        return 'INTRASTAT_X_E' if extended else 'INTRASTAT_X_S'
+
+    def _get_expedition_form(self, extended):
+        return 'INTRASTAT_X_EF' if extended else 'INTRASTAT_X_SF'
diff --git a/addons/l10n_be_intrastat_2019/models/account_invoice_line.py b/addons/l10n_be_intrastat_2019/models/account_invoice_line.py
new file mode 100644
index 0000000000000000000000000000000000000000..01199b0174aa82852b749e4fb5266ed1654e1eda
--- /dev/null
+++ b/addons/l10n_be_intrastat_2019/models/account_invoice_line.py
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models
+
+class AccountInvoiceLine(models.Model):
+    _inherit = 'account.invoice.line'
+
+    intrastat_product_origin_country_id = fields.Many2one('res.country', string='Origin Country of Product')
diff --git a/addons/l10n_be_intrastat_2019/views/account_invoice_line_view.xml b/addons/l10n_be_intrastat_2019/views/account_invoice_line_view.xml
new file mode 100644
index 0000000000000000000000000000000000000000..187e32b161e8e0fa4fa7f52e0c4bd26a647f7550
--- /dev/null
+++ b/addons/l10n_be_intrastat_2019/views/account_invoice_line_view.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+    <record id="invoice_line_be_intrastat_data_form" model="ir.ui.view">
+        <field name="name">account.invoice.form.inherit.account.be.intrastat</field>
+        <field name="model">account.invoice</field>
+        <field name="inherit_id" ref="account.invoice_form"/>
+        <field name="arch" type="xml">
+             <xpath expr="//field[@name='invoice_line_ids']//field[@name='quantity']" position="before">
+                <field name="intrastat_product_origin_country_id" options="{'no_create_edit': True}"/>
+            </xpath>
+        </field>
+    </record>
+</odoo>
diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py
index ad0a327d591f2498bc93e25b6b28df6b7cd87ed3..a5c92708f6ae415b39f179588731eb5ed5b458a3 100644
--- a/addons/sale/models/sale.py
+++ b/addons/sale/models/sale.py
@@ -467,7 +467,7 @@ class SaleOrder(models.Model):
             order.state = 'sale'
             order.confirmation_date = fields.Datetime.now()
             if self.env.context.get('send_email'):
-                self.force_quotation_send()
+                order.force_quotation_send()
             order.order_line._action_procurement_create()
         if self.env['ir.values'].get_default('sale.config.settings', 'auto_done_setting'):
             self.action_done()
diff --git a/addons/web/static/src/js/views/form_relational_widgets.js b/addons/web/static/src/js/views/form_relational_widgets.js
index 2ca483eac2bb028d2a92f2cd496b8bae0eec1af5..1dd61553ca296cccbfaf0043aa013cbe978f5ee7 100644
--- a/addons/web/static/src/js/views/form_relational_widgets.js
+++ b/addons/web/static/src/js/views/form_relational_widgets.js
@@ -264,6 +264,12 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com
             }
         });
 
+        // avoid ignoring autocomplete="off" by obfuscating placeholder, see #30439
+        if (this.$input.attr('placeholder')) {
+            this.$input.attr('placeholder', function (index, val) {
+                return val.split('').join('\ufeff');
+            });
+        }
         var isSelecting = false;
         // autocomplete
         this.$input.autocomplete({
diff --git a/doc/cla/corporate/levelprime.md b/doc/cla/corporate/levelprime.md
index bca6d6aa7d9fd16532853e4ab5f990b20491604c..d2d579302d8761d51b155ec67ae9216af9004b58 100644
--- a/doc/cla/corporate/levelprime.md
+++ b/doc/cla/corporate/levelprime.md
@@ -9,9 +9,12 @@ declaration.
 Signed,
 
 Roberto Fichera roberto.fichera@levelprime.com https://github.com/robyf70
+Filippo Iovine - filippo.iovine@levelprime.com - https://github.com/FilippoIOVINE
 
 
 List of contributors:
 
 Roberto Fichera roberto.fichera@levelprime.com https://github.com/robyf70
 Roberto Fichera robyf@tekno-soft.it https://github.com/robyf70
+Filippo Iovine - filippo.iovine@levelprime.com - https://github.com/FilippoIOVINE
+Filippo Iovine - filippo.jovine@gmail.com - https://github.com/FilippoIOVINE
diff --git a/odoo/addons/base/ir/ir_ui_view.py b/odoo/addons/base/ir/ir_ui_view.py
index 6cd9fa8ec59245e00d97354b9ae98cfa74c3951b..275dc7d3490c39fddcfa91b0eee0f189f25530d1 100644
--- a/odoo/addons/base/ir/ir_ui_view.py
+++ b/odoo/addons/base/ir/ir_ui_view.py
@@ -362,7 +362,10 @@ actual arch.
 
     def _compute_defaults(self, values):
         if 'inherit_id' in values:
-            values.setdefault('mode', 'extension' if values['inherit_id'] else 'primary')
+            # Do not automatically change the mode if the view already has an inherit_id,
+            # and the user change it to another.
+            if not values['inherit_id'] or all(not view.inherit_id for view in self):
+                values.setdefault('mode', 'extension' if values['inherit_id'] else 'primary')
         return values
 
     @api.model
diff --git a/odoo/addons/base/tests/test_views.py b/odoo/addons/base/tests/test_views.py
index bd462932e5f2366ad95b336dc8aefdd4d2ebc90d..16b859464393fb98e76f7b796726ee5e7a3c9e14 100644
--- a/odoo/addons/base/tests/test_views.py
+++ b/odoo/addons/base/tests/test_views.py
@@ -905,6 +905,12 @@ class ViewModeField(ViewCase):
         })
         self.assertEqual(view2.mode, 'extension')
 
+        view2.write({'inherit_id': None})
+        self.assertEqual(view2.mode, 'primary')
+
+        view2.write({'inherit_id': view.id})
+        self.assertEqual(view2.mode, 'extension')
+
     @mute_logger('odoo.sql_db')
     def testModeExplicit(self):
         view = self.View.create({
@@ -917,6 +923,7 @@ class ViewModeField(ViewCase):
             'arch': '<qweb/>'
         })
         self.assertEqual(view.mode, 'primary')
+        self.assertEqual(view2.mode, 'primary')
 
         with self.assertRaises(IntegrityError):
             self.View.create({
@@ -968,6 +975,27 @@ class ViewModeField(ViewCase):
 
         view.write({'mode': 'primary'})
 
+    def testChangeInheritOfPrimary(self):
+        """
+        A primary view with an inherit_id must remain primary when changing the inherit_id
+        """
+        base1 = self.View.create({
+            'inherit_id': None,
+            'arch': '<qweb/>',
+        })
+        base2 = self.View.create({
+            'inherit_id': None,
+            'arch': '<qweb/>',
+        })
+        view = self.View.create({
+            'mode': 'primary',
+            'inherit_id': base1.id,
+            'arch': '<qweb/>',
+        })
+        self.assertEqual(view.mode, 'primary')
+        view.write({'inherit_id': base2.id})
+        self.assertEqual(view.mode, 'primary')
+
 
 class TestDefaultView(ViewCase):
     def testDefaultViewBase(self):