diff --git a/.badges/coverage.svg b/.badges/coverage.svg
index 246f074af9f5041eefcd54b1974576598bf98f8f_LmJhZGdlcy9jb3ZlcmFnZS5zdmc=..1ec96eae270f2bac3b80666e469d67d24b27874b_LmJhZGdlcy9jb3ZlcmFnZS5zdmc= 100644
--- a/.badges/coverage.svg
+++ b/.badges/coverage.svg
@@ -1,2 +1,2 @@
-<?xml version="1.0" encoding="UTF-8"?>
+<?xml version="1.0" encoding="UTF-8" ?>
 <svg xmlns="http://www.w3.org/2000/svg" width="124" height="20">
@@ -2,22 +2,32 @@
 <svg xmlns="http://www.w3.org/2000/svg" width="124" height="20">
-    <linearGradient id="b" x2="0" y2="100%">
-        <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
-        <stop offset="1" stop-opacity=".1"/>
-    </linearGradient>
-    <mask id="anybadge_1">
-        <rect width="124" height="20" rx="3" fill="#fff"/>
-    </mask>
-    <g mask="url(#anybadge_1)">
-        <path fill="#555" d="M0 0h65v20H0z"/>
-        <path fill="#808080" d="M65 0h59v20H65z"/>
-        <path fill="url(#b)" d="M0 0h124v20H0z"/>
-    </g>
-    <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
-        <text x="33.5" y="15" fill="#010101" fill-opacity=".3">coverage</text>
-        <text x="32.5" y="14">coverage</text>
-    </g>
-    <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
-        <text x="95.5" y="15" fill="#010101" fill-opacity=".3">unknown</text>
-        <text x="94.5" y="14">unknown</text>
-    </g>
-</svg>
\ No newline at end of file
+  <linearGradient id="b" x2="0" y2="100%">
+    <stop offset="0" stop-color="#bbb" stop-opacity=".1" />
+    <stop offset="1" stop-opacity=".1" />
+  </linearGradient>
+  <mask id="anybadge_1">
+    <rect width="124" height="20" rx="3" fill="#fff" />
+  </mask>
+  <g mask="url(#anybadge_1)">
+    <path fill="#555" d="M0 0h65v20H0z" />
+    <path fill="#808080" d="M65 0h59v20H65z" />
+    <path fill="url(#b)" d="M0 0h124v20H0z" />
+  </g>
+  <g
+    fill="#fff"
+    text-anchor="middle"
+    font-family="DejaVu Sans,Verdana,Geneva,sans-serif"
+    font-size="11"
+  >
+    <text x="33.5" y="15" fill="#010101" fill-opacity=".3">coverage</text>
+    <text x="32.5" y="14">coverage</text>
+  </g>
+  <g
+    fill="#fff"
+    text-anchor="middle"
+    font-family="DejaVu Sans,Verdana,Geneva,sans-serif"
+    font-size="11"
+  >
+    <text x="95.5" y="15" fill="#010101" fill-opacity=".3">unknown</text>
+    <text x="94.5" y="14">unknown</text>
+  </g>
+</svg>
diff --git a/.badges/maturity.svg b/.badges/maturity.svg
index 246f074af9f5041eefcd54b1974576598bf98f8f_LmJhZGdlcy9tYXR1cml0eS5zdmc=..1ec96eae270f2bac3b80666e469d67d24b27874b_LmJhZGdlcy9tYXR1cml0eS5zdmc= 100644
--- a/.badges/maturity.svg
+++ b/.badges/maturity.svg
@@ -1,23 +1,33 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="97" height="20">
-    <linearGradient id="b" x2="0" y2="100%">
-        <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
-        <stop offset="1" stop-opacity=".1"/>
-    </linearGradient>
-    <mask id="anybadge_1">
-        <rect width="97" height="20" rx="3" fill="#fff"/>
-    </mask>
-    <g mask="url(#anybadge_1)">
-        <path fill="#555" d="M0 0h61v20H0z"/>
-        <path fill="#dfb317" d="M61 0h36v20H61z"/>
-        <path fill="url(#b)" d="M0 0h97v20H0z"/>
-    </g>
-    <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
-        <text x="31.5" y="15" fill="#010101" fill-opacity=".3">maturity</text>
-        <text x="30.5" y="14">maturity</text>
-    </g>
-    <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
-        <text x="80.0" y="15" fill="#010101" fill-opacity=".3">Beta</text>
-        <text x="79.0" y="14">Beta</text>
-    </g>
-</svg>
\ No newline at end of file
+<?xml version="1.0" encoding="UTF-8" ?>
+<svg xmlns="http://www.w3.org/2000/svg" width="103" height="20">
+  <linearGradient id="b" x2="0" y2="100%">
+    <stop offset="0" stop-color="#bbb" stop-opacity=".1" />
+    <stop offset="1" stop-opacity=".1" />
+  </linearGradient>
+  <mask id="anybadge_1">
+    <rect width="103" height="20" rx="3" fill="#fff" />
+  </mask>
+  <g mask="url(#anybadge_1)">
+    <path fill="#555" d="M0 0h61v20H0z" />
+    <path fill="#e05d44" d="M61 0h42v20H61z" />
+    <path fill="url(#b)" d="M0 0h103v20H0z" />
+  </g>
+  <g
+    fill="#fff"
+    text-anchor="middle"
+    font-family="DejaVu Sans,Verdana,Geneva,sans-serif"
+    font-size="11"
+  >
+    <text x="31.5" y="15" fill="#010101" fill-opacity=".3">maturity</text>
+    <text x="30.5" y="14">maturity</text>
+  </g>
+  <g
+    fill="#fff"
+    text-anchor="middle"
+    font-family="DejaVu Sans,Verdana,Geneva,sans-serif"
+    font-size="11"
+  >
+    <text x="83.0" y="15" fill="#010101" fill-opacity=".3">Alpha</text>
+    <text x="82.0" y="14">Alpha</text>
+  </g>
+</svg>
diff --git a/.editorconfig b/.editorconfig
index 246f074af9f5041eefcd54b1974576598bf98f8f_LmVkaXRvcmNvbmZpZw==..1ec96eae270f2bac3b80666e469d67d24b27874b_LmVkaXRvcmNvbmZpZw== 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,5 +1,5 @@
 # Configuration for known file extensions
