From 16e63783882fd617ce60f9b3c85b2891898aeaf2 Mon Sep 17 00:00:00 2001
From: bobrador <bobrador@apsl.net>
Date: Thu, 7 Nov 2024 15:18:37 +0100
Subject: [PATCH] [ADD] account_analytic_report: New module
 account_analytic_report

---
 account_analytic_report/README.rst            | 129 +++
 account_analytic_report/__init__.py           |   2 +
 account_analytic_report/__manifest__.py       |  30 +
 account_analytic_report/i18n/es.po            | 410 +++++++++
 account_analytic_report/menuitems.xml         |  15 +
 account_analytic_report/pyproject.toml        |   3 +
 .../readme/CONTRIBUTORS.md                    |   3 +
 account_analytic_report/readme/DESCRIPTION.md |   1 +
 account_analytic_report/readme/USAGE.md       |  25 +
 account_analytic_report/report/__init__.py    |   2 +
 .../templates/trial_balance_analytic.xml      | 492 ++++++++++
 .../report/trial_balance_analytic.py          | 837 ++++++++++++++++++
 .../report/trial_balance_analytic_xlsx.py     | 643 ++++++++++++++
 account_analytic_report/reports.xml           |  47 +
 .../security/ir.model.access.csv              |   2 +
 .../static/description/icon.png               | Bin 0 -> 36663 bytes
 .../static/description/index.html             | 483 ++++++++++
 account_analytic_report/tests/__init__.py     |   1 +
 .../tests/test_trial_analytic_balance.py      | 374 ++++++++
 .../views/account_analytic_line.xml           |  14 +
 .../views/report_trial_balance_analytic.xml   |  11 +
 account_analytic_report/wizard/__init__.py    |   1 +
 .../trial_balance_analytic_wizard_view.py     | 170 ++++
 .../trial_balance_analytic_wizard_view.xml    |  94 ++
 24 files changed, 3789 insertions(+)
 create mode 100644 account_analytic_report/README.rst
 create mode 100644 account_analytic_report/__init__.py
 create mode 100644 account_analytic_report/__manifest__.py
 create mode 100644 account_analytic_report/i18n/es.po
 create mode 100644 account_analytic_report/menuitems.xml
 create mode 100644 account_analytic_report/pyproject.toml
 create mode 100644 account_analytic_report/readme/CONTRIBUTORS.md
 create mode 100644 account_analytic_report/readme/DESCRIPTION.md
 create mode 100644 account_analytic_report/readme/USAGE.md
 create mode 100644 account_analytic_report/report/__init__.py
 create mode 100644 account_analytic_report/report/templates/trial_balance_analytic.xml
 create mode 100644 account_analytic_report/report/trial_balance_analytic.py
 create mode 100644 account_analytic_report/report/trial_balance_analytic_xlsx.py
 create mode 100644 account_analytic_report/reports.xml
 create mode 100644 account_analytic_report/security/ir.model.access.csv
 create mode 100644 account_analytic_report/static/description/icon.png
 create mode 100644 account_analytic_report/static/description/index.html
 create mode 100644 account_analytic_report/tests/__init__.py
 create mode 100644 account_analytic_report/tests/test_trial_analytic_balance.py
 create mode 100644 account_analytic_report/views/account_analytic_line.xml
 create mode 100644 account_analytic_report/views/report_trial_balance_analytic.xml
 create mode 100644 account_analytic_report/wizard/__init__.py
 create mode 100644 account_analytic_report/wizard/trial_balance_analytic_wizard_view.py
 create mode 100644 account_analytic_report/wizard/trial_balance_analytic_wizard_view.xml

