diff --git a/doc/source/modules130-140.rst b/doc/source/modules130-140.rst
index d45084696221..5fc469e127a9 100644
--- a/doc/source/modules130-140.rst
+++ b/doc/source/modules130-140.rst
@@ -390,7 +390,7 @@ Module coverage 13.0 -> 14.0
+--------------------------------------------+-------------------------------------------------+
|lunch | |
+--------------------------------------------+-------------------------------------------------+
-|mail | |
+|mail | Done |
+--------------------------------------------+-------------------------------------------------+
|mail_bot | |
+--------------------------------------------+-------------------------------------------------+
diff --git a/openupgrade_scripts/scripts/mail/14.0.1.0/noupdate_changes.xml b/openupgrade_scripts/scripts/mail/14.0.1.0/noupdate_changes.xml
index 58b29042eadd..3f0c7f0d2204 100644
--- a/openupgrade_scripts/scripts/mail/14.0.1.0/noupdate_changes.xml
+++ b/openupgrade_scripts/scripts/mail/14.0.1.0/noupdate_changes.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/openupgrade_scripts/scripts/mail/14.0.1.0/post-migration.py b/openupgrade_scripts/scripts/mail/14.0.1.0/post-migration.py
new file mode 100644
index 000000000000..0ab499b78637
--- /dev/null
+++ b/openupgrade_scripts/scripts/mail/14.0.1.0/post-migration.py
@@ -0,0 +1,16 @@
+# Copyright 2021 ForgeFlow S.L.
+# Copyright 2021 Tecnativa - Pedro M. Baeza
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+from openupgradelib import openupgrade
+
+
+@openupgrade.migrate()
+def migrate(env, version):
+ # Current noupdate changes are better to not be applied for preserving continuity
+ # openupgrade.load_data(env.cr, "mail", "14.0.1.0/noupdate_changes.xml")
+ openupgrade.delete_records_safely_by_xml_id(
+ env,
+ [
+ "mail.mail_followers_read_write_own",
+ ],
+ )
diff --git a/openupgrade_scripts/scripts/mail/14.0.1.0/pre-migration.py b/openupgrade_scripts/scripts/mail/14.0.1.0/pre-migration.py
new file mode 100644
index 000000000000..409073e07681
--- /dev/null
+++ b/openupgrade_scripts/scripts/mail/14.0.1.0/pre-migration.py
@@ -0,0 +1,42 @@
+# Copyright 2021 ForgeFlow S.L.
+# Copyright 2021 Tecnativa - Pedro M. Baeza
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+from openupgradelib import openupgrade
+
+
+def fill_mail_tracking_value_field(env):
+ """Now the field is a hard many2one reference, so we need to traverse the
+ ir.model.fields record and fill it.
+
+ As the column is required, we do it on pre, and we need also to remove those
+ records whose field reference doesn't exist anymore.
+ """
+ openupgrade.logged_query(env.cr, "ALTER TABLE mail_tracking_value ADD field int4")
+ openupgrade.logged_query(
+ env.cr,
+ """
+ UPDATE mail_tracking_value mtv
+ SET field = imf.id
+ FROM ir_model_fields imf
+ JOIN mail_message mm ON imf.model = mm.model
+ WHERE imf.name = mtv.{} AND mtv.mail_message_id = mm.id
+ """.format(
+ openupgrade.get_legacy_name("field")
+ ),
+ )
+ openupgrade.logged_query(
+ env.cr, "DELETE FROM mail_tracking_value WHERE field IS NULL"
+ )
+
+
+@openupgrade.migrate()
+def migrate(env, version):
+ openupgrade.rename_models(
+ env.cr, [("email_template.preview", "mail.template.preview")]
+ )
+ openupgrade.rename_tables(
+ env.cr, [("email_template_preview", "mail_template_preview")]
+ )
+ openupgrade.set_xml_ids_noupdate_value(env, "mail", ["mail_channel_rule"], True)
+ openupgrade.rename_columns(env.cr, {"mail_tracking_value": [("field", None)]})
+ fill_mail_tracking_value_field(env)
diff --git a/openupgrade_scripts/scripts/mail/14.0.1.0/upgrade_analysis_work.txt b/openupgrade_scripts/scripts/mail/14.0.1.0/upgrade_analysis_work.txt
new file mode 100644
index 000000000000..444157f84e53
--- /dev/null
+++ b/openupgrade_scripts/scripts/mail/14.0.1.0/upgrade_analysis_work.txt
@@ -0,0 +1,96 @@
+---Models in module 'mail'---
+obsolete model email_template.preview (renamed to mail.template.preview) [transient]
+new model mail.template.preview (renamed from email_template.preview) [transient]
+# DONE: pre-migration: renamed model and table
+
+obsolete model mail.address.mixin [abstract]
+new model mail.render.mixin [abstract]
+new model mail.blacklist.remove [transient]
+# NOTHING TO DO.
+
+---Fields in module 'mail'---
+mail / ir.ui.view / type (False) : selection_keys is now '['activity', 'calendar', 'form', 'gantt', 'graph', 'kanban', 'pivot', 'qweb', 'search', 'tree']' ('['activity', 'calendar', 'diagram', 'form', 'gantt', 'graph', 'kanban', 'pivot', 'qweb', 'search', 'tree']')
+# NOTHING TO DO: Add new view type diagram
+
+mail / mail.activity / request_partner_id (many2one) : NEW relation:
+# NOTHING TO DO: New feature only used for now by website_slides (or maybe enterprise) modules, for a kind of tier validation workflow
+
+mail / mail.alias / alias_bounced_content (html) : NEW
+# NOTHING TO DO: New feature for specifying the message to return when bouncing. Not required and if empty, fallback one is used.
+
+mail / mail.channel / active (boolean) : NEW hasdefault
+# NOTHING TO DO: New feature that allows you to hide a channel without removing it. Let Odoo to fill the default (True).
+
+mail / mail.mail / description (char) : previously in module website_mail
+mail / mail.message / description (char) : previously in module website_mail
+# NOTHING TO DO: Handled by regular Odoo update mechanism
+
+mail / mail.message / is_internal (boolean) : NEW
+# NOTHING TO DO: New feature. You can hide messages from some users independently from subtype configuration. Default (False) is OK.
+
+mail / mail.template / copyvalue (char) : not stored anymore
+mail / mail.template / model_object_field (many2one) : not stored anymore
+mail / mail.template / null_value (char) : not stored anymore
+mail / mail.template / sub_model_object_field (many2one): not stored anymore
+mail / mail.template / sub_object (many2one) : not stored anymore
+# NOTHING TO DO: fields don't need to be stored any more. They are now variables used to create expressions in advanced tab in mail templates
+
+mail / mail.template / user_signature (boolean) : DEL
+# NOTHING TO DO: User signature not a feature anymore. Not deleted to preserve data
+
+mail / mail.tracking.value / field (char) : relation is now 'ir.model.fields' ('False') [nothing to do]
+mail / mail.tracking.value / field (char) : type is now 'many2one' ('char')
+# DONE: pre-migration: rename field column
+# DONE: pre-migration: pre-create the field and fill it with the id of the field from the original column, and remove records for obsolete fields
+
+mail / res.users / alias_id (many2one) : DEL relation: mail.alias
+mail / res.users / out_of_office_message (char) : DEL
+# NOTHING TO DO: old features not used in new version. Alias id is not required anymore.
+
+---XML records in module 'mail'---
+NEW ir.actions.act_window: mail.mail_notification_action
+NEW ir.actions.act_window: mail.mail_template_preview_action
+DEL ir.actions.act_window: mail.wizard_email_template_preview
+NEW ir.model.access: mail.access_mail_blacklist_remove_system
+NEW ir.model.access: mail.access_mail_channel_partner_user
+NEW ir.model.access: mail.access_mail_compose_message
+NEW ir.model.access: mail.access_mail_compose_message_portal
+NEW ir.model.access: mail.access_mail_resend_cancel
+NEW ir.model.access: mail.access_mail_resend_message
+NEW ir.model.access: mail.access_mail_resend_partner
+NEW ir.model.access: mail.access_mail_template_preview
+NEW ir.model.access: mail.access_mail_wizard_invite
+NEW ir.model.access: mail.ir_actions_report_access_user
+DEL ir.model.access: mail.access mail_channel_partner_user
+DEL ir.model.access: mail.access_mail_followers_portal
+DEL ir.model.access: mail.access_mail_thread_all
+NEW ir.ui.menu: mail.mail_menu_technical
+NEW ir.ui.menu: mail.mail_notification_menu
+NEW ir.ui.view: mail.assets_backend_prod_only
+NEW ir.ui.view: mail.assets_tests
+NEW ir.ui.view: mail.mail_blacklist_remove_view_form
+NEW ir.ui.view: mail.mail_bounce_alias_security
+NEW ir.ui.view: mail.mail_message_view_form
+NEW ir.ui.view: mail.mail_notification_view_form
+NEW ir.ui.view: mail.mail_notification_view_tree
+NEW ir.ui.view: mail.mail_template_preview_view_form
+NEW ir.ui.view: mail.qunit_mobile_suite
+NEW ir.ui.view: mail.res_partner_view_kanban_inherit_mail
+NEW ir.ui.view: mail.res_partner_view_tree_inherit_mail
+NEW ir.ui.view: mail.tests_assets
+DEL ir.ui.view: mail.email_template_preview_form
+DEL ir.ui.view: mail.js_test_assets
+DEL ir.ui.view: mail.res_partner_view_form_short
+DEL ir.ui.view: mail.view_message_form
+# NOTHING TO DO: noupdate="0" records
+
+ir.rule: mail.mail_channel_rule (noupdate) (noupdate switched)
+# DONE: pre-migration: noupdate changed to "1" with no changes from 13.0
+
+NEW ir.rule: mail.ir_rule_mail_channel_partner_group_user (noupdate)
+NEW ir.rule: mail.mail_compose_message_rule (noupdate)
+NEW mail.channel.partner: mail.channel_partner_general_channel_for_admin (noupdate)
+# NOTHING TO DO: New noupdate="1" records
+
+DEL ir.rule: mail.mail_followers_read_write_own (noupdate)
+# DONE: post-migration: try to delete
\ No newline at end of file