From 4c380a3224c2b0b14fef66b3a6dbf9aa1f2db8e6 Mon Sep 17 00:00:00 2001
From: Diep Huu Hoang <hoang@trobz.com>
Date: Tue, 28 Mar 2023 12:48:05 +0700
Subject: [PATCH] [16.0][MIG] web_m2x_options: Migration to 16.0

---
 web_m2x_options/README.rst                    |  17 +-
 web_m2x_options/__manifest__.py               |  10 +-
 web_m2x_options/i18n/web_m2x_options.pot      |  87 ++--
 web_m2x_options/readme/CONTRIBUTORS.rst       |   2 +
 web_m2x_options/readme/CREDITS.rst            |   1 +
 .../static/src/components/base.xml            |  46 ++
 .../static/src/components/form.esm.js         | 418 +++++++++++++++
 .../src/components/relational_utils.esm.js    | 231 +++++++++
 web_m2x_options/static/src/js/form.js         | 489 ------------------
 web_m2x_options/static/src/js/ir_options.js   |  12 -
 web_m2x_options/static/src/xml/base.xml       |  12 -
 11 files changed, 758 insertions(+), 567 deletions(-)
 create mode 100644 web_m2x_options/readme/CREDITS.rst
 create mode 100644 web_m2x_options/static/src/components/base.xml
 create mode 100644 web_m2x_options/static/src/components/form.esm.js
 create mode 100644 web_m2x_options/static/src/components/relational_utils.esm.js
 delete mode 100644 web_m2x_options/static/src/js/form.js
 delete mode 100644 web_m2x_options/static/src/js/ir_options.js
 delete mode 100644 web_m2x_options/static/src/xml/base.xml

diff --git a/web_m2x_options/README.rst b/web_m2x_options/README.rst
index 66002974a..4f10772c6 100644
--- a/web_m2x_options/README.rst
+++ b/web_m2x_options/README.rst
@@ -14,13 +14,13 @@ web_m2x_options
     :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
     :alt: License: AGPL-3
 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github
-    :target: https://github.com/OCA/web/tree/15.0/web_m2x_options
+    :target: https://github.com/OCA/web/tree/16.0/web_m2x_options
     :alt: OCA/web
 .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
-    :target: https://translation.odoo-community.org/projects/web-15-0/web-15-0-web_m2x_options
+    :target: https://translation.odoo-community.org/projects/web-16-0/web-16-0-web_m2x_options
     :alt: Translate me on Weblate
 .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
-    :target: https://runbot.odoo-community.org/runbot/162/15.0
+    :target: https://runbot.odoo-community.org/runbot/162/16.0
     :alt: Try me on Runbot
 
 |badge1| |badge2| |badge3| |badge4| |badge5| 
@@ -154,7 +154,7 @@ Bug Tracker
 Bugs are tracked on `GitHub Issues <https://github.com/OCA/web/issues>`_.
 In case of trouble, please check there if your issue has already been reported.
 If you spotted it first, help us smashing it by providing a detailed and welcomed
-`feedback <https://github.com/OCA/web/issues/new?body=module:%20web_m2x_options%0Aversion:%2015.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
+`feedback <https://github.com/OCA/web/issues/new?body=module:%20web_m2x_options%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
 
 Do not contact contributors directly about support or help with technical issues.
 