diff --git a/account_analytic_report/README.rst b/account_analytic_report/README.rst
new file mode 100644
index 00000000000..23508571912
--- /dev/null
+++ b/account_analytic_report/README.rst
@@ -0,0 +1,129 @@
+========================
+Account Analytic Reports
+========================
+
+.. 
+   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+   !! This file is generated by oca-gen-addon-readme !!
+   !! changes will be overwritten.                   !!
+   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+   !! source digest: sha256:e3b2f8d263dd282038c6d240451ddf65612a4d8dfbf754af136900aa97285230
+   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
+    :target: https://odoo-community.org/page/development-status
+    :alt: Beta
+.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
+    :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
+    :alt: License: AGPL-3
+.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Faccount--financial--reporting-lightgray.png?logo=github
+    :target: https://github.com/OCA/account-financial-reporting/tree/17.0/account_analytic_report
+    :alt: OCA/account-financial-reporting
+.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
+    :target: https://translation.odoo-community.org/projects/account-financial-reporting-17-0/account-financial-reporting-17-0-account_analytic_report
+    :alt: Translate me on Weblate
+.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
+    :target: https://runboat.odoo-community.org/builds?repo=OCA/account-financial-reporting&target_branch=17.0
+    :alt: Try me on Runboat
+
+|badge1| |badge2| |badge3| |badge4| |badge5|
+
+This module introduces an analytic report that provides an intuitive way
+to view and analyze analytic balances. It simplifies the process,
+offering enhanced insights and making it easier to leverage this
+information effectively.
+
+**Table of contents**
+
+.. contents::
+   :local:
+
+Usage
+=====
+
+Using this module is straightforward. Follow these steps:
+
+-  | **Navigate to the Report**:
+   | Go to **Invoicing** -> **Reporting** -> **Analytic Trial Balance**.
+
+-  | **Customize the Report with Filters**:
+   | Adjust the report using the available options:
+
+   -  | **Group by Analytic Account**:
+      | Groups the results by analytic accounts instead of financial
+        accounts.
+
+   -  | **Show Hierarchy and Limit Hierarchy Level**:
+      | Displays the amounts split by the hierarchy levels of financial
+        accounts.
+
+   -  | **Filter Accounts**:
+      | When used independently (without grouping by analytic accounts
+        or showing hierarchy), the results will be split by both
+        financial accounts.
+      | **Example**: Filtering by accounts *Test 1* and *Test 2*:
+
+      .. code:: text
+
+                 | Initial Balance | Test 1   | Test 2   | Ending Balance
+         400000  |        0        | $3600    | $2400    |     $6000
+
+   -  | **Show Months** (Excel export only):
+      | Enabled when filtering accounts without grouping by analytic
+        accounts or showing hierarchy. It generates a separate sheet in
+        the Excel file for each filtered account, detailing the amounts
+        by month within the selected date range.
+
+Bug Tracker
+===========
+
+Bugs are tracked on `GitHub Issues <https://github.com/OCA/account-financial-reporting/issues>`_.
+In case of trouble, please check there if your issue has already been reported.
+If you spotted it first, help us to smash it by providing a detailed and welcomed
+`feedback <https://github.com/OCA/account-financial-reporting/issues/new?body=module:%20account_analytic_report%0Aversion:%2017.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
+
+Do not contact contributors directly about support or help with technical issues.
+
+Credits
+=======
+
+Authors
+-------
+
+* APSL-Nagarro
+
+Contributors
+------------
+
+-  `APSL-Nagarro <https://apsl.tech>`__:
+
+   -  Bernat Obrador
+   -  Miquel Alzanillas
+
+Maintainers
+-----------
+
+This module is maintained by the OCA.
+
+.. image:: https://odoo-community.org/logo.png
+   :alt: Odoo Community Association
+   :target: https://odoo-community.org
+
+OCA, or the Odoo Community Association, is a nonprofit organization whose
+mission is to support the collaborative development of Odoo features and
+promote its widespread use.
+
+.. |maintainer-BernatObrador| image:: https://github.com/BernatObrador.png?size=40px
+    :target: https://github.com/BernatObrador
+    :alt: BernatObrador
+.. |maintainer-miquelalzanillas| image:: https://github.com/miquelalzanillas.png?size=40px
+    :target: https://github.com/miquelalzanillas
+    :alt: miquelalzanillas
+
+Current `maintainers <https://odoo-community.org/page/maintainer-role>`__:
+
+|maintainer-BernatObrador| |maintainer-miquelalzanillas| 
+
+This module is part of the `OCA/account-financial-reporting <https://github.com/OCA/account-financial-reporting/tree/17.0/account_analytic_report>`_ project on GitHub.
+
+You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/account_analytic_report/__init__.py b/account_analytic_report/__init__.py
new file mode 100644
index 00000000000..c4e388b714a
--- /dev/null
+++ b/account_analytic_report/__init__.py
@@ -0,0 +1,2 @@
+from . import report
+from . import wizard
diff --git a/account_analytic_report/__manifest__.py b/account_analytic_report/__manifest__.py
new file mode 100644
index 00000000000..a0b96320fad
--- /dev/null
+++ b/account_analytic_report/__manifest__.py
@@ -0,0 +1,30 @@
+# Copyright 2024 (APSL - Nagarro) Bernat Obrador
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+{
+    "name": "Account Analytic Reports",
+    "version": "17.0.1.0.0",
+    "summary": "OCA Analytic Reports",
+    "author": "APSL-Nagarro, Odoo Community Association (OCA)",
+    "website": "https://github.com/OCA/account-financial-reporting",
+    "category": "Account",
+    "depends": ["analytic", "account_financial_report"],
+    "maintainers": ["BernatObrador", "miquelalzanillas"],
+    "data": [
+        "security/ir.model.access.csv",
+        "wizard/trial_balance_analytic_wizard_view.xml",
+        "menuitems.xml",
+        "reports.xml",
+        "report/templates/trial_balance_analytic.xml",
+        "views/report_trial_balance_analytic.xml",
+        "views/account_analytic_line.xml",
+    ],
+    "assets": {
+        "web.assets_backend": [
+            "account_analytic_report/static/src/js/*",
+        ],
+    },
+    "application": False,
+    "installable": True,
+    "auto_install": False,
+    "license": "AGPL-3",
+}
diff --git a/account_analytic_report/i18n/es.po b/account_analytic_report/i18n/es.po
new file mode 100644
index 00000000000..e46cb22cdb4
--- /dev/null
+++ b/account_analytic_report/i18n/es.po
@@ -0,0 +1,410 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# 	* account_analytic_report
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 17.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2024-11-14 07:15+0000\n"
+"PO-Revision-Date: 2024-11-14 07:15+0000\n"
+"Last-Translator: \n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: account_analytic_report
+#: model:ir.model.fields,help:account_analytic_report.field_ac_trial_balance_report_wizard__show_months
+msgid ""
+"\n"
+"    This option works only when exporting to Excel. It will create a separate sheet\n"
+"    for each selected analytic account, displaying all financial accounts with a\n"
+"    balance.\n"
+"    For each account, it shows the monthly balance within the selected date range.\n"
+"    "
+msgstr ""
+"Esta opción funciona solo al exportar a Excel. Creará una hoja separada para cada cuenta analítica seleccionada, mostrando todas las cuentas financieras con un saldo.\n"
+"Para cada cuenta, muestra el saldo mensual dentro del rango de fechas seleccionado."
+
+#. module: account_analytic_report
+#. odoo-python
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.report_trial_balance_lines_header
+#, python-format
+msgid "Account"
+msgstr "Cuenta"
+
+#. module: account_analytic_report
+#. odoo-python
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.report_trial_balance_filters
+#, python-format
+msgid "Account at 0 filter"
+msgstr "Filtro de cuentas a 0"
+
+#. module: account_analytic_report
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.report_trial_balance_filters
+msgid "All accounts"
+msgstr "Todas las cuentas"
+
+#. module: account_analytic_report
+#. odoo-python
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#: model:ir.actions.act_window,name:account_analytic_report.action_analytic_trial_balance_wizard
+#: model:ir.ui.menu,name:account_analytic_report.menu_analytic_trial_balance
+#, python-format
+msgid "Analytic Trial Balance"
+msgstr "Balance Analítico"
+
+#. module: account_analytic_report
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.report_trial_balance_base
+msgid "Analytic Trial Balance -"
+msgstr "Balance Analítico -"
+
+#. module: account_analytic_report
+#: model:ir.model,name:account_analytic_report.model_ac_trial_balance_report_wizard
+msgid "Analytic Trial Balance Report Wizard"
+msgstr "Asistente de balance analítico"
+
+#. module: account_analytic_report
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.analytic_trial_balance_wizard
+msgid "Cancel"
+msgstr "Cancelar"
+
+#. module: account_analytic_report
+#. odoo-python
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.report_trial_balance_lines_header
+#, python-format
+msgid "Code"
+msgstr "Código"
+
+#. module: account_analytic_report
+#: model:ir.model.fields,field_description:account_analytic_report.field_ac_trial_balance_report_wizard__company_id
+msgid "Company"
+msgstr "Compañía"
+
+#. module: account_analytic_report
+#: model:ir.model.fields,field_description:account_analytic_report.field_ac_trial_balance_report_wizard__create_uid
+msgid "Created by"
+msgstr ""
+
+#. module: account_analytic_report
+#: model:ir.model.fields,field_description:account_analytic_report.field_ac_trial_balance_report_wizard__create_date
+msgid "Created on"
+msgstr ""
+
+#. module: account_analytic_report
+#: model:ir.model.fields,field_description:account_analytic_report.field_ac_trial_balance_report_wizard__date_from
+msgid "Date From"
+msgstr "Desde"
+
+#. module: account_analytic_report
+#: model:ir.model.fields,field_description:account_analytic_report.field_ac_trial_balance_report_wizard__date_to
+msgid "Date To"
+msgstr "Hasta"
+
+#. module: account_analytic_report
+#: model:ir.model.fields,field_description:account_analytic_report.field_ac_trial_balance_report_wizard__date_range_id
+msgid "Date range"
+msgstr "Periodo"
+
+#. module: account_analytic_report
+#. odoo-python
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.report_trial_balance_filters
+#, python-format
+msgid "Date range filter"
+msgstr "Filtro de fechas"
+
+#. module: account_analytic_report
+#: model:ir.model.fields,field_description:account_analytic_report.field_ac_trial_balance_report_wizard__display_name
+msgid "Display Name"
+msgstr ""
+
+#. module: account_analytic_report
+#. odoo-python
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.report_trial_balance_lines_header
+#, python-format
+msgid "Ending balance"
+msgstr "Saldo Final"
+
+#. module: account_analytic_report
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.analytic_trial_balance_wizard
+msgid "Export PDF"
+msgstr "Exportar PDF"
+
+#. module: account_analytic_report
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.analytic_trial_balance_wizard
+msgid "Export XLSX"
+msgstr "Exportar XLSX"
+
+#. module: account_analytic_report
+#: model:ir.model.fields,field_description:account_analytic_report.field_ac_trial_balance_report_wizard__account_ids
+msgid "Filter accounts"
+msgstr "Filtrar cuentas"
+
+#. module: account_analytic_report
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.report_trial_balance_filters
+msgid "From:"
+msgstr "Desde:"
+
+#. module: account_analytic_report
+#. odoo-python
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#, python-format
+msgid "From: %(date_from)s To: %(date_to)s"
+msgstr ""
+
+#. module: account_analytic_report
+#: model:ir.model.fields,field_description:account_analytic_report.field_ac_trial_balance_report_wizard__fy_start_date
+msgid "Fy Start Date"
+msgstr "Fecha inicio ejercicio fiscal"
+
+#. module: account_analytic_report
+#: model:ir.model.fields,field_description:account_analytic_report.field_ac_trial_balance_report_wizard__group_by_analytic_account
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.report_trial_balance_filters
+msgid "Group by Analytic Account"
+msgstr "Agrupar por cuenta analítica"
+
+#. module: account_analytic_report
+#. odoo-python
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#, python-format
+msgid "Grouped by analytic account"
+msgstr ""
+
+#. module: account_analytic_report
+#. odoo-python
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.report_trial_balance_filters
+#, python-format
+msgid "Hide"
+msgstr "Ocultar"
+
+#. module: account_analytic_report
+#: model:ir.model.fields,field_description:account_analytic_report.field_ac_trial_balance_report_wizard__hide_account_at_0
+msgid "Hide accounts at 0"
+msgstr "Ocultar cuentas a 0"
+
+#. module: account_analytic_report
+#: model:ir.model.fields,field_description:account_analytic_report.field_ac_trial_balance_report_wizard__hierarchy_level
+msgid "Hierarchy Level"
+msgstr "Nivel de Jerarquía\n"
+
+#. module: account_analytic_report
+#: model:ir.model.fields,help:account_analytic_report.field_ac_trial_balance_report_wizard__hierarchy_level
+msgid "Hierarchy levels to show"
+msgstr "Nivel de Jerarquía a mostrar\n"
+
+#. module: account_analytic_report
+#: model:ir.model.fields,field_description:account_analytic_report.field_ac_trial_balance_report_wizard__id
+msgid "ID"
+msgstr ""
+
+#. module: account_analytic_report
+#. odoo-python
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.report_trial_balance_lines_header
+#, python-format
+msgid "Initial balance"
+msgstr "Saldo Inicial"
+
+#. module: account_analytic_report
+#: model:ir.model.fields,field_description:account_analytic_report.field_ac_trial_balance_report_wizard__write_uid
+msgid "Last Updated by"
+msgstr ""
+
+#. module: account_analytic_report
+#: model:ir.model.fields,field_description:account_analytic_report.field_ac_trial_balance_report_wizard__write_date
+msgid "Last Updated on"
+msgstr ""
+
+#. module: account_analytic_report
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.report_trial_balance_filters
+msgid "Level"
+msgstr "Nivel"
+
+#. module: account_analytic_report
+#. odoo-python
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#, python-format
+msgid "Level %s"
+msgstr ""
+
+#. module: account_analytic_report
+#: model:ir.model.fields,field_description:account_analytic_report.field_ac_trial_balance_report_wizard__limit_hierarchy_level
+msgid "Limit Hierarchy Level"
+msgstr "Limitar niveles de jerarquía"
+
+#. module: account_analytic_report
+#. odoo-python
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.report_trial_balance_filters
+#, python-format
+msgid "Limit hierarchy levels"
+msgstr "Limitar niveles de jerarquía"
+
+#. module: account_analytic_report
+#: model:ir.model.fields,help:account_analytic_report.field_ac_trial_balance_report_wizard__limit_hierarchy_level
+msgid "Limits hierarchy level"
+msgstr "Límites de niveles de jerarquía"
+
+#. module: account_analytic_report
+#. odoo-python
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.report_trial_balance_filters
+#, python-format
+msgid "No"
+msgstr ""
+
+#. module: account_analytic_report
+#. odoo-python
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.report_trial_balance_filters
+#, python-format
+msgid "No limit"
+msgstr "Sin Nivel"
+
+#. module: account_analytic_report
+#: model:ir.ui.menu,name:account_analytic_report.menu_oca_analytic_reports
+msgid "OCA Analytic reports"
+msgstr "Reportes Analíticos OCA"
+
+#. module: account_analytic_report
+#. odoo-python
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.report_trial_balance_lines_header
+#, python-format
+msgid "Period balance"
+msgstr "Saldo Periodo"
+
+#. module: account_analytic_report
+#: model:ir.model.fields,field_description:account_analytic_report.field_ac_trial_balance_report_wizard__plan_id
+msgid "Plan"
+msgstr "Plan Contable"
+
+#. module: account_analytic_report
+#. odoo-python
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#, python-format
+msgid "Selected Plan"
+msgstr ""
+
+#. module: account_analytic_report
+#. odoo-python
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.report_trial_balance_filters
+#, python-format
+msgid "Show"
+msgstr "Mostrar"
+
+#. module: account_analytic_report
+#: model:ir.model.fields,field_description:account_analytic_report.field_ac_trial_balance_report_wizard__show_hierarchy
+msgid "Show Hierarchy"
+msgstr "Mostrar jerarquía"
+
+#. module: account_analytic_report
+#: model:ir.model.fields,field_description:account_analytic_report.field_ac_trial_balance_report_wizard__show_months
+msgid "Show Months"
+msgstr "Mostrar meses"
+
+#. module: account_analytic_report
+#: model:ir.model.fields,help:account_analytic_report.field_ac_trial_balance_report_wizard__show_hierarchy
+msgid "Shows hierarchy of the financial accounts"
+msgstr "Mostrar jerarquía de cuentas financieras"
+
+#. module: account_analytic_report
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.report_trial_balance_filters
+msgid "Target Plan"
+msgstr "Plan analítico"
+
+#. module: account_analytic_report
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.report_trial_balance_filters
+msgid "Target accounts filter"
+msgstr "Filtro de cuentas"
+
+#. module: account_analytic_report
+#. odoo-python
+#: code:addons/account_analytic_report/wizard/trial_balance_analytic_wizard_view.py:0
+#, python-format
+msgid ""
+"The Company in the Trial Balance Report Wizard and in Date Range must be the"
+" same."
+msgstr ""
+
+#. module: account_analytic_report
+#. odoo-python
+#: code:addons/account_analytic_report/wizard/trial_balance_analytic_wizard_view.py:0
+#, python-format
+msgid "The hierarchy level to filter on must be greater than 0."
+msgstr ""
+
+#. module: account_analytic_report
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.report_trial_balance_filters
+msgid "To"
+msgstr "Hasta:"
+
+#. module: account_analytic_report
+#. odoo-python
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#, python-format
+msgid "Total"
+msgstr ""
+
+#. module: account_analytic_report
+#: model:ir.actions.report,name:account_analytic_report.action_report_analytic_trial_balance_html
+#: model:ir.actions.report,name:account_analytic_report.action_report_analytic_trial_balance_qweb
+msgid "Trial Analytic Balance"
+msgstr "Balance Analítico"
+
+#. module: account_analytic_report
+#: model:ir.model,name:account_analytic_report.model_report_account_analytic_report_trial_balance_analytic
+msgid "Trial Balance Analytic Report"
+msgstr "Informe de balance analítico"
+
+#. module: account_analytic_report
+#: model:ir.actions.report,name:account_analytic_report.action_report_analytic_trial_balance_xlsx
+msgid "Trial Balance XLSX"
+msgstr "Balance Analítico XLSX"
+
+#. module: account_analytic_report
+#: model:ir.model,name:account_analytic_report.model_report_a_f_r_report_trial_balance_analytic_xlsx
+msgid "Trial Balance XLSX Report"
+msgstr "Informe XLSX de balance analítico"
+
+#. module: account_analytic_report
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.analytic_trial_balance_wizard
+msgid "View"
+msgstr "Ver"
+
+#. module: account_analytic_report
+#: model:ir.model.fields,help:account_analytic_report.field_ac_trial_balance_report_wizard__hide_account_at_0
+msgid ""
+"When this option is enabled, the trial balance will not display accounts "
+"that have initial balance = debit = credit = end balance = 0"
+msgstr ""
+"Cuando esta opción está habilitada, el balance de comprobación no mostrará "
+"cuentas cuyo saldo inicial = débito = crédito = saldo final = 0."
+
+#. module: account_analytic_report
+#. odoo-python
+#: code:addons/account_analytic_report/report/trial_balance_analytic_xlsx.py:0
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.report_trial_balance_filters
+#, python-format
+msgid "Yes"
+msgstr "Si"
+
+#. module: account_analytic_report
+#: model_terms:ir.ui.view,arch_db:account_analytic_report.analytic_trial_balance_wizard
+msgid "or"
+msgstr "o"
diff --git a/account_analytic_report/menuitems.xml b/account_analytic_report/menuitems.xml
new file mode 100644
index 00000000000..64f8e6a2f93
--- /dev/null
+++ b/account_analytic_report/menuitems.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<odoo>
+    <menuitem
+        parent="account.menu_finance_reports"
+        id="menu_oca_analytic_reports"
+        name="OCA Analytic reports"
+        groups="analytic.group_analytic_accounting"
+    />
+    <menuitem
+        parent="menu_oca_analytic_reports"
+        action="action_analytic_trial_balance_wizard"
+        id="menu_analytic_trial_balance"
+        sequence="10"
+    />
+</odoo>
diff --git a/account_analytic_report/pyproject.toml b/account_analytic_report/pyproject.toml
new file mode 100644
index 00000000000..4231d0cccb3
--- /dev/null
+++ b/account_analytic_report/pyproject.toml
@@ -0,0 +1,3 @@
+[build-system]
+requires = ["whool"]
+build-backend = "whool.buildapi"
diff --git a/account_analytic_report/readme/CONTRIBUTORS.md b/account_analytic_report/readme/CONTRIBUTORS.md
new file mode 100644
index 00000000000..4ad78d6af5f
--- /dev/null
+++ b/account_analytic_report/readme/CONTRIBUTORS.md
@@ -0,0 +1,3 @@
+- [APSL-Nagarro](https://apsl.tech):
+  - Bernat Obrador
+  - Miquel Alzanillas
diff --git a/account_analytic_report/readme/DESCRIPTION.md b/account_analytic_report/readme/DESCRIPTION.md
new file mode 100644
index 00000000000..7f76bb60ec1
--- /dev/null
+++ b/account_analytic_report/readme/DESCRIPTION.md
@@ -0,0 +1 @@
+This module introduces an analytic report that provides an intuitive way to view and analyze analytic balances. It simplifies the process, offering enhanced insights and making it easier to leverage this information effectively.
\ No newline at end of file
diff --git a/account_analytic_report/readme/USAGE.md b/account_analytic_report/readme/USAGE.md
new file mode 100644
index 00000000000..36d21cd620b
--- /dev/null
+++ b/account_analytic_report/readme/USAGE.md
@@ -0,0 +1,25 @@
+Using this module is straightforward. Follow these steps:
+
+* **Navigate to the Report**:  
+  Go to **Invoicing** -> **Reporting** -> **Analytic Trial Balance**.
+
+* **Customize the Report with Filters**:  
+  Adjust the report using the available options:
+
+  * **Group by Analytic Account**:  
+    Groups the results by analytic accounts instead of financial accounts.
+
+  * **Show Hierarchy and Limit Hierarchy Level**:  
+    Displays the amounts split by the hierarchy levels of financial accounts.
+
+  * **Filter Accounts**:  
+    When used independently (without grouping by analytic accounts or showing hierarchy), the results will be split by both financial accounts.  
+    **Example**: Filtering by accounts *Test 1* and *Test 2*:
+
+    ```text
+            | Initial Balance | Test 1   | Test 2   | Ending Balance
+    400000  |        0        | $3600    | $2400    |     $6000
+    ```
+
+  * **Show Months** (Excel export only):  
+    Enabled when filtering accounts without grouping by analytic accounts or showing hierarchy. It generates a separate sheet in the Excel file for each filtered account, detailing the amounts by month within the selected date range.  
diff --git a/account_analytic_report/report/__init__.py b/account_analytic_report/report/__init__.py
new file mode 100644
index 00000000000..db771ac683a
--- /dev/null
+++ b/account_analytic_report/report/__init__.py
@@ -0,0 +1,2 @@
+from . import trial_balance_analytic
+from . import trial_balance_analytic_xlsx
diff --git a/account_analytic_report/report/templates/trial_balance_analytic.xml b/account_analytic_report/report/templates/trial_balance_analytic.xml
new file mode 100644
index 00000000000..d83721f155e
--- /dev/null
+++ b/account_analytic_report/report/templates/trial_balance_analytic.xml
@@ -0,0 +1,492 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<odoo>
+    <template id="trial_balance_analytic">
+        <t t-call="account_financial_report.html_container">
+            <t t-foreach="docs" t-as="o">
+                <t t-call="account_financial_report.internal_layout">
+                    <t t-call="account_analytic_report.report_trial_balance_base" />
+                </t>
+            </t>
+        </t>
+    </template>
+    <template id="report_trial_balance_base">
+        <!-- Saved flag fields into variables, used to define columns display -->
+        <t t-set="foreign_currency" t-value="foreign_currency" />
+        <t t-set="hierarchy_level" t-value="hierarchy_level" />
+        <t t-set="limit_hierarchy_level" t-value="limit_hierarchy_level" />
+        <t t-set="account_codes" t-value="account_codes" />
+        <t t-set="account_code_list" t-value="account_code_list" />
+        <t t-set="show_hierarchy" t-value="show_hierarchy" />
+
+        <!-- Defines global variables used by internal layout -->
+        <t t-set="title">
+            Analytic Trial Balance -
+            <t t-out="company_name" />
+            -
+            <t t-out="currency_name" />
+        </t>
+        <t t-set="company_name" t-value="Company_Name" />
+        <!-- <t t-set="res_company" t-value="company_id"/> -->
+        <t class="page">
+            <div class="row">
+                <h4
+                    class="mt0"
+                    t-out="title or 'Odoo Report'"
+                    style="text-align: center;"
+                />
+            </div>
+            <!-- Display filters -->
+            <t t-call="account_analytic_report.report_trial_balance_filters" />
+        <div class="table" style="overflow-x: auto; width: 100%;">
+            <div
+                    class="act_as_table list_table"
+                    style="margin-top: 10px;  width: max-content;"
+                />
+            <!-- Display account lines -->
+            <div class="act_as_table data_table" style="width: 100%;">
+                <!-- Display account header -->
+                <t t-call="account_analytic_report.report_trial_balance_lines_header" />
+                <!-- Display each lines -->
+                <t t-foreach="trial_balance" t-as="balance">
+                    <!-- Adapt -->
+                    <t t-set="style" t-value="'font-size:12px;'" />
+                    <!-- Different style for account group -->
+                    <t t-if="show_hierarchy">
+                        <t t-if="balance['type'] == 'group_type'">
+                            <t
+                                    t-set="style"
+                                    t-value="style + 'font-weight: bold; color: blue; vertical-align: middle;'"
+                                />
+                        </t>
+                    </t>
+                    <t t-if="limit_hierarchy_level and hierarchy_level">
+                        <t t-if="hierarchy_level > balance['level']">
+                            <t
+                                    t-call="account_analytic_report.report_trial_balance_line"
+                                />
+                        </t>
+                    </t>
+                    <t t-else="">
+                        <t t-call="account_analytic_report.report_trial_balance_line" />
+                    </t>
+                </t>
+                <!-- Show total amounts by account type -->
+                <t
+                        t-call="account_analytic_report.report_trial_analytic_balance_total_by_acc_type"
+                    />
+
+                <!-- Show total amounts -->
+                <t
+                        t-call="account_analytic_report.report_trial_analytic_balance_totals"
+                    />
+            </div>
+        </div>
+        </t>
+    </template>
+    <template id="report_trial_balance_filters">
+        <div class="act_as_table data_table" style="width: 100%;">
+            <div class="act_as_row labels">
+                <div class="act_as_cell">Date range filter</div>
+                <div class="act_as_cell">Target accounts filter</div>
+                <div class="act_as_cell">Limit hierarchy levels</div>
+                <div class="act_as_cell">Target Plan</div>
+                <div class="act_as_cell">Group by Analytic Account</div>
+            </div>
+            <div class="act_as_row">
+                <div class="act_as_cell">
+                    From:
+                    <span t-out="date_from" t-options="{'widget': 'date'}" />
+                    To
+                    <span t-out="date_to" t-options="{'widget': 'date'}" />
+                </div>
+                <div class="act_as_cell">
+                    <t t-if="account_codes">
+                        <span t-out="account_codes" />
+                    </t>
+                    <t t-else="">
+                        All accounts
+                    </t>
+                </div>
+                <div class="act_as_cell">
+                    <t t-if="limit_hierarchy_level">
+                        Level
+                        <span t-out="hierarchy_level" />
+                    </t>
+                    <t t-if="not limit_hierarchy_level">No limit</t>
+                </div>
+                <div class="act_as_cell">
+                    <span t-out="plan_name" />
+                </div>
+                <div class="act_as_cell">
+                    <t t-if="group_by_analytic_account"> Yes </t>
+                    <t t-if="not group_by_analytic_account"> No </t>
+                </div>
+            </div>
+        </div>
+    </template>
+    <template id="report_trial_balance_lines_header">
+        <!-- Display table headers for lines -->
+        <div class="act_as_thead">
+            <div class="act_as_row labels">
+                    <!--## Code-->
+                <div class="act_as_cell" style="width: 8%;">Code</div>
+                <!--## Account-->
+                <div class="act_as_cell" style="width: 25%;">Account</div>
+                <!--## Initial balance-->
+                <div class="act_as_cell" style="width: 9%;">
+                    Initial balance
+                </div>
+                <t
+                    t-if="not account_ids or group_by_analytic_account or show_hierarchy"
+                >
+                     <!--## Period balance-->
+                    <div class="act_as_cell" style="width: 9%;">Period balance</div>
+                </t>
+                <t t-else="">
+                   <t t-foreach="account_code_list" t-as="code">
+                        <div
+                            class="act_as_cell"
+                            style="min-width: 15%; flex-shrink: 0;"
+                        >
+                            <span t-out="code" />
+                        </div>
+                    </t>
+                </t>
+                <!--## Ending balance-->
+                <div class="act_as_cell" style="width: 9%;">Ending balance</div>
+            </div>
+        </div>
+    </template>
+    <template id="report_trial_balance_line">
+
+        <t t-if="not group_by_analytic_account">
+            <t t-set="account_id_field" t-value="'general_account_id'" />
+            <t t-set="res_model" t-value="'account.account'" />
+        </t>
+        <t t-else="">
+            <t t-set="res_model" t-value="'account.analytic.account'" />
+            <t t-set="account_id_field" t-value="plan_field" />
+        </t>
+
+        <t
+            t-set="base_domain"
+            t-value="[(plan_field, '!=', False),
+            ('company_id', '=', res_company.id),
+            (plan_field, 'not in', archived_accounts)]"
+        />
+        <!-- # line  -->
+        <div class="act_as_row lines">
+            <!--## Code-->
+            <t t-if="balance['type'] == 'account_type'">
+                <div class="act_as_cell left" t-att-style="style">
+                    <span
+                        t-att-res-id="balance['id']"
+                        t-att-res-model="res_model"
+                        view-type="form"
+                    >
+                        <t t-out="balance['code']" />
+                    </span>
+                </div>
+                <!-- ## Account -->
+                <div class="act_as_cell left" t-att-style="style">
+                    <span
+                        t-att-res-id="balance['id']"
+                        t-att-res-model="res_model"
+                        view-type="form"
+                    >
+                        <t t-out="balance['name']" />
+                    </span>
+                </div>
+            </t>
+            <t t-if="balance['type'] == 'group_type'">
+                <div class="act_as_cell left" t-att-style="style">
+                    <t t-set="res_model" t-value="'account.group'" />
+                    <span
+                        t-att-res-id="balance['id']"
+                        res-model="account.group"
+                        view-type="form"
+                    >
+                        <t t-out="balance['code']" />
+                    </span>
+                </div>
+                <div class="act_as_cell left" t-att-style="style">
+                    <t t-set="res_model" t-value="'account.group'" />
+                    <span
+                        t-att-res-id="balance['id']"
+                        res-model="account.group"
+                        view-type="form"
+                    >
+                        <t t-out="balance['name']" />
+                    </span>
+                </div>
+            </t>
+            <!--## Initial balance-->
+            <div class="act_as_cell amount" t-att-style="style">
+                <t t-if="balance['type'] == 'account_type'">
+                    <t
+                        t-set="domain"
+                        t-value="[
+                            (account_id_field, '=', balance['id']),
+                            ('date', '&lt;', date_from),
+                        ]"
+                    />
+                    <span
+                        t-att-domain="domain+base_domain"
+                        res-model="account.analytic.line"
+                        view-type="tree"
+                        id="initial-balance"
+                    >
+                        <t
+                            t-out="balance['initial_balance']"
+                            t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"
+                        />
+                    </span>
+                </t>
+                <t t-if="balance['type'] == 'group_type'">
+                    <t
+                        t-set="domain"
+                        t-value="[(account_id_field, 'in', balance['account_ids']),
+                                ('date', '&lt;', date_from)]"
+                    />
+                    <span
+                        t-att-domain="domain+base_domain"
+                        res-model="account.analytic.line"
+                        id="initial-balance-group"
+                    >
+                        <t
+                            t-out="balance['initial_balance']"
+                            t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"
+                        />
+                    </span>
+                </t>
+            </div>
+            <t t-if="not account_ids or group_by_analytic_account or show_hierarchy">
+                <!--            &lt;!&ndash;## Period balance&ndash;&gt;-->
+                 <div class="act_as_cell amount" t-att-style="style">
+                    <t t-if="balance['type'] == 'account_type'">
+                        <t
+                            t-set="domain"
+                            t-value="[(account_id_field, '=', balance['id']),
+                                        ('date', '&gt;=', date_from),
+                                        ('date', '&lt;=', date_to),
+                                        ('amount', '&lt;&gt;', 0)]"
+                        />
+                        <span
+                            t-att-domain="domain+base_domain"
+                            res-model="account.analytic.line"
+                            id="period-balance"
+                        >
+                            <t
+                                t-out="balance['balance']"
+                                t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"
+                            />
+                        </span>
+                    </t>
+                    <t t-if="balance['type'] == 'group_type'">
+                        <t
+                            t-set="domain"
+                            t-value="[(account_id_field, 'in', balance['account_ids']),
+                                        ('date', '&gt;=', date_from),
+                                        ('date', '&lt;=', date_to)]"
+                        />
+                        <span
+                            t-att-domain="domain+base_domain"
+                            res-model="account.analytic.line"
+                            id="period-balance-group"
+                        >
+                            <t
+                                t-out="balance['balance']"
+                                t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"
+                            />
+                        </span>
+                    </t>
+                </div>
+            </t>
+            <t t-else="">
+            <!-- Displays period balance splited by accounts -->
+                <t t-foreach="balance['accounts'].items()" t-as="account">
+                    <div class="act_as_cell amount" t-att-style="style">
+                        <t t-if="balance['type'] == 'account_type'">
+                            <t
+                                t-set="domain"
+                                t-value="[(plan_field, '=', account[0]),
+                                            ('general_account_id', '=', balance['id']),
+                                            ('date', '&gt;=', date_from),
+                                            ('date', '&lt;=', date_to),
+                                            ('amount', '&lt;&gt;', 0)]"
+                            />
+                            <span
+                                t-att-domain="domain+base_domain"
+                                res-model="account.analytic.line"
+                                id="period-balance"
+                            >
+                                <t
+                                    t-out="account[1]"
+                                    t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"
+                                />
+                            </span>
+                        </t>
+                        <t t-if="balance['type'] == 'group_type'">
+                            <t
+                                t-set="domain"
+                                t-value="[(account_id_field, 'in', balance['account_ids']),
+                                            ('date', '&gt;=', date_from),
+                                            ('date', '&lt;=', date_to)]"
+                            />
+                            <span
+                                t-att-domain="domain+base_domain"
+                                res-model="account.analytic.line"
+                                id="period-balance-group"
+                            >
+                                <t
+                                    t-out="balance['balance']"
+                                    t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"
+                                />
+                            </span>
+                        </t>
+                    </div>
+                </t>
+            </t>
+            <!--            &lt;!&ndash;## Ending balance&ndash;&gt;-->
+            <div class="act_as_cell amount" t-att-style="style">
+                <t t-if="balance['type'] == 'account_type'">
+                    <t
+                        t-set="domain"
+                        t-value="[(account_id_field, '=', balance['id']),
+                                        ('date', '&lt;=', date_to)]"
+                    />
+                    <span
+                        t-att-domain="domain+base_domain"
+                        res-model="account.analytic.line"
+                        id="ending-balance"
+                    >
+                        <t
+                            t-out="balance['ending_balance']"
+                            t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"
+                        />
+                    </span>
+                </t>
+                <t t-if="balance['type'] == 'group_type'">
+                    <t
+                        t-set="domain"
+                        t-value="[(account_id_field, 'in', balance['account_ids'])]"
+                    />
+                    <span
+                        t-att-domain="domain+base_domain"
+                        res-model="account.analytic.line"
+                        id="ending-balance-group"
+                    >
+                        <t
+                            t-out="balance['ending_balance']"
+                            t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"
+                        />
+                    </span>
+                </t>
+            </div>
+        </div>
+    </template>
+
+    <template id="report_trial_analytic_balance_total_by_acc_type">
+        <t t-foreach="totals_by_acc_type.items()" t-as="total">
+            <div
+                class="act_as_row total_by_account_type"
+                style="background-color: #f5f5f5; border-top: 2px solid #ccc; margin-top: 10px; padding: 5px;"
+            >
+                <div class="act_as_cell" />
+                <div class="act_as_cell left" style="font-weight: bold; color: #333;">
+                    <t t-out="total[0]" />
+                </div>
+                <div class="act_as_cell amount" style="font-weight: bold; color: #555;">
+                    <t
+                        t-out="total[1]['total_initial_balance']"
+                        t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"
+                    />
+                </div>
+                <t
+                    t-if="not account_ids or group_by_analytic_account or show_hierarchy"
+                >
+                    <div
+                        class="act_as_cell amount"
+                        style="font-weight: bold; color: #555;"
+                    >
+                        <t
+                            t-out="total[1]['total_period_balance']"
+                            t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"
+                        />
+                    </div>
+                </t>
+                <t t-else="">
+                    <t
+                        t-foreach="total[1]['total_period_balance'].items()"
+                        t-as="total_by_acc"
+                    >
+                        <div
+                            class="act_as_cell amount"
+                            style="font-weight: bold; color: #555;"
+                        >
+                            <t
+                                t-out="total_by_acc[1]"
+                                t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"
+                            />
+                        </div>
+                    </t>
+                </t>
+                <div class="act_as_cell amount" style="font-weight: bold; color: #555;">
+                    <t
+                        t-out="total[1]['total_ending_balance']"
+                        t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"
+                    />
+                </div>
+            </div>
+        </t>
+    </template>
+
+    <template id="report_trial_analytic_balance_totals">
+        <div
+            class="act_as_row total"
+            style="background-color: #e6e6e6; border-top: 3px solid #333; margin-top: 15px; padding: 10px;"
+        >
+            <div class="act_as_cell" />
+            <div
+                class="act_as_cell left"
+                style="font-weight: bold; color: #000;"
+            >Total</div>
+            <div class="act_as_cell amount" style="font-weight: bold; color: #444;">
+                <t
+                    t-out="total_amounts['total_initial_balance']"
+                    t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"
+                />
+            </div>
+            <t t-if="not account_ids or group_by_analytic_account or show_hierarchy">
+                <div class="act_as_cell amount" style="font-weight: bold; color: #444;">
+                    <t
+                        t-out="total_amounts['total_period_balance']"
+                        t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"
+                    />
+                </div>
+            </t>
+            <t t-else="">
+                <t
+                    t-foreach="total_amounts['total_period_balance'].items()"
+                    t-as="amount"
+                >
+                    <div
+                        class="act_as_cell amount"
+                        style="font-weight: bold; color: #444;"
+                    >
+                        <t
+                            t-out="amount[1]"
+                            t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"
+                        />
+                    </div>
+                </t>
+            </t>
+            <div class="act_as_cell amount" style="font-weight: bold; color: #444;">
+                <t
+                    t-out="total_amounts['total_ending_balance']"
+                    t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"
+                />
+            </div>
+        </div>
+    </template>
+</odoo>
diff --git a/account_analytic_report/report/trial_balance_analytic.py b/account_analytic_report/report/trial_balance_analytic.py
new file mode 100644
index 00000000000..fa14f131f7c
--- /dev/null
+++ b/account_analytic_report/report/trial_balance_analytic.py
@@ -0,0 +1,837 @@
+# Copyright 2024 (APSL - Nagarro) Bernat Obrador
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+
+from odoo import api, models
+from odoo.tools.float_utils import float_is_zero
+
+
+class TrialBalanceAnalyticReport(models.AbstractModel):
+    _name = "report.account_analytic_report.trial_balance_analytic"
+    _description = "Trial Balance Analytic Report"
+    _inherit = "report.account_financial_report.abstract_report"
+
+    def _get_accounts_data(self, accounts_ids, group_by_field):
+        if group_by_field == "general_account_id":
+            accounts = self.env["account.account"].search([("id", "in", accounts_ids)])
+        else:
+            accounts = self.env["account.analytic.account"].search(
+                [("id", "in", accounts_ids)]
+            )
+        accounts_data = {}
+        for account in accounts:
+            accounts_data.update(
+                {
+                    account.id: {
+                        "id": account.id,
+                        "name": account.name,
+                        "code": account.code if account.code else account.name,
+                    }
+                }
+            )
+        return accounts_data
+
+    def _get_base_domain(
+        self, account_ids, company_id, account_id_field, plan_id, group_by_field
+    ):
+        accounts_domain = [
+            ("company_id", "=", company_id),
+            ("root_plan_id", "=", plan_id),
+        ]
+        if account_ids:
+            accounts_domain += [("id", "in", account_ids)]
+        accounts = self.env["account.analytic.account"].search(accounts_domain)
+
+        domain = [
+            (account_id_field, "in", accounts.ids),
+            (account_id_field, "!=", False),
+            (group_by_field, "!=", False),
+        ]
+        if company_id:
+            domain += [("company_id", "=", company_id)]
+        return domain
+
+    def _get_initial_balances_bs_ml_domain(self, domain, date_from, fy_start_date):
+        bs_ml_domain = domain + [
+            ("date", "<", date_from),
+            ("date", ">=", fy_start_date),
+        ]
+        return bs_ml_domain
+
+    @api.model
+    def _get_period_ml_domain(
+        self,
+        domain,
+        date_to,
+        date_from,
+    ):
+        ml_domain = domain + [
+            ("date", ">=", date_from),
+            ("date", "<=", date_to),
+        ]
+        return ml_domain
+
+    @api.model
+    def _compute_account_amount(
+        self,
+        total_amount,
+        tb_initial_acc,
+        tb_period_acc,
+        group_by_field,
+        account_id_field=None,
+        account_ids=None,
+    ):
+        """
+        Prepares the total amount dict with inital balance, period balance and
+        ending balance.
+        If account_ids is not null and we are not grouping by analytic account
+        it will split the ammount in the analytic account and financial account
+        """
+        for tb in tb_period_acc:
+            if tb[group_by_field]:
+                self._prepare_amounts(
+                    tb, group_by_field, total_amount, account_id_field, account_ids
+                )
+        for tb in tb_initial_acc:
+            id_field = group_by_field if account_ids else "account_id"
+            acc_id = tb[id_field]
+            if acc_id not in total_amount.keys():
+                total_amount[acc_id] = self._prepare_total_amount(tb, account_ids)
+            else:
+                total_amount[acc_id]["initial_balance"] = tb["amount"]
+                total_amount[acc_id]["ending_balance"] += tb["amount"]
+        return total_amount
+
+    def _prepare_amounts(
+        self, tb, group_by_field, total_amount, account_id_field, account_ids=None
+    ):
+        if account_ids:
+            acc_id = tb[group_by_field][0]
+            if acc_id not in total_amount.keys():
+                total_amount[acc_id] = self._prepare_total_amount(tb, account_ids)
+                total_amount[acc_id][tb[account_id_field][0]] = tb["amount"]
+                total_amount[acc_id]["initial_balance"] = 0.0
+            else:
+                total_amount[acc_id][tb[account_id_field][0]] = tb["amount"]
+                total_amount[acc_id]["ending_balance"] += tb["amount"]
+                total_amount[acc_id]["initial_balance"] = 0.0
+        else:
+            acc_id = tb[group_by_field][0]
+            total_amount[acc_id] = self._prepare_total_amount(tb)
+            total_amount[acc_id]["amount"] = tb["amount"]
+            total_amount[acc_id]["initial_balance"] = 0.0
+
+    @api.model
+    def _prepare_total_amount(self, tb, account_ids=None):
+        res = {
+            "amount": 0.0,
+            "initial_balance": tb["amount"],
+            "ending_balance": tb["amount"],
+        }
+        if account_ids:
+            for account in account_ids:
+                res[account] = 0.0
+
+        return res
+
+    def _remove_accounts_at_cero(self, total_amount, company):
+        def is_removable(d):
+            rounding = company.currency_id.rounding
+            return float_is_zero(
+                d["initial_balance"], precision_rounding=rounding
+            ) and float_is_zero(d["ending_balance"], precision_rounding=rounding)
+
+        accounts_to_remove = []
+        for acc_id, ta_data in total_amount.items():
+            if is_removable(ta_data):
+                accounts_to_remove.append(acc_id)
+        for account_id in accounts_to_remove:
+            del total_amount[account_id]
+
+    def _get_hierarchy_groups(self, group_ids, groups_data):
+        for group_id in group_ids:
+            parent_id = groups_data[group_id]["parent_id"]
+            while parent_id:
+                if parent_id not in groups_data.keys():
+                    group = self.env["account.group"].browse(parent_id)
+                    groups_data[group.id] = {
+                        "id": group.id,
+                        "code": group.code_prefix_start,
+                        "name": group.name,
+                        "parent_id": group.parent_id.id,
+                        "parent_path": group.parent_path,
+                        "complete_code": group.complete_code,
+                        "account_ids": group.compute_account_ids.ids,
+                        "type": "group_type",
+                        "initial_balance": 0,
+                        "balance": 0,
+                        "ending_balance": 0,
+                    }
+                acc_keys = ["balance"]
+                acc_keys += ["initial_balance", "ending_balance"]
+                for acc_key in acc_keys:
+                    groups_data[parent_id][acc_key] += groups_data[group_id][acc_key]
+                parent_id = groups_data[parent_id]["parent_id"]
+        return groups_data
+
+    def _get_groups_data(self, accounts_data, total_amount):
+        accounts_ids = list(accounts_data.keys())
+        accounts = self.env["account.account"].browse(accounts_ids)
+        account_group_relation = {}
+        for account in accounts:
+            accounts_data[account.id]["complete_code"] = (
+                account.group_id.complete_code + " / " + account.code
+                if account.group_id.id
+                else ""
+            )
+            if account.group_id.id:
+                if account.group_id.id not in account_group_relation.keys():
+                    account_group_relation.update({account.group_id.id: [account.id]})
+                else:
+                    account_group_relation[account.group_id.id].append(account.id)
+        groups = self.env["account.group"].browse(account_group_relation.keys())
+        groups_data = {}
+        for group in groups:
+            groups_data.update(
+                {
+                    group.id: {
+                        "id": group.id,
+                        "code": group.code_prefix_start,
+                        "name": group.name,
+                        "parent_id": group.parent_id.id,
+                        "parent_path": group.parent_path,
+                        "type": "group_type",
+                        "complete_code": group.complete_code,
+                        "account_ids": group.compute_account_ids.ids,
+                        "initial_balance": 0.0,
+                        "balance": 0.0,
+                        "ending_balance": 0.0,
+                    }
+                }
+            )
+        for group_id in account_group_relation.keys():
+            for account_id in account_group_relation[group_id]:
+                groups_data[group_id]["initial_balance"] += total_amount[account_id][
+                    "initial_balance"
+                ]
+                groups_data[group_id]["balance"] += total_amount[account_id]["amount"]
+                groups_data[group_id]["ending_balance"] += total_amount[account_id][
+                    "ending_balance"
+                ]
+        group_ids = list(groups_data.keys())
+        groups_data = self._get_hierarchy_groups(
+            group_ids,
+            groups_data,
+        )
+        return groups_data
+
+    def _hide_accounts_at_0(self, company_id, total_amount):
+        company = self.env["res.company"].browse(company_id)
+        self._remove_accounts_at_cero(total_amount, company)
+
+    def _get_tb_initial_acc_bs(
+        self, domain, date_from, fy_start_date, fields, group_by, lazy=True
+    ):
+        initial_domain_bs = self._get_initial_balances_bs_ml_domain(
+            domain,
+            date_from,
+            fy_start_date,
+        )
+        return self.env["account.analytic.line"].read_group(
+            domain=initial_domain_bs,
+            fields=fields,
+            groupby=group_by,
+            lazy=lazy,
+        )
+
+    def _get_tb_period_acc(
+        self, domain, date_to, date_from, fields, group_by, lazy=True
+    ):
+        period_domain = self._get_period_ml_domain(
+            domain,
+            date_to,
+            date_from,
+        )
+        return self.env["account.analytic.line"].read_group(
+            domain=period_domain, fields=fields, groupby=group_by, lazy=lazy
+        )
+
+    def _get_account_codes(self, account_ids):
+        analytic_accounts = self.env["account.analytic.account"].search(
+            [("id", "in", account_ids)]
+        )
+        account_codes = [
+            account.code if account.code else account.name
+            for account in sorted(analytic_accounts, key=lambda account: account.id)
+        ]
+        codes_string = ", ".join(account_codes)
+        return codes_string
+
+    def _clean_account_codes(self, account_codes):
+        return (
+            [code.strip() for code in account_codes.split(",")]
+            if account_codes
+            else None
+        )
+
+    def _update_accounts_data(
+        self,
+        accounts_data,
+        total_amount,
+        total_amounts,
+        include_both_accounts=False,
+        account_ids=None,
+    ):
+        for account_id in accounts_data.keys():
+            accounts_data[account_id].update(
+                {
+                    "initial_balance": total_amount[account_id]["initial_balance"],
+                    "ending_balance": total_amount[account_id]["ending_balance"],
+                    "type": "account_type",
+                    "code": accounts_data[account_id]["code"],
+                }
+            )
+            total_amounts["total_initial_balance"] += total_amount[account_id][
+                "initial_balance"
+            ]
+            total_amounts["total_ending_balance"] += total_amount[account_id][
+                "ending_balance"
+            ]
+            # If the report requires both account details, add a nested
+            # structure within each account. So now we can have the amount
+            # by the analytic account and the financial account
+            if include_both_accounts:
+                accounts_data[account_id]["accounts"] = {}
+                for account in account_ids:
+                    accounts_data[account_id]["accounts"][account] = total_amount[
+                        account_id
+                    ][account]
+                    if account not in total_amounts["total_period_balance"]:
+                        total_amounts["total_period_balance"][account] = 0
+                    total_amounts["total_period_balance"][account] += total_amount[
+                        account_id
+                    ][account]
+            else:
+                accounts_data[account_id].update(
+                    {"balance": total_amount[account_id]["amount"]}
+                )
+                total_amounts["total_period_balance"] += total_amount[account_id][
+                    "amount"
+                ]
+
+    def _get_trial_balance(self, accounts_data, total_amount, show_hierarchy):
+        if show_hierarchy:
+            groups_data = self._get_groups_data(accounts_data, total_amount)
+            trial_balance = list(groups_data.values()) + list(accounts_data.values())
+            trial_balance = sorted(trial_balance, key=lambda k: k["complete_code"])
+            for trial in trial_balance:
+                trial["level"] = trial["complete_code"].count("/")
+        else:
+            trial_balance = list(accounts_data.values())
+        return trial_balance
+
+    def _get_total_amounts_dict(self, include_both_accounts):
+        return {
+            "total_initial_balance": 0,
+            "total_period_balance": {} if include_both_accounts else 0,
+            "total_ending_balance": 0,
+        }
+
+    def _get_archived_account_ids(self, company_id):
+        return (
+            self.env["account.analytic.account"]
+            .search([("company_id", "=", company_id), ("active", "=", False)])
+            .ids
+        )
+
+    @api.model
+    def _get_data_splited_by_accounts(
+        self,
+        account_ids,
+        company_id,
+        date_to,
+        date_from,
+        fy_start_date,
+        plan_field,
+        plan_id,
+    ):
+        """
+        This function gives the report grouped by financial account and
+        analytic account spliting the ammount by the 2 accounts
+        """
+        domain = self._get_base_domain(
+            account_ids, company_id, plan_field, plan_id, "general_account_id"
+        )
+        tb_initial_acc_bs = self._get_tb_initial_acc_bs(
+            domain=domain,
+            date_from=date_from,
+            fy_start_date=fy_start_date,
+            fields=[plan_field, "general_account_id", "amount"],
+            group_by=["general_account_id", plan_field],
+            lazy=False,
+        )
+        tb_initial_acc = []
+        for line in tb_initial_acc_bs:
+            tb_initial_acc.append(
+                {
+                    "general_account_id": line["general_account_id"][0],
+                    plan_field: line[plan_field][0],
+                    "amount": line["amount"],
+                }
+            )
+
+        tb_initial_acc = [p for p in tb_initial_acc if p["amount"] != 0]
+
+        tb_period_acc = self._get_tb_period_acc(
+            domain=domain,
+            date_to=date_to,
+            date_from=date_from,
+            fields=[plan_field, "general_account_id", "amount"],
+            group_by=["general_account_id", plan_field],
+            lazy=False,
+        )
+
+        total_amount = {}
+        total_amount = self._compute_account_amount(
+            total_amount,
+            tb_initial_acc,
+            tb_period_acc,
+            "general_account_id",
+            plan_field,
+            account_ids,
+        )
+
+        self._hide_accounts_at_0(company_id, total_amount)
+
+        accounts_ids = list(total_amount.keys())
+        accounts_data = self._get_accounts_data(accounts_ids, "general_account_id")
+
+        return total_amount, accounts_data
+
+    @api.model
+    def _get_data(
+        self,
+        account_ids,
+        company_id,
+        date_to,
+        date_from,
+        fy_start_date,
+        plan_field,
+        plan_id,
+        group_by_analytic_account,
+    ):
+        """
+        This function gives the report grouped by financial account
+        """
+        group_by_field = (
+            plan_field if group_by_analytic_account else "general_account_id"
+        )
+
+        domain = self._get_base_domain(
+            account_ids, company_id, plan_field, plan_id, group_by_field
+        )
+
+        accounts_domain = [("company_id", "=", company_id)]
+        if account_ids:
+            accounts_domain += [("id", "in", account_ids)]
+
+        if group_by_field == "general_account_id":
+            accounts = self.env["account.account"].search(accounts_domain)
+        else:
+            accounts = self.env["account.analytic.account"].search(accounts_domain)
+        tb_initial_acc = []
+
+        for account in accounts:
+            tb_initial_acc.append({"account_id": account.id, "amount": 0.0})
+
+        tb_initial_acc_bs = self._get_tb_initial_acc_bs(
+            domain=domain,
+            date_from=date_from,
+            fy_start_date=fy_start_date,
+            fields=[plan_field, "general_account_id", "amount"],
+            group_by=[group_by_field],
+        )
+        for account_rg in tb_initial_acc_bs:
+            element = list(
+                filter(
+                    lambda acc_dict: acc_dict["account_id"]
+                    == account_rg[group_by_field][0],
+                    tb_initial_acc,
+                )
+            )
+            if element:
+                element[0]["amount"] += account_rg["amount"]
+
+        tb_initial_acc = [p for p in tb_initial_acc if p["amount"] != 0]
+
+        tb_period_acc = self._get_tb_period_acc(
+            domain=domain,
+            date_to=date_to,
+            date_from=date_from,
+            fields=[plan_field, "general_account_id", "amount"],
+            group_by=[group_by_field],
+        )
+
+        total_amount = {}
+        total_amount = self._compute_account_amount(
+            total_amount, tb_initial_acc, tb_period_acc, group_by_field
+        )
+
+        self._hide_accounts_at_0(company_id, total_amount)
+
+        accounts_ids = list(total_amount.keys())
+        accounts_data = self._get_accounts_data(accounts_ids, group_by_field)
+
+        return total_amount, accounts_data
+
+    def _get_base_total_by_acc_type_select(self, include_both_accounts, plan_field):
+        if include_both_accounts:
+            return f"""
+            SELECT aa.account_type, aal.{plan_field}, sum(amount)
+            FROM account_analytic_line AS aal
+            INNER JOIN account_account AS aa ON aa.id = aal.general_account_id
+            """
+        return """
+        SELECT aa.account_type, sum(amount)
+        FROM account_analytic_line AS aal
+        INNER JOIN account_account AS aa ON aa.id = aal.general_account_id
+        """
+
+    def _get_base_total_by_acc_type_where(self, company_id, account_ids, plan_field):
+        account_ids_where = (
+            f"AND aal.{plan_field} in ({','.join(map(str, account_ids))})"
+            if account_ids
+            else ""
+        )
+        archives_account_ids = self._get_archived_account_ids(company_id)
+        acrhived_account_ids_where = (
+            f"AND aal.{plan_field} not in ({','.join(map(str, archives_account_ids))})"
+            if archives_account_ids
+            else ""
+        )
+
+        return f"""
+        WHERE aal.company_id = {company_id}
+        {account_ids_where}
+        {acrhived_account_ids_where}
+        AND aal.{plan_field} is not null
+        """
+
+    def _get_base_total_acc_type_group_by(self, include_both_accounts, plan_field):
+        if include_both_accounts:
+            return f"""
+            GROUP BY aa.account_type, aal.{plan_field}
+            """
+        return """
+        GROUP BY aa.account_type
+        """
+
+    def _get_account_type_mapping(self):
+        return dict(
+            self.env["account.account"].fields_get(allfields=["account_type"])[
+                "account_type"
+            ]["selection"]
+        )
+
+    def _map_accounts_type_by_name(
+        self, results, account_type_mapping, balance_type, include_both_accounts
+    ):
+        result_dict = {}
+
+        # If balance type its period we need to make a specific key for the account_ids
+        # To have the amount splitted by financial account and analytic account
+        if balance_type == "total_period_balance" and include_both_accounts:
+            key_format = "{}|{}"
+        else:
+            key_format = "{}"
+
+        for result in results:
+            if len(result) == 3:
+                account_type, account_id, total = result
+            elif len(result) == 2:
+                account_type, total = result
+                account_id = None
+            else:
+                continue
+
+            account_type_name = account_type_mapping.get(account_type, account_type)
+
+            if include_both_accounts and account_id is not None:
+                key = key_format.format(account_type_name, account_id)
+            else:
+                key = key_format.format(account_type_name)
+
+            if key in result_dict:
+                result_dict[key] += total
+            else:
+                result_dict[key] = total
+
+        return result_dict
+
+    def _get_total_initial_by_acc_type(
+        self,
+        base_select,
+        base_where,
+        base_group_by,
+        date_from,
+        fy_start_date,
+    ):
+        query = f"""
+            {base_select}
+            {base_where}
+            AND aal.date < %s
+            AND aal.date >= %s
+            {base_group_by}
+        """
+        params = [date_from, fy_start_date]
+        self.env.cr.execute(query, params)
+
+        return self.env.cr.fetchall()
+
+    def _get_total_period_by_acc_type(
+        self,
+        base_select,
+        base_where,
+        base_group_by,
+        date_from,
+        date_to,
+    ):
+        query = f"""
+            {base_select}
+            {base_where}
+            AND aal.date >= %s
+            AND aal.date <= %s
+            {base_group_by}
+        """
+        params = [date_from, date_to]
+        self.env.cr.execute(query, params)
+
+        return self.env.cr.fetchall()
+
+    def _update_balance_by_account_type(
+        self, balance_type, totals_by_acc_type, totals_dict
+    ):
+        for acc_type in totals_by_acc_type:
+            totals_dict[acc_type][balance_type] = totals_by_acc_type[acc_type]
+            totals_dict[acc_type]["total_ending_balance"] += totals_by_acc_type[
+                acc_type
+            ]
+
+    def _get_totals_by_acc_type(
+        self,
+        company_id,
+        account_ids,
+        date_from,
+        date_to,
+        plan_field,
+        group_by_analytic_account,
+        include_both_accounts,
+        fy_start_date,
+    ):
+        """
+        This function calculates and returns the totals
+        for each account type, providing greater analytical
+        precision for the report.
+        Period balance will change if the report includes
+        the analytic accounts too.
+        ex:
+                    Inital Balance | Period Balance | Ending Balance
+            Income:       1.250€            250€            1.500€
+            Epxense:       -500€           -125€            -625€
+        """
+        account_type_mapping = self._get_account_type_mapping()
+        base_select = self._get_base_total_by_acc_type_select(
+            include_both_accounts, plan_field
+        )
+        base_where = self._get_base_total_by_acc_type_where(
+            company_id, account_ids, plan_field
+        )
+        base_group_by = self._get_base_total_acc_type_group_by(
+            include_both_accounts, plan_field
+        )
+
+        account_types_total_dict = {
+            account_type_name: self._get_total_amounts_dict(include_both_accounts)
+            for _account_type, account_type_name in account_type_mapping.items()
+        }
+        for _account_type, balances in account_types_total_dict.items():
+            for account_id in account_ids:
+                # Si tenemos que incluir los dos tipos de cuentas entonces
+                # Debemos crear un subapartado por cada cuenta
+                if include_both_accounts:
+                    if account_id not in balances["total_period_balance"]:
+                        balances["total_period_balance"][account_id] = 0
+                else:
+                    balances["total_period_balance"] = 0
+
+        total_initial_by_acc_type = self._get_total_initial_by_acc_type(
+            base_select, base_where, base_group_by, date_from, fy_start_date
+        )
+
+        total_period_by_acc_type = self._get_total_period_by_acc_type(
+            base_select,
+            base_where,
+            base_group_by,
+            date_from,
+            date_to,
+        )
+
+        total_initial_by_acc_type = self._map_accounts_type_by_name(
+            total_initial_by_acc_type,
+            account_type_mapping,
+            "total_initial_balance",
+            include_both_accounts,
+        )
+        total_period_by_acc_type = self._map_accounts_type_by_name(
+            total_period_by_acc_type,
+            account_type_mapping,
+            "total_period_balance",
+            include_both_accounts,
+        )
+
+        if include_both_accounts:
+            for key, value in total_period_by_acc_type.items():
+                account_type, account_id = key.split("|")
+                account_id = int(account_id)
+                if (
+                    account_id
+                    not in account_types_total_dict[account_type][
+                        "total_period_balance"
+                    ].keys()
+                ):
+                    account_types_total_dict[account_type]["total_period_balance"][
+                        account_id
+                    ] = 0
+                account_types_total_dict[account_type]["total_period_balance"][
+                    account_id
+                ] += value
+                account_types_total_dict[account_type]["total_ending_balance"] += value
+        else:
+            self._update_balance_by_account_type(
+                "total_period_balance",
+                total_period_by_acc_type,
+                account_types_total_dict,
+            )
+
+        self._update_balance_by_account_type(
+            "total_initial_balance", total_initial_by_acc_type, account_types_total_dict
+        )
+
+        # Deletes account types with 0 amounts
+        filtered_account_types_total_dict = {
+            account_type_name: balances
+            for account_type_name, balances in account_types_total_dict.items()
+            if balances["total_ending_balance"]
+        }
+
+        return filtered_account_types_total_dict
+
+    def _get_report_values(self, docids, data):
+        wizard_id = data["wizard_id"]
+        company = self.env["res.company"].browse(data["company_id"])
+
+        account_codes = self._get_account_codes(data["account_ids"])
+        account_code_list = self._clean_account_codes(account_codes)
+
+        if (
+            data["account_ids"]
+            and not data["group_by_analytic_account"]
+            and not data["show_hierarchy"]
+        ):
+            total_amount, accounts_data = self._get_data_splited_by_accounts(
+                data["account_ids"],
+                data["company_id"],
+                data["date_to"],
+                data["date_from"],
+                data["fy_start_date"],
+                data["plan_field"],
+                data["plan_id"],
+            )
+            include_both_accounts = True
+        else:
+            total_amount, accounts_data = self._get_data(
+                data["account_ids"],
+                data["company_id"],
+                data["date_to"],
+                data["date_from"],
+                data["fy_start_date"],
+                data["plan_field"],
+                data["plan_id"],
+                data["group_by_analytic_account"],
+            )
+            include_both_accounts = False
+
+        totals_by_acc_type = self._get_totals_by_acc_type(
+            data["company_id"],
+            data["account_ids"],
+            data["date_from"],
+            data["date_to"],
+            data["plan_field"],
+            data["group_by_analytic_account"],
+            include_both_accounts,
+            data["fy_start_date"],
+        )
+
+        total_amounts = self._get_total_amounts_dict(include_both_accounts)
+        self._update_accounts_data(
+            accounts_data,
+            total_amount,
+            total_amounts,
+            include_both_accounts=include_both_accounts,
+            account_ids=data["account_ids"],
+        )
+        trial_balance = self._get_trial_balance(
+            accounts_data, total_amount, data["show_hierarchy"]
+        )
+
+        return self._prepare_report_values(
+            wizard_id,
+            company,
+            data,
+            trial_balance,
+            total_amount,
+            accounts_data,
+            account_codes,
+            account_code_list,
+            total_amounts,
+            totals_by_acc_type,
+        )
+
+    def _prepare_report_values(
+        self,
+        wizard_id,
+        company,
+        data,
+        trial_balance,
+        total_amount,
+        accounts_data,
+        account_codes,
+        account_code_list,
+        total_amounts,
+        totals_by_acc_type,
+    ):
+        return {
+            "doc_ids": [wizard_id],
+            "doc_model": "ac.trial.balance.report.wizard",
+            "docs": self.env["ac.trial.balance.report.wizard"].browse(wizard_id),
+            "company_name": company.display_name,
+            "currency_name": company.currency_id.name,
+            "date_from": data["date_from"],
+            "date_to": data["date_to"],
+            "trial_balance": trial_balance,
+            "total_amount": total_amount,
+            "accounts_data": accounts_data,
+            "plan_name": data["plan_name"],
+            "plan_field": data["plan_field"],
+            "group_by_analytic_account": data["group_by_analytic_account"],
+            "show_hierarchy": data["show_hierarchy"],
+            "limit_hierarchy_level": data["limit_hierarchy_level"],
+            "show_hierarchy_level": data["hierarchy_level"],
+            "account_codes": account_codes,
+            "account_code_list": account_code_list,
+            "account_ids": data["account_ids"],
+            "show_months": data["show_months"],
+            "total_amounts": total_amounts,
+            "archived_accounts": tuple(self._get_archived_account_ids(company.id)),
+            "totals_by_acc_type": totals_by_acc_type,
+        }
diff --git a/account_analytic_report/report/trial_balance_analytic_xlsx.py b/account_analytic_report/report/trial_balance_analytic_xlsx.py
new file mode 100644
index 00000000000..4f01bc44092
--- /dev/null
+++ b/account_analytic_report/report/trial_balance_analytic_xlsx.py
@@ -0,0 +1,643 @@
+# Copyright 2024 (APSL - Nagarro) Bernat Obrador
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+from datetime import datetime
+
+from dateutil.relativedelta import relativedelta
+
+from odoo import _, models
+
+
+class TrialBalanceXslx(models.AbstractModel):
+    _name = "report.a_f_r.report_trial_balance_analytic_xlsx"
+    _description = "Trial Balance XLSX Report"
+    _inherit = "report.account_financial_report.abstract_report_xlsx"
+
+    def _get_report_name(self, report, data=False):
+        company_id = data.get("company_id", False)
+        account_code = data.get("account_code", False)
+        report_name = _("Analytic Trial Balance")
+        if company_id:
+            company = self.env["res.company"].browse(company_id)
+            suffix = f" - {company.name} - {company.currency_id.name}"
+            report_name = report_name + suffix
+        if account_code:
+            report_name += f" [{account_code}]"
+        return report_name
+
+    def _define_formats(self, workbook, report_data):
+        super()._define_formats(workbook, report_data)
+        currency_id = self.env["res.company"]._default_currency_id()
+        col_format_totals = {
+            "bold": True,
+            "bg_color": "#90cf00",
+            "border": True,
+        }
+
+        col_format_totals_by_acc_type = {
+            "bold": True,
+            "bg_color": "#D9EBD3",
+            "border": True,
+        }
+        report_data["formats"]["format_total"] = workbook.add_format(col_format_totals)
+        report_data["formats"]["format_amount_total"] = workbook.add_format(
+            col_format_totals
+        )
+        report_data["formats"]["format_amount_total"].set_num_format(
+            "#,##0." + "0" * currency_id.decimal_places
+        )
+
+        report_data["formats"]["format_acc_type_total"] = workbook.add_format(
+            col_format_totals_by_acc_type
+        )
+        report_data["formats"]["format_acc_type_amount_total"] = workbook.add_format(
+            col_format_totals_by_acc_type
+        )
+        report_data["formats"]["format_acc_type_amount_total"].set_num_format(
+            "#,##0." + "0" * currency_id.decimal_places
+        )
+
+        return True
+
+    def _is_report_with_include_both_accounts(self, report):
+        return report.account_ids and not report.group_by_analytic_account
+
+    def _get_report_columns(self, report):
+        if self._is_report_with_include_both_accounts(report):
+            codes = self.env["account.analytic.account"].search(
+                [("id", "in", report.account_ids.ids)]
+            )
+            res = {
+                0: {"header": _("Code"), "field": "code", "width": 15},
+                1: {"header": _("Account"), "field": "name", "width": 70},
+                2: {
+                    "header": _("Initial balance"),
+                    "field": "initial_balance",
+                    "type": "amount",
+                    "width": 14,
+                },
+            }
+            for i, account in enumerate(codes):
+                res[i + 3] = {
+                    "header": account.code,
+                    "id": account.id,
+                    "field": "accounts",
+                    "type": "amount",
+                    "width": 14,
+                }
+
+            res[len(res)] = {
+                "header": _("Ending balance"),
+                "field": "ending_balance",
+                "type": "amount",
+                "width": 14,
+            }
+        else:
+            res = {
+                0: {"header": _("Code"), "field": "code", "width": 10},
+                1: {"header": _("Account"), "field": "name", "width": 70},
+                2: {
+                    "header": _("Initial balance"),
+                    "field": "initial_balance",
+                    "type": "amount",
+                    "width": 14,
+                },
+                3: {
+                    "header": _("Period balance"),
+                    "field": "balance",
+                    "type": "amount",
+                    "width": 14,
+                },
+                4: {
+                    "header": _("Ending balance"),
+                    "field": "ending_balance",
+                    "type": "amount",
+                    "width": 14,
+                },
+            }
+        return res
+
+    def _get_report_filters(self, report):
+        report_filters = [
+            [
+                _("Date range filter"),
+                _("From: %(date_from)s To: %(date_to)s")
+                % ({"date_from": report.date_from, "date_to": report.date_to}),
+            ],
+            [_("Selected Plan"), report.plan_id.name],
+            [
+                _("Grouped by analytic account"),
+                _("Yes") if report.group_by_analytic_account else _("No"),
+            ],
+        ]
+
+        if report.account_ids:
+            account_codes = self.env[
+                "report.account_analytic_report.trial_balance_analytic"
+            ]._get_account_codes(report.account_ids.ids)
+            report_filters.append(
+                [
+                    _("Accounts Filter"),
+                    account_codes,
+                ]
+            )
+
+        report_filters.append(
+            [
+                _("Limit hierarchy levels"),
+                (
+                    _("Level %s") % (report.hierarchy_level)
+                    if report.limit_hierarchy_level
+                    else _("No limit")
+                ),
+            ]
+        )
+
+        return report_filters
+
+    def _get_col_count_filter_name(self):
+        return 2
+
+    def _get_col_count_filter_value(self):
+        return 3
+
+    def _write_trial_analytic(self, report_values, report_data):
+        for balance in report_values["trial_balance"]:
+            if (
+                report_values["show_hierarchy"]
+                and report_values["limit_hierarchy_level"]
+            ):
+                if report_values["hierarchy_level"] > balance["level"]:
+                    self.write_line_from_dict(balance, report_data)
+            else:
+                self.write_line_from_dict(balance, report_data)
+
+    def _generate_report_content(self, workbook, report, data, report_data):
+        report_values = self._get_values_from_report(report, data)
+        report_data["account_code_list"] = report_values["account_code_list"]
+        report_data["group_by_analytic_account"] = report_values[
+            "group_by_analytic_account"
+        ]
+        report_data["total_amounts"] = report_values["total_amounts"]
+        self.write_array_header(report_data)
+
+        self._write_trial_analytic(report_values, report_data)
+
+        total_rows_by_account_type = self._prepare_total_rows_by_account_type(
+            report_values, report
+        )
+
+        for row in total_rows_by_account_type:
+            self._write_line_with_format(
+                report_data,
+                row,
+                report_data["formats"]["format_acc_type_total"],
+                report_data["formats"]["format_acc_type_amount_total"],
+            )
+
+        total_row = self._prepare_total_row(report_values, report)
+        self._write_line_with_format(
+            report_data,
+            total_row,
+            report_data["formats"]["format_total"],
+            report_data["formats"]["format_amount_total"],
+        )
+
+        if report_values["show_months"]:
+            self.create_page_by_anlytic_accounts(
+                workbook, report, report_data, report_values
+            )
+
+    def _prepare_total_row(self, report_values, report):
+        total_row = {
+            "name": _("Total"),
+            "initial_balance": report_values["total_amounts"]["total_initial_balance"],
+            "ending_balance": report_values["total_amounts"]["total_ending_balance"],
+        }
+        if self._is_report_with_include_both_accounts(report):
+            total_row["accounts"] = {}
+            codes = self.env["account.analytic.account"].search(
+                [("id", "in", report.account_ids.ids)]
+            )
+            for account in codes:
+                total_row["accounts"].update(
+                    {
+                        account.id: report_values["total_amounts"][
+                            "total_period_balance"
+                        ][account.id]
+                    }
+                )
+        else:
+            total_row["balance"] = report_values["total_amounts"][
+                "total_period_balance"
+            ]
+
+        return total_row
+
+    def _prepare_total_rows_by_account_type(self, report_values, report):
+        total_rows = []
+        for acc_type, balances in report_values["totals_by_acc_type"].items():
+            total_row = {
+                "name": acc_type,
+                "initial_balance": balances["total_initial_balance"],
+                "ending_balance": balances["total_ending_balance"],
+            }
+            if self._is_report_with_include_both_accounts(report):
+                total_row["accounts"] = {}
+                codes = self.env["account.analytic.account"].search(
+                    [("id", "in", report.account_ids.ids)]
+                )
+                for account in codes:
+                    if account.id in balances["total_period_balance"].keys():
+                        total_row["accounts"].update(
+                            {account.id: balances["total_period_balance"][account.id]}
+                        )
+            else:
+                total_row["balance"] = balances["total_period_balance"]
+            total_rows.append(total_row)
+        return total_rows
+
+    def _get_values_from_report(self, report, data):
+        res_data = self.env[
+            "report.account_analytic_report.trial_balance_analytic"
+        ]._get_report_values(report, data)
+        return {
+            "show_hierarchy": res_data["show_hierarchy"],
+            "hierarchy_level": res_data["show_hierarchy_level"],
+            "limit_hierarchy_level": res_data["limit_hierarchy_level"],
+            "account_code_list": res_data["account_code_list"],
+            "show_months": res_data["show_months"],
+            "group_by_analytic_account": res_data["group_by_analytic_account"],
+            "trial_balance": res_data["trial_balance"],
+            "plan_field": res_data["plan_field"],
+            "total_amounts": res_data["total_amounts"],
+            "totals_by_acc_type": res_data["totals_by_acc_type"],
+            "account_ids": res_data["account_ids"],
+        }
+
+    def write_line_from_dict(self, line_dict, report_data):
+        if not (
+            report_data["account_code_list"]
+            and not report_data["group_by_analytic_account"]
+        ):
+            return super().write_line_from_dict(line_dict, report_data)
+        else:
+            for col_pos, column in report_data["columns"].items():
+                value = line_dict.get(column["field"], False)
+                cell_type = column.get("type", "string")
+                if cell_type == "string":
+                    if line_dict.get("type", "") == "group_type":
+                        report_data["sheet"].write_string(
+                            report_data["row_pos"],
+                            col_pos,
+                            value or "",
+                            report_data["formats"]["format_bold"],
+                        )
+                    else:
+                        if (
+                            not isinstance(value, str)
+                            and not isinstance(value, bool)
+                            and not isinstance(value, int)
+                        ):
+                            value = value and value.strftime("%d/%m/%Y")
+                        report_data["sheet"].write_string(
+                            report_data["row_pos"], col_pos, value or ""
+                        )
+                elif cell_type == "amount":
+                    if (
+                        line_dict.get("account_group_id", False)
+                        and line_dict["account_group_id"]
+                    ):
+                        cell_format = report_data["formats"]["format_amount_bold"]
+                    else:
+                        cell_format = report_data["formats"]["format_amount"]
+                    if column["field"] == "accounts":
+                        value_to_write = value[column["id"]]
+                        report_data["sheet"].write_number(
+                            report_data["row_pos"],
+                            col_pos,
+                            float(value_to_write),
+                            cell_format,
+                        )
+                    else:
+                        report_data["sheet"].write_number(
+                            report_data["row_pos"], col_pos, float(value), cell_format
+                        )
+                elif cell_type == "amount_currency":
+                    if line_dict.get("currency_name", False):
+                        format_amt = self._get_currency_amt_format_dict(
+                            line_dict, report_data
+                        )
+                        report_data["sheet"].write_number(
+                            report_data["row_pos"], col_pos, float(value), format_amt
+                        )
+                elif cell_type == "currency_name":
+                    report_data["sheet"].write_string(
+                        report_data["row_pos"],
+                        col_pos,
+                        value or "",
+                        report_data["formats"]["format_right"],
+                    )
+                else:
+                    self.write_non_standard_column(cell_type, col_pos, value)
+            report_data["row_pos"] += 1
+
+    def _write_line_with_format(self, report_data, row, str_format, amount_format):
+        for col_pos, column in report_data["columns"].items():
+            value = row.get(column["field"], False)
+            cell_type = column.get("type", "string")
+
+            if cell_type == "amount":
+                value = value if value else 0
+                cell_format = amount_format
+
+                if column["field"] == "accounts":
+                    value = value[column["id"]]
+                report_data["sheet"].write_number(
+                    report_data["row_pos"], col_pos, float(value), cell_format
+                )
+            elif cell_type == "string":
+                value = value if value else ""
+                report_data["sheet"].write_string(
+                    report_data["row_pos"], col_pos, str(value), str_format
+                )
+        report_data["row_pos"] += 1
+
+    def _prepare_data_for_page(self, report):
+        date_from = report.date_from.strftime("%Y-%m-%d")
+        date_to = report.date_to.strftime("%Y-%m-%d")
+        account_id_field = report.plan_id._column_name()
+        company_id = report.company_id.id
+        filters = self._get_report_filters(report)
+
+        return {
+            "date_from": date_from,
+            "date_to": date_to,
+            "account_id_field": account_id_field,
+            "company_id": company_id,
+            "filters": filters,
+        }
+
+    def _create_page_for_account(
+        self,
+        workbook,
+        company_id,
+        report_data,
+        account,
+        filters,
+        date_from,
+        date_to,
+        report_values,
+    ):
+        """
+        Adds a new worksheet for each account using its code as the sheet name.
+        """
+        sheet = workbook.add_worksheet(account.code)
+        report_data["sheet"] = sheet
+        report_data["row_pos"] = 0
+        filters[4][1] = account.code
+
+        self._write_report_title(
+            self._get_report_name(
+                report_data, {"company_id": company_id, "account_code": account.code}
+            ),
+            report_data,
+        )
+        self._write_filters(filters, report_data)
+
+        return sheet
+
+    def _get_report_columns_by_month(self, date_from, date_to, account):
+        res = {
+            0: {"header": _("Code"), "field": "code", "width": 15},
+            1: {"header": _("Account"), "field": "name", "width": 50},
+        }
+
+        date_from = datetime.strptime(date_from, "%Y-%m-%d")
+        date_to = datetime.strptime(date_to, "%Y-%m-%d")
+
+        current_date = date_from
+
+        # Loop through each month between date_from and date_to
+        while current_date <= date_to:
+            month_year = current_date.strftime("%m-%Y")
+
+            # Add a new column for this month
+            res[len(res)] = {
+                "header": month_year,
+                "field": f"{current_date.month}-{current_date.year}",
+                "type": "amount",
+                "width": 14,
+            }
+
+            # Move to the next month
+            current_date += relativedelta(months=1)
+
+        res[len(res)] = {
+            "header": _("Total"),
+            "field": "total",
+            "type": "amount",
+            "width": 14,
+        }
+        return res
+
+    def _get_months_query(
+        self, company_id, account_id_field, account, date_from, date_to, report_values
+    ):
+        return f"""
+            SELECT "account_analytic_line"."general_account_id",
+            date_trunc('month',
+            "account_analytic_line"."date"::timestamp)::date,COUNT(*),
+            SUM("account_analytic_line"."amount")
+            FROM "account_analytic_line"
+            LEFT JOIN "account_account" AS "account_analytic_line__general_account_id"
+            ON ("account_analytic_line"."general_account_id" =
+            "account_analytic_line__general_account_id"."id")
+            LEFT JOIN "res_company" AS
+            "account_analytic_line__general_account_id__company_id"
+            ON ("account_analytic_line__general_account_id"."company_id" =
+            "account_analytic_line__general_account_id__company_id"."id")
+            WHERE (
+                ("account_analytic_line".{account_id_field} = {account.id})
+                AND ("account_analytic_line"."company_id" = {company_id})
+                AND ("account_analytic_line"."date" >= '{date_from}')
+                AND ("account_analytic_line"."date" <= '{date_to}')
+            )
+            GROUP BY "account_analytic_line"."general_account_id",
+            date_trunc('month', "account_analytic_line"."date"::timestamp)::date,
+            "account_analytic_line__general_account_id"."code",
+            "account_analytic_line__general_account_id__company_id"."sequence",
+            "account_analytic_line__general_account_id__company_id"."name"
+            ORDER BY "account_analytic_line__general_account_id"."code",
+            "account_analytic_line__general_account_id__company_id"."sequence",
+            "account_analytic_line__general_account_id__company_id"."name",
+            date_trunc('month', "account_analytic_line"."date"::timestamp)::date ASC
+            """
+
+    def _get_total_acc_type_by_month_query(
+        self, company_id, account_id_field, account, date_from, date_to
+    ):
+        return f"""
+        SELECT
+            aa.account_type,
+            aal.{account_id_field},
+            date_trunc('month', aal.date::timestamp)::date AS month,
+            SUM(amount) AS total_amount
+        FROM
+            account_analytic_line AS aal
+        INNER JOIN
+            account_account AS aa ON aa.id = aal.general_account_id
+        WHERE
+            aal.company_id = {company_id}
+            AND aal.{account_id_field} IS NOT NULL
+            AND aal.date >= '{date_from}'
+            AND aal.date <= '{date_to}'
+            AND aal.{account_id_field} = {account.id}
+        GROUP BY
+            aa.account_type,
+            aal.{account_id_field},
+            date_trunc('month', aal.date::timestamp)::date
+        ORDER BY
+            month ASC;
+        """
+
+    def _get_total_acc_type_by_month(
+        self, company_id, account_id_field, account, date_from, date_to
+    ):
+        self.env.cr.execute(
+            self._get_total_acc_type_by_month_query(
+                company_id, account_id_field, account, date_from, date_to
+            )
+        )
+        total_acc_type_by_months = self.env.cr.fetchall()
+
+        # Maps the accounts with his redebale name
+        account_type_mapping = self.env[
+            "report.account_analytic_report.trial_balance_analytic"
+        ]._get_account_type_mapping()
+
+        for i, acc_type in enumerate(total_acc_type_by_months):
+            total_acc_type_by_months[i] = (
+                account_type_mapping[acc_type[0]],
+                *acc_type[1:],
+            )
+
+        return total_acc_type_by_months
+
+    def _get_total_acc_type_by_months_rows(self, total_acc_type_by_months):
+        total_acc_type_by_months_rows = {}
+        for total_acc_type in total_acc_type_by_months:
+            account_type_name = total_acc_type[0]
+            month_year = f"{total_acc_type[2].month}-{total_acc_type[2].year}"
+            amount = total_acc_type[3]
+
+            if account_type_name not in total_acc_type_by_months_rows:
+                total_acc_type_by_months_rows[account_type_name] = {
+                    "name": account_type_name,
+                    "total": 0,
+                }
+
+            total_acc_type_by_months_rows[account_type_name][month_year] = amount
+            total_acc_type_by_months_rows[account_type_name]["total"] += amount
+
+        return total_acc_type_by_months_rows
+
+    def _get_amounts_and_total_by_analytic_account(self, amounts_data_by_month):
+        amounts_by_month = {}
+        total_row = {"code": _("Total"), "total": 0}
+        for amount_data in amounts_data_by_month:
+            account_account = self.env["account.account"].browse(amount_data[0])
+            key = f"{amount_data[1].month}-{amount_data[1].year}"
+            amount = amount_data[3]
+
+            total_row[key] = total_row.get(key, 0) + amount
+            total_row["total"] += amount
+
+            if account_account.id not in amounts_by_month:
+                amounts_by_month[account_account.id] = {
+                    "code": account_account.code,
+                    "name": account_account.name,
+                    "total": 0,
+                }
+
+            amounts_by_month[account_account.id][key] = amount
+            amounts_by_month[account_account.id]["total"] += amount
+        return amounts_by_month, total_row
+
+    def _write_amount_by_month(self, amounts_by_month, report_data):
+        for amount_by_month in amounts_by_month.values():
+            if isinstance(amount_by_month, dict):
+                self.write_line_from_dict(amount_by_month, report_data)
+
+    def _write_totals_by_acc_type(self, total_acc_type_by_months, report_data):
+        total_acc_type_month_row = self._get_total_acc_type_by_months_rows(
+            total_acc_type_by_months
+        )
+        # Writes total by account type
+        for row in total_acc_type_month_row.values():
+            self._write_line_with_format(
+                report_data,
+                row,
+                report_data["formats"]["format_acc_type_total"],
+                report_data["formats"]["format_acc_type_amount_total"],
+            )
+
+    def _write_total_row(self, total_row, report_data):
+        # Writes total row
+        self._write_line_with_format(
+            report_data,
+            total_row,
+            report_data["formats"]["format_total"],
+            report_data["formats"]["format_amount_total"],
+        )
+
+    def create_page_by_anlytic_accounts(
+        self, workbook, report, report_data, report_values
+    ):
+        report_data_values = self._prepare_data_for_page(report)
+        date_from = report_data_values["date_from"]
+        date_to = report_data_values["date_to"]
+        account_id_field = report_data_values["account_id_field"]
+        filters = report_data_values["filters"]
+        company_id = report_data_values["company_id"]
+        for account in report.account_ids:
+            self._create_page_for_account(
+                workbook,
+                company_id,
+                report_data,
+                account,
+                filters,
+                date_from,
+                date_to,
+                report_values,
+            )
+
+            query = self._get_months_query(
+                company_id, account_id_field, account, date_from, date_to, report_values
+            )
+
+            self.env.cr.execute(query)
+
+            amounts_data_by_month = self.env.cr.fetchall()
+
+            report_data["columns"] = self._get_report_columns_by_month(
+                date_from, date_to, account
+            )
+
+            self.write_array_header(report_data)
+            self._set_column_width(report_data)
+
+            (
+                amounts_by_month,
+                total_row,
+            ) = self._get_amounts_and_total_by_analytic_account(amounts_data_by_month)
+            amounts_by_month.update({"account_id": account.id})
+            self._write_amount_by_month(amounts_by_month, report_data)
+
+            total_acc_type_by_months = self._get_total_acc_type_by_month(
+                company_id, account_id_field, account, date_from, date_to
+            )
+
+            self._write_totals_by_acc_type(total_acc_type_by_months, report_data)
+
+            self._write_total_row(total_row, report_data)
diff --git a/account_analytic_report/reports.xml b/account_analytic_report/reports.xml
new file mode 100644
index 00000000000..df50cda93d6
--- /dev/null
+++ b/account_analytic_report/reports.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<odoo>
+        <!-- Trial Balance -->
+        <record
+        id="action_report_analytic_trial_balance_qweb"
+        model="ir.actions.report"
+    >
+            <field name="name">Trial Analytic Balance</field>
+            <field name="model">ac.trial.balance.report.wizard</field>
+            <field name="report_type">qweb-pdf</field>
+            <field
+            name="report_name"
+        >account_analytic_report.trial_balance_analytic</field>
+            <field
+            name="report_file"
+        >account_analytic_report.trial_balance_analytic</field>
+            <field
+            name="paperformat_id"
+            ref="account_financial_report.report_qweb_paperformat"
+        />
+        </record>
+        <record
+        id="action_report_analytic_trial_balance_html"
+        model="ir.actions.report"
+    >
+            <field name="name">Trial Analytic Balance</field>
+            <field name="model">ac.trial.balance.report.wizard</field>
+            <field name="report_type">qweb-html</field>
+            <field
+            name="report_name"
+        >account_analytic_report.trial_balance_analytic</field>
+            <field
+            name="report_file"
+        >account_analytic_report.trial_balance_analytic</field>
+        </record>
+        <record
+        id="action_report_analytic_trial_balance_xlsx"
+        model="ir.actions.report"
+    >
+            <field name="name">Trial Balance XLSX</field>
+            <field name="model">ac.trial.balance.report.wizard</field>
+            <field name="type">ir.actions.report</field>
+            <field name="report_name">a_f_r.report_trial_balance_analytic_xlsx</field>
+            <field name="report_type">xlsx</field>
+            <field name="report_file">report_trial_balance_analytic</field>
+        </record>
+</odoo>
diff --git a/account_analytic_report/security/ir.model.access.csv b/account_analytic_report/security/ir.model.access.csv
new file mode 100644
index 00000000000..e16db37c534
--- /dev/null
+++ b/account_analytic_report/security/ir.model.access.csv
@@ -0,0 +1,2 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_trial_balance_analytic_report_wizard,access_trial_balance_analytic_report_wizard,model_ac_trial_balance_report_wizard,base.group_user,1,1,1,1
diff --git a/account_analytic_report/static/description/icon.png b/account_analytic_report/static/description/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..0a917e513df1e26aee5a41b73430e102f591fd07
GIT binary patch
literal 36663
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
Y0oraPMwFFf`v3p{07*qoM6N<$f+G9j1poj5

literal 0
HcmV?d00001

diff --git a/account_analytic_report/static/description/index.html b/account_analytic_report/static/description/index.html
new file mode 100644
index 00000000000..45f2b3c7a97
--- /dev/null
+++ b/account_analytic_report/static/description/index.html
@@ -0,0 +1,483 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
+<title>Account Analytic Reports</title>
+<style type="text/css">
+
+/*
+:Author: David Goodger (goodger@python.org)
+:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
+:Copyright: This stylesheet has been placed in the public domain.
+
+Default cascading style sheet for the HTML output of Docutils.
+Despite the name, some widely supported CSS2 features are used.
+
+See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
+customize this style sheet.
+*/
+
+/* used to remove borders from tables and images */
+.borderless, table.borderless td, table.borderless th {
+  border: 0 }
+
+table.borderless td, table.borderless th {
+  /* Override padding for "table.docutils td" with "! important".
+     The right padding separates the table cells. */
+  padding: 0 0.5em 0 0 ! important }
+
+.first {
+  /* Override more specific margin styles with "! important". */
+  margin-top: 0 ! important }
+
+.last, .with-subtitle {
+  margin-bottom: 0 ! important }
+
+.hidden {
+  display: none }
+
+.subscript {
+  vertical-align: sub;
+  font-size: smaller }
+
+.superscript {
+  vertical-align: super;
+  font-size: smaller }
+
+a.toc-backref {
+  text-decoration: none ;
+  color: black }
+
+blockquote.epigraph {
+  margin: 2em 5em ; }
+
+dl.docutils dd {
+  margin-bottom: 0.5em }
+
+object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
+  overflow: hidden;
+}
+
+/* Uncomment (and remove this text!) to get bold-faced definition list terms
+dl.docutils dt {
+  font-weight: bold }
+*/
+
+div.abstract {
+  margin: 2em 5em }
+
+div.abstract p.topic-title {
+  font-weight: bold ;
+  text-align: center }
+
+div.admonition, div.attention, div.caution, div.danger, div.error,
+div.hint, div.important, div.note, div.tip, div.warning {
+  margin: 2em ;
+  border: medium outset ;
+  padding: 1em }
+
+div.admonition p.admonition-title, div.hint p.admonition-title,
+div.important p.admonition-title, div.note p.admonition-title,
+div.tip p.admonition-title {
+  font-weight: bold ;
+  font-family: sans-serif }
+
+div.attention p.admonition-title, div.caution p.admonition-title,
+div.danger p.admonition-title, div.error p.admonition-title,
+div.warning p.admonition-title, .code .error {
+  color: red ;
+  font-weight: bold ;
+  font-family: sans-serif }
+
+/* Uncomment (and remove this text!) to get reduced vertical space in
+   compound paragraphs.
+div.compound .compound-first, div.compound .compound-middle {
+  margin-bottom: 0.5em }
+
+div.compound .compound-last, div.compound .compound-middle {
+  margin-top: 0.5em }
+*/
+
+div.dedication {
+  margin: 2em 5em ;
+  text-align: center ;
+  font-style: italic }
+
+div.dedication p.topic-title {
+  font-weight: bold ;
+  font-style: normal }
+
+div.figure {
+  margin-left: 2em ;
+  margin-right: 2em }
+
+div.footer, div.header {
+  clear: both;
+  font-size: smaller }
+
+div.line-block {
+  display: block ;
+  margin-top: 1em ;
+  margin-bottom: 1em }
+
+div.line-block div.line-block {
+  margin-top: 0 ;
+  margin-bottom: 0 ;
+  margin-left: 1.5em }
+
+div.sidebar {
+  margin: 0 0 0.5em 1em ;
+  border: medium outset ;
+  padding: 1em ;
+  background-color: #ffffee ;
+  width: 40% ;
+  float: right ;
+  clear: right }
+
+div.sidebar p.rubric {
+  font-family: sans-serif ;
+  font-size: medium }
+
+div.system-messages {
+  margin: 5em }
+
+div.system-messages h1 {
+  color: red }
+
+div.system-message {
+  border: medium outset ;
+  padding: 1em }
+
+div.system-message p.system-message-title {
+  color: red ;
+  font-weight: bold }
+
+div.topic {
+  margin: 2em }
+
+h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
+h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
+  margin-top: 0.4em }
+
+h1.title {
+  text-align: center }
+
+h2.subtitle {
+  text-align: center }
+
+hr.docutils {
+  width: 75% }
+
+img.align-left, .figure.align-left, object.align-left, table.align-left {
+  clear: left ;
+  float: left ;
+  margin-right: 1em }
+
+img.align-right, .figure.align-right, object.align-right, table.align-right {
+  clear: right ;
+  float: right ;
+  margin-left: 1em }
+
+img.align-center, .figure.align-center, object.align-center {
+  display: block;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+table.align-center {
+  margin-left: auto;
+  margin-right: auto;
+}
+
+.align-left {
+  text-align: left }
+
+.align-center {
+  clear: both ;
+  text-align: center }
+
+.align-right {
+  text-align: right }
+
+/* reset inner alignment in figures */
+div.align-right {
+  text-align: inherit }
+
+/* div.align-center * { */
+/*   text-align: left } */
+
+.align-top    {
+  vertical-align: top }
+
+.align-middle {
+  vertical-align: middle }
+
+.align-bottom {
+  vertical-align: bottom }
+
+ol.simple, ul.simple {
+  margin-bottom: 1em }
+
+ol.arabic {
+  list-style: decimal }
+
+ol.loweralpha {
+  list-style: lower-alpha }
+
+ol.upperalpha {
+  list-style: upper-alpha }
+
+ol.lowerroman {
+  list-style: lower-roman }
+
+ol.upperroman {
+  list-style: upper-roman }
+
+p.attribution {
+  text-align: right ;
+  margin-left: 50% }
+
+p.caption {
+  font-style: italic }
+
+p.credits {
+  font-style: italic ;
+  font-size: smaller }
+
+p.label {
+  white-space: nowrap }
+
+p.rubric {
+  font-weight: bold ;
+  font-size: larger ;
+  color: maroon ;
+  text-align: center }
+
+p.sidebar-title {
+  font-family: sans-serif ;
+  font-weight: bold ;
+  font-size: larger }
+
+p.sidebar-subtitle {
+  font-family: sans-serif ;
+  font-weight: bold }
+
+p.topic-title {
+  font-weight: bold }
+
+pre.address {
+  margin-bottom: 0 ;
+  margin-top: 0 ;
+  font: inherit }
+
+pre.literal-block, pre.doctest-block, pre.math, pre.code {
+  margin-left: 2em ;
+  margin-right: 2em }
+
+pre.code .ln { color: gray; } /* line numbers */
+pre.code, code { background-color: #eeeeee }
+pre.code .comment, code .comment { color: #5C6576 }
+pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
+pre.code .literal.string, code .literal.string { color: #0C5404 }
+pre.code .name.builtin, code .name.builtin { color: #352B84 }
+pre.code .deleted, code .deleted { background-color: #DEB0A1}
+pre.code .inserted, code .inserted { background-color: #A3D289}
+
+span.classifier {
+  font-family: sans-serif ;
+  font-style: oblique }
+
+span.classifier-delimiter {
+  font-family: sans-serif ;
+  font-weight: bold }
+
+span.interpreted {
+  font-family: sans-serif }
+
+span.option {
+  white-space: nowrap }
+
+span.pre {
+  white-space: pre }
+
+span.problematic, pre.problematic {
+  color: red }
+
+span.section-subtitle {
+  /* font-size relative to parent (h1..h6 element) */
+  font-size: 80% }
+
+table.citation {
+  border-left: solid 1px gray;
+  margin-left: 1px }
+
+table.docinfo {
+  margin: 2em 4em }
+
+table.docutils {
+  margin-top: 0.5em ;
+  margin-bottom: 0.5em }
+
+table.footnote {
+  border-left: solid 1px black;
+  margin-left: 1px }
+
+table.docutils td, table.docutils th,
+table.docinfo td, table.docinfo th {
+  padding-left: 0.5em ;
+  padding-right: 0.5em ;
+  vertical-align: top }
+
+table.docutils th.field-name, table.docinfo th.docinfo-name {
+  font-weight: bold ;
+  text-align: left ;
+  white-space: nowrap ;
+  padding-left: 0 }
+
+/* "booktabs" style (no vertical lines) */
+table.docutils.booktabs {
+  border: 0px;
+  border-top: 2px solid;
+  border-bottom: 2px solid;
+  border-collapse: collapse;
+}
+table.docutils.booktabs * {
+  border: 0px;
+}
+table.docutils.booktabs th {
+  border-bottom: thin solid;
+  text-align: left;
+}
+
+h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
+h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
+  font-size: 100% }
+
+ul.auto-toc {
+  list-style-type: none }
+
+</style>
+</head>
+<body>
+<div class="document" id="account-analytic-reports">
+<h1 class="title">Account Analytic Reports</h1>
+
+<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!! This file is generated by oca-gen-addon-readme !!
+!! changes will be overwritten.                   !!
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!! source digest: sha256:e3b2f8d263dd282038c6d240451ddf65612a4d8dfbf754af136900aa97285230
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
+<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/account-financial-reporting/tree/17.0/account_analytic_report"><img alt="OCA/account-financial-reporting" src="https://img.shields.io/badge/github-OCA%2Faccount--financial--reporting-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/account-financial-reporting-17-0/account-financial-reporting-17-0-account_analytic_report"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/account-financial-reporting&amp;target_branch=17.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
+<p>This module introduces an analytic report that provides an intuitive way
+to view and analyze analytic balances. It simplifies the process,
+offering enhanced insights and making it easier to leverage this
+information effectively.</p>
+<p><strong>Table of contents</strong></p>
+<div class="contents local topic" id="contents">
+<ul class="simple">
+<li><a class="reference internal" href="#usage" id="toc-entry-1">Usage</a></li>
+<li><a class="reference internal" href="#bug-tracker" id="toc-entry-2">Bug Tracker</a></li>
+<li><a class="reference internal" href="#credits" id="toc-entry-3">Credits</a><ul>
+<li><a class="reference internal" href="#authors" id="toc-entry-4">Authors</a></li>
+<li><a class="reference internal" href="#contributors" id="toc-entry-5">Contributors</a></li>
+<li><a class="reference internal" href="#maintainers" id="toc-entry-6">Maintainers</a></li>
+</ul>
+</li>
+</ul>
+</div>
+<div class="section" id="usage">
+<h1><a class="toc-backref" href="#toc-entry-1">Usage</a></h1>
+<p>Using this module is straightforward. Follow these steps:</p>
+<ul>
+<li><div class="first line-block">
+<div class="line"><strong>Navigate to the Report</strong>:</div>
+<div class="line">Go to <strong>Invoicing</strong> -&gt; <strong>Reporting</strong> -&gt; <strong>Analytic Trial Balance</strong>.</div>
+</div>
+</li>
+<li><div class="first line-block">
+<div class="line"><strong>Customize the Report with Filters</strong>:</div>
+<div class="line">Adjust the report using the available options:</div>
+</div>
+<ul>
+<li><div class="first line-block">
+<div class="line"><strong>Group by Analytic Account</strong>:</div>
+<div class="line">Groups the results by analytic accounts instead of financial
+accounts.</div>
+</div>
+</li>
+<li><div class="first line-block">
+<div class="line"><strong>Show Hierarchy and Limit Hierarchy Level</strong>:</div>
+<div class="line">Displays the amounts split by the hierarchy levels of financial
+accounts.</div>
+</div>
+</li>
+<li><div class="first line-block">
+<div class="line"><strong>Filter Accounts</strong>:</div>
+<div class="line">When used independently (without grouping by analytic accounts
+or showing hierarchy), the results will be split by both
+financial accounts.</div>
+<div class="line"><strong>Example</strong>: Filtering by accounts <em>Test 1</em> and <em>Test 2</em>:</div>
+</div>
+<pre class="code text literal-block">
+        | Initial Balance | Test 1   | Test 2   | Ending Balance
+400000  |        0        | $3600    | $2400    |     $6000
+</pre>
+</li>
+<li><div class="first line-block">
+<div class="line"><strong>Show Months</strong> (Excel export only):</div>
+<div class="line">Enabled when filtering accounts without grouping by analytic
+accounts or showing hierarchy. It generates a separate sheet in
+the Excel file for each filtered account, detailing the amounts
+by month within the selected date range.</div>
+</div>
+</li>
+</ul>
+</li>
+</ul>
+</div>
+<div class="section" id="bug-tracker">
+<h1><a class="toc-backref" href="#toc-entry-2">Bug Tracker</a></h1>
+<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/account-financial-reporting/issues">GitHub Issues</a>.
+In case of trouble, please check there if your issue has already been reported.
+If you spotted it first, help us to smash it by providing a detailed and welcomed
+<a class="reference external" href="https://github.com/OCA/account-financial-reporting/issues/new?body=module:%20account_analytic_report%0Aversion:%2017.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
+<p>Do not contact contributors directly about support or help with technical issues.</p>
+</div>
+<div class="section" id="credits">
+<h1><a class="toc-backref" href="#toc-entry-3">Credits</a></h1>
+<div class="section" id="authors">
+<h2><a class="toc-backref" href="#toc-entry-4">Authors</a></h2>
+<ul class="simple">
+<li>APSL-Nagarro</li>
+</ul>
+</div>
+<div class="section" id="contributors">
+<h2><a class="toc-backref" href="#toc-entry-5">Contributors</a></h2>
+<ul class="simple">
+<li><a class="reference external" href="https://apsl.tech">APSL-Nagarro</a>:<ul>
+<li>Bernat Obrador</li>
+<li>Miquel Alzanillas</li>
+</ul>
+</li>
+</ul>
+</div>
+<div class="section" id="maintainers">
+<h2><a class="toc-backref" href="#toc-entry-6">Maintainers</a></h2>
+<p>This module is maintained by the OCA.</p>
+<a class="reference external image-reference" href="https://odoo-community.org">
+<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
+</a>
+<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
+mission is to support the collaborative development of Odoo features and
+promote its widespread use.</p>
+<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainers</a>:</p>
+<p><a class="reference external image-reference" href="https://github.com/BernatObrador"><img alt="BernatObrador" src="https://github.com/BernatObrador.png?size=40px" /></a> <a class="reference external image-reference" href="https://github.com/miquelalzanillas"><img alt="miquelalzanillas" src="https://github.com/miquelalzanillas.png?size=40px" /></a></p>
+<p>This module is part of the <a class="reference external" href="https://github.com/OCA/account-financial-reporting/tree/17.0/account_analytic_report">OCA/account-financial-reporting</a> project on GitHub.</p>
+<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
+</div>
+</div>
+</div>
+</body>
+</html>
diff --git a/account_analytic_report/tests/__init__.py b/account_analytic_report/tests/__init__.py
new file mode 100644
index 00000000000..d2dd961ed87
--- /dev/null
+++ b/account_analytic_report/tests/__init__.py
@@ -0,0 +1 @@
+from . import test_trial_analytic_balance
diff --git a/account_analytic_report/tests/test_trial_analytic_balance.py b/account_analytic_report/tests/test_trial_analytic_balance.py
new file mode 100644
index 00000000000..c1bac8594c6
--- /dev/null
+++ b/account_analytic_report/tests/test_trial_analytic_balance.py
@@ -0,0 +1,374 @@
+# Copyright 2024 (APSL - Nagarro) Bernat Obrador
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+
+from odoo.tests import tagged
+from odoo.tests.common import TransactionCase
+
+
+@tagged("post_install", "-at_install")
+class TestTrialAnalyticBalanceReport(TransactionCase):
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+
+        cls.account_type_map = cls.env[
+            "report.account_analytic_report.trial_balance_analytic"
+        ]._get_account_type_mapping()
+
+        cls.analytic_plan_1 = cls.env["account.analytic.plan"].create(
+            {
+                "name": "Plan 1",
+            }
+        )
+        account_group = cls.env["account.group"]
+        cls.group5 = account_group.create({"code_prefix_start": "5", "name": "Group 5"})
+        cls.group4 = account_group.create({"code_prefix_start": "4", "name": "Group 4"})
+        cls.group42 = account_group.create(
+            {"code_prefix_start": "42", "name": "Group 4", "parent_id": cls.group4.id}
+        )
+
+        cls.expense_account = cls.env["account.account"].create(
+            {
+                "name": "Expenses Account",
+                "code": "5000",
+                "account_type": "expense",
+                "company_id": cls.env.user.company_id.id,
+                "group_id": cls.group5.id,
+            }
+        )
+        cls.income_account = cls.env["account.account"].create(
+            {
+                "name": "Income Account",
+                "code": "4000",
+                "account_type": "income",
+                "company_id": cls.env.user.company_id.id,
+                "group_id": cls.group4.id,
+            }
+        )
+        cls.income_account_2 = cls.env["account.account"].create(
+            {
+                "name": "Income Account 2",
+                "code": "4200",
+                "account_type": "income",
+                "company_id": cls.env.user.company_id.id,
+                "group_id": cls.group42.id,
+            }
+        )
+
+        cls.aaa_1 = cls.env["account.analytic.account"].create(
+            {"name": "Account 1", "plan_id": cls.analytic_plan_1.id}
+        )
+
+        cls.aaa_2 = cls.env["account.analytic.account"].create(
+            {"name": "Account 2", "plan_id": cls.analytic_plan_1.id}
+        )
+        cls.account_field = cls.analytic_plan_1._column_name()
+        cls.aal_1 = cls.env["account.analytic.line"].create(
+            {
+                "name": "aal 1",
+                cls.account_field: cls.aaa_1.id,
+                "general_account_id": cls.expense_account.id,
+                "amount": -150.0,
+                "date": "2024-09-30",
+            }
+        )
+        cls.aal_2 = cls.env["account.analytic.line"].create(
+            {
+                "name": "aal 1",
+                cls.account_field: cls.aaa_2.id,
+                "general_account_id": cls.expense_account.id,
+                "amount": -50,
+                "date": "2024-11-30",
+            }
+        )
+        cls.aal_3 = cls.env["account.analytic.line"].create(
+            {
+                "name": "aal 1",
+                cls.account_field: cls.aaa_2.id,
+                "general_account_id": cls.income_account.id,
+                "amount": 250,
+                "date": "2024-12-31",
+            }
+        )
+
+        cls.date_from = "2024-10-01"
+        cls.date_to = "2024-12-31"
+        cls.fy_start_date = "2024-01-01"
+
+    def _get_report_lines(
+        self, account_ids=False, show_hierarchy=False, group_by_analytic_account=False
+    ):
+        company = self.env.user.company_id
+        trial_analytic_balance = self.env["ac.trial.balance.report.wizard"].create(
+            {
+                "date_from": self.date_from,
+                "date_to": self.date_to,
+                "show_hierarchy": show_hierarchy,
+                "company_id": company.id,
+                "account_ids": account_ids,
+                "fy_start_date": self.fy_start_date,
+                "plan_id": self.analytic_plan_1.id,
+                "group_by_analytic_account": group_by_analytic_account,
+            }
+        )
+        data = trial_analytic_balance._prepare_report_trial_balance_analytic()
+        res_data = self.env[
+            "report.account_analytic_report.trial_balance_analytic"
+        ]._get_report_values(trial_analytic_balance, data)
+        return res_data
+
+    def _accounts_in_report(self, trial_balance):
+        accounts_in_report = []
+        for account in trial_balance:
+            accounts_in_report.append(account["id"])
+
+        return accounts_in_report
+
+    def _check_total_amounts_by_acc_type(
+        self, totals_by_acc_type, include_both_accounts=False
+    ):
+        for type_name, total_by_acc_type in totals_by_acc_type.items():
+            if type_name == self.account_type_map["expense"]:
+                self.assertTrue(total_by_acc_type["total_initial_balance"] == -150)
+                self.assertTrue(total_by_acc_type["total_ending_balance"] == -200)
+
+                if include_both_accounts:
+                    for aaa_id, amount in total_by_acc_type[
+                        "total_period_balance"
+                    ].items():
+                        if aaa_id == self.aaa_2.id:
+                            self.assertEqual(amount, -50)
+                        else:
+                            self.assertEqual(amount, 0)
+                else:
+                    self.assertTrue(total_by_acc_type["total_period_balance"] == -50)
+            elif type_name == self.account_type_map["income"]:
+                self.assertTrue(total_by_acc_type["total_initial_balance"] == 0)
+                self.assertTrue(total_by_acc_type["total_ending_balance"] == 250)
+                if include_both_accounts:
+                    for aaa_id, amount in total_by_acc_type[
+                        "total_period_balance"
+                    ].items():
+                        if aaa_id == self.aaa_2.id:
+                            self.assertEqual(amount, 250)
+                        else:
+                            self.assertEqual(amount, 0)
+                else:
+                    self.assertTrue(total_by_acc_type["total_period_balance"] == 250)
+
+    def test01_trial_analytic_balance(self):
+        res_data = self._get_report_lines()
+        trial_analytic_balance = res_data["trial_balance"]
+        accounts_in_report = self._accounts_in_report(trial_analytic_balance)
+        totals = res_data["total_amounts"]
+
+        self.assertTrue(len(accounts_in_report) == 2)
+        self.assertTrue(self.expense_account.id in accounts_in_report)
+        self.assertTrue(self.income_account.id in accounts_in_report)
+        self.assertFalse(self.income_account_2.id in accounts_in_report)
+
+        # Checks total amounts by account type
+        self._check_total_amounts_by_acc_type(res_data["totals_by_acc_type"])
+
+        # Checks total amounts
+        self.assertEqual(totals["total_initial_balance"], -150)
+        self.assertEqual(totals["total_period_balance"], -50 + 250)
+        self.assertEqual(
+            totals["total_ending_balance"],
+            -150 + -50 + 250,
+        )
+
+        # Check balances for every account
+        for account in trial_analytic_balance:
+            if account["id"] == self.income_account.id:
+                self.assertEqual(account["initial_balance"], 0)
+                self.assertEqual(account["balance"], 250)
+                self.assertEqual(account["ending_balance"], 250)
+            else:
+                self.assertEqual(account["initial_balance"], -150)
+                self.assertEqual(account["balance"], -50)
+                self.assertEqual(account["ending_balance"], -150 + -50)
+
+    def test02_trial_analytic_balance_with_splited_accounts(self):
+        res_data = self._get_report_lines(account_ids=[self.aaa_1.id, self.aaa_2.id])
+        trial_analytic_balance = res_data["trial_balance"]
+        totals = res_data["total_amounts"]
+
+        accounts_in_report = self._accounts_in_report(trial_analytic_balance)
+        self.assertTrue(len(accounts_in_report) == 2)
+        self.assertTrue(self.expense_account.id in accounts_in_report)
+        self.assertTrue(self.income_account.id in accounts_in_report)
+        self.assertFalse(self.income_account_2.id in accounts_in_report)
+
+        self.assertTrue(self.aaa_1.name in res_data["account_code_list"])
+        self.assertTrue(self.aaa_2.name in res_data["account_code_list"])
+
+        # Checks total amounts by account type
+        self._check_total_amounts_by_acc_type(
+            res_data["totals_by_acc_type"], include_both_accounts=True
+        )
+
+        # Checks total amounts
+        self.assertEqual(totals["total_initial_balance"], -150)
+        self.assertEqual(
+            totals["total_ending_balance"],
+            -150 + -50 + 250,
+        )
+
+        for aaa_id, amount in totals["total_period_balance"].items():
+            if aaa_id == self.aaa_2.id:
+                self.assertEqual(amount, -50 + 250)
+            else:
+                self.assertEqual(amount, 0)
+
+        # Check balances for every account
+        for account in trial_analytic_balance:
+            if account["id"] == self.income_account.id:
+                self.assertEqual(account["initial_balance"], 0)
+                self.assertEqual(account["ending_balance"], 250)
+                for aaa_id, amount in account["accounts"].items():
+                    if aaa_id == self.aaa_2.id:
+                        self.assertEqual(amount, 250)
+                    else:
+                        self.assertEqual(amount, 0)
+            else:
+                self.assertEqual(account["initial_balance"], -150)
+                self.assertEqual(account["ending_balance"], -150 + -50)
+                for aaa_id, amount in account["accounts"].items():
+                    if aaa_id == self.aaa_2.id:
+                        self.assertEqual(amount, -50)
+                    else:
+                        self.assertEqual(amount, 0)
+
+    def test03_trial_analytic_balance_gruped_by_analytic_account(self):
+        res_data = self._get_report_lines(group_by_analytic_account=True)
+        trial_analytic_balance = res_data["trial_balance"]
+        totals = res_data["total_amounts"]
+        accounts_in_report = self._accounts_in_report(trial_analytic_balance)
+
+        self.assertTrue(len(accounts_in_report) == 2)
+        self.assertTrue(self.aaa_1.id in accounts_in_report)
+        self.assertTrue(self.aaa_2.id in accounts_in_report)
+
+        self._check_total_amounts_by_acc_type(res_data["totals_by_acc_type"])
+        self.assertEqual(totals["total_initial_balance"], -150)
+        self.assertEqual(totals["total_period_balance"], -50 + 250)
+        self.assertEqual(
+            totals["total_ending_balance"],
+            -150 + -50 + 250,
+        )
+
+        for account in trial_analytic_balance:
+            if account["id"] == self.aaa_1.id:
+                self.assertEqual(account["initial_balance"], -150)
+                self.assertEqual(account["balance"], 0)
+                self.assertEqual(account["ending_balance"], -150)
+            else:
+                self.assertEqual(account["initial_balance"], 0)
+                self.assertEqual(account["balance"], -50 + 250)
+                self.assertEqual(account["ending_balance"], -50 + 250)
+
+    def test04_trial_analytic_balance_gruped_by_analytic_account_filtered(self):
+        res_data = self._get_report_lines(
+            group_by_analytic_account=True, account_ids=[self.aaa_1.id]
+        )
+        trial_analytic_balance = res_data["trial_balance"]
+        totals = res_data["total_amounts"]
+        accounts_in_report = self._accounts_in_report(trial_analytic_balance)
+
+        self.assertTrue(len(accounts_in_report) == 1)
+        self.assertTrue(self.aaa_1.id in accounts_in_report)
+        self.assertFalse(self.aaa_2.id in accounts_in_report)
+
+        for type_name, total_by_acc_type in res_data["totals_by_acc_type"].items():
+            if type_name == self.account_type_map["expense"]:
+                self.assertEqual(total_by_acc_type["total_initial_balance"], -150)
+                self.assertEqual(total_by_acc_type["total_period_balance"], 0)
+                self.assertEqual(total_by_acc_type["total_ending_balance"], -150)
+        self.assertEqual(totals["total_initial_balance"], -150)
+        self.assertEqual(totals["total_period_balance"], 0)
+        self.assertEqual(totals["total_ending_balance"], -150)
+
+        for account in trial_analytic_balance:
+            if account["id"] == self.aaa_1.id:
+                self.assertEqual(account["initial_balance"], -150)
+                self.assertEqual(account["balance"], 0)
+                self.assertEqual(account["ending_balance"], -150)
+
+    def test05_trial_analytic_balance_show_hirarchy(self):
+        self.env["account.analytic.line"].create(
+            {
+                "name": "aal 1",
+                self.account_field: self.aaa_2.id,
+                "general_account_id": self.income_account_2.id,
+                "amount": 300,
+                "date": "2024-12-31",
+            }
+        )
+        res_data = self._get_report_lines(show_hierarchy=True)
+        trial_analytic_balance = res_data["trial_balance"]
+        accounts_in_report = self._accounts_in_report(trial_analytic_balance)
+        totals = res_data["total_amounts"]
+
+        self.assertTrue(len(accounts_in_report) == 6)
+        self.assertTrue(self.expense_account.id in accounts_in_report)
+        self.assertTrue(self.income_account.id in accounts_in_report)
+        self.assertTrue(self.income_account_2.id in accounts_in_report)
+
+        # Checks total amounts by account type
+        for type_name, total_by_acc_type in res_data["totals_by_acc_type"].items():
+            if type_name == self.account_type_map["expense"]:
+                self.assertTrue(total_by_acc_type["total_initial_balance"] == -150)
+                self.assertTrue(total_by_acc_type["total_period_balance"] == -50)
+                self.assertTrue(total_by_acc_type["total_ending_balance"] == -200)
+            elif type_name == self.account_type_map["income"]:
+                self.assertTrue(total_by_acc_type["total_initial_balance"] == 0)
+                self.assertTrue(total_by_acc_type["total_period_balance"] == 250 + 300)
+                self.assertTrue(total_by_acc_type["total_ending_balance"] == 250 + 300)
+
+        # Checks total amounts
+        self.assertEqual(totals["total_initial_balance"], -150)
+        self.assertEqual(totals["total_period_balance"], -50 + 250 + 300)
+        self.assertEqual(totals["total_ending_balance"], -150 + -50 + 250 + 300)
+
+        # Check balances for every account
+        for account in trial_analytic_balance:
+            if account["type"] == "group_type":
+                if account["code"] == "4":
+                    self.assertEqual(account["name"], "Group 4")
+                    self.assertEqual(account["complete_code"], "4")
+                    self.assertEqual(account["level"], 0)
+                    self.assertTrue(self.income_account.id in account["account_ids"])
+                    self.assertEqual(account["initial_balance"], 0)
+                    self.assertEqual(account["balance"], 250 + 300)
+                    self.assertEqual(account["ending_balance"], 250 + 300)
+                if account["code"] == "42":
+                    self.assertEqual(account["name"], "Group 4")
+                    self.assertEqual(account["complete_code"], "4/42")
+                    self.assertEqual(account["level"], 1)
+                    self.assertTrue(self.income_account_2.id in account["account_ids"])
+                    self.assertEqual(account["initial_balance"], 0)
+                    self.assertEqual(account["balance"], 300)
+                    self.assertEqual(account["ending_balance"], 300)
+                if account["code"] == "5":
+                    self.assertEqual(account["name"], "Group 5")
+                    self.assertEqual(account["complete_code"], "5")
+                    self.assertEqual(account["level"], 0)
+                    self.assertTrue(self.expense_account.id in account["account_ids"])
+                    self.assertEqual(account["initial_balance"], -150)
+                    self.assertEqual(account["balance"], -50)
+                    self.assertEqual(account["ending_balance"], -150 + -50)
+            else:
+                if account["id"] == self.income_account.id:
+                    self.assertEqual(account["initial_balance"], 0)
+                    self.assertEqual(account["balance"], 250)
+                    self.assertEqual(account["ending_balance"], 250)
+                elif account["id"] == self.income_account_2.id:
+                    self.assertEqual(account["initial_balance"], 0)
+                    self.assertEqual(account["balance"], 300)
+                    self.assertEqual(account["ending_balance"], 300)
+                else:
+                    self.assertEqual(account["initial_balance"], -150)
+                    self.assertEqual(account["balance"], -50)
+                    self.assertEqual(account["ending_balance"], -150 + -50)
diff --git a/account_analytic_report/views/account_analytic_line.xml b/account_analytic_report/views/account_analytic_line.xml
new file mode 100644
index 00000000000..08ae8458450
--- /dev/null
+++ b/account_analytic_report/views/account_analytic_line.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<odoo>
+<!-- # Copyright 2024 (APSL - Nagarro) Bernat Obrador
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
+    <!--
+        This view modification sets the priority of the account.analytic.line tree view
+        to 1, ensuring that this view is used instead of the timesheet view when clicking
+        on the amounts in the report. This prioritization helps in displaying the correct
+        tree view for analytic lines rather than the timesheet-related view.
+    -->
+    <record id="analytic.view_account_analytic_line_tree" model="ir.ui.view">
+        <field name="priority">1</field>
+    </record>
+</odoo>
diff --git a/account_analytic_report/views/report_trial_balance_analytic.xml b/account_analytic_report/views/report_trial_balance_analytic.xml
new file mode 100644
index 00000000000..3322d05a8fb
--- /dev/null
+++ b/account_analytic_report/views/report_trial_balance_analytic.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<odoo>
+<!-- # Copyright 2024 (APSL - Nagarro) Bernat Obrador
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
+    <template id="report_trial_balance_analytic">
+        <div class="o_account_financial_reports_page">
+            <t t-call="account_financial_report.report_buttons" />
+            <t t-call="account_financial_report.report_trial_balance_base" />
+        </div>
+    </template>
+</odoo>
diff --git a/account_analytic_report/wizard/__init__.py b/account_analytic_report/wizard/__init__.py
new file mode 100644
index 00000000000..801628b39d4
--- /dev/null
+++ b/account_analytic_report/wizard/__init__.py
@@ -0,0 +1 @@
+from . import trial_balance_analytic_wizard_view
diff --git a/account_analytic_report/wizard/trial_balance_analytic_wizard_view.py b/account_analytic_report/wizard/trial_balance_analytic_wizard_view.py
new file mode 100644
index 00000000000..1f469a5a84d
--- /dev/null
+++ b/account_analytic_report/wizard/trial_balance_analytic_wizard_view.py
@@ -0,0 +1,170 @@
+# Copyright 2024 (APSL - Nagarro) Bernat Obrador
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+from odoo import _, api, fields, models
+from odoo.exceptions import UserError, ValidationError
+from odoo.tools import date_utils
+
+
+class AnalyticTrialBalanceReportWizard(models.TransientModel):
+    """Trial balance report wizard."""
+
+    _name = "ac.trial.balance.report.wizard"
+    _description = "Analytic Trial Balance Report Wizard"
+    _inherit = "account_financial_report_abstract_wizard"
+
+    date_range_id = fields.Many2one(comodel_name="date.range", string="Date range")
+    date_from = fields.Date(required=True)
+    date_to = fields.Date(required=True)
+    fy_start_date = fields.Date(compute="_compute_fy_start_date")
+    account_ids = fields.Many2many(
+        comodel_name="account.analytic.account", string="Filter accounts"
+    )
+    plan_id = fields.Many2one(
+        "account.analytic.plan", domain="[('parent_id', '=', False)]", required=True
+    )
+
+    group_by_analytic_account = fields.Boolean(string="Group by Analytic Account")
+    show_hierarchy = fields.Boolean(help="Shows hierarchy of the financial accounts")
+    limit_hierarchy_level = fields.Boolean(help="Limits hierarchy level")
+    hierarchy_level = fields.Integer(help="Hierarchy levels to show", default=1)
+
+    show_months = fields.Boolean(
+        help="""
+    This option works only when exporting to Excel. It will create a separate sheet
+    for each selected analytic account, displaying all financial accounts with a
+    balance.
+    For each account, it shows the monthly balance within the selected date range.
+    """
+    )
+
+    @api.depends("date_from")
+    def _compute_fy_start_date(self):
+        for wiz in self:
+            if wiz.date_from:
+                date_from, date_to = date_utils.get_fiscal_year(
+                    wiz.date_from,
+                    day=self.company_id.fiscalyear_last_day,
+                    month=int(self.company_id.fiscalyear_last_month),
+                )
+                wiz.fy_start_date = date_from
+            else:
+                wiz.fy_start_date = False
+
+    @api.onchange("company_id")
+    def onchange_company_id(self):
+        """Handle company change."""
+        if (
+            self.company_id
+            and self.date_range_id.company_id
+            and self.date_range_id.company_id != self.company_id
+        ):
+            self.date_range_id = False
+
+        res = {
+            "domain": {
+                "date_range_id": [],
+            }
+        }
+        if not self.company_id:
+            return res
+        else:
+            # res["domain"]["account_ids"] += [("company_id", "=", self.company_id.id)]
+            res["domain"]["date_range_id"] += [
+                "|",
+                ("company_id", "=", self.company_id.id),
+                ("company_id", "=", False),
+            ]
+        return res
+
+    @api.onchange("date_range_id")
+    def onchange_date_range_id(self):
+        """Handle date range change."""
+        self.date_from = self.date_range_id.date_start
+        self.date_to = self.date_range_id.date_end
+
+    @api.onchange("group_by_analytic_account")
+    def onchange_group_by_analytic_account(self):
+        if self.group_by_analytic_account:
+            self._not_show_hierarchy()
+
+    @api.onchange("plan_id")
+    def _onchange_plan_id(self):
+        if self.account_ids:
+            self.account_ids = False
+            self.show_months = False
+
+    @api.constrains("company_id", "date_range_id")
+    def _check_company_id_date_range_id(self):
+        for rec in self.sudo():
+            if (
+                rec.company_id
+                and rec.date_range_id.company_id
+                and rec.company_id != rec.date_range_id.company_id
+            ):
+                raise ValidationError(
+                    _(
+                        "The Company in the Trial Balance Report Wizard and in "
+                        "Date Range must be the same."
+                    )
+                )
+
+    @api.constrains("show_hierarchy", "hierarchy_level")
+    def _check_show_hierarchy_level(self):
+        for rec in self:
+            if rec.show_hierarchy and rec.hierarchy_level <= 0:
+                raise UserError(
+                    _("The hierarchy level to filter on must be greater than 0.")
+                )
+
+    @api.onchange("account_ids")
+    def _onchange_account_ids(self):
+        if self.account_ids:
+            self._not_show_hierarchy()
+
+    def _print_report(self, report_type):
+        self.ensure_one()
+        data = self._prepare_report_trial_balance_analytic()
+        if report_type == "xlsx":
+            report_name = "a_f_r.report_trial_balance_analytic_xlsx"
+        else:
+            report_name = "account_analytic_report.trial_balance_analytic"
+
+        return (
+            self.env["ir.actions.report"]
+            .search(
+                [("report_name", "=", report_name), ("report_type", "=", report_type)],
+                limit=1,
+            )
+            .report_action(self, data=data)
+        )
+
+    def _not_show_hierarchy(self):
+        self.show_hierarchy = False
+        self.limit_hierarchy_level = False
+        self.hierarchy_level = 1
+
+    def _prepare_report_trial_balance_analytic(self):
+        self.ensure_one()
+        sorted_accounts_ids = sorted([account.id for account in self.account_ids])
+        return {
+            "wizard_id": self.id,
+            "date_from": self.date_from,
+            "date_to": self.date_to,
+            "company_id": self.company_id.id,
+            "account_ids": sorted_accounts_ids or [],
+            "fy_start_date": self.fy_start_date,
+            "account_financial_report_lang": self.env.lang,
+            "plan_field": self.plan_id._column_name(),
+            "plan_name": self.plan_id.name,
+            "plan_id": self.plan_id.id,
+            "group_by_analytic_account": self.group_by_analytic_account,
+            "show_hierarchy": self.show_hierarchy,
+            "limit_hierarchy_level": self.limit_hierarchy_level,
+            "hierarchy_level": self.hierarchy_level,
+            "show_months": self.show_months,
+        }
+
+    def _export(self, report_type):
+        """Default export is PDF."""
+        return self._print_report(report_type)
diff --git a/account_analytic_report/wizard/trial_balance_analytic_wizard_view.xml b/account_analytic_report/wizard/trial_balance_analytic_wizard_view.xml
new file mode 100644
index 00000000000..fca61181a04
--- /dev/null
+++ b/account_analytic_report/wizard/trial_balance_analytic_wizard_view.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<odoo>
+    <!-- # Copyright 2024 (APSL - Nagarro) Bernat Obrador
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
+    <record id="analytic_trial_balance_wizard" model="ir.ui.view">
+        <field name="name">Analytic Trial Balance</field>
+        <field name="model">ac.trial.balance.report.wizard</field>
+        <field name="arch" type="xml">
+            <form>
+                <group name="main_info">
+                    <field
+                        name="company_id"
+                        options="{'no_create': True}"
+                        groups="base.group_multi_company"
+                    />
+                </group>
+                <div>
+                    <group name="filters">
+                        <group name="date_range">
+                            <field name="date_range_id" />
+                            <field name="date_from" />
+                            <field name="date_to" />
+                            <field name="fy_start_date" invisible="1" />
+                        </group>
+                        <group name="other_filters">
+                            <field name="plan_id" />
+                            <field name="group_by_analytic_account" />
+                            <field
+                                name="show_hierarchy"
+                                invisible="group_by_analytic_account or account_ids"
+                            />
+                            <field
+                                name="limit_hierarchy_level"
+                                invisible="not show_hierarchy or group_by_analytic_account"
+                            />
+                            <field
+                                name="hierarchy_level"
+                                invisible="not limit_hierarchy_level or group_by_analytic_account"
+                            />
+                        </group>
+                    </group>
+                    <div />
+                    <group name="account_filter" col="4">
+                        <label for="account_ids" colspan="4" />
+                        <field
+                            name="account_ids"
+                            nolabel="1"
+                            widget="many2many_tags"
+                            options="{'no_create': True}"
+                            colspan="4"
+                            domain="[('root_plan_id', '=', plan_id)]"
+                        />
+                        <field
+                            name="show_months"
+                            invisible="not account_ids or group_by_analytic_account"
+                        />
+                    </group>
+                </div>
+                <footer>
+                    <div>
+                        <button
+                            name="button_export_html"
+                            string="View"
+                            type="object"
+                            default_focus="1"
+                            class="oe_highlight"
+                        />
+                        or
+                        <button
+                            name="button_export_pdf"
+                            string="Export PDF"
+                            type="object"
+                        />
+                        or
+                        <button
+                            name="button_export_xlsx"
+                            string="Export XLSX"
+                            type="object"
+                        />
+                        or
+                        <button string="Cancel" class="oe_link" special="cancel" />
+                    </div>
+                </footer>
+            </form>
+        </field>
+    </record>
+    <record id="action_analytic_trial_balance_wizard" model="ir.actions.act_window">
+        <field name="name">Analytic Trial Balance</field>
+        <field name="res_model">ac.trial.balance.report.wizard</field>
+        <field name="view_mode">form</field>
+        <field name="view_id" ref="analytic_trial_balance_wizard" />
+        <field name="target">new</field>
+    </record>
+</odoo>