-[*.{css,js,json,less,md,py,rst,sass,scss,xml,yaml,yml}]
+[*.{css,htm,html,js,json,jsx,less,markdown,md,py,rst,sass,scss,toml,xml,yaml,yml}]
 charset = utf-8
 end_of_line = lf
 indent_size = 4
@@ -7,6 +7,6 @@
 insert_final_newline = true
 trim_trailing_whitespace = true
 
-[*.{json,yml,yaml,rst,md}]
+[*.{json,yml,yaml,rst,markdown,md,toml}]
 indent_size = 2
 
@@ -11,7 +11,7 @@
 indent_size = 2
 
-# Do not configure editor for libs and autogenerated content
-[{*/static/{lib,src/lib}/**,*/static/description/index.html,*/readme/../README.rst}]
+# Do not configure editor for libs
+[*/static/{lib,src/lib}/**]
 charset = unset
 end_of_line = unset
 indent_size = unset
diff --git a/.flake8 b/.flake8
index 246f074af9f5041eefcd54b1974576598bf98f8f_LmZsYWtlOA==..1ec96eae270f2bac3b80666e469d67d24b27874b_LmZsYWtlOA== 100644
--- a/.flake8
+++ b/.flake8
@@ -1,2 +1,5 @@
 [flake8]
 max-line-length = 88
+per-file-ignores=
+    __init__.py:F401
+    __manifest__.py:B018
diff --git a/.hgtags b/.hgtags
index 246f074af9f5041eefcd54b1974576598bf98f8f_LmhndGFncw==..1ec96eae270f2bac3b80666e469d67d24b27874b_LmhndGFncw== 100644
--- a/.hgtags
+++ b/.hgtags
@@ -7,3 +7,6 @@
 0f5364c5f0676b4894bffad5d54c1d47ada47880 13.0.3.0.0
 5db7fccd06c7956ae0bf11e9b578e13b47476bef 13.0.3.0.0
 480a9d15f5a00f29127d7dafef1b2b90f853af58 15.0.2.0.0
+2c908ba2f79caedbc299fb18d5c929480bcbd464 13.0.3.0.1
+b9202f7d5dc1fe8c3d532c8c441c9f11b5911acb 13.0.3.0.2
+bc4a32eb10ad712d229fed2017dde115cdb1d432 13.0.3.1.0
diff --git a/NEWS.rst b/NEWS.rst
index 246f074af9f5041eefcd54b1974576598bf98f8f_TkVXUy5yc3Q=..1ec96eae270f2bac3b80666e469d67d24b27874b_TkVXUy5yc3Q= 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -1,6 +1,29 @@
 Changelog
 =========
 
+15.0.3.1.0
+----------
+
+(port from 13.0.3.1.0)
+~~~~~~~~~~~~~~~~~~~~~~
+
+* Code formatting, add license / docs / badges.
+
+(port from 13.0.3.1.0 / 11.0.1.2.0)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* MailTemplate converter: Allow multiple records.
+* 15.0: In the process, also fix existing MailTemplate converter (found by these new tests).
+
+(port from 13.0.3.0.2)
+~~~~~~~~~~~~~~~~~~~~~~
+
+Fix issue with Switch and the use of Skip as a converter, also make switch send Skip when nothing matches its rules.
+
+Fix issue with RelationToMany that always convert to Skip when send_empty is False.
+
+Fix issue with chaining relation converter with empty values.
+
 15.0.3.0.0
 ----------
 
diff --git a/README.rst b/README.rst
index 246f074af9f5041eefcd54b1974576598bf98f8f_UkVBRE1FLnJzdA==..1ec96eae270f2bac3b80666e469d67d24b27874b_UkVBRE1FLnJzdA== 100644
--- a/README.rst
+++ b/README.rst
@@ -11,7 +11,7 @@
     :alt: pylint score
 .. |maturity| image:: .badges/maturity.svg
     :target: https://odoo-community.org/page/development-status
-    :alt: Beta
+    :alt: Stable
 .. |license| image:: .badges/licence-AGPL--3-blue.svg
     :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
     :alt: License: AGPL-3
diff --git a/__init__.py b/__init__.py
index 246f074af9f5041eefcd54b1974576598bf98f8f_X19pbml0X18ucHk=..1ec96eae270f2bac3b80666e469d67d24b27874b_X19pbml0X18ucHk= 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1,18 +1,46 @@
-from . import models  # noqa: F401
-from .base import *  # noqa: F401,F403
-from .exception import InternalError  # noqa: F401
-from .field import Field, TranslatedSelection  # noqa: F401
-from .keyfield import KeyField  # noqa: F401
-from .list import List  # noqa: F401
-from .mail_template import MailTemplate  # noqa: F401
-from .model import Model  # noqa: F401
-from .relation import RelationToManyMap  # noqa: F401
-from .relation import RelationToMany, RelationToOne, relation  # noqa: F401
-from .switch import Switch  # noqa: F401
-from .validate import (  # noqa: F401
+##############################################################################
+#
+#    Converter Odoo module
+#    Copyright (C) 2020 XCG Consulting <https://xcg-consulting.fr>
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from . import models
+from .base import (
+    Computed,
+    Constant,
+    ContextBuilder,
+    Converter,
+    Newinstance,
+    Readonly,
+    Skip,
+    message_to_odoo,
+)
+from .exception import InternalError
+from .field import Field, TranslatedSelection
+from .keyfield import KeyField
+from .list import List
+from .mail_template import MailTemplate
+from .model import Model
+from .relation import RelationToMany, RelationToManyMap, RelationToOne, relation
+from .switch import Switch
+from .validate import (
     VALIDATION_SKIP,
     VALIDATION_SOFT,
     VALIDATION_STRICT,
     NotInitialized,
     Validator,
 )
@@ -13,7 +41,7 @@
     VALIDATION_SKIP,
     VALIDATION_SOFT,
     VALIDATION_STRICT,
     NotInitialized,
     Validator,
 )
-from .xref import Xref  # noqa: F401
+from .xref import Xref
diff --git a/__manifest__.py b/__manifest__.py
index 246f074af9f5041eefcd54b1974576598bf98f8f_X19tYW5pZmVzdF9fLnB5..1ec96eae270f2bac3b80666e469d67d24b27874b_X19tYW5pZmVzdF9fLnB5 100644
--- a/__manifest__.py
+++ b/__manifest__.py
@@ -1,7 +1,7 @@
 ##############################################################################
 #
-#    Converter for Odoo
-#    Copyright (C) 2020, 2022 XCG Consulting <https://xcg-consulting.fr/>
+#    Converter Odoo module
+#    Copyright (C) 2020 XCG Consulting <https://xcg-consulting.fr>
 #
 #    This program is free software: you can redistribute it and/or modify
 #    it under the terms of the GNU Affero General Public License as
@@ -17,7 +17,8 @@
 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 ##############################################################################
+
 {
     "name": "Converter",
     "license": "AGPL-3",
     "summary": "Convert odoo records to/from plain data structures.",
@@ -20,10 +21,10 @@
 {
     "name": "Converter",
     "license": "AGPL-3",
     "summary": "Convert odoo records to/from plain data structures.",
-    "version": "15.0.3.0.0",
+    "version": "15.0.3.1.0",
     "category": "Hidden",
     "author": "XCG Consulting",
     "website": "https://odoo.consulting/",
     "depends": ["base", "mail"],
     "data": [],
@@ -25,6 +26,8 @@
     "category": "Hidden",
     "author": "XCG Consulting",
     "website": "https://odoo.consulting/",
     "depends": ["base", "mail"],
     "data": [],
+    "installable": True,
+    # These dependencies are in the "requirements" file.
     "external_dependencies": {"python": ["jsonschema"]},
@@ -30,3 +33,2 @@
     "external_dependencies": {"python": ["jsonschema"]},
-    "installable": True,
 }
diff --git a/base.py b/base.py
index 246f074af9f5041eefcd54b1974576598bf98f8f_YmFzZS5weQ==..1ec96eae270f2bac3b80666e469d67d24b27874b_YmFzZS5weQ== 100644
--- a/base.py
+++ b/base.py
@@ -1,7 +1,7 @@
 ##############################################################################
 #
-#    Converter for Odoo
-#    Copyright (C) 2020, 2022 XCG Consulting <http://odoo.consulting>
+#    Converter Odoo module
+#    Copyright (C) 2020 XCG Consulting <https://xcg-consulting.fr>
 #
 #    This program is free software: you can redistribute it and/or modify
 #    it under the terms of the GNU Affero General Public License as
diff --git a/exception.py b/exception.py
index 246f074af9f5041eefcd54b1974576598bf98f8f_ZXhjZXB0aW9uLnB5..1ec96eae270f2bac3b80666e469d67d24b27874b_ZXhjZXB0aW9uLnB5 100644
--- a/exception.py
+++ b/exception.py
@@ -1,7 +1,7 @@
 ##############################################################################
 #
-#    Converter for Odoo
-#    Copyright (C) 2022 XCG Consulting <https://xcg-consulting.fr/>
+#    Converter Odoo module
+#    Copyright (C) 2022 XCG Consulting <https://xcg-consulting.fr>
 #
 #    This program is free software: you can redistribute it and/or modify
 #    it under the terms of the GNU Affero General Public License as
@@ -17,5 +17,7 @@
 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 ##############################################################################
+
+
 class InternalError(Exception):
     pass
diff --git a/field.py b/field.py
index 246f074af9f5041eefcd54b1974576598bf98f8f_ZmllbGQucHk=..1ec96eae270f2bac3b80666e469d67d24b27874b_ZmllbGQucHk= 100644
--- a/field.py
+++ b/field.py
@@ -1,3 +1,23 @@
+##############################################################################
+#
+#    Converter Odoo module
+#    Copyright (C) 2020 XCG Consulting <https://xcg-consulting.fr>
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
 import datetime
 from typing import Any, Callable, Optional
 
@@ -31,7 +51,8 @@
         Odoo
         :param message_formatter: method that will be used to format the value
         in the message. Signature should be (value: Any, is_blank: bool).
-        In the case of datetime, isoformat will not be used if this is defined.
+        In the case of datetime and date, isoformat will not be used if this is
+        defined.
         :param odoo_formatter: method that will be used to format the value
         for odoo.
         """
@@ -89,6 +110,9 @@
             )
             if not self._message_formatter:
                 value = value.isoformat()
+        if isinstance(value, datetime.date):
+            if not self._message_formatter:
+                value = value.isoformat()
         if self._message_formatter:
             value = self._message_formatter(value, False)
         return value
diff --git a/keyfield.py b/keyfield.py
index 246f074af9f5041eefcd54b1974576598bf98f8f_a2V5ZmllbGQucHk=..1ec96eae270f2bac3b80666e469d67d24b27874b_a2V5ZmllbGQucHk= 100644
--- a/keyfield.py
+++ b/keyfield.py
@@ -1,3 +1,23 @@
+##############################################################################
+#
+#    Converter Odoo module
+#    Copyright (C) 2020 XCG Consulting <https://xcg-consulting.fr>
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
 from typing import Any, Optional
 
 from odoo import models
diff --git a/list.py b/list.py
index 246f074af9f5041eefcd54b1974576598bf98f8f_bGlzdC5weQ==..1ec96eae270f2bac3b80666e469d67d24b27874b_bGlzdC5weQ== 100644
--- a/list.py
+++ b/list.py
@@ -1,3 +1,23 @@
+##############################################################################
+#
+#    Converter Odoo module
+#    Copyright (C) 2020 XCG Consulting <https://xcg-consulting.fr>
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
 from typing import Any, Optional
 
 from odoo import models
diff --git a/mail_template.py b/mail_template.py
index 246f074af9f5041eefcd54b1974576598bf98f8f_bWFpbF90ZW1wbGF0ZS5weQ==..1ec96eae270f2bac3b80666e469d67d24b27874b_bWFpbF90ZW1wbGF0ZS5weQ== 100644
--- a/mail_template.py
+++ b/mail_template.py
@@ -1,3 +1,23 @@
+##############################################################################
+#
+#    Converter Odoo module
+#    Copyright (C) 2020 XCG Consulting <https://xcg-consulting.fr>
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
 import ast
 from typing import Any, Optional
 
@@ -7,7 +27,12 @@
 
 
 class MailTemplate(base.Converter):
+    """This converter wraps ``mail.template::_render_template``.
+    Multiple records are allowed but ``mail.template::_render_template`` still
+    runs once per record; to accomodate, we provide ``ctx["records"]``.
+    """
+
     def __init__(self, template: str, post_eval: bool = False):
         self.template = template
         self.post_eval = post_eval
 
@@ -10,12 +35,14 @@
     def __init__(self, template: str, post_eval: bool = False):
         self.template = template
         self.post_eval = post_eval
 
-    def odoo_to_message(
-        self, instance: models.Model, ctx: Optional[dict] = None
-    ) -> Any:
-        template = instance.env["mail.template"].with_context(safe=True)
-        value = template._render_template(self.template, instance._name, instance.id)
+    def odoo_to_message(self, records: models.Model, ctx: Optional[dict] = None) -> Any:
+        value = (
+            records.env["mail.template"]
+            .with_context(records=records, safe=True)
+            ._render_template(self.template, records._name, records.ids)
+        )
+        value = value[records[0].id]  # _render_template outputs indexed by record ID
         if self.post_eval:
             value = ast.literal_eval(value)
         return value
diff --git a/model.py b/model.py
index 246f074af9f5041eefcd54b1974576598bf98f8f_bW9kZWwucHk=..1ec96eae270f2bac3b80666e469d67d24b27874b_bW9kZWwucHk= 100644
--- a/model.py
+++ b/model.py
@@ -1,3 +1,23 @@
+##############################################################################
+#
+#    Converter Odoo module
+#    Copyright (C) 2020 XCG Consulting <https://xcg-consulting.fr>
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
 import logging
 from collections import OrderedDict
 from collections.abc import Iterable, Mapping
diff --git a/models/__init__.py b/models/__init__.py
index 246f074af9f5041eefcd54b1974576598bf98f8f_bW9kZWxzL19faW5pdF9fLnB5..1ec96eae270f2bac3b80666e469d67d24b27874b_bW9kZWxzL19faW5pdF9fLnB5 100644
--- a/models/__init__.py
+++ b/models/__init__.py
@@ -1,1 +1,1 @@
-from . import ir_model_data  # noqa: F401
+from . import ir_model_data
diff --git a/models/ir_model_data.py b/models/ir_model_data.py
index 246f074af9f5041eefcd54b1974576598bf98f8f_bW9kZWxzL2lyX21vZGVsX2RhdGEucHk=..1ec96eae270f2bac3b80666e469d67d24b27874b_bW9kZWxzL2lyX21vZGVsX2RhdGEucHk= 100644
--- a/models/ir_model_data.py
+++ b/models/ir_model_data.py
@@ -1,7 +1,7 @@
 ##############################################################################
 #
-#    Converter for Odoo
-#    Copyright (C) 2021, 2022 XCG Consulting <https://xcg-consulting.fr/>
+#    Converter Odoo module
+#    Copyright (C) 2020 XCG Consulting <https://xcg-consulting.fr>
 #
 #    This program is free software: you can redistribute it and/or modify
 #    it under the terms of the GNU Affero General Public License as
@@ -17,6 +17,7 @@
 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 ##############################################################################
+
 import uuid
 
 from odoo import api, models
diff --git a/relation.py b/relation.py
index 246f074af9f5041eefcd54b1974576598bf98f8f_cmVsYXRpb24ucHk=..1ec96eae270f2bac3b80666e469d67d24b27874b_cmVsYXRpb24ucHk= 100644
--- a/relation.py
+++ b/relation.py
@@ -1,3 +1,23 @@
+##############################################################################
+#
+#    Converter Odoo module
+#    Copyright (C) 2020 XCG Consulting <https://xcg-consulting.fr>
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
 import logging
 from typing import Any, Callable, Optional
 
@@ -13,7 +33,7 @@
     def __init__(
         self,
         field_name: str,
-        model_name: Optional[str],
+        model_name: str,
         converter: Converter,
         send_empty: bool = True,
         context: Optional[ContextBuilder] = None,
@@ -30,8 +50,11 @@
         ctx = build_context(instance, ctx, self.context)
         # do not use super, otherwise if empty, will convert that
         relation_instance = getattr(instance, self.field_name)
-        if not self._send_empty:
-            return Skip
+        if not relation_instance:
+            if not self._send_empty:
+                return Skip
+            else:
+                relation_instance = instance.env[self.model_name]
         return self.converter.odoo_to_message(relation_instance, ctx)
 
     def message_to_odoo(
diff --git a/switch.py b/switch.py
index 246f074af9f5041eefcd54b1974576598bf98f8f_c3dpdGNoLnB5..1ec96eae270f2bac3b80666e469d67d24b27874b_c3dpdGNoLnB5 100644
--- a/switch.py
+++ b/switch.py
@@ -1,5 +1,25 @@
+##############################################################################
+#
+#    Converter Odoo module
+#    Copyright (C) 2020 XCG Consulting <https://xcg-consulting.fr>
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
 from collections.abc import Mapping
 from typing import Any, Callable, Optional
 
 from odoo import api, models
 
@@ -1,9 +21,9 @@
 from collections.abc import Mapping
 from typing import Any, Callable, Optional
 
 from odoo import api, models
 
-from .base import ContextBuilder, Converter, Skip
+from .base import ContextBuilder, Converter, Skip, SkipType
 from .validate import VALIDATION_SKIP, Validator
 
 
@@ -59,5 +79,8 @@
     ) -> Any:
         for out_cond, _in_cond, converter in self._converters:
             if out_cond is None or out_cond(instance):
-                return converter.odoo_to_message(instance, ctx)
+                if isinstance(converter, SkipType):
+                    return converter
+                else:
+                    return converter.odoo_to_message(instance, ctx)
 
@@ -63,5 +86,5 @@
 
-        return None
+        return Skip
 
     def message_to_odoo(
         self,
diff --git a/tests/__init__.py b/tests/__init__.py
index 246f074af9f5041eefcd54b1974576598bf98f8f_dGVzdHMvX19pbml0X18ucHk=..1ec96eae270f2bac3b80666e469d67d24b27874b_dGVzdHMvX19pbml0X18ucHk= 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -1,6 +1,9 @@
-from . import test_base  # noqa: F401
-from . import test_converters  # noqa: F401
-from . import test_field  # noqa: F401
-from . import test_ir_model  # noqa: F401
-from . import test_relation  # noqa: F401
-from . import test_switch  # noqa: F401
+from . import (
+    test_base,
+    test_converters,
+    test_field,
+    test_ir_model,
+    test_mail_template,
+    test_relation,
+    test_switch,
+)
diff --git a/tests/test_base.py b/tests/test_base.py
index 246f074af9f5041eefcd54b1974576598bf98f8f_dGVzdHMvdGVzdF9iYXNlLnB5..1ec96eae270f2bac3b80666e469d67d24b27874b_dGVzdHMvdGVzdF9iYXNlLnB5 100644
--- a/tests/test_base.py
+++ b/tests/test_base.py
@@ -1,7 +1,7 @@
 ##############################################################################
 #
-#    Converter for Odoo
-#    Copyright (C) 2021, 2022 XCG Consulting <https://xcg-consulting.fr/>
+#    Converter Odoo module
+#    Copyright (C) 2020 XCG Consulting <https://xcg-consulting.fr>
 #
 #    This program is free software: you can redistribute it and/or modify
 #    it under the terms of the GNU Affero General Public License as
@@ -17,6 +17,7 @@
 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 ##############################################################################
+
 from odoo import tests
 
 from odoo.addons.converter import Constant, Field, Model, Xref, message_to_odoo
diff --git a/tests/test_converters.py b/tests/test_converters.py
index 246f074af9f5041eefcd54b1974576598bf98f8f_dGVzdHMvdGVzdF9jb252ZXJ0ZXJzLnB5..1ec96eae270f2bac3b80666e469d67d24b27874b_dGVzdHMvdGVzdF9jb252ZXJ0ZXJzLnB5 100644
--- a/tests/test_converters.py
+++ b/tests/test_converters.py
@@ -1,7 +1,7 @@
 ##############################################################################
 #
-#    Converter for Odoo
-#    Copyright (C) 2020, 2022 XCG Consulting <https://xcg-consulting.fr/>
+#    Converter Odoo module
+#    Copyright (C) 2020 XCG Consulting <https://xcg-consulting.fr>
 #
 #    This program is free software: you can redistribute it and/or modify
 #    it under the terms of the GNU Affero General Public License as
diff --git a/tests/test_field.py b/tests/test_field.py
index 246f074af9f5041eefcd54b1974576598bf98f8f_dGVzdHMvdGVzdF9maWVsZC5weQ==..1ec96eae270f2bac3b80666e469d67d24b27874b_dGVzdHMvdGVzdF9maWVsZC5weQ== 100644
--- a/tests/test_field.py
+++ b/tests/test_field.py
@@ -1,7 +1,7 @@
 ##############################################################################
 #
-#    Converter for Odoo
-#    Copyright (C) 2021, 2022 XCG Consulting <https://xcg-consulting.fr/>
+#    Converter Odoo module
+#    Copyright (C) 2021 XCG Consulting <https://xcg-consulting.fr>
 #
 #    This program is free software: you can redistribute it and/or modify
 #    it under the terms of the GNU Affero General Public License as
@@ -17,6 +17,7 @@
 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 ##############################################################################
+
 from typing import Any
 
 from odoo import tests
diff --git a/tests/test_ir_model.py b/tests/test_ir_model.py
index 246f074af9f5041eefcd54b1974576598bf98f8f_dGVzdHMvdGVzdF9pcl9tb2RlbC5weQ==..1ec96eae270f2bac3b80666e469d67d24b27874b_dGVzdHMvdGVzdF9pcl9tb2RlbC5weQ== 100644
--- a/tests/test_ir_model.py
+++ b/tests/test_ir_model.py
@@ -1,2 +1,2 @@
-###############################################################################
+##############################################################################
 #
@@ -2,6 +2,6 @@
 #
-#    Converter for Odoo
-#    Copyright (C) 2018, 2022 XCG Consulting (https://xcg-consulting.fr/)
+#    Converter Odoo module
+#    Copyright (C) 2020 XCG Consulting <https://xcg-consulting.fr>
 #
 #    This program is free software: you can redistribute it and/or modify
 #    it under the terms of the GNU Affero General Public License as
@@ -16,7 +16,8 @@
 #    You should have received a copy of the GNU Affero General Public License
 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
-###############################################################################
+##############################################################################
+
 from odoo.tests import TransactionCase, tagged
 
 
diff --git a/tests/test_mail_template.py b/tests/test_mail_template.py
new file mode 100644
index 0000000000000000000000000000000000000000..1ec96eae270f2bac3b80666e469d67d24b27874b_dGVzdHMvdGVzdF9tYWlsX3RlbXBsYXRlLnB5
--- /dev/null
+++ b/tests/test_mail_template.py
@@ -0,0 +1,44 @@
+##############################################################################
+#
+#    Converter Odoo module
+#    Copyright (C) 2023 XCG Consulting <https://xcg-consulting.fr>
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from odoo import tests
+
+from odoo.addons.converter import MailTemplate
+
+
+class Test(tests.TransactionCase):
+    """Test converter that wraps ``mail.template::_render_template``."""
+
+    def setUp(self):
+        super().setUp()
+        self.user_admin = self.env.ref("base.user_admin")
+        self.user_demo = self.env.ref("base.user_demo")
+
+    def test_mail_template_odoo_to_message(self):
+        converter = MailTemplate("hello {{ object.login }}")
+        converted = converter.odoo_to_message(self.user_demo)
+        self.assertEqual(converted, "hello demo")
+
+    def test_mail_template_odoo_to_message_multiple_records(self):
+        converter = MailTemplate(
+            "hello {{ ' & '.join(ctx['records'].sorted('login').mapped('login')) }}"
+        )
+        converted = converter.odoo_to_message(self.user_admin | self.user_demo)
+        self.assertEqual(converted, "hello admin & demo")
diff --git a/tests/test_relation.py b/tests/test_relation.py
index 246f074af9f5041eefcd54b1974576598bf98f8f_dGVzdHMvdGVzdF9yZWxhdGlvbi5weQ==..1ec96eae270f2bac3b80666e469d67d24b27874b_dGVzdHMvdGVzdF9yZWxhdGlvbi5weQ== 100644
--- a/tests/test_relation.py
+++ b/tests/test_relation.py
@@ -1,7 +1,7 @@
 ##############################################################################
 #
-#    Converter for Odoo
-#    Copyright (C) 2021, 2022 XCG Consulting <https://xcg-consulting.fr/>
+#    Converter Odoo module
+#    Copyright (C) 2021 XCG Consulting <https://xcg-consulting.fr>
 #
 #    This program is free software: you can redistribute it and/or modify
 #    it under the terms of the GNU Affero General Public License as
@@ -17,5 +17,6 @@
 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 ##############################################################################
+
 from odoo import tests
 
@@ -20,6 +21,13 @@
 from odoo import tests
 
-from odoo.addons.converter import Field, Model, RelationToOne, Xref, message_to_odoo
+from odoo.addons.converter import (
+    Field,
+    Model,
+    RelationToOne,
+    Skip,
+    Xref,
+    message_to_odoo,
+)
 
 
 class Test(tests.TransactionCase):
@@ -28,6 +36,12 @@
         super(Test, cls).setUpClass()
         cls.converter1 = RelationToOne("company_id", "res.company", Xref(None))
         cls.converter2 = RelationToOne("action_id", "res.company", Xref(None))
+        cls.converter3 = RelationToOne(
+            "action_id", "res.company", Xref(None), send_empty=False
+        )
+        cls.converter4 = RelationToOne(
+            "company_id", "res.company", Xref(None), send_empty=False
+        )
 
     def setUp(self):
         super().setUp()
@@ -40,7 +54,11 @@
         message = self.converter1.odoo_to_message(self.user_root)
         self.assertEqual(message, "base.main_company")
 
+    def test_many2one_skip_from_odoo(self):
+        message = self.converter4.odoo_to_message(self.user_admin)
+        self.assertEqual(message, "base.main_company")
+
     def test_empty_many2one_from_odoo(self):
         message = self.converter2.odoo_to_message(self.user_root)
         self.assertEqual(message, "")
 
@@ -43,7 +61,11 @@
     def test_empty_many2one_from_odoo(self):
         message = self.converter2.odoo_to_message(self.user_root)
         self.assertEqual(message, "")
 
+    def test_empty_many2one_skip_from_odoo(self):
+        message = self.converter3.odoo_to_message(self.user_root)
+        self.assertEqual(message, Skip)
+
     def test_many2one_to_odoo(self):
         """Ensure a sub-object linked from the main one gets updated when Odoo
         receives a message.
diff --git a/tests/test_switch.py b/tests/test_switch.py
index 246f074af9f5041eefcd54b1974576598bf98f8f_dGVzdHMvdGVzdF9zd2l0Y2gucHk=..1ec96eae270f2bac3b80666e469d67d24b27874b_dGVzdHMvdGVzdF9zd2l0Y2gucHk= 100644
--- a/tests/test_switch.py
+++ b/tests/test_switch.py
@@ -1,7 +1,7 @@
 ##############################################################################
 #
-#    Converter for Odoo
-#    Copyright (C) 2021, 2022 XCG Consulting <https://xcg-consulting.fr/>
+#    Converter Odoo module
+#    Copyright (C) 2021 XCG Consulting <https://xcg-consulting.fr>
 #
 #    This program is free software: you can redistribute it and/or modify
 #    it under the terms of the GNU Affero General Public License as
@@ -17,6 +17,7 @@
 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 ##############################################################################
+
 from odoo import tests
 
 from .. import Field, Model, Switch
diff --git a/validate.py b/validate.py
index 246f074af9f5041eefcd54b1974576598bf98f8f_dmFsaWRhdGUucHk=..1ec96eae270f2bac3b80666e469d67d24b27874b_dmFsaWRhdGUucHk= 100644
--- a/validate.py
+++ b/validate.py
@@ -1,3 +1,23 @@
+##############################################################################
+#
+#    Converter Odoo module
+#    Copyright (C) 2020 XCG Consulting <https://xcg-consulting.fr>
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
 import json
 import math
 import os
diff --git a/xref.py b/xref.py
index 246f074af9f5041eefcd54b1974576598bf98f8f_eHJlZi5weQ==..1ec96eae270f2bac3b80666e469d67d24b27874b_eHJlZi5weQ== 100644
--- a/xref.py
+++ b/xref.py
@@ -1,3 +1,23 @@
+##############################################################################
+#
+#    Converter Odoo module
+#    Copyright (C) 2020 XCG Consulting <https://xcg-consulting.fr>
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
 from typing import Any, Optional
 
 from odoo import models