@@ -186,6 +186,13 @@ Contributors
   * Carlos Roca
 * Bhavesh Odedra <bodedra@opensourceintegrators.com>
 * Dhara Solanki <dhara.solanki@initos.com> (http://www.initos.com)
+* `Trobz <https://trobz.com>`_:
+    * Hoang Diep <hoang@trobz.com>
+
+Other credits
+~~~~~~~~~~~~~
+
+The migration of this module from 15.0 to 16.0 was financially supported by Camptocamp
 
 Maintainers
 ~~~~~~~~~~~
@@ -200,6 +207,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose
 mission is to support the collaborative development of Odoo features and
 promote its widespread use.
 
-This module is part of the `OCA/web <https://github.com/OCA/web/tree/15.0/web_m2x_options>`_ project on GitHub.
+This module is part of the `OCA/web <https://github.com/OCA/web/tree/16.0/web_m2x_options>`_ project on GitHub.
 
 You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/web_m2x_options/__manifest__.py b/web_m2x_options/__manifest__.py
index af30beb4b..9da81d7df 100644
--- a/web_m2x_options/__manifest__.py
+++ b/web_m2x_options/__manifest__.py
@@ -6,7 +6,7 @@
 
 {
     "name": "web_m2x_options",
-    "version": "15.0.1.1.0",
+    "version": "16.0.1.1.0",
     "category": "Web",
     "author": "initOS GmbH,"
     "ACSONE SA/NV, "
@@ -16,12 +16,6 @@
     "website": "https://github.com/OCA/web",
     "license": "AGPL-3",
     "depends": ["web"],
-    "assets": {
-        "web.assets_backend": [
-            "web_m2x_options/static/src/js/form.js",
-            "web_m2x_options/static/src/js/ir_options.js",
-        ],
-        "web.assets_qweb": ["web_m2x_options/static/src/xml/base.xml"],
-    },
+    "assets": {"web.assets_backend": ["web_m2x_options/static/src/components/*"]},
     "installable": True,
 }
diff --git a/web_m2x_options/i18n/web_m2x_options.pot b/web_m2x_options/i18n/web_m2x_options.pot
index 50637f9b4..1f35533df 100644
--- a/web_m2x_options/i18n/web_m2x_options.pot
+++ b/web_m2x_options/i18n/web_m2x_options.pot
@@ -4,7 +4,7 @@
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: Odoo Server 15.0\n"
+"Project-Id-Version: Odoo Server 16.0\n"
 "Report-Msgid-Bugs-To: \n"
 "Last-Translator: \n"
 "Language-Team: \n"
@@ -14,82 +14,80 @@ msgstr ""
 "Plural-Forms: \n"
 
 #. module: web_m2x_options
-#. openerp-web
-#: code:addons/web_m2x_options/static/src/xml/base.xml:0
+#. odoo-javascript
+#: code:addons/web_m2x_options/static/src/components/base.xml:0
 #, python-format
-msgid ""
-"!(widget.nodeOptions.no_open || widget.nodeOptions.no_open_edit || "
-"widget.noOpen)"
+msgid ", are you sure it does not exist yet?"
 msgstr ""
 
 #. module: web_m2x_options
-#. openerp-web
-#: code:addons/web_m2x_options/static/src/js/form.js:0
-#, python-format
-msgid "Cancel"
-msgstr ""
-
-#. module: web_m2x_options
-#. openerp-web
-#: code:addons/web_m2x_options/static/src/js/form.js:0
+#. odoo-javascript
+#: code:addons/web_m2x_options/static/src/components/base.xml:0
 #, python-format
 msgid "Create"
 msgstr ""
 
 #. module: web_m2x_options
-#. openerp-web
-#: code:addons/web_m2x_options/static/src/js/form.js:0
+#. odoo-javascript
+#: code:addons/web_m2x_options/static/src/components/relational_utils.esm.js:0
 #, python-format
-msgid "Create \"<strong>%s</strong>\""
+msgid "Create \"%s\""
 msgstr ""
 
 #. module: web_m2x_options
-#. openerp-web
-#: code:addons/web_m2x_options/static/src/js/form.js:0
+#. odoo-javascript
+#: code:addons/web_m2x_options/static/src/components/base.xml:0
 #, python-format
-msgid "Create a %s"
+msgid "Create and Edit"
 msgstr ""
 
 #. module: web_m2x_options
-#. openerp-web
-#: code:addons/web_m2x_options/static/src/js/form.js:0
+#. odoo-javascript
+#: code:addons/web_m2x_options/static/src/components/relational_utils.esm.js:0
 #, python-format
-msgid "Create and Edit..."
+msgid "Create and edit..."
 msgstr ""
 
 #. module: web_m2x_options
-#. openerp-web
-#: code:addons/web_m2x_options/static/src/js/form.js:0
+#. odoo-javascript
+#: code:addons/web_m2x_options/static/src/components/base.xml:0
 #, python-format
-msgid "Create and edit"
+msgid "Discard"
 msgstr ""
 
 #. module: web_m2x_options
-#. openerp-web
-#: code:addons/web_m2x_options/static/src/js/form.js:0
+#. odoo-javascript
+#: code:addons/web_m2x_options/static/src/components/form.esm.js:0
 #, python-format
-msgid "No results to show..."
+msgid "New: %s"
 msgstr ""
 
 #. module: web_m2x_options
-#. openerp-web
-#: code:addons/web_m2x_options/static/src/js/form.js:0
+#. odoo-javascript
+#: code:addons/web_m2x_options/static/src/components/relational_utils.esm.js:0
+#, python-format
+msgid "No records"
+msgstr ""
+
+#. module: web_m2x_options
+#. odoo-javascript
+#: code:addons/web_m2x_options/static/src/components/form.esm.js:0
 #, python-format
 msgid "Open: "
 msgstr ""
 
 #. module: web_m2x_options
-#. openerp-web
-#: code:addons/web_m2x_options/static/src/js/form.js:0
+#. odoo-javascript
+#: code:addons/web_m2x_options/static/src/components/relational_utils.esm.js:0
 #, python-format
-msgid "Quick search: %s"
+msgid "Search More..."
 msgstr ""
 
 #. module: web_m2x_options
-#. openerp-web
-#: code:addons/web_m2x_options/static/src/js/form.js:0
+#. odoo-javascript
+#: code:addons/web_m2x_options/static/src/components/relational_utils.esm.js:0
 #, python-format
-msgid "Search More..."
+msgid "Start typing..."
 msgstr ""
 
 #. module: web_m2x_options
@@ -98,8 +96,15 @@ msgid "System Parameter"
 msgstr ""
 
 #. module: web_m2x_options
-#. openerp-web
-#: code:addons/web_m2x_options/static/src/js/form.js:0
+#. odoo-javascript
+#: code:addons/web_m2x_options/static/src/components/base.xml:0
 #, python-format
-msgid "You are creating a new %s, are you sure it does not exist yet?"
+msgid "You are creating a new"
+msgstr ""
+
+#. module: web_m2x_options
+#. odoo-javascript
+#: code:addons/web_m2x_options/static/src/components/base.xml:0
+#, python-format
+msgid "as a new"
 msgstr ""
diff --git a/web_m2x_options/readme/CONTRIBUTORS.rst b/web_m2x_options/readme/CONTRIBUTORS.rst
index c42c7afe7..e7ae138b5 100644
--- a/web_m2x_options/readme/CONTRIBUTORS.rst
+++ b/web_m2x_options/readme/CONTRIBUTORS.rst
@@ -12,3 +12,5 @@
   * Carlos Roca
 * Bhavesh Odedra <bodedra@opensourceintegrators.com>
 * Dhara Solanki <dhara.solanki@initos.com> (http://www.initos.com)
+* `Trobz <https://trobz.com>`_:
+    * Hoang Diep <hoang@trobz.com>
diff --git a/web_m2x_options/readme/CREDITS.rst b/web_m2x_options/readme/CREDITS.rst
new file mode 100644
index 000000000..70bc307ad
--- /dev/null
+++ b/web_m2x_options/readme/CREDITS.rst
@@ -0,0 +1 @@
+The migration of this module from 15.0 to 16.0 was financially supported by Camptocamp
diff --git a/web_m2x_options/static/src/components/base.xml b/web_m2x_options/static/src/components/base.xml
new file mode 100644
index 000000000..d8719f7d0
--- /dev/null
+++ b/web_m2x_options/static/src/components/base.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- Copyright 2017 Jairo Llopis <jairo.llopis@tecnativa.com>
+     License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
+<templates xml:space="preserve">
+
+
+    <t
+        t-name="web_m2x_options.AutoComplete"
+        t-inherit="web.AutoComplete"
+        t-inherit-mode="extension"
+        owl="1"
+    >
+        <xpath expr="//t[@t-foreach='source.options']/li/a" position="attributes">
+            <attribute name="t-attf-style">{{ option.style }}</attribute>
+        </xpath>
+    </t>
+
+    <t
+        t-name="web_m2x_options.Many2ManyTagsField"
+        t-inherit="web.Many2ManyTagsField"
+        t-inherit-mode="extension"
+        owl="1"
+    >
+        <xpath expr="//Many2XAutocomplete" position="attributes">
+            <attribute name="nodeOptions">props.nodeOptions</attribute>
+        </xpath>
+    </t>
+
+    <t t-name="web_m2x_options.Many2OneField.CreateConfirmationDialog" owl="1">
+        <Dialog title="title" size="'md'">
+            <div>
+                You are creating a new <strong t-esc="props.value" /> as a new <t
+                    t-esc="props.name"
+                />, are you sure it does not exist yet?
+            </div>
+            <t t-set-slot="footer">
+                <button class="btn btn-primary" t-on-click="onCreate">Create</button>
+                <button
+                    class="btn btn-primary"
+                    t-on-click="onCreateEdit"
+                >Create and Edit</button>
+                <button class="btn" t-on-click="() => props.close()">Discard</button>
+            </t>
+        </Dialog>
+    </t>
+</templates>
diff --git a/web_m2x_options/static/src/components/form.esm.js b/web_m2x_options/static/src/components/form.esm.js
new file mode 100644
index 000000000..2b2923105
--- /dev/null
+++ b/web_m2x_options/static/src/components/form.esm.js
@@ -0,0 +1,418 @@
+/** @odoo-module **/
+
+import {
+    Many2ManyTagsField,
+    Many2ManyTagsFieldColorEditable,
+} from "@web/views/fields/many2many_tags/many2many_tags_field";
+
+import {Dialog} from "@web/core/dialog/dialog";
+import {FormController} from "@web/views/form/form_controller";
+import {FormViewDialog} from "@web/views/view_dialogs/form_view_dialog";
+import {Many2OneAvatarField} from "@web/views/fields/many2one_avatar/many2one_avatar_field";
+import {Many2OneBarcodeField} from "@web/views/fields/many2one_barcode/many2one_barcode_field";
+import {Many2OneField} from "@web/views/fields/many2one/many2one_field";
+import {ReferenceField} from "@web/views/fields/reference/reference_field";
+import {X2ManyField} from "@web/views/fields/x2many/x2many_field";
+import {isX2Many} from "@web/views/utils";
+import {is_option_set} from "@web_m2x_options/components/relational_utils.esm";
+import {patch} from "@web/core/utils/patch";
+import {sprintf} from "@web/core/utils/strings";
+import {useService} from "@web/core/utils/hooks";
+
+const {Component, onWillStart} = owl;
+
+/**
+ *  Patch Many2ManyTagsField
+ **/
+patch(Many2ManyTagsField.prototype, "web_m2x_options.Many2ManyTagsField", {
+    setup() {
+        this._super(...arguments);
+        this.actionService = useService("action");
+    },
+    /**
+     * @override
+     */
+    getTagProps(record) {
+        const props = this._super(...arguments);
+        props.onClick = (ev) => this.onMany2ManyBadgeClick(ev, record);
+        return props;
+    },
+    async onMany2ManyBadgeClick(event, record) {
+        var self = this;
+        if (self.props.open) {
+            var context = self.context;
+            var id = record.data.id;
+            if (self.props.readonly) {
+                event.preventDefault();
+                event.stopPropagation();
+                const action = await self.orm.call(
+                    self.props.relation,
+                    "get_formview_action",
+                    [[id]],
+                    {context: context}
+                );
+                self.actionService.doAction(action);
+            } else {
+                const view_id = await self.orm.call(
+                    self.props.relation,
+                    "get_formview_id",
+                    [[id]],
+                    {context: context}
+                );
+
+                const write_access = await self.orm.call(
+                    self.props.relation,
+                    "check_access_rights",
+                    [],
+                    {operation: "write", raise_exception: false}
+                );
+                var can_write = self.props.canWrite;
+                self.dialog.add(FormViewDialog, {
+                    resModel: self.props.relation,
+                    resId: id,
+                    context: context,
+                    title: self.env._t("Open: ") + self.string,
+                    viewId: view_id,
+                    mode: !can_write || !write_access ? "readonly" : "edit",
+                    onRecordSaved: () => self.props.value.model.load(),
+                });
+            }
+        }
+    },
+});
+
+Many2ManyTagsField.props = {
+    ...Many2ManyTagsField.props,
+    open: {type: Boolean, optional: true},
+    canWrite: {type: Boolean, optional: true},
+    nodeOptions: {type: Object, optional: true},
+};
+
+const Many2ManyTagsFieldExtractProps = Many2ManyTagsField.extractProps;
+Many2ManyTagsField.extractProps = ({attrs, field}) => {
+    const canOpen = Boolean(attrs.options.open);
+    const canWrite = attrs.can_write && Boolean(JSON.parse(attrs.can_write));
+    return Object.assign(Many2ManyTagsFieldExtractProps({attrs, field}), {
+        open: canOpen,
+        canWrite: canWrite,
+        nodeOptions: attrs.options,
+    });
+};
+
+/**
+ *  Many2ManyTagsFieldColorEditable
+ **/
+patch(
+    Many2ManyTagsFieldColorEditable.prototype,
+    "web_m2x_options.Many2ManyTagsFieldColorEditable",
+    {
+        async onBadgeClick(event, record) {
+            if (this.props.canEditColor && !this.props.open) {
+                this._super(...arguments);
+            }
+            if (this.props.open) {
+                Many2ManyTagsField.prototype.onMany2ManyBadgeClick.bind(this)(
+                    event,
+                    record
+                );
+            }
+        },
+    }
+);
+
+Many2ManyTagsFieldColorEditable.props = {
+    ...Many2ManyTagsFieldColorEditable.props,
+    open: {type: Boolean, optional: true},
+    canWrite: {type: Boolean, optional: true},
+    nodeOptions: {type: Object, optional: true},
+};
+
+/**
+ *  CreateConfirmationDialog
+ *  New customized component for Many2One Field
+ **/
+
+class CreateConfirmationDialog extends Component {
+    get title() {
+        return sprintf(this.env._t("New: %s"), this.props.name);
+    }
+
+    async onCreate() {
+        await this.props.create();
+        this.props.close();
+    }
+    async onCreateEdit() {
+        await this.props.createEdit();
+        this.props.close();
+    }
+}
+CreateConfirmationDialog.components = {Dialog};
+CreateConfirmationDialog.template =
+    "web_m2x_options.Many2OneField.CreateConfirmationDialog";
+
+/**
+ *  Many2OneField
+ **/
+
+patch(Many2OneField.prototype, "web_m2x_options.Many2OneField", {
+    setup() {
+        this._super(...arguments);
+        const ormService = useService("orm");
+        this.user_context = Component.env.session.user_context;
+        onWillStart(async () => {
+            this.ir_options = await ormService.call(
+                "ir.config_parameter",
+                "get_web_m2x_options",
+                [],
+                {context: this.user_context}
+            );
+        });
+    },
+    /**
+     * @override
+     */
+    get Many2XAutocompleteProps() {
+        const props = this._super(...arguments);
+        return {
+            ...props,
+            searchLimit: this.props.searchLimit,
+            searchMore: this.props.searchMore,
+            canCreate: this.props.canCreate,
+            nodeOptions: this.props.nodeOptions,
+        };
+    },
+
+    async openConfirmationDialog(request) {
+        var m2o_dialog_opt =
+            is_option_set(this.props.nodeOptions.m2o_dialog) ||
+            (_.isUndefined(this.props.nodeOptions.m2o_dialog) &&
+                is_option_set(this.ir_options["web_m2x_options.m2o_dialog"])) ||
+            (_.isUndefined(this.props.nodeOptions.m2o_dialog) &&
+                _.isUndefined(this.ir_options["web_m2x_options.m2o_dialog"]));
+        if (this.props.canCreate && this.state.isFloating && m2o_dialog_opt) {
+            return new Promise((resolve, reject) => {
+                this.addDialog(CreateConfirmationDialog, {
+                    value: request,
+                    name: this.props.string,
+                    create: async () => {
+                        try {
+                            await this.quickCreate(request);
+                            resolve();
+                        } catch (e) {
+                            reject(e);
+                        }
+                    },
+                    createEdit: async () => {
+                        try {
+                            await this.quickCreate(request);
+                            await this.props.record.model.load();
+                            this.openMany2X({
+                                resId: this.props.value[0],
+                                context: this.user_context,
+                            });
+                            resolve();
+                        } catch (e) {
+                            reject(e);
+                        }
+                    },
+                });
+            });
+        }
+    },
+});
+
+const Many2OneFieldExtractProps = Many2OneField.extractProps;
+Many2OneField.extractProps = ({attrs, field}) => {
+    return Object.assign(Many2OneFieldExtractProps({attrs, field}), {
+        searchLimit: attrs.options.limit,
+        searchMore: attrs.options.search_more,
+        nodeOptions: attrs.options,
+    });
+};
+
+Many2OneField.props = {
+    ...Many2OneField.props,
+    searchMore: {type: Boolean, optional: true},
+    nodeOptions: {type: Object, optional: true},
+};
+
+/**
+ * FIXME: find better way to extend props in Many2OneField
+ * Override ReferenceField
+ * Since extracted/added props: nodeOptions and searchMore into Many2OneField props
+ * and this component inherited props from Many2OneField
+ * So, must override props here to avoid constraint validateProps (props schema) in owl core
+ */
+
+ReferenceField.props = {
+    ...ReferenceField.props,
+    searchMore: {type: Boolean, optional: true},
+    nodeOptions: {type: Object, optional: true},
+};
+
+/**
+ * FIXME: find better way to extend props in Many2OneField
+ * Override Many2OneBarcodeField
+ * Since extracted/added props: nodeOptions and searchMore into Many2OneField props
+ * and this component inherited props from Many2OneField
+ * So, must override props here to avoid constraint validateProps (props schema) in owl core
+ */
+
+Many2OneBarcodeField.props = {
+    ...Many2OneBarcodeField.props,
+    searchMore: {type: Boolean, optional: true},
+    nodeOptions: {type: Object, optional: true},
+};
+
+/**
+ * FIXME: find better way to extend props in Many2OneField
+ * Override Many2OneAvatarField
+ * Since extracted/added props: nodeOptions and searchMore into Many2OneField props
+ * and this component inherited props from Many2OneField
+ * So, must override props here to avoid constraint validateProps (props schema) in owl core
+ */
+Many2OneAvatarField.props = {
+    ...Many2OneAvatarField.props,
+    searchMore: {type: Boolean, optional: true},
+    nodeOptions: {type: Object, optional: true},
+};
+
+/**
+ * FIXME: find better way to extend props in Many2OneField
+ * Override mailing_m2o_filter
+ * Since extracted/added props: nodeOptions and searchMore into Many2OneField props
+ * and this component inherited props from Many2OneField
+ * So, must override props here to avoid constraint validateProps (props schema) in owl core
+ * This component is in module mass_mailing as optional module,
+ * So need to import dynamic way
+ */
+try {
+    (async () => {
+        // Make sure component mailing_m2o_filter in mass mailing module loaded
+        const installed_mass_mailing = await odoo.ready(
+            "@mass_mailing/js/mailing_m2o_filter"
+        );
+        if (installed_mass_mailing) {
+            var FieldMany2OneMailingFilter = await odoo.runtimeImport(
+                "@mass_mailing/js/mailing_m2o_filter"
+            );
+            FieldMany2OneMailingFilter.props = {
+                ...FieldMany2OneMailingFilter.props,
+                searchMore: {type: Boolean, optional: true},
+                nodeOptions: {type: Object, optional: true},
+            };
+        }
+    })();
+} catch {
+    console.log(
+        "Ignore overriding props of component mailing_m2o_filter since the module is not installed"
+    );
+}
+
+/**
+ *  X2ManyField
+ **/
+patch(X2ManyField.prototype, "web_m2x_options.X2ManyField", {
+    /**
+     * @override
+     */
+    async openRecord(record) {
+        var self = this;
+        var open = this.props.open;
+        if (open && self.props.readonly) {
+            var res_id = record.data.id;
+            const action = await self.env.model.orm.call(
+                self.props.value.resModel,
+                "get_formview_action",
+                [[res_id]]
+            );
+            return self.env.model.actionService.doAction(action);
+        }
+        return this._super.apply(this, arguments);
+    },
+});
+
+const X2ManyFieldExtractProps = X2ManyField.extractProps;
+X2ManyField.extractProps = ({attrs}) => {
+    const canOpen = Boolean(attrs.options.open);
+    return Object.assign(X2ManyFieldExtractProps({attrs}), {
+        open: canOpen,
+    });
+};
+
+X2ManyField.props = {
+    ...X2ManyField.props,
+    open: {type: Boolean, optional: true},
+};
+
+/**
+ *  FormController
+ **/
+patch(FormController.prototype, "web_m2x_options.FormController", {
+    /**
+     * @override
+     */
+    setup() {
+        var self = this;
+        this._super(...arguments);
+
+        /**  Due to problem of 2 onWillStart in native web core
+         * (see: https://github.com/odoo/odoo/blob/16.0/addons/web/static/src/views/model.js#L142)
+         * do the trick to override beforeLoadResolver here to customize viewLimit
+         */
+        this.superBeforeLoadResolver = this.beforeLoadResolver;
+        this.beforeLoadResolver = async () => {
+            await self._setSubViewLimit();
+            self.superBeforeLoadResolver();
+        };
+    },
+    /**
+     * @override
+     * add more method to add subview limit on formview
+     */
+    async _setSubViewLimit() {
+        const ir_options = await this.model.orm.call(
+            "ir.config_parameter",
+            "get_web_m2x_options",
+            [],
+            {context: this.user_context}
+        );
+
+        const activeFields = this.archInfo.activeFields,
+            fields = this.props.fields,
+            isSmall = this.user;
+
+        var limit = ir_options["web_m2x_options.field_limit_entries"];
+        if (!_.isUndefined(limit)) {
+            limit = parseInt(limit, 10);
+        }
+
+        for (const fieldName in activeFields) {
+            const field = fields[fieldName];
+            if (!isX2Many(field)) {
+                // What follows only concerns x2many fields
+                continue;
+            }
+            const fieldInfo = activeFields[fieldName];
+            if (fieldInfo.modifiers.invisible === true) {
+                // No need to fetch the sub view if the field is always invisible
+                continue;
+            }
+
+            if (!fieldInfo.FieldComponent.useSubView) {
+                // The FieldComponent used to render the field doesn't need a sub view
+                continue;
+            }
+
+            let viewType = fieldInfo.viewMode || "list,kanban";
+            viewType = viewType.replace("tree", "list");
+            if (viewType.includes(",")) {
+                viewType = isSmall ? "kanban" : "list";
+            }
+            fieldInfo.viewMode = viewType;
+            if (fieldInfo.views[viewType] && limit) {
+                fieldInfo.views[viewType].limit = limit;
+            }
+        }
+    },
+});
diff --git a/web_m2x_options/static/src/components/relational_utils.esm.js b/web_m2x_options/static/src/components/relational_utils.esm.js
new file mode 100644
index 000000000..645fa1bd9
--- /dev/null
+++ b/web_m2x_options/static/src/components/relational_utils.esm.js
@@ -0,0 +1,231 @@
+/** @odoo-module **/
+
+import {Many2XAutocomplete} from "@web/views/fields/relational_utils";
+import {patch} from "@web/core/utils/patch";
+import {sprintf} from "@web/core/utils/strings";
+import {useService} from "@web/core/utils/hooks";
+const {Component, onWillStart} = owl;
+
+export function is_option_set(option) {
+    if (_.isUndefined(option)) return false;
+    if (typeof option === "string") return option === "true" || option === "True";
+    if (typeof option === "boolean") return option;
+    return false;
+}
+
+patch(Many2XAutocomplete.prototype, "web_m2x_options.Many2XAutocomplete", {
+    setup() {
+        this._super(...arguments);
+        const ormService = useService("orm");
+        this.user_context = Component.env.session.user_context;
+        onWillStart(async () => {
+            this.ir_options = await ormService.call(
+                "ir.config_parameter",
+                "get_web_m2x_options",
+                [],
+                {context: this.user_context}
+            );
+        });
+    },
+
+    async loadOptionsSource(request) {
+        if (this.lastProm) {
+            this.lastProm.abort(false);
+        }
+        // Add options limit used to change number of selections record
+        // returned.
+        if (!_.isUndefined(this.ir_options["web_m2x_options.limit"])) {
+            this.props.searchLimit = parseInt(
+                this.ir_options["web_m2x_options.limit"],
+                10
+            );
+            this.limit = this.props.searchLimit;
+        }
+
+        if (typeof this.props.nodeOptions.limit === "number") {
+            this.props.searchLimit = this.props.nodeOptions.limit;
+            this.limit = this.props.searchLimit;
+        }
+
+        // Add options field_color and colors to color item(s) depending on field_color value
+        this.field_color = this.props.nodeOptions.field_color;
+        this.colors = this.props.nodeOptions.colors;
+
+        this.lastProm = this.orm.call(this.props.resModel, "name_search", [], {
+            name: request,
+            operator: "ilike",
+            args: this.props.getDomain(),
+            limit: this.props.searchLimit + 1,
+            context: this.props.context,
+        });
+        const records = await this.lastProm;
+
+        var options = records.map((result) => ({
+            value: result[0],
+            id: result[0],
+            label: result[1].split("\n")[0],
+        }));
+
+        // Limit results if there is a custom limit options
+        if (this.limit) {
+            options = options.slice(0, this.props.searchLimit);
+        }
+
+        // Search result value colors
+        if (this.colors && this.field_color) {
+            var value_ids = options.map((result) => result.value);
+            const objects = await this.orm.call(
+                this.props.resModel,
+                "search_read",
+                [],
+                {
+                    domain: [["id", "in", value_ids]],
+                    fields: [this.field_color],
+                }
+            );
+            for (var index in objects) {
+                for (var index_value in options) {
+                    if (options[index_value].id === objects[index].id) {
+                        // Find value in values by comparing ids
+                        var option = options[index_value];
+                        // Find color with field value as key
+                        var color =
+                            this.colors[objects[index][this.field_color]] || "black";
+                        option.style = "color:" + color;
+                        break;
+                    }
+                }
+            }
+        }
+
+        // Quick create
+        // Note: Create should be before `search_more` (reserve native order)
+        // One more reason: when calling `onInputBlur`, native select the first option (activeSourceOption)
+        // which triggers m2o_dialog if m2o_dialog=true
+        var create_enabled =
+            this.props.quickCreate && !this.props.nodeOptions.no_create;
+
+        var raw_result = _.map(records, function (x) {
+            return x[1];
+        });
+        var quick_create = is_option_set(this.props.nodeOptions.create),
+            quick_create_undef = _.isUndefined(this.props.nodeOptions.create),
+            m2x_create_undef = _.isUndefined(this.ir_options["web_m2x_options.create"]),
+            m2x_create = is_option_set(this.ir_options["web_m2x_options.create"]);
+        var show_create =
+            (!this.props.nodeOptions && (m2x_create_undef || m2x_create)) ||
+            (this.props.nodeOptions &&
+                (quick_create ||
+                    (quick_create_undef && (m2x_create_undef || m2x_create))));
+        if (
+            create_enabled &&
+            !this.props.nodeOptions.no_quick_create &&
+            request.length > 0 &&
+            !_.contains(raw_result, request) &&
+            show_create
+        ) {
+            options.push({
+                label: sprintf(this.env._t(`Create "%s"`), request),
+                classList: "o_m2o_dropdown_option o_m2o_dropdown_option_create",
+                action: async (params) => {
+                    try {
+                        await this.props.quickCreate(request, params);
+                    } catch {
+                        const context = this.getCreationContext(request);
+                        return this.openMany2X({context});
+                    }
+                },
+            });
+        }
+
+        // Search more...
+        // Resolution order:
+        // 1- check if "search_more" is set locally in node's options
+        // 2- if set locally, apply its value
+        // 3- if not set locally, check if it's set globally via ir.config_parameter
+        // 4- if set globally, apply its value
+        // 5- if not set globally either, check if returned values are more than node's limit
+        var search_more = false;
+        if (!_.isUndefined(this.props.nodeOptions.search_more)) {
+            search_more = is_option_set(this.props.nodeOptions.search_more);
+        } else if (!_.isUndefined(this.ir_options["web_m2x_options.search_more"])) {
+            search_more = is_option_set(this.ir_options["web_m2x_options.search_more"]);
+        } else {
+            search_more =
+                !this.props.noSearchMore && this.props.searchLimit < records.length;
+        }
+        if (search_more) {
+            options.push({
+                label: this.env._t("Search More..."),
+                action: this.onSearchMore.bind(this, request),
+                classList: "o_m2o_dropdown_option o_m2o_dropdown_option_search_more",
+            });
+        }
+
+        // Create and Edit
+        const canCreateEdit =
+            "createEdit" in this.activeActions
+                ? this.activeActions.createEdit
+                : this.activeActions.create;
+        if (
+            !request.length &&
+            !this.props.value &&
+            (this.props.quickCreate || canCreateEdit)
+        ) {
+            options.push({
+                label: this.env._t("Start typing..."),
+                classList: "o_m2o_start_typing",
+                unselectable: true,
+            });
+        }
+
+        // Create and edit ...
+        var create_edit =
+                is_option_set(this.props.nodeOptions.create) ||
+                is_option_set(this.props.nodeOptions.create_edit),
+            create_edit_undef =
+                _.isUndefined(this.props.nodeOptions.create) &&
+                _.isUndefined(this.props.nodeOptions.create_edit),
+            m2x_create_edit_undef = _.isUndefined(
+                this.ir_options["web_m2x_options.create_edit"]
+            ),
+            m2x_create_edit = is_option_set(
+                this.ir_options["web_m2x_options.create_edit"]
+            );
+        var show_create_edit =
+            (!this.props.nodeOptions && (m2x_create_edit_undef || m2x_create_edit)) ||
+            (this.props.nodeOptions &&
+                (create_edit ||
+                    (create_edit_undef && (m2x_create_edit_undef || m2x_create_edit))));
+        if (
+            create_enabled &&
+            !this.props.nodeOptions.no_create_edit &&
+            show_create_edit &&
+            request.length &&
+            canCreateEdit
+        ) {
+            const context = this.getCreationContext(request);
+            options.push({
+                label: this.env._t("Create and edit..."),
+                classList: "o_m2o_dropdown_option o_m2o_dropdown_option_create_edit",
+                action: () => this.openMany2X({context}),
+            });
+        }
+
+        // No records
+        if (!records.length && !this.activeActions.create) {
+            options.push({
+                label: this.env._t("No records"),
+                classList: "o_m2o_no_result",
+                unselectable: true,
+            });
+        }
+
+        return options;
+    },
+});
+
+Many2XAutocomplete.defaultProps = {
+    ...Many2XAutocomplete.defaultProps,
+    nodeOptions: {},
+};
diff --git a/web_m2x_options/static/src/js/form.js b/web_m2x_options/static/src/js/form.js
deleted file mode 100644
index 46359c4bf..000000000
--- a/web_m2x_options/static/src/js/form.js
+++ /dev/null
@@ -1,489 +0,0 @@
-/* Copyright 2016 0k.io,ACSONE SA/NV
- *  * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
-
-odoo.define("web_m2x_options.web_m2x_options", function (require) {
-    "use strict";
-    var core = require("web.core"),
-        data = require("web.data"),
-        Dialog = require("web.Dialog"),
-        FormView = require("web.FormView"),
-        view_dialogs = require("web.view_dialogs"),
-        relational_fields = require("web.relational_fields"),
-        ir_options = require("web_m2x_options.ir_options");
-
-    var _t = core._t,
-        FieldMany2ManyTags = relational_fields.FieldMany2ManyTags,
-        FieldMany2One = relational_fields.FieldMany2One,
-        FieldOne2Many = relational_fields.FieldOne2Many,
-        FormFieldMany2ManyTags = relational_fields.FormFieldMany2ManyTags;
-
-    function is_option_set(option) {
-        if (_.isUndefined(option)) return false;
-        if (typeof option === "string") return option === "true" || option === "True";
-        if (typeof option === "boolean") return option;
-        return false;
-    }
-
-    var M2ODialog = Dialog.extend({
-        template: "M2ODialog",
-        init: function (parent, name, value) {
-            this.name = name;
-            this.value = value;
-            this._super(parent, {
-                title: _.str.sprintf(_t("Create a %s"), this.name),
-                size: "medium",
-                buttons: [
-                    {
-                        text: _t("Create"),
-                        classes: "btn-primary",
-                        click: function () {
-                            if (this.$("input").val()) {
-                                this.trigger_up("quick_create", {
-                                    value: this.$("input").val(),
-                                });
-                                this.close(true);
-                            } else {
-                                this.$("input").focus();
-                            }
-                        },
-                    },
-                    {
-                        text: _t("Create and edit"),
-                        classes: "btn-primary",
-                        close: true,
-                        click: function () {
-                            this.trigger_up("search_create_popup", {
-                                view_type: "form",
-                                value: this.$("input").val(),
-                            });
-                        },
-                    },
-                    {
-                        text: _t("Cancel"),
-                        close: true,
-                    },
-                ],
-            });
-        },
-        start: function () {
-            this.$("p").text(
-                _.str.sprintf(
-                    _t(
-                        "You are creating a new %s, are you sure it does not exist yet?"
-                    ),
-                    this.name
-                )
-            );
-            this.$("input").val(this.value);
-        },
-        /**
-         * @override
-         * @param {Boolean} isSet
-         */
-        close: function (isSet) {
-            this.isSet = isSet;
-            this._super.apply(this, arguments);
-        },
-        /**
-         * @override
-         */
-        destroy: function () {
-            if (!this.isSet) {
-                this.trigger_up("closed_unset");
-            }
-            this._super.apply(this, arguments);
-        },
-    });
-
-    FieldMany2One.include({
-        _onInputFocusout: function () {
-            var m2o_dialog_opt =
-                is_option_set(this.nodeOptions.m2o_dialog) ||
-                (_.isUndefined(this.nodeOptions.m2o_dialog) &&
-                    is_option_set(ir_options["web_m2x_options.m2o_dialog"])) ||
-                (_.isUndefined(this.nodeOptions.m2o_dialog) &&
-                    _.isUndefined(ir_options["web_m2x_options.m2o_dialog"]));
-            if (this.can_create && this.floating && m2o_dialog_opt) {
-                new M2ODialog(this, this.string, this.$input.val()).open();
-            }
-        },
-
-        _search: function (search_val) {
-            var self = this;
-
-            var def = new Promise((resolve) => {
-                // Add options limit used to change number of selections record
-                // returned.
-                if (!_.isUndefined(ir_options["web_m2x_options.limit"])) {
-                    this.limit = parseInt(ir_options["web_m2x_options.limit"], 10);
-                }
-
-                if (typeof self.nodeOptions.limit === "number") {
-                    self.limit = self.nodeOptions.limit;
-                }
-
-                // Add options field_color and colors to color item(s) depending on field_color value
-                self.field_color = self.nodeOptions.field_color;
-                self.colors = self.nodeOptions.colors;
-
-                var context = self.record.getContext(self.recordParams);
-                var domain = self.record.getDomain(self.recordParams);
-
-                var blacklisted_ids = self._getSearchBlacklist();
-                if (blacklisted_ids.length > 0) {
-                    domain.push(["id", "not in", blacklisted_ids]);
-                }
-
-                self._rpc({
-                    model: self.field.relation,
-                    method: "name_search",
-                    kwargs: {
-                        name: search_val,
-                        args: domain,
-                        operator: "ilike",
-                        limit: self.limit + 1,
-                        context: context,
-                    },
-                }).then((result) => {
-                    // Possible selections for the m2o
-                    var values = _.map(result, (x) => {
-                        x[1] = self._getDisplayName(x[1]);
-                        return {
-                            label:
-                                _.str.escapeHTML(x[1].trim()) || data.noDisplayContent,
-                            value: x[1],
-                            name: x[1],
-                            id: x[0],
-                        };
-                    });
-
-                    // Search result value colors
-                    if (self.colors && self.field_color) {
-                        var value_ids = [];
-                        for (var val_index in values) {
-                            value_ids.push(values[val_index].id);
-                        }
-                        self._rpc({
-                            model: self.field.relation,
-                            method: "search_read",
-                            fields: [self.field_color],
-                            domain: [["id", "in", value_ids]],
-                        }).then((objects) => {
-                            for (var index in objects) {
-                                for (var index_value in values) {
-                                    if (values[index_value].id === objects[index].id) {
-                                        // Find value in values by comparing ids
-                                        var value = values[index_value];
-                                        // Find color with field value as key
-                                        var color =
-                                            self.colors[
-                                                objects[index][self.field_color]
-                                            ] || "black";
-                                        value.label =
-                                            '<span style="color:' +
-                                            color +
-                                            '">' +
-                                            value.label +
-                                            "</span>";
-                                        break;
-                                    }
-                                }
-                            }
-                            resolve(values);
-                        });
-                    }
-
-                    // Search more...
-                    // Resolution order:
-                    // 1- check if "search_more" is set locally in node's options
-                    // 2- if set locally, apply its value
-                    // 3- if not set locally, check if it's set globally via ir.config_parameter
-                    // 4- if set globally, apply its value
-                    // 5- if not set globally either, check if returned values are more than node's limit
-                    if (!_.isUndefined(self.nodeOptions.search_more)) {
-                        var search_more = is_option_set(self.nodeOptions.search_more);
-                    } else if (
-                        !_.isUndefined(ir_options["web_m2x_options.search_more"])
-                    ) {
-                        var search_more = is_option_set(
-                            ir_options["web_m2x_options.search_more"]
-                        );
-                    } else {
-                        var search_more = values.length > self.limit;
-                    }
-
-                    if (search_more) {
-                        values = values.slice(0, self.limit);
-                        values.push({
-                            label: _t("Search More..."),
-                            action: function () {
-                                var prom = [];
-                                if (search_val !== "") {
-                                    prom = self._rpc({
-                                        model: self.field.relation,
-                                        method: "name_search",
-                                        kwargs: {
-                                            name: search_val,
-                                            args: domain,
-                                            operator: "ilike",
-                                            limit: self.SEARCH_MORE_LIMIT,
-                                            context: context,
-                                        },
-                                    });
-                                }
-                                Promise.resolve(prom).then(function (results) {
-                                    var dynamicFilters = [];
-                                    if (results) {
-                                        var ids = _.map(results, function (x) {
-                                            return x[0];
-                                        });
-                                        if (search_val) {
-                                            dynamicFilters = [
-                                                {
-                                                    description: _.str.sprintf(
-                                                        _t("Quick search: %s"),
-                                                        search_val
-                                                    ),
-                                                    domain: [["id", "in", ids]],
-                                                },
-                                            ];
-                                        } else {
-                                            dynamicFilters = [];
-                                        }
-                                    }
-                                    self._searchCreatePopup(
-                                        "search",
-                                        false,
-                                        {},
-                                        dynamicFilters
-                                    );
-                                });
-                            },
-                            classname: "o_m2o_dropdown_option",
-                        });
-                    }
-
-                    var create_enabled = self.can_create && !self.nodeOptions.no_create;
-                    // Quick create
-                    var raw_result = _.map(result, function (x) {
-                        return x[1];
-                    });
-                    var quick_create = is_option_set(self.nodeOptions.create),
-                        quick_create_undef = _.isUndefined(self.nodeOptions.create),
-                        m2x_create_undef = _.isUndefined(
-                            ir_options["web_m2x_options.create"]
-                        ),
-                        m2x_create = is_option_set(
-                            ir_options["web_m2x_options.create"]
-                        );
-                    var show_create =
-                        (!self.nodeOptions && (m2x_create_undef || m2x_create)) ||
-                        (self.nodeOptions &&
-                            (quick_create ||
-                                (quick_create_undef &&
-                                    (m2x_create_undef || m2x_create))));
-                    if (
-                        create_enabled &&
-                        !self.nodeOptions.no_quick_create &&
-                        search_val.length > 0 &&
-                        !_.contains(raw_result, search_val) &&
-                        show_create
-                    ) {
-                        values.push({
-                            label: _.str.sprintf(
-                                _t('Create "<strong>%s</strong>"'),
-                                $("<span />").text(search_val).html()
-                            ),
-                            action: self._quickCreate.bind(self, search_val),
-                            classname: "o_m2o_dropdown_option",
-                        });
-                    }
-                    // Create and edit ...
-
-                    var create_edit =
-                            is_option_set(self.nodeOptions.create) ||
-                            is_option_set(self.nodeOptions.create_edit),
-                        create_edit_undef =
-                            _.isUndefined(self.nodeOptions.create) &&
-                            _.isUndefined(self.nodeOptions.create_edit),
-                        m2x_create_edit_undef = _.isUndefined(
-                            ir_options["web_m2x_options.create_edit"]
-                        ),
-                        m2x_create_edit = is_option_set(
-                            ir_options["web_m2x_options.create_edit"]
-                        );
-                    var show_create_edit =
-                        (!self.nodeOptions &&
-                            (m2x_create_edit_undef || m2x_create_edit)) ||
-                        (self.nodeOptions &&
-                            (create_edit ||
-                                (create_edit_undef &&
-                                    (m2x_create_edit_undef || m2x_create_edit))));
-                    if (
-                        create_enabled &&
-                        !self.nodeOptions.no_create_edit &&
-                        show_create_edit
-                    ) {
-                        var createAndEditAction = function () {
-                            // Clear the value in case the user clicks on discard
-                            self.$("input").val("");
-                            return self._searchCreatePopup(
-                                "form",
-                                false,
-                                self._createContext(search_val)
-                            );
-                        };
-                        values.push({
-                            label: _t("Create and Edit..."),
-                            action: createAndEditAction,
-                            classname: "o_m2o_dropdown_option",
-                        });
-                    } else if (values.length === 0) {
-                        values.push({
-                            label: _t("No results to show..."),
-                        });
-                    }
-                    // Check if colors specified to wait for RPC
-                    if (!(self.field_color && self.colors)) {
-                        resolve(values);
-                    }
-                });
-            });
-            this.orderer.add(def);
-
-            // Add options limit used to change number of selections record
-            // returned.
-            if (!_.isUndefined(ir_options["web_m2x_options.limit"])) {
-                this.limit = parseInt(ir_options["web_m2x_options.limit"], 10);
-            }
-
-            if (typeof this.nodeOptions.limit === "number") {
-                this.limit = this.nodeOptions.limit;
-            }
-
-            return def;
-        },
-    });
-
-    FieldMany2ManyTags.include({
-        events: _.extend({}, FieldMany2ManyTags.prototype.events, {
-            "click .badge": "_onOpenBadge",
-        }),
-
-        _onDeleteTag: function (event) {
-            var result = this._super.apply(this, arguments);
-            event.stopPropagation();
-            return result;
-        },
-
-        _onOpenBadge: function (event) {
-            var self = this;
-            var open = self.nodeOptions && is_option_set(self.nodeOptions.open);
-            if (open) {
-                var context = self.record.getContext(self.recordParams);
-                var id = parseInt($(event.currentTarget).data("id"), 10);
-
-                if (self.mode === "readonly") {
-                    event.preventDefault();
-                    event.stopPropagation();
-                    self._rpc({
-                        model: self.field.relation,
-                        method: "get_formview_action",
-                        args: [[id]],
-                        context: context,
-                    }).then(function (action) {
-                        self.trigger_up("do_action", {action: action});
-                    });
-                } else {
-                    $.when(
-                        self._rpc({
-                            model: self.field.relation,
-                            method: "get_formview_id",
-                            args: [[id]],
-                            context: context,
-                        }),
-                        self._rpc({
-                            model: self.field.relation,
-                            method: "check_access_rights",
-                            kwargs: {operation: "write", raise_exception: false},
-                        })
-                    ).then(function (view_id, write_access) {
-                        var can_write =
-                            "can_write" in self.attrs
-                                ? JSON.parse(self.attrs.can_write)
-                                : true;
-                        new view_dialogs.FormViewDialog(self, {
-                            res_model: self.field.relation,
-                            res_id: id,
-                            context: context,
-                            title: _t("Open: ") + self.string,
-                            view_id: view_id,
-                            readonly: !can_write || !write_access,
-                            on_saved: function (record, changed) {
-                                if (changed) {
-                                    self._setValue(self.value.data, {
-                                        forceChange: true,
-                                    });
-                                    self.trigger_up("reload", {db_id: self.value.id});
-                                }
-                            },
-                        }).open();
-                    });
-                }
-            }
-        },
-    });
-
-    FieldOne2Many.include({
-        _onOpenRecord: function (ev) {
-            var self = this;
-            var open = this.nodeOptions.open;
-            if (open && self.mode === "readonly") {
-                ev.stopPropagation();
-                var id = ev.data.id;
-                var res_id = self.record.data[self.name].data.filter(
-                    (line) => line.id === id
-                )[0].res_id;
-                self._rpc({
-                    model: self.field.relation,
-                    method: "get_formview_action",
-                    args: [[res_id]],
-                }).then(function (action) {
-                    return self.do_action(action);
-                });
-            } else {
-                return this._super.apply(this, arguments);
-            }
-        },
-    });
-
-    FormFieldMany2ManyTags.include({
-        events: _.extend({}, FormFieldMany2ManyTags.prototype.events, {
-            "click .badge": "_onOpenBadge",
-        }),
-
-        _onOpenBadge: function (event) {
-            var open = is_option_set(this.nodeOptions.open);
-            var no_color_picker = is_option_set(this.nodeOptions.no_color_picker);
-            this._super.apply(this, arguments);
-            if (!open && !no_color_picker) {
-                this._onOpenColorPicker(event);
-            } else {
-                event.preventDefault();
-                event.stopPropagation();
-            }
-        },
-    });
-
-    // Extending class to allow change the limit of o2m registry entries using the
-    // system parameter "web_m2x_options.field_limit_entries".
-    FormView.include({
-        _setSubViewLimit: function (attrs) {
-            this._super(attrs);
-            var limit = ir_options["web_m2x_options.field_limit_entries"];
-            if (!_.isUndefined(limit)) {
-                attrs.limit = parseInt(limit);
-            }
-        },
-    });
-});
diff --git a/web_m2x_options/static/src/js/ir_options.js b/web_m2x_options/static/src/js/ir_options.js
deleted file mode 100644
index 293c66c51..000000000
--- a/web_m2x_options/static/src/js/ir_options.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Copyright 2020 Tecnativa - Carlos Roca
- *  * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
-odoo.define("web_m2x_options.ir_options", function (require) {
-    "use strict";
-
-    var rpc = require("web.rpc");
-
-    return rpc.query({
-        model: "ir.config_parameter",
-        method: "get_web_m2x_options",
-    });
-});
diff --git a/web_m2x_options/static/src/xml/base.xml b/web_m2x_options/static/src/xml/base.xml
deleted file mode 100644
index d2ac41b8a..000000000
--- a/web_m2x_options/static/src/xml/base.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!-- Copyright 2017 Jairo Llopis <jairo.llopis@tecnativa.com>
-     License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
-<templates xml:space="preserve">
-    <t t-extend="FieldMany2One">
-        <t t-jquery=".o_external_button" t-operation="attributes">
-            <attribute name="t-if">
-                !(widget.nodeOptions.no_open || widget.nodeOptions.no_open_edit || widget.noOpen)
-            </attribute>
-        </t>
-    </t>
-</templates>