diff --git a/.eslintrc.yml b/.eslintrc.yml
index 9429bc688aab4993eae460767368f65089afa323..fed88d70d23ecb3297ea28854b320c4d62ee3c26 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -22,6 +22,7 @@ globals:
   odoo: readonly
   openerp: readonly
   owl: readonly
+  luxon: readonly
 
 # Styling is handled by Prettier, so we only need to enable AST rules;
 # see https://github.com/OCA/maintainer-quality-tools/pull/618#issuecomment-558576890
diff --git a/.flake8 b/.flake8
deleted file mode 100644
index e397e8ed4e3e7f7fe7785dd391bb80aa6d85575e..0000000000000000000000000000000000000000
--- a/.flake8
+++ /dev/null
@@ -1,12 +0,0 @@
-[flake8]
-max-line-length = 88
-max-complexity = 16
-# B = bugbear
-# B9 = bugbear opinionated (incl line length)
-select = C,E,F,W,B,B9
-# E203: whitespace before ':' (black behaviour)
-# E501: flake8 line length (covered by bugbear B950)
-# W503: line break before binary operator (black behaviour)
-ignore = E203,E501,W503
-per-file-ignores=
-    __init__.py:F401
diff --git a/.gitignore b/.gitignore
index 9c283fd41f6cc3330653f1d90a820b85e23caf65..d99361a24aa02c2d19d8165c3a057b84bc575166 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@ __pycache__/
 *.py[cod]
 /.venv
 /.pytest_cache
+/.ruff_cache
 
 # C extensions
 *.so
@@ -23,6 +24,7 @@ var/
 .installed.cfg
 *.egg
 *.eggs
+.copier-answers.yml
 
 # Installer logs
 pip-log.txt
diff --git a/.isort.cfg b/.isort.cfg
deleted file mode 100644
index 0ec187efd1bf802844749f508cda0c8f138970f9..0000000000000000000000000000000000000000
--- a/.isort.cfg
+++ /dev/null
@@ -1,13 +0,0 @@
-[settings]
-; see https://github.com/psf/black
-multi_line_output=3
-include_trailing_comma=True
-force_grid_wrap=0
-combine_as_imports=True
-use_parentheses=True
-line_length=88
-known_odoo=odoo
-known_odoo_addons=odoo.addons
-sections=FUTURE,STDLIB,THIRDPARTY,ODOO,ODOO_ADDONS,FIRSTPARTY,LOCALFOLDER
-default_section=THIRDPARTY
-ensure_newline_before_comments = True
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 10acf1e37e92961168f7539a4a84f689280f3710..3efb4d9157dc36d84e372eec5fc21e7f4a417a58 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -12,6 +12,10 @@ exclude: |
   /static/(src/)?lib/|
   # Repos using Sphinx to generate docs don't need prettying
   ^docs/_templates/.*\.html$|
+  # Don't bother non-technical authors with formatting issues in docs
+  readme/.*\.(rst|md)$|
+  # Ignore build and dist directories in addons
+  /build/|/dist/|
   # You don't usually want a bot to modify your legal texts
   (LICENSE.*|COPYING.*)
 default_language_version:
@@ -33,27 +37,25 @@ repos:
         language: fail
         files: '[a-zA-Z0-9_]*/i18n/en\.po$'
   - repo: https://github.com/oca/maintainer-tools
-    rev: 4cd2b852214dead80822e93e6749b16f2785b2fe
+    rev: f71041f22b8cd68cf7c77b73a14ca8d8cd190a60
     hooks:
       # update the NOT INSTALLABLE ADDONS section above
       - id: oca-update-pre-commit-excluded-addons
       - id: oca-fix-manifest-website
         args: ["https://le-filament.com"]
-  - repo: https://github.com/myint/autoflake
-    rev: v1.6.1
-    hooks:
-      - id: autoflake
+      - id: oca-gen-addon-readme
         args:
-          - --expand-star-imports
-          - --ignore-init-module-imports
-          - --in-place
-          - --remove-all-unused-imports
-          - --remove-duplicate-keys
-          - --remove-unused-variables
-  - repo: https://github.com/psf/black
-    rev: 22.8.0
+          - --addons-dir=.
+          - --branch=16.0
+          - --org-name=lefilament
+          - --repo-name=template_module
+          - --if-source-changed
+          - --keep-source-digest
+  - repo: https://github.com/OCA/odoo-pre-commit-hooks
+    rev: v0.0.25
     hooks:
-      - id: black
+      - id: oca-checks-odoo-module
+      - id: oca-checks-po
   - repo: https://github.com/pre-commit/mirrors-prettier
     rev: v2.7.1
     hooks:
@@ -95,27 +97,14 @@ repos:
       - id: check-xml
       - id: mixed-line-ending
         args: ["--fix=lf"]
-  - repo: https://github.com/asottile/pyupgrade
-    rev: v2.38.2
-    hooks:
-      - id: pyupgrade
-        args: ["--keep-percent-format"]
-  - repo: https://github.com/PyCQA/isort
-    rev: 5.12.0
-    hooks:
-      - id: isort
-        name: isort except __init__.py
-        args:
-          - --settings=.
-        exclude: /__init__\.py$
-  - repo: https://github.com/PyCQA/flake8
-    rev: 3.9.2
+  - repo: https://github.com/astral-sh/ruff-pre-commit
+    rev: v0.1.3
     hooks:
-      - id: flake8
-        name: flake8
-        additional_dependencies: ["flake8-bugbear==21.9.2"]
+      - id: ruff
+        args: [--fix, --exit-non-zero-on-fix]
+      - id: ruff-format
   - repo: https://github.com/OCA/pylint-odoo
-    rev: 7.0.2
+    rev: v8.0.19
     hooks:
       - id: pylint_odoo
         name: pylint with optional checks
diff --git a/.pylintrc-mandatory b/.pylintrc-mandatory
index ed2c2171ffe63ac191e30ed0759c4b6211ad5f15..99064933ef82c469ba5fda5b2904447c05c99dbe 100644
--- a/.pylintrc-mandatory
+++ b/.pylintrc-mandatory
@@ -4,12 +4,12 @@ load-plugins=pylint_odoo
 score=n
 
 [ODOOLINT]
-readme_template_url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst"
-manifest_required_authors=Le Filament
-manifest_required_keys=license
-manifest_deprecated_keys=description,active
-license_allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3
-valid_odoo_versions=16.0
+readme-template-url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst"
+manifest-required-authors=Le Filament
+manifest-required-keys=license
+manifest-deprecated-keys=description,active
+license-allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3
+valid-odoo-versions=16.0
 
 [MESSAGES CONTROL]
 disable=all
diff --git a/.ruff.toml b/.ruff.toml
new file mode 100644
index 0000000000000000000000000000000000000000..0240c75f6a4ae4550f3473ad0a5faaef022bf6c7
--- /dev/null
+++ b/.ruff.toml
@@ -0,0 +1,30 @@
+
+target-version = "py310"
+fix = true
+
+[lint]
+extend-select = [
+    "B",
+    "C90",
+    "E501",  # line too long (default 88)
+    "I",  # isort
+    "UP",  # pyupgrade
+]
+exclude = ["setup/*"]
+
+[format]
+exclude = ["setup/*"]
+
+[per-file-ignores]
+"__init__.py" = ["F401", "I001"]  # ignore unused and unsorted imports in __init__.py
+"__manifest__.py" = ["B018"]  # useless expression
+
+[isort]
+section-order = ["future", "standard-library", "third-party", "odoo", "odoo-addons", "first-party", "local-folder"]
+
+[isort.sections]
+"odoo" = ["odoo"]
+"odoo-addons" = ["odoo.addons"]
+
+[mccabe]
+max-complexity = 16
diff --git a/LICENSE b/LICENSE
index be3f7b28e564e7dd05eaf59d64adba1a4065ac0e..12334e0fdd8d3412d5e1deed72220fcc03431ff2 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,21 +1,205 @@
-                    GNU AFFERO GENERAL PUBLIC LICENSE
-                       Version 3, 19 November 2007
 
- Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+For copyright information, please see the COPYRIGHT file.
+
+Odoo is published under the GNU LESSER GENERAL PUBLIC LICENSE, Version 3
+(LGPLv3), as included below. Since the LGPL is a set of additional
+permissions on top of the GPL, the text of the GPL is included at the
+bottom as well.
+
+Some external libraries and contributions bundled with Odoo may be published
+under other GPL-compatible licenses. For these, please refer to the relevant
+source files and/or license files, in the source code tree.
+
+**************************************************************************
+
+                   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
+
+**************************************************************************
+
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
  Everyone is permitted to copy and distribute verbatim copies
  of this license document, but changing it is not allowed.
 
                             Preamble
 
-  The GNU Affero General Public License is a free, copyleft license for
-software and other kinds of works, specifically designed to ensure
-cooperation with the community in the case of network server software.
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
 
   The licenses for most software and other practical works are designed
 to take away your freedom to share and change the works.  By contrast,
-our General Public Licenses are intended to guarantee your freedom to
+the GNU General Public License is intended to guarantee your freedom to
 share and change all versions of a program--to make sure it remains free
-software for all its users.
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
 
   When we speak of free software, we are referring to freedom, not
 price.  Our General Public Licenses are designed to make sure that you
@@ -24,34 +208,44 @@ them if you wish), that you receive source code or can get it if you
 want it, that you can change the software or use pieces of it in new
 free programs, and that you know you can do these things.
 
-  Developers that use our General Public Licenses protect your rights
-with two steps: (1) assert copyright on the software, and (2) offer
-you this License which gives you legal permission to copy, distribute
-and/or modify the software.
-
-  A secondary benefit of defending all users' freedom is that
-improvements made in alternate versions of the program, if they
-receive widespread use, become available for other developers to
-incorporate.  Many developers of free software are heartened and
-encouraged by the resulting cooperation.  However, in the case of
-software used on network servers, this result may fail to come about.
-The GNU General Public License permits making a modified version and
-letting the public access it on a server without ever releasing its
-source code to the public.
-
-  The GNU Affero General Public License is designed specifically to
-ensure that, in such cases, the modified source code becomes available
-to the community.  It requires the operator of a network server to
-provide the source code of the modified version running there to the
-users of that server.  Therefore, public use of a modified version, on
-a publicly accessible server, gives the public access to the source
-code of the modified version.
-
-  An older license, called the Affero General Public License and
-published by Affero, was designed to accomplish similar goals.  This is
-a different license, not a version of the Affero GPL, but Affero has
-released a new version of the Affero GPL which permits relicensing under
-this license.
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
 
   The precise terms and conditions for copying, distribution and
 modification follow.
@@ -60,7 +254,7 @@ modification follow.
 
   0. Definitions.
 
-  "This License" refers to version 3 of the GNU Affero General Public License.
+  "This License" refers to version 3 of the GNU General Public License.
 
   "Copyright" also means copyright-like laws that apply to other kinds of
 works, such as semiconductor masks.
@@ -537,45 +731,35 @@ to collect a royalty for further conveying from those to whom you convey
 the Program, the only way you could satisfy both those terms and this
 License would be to refrain entirely from conveying the Program.
 
-  13. Remote Network Interaction; Use with the GNU General Public License.
-
-  Notwithstanding any other provision of this License, if you modify the
-Program, your modified version must prominently offer all users
-interacting with it remotely through a computer network (if your version
-supports such interaction) an opportunity to receive the Corresponding
-Source of your version by providing access to the Corresponding Source
-from a network server at no charge, through some standard or customary
-means of facilitating copying of software.  This Corresponding Source
-shall include the Corresponding Source for any work covered by version 3
-of the GNU General Public License that is incorporated pursuant to the
-following paragraph.
+  13. Use with the GNU Affero General Public License.
 
   Notwithstanding any other provision of this License, you have
 permission to link or combine any covered work with a work licensed
-under version 3 of the GNU General Public License into a single
+under version 3 of the GNU Affero General Public License into a single
 combined work, and to convey the resulting work.  The terms of this
 License will continue to apply to the part which is the covered work,
-but the work with which it is combined will remain governed by version
-3 of the GNU General Public License.
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
 
   14. Revised Versions of this License.
 
   The Free Software Foundation may publish revised and/or new versions of
-the GNU Affero General Public License from time to time.  Such new versions
-will be similar in spirit to the present version, but may differ in detail to
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
 address new problems or concerns.
 
   Each version is given a distinguishing version number.  If the
-Program specifies that a certain numbered version of the GNU Affero General
+Program specifies that a certain numbered version of the GNU General
 Public License "or any later version" applies to it, you have the
 option of following the terms and conditions either of that numbered
 version or of any later version published by the Free Software
 Foundation.  If the Program does not specify a version number of the
-GNU Affero General Public License, you may choose any version ever published
+GNU General Public License, you may choose any version ever published
 by the Free Software Foundation.
 
   If the Program specifies that a proxy can decide which future
-versions of the GNU Affero General Public License can be used, that proxy's
+versions of the GNU General Public License can be used, that proxy's
 public statement of acceptance of a version permanently authorizes you
 to choose that version for the Program.
 
@@ -633,29 +817,43 @@ the "copyright" line and a pointer to where the full notice is found.
     Copyright (C) <year>  <name of author>
 
     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
+    it under the terms of the GNU 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.
+    GNU 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 <https://www.gnu.org/licenses/>.
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 Also add information on how to contact you by electronic and paper mail.
 
-  If your software can interact with users remotely through a computer
-network, you should also make sure that it provides a way for users to
-get its source.  For example, if your program is a web application, its
-interface could display a "Source" link that leads users to an archive
-of the code.  There are many ways you could offer source, and different
-solutions will be better for different programs; see section 13 for the
-specific requirements.
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
 
   You should also get your employer (if you work as a programmer) or school,
 if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU AGPL, see
-<https://www.gnu.org/licenses/>.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+
+
+**************************************************************************
\ No newline at end of file
diff --git a/README.rst b/README.rst
index 4ada9c6a0a0e6ff46924fac9eaae99815c1ee513..b9915346082f1e03fd8c6dfc9224556e70ae3963 100644
--- a/README.rst
+++ b/README.rst
@@ -1,6 +1,6 @@
-.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
-   :target: http://www.gnu.org/licenses/agpl.html
-   :alt: License: AGPL-3
+.. image:: https://img.shields.io/badge/licence-LGPL--3-blue.svg
+   :target: http://www.gnu.org/licenses/lgpl.html
+   :alt: License: LGPL-3
 
 
 ==================
diff --git a/__init__.py b/__init__.py
index 0650744f6bc69b9f0b865e8c7174c813a5f5995e..72d3ea60a8cf7cd1d26a4066c05e6817315bce8b 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1 +1 @@
-from . import models
+from . import controllers, models
diff --git a/__manifest__.py b/__manifest__.py
index 3e6dbfe540362b4129d41513ce4ee2327c45c3ce..d9786260b7cb524b3aedead5a116e5da10b7764a 100644
--- a/__manifest__.py
+++ b/__manifest__.py
@@ -1,15 +1,16 @@
 {
     "name": "Solagro - Contacts",
-    "author": "Le Filament",
+    "author": "Le Filament, Odoo S.A.",
     "website": "https://le-filament.com",
     "version": "16.0.1.0.0",
-    "license": "AGPL-3",
+    "license": "LGPL-3",
     "depends": ["contacts", "l10n_fr_siret"],
     "data": [
         "security/ir.model.access.csv",
         "data/res_partner_domain_data.xml",
         "views/event_old_view.xml",
         "views/journalist_topic_view.xml",
+        "views/portal_details_template.xml",
         "views/res_partner_view.xml",
         "views/res_partner_interest_view.xml",
         "views/res_partner_theme_view.xml",
diff --git a/controllers/__init__.py b/controllers/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..8c3feb6f5623af66422dfca59396a03278ec8c59
--- /dev/null
+++ b/controllers/__init__.py
@@ -0,0 +1 @@
+from . import portal
diff --git a/controllers/portal.py b/controllers/portal.py
new file mode 100644
index 0000000000000000000000000000000000000000..c6ec3c876674be9c5abe73a4afed0e69d9b1363f
--- /dev/null
+++ b/controllers/portal.py
@@ -0,0 +1,203 @@
+# Copyright 2024- Le Filame:qnt (https://le-filament.com)
+# Copyright Odoo S.A. (https://odoo.com)
+# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
+
+# Functions in this modules could not be inherited and are therefore copied
+# from Odoo code (https://github.com/odoo/odoo/blob/16.0/addons/portal/controllers/portal.py)
+# and modified to change fields displayed on portal
+
+from odoo import _, tools
+from odoo.exceptions import ValidationError
+from odoo.http import request, route
+
+from odoo.addons.portal.controllers import portal
+
+
+class CustomerPortal(portal.CustomerPortal):
+    # Added new lists with mandatory / optional fields
+    MANDATORY_CONTACT_FIELDS = ["name", "email"]
+    OPTIONAL_CONTACT_FIELDS = [
+        "zipcode",
+        "phone",
+        "street",
+        "city",
+        "country_id",
+        "state_id",
+        "vat",
+        "company_name",
+        "department",
+        "function",
+    ]
+
+    @route(["/my/account"], type="http", auth="user", website=True)
+    def account(self, redirect=None, **post):
+        values = self._prepare_portal_layout_values()
+        partner = request.env.user.partner_id
+        values.update(
+            {
+                "error": {},
+                "error_message": [],
+            }
+        )
+        # Added list of thematics and domains
+        thematics = request.env["res.partner.theme"].sudo().search([])
+        domains = request.env["res.partner.domain"].sudo().search([])
+
+        if post and request.httprequest.method == "POST":
+            error, error_message = self.details_form_validate(post)
+            values.update({"error": error, "error_message": error_message})
+            values.update(post)
+            if not error:
+                # Updated to get mandatory and optional fields from this
+                # module instead of Odoo default
+                values = {key: post[key] for key in self.MANDATORY_CONTACT_FIELDS}
+                values.update(
+                    {
+                        key: post[key]
+                        for key in self.OPTIONAL_CONTACT_FIELDS
+                        if key in post
+                    }
+                )
+                for field in {"country_id", "state_id"} & set(values.keys()):
+                    try:
+                        values[field] = int(values[field])
+                    except Exception:
+                        values[field] = False
+                # Added compute of m2m thematic_ids and domain_ids
+                values.update(
+                    {
+                        "thematic_ids": [
+                            (
+                                6,
+                                0,
+                                [
+                                    int(post.get(f"theme-{theme_id}", ""))
+                                    for theme_id in thematics.ids
+                                    if f"theme-{theme_id}" in post
+                                ],
+                            )
+                        ]
+                    }
+                )
+                values.update(
+                    {
+                        "domain_ids": [
+                            (
+                                6,
+                                0,
+                                [
+                                    int(post.get(f"domain-{domain_id}", ""))
+                                    for domain_id in domains.ids
+                                    if f"domain-{domain_id}" in post
+                                ],
+                            )
+                        ]
+                    }
+                )
+                values.update({"zip": values.pop("zipcode", "")})
+                self.on_account_update(values, partner)
+                partner.sudo().write(values)
+                if redirect:
+                    return request.redirect(redirect)
+                return request.redirect("/my/home")
+
+        countries = request.env["res.country"].sudo().search([])
+        states = request.env["res.country.state"].sudo().search([])
+
+        values.update(
+            {
+                "partner": partner,
+                "countries": countries,
+                "states": states,
+                # Added list of thematics and themes
+                "thematics": thematics,
+                "domains": domains,
+                "has_check_vat": hasattr(request.env["res.partner"], "check_vat"),
+                "partner_can_edit_vat": partner.can_edit_vat(),
+                "redirect": redirect,
+                "page_name": "my_details",
+            }
+        )
+
+        response = request.render("portal.portal_my_details", values)
+        response.headers["X-Frame-Options"] = "SAMEORIGIN"
+        response.headers["Content-Security-Policy"] = "frame-ancestors 'self'"
+        return response
+
+    def details_form_validate(self, data, partner_creation=False):
+        error = dict()
+        error_message = []
+
+        # Validation
+        # Updated to use this module mandatory fields variable iso Odoo default
+        for field_name in self.MANDATORY_CONTACT_FIELDS:
+            if not data.get(field_name):
+                error[field_name] = "missing"
+
+        # email validation
+        if data.get("email") and not tools.single_email_re.match(data.get("email")):
+            error["email"] = "error"
+            error_message.append(
+                _("Invalid Email! Please enter a valid email address.")
+            )
+
+        # vat validation
+        partner = request.env.user.partner_id
+        if data.get("vat") and partner and partner.vat != data.get("vat"):
+            # Check the VAT if it is the public user too.
+            if partner_creation or partner.can_edit_vat():
+                if hasattr(partner, "check_vat"):
+                    if data.get("country_id"):
+                        data["vat"] = request.env["res.partner"].fix_eu_vat_number(
+                            int(data.get("country_id")), data.get("vat")
+                        )
+                    partner_dummy = partner.new(
+                        {
+                            "vat": data["vat"],
+                            "country_id": (
+                                int(data["country_id"])
+                                if data.get("country_id")
+                                else False
+                            ),
+                        }
+                    )
+                    try:
+                        partner_dummy.check_vat()
+                    except ValidationError as e:
+                        error["vat"] = "error"
+                        error_message.append(e.args[0])
+            else:
+                error_message.append(
+                    _(
+                        "Changing VAT number is not allowed once document(s) have been "
+                        "issued for your account. "
+                        "Please contact us directly for this operation."
+                    )
+                )
+
+        # error message for empty required fields
+        if [err for err in error.values() if err == "missing"]:
+            error_message.append(_("Some required fields are empty."))
+
+        # Compute list of thematics and domains expected in values
+        # (so that they are not considered unknown fields)
+        thematics = request.env["res.partner.theme"].sudo().search([])
+        domains = request.env["res.partner.domain"].sudo().search([])
+
+        theme_ids = [f"theme-{theme_id}" for theme_id in thematics.ids]
+        domain_ids = [f"domain-{domain_id}" for domain_id in domains.ids]
+
+        unknown = [
+            k
+            for k in data
+            if k
+            not in self.MANDATORY_CONTACT_FIELDS
+            + self.OPTIONAL_CONTACT_FIELDS
+            + theme_ids
+            + domain_ids
+        ]
+        if unknown:
+            error["common"] = "Unknown field"
+            error_message.append("Unknown field '%s'" % ",".join(unknown))
+
+        return error, error_message
diff --git a/models/__init__.py b/models/__init__.py
index 53e841893296374947a6ae35ff3cc95e7a2ac665..50ae1ff9c05606b43349996b203a22f4d57df5e5 100644
--- a/models/__init__.py
+++ b/models/__init__.py
@@ -1,6 +1,3 @@
-# Copyright 2023 Le Filament (https://le-filament.com)
-# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
-
 from . import event_old
 from . import journalist_topic
 from . import main_date
diff --git a/models/event_old.py b/models/event_old.py
index 09d7d15367b900a4371f30cf87bcd4c91331ee76..1ba4d84a26c582ba8731e77b5a3b208e9dd09c83 100644
--- a/models/event_old.py
+++ b/models/event_old.py
@@ -1,5 +1,5 @@
 # Copyright 2023 Le Filament (https://le-filament.com)
-# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
+# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
 
 from odoo import fields, models
 
diff --git a/models/journalist_topic.py b/models/journalist_topic.py
index 5a4a2a401fe917fb5abb3c642d18702ab9ce9533..2d56198b48e2aee1cb6c9b813df31e9e80949457 100644
--- a/models/journalist_topic.py
+++ b/models/journalist_topic.py
@@ -1,5 +1,5 @@
 # Copyright 2023 Le Filament (https://le-filament.com)
-# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
+# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
 
 from odoo import fields, models
 
diff --git a/models/main_date.py b/models/main_date.py
index ec5f15f3abffcf0462827ea4a741ef5036b4e1f8..8fa4dbb984310fceeb314407e98c6de23a0cf0f7 100644
--- a/models/main_date.py
+++ b/models/main_date.py
@@ -1,5 +1,5 @@
 # Copyright 2023 Le Filament (https://le-filament.com)
-# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
+# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
 
 from odoo import fields, models
 
diff --git a/models/res_partner.py b/models/res_partner.py
index 6d0f82ed704c780272ac830e1d56bce75411752b..24b442df8b8316c42ab31f1ba6916b8d97d4f666 100644
--- a/models/res_partner.py
+++ b/models/res_partner.py
@@ -1,5 +1,5 @@
 # Copyright 2023 Le Filament (https://le-filament.com)
-# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
+# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
 
 from odoo import fields, models
 
diff --git a/models/res_partner_domain.py b/models/res_partner_domain.py
index 6eccf095930847c5c1b8638ba5aaeafd8c516579..dc27d153b182b98847a2767564ebe99e1501b653 100644
--- a/models/res_partner_domain.py
+++ b/models/res_partner_domain.py
@@ -1,5 +1,5 @@
 # Copyright 2023 Le Filament (https://le-filament.com)
-# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
+# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
 
 from odoo import fields, models
 
diff --git a/models/res_partner_interest.py b/models/res_partner_interest.py
index b044584122f847ced1dfa98acc9ef67de27a2765..f18e98cbbebf6f30eb2e48a0f7b9ec3ec6d374af 100644
--- a/models/res_partner_interest.py
+++ b/models/res_partner_interest.py
@@ -1,5 +1,5 @@
 # Copyright 2023 Le Filament (https://le-filament.com)
-# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
+# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
 
 from random import randint
 
diff --git a/models/res_partner_theme.py b/models/res_partner_theme.py
index dee70c8ec32acdd408a2837ba67466bfc0719fdb..fd9be0d784d292437a1ff680c46177b9da7f40ed 100644
--- a/models/res_partner_theme.py
+++ b/models/res_partner_theme.py
@@ -1,5 +1,5 @@
 # Copyright 2023 Le Filament (https://le-filament.com)
-# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
+# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
 
 from random import randint
 
diff --git a/views/event_old_view.xml b/views/event_old_view.xml
index c2a235a8d98ffff6094d3c927b8c601a878b71c6..5208c1d1ed28ec60c3f4e95e812aadc0f70e7c39 100644
--- a/views/event_old_view.xml
+++ b/views/event_old_view.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8" ?>
 <!-- Copyright 2023 Le Filament (https://le-filament.com)
-     License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
+     License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). -->
 <odoo>
     <!-- Search View -->
     <record id="event_old_search_view" model="ir.ui.view">
diff --git a/views/journalist_topic_view.xml b/views/journalist_topic_view.xml
index 86cd2f8bb9d355c85dbe614141f9bdcfb63a5671..9374d9b48083de32bd8cb5b798b64901ce126486 100644
--- a/views/journalist_topic_view.xml
+++ b/views/journalist_topic_view.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8" ?>
 <!-- Copyright 2023 Le Filament (https://le-filament.com)
-     License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
+     License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). -->
 <odoo>
     <!-- Tree View -->
     <record id="journalist_topic_tree_view" model="ir.ui.view">
diff --git a/views/portal_details_template.xml b/views/portal_details_template.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9c587a1a445597fb1d977abc85313f1bbd1b0919
--- /dev/null
+++ b/views/portal_details_template.xml
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<odoo>
+<!-- Copyright 2023 Le Filament (https://le-filament.com)
+     License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). -->
+
+    <template
+        id="portal_my_details_fields_solagro"
+        inherit_id="portal.portal_my_details_fields"
+        name="portal_my_details_fields"
+    >
+        <xpath expr="//label[@for='phone']" position="attributes">
+            <attribute name="class">col-form-label label-optional</attribute>
+        </xpath>
+        <xpath expr="//label[@for='street']" position="attributes">
+            <attribute name="class">col-form-label label-optional</attribute>
+        </xpath>
+        <xpath expr="//label[@for='city']" position="attributes">
+            <attribute name="class">col-form-label label-optional</attribute>
+        </xpath>
+        <xpath expr="//label[@for='country_id']" position="attributes">
+            <attribute name="class">col-form-label label-optional</attribute>
+        </xpath>
+        <xpath expr="//label[@for='state_id']/.." position="after">
+            <div class="clearfix" />
+            <div
+                t-attf-class="mb-3 #{error.get('department') and 'o_has_error' or ''} col-xl-6"
+            >
+                <label
+                    class="col-form-label label-optional"
+                    for="department"
+                >Service</label>
+                <input
+                    type="text"
+                    name="department"
+                    t-attf-class="form-control #{error.get('city') and 'is-invalid' or ''}"
+                    t-att-value="department or partner.department"
+                />
+            </div>
+            <div
+                t-attf-class="mb-3 #{error.get('function') and 'o_has_error' or ''} col-xl-6"
+            >
+                <label class="col-form-label label-optional" for="zipcode">Poste</label>
+                <input
+                    type="text"
+                    name="function"
+                    t-attf-class="form-control #{error.get('function') and 'is-invalid' or ''}"
+                    t-att-value="function or partner.function"
+                />
+            </div>
+            <div
+                t-attf-class="mb-3 #{error.get('thematic_ids') and 'o_has_error' or ''} col-xl-6"
+            >
+                <label
+                    class="col-form-label label-optional"
+                    for="thematic_ids"
+                >Champs thématiques</label>
+                <t t-foreach="thematics" t-as="answer">
+                    <div class="form-check">
+                        <input
+                            type="checkbox"
+                            class="form-check-input"
+                            t-att-value="answer.id"
+                            t-attf-id="theme-#{answer.id}"
+                            t-attf-name="theme-#{answer.id}"
+                            t-att-checked="'checked' if answer in partner.thematic_ids else False"
+                        />
+                        <label
+                            t-attf-for="theme-#{answer.id}"
+                            class="form-check-label fw-normal"
+                        ><span t-out="answer.name" />
+                        </label>
+                    </div>
+                </t>
+            </div>
+            <div
+                t-attf-class="mb-3 #{error.get('domain_ids') and 'o_has_error' or ''} col-xl-6"
+            >
+                <label
+                    class="col-form-label label-optional"
+                    for="domain_ids"
+                >Domaines</label>
+                <t t-foreach="domains" t-as="answer">
+                    <div class="form-check">
+                        <input
+                            type="checkbox"
+                            class="form-check-input"
+                            t-att-value="answer.id"
+                            t-attf-id="domain-#{answer.id}"
+                            t-attf-name="domain-#{answer.id}"
+                            t-att-checked="'checked' if answer in partner.domain_ids else False"
+                        />
+
+                        <label
+                            t-attf-for="domain-#{answer.id}"
+                            class="form-check-label fw-normal"
+                        ><span t-out="answer.name" />
+                        </label>
+                    </div>
+                </t>
+            </div>
+        </xpath>
+    </template>
+
+    <template
+        id="portal_my_home_solagro"
+        inherit_id="portal.portal_my_home"
+        name="My Portal Solagro"
+    >
+        <xpath expr="//h3" position="replace">
+            <h3>Mon portail utilisateur Solagro</h3>
+        </xpath>
+        <xpath expr="//div[hasclass('o_portal_docs')]" position="attributes">
+            <attribute name="class">o_portal_docs list-group d-none</attribute>
+        </xpath>
+    </template>
+</odoo>
diff --git a/views/res_partner_domain_view.xml b/views/res_partner_domain_view.xml
index 5f2f86a1448b27869e0112f841aeb656ed88516d..2292b8be2b6d66caeebad1dc4cf1fc19f6d8c1af 100644
--- a/views/res_partner_domain_view.xml
+++ b/views/res_partner_domain_view.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8" ?>
 <!-- Copyright 2023 Le Filament (https://le-filament.com)
-     License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
+     License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). -->
 <odoo>
     <!-- Tree View -->
     <record id="res_partner_domain_tree_view" model="ir.ui.view">
diff --git a/views/res_partner_interest_view.xml b/views/res_partner_interest_view.xml
index 2527452dad383bb6590b4943d9ff403cdc9a7499..6bffc70238f3bc56371e69456f496eb1245f659c 100644
--- a/views/res_partner_interest_view.xml
+++ b/views/res_partner_interest_view.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8" ?>
 <!-- Copyright 2023 Le Filament (https://le-filament.com)
-     License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
+     License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). -->
 <odoo>
     <!-- Tree View -->
     <record id="res_partner_interest_tree_view" model="ir.ui.view">
diff --git a/views/res_partner_theme_view.xml b/views/res_partner_theme_view.xml
index d6e1782aed017b170929279202ca6512853b820b..0e7ba1ecd9fa872d4d497b3f2f1eced016d2d80d 100644
--- a/views/res_partner_theme_view.xml
+++ b/views/res_partner_theme_view.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8" ?>
 <!-- Copyright 2023 Le Filament (https://le-filament.com)
-     License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
+     License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). -->
 <odoo>
     <!-- Tree View -->
     <record id="thematic_tree_view" model="ir.ui.view">
diff --git a/views/res_partner_view.xml b/views/res_partner_view.xml
index 16efa446e8dc5bbe29ebf6c63f43d819cc54cda4..2e0b8b8f1af97bf9e8b5edc87515ca910d56152c 100644
--- a/views/res_partner_view.xml
+++ b/views/res_partner_view.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8" ?>
 <!-- Copyright 2023 Le Filament (https://le-filament.com)
-     License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
+     License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). -->
 <odoo>
     <record model="ir.ui.view" id="view_partner_form">
         <field name="name">res.partner.form</field>