From 554ad61027c81e68fc1db5ac48c4fe50d5bb0f74 Mon Sep 17 00:00:00 2001 From: Jordi Ballester Date: Thu, 23 Nov 2017 16:23:46 +0100 Subject: [PATCH 001/310] [MIG] account_financial_report_qweb: Migration to 11.0 * dynamize general ledger --- account_financial_report/README.rst | 72 + account_financial_report/__init__.py | 8 + account_financial_report/__manifest__.py | 44 + .../i18n/account_financial_report.pot | 1365 ++++++++++++++++ account_financial_report/menuitems.xml | 87 + account_financial_report/models/__init__.py | 6 + account_financial_report/models/account.py | 14 + account_financial_report/report/__init__.py | 15 + .../report/abstract_report_xlsx.py | 301 ++++ .../report/aged_partner_balance.py | 628 ++++++++ .../report/aged_partner_balance_xlsx.py | 257 +++ .../report/general_ledger.py | 1431 +++++++++++++++++ .../report/general_ledger_xlsx.py | 137 ++ account_financial_report/report/open_items.py | 776 +++++++++ .../report/open_items_xlsx.py | 105 ++ .../report/templates/aged_partner_balance.xml | 432 +++++ .../report/templates/general_ledger.xml | 263 +++ .../report/templates/layouts.xml | 36 + .../report/templates/open_items.xml | 233 +++ .../report/templates/trial_balance.xml | 155 ++ .../report/trial_balance.py | 263 +++ .../report/trial_balance_xlsx.py | 122 ++ account_financial_report/reports.xml | 151 ++ .../static/description/icon.png | Bin 0 -> 15230 bytes .../static/src/css/report.css | 105 ++ .../js/account_financial_report_backend.js | 109 ++ .../js/account_financial_report_widgets.js | 37 + account_financial_report/tests/__init__.py | 9 + .../tests/abstract_test.py | 246 +++ .../tests/test_aged_partner_balance.py | 42 + .../tests/test_general_ledger.py | 466 ++++++ .../tests/test_open_items.py | 41 + .../tests/test_trial_balance.py | 54 + .../view/account_view.xml | 42 + .../view/report_aged_partner_balance.xml | 9 + .../view/report_general_ledger.xml | 9 + .../view/report_open_items.xml | 9 + .../view/report_template.xml | 45 + .../view/report_trial_balance.xml | 9 + account_financial_report/wizard/__init__.py | 9 + .../wizard/aged_partner_balance_wizard.py | 103 ++ .../aged_partner_balance_wizard_view.xml | 53 + .../wizard/general_ledger_wizard.py | 163 ++ .../wizard/general_ledger_wizard_view.xml | 73 + .../wizard/open_items_wizard.py | 111 ++ .../wizard/open_items_wizard_view.xml | 53 + .../wizard/trial_balance_wizard.py | 155 ++ .../wizard/trial_balance_wizard_view.xml | 70 + 48 files changed, 8923 insertions(+) create mode 100644 account_financial_report/README.rst create mode 100644 account_financial_report/__init__.py create mode 100644 account_financial_report/__manifest__.py create mode 100644 account_financial_report/i18n/account_financial_report.pot create mode 100644 account_financial_report/menuitems.xml create mode 100644 account_financial_report/models/__init__.py create mode 100644 account_financial_report/models/account.py create mode 100644 account_financial_report/report/__init__.py create mode 100644 account_financial_report/report/abstract_report_xlsx.py create mode 100644 account_financial_report/report/aged_partner_balance.py create mode 100644 account_financial_report/report/aged_partner_balance_xlsx.py create mode 100644 account_financial_report/report/general_ledger.py create mode 100644 account_financial_report/report/general_ledger_xlsx.py create mode 100644 account_financial_report/report/open_items.py create mode 100644 account_financial_report/report/open_items_xlsx.py create mode 100644 account_financial_report/report/templates/aged_partner_balance.xml create mode 100644 account_financial_report/report/templates/general_ledger.xml create mode 100644 account_financial_report/report/templates/layouts.xml create mode 100644 account_financial_report/report/templates/open_items.xml create mode 100644 account_financial_report/report/templates/trial_balance.xml create mode 100644 account_financial_report/report/trial_balance.py create mode 100644 account_financial_report/report/trial_balance_xlsx.py create mode 100644 account_financial_report/reports.xml create mode 100644 account_financial_report/static/description/icon.png create mode 100644 account_financial_report/static/src/css/report.css create mode 100644 account_financial_report/static/src/js/account_financial_report_backend.js create mode 100644 account_financial_report/static/src/js/account_financial_report_widgets.js create mode 100644 account_financial_report/tests/__init__.py create mode 100644 account_financial_report/tests/abstract_test.py create mode 100644 account_financial_report/tests/test_aged_partner_balance.py create mode 100644 account_financial_report/tests/test_general_ledger.py create mode 100644 account_financial_report/tests/test_open_items.py create mode 100644 account_financial_report/tests/test_trial_balance.py create mode 100644 account_financial_report/view/account_view.xml create mode 100644 account_financial_report/view/report_aged_partner_balance.xml create mode 100644 account_financial_report/view/report_general_ledger.xml create mode 100644 account_financial_report/view/report_open_items.xml create mode 100644 account_financial_report/view/report_template.xml create mode 100644 account_financial_report/view/report_trial_balance.xml create mode 100644 account_financial_report/wizard/__init__.py create mode 100644 account_financial_report/wizard/aged_partner_balance_wizard.py create mode 100644 account_financial_report/wizard/aged_partner_balance_wizard_view.xml create mode 100644 account_financial_report/wizard/general_ledger_wizard.py create mode 100644 account_financial_report/wizard/general_ledger_wizard_view.xml create mode 100644 account_financial_report/wizard/open_items_wizard.py create mode 100644 account_financial_report/wizard/open_items_wizard_view.xml create mode 100644 account_financial_report/wizard/trial_balance_wizard.py create mode 100644 account_financial_report/wizard/trial_balance_wizard_view.xml diff --git a/account_financial_report/README.rst b/account_financial_report/README.rst new file mode 100644 index 000000000000..ec4cdd122a14 --- /dev/null +++ b/account_financial_report/README.rst @@ -0,0 +1,72 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: https://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +====================== +QWeb Financial Reports +====================== + +This module adds a set of financial reports. They are accessible under +Accounting / Reporting / OCA Reports. + +- General ledger +- Trial Balance +- Open Items +- Aged Partner Balance + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/91/11.0 + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smashing it by providing a detailed and welcomed feedback. + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Jordi Ballester +* Yannick Vaucher +* Simone Orsi +* Leonardo Pistone +* Damien Crier +* Andrea Stirpe +* Thomas Rehn +* Andrea Gallina <4everamd@gmail.com> +* Robert Rottermann +* Ciro Urselli +* Francesco Apruzzese +* Lorenzo Battistini +* Julien Coux +* Akim Juillerat +* Alexis de Lattre + +Much of the work in this module was done at a sprint in Sorrento, Italy in +April 2016. + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +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. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/account_financial_report/__init__.py b/account_financial_report/__init__.py new file mode 100644 index 000000000000..b8f605438042 --- /dev/null +++ b/account_financial_report/__init__.py @@ -0,0 +1,8 @@ + +# Author: Damien Crier +# Copyright 2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import models +from . import report +from . import wizard diff --git a/account_financial_report/__manifest__.py b/account_financial_report/__manifest__.py new file mode 100644 index 000000000000..b01409d62c82 --- /dev/null +++ b/account_financial_report/__manifest__.py @@ -0,0 +1,44 @@ + +# Author: Damien Crier +# Author: Julien Coux +# Copyright 2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + 'name': 'Account Financial Reports', + 'version': '11.0.1.1.0', + 'category': 'Reporting', + 'summary': 'OCA Financial Reports', + 'author': 'Camptocamp SA,' + 'initOS GmbH,' + 'redCOR AG,' + 'Odoo Community Association (OCA)', + "website": "https://odoo-community.org/", + 'depends': [ + 'account', + 'date_range', + 'report_xlsx', + ], + 'data': [ + 'wizard/aged_partner_balance_wizard_view.xml', + 'wizard/general_ledger_wizard_view.xml', + 'wizard/open_items_wizard_view.xml', + 'wizard/trial_balance_wizard_view.xml', + 'menuitems.xml', + 'reports.xml', + 'report/templates/layouts.xml', + 'report/templates/aged_partner_balance.xml', + 'report/templates/general_ledger.xml', + 'report/templates/open_items.xml', + 'report/templates/trial_balance.xml', + 'view/account_view.xml', + 'view/report_template.xml', + 'view/report_general_ledger.xml', + 'view/report_trial_balance.xml', + 'view/report_open_items.xml', + 'view/report_aged_partner_balance.xml', + ], + 'installable': True, + 'application': True, + 'auto_install': False, + 'license': 'AGPL-3', +} diff --git a/account_financial_report/i18n/account_financial_report.pot b/account_financial_report/i18n/account_financial_report.pot new file mode 100644 index 000000000000..3cea2e427a48 --- /dev/null +++ b/account_financial_report/i18n/account_financial_report.pot @@ -0,0 +1,1365 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_financial_report +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0c\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-08-12 12:17+0000\n" +"PO-Revision-Date: 2016-08-12 12:17+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_financial_report +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:72 +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:27 +#: code:addons/account_financial_report/report/open_items_xlsx.py:26 +#: code:addons/account_financial_report/report/trial_balance_xlsx.py:25 +#: model:ir.model,name:account_financial_report.model_account_account +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_move_line_account +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_move_line_account +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_move_line_account +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_move_lines +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_lines +#: model:ir.ui.view,arch_db:account_financial_report.report_open_items_qweb_lines +#: model:ir.ui.view,arch_db:account_financial_report.report_trial_balance_qweb_lines_header +#, python-format +msgid "Account" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:66 +#: code:addons/account_financial_report/report/open_items_xlsx.py:56 +#: code:addons/account_financial_report/report/trial_balance_xlsx.py:71 +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_filters +#: model:ir.ui.view,arch_db:account_financial_report.report_open_items_qweb_filters +#: model:ir.ui.view,arch_db:account_financial_report.report_trial_balance_qweb_filters +#, python-format +msgid "Account balance at 0 filter" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_account_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_account_account_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_account_account_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_account_account_id +msgid "Account id" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_ids +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_account_ids +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_account_ids +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_account_ids +msgid "Account ids" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_general_ledger_report_wizard_centralize +msgid "Activate centralization" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_line_age_120_days +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_move_line_age_120_days +msgid "Age 120 days" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_line_age_30_days +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_move_line_age_30_days +msgid "Age 30 days" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_line_age_60_days +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_move_line_age_60_days +msgid "Age 60 days" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_line_age_90_days +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_move_line_age_90_days +msgid "Age 90 days" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:54 +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:110 +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_lines_header +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_move_lines +#, python-format +msgid "Age ??? 120 d." +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:36 +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:89 +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_lines_header +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_move_lines +#, python-format +msgid "Age ??? 30 d." +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:42 +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:96 +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_lines_header +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_move_lines +#, python-format +msgid "Age ??? 60 d." +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:48 +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:103 +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_lines_header +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_move_lines +#, python-format +msgid "Age ??? 90 d." +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:19 +#: model:ir.actions.act_window,name:account_financial_report.action_aged_partner_balance_wizard +#: model:ir.actions.report.xml,name:account_financial_report.action_report_aged_partner_balance_qweb +#: model:ir.ui.menu,name:account_financial_report.menu_aged_partner_balance_wizard +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb +#, python-format +msgid "Aged Partner Balance" +msgstr "" + +#. module: account_financial_report +#: model:ir.model,name:account_financial_report.model_aged_partner_balance_wizard +msgid "Aged Partner Balance Wizard" +msgstr "" + +#. module: account_financial_report +#: model:ir.actions.report.xml,name:account_financial_report.action_report_aged_partner_balance_xlsx +msgid "Aged Partner Balance XLSX" +msgstr "" + +#. module: account_financial_report +#: selection:aged.partner.balance.wizard,target_move:0 +#: selection:general.ledger.report.wizard,target_move:0 +#: selection:open.items.report.wizard,target_move:0 +#: selection:trial.balance.report.wizard,target_move:0 +msgid "All Entries" +msgstr "" + +#. module: account_financial_report +#: selection:aged.partner.balance.wizard,target_move:0 +#: selection:general.ledger.report.wizard,target_move:0 +#: selection:open.items.report.wizard,target_move:0 +#: selection:trial.balance.report.wizard,target_move:0 +msgid "All Posted Entries" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:131 +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:65 +#: code:addons/account_financial_report/report/open_items_xlsx.py:55 +#: code:addons/account_financial_report/report/trial_balance_xlsx.py:70 +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_filters +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_filters +#: model:ir.ui.view,arch_db:account_financial_report.report_open_items_qweb_filters +#: model:ir.ui.view,arch_db:account_financial_report.report_trial_balance_qweb_filters +#, python-format +msgid "All entries" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:130 +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:64 +#: code:addons/account_financial_report/report/open_items_xlsx.py:54 +#: code:addons/account_financial_report/report/trial_balance_xlsx.py:69 +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_filters +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_filters +#: model:ir.ui.view,arch_db:account_financial_report.report_open_items_qweb_filters +#: model:ir.ui.view,arch_db:account_financial_report.report_trial_balance_qweb_filters +#, python-format +msgid "All posted entries" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:53 +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_lines +#, python-format +msgid "Amount cur." +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_move_line_amount_currency +msgid "Amount currency" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_line_amount_residual +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_move_line_amount_residual +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_move_line_amount_residual +msgid "Amount residual" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_move_line_amount_residual_currency +msgid "Amount residual currency" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_move_line_amount_total_due +msgid "Amount total due" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_move_line_amount_total_due_currency +msgid "Amount total due currency" +msgstr "" + +#. module: account_financial_report +#: model:ir.ui.view,arch_db:account_financial_report.aged_partner_balance_wizard +#: model:ir.ui.view,arch_db:account_financial_report.general_ledger_wizard +#: model:ir.ui.view,arch_db:account_financial_report.open_items_wizard +#: model:ir.ui.view,arch_db:account_financial_report.trial_balance_wizard +msgid "Cancel" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_centralize +msgid "Centralize" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:68 +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_filters +#, python-format +msgid "Centralize filter" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_account_account_centralized +msgid "Centralized" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/general_ledger.py:899 +#, python-format +msgid "Centralized Entries" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/trial_balance_xlsx.py:24 +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_code +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_account_code +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_account_code +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_account_code +#: model:ir.ui.view,arch_db:account_financial_report.report_trial_balance_qweb_lines_header +#, python-format +msgid "Code" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_aged_partner_balance_wizard_company_id +#: model:ir.model.fields,field_description:account_financial_report.field_general_ledger_report_wizard_company_id +#: model:ir.model.fields,field_description:account_financial_report.field_open_items_report_wizard_company_id +#: model:ir.model.fields,field_description:account_financial_report.field_trial_balance_report_wizard_company_id +msgid "Company" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_company_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_company_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_company_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_company_id +msgid "Company id" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:30 +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_move_line_cost_center +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_lines +#, python-format +msgid "Cost center" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_aged_partner_balance_wizard_create_uid +#: model:ir.model.fields,field_description:account_financial_report.field_general_ledger_report_wizard_create_uid +#: model:ir.model.fields,field_description:account_financial_report.field_open_items_report_wizard_create_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_create_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_create_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_line_create_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_move_line_create_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_partner_create_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_account_create_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_create_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_move_line_create_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_partner_create_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_account_create_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_create_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_move_line_create_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_partner_create_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_account_create_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_create_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_partner_create_uid +#: model:ir.model.fields,field_description:account_financial_report.field_trial_balance_report_wizard_create_uid +msgid "Created by" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_aged_partner_balance_wizard_create_date +#: model:ir.model.fields,field_description:account_financial_report.field_general_ledger_report_wizard_create_date +#: model:ir.model.fields,field_description:account_financial_report.field_open_items_report_wizard_create_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_create_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_create_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_line_create_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_move_line_create_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_partner_create_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_account_create_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_create_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_move_line_create_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_partner_create_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_account_create_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_create_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_move_line_create_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_partner_create_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_account_create_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_create_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_partner_create_date +#: model:ir.model.fields,field_description:account_financial_report.field_trial_balance_report_wizard_create_date +msgid "Created on" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:40 +#: code:addons/account_financial_report/report/trial_balance_xlsx.py:34 +#: code:addons/account_financial_report/report/trial_balance_xlsx.py:54 +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_move_line_credit +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_account_credit +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_partner_credit +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_lines +#: model:ir.ui.view,arch_db:account_financial_report.report_trial_balance_qweb_lines_header +#, python-format +msgid "Credit" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_cumul_age_120_days +msgid "Cumul age 120 days" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_cumul_age_30_days +msgid "Cumul age 30 days" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_cumul_age_60_days +msgid "Cumul age 60 days" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_cumul_age_90_days +msgid "Cumul age 90 days" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_cumul_amount_residual +msgid "Cumul amount residual" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_move_line_cumul_balance +msgid "Cumul balance" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_cumul_current +msgid "Cumul current" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_cumul_older +msgid "Cumul older" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:46 +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_lines +#, python-format +msgid "Cumul. Bal." +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:52 +#: code:addons/account_financial_report/report/open_items_xlsx.py:39 +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_lines +#: model:ir.ui.view,arch_db:account_financial_report.report_open_items_qweb_lines +#, python-format +msgid "Cur." +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/open_items_xlsx.py:40 +#: model:ir.ui.view,arch_db:account_financial_report.report_open_items_qweb_lines +#, python-format +msgid "Cur. Original" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/open_items_xlsx.py:44 +#: model:ir.ui.view,arch_db:account_financial_report.report_open_items_qweb_lines +#, python-format +msgid "Cur. Residual" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_move_line_currency_name +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_move_line_currency_name +msgid "Currency name" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:30 +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:82 +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_line_current +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_move_line_current +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_lines_header +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_move_lines +#, python-format +msgid "Current" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:69 +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:24 +#: code:addons/account_financial_report/report/open_items_xlsx.py:23 +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_move_line_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_move_line_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_move_line_date +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_move_lines +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_lines +#: model:ir.ui.view,arch_db:account_financial_report.report_open_items_qweb_lines +#, python-format +msgid "Date" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_aged_partner_balance_wizard_date_at +#: model:ir.model.fields,field_description:account_financial_report.field_open_items_report_wizard_date_at +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_date_at +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_date_at +msgid "Date at" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:128 +#: code:addons/account_financial_report/report/open_items_xlsx.py:52 +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_filters +#: model:ir.ui.view,arch_db:account_financial_report.report_open_items_qweb_filters +#, python-format +msgid "Date at filter" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_move_line_date_due +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_move_line_date_due +msgid "Date due" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_general_ledger_report_wizard_date_from +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_date_from +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_date_from +#: model:ir.model.fields,field_description:account_financial_report.field_trial_balance_report_wizard_date_from +msgid "Date from" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_general_ledger_report_wizard_date_range_id +#: model:ir.model.fields,field_description:account_financial_report.field_trial_balance_report_wizard_date_range_id +msgid "Date range" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:61 +#: code:addons/account_financial_report/report/trial_balance_xlsx.py:66 +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_filters +#: model:ir.ui.view,arch_db:account_financial_report.report_trial_balance_qweb_filters +#, python-format +msgid "Date range filter" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_general_ledger_report_wizard_date_to +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_date_to +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_date_to +#: model:ir.model.fields,field_description:account_financial_report.field_trial_balance_report_wizard_date_to +msgid "Date to" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:34 +#: code:addons/account_financial_report/report/trial_balance_xlsx.py:30 +#: code:addons/account_financial_report/report/trial_balance_xlsx.py:50 +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_move_line_debit +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_account_debit +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_partner_debit +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_lines +#: model:ir.ui.view,arch_db:account_financial_report.report_trial_balance_qweb_lines_header +#, python-format +msgid "Debit" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_aged_partner_balance_wizard_display_name +#: model:ir.model.fields,field_description:account_financial_report.field_general_ledger_report_wizard_display_name +#: model:ir.model.fields,field_description:account_financial_report.field_open_items_report_wizard_display_name +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_display_name +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_display_name +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_line_display_name +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_move_line_display_name +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_partner_display_name +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_account_display_name +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_display_name +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_move_line_display_name +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_partner_display_name +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_account_display_name +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_display_name +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_move_line_display_name +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_partner_display_name +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_account_display_name +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_display_name +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_partner_display_name +#: model:ir.model.fields,field_description:account_financial_report.field_trial_balance_report_wizard_display_name +msgid "Display Name" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:75 +#: code:addons/account_financial_report/report/open_items_xlsx.py:29 +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_move_lines +#: model:ir.ui.view,arch_db:account_financial_report.report_open_items_qweb_lines +#, python-format +msgid "Due date" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:139 +#: code:addons/account_financial_report/report/open_items_xlsx.py:109 +#: code:addons/account_financial_report/report/trial_balance_xlsx.py:38 +#: code:addons/account_financial_report/report/trial_balance_xlsx.py:58 +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_ending_cumul +#: model:ir.ui.view,arch_db:account_financial_report.report_open_items_qweb_ending_cumul +#: model:ir.ui.view,arch_db:account_financial_report.report_trial_balance_qweb_lines_header +#, python-format +msgid "Ending balance" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:70 +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:25 +#: code:addons/account_financial_report/report/open_items_xlsx.py:24 +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_move_line_entry +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_move_line_entry +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_move_line_entry +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_move_lines +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_lines +#: model:ir.ui.view,arch_db:account_financial_report.report_open_items_qweb_lines +#, python-format +msgid "Entry" +msgstr "" + +#. module: account_financial_report +#: model:ir.ui.view,arch_db:account_financial_report.aged_partner_balance_wizard +#: model:ir.ui.view,arch_db:account_financial_report.general_ledger_wizard +#: model:ir.ui.view,arch_db:account_financial_report.open_items_wizard +#: model:ir.ui.view,arch_db:account_financial_report.trial_balance_wizard +msgid "Export PDF" +msgstr "" + +#. module: account_financial_report +#: model:ir.ui.view,arch_db:account_financial_report.aged_partner_balance_wizard +#: model:ir.ui.view,arch_db:account_financial_report.general_ledger_wizard +#: model:ir.ui.view,arch_db:account_financial_report.open_items_wizard +#: model:ir.ui.view,arch_db:account_financial_report.trial_balance_wizard +msgid "Export XLSX" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_filter_account_ids +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_filter_account_ids +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_filter_account_ids +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_filter_account_ids +msgid "Filter account ids" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_aged_partner_balance_wizard_account_ids +#: model:ir.model.fields,field_description:account_financial_report.field_general_ledger_report_wizard_account_ids +#: model:ir.model.fields,field_description:account_financial_report.field_open_items_report_wizard_account_ids +#: model:ir.model.fields,field_description:account_financial_report.field_trial_balance_report_wizard_account_ids +msgid "Filter accounts" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_filter_cost_center_ids +msgid "Filter cost center ids" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_general_ledger_report_wizard_cost_center_ids +msgid "Filter cost centers" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_filter_partner_ids +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_filter_partner_ids +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_filter_partner_ids +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_filter_partner_ids +msgid "Filter partner ids" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_aged_partner_balance_wizard_partner_ids +#: model:ir.model.fields,field_description:account_financial_report.field_general_ledger_report_wizard_partner_ids +#: model:ir.model.fields,field_description:account_financial_report.field_open_items_report_wizard_partner_ids +#: model:ir.model.fields,field_description:account_financial_report.field_trial_balance_report_wizard_partner_ids +msgid "Filter partners" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_account_final_amount_residual +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_partner_final_amount_residual +msgid "Final amount residual" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_account_final_balance +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_partner_final_balance +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_account_final_balance +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_partner_final_balance +msgid "Final balance" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_account_final_credit +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_partner_final_credit +msgid "Final credit" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_account_final_debit +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_partner_final_debit +msgid "Final debit" +msgstr "" + +#. module: account_financial_report +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_filters +#: model:ir.ui.view,arch_db:account_financial_report.report_trial_balance_qweb_filters +msgid "From:" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:62 +#: code:addons/account_financial_report/report/trial_balance_xlsx.py:67 +#, python-format +msgid "From: %s To: %s" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_general_ledger_report_wizard_fy_start_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_fy_start_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_fy_start_date +#: model:ir.model.fields,field_description:account_financial_report.field_trial_balance_report_wizard_fy_start_date +msgid "Fy start date" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:20 +#: model:ir.actions.act_window,name:account_financial_report.action_general_ledger_wizard +#: model:ir.actions.report.xml,name:account_financial_report.action_report_general_ledger_qweb +#: model:ir.ui.menu,name:account_financial_report.menu_general_ledger_wizard +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb +#, python-format +msgid "General Ledger" +msgstr "" + +#. module: account_financial_report +#: model:ir.model,name:account_financial_report.model_general_ledger_report_wizard +msgid "General Ledger Report Wizard" +msgstr "" + +#. module: account_financial_report +#: model:ir.actions.report.xml,name:account_financial_report.action_report_general_ledger_xlsx +msgid "General Ledger XLSX" +msgstr "" + +#. module: account_financial_report +#: model:ir.ui.view,arch_db:account_financial_report.general_ledger_wizard +msgid "General Ledger can be computed only if selected company have only one unaffected earnings account." +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_general_ledger_id +msgid "General ledger id" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_has_second_currency +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_has_second_currency +msgid "Has second currency" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:67 +#: code:addons/account_financial_report/report/open_items_xlsx.py:57 +#: code:addons/account_financial_report/report/trial_balance_xlsx.py:72 +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_filters +#: model:ir.ui.view,arch_db:account_financial_report.report_open_items_qweb_filters +#: model:ir.ui.view,arch_db:account_financial_report.report_trial_balance_qweb_filters +#, python-format +msgid "Hide" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_hide_account_balance_at_0 +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_hide_account_balance_at_0 +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_hide_account_balance_at_0 +msgid "Hide account balance at 0" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_general_ledger_report_wizard_hide_account_balance_at_0 +#: model:ir.model.fields,field_description:account_financial_report.field_open_items_report_wizard_hide_account_balance_at_0 +#: model:ir.model.fields,field_description:account_financial_report.field_trial_balance_report_wizard_hide_account_balance_at_0 +msgid "Hide account ending balance at 0" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_aged_partner_balance_wizard_id +#: model:ir.model.fields,field_description:account_financial_report.field_general_ledger_report_wizard_id +#: model:ir.model.fields,field_description:account_financial_report.field_open_items_report_wizard_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_line_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_move_line_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_partner_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_account_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_move_line_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_partner_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_account_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_move_line_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_partner_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_account_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_partner_id +#: model:ir.model.fields,field_description:account_financial_report.field_trial_balance_report_wizard_id +msgid "ID" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,help:account_financial_report.field_account_account_centralized +msgid "If flagged, no details will be displayed in the General Ledger report (the webkit one only), only centralized amounts per period." +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:98 +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:114 +#: code:addons/account_financial_report/report/trial_balance_xlsx.py:26 +#: code:addons/account_financial_report/report/trial_balance_xlsx.py:46 +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_account_initial_balance +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_partner_initial_balance +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_account_initial_balance +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_partner_initial_balance +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_lines +#: model:ir.ui.view,arch_db:account_financial_report.report_trial_balance_qweb_lines_header +#, python-format +msgid "Initial balance" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_account_initial_credit +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_partner_initial_credit +msgid "Initial credit" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_account_initial_debit +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_partner_initial_debit +msgid "Initial debit" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_account_is_partner_account +msgid "Is partner account" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:71 +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:26 +#: code:addons/account_financial_report/report/open_items_xlsx.py:25 +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_move_line_journal +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_move_line_journal +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_move_line_journal +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_move_lines +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_lines +#: model:ir.ui.view,arch_db:account_financial_report.report_open_items_qweb_lines +#, python-format +msgid "Journal" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_move_line_label +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_move_line_label +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_move_line_label +msgid "Label" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_aged_partner_balance_wizard___last_update +#: model:ir.model.fields,field_description:account_financial_report.field_general_ledger_report_wizard___last_update +#: model:ir.model.fields,field_description:account_financial_report.field_open_items_report_wizard___last_update +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb___last_update +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account___last_update +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_line___last_update +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_move_line___last_update +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_partner___last_update +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb___last_update +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_account___last_update +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_move_line___last_update +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_partner___last_update +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb___last_update +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_account___last_update +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_move_line___last_update +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_partner___last_update +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb___last_update +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_account___last_update +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_partner___last_update +#: model:ir.model.fields,field_description:account_financial_report.field_trial_balance_report_wizard___last_update +msgid "Last Modified on" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_aged_partner_balance_wizard_write_uid +#: model:ir.model.fields,field_description:account_financial_report.field_general_ledger_report_wizard_write_uid +#: model:ir.model.fields,field_description:account_financial_report.field_open_items_report_wizard_write_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_write_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_line_write_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_move_line_write_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_partner_write_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_write_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_account_write_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_move_line_write_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_partner_write_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_write_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_account_write_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_move_line_write_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_partner_write_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_write_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_account_write_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_partner_write_uid +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_write_uid +#: model:ir.model.fields,field_description:account_financial_report.field_trial_balance_report_wizard_write_uid +msgid "Last Updated by" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_aged_partner_balance_wizard_write_date +#: model:ir.model.fields,field_description:account_financial_report.field_general_ledger_report_wizard_write_date +#: model:ir.model.fields,field_description:account_financial_report.field_open_items_report_wizard_write_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_write_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_line_write_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_move_line_write_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_partner_write_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_write_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_account_write_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_move_line_write_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_partner_write_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_write_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_account_write_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_move_line_write_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_partner_write_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_write_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_account_write_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_partner_write_date +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_write_date +#: model:ir.model.fields,field_description:account_financial_report.field_trial_balance_report_wizard_write_date +msgid "Last Updated on" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_partner_line_ids +msgid "Line ids" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_move_line_matching_number +msgid "Matching number" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_move_line_move_line_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_move_line_move_line_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_move_line_move_line_id +msgid "Move line id" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_partner_move_line_ids +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_account_move_line_ids +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_partner_move_line_ids +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_partner_move_line_ids +msgid "Move line ids" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_name +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_partner_name +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_account_name +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_partner_name +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_account_name +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_partner_name +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_account_name +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_partner_name +msgid "Name" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:69 +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_filters +#, python-format +msgid "No" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/general_ledger.py:491 +#: code:addons/account_financial_report/report/general_ledger.py:707 +#: code:addons/account_financial_report/report/open_items.py:277 +#: code:addons/account_financial_report/report/open_items.py:493 +#, python-format +msgid "No partner allocated" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_general_ledger_report_wizard_not_only_one_unaffected_earnings_account +#: model:ir.model.fields,field_description:account_financial_report.field_trial_balance_report_wizard_not_only_one_unaffected_earnings_account +msgid "Not only one unaffected earnings account" +msgstr "" + +#. module: account_financial_report +#: model:ir.ui.menu,name:account_financial_report.menu_oca_reports +msgid "OCA accounting reports" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:60 +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:117 +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_line_older +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_move_line_older +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_lines_header +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_move_lines +#, python-format +msgid "Older" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_only_posted_moves +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_only_posted_moves +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_only_posted_moves +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_only_posted_moves +msgid "Only posted moves" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/open_items_xlsx.py:19 +#: model:ir.actions.act_window,name:account_financial_report.action_open_items_wizard +#: model:ir.actions.report.xml,name:account_financial_report.action_report_open_items_qweb +#: model:ir.ui.menu,name:account_financial_report.menu_open_items_wizard +#: model:ir.ui.view,arch_db:account_financial_report.report_open_items_qweb +#, python-format +msgid "Open Items" +msgstr "" + +#. module: account_financial_report +#: model:ir.model,name:account_financial_report.model_open_items_report_wizard +msgid "Open Items Report Wizard" +msgstr "" + +#. module: account_financial_report +#: model:ir.actions.report.xml,name:account_financial_report.action_report_open_items_xlsx +msgid "Open Items XLSX" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_open_items_id +msgid "Open items id" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/open_items_xlsx.py:30 +#: model:ir.ui.view,arch_db:account_financial_report.report_open_items_qweb_lines +#, python-format +msgid "Original" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:24 +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:73 +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:28 +#: code:addons/account_financial_report/report/open_items_xlsx.py:27 +#: code:addons/account_financial_report/report/trial_balance_xlsx.py:45 +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_line_partner +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_move_line_partner +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_move_line_partner +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_move_line_partner +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_lines_header +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_move_lines +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_lines +#: model:ir.ui.view,arch_db:account_financial_report.report_open_items_qweb_lines +#: model:ir.ui.view,arch_db:account_financial_report.report_trial_balance_qweb_lines_header +#, python-format +msgid "Partner" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:230 +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_partner_ending_cumul +#, python-format +msgid "Partner cumul aged balance" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:136 +#: code:addons/account_financial_report/report/open_items_xlsx.py:106 +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_ending_cumul +#: model:ir.ui.view,arch_db:account_financial_report.report_open_items_qweb_ending_cumul +#, python-format +msgid "Partner ending balance" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_partner_partner_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_partner_partner_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_partner_partner_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_partner_partner_id +msgid "Partner id" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_partner_ids +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_account_partner_ids +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_account_partner_ids +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_account_partner_ids +msgid "Partner ids" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_aged_partner_balance_wizard_payable_accounts_only +#: model:ir.model.fields,field_description:account_financial_report.field_general_ledger_report_wizard_payable_accounts_only +#: model:ir.model.fields,field_description:account_financial_report.field_open_items_report_wizard_payable_accounts_only +#: model:ir.model.fields,field_description:account_financial_report.field_trial_balance_report_wizard_payable_accounts_only +msgid "Payable accounts only" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_percent_age_120_days +msgid "Percent age 120 days" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_percent_age_30_days +msgid "Percent age 30 days" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_percent_age_60_days +msgid "Percent age 60 days" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_percent_age_90_days +msgid "Percent age 90 days" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_percent_current +msgid "Percent current" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_percent_older +msgid "Percent older" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:173 +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:215 +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_account_ending_cumul +#, python-format +msgid "Percents" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:33 +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_lines +#, python-format +msgid "Rec." +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_aged_partner_balance_wizard_receivable_accounts_only +#: model:ir.model.fields,field_description:account_financial_report.field_general_ledger_report_wizard_receivable_accounts_only +#: model:ir.model.fields,field_description:account_financial_report.field_open_items_report_wizard_receivable_accounts_only +#: model:ir.model.fields,field_description:account_financial_report.field_trial_balance_report_wizard_receivable_accounts_only +msgid "Receivable accounts only" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:74 +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:29 +#: code:addons/account_financial_report/report/open_items_xlsx.py:28 +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_move_lines +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_lines +#: model:ir.ui.view,arch_db:account_financial_report.report_open_items_qweb_lines +#, python-format +msgid "Ref - Label" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_partner_report_account_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_move_line_report_account_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_partner_report_account_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_partner_report_account_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_partner_report_account_id +msgid "Report account id" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_account_report_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_account_report_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_account_report_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_account_report_id +msgid "Report id" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_line_report_partner_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_move_line_report_partner_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_move_line_report_partner_id +#: model:ir.model.fields,field_description:account_financial_report.field_report_open_items_qweb_move_line_report_partner_id +msgid "Report partner id" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:25 +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:76 +#: code:addons/account_financial_report/report/open_items_xlsx.py:34 +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_lines_header +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_move_lines +#: model:ir.ui.view,arch_db:account_financial_report.report_open_items_qweb_lines +#, python-format +msgid "Residual" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:67 +#: code:addons/account_financial_report/report/open_items_xlsx.py:57 +#: code:addons/account_financial_report/report/trial_balance_xlsx.py:72 +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_filters +#: model:ir.ui.view,arch_db:account_financial_report.report_open_items_qweb_filters +#: model:ir.ui.view,arch_db:account_financial_report.report_trial_balance_qweb_filters +#, python-format +msgid "Show" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_show_cost_center +msgid "Show cost center" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_aged_partner_balance_wizard_show_move_line_details +#: model:ir.model.fields,field_description:account_financial_report.field_report_aged_partner_balance_qweb_show_move_line_details +msgid "Show move line details" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_trial_balance_qweb_show_partner_details +#: model:ir.model.fields,field_description:account_financial_report.field_trial_balance_report_wizard_show_partner_details +msgid "Show partner details" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_aged_partner_balance_wizard_target_move +#: model:ir.model.fields,field_description:account_financial_report.field_general_ledger_report_wizard_target_move +#: model:ir.model.fields,field_description:account_financial_report.field_open_items_report_wizard_target_move +#: model:ir.model.fields,field_description:account_financial_report.field_trial_balance_report_wizard_target_move +msgid "Target Moves" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:129 +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:63 +#: code:addons/account_financial_report/report/open_items_xlsx.py:53 +#: code:addons/account_financial_report/report/trial_balance_xlsx.py:68 +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_filters +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_filters +#: model:ir.ui.view,arch_db:account_financial_report.report_open_items_qweb_filters +#: model:ir.ui.view,arch_db:account_financial_report.report_trial_balance_qweb_filters +#, python-format +msgid "Target moves filter" +msgstr "" + +#. module: account_financial_report +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_filters +#: model:ir.ui.view,arch_db:account_financial_report.report_trial_balance_qweb_filters +msgid "To:" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:166 +#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:208 +#: model:ir.ui.view,arch_db:account_financial_report.report_aged_partner_balance_qweb_account_ending_cumul +#, python-format +msgid "Total" +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/trial_balance_xlsx.py:19 +#: model:ir.actions.act_window,name:account_financial_report.action_trial_balance_wizard +#: model:ir.actions.report.xml,name:account_financial_report.action_report_trial_balance_qweb +#: model:ir.ui.menu,name:account_financial_report.menu_trial_balance_wizard +#: model:ir.ui.view,arch_db:account_financial_report.report_trial_balance_qweb +#, python-format +msgid "Trial Balance" +msgstr "" + +#. module: account_financial_report +#: model:ir.model,name:account_financial_report.model_trial_balance_report_wizard +msgid "Trial Balance Report Wizard" +msgstr "" + +#. module: account_financial_report +#: model:ir.actions.report.xml,name:account_financial_report.action_report_trial_balance_xlsx +msgid "Trial Balance XLSX" +msgstr "" + +#. module: account_financial_report +#: model:ir.ui.view,arch_db:account_financial_report.trial_balance_wizard +msgid "Trial Balance can be computed only if selected company have only one unaffected earnings account." +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,field_description:account_financial_report.field_report_general_ledger_qweb_unaffected_earnings_account +msgid "Unaffected earnings account" +msgstr "" + +#. module: account_financial_report +#: model:ir.model.fields,help:account_financial_report.field_general_ledger_report_wizard_hide_account_balance_at_0 +#: model:ir.model.fields,help:account_financial_report.field_open_items_report_wizard_hide_account_balance_at_0 +#: model:ir.model.fields,help:account_financial_report.field_trial_balance_report_wizard_hide_account_balance_at_0 +msgid "Use this filter to hide an account or a partner with an ending balance at 0. If partners are filtered, debits and credits totals will not match the trial balance." +msgstr "" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/general_ledger_xlsx.py:69 +#: model:ir.ui.view,arch_db:account_financial_report.report_general_ledger_qweb_filters +#, python-format +msgid "Yes" +msgstr "" + +#. module: account_financial_report +#: model:ir.ui.view,arch_db:account_financial_report.aged_partner_balance_wizard +#: model:ir.ui.view,arch_db:account_financial_report.general_ledger_wizard +#: model:ir.ui.view,arch_db:account_financial_report.open_items_wizard +#: model:ir.ui.view,arch_db:account_financial_report.trial_balance_wizard +msgid "or" +msgstr "" + +#. module: account_financial_report +#: model:ir.model,name:account_financial_report.model_report_aged_partner_balance_qweb +msgid "report_aged_partner_balance_qweb" +msgstr "" + +#. module: account_financial_report +#: model:ir.model,name:account_financial_report.model_report_aged_partner_balance_qweb_account +msgid "report_aged_partner_balance_qweb_account" +msgstr "" + +#. module: account_financial_report +#: model:ir.model,name:account_financial_report.model_report_aged_partner_balance_qweb_line +msgid "report_aged_partner_balance_qweb_line" +msgstr "" + +#. module: account_financial_report +#: model:ir.model,name:account_financial_report.model_report_aged_partner_balance_qweb_move_line +msgid "report_aged_partner_balance_qweb_move_line" +msgstr "" + +#. module: account_financial_report +#: model:ir.model,name:account_financial_report.model_report_aged_partner_balance_qweb_partner +msgid "report_aged_partner_balance_qweb_partner" +msgstr "" + +#. module: account_financial_report +#: model:ir.model,name:account_financial_report.model_report_general_ledger_qweb +msgid "report_general_ledger_qweb" +msgstr "" + +#. module: account_financial_report +#: model:ir.model,name:account_financial_report.model_report_general_ledger_qweb_account +msgid "report_general_ledger_qweb_account" +msgstr "" + +#. module: account_financial_report +#: model:ir.model,name:account_financial_report.model_report_general_ledger_qweb_move_line +msgid "report_general_ledger_qweb_move_line" +msgstr "" + +#. module: account_financial_report +#: model:ir.model,name:account_financial_report.model_report_general_ledger_qweb_partner +msgid "report_general_ledger_qweb_partner" +msgstr "" + +#. module: account_financial_report +#: model:ir.model,name:account_financial_report.model_report_open_items_qweb +msgid "report_open_items_qweb" +msgstr "" + +#. module: account_financial_report +#: model:ir.model,name:account_financial_report.model_report_open_items_qweb_account +msgid "report_open_items_qweb_account" +msgstr "" + +#. module: account_financial_report +#: model:ir.model,name:account_financial_report.model_report_open_items_qweb_move_line +msgid "report_open_items_qweb_move_line" +msgstr "" + +#. module: account_financial_report +#: model:ir.model,name:account_financial_report.model_report_open_items_qweb_partner +msgid "report_open_items_qweb_partner" +msgstr "" + +#. module: account_financial_report +#: model:ir.model,name:account_financial_report.model_report_trial_balance_qweb +msgid "report_trial_balance_qweb" +msgstr "" + +#. module: account_financial_report +#: model:ir.model,name:account_financial_report.model_report_trial_balance_qweb_account +msgid "report_trial_balance_qweb_account" +msgstr "" + +#. module: account_financial_report +#: model:ir.model,name:account_financial_report.model_report_trial_balance_qweb_partner +msgid "report_trial_balance_qweb_partner" +msgstr "" + diff --git a/account_financial_report/menuitems.xml b/account_financial_report/menuitems.xml new file mode 100644 index 000000000000..4d36885fa118 --- /dev/null +++ b/account_financial_report/menuitems.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/account_financial_report/models/__init__.py b/account_financial_report/models/__init__.py new file mode 100644 index 000000000000..df4d025b93e5 --- /dev/null +++ b/account_financial_report/models/__init__.py @@ -0,0 +1,6 @@ + +# Author: Damien Crier +# Copyright 2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import account diff --git a/account_financial_report/models/account.py b/account_financial_report/models/account.py new file mode 100644 index 000000000000..ea0db81035f0 --- /dev/null +++ b/account_financial_report/models/account.py @@ -0,0 +1,14 @@ + +# ?? 2011 Guewen Baconnier (Camptocamp) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).- +from odoo import models, fields + + +class AccountAccount(models.Model): + _inherit = 'account.account' + + centralized = fields.Boolean( + 'Centralized', + help="If flagged, no details will be displayed in " + "the General Ledger report (the webkit one only), " + "only centralized amounts per period.") diff --git a/account_financial_report/report/__init__.py b/account_financial_report/report/__init__.py new file mode 100644 index 000000000000..5fef1b7ef235 --- /dev/null +++ b/account_financial_report/report/__init__.py @@ -0,0 +1,15 @@ + +# ?? 2015 Yannick Vaucher (Camptocamp) +# ?? 2016 Damien Crier (Camptocamp) +# ?? 2016 Julien Coux (Camptocamp) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).- + +from . import abstract_report_xlsx +from . import aged_partner_balance +from . import aged_partner_balance_xlsx +from . import general_ledger +from . import general_ledger_xlsx +from . import open_items +from . import open_items_xlsx +from . import trial_balance +from . import trial_balance_xlsx diff --git a/account_financial_report/report/abstract_report_xlsx.py b/account_financial_report/report/abstract_report_xlsx.py new file mode 100644 index 000000000000..7c97a2eb6730 --- /dev/null +++ b/account_financial_report/report/abstract_report_xlsx.py @@ -0,0 +1,301 @@ + +# Author: Julien Coux +# Copyright 2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo import models + + +class AbstractReportXslx(models.AbstractModel): + _name = 'report.account_financial_report.abstract_report_xlsx' + _inherit = 'report.report_xlsx.abstract' + + def __init__(self, pool, cr): + # main sheet which will contains report + self.sheet = None + + # columns of the report + self.columns = None + + # row_pos must be incremented at each writing lines + self.row_pos = None + + # Formats + self.format_right = None + self.format_right_bold_italic = None + self.format_bold = None + self.format_header_left = None + self.format_header_center = None + self.format_header_right = None + self.format_header_amount = None + self.format_amount = None + self.format_percent_bold_italic = None + + def get_workbook_options(self): + return {'constant_memory': True} + + def generate_xlsx_report(self, workbook, data, objects): + report = objects + + self.row_pos = 0 + + self._define_formats(workbook) + + report_name = self._get_report_name() + filters = self._get_report_filters(report) + self.columns = self._get_report_columns(report) + + self.sheet = workbook.add_worksheet(report_name[:31]) + + self._set_column_width() + + self._write_report_title(report_name) + + self._write_filters(filters) + + self._generate_report_content(workbook, report) + + def _define_formats(self, workbook): + """ Add cell formats to current workbook. + Those formats can be used on all cell. + + Available formats are : + * format_bold + * format_right + * format_right_bold_italic + * format_header_left + * format_header_center + * format_header_right + * format_header_amount + * format_amount + * format_percent_bold_italic + """ + self.format_bold = workbook.add_format({'bold': True}) + self.format_right = workbook.add_format({'align': 'right'}) + self.format_right_bold_italic = workbook.add_format( + {'align': 'right', 'bold': True, 'italic': True} + ) + self.format_header_left = workbook.add_format( + {'bold': True, + 'border': True, + 'bg_color': '#FFFFCC'}) + self.format_header_center = workbook.add_format( + {'bold': True, + 'align': 'center', + 'border': True, + 'bg_color': '#FFFFCC'}) + self.format_header_right = workbook.add_format( + {'bold': True, + 'align': 'right', + 'border': True, + 'bg_color': '#FFFFCC'}) + self.format_header_amount = workbook.add_format( + {'bold': True, + 'border': True, + 'bg_color': '#FFFFCC'}) + self.format_header_amount.set_num_format('#,##0.00') + self.format_amount = workbook.add_format() + self.format_amount.set_num_format('#,##0.00') + self.format_percent_bold_italic = workbook.add_format( + {'bold': True, 'italic': True} + ) + self.format_percent_bold_italic.set_num_format('#,##0.00%') + + def _set_column_width(self): + """Set width for all defined columns. + Columns are defined with `_get_report_columns` method. + """ + for position, column in self.columns.items(): + self.sheet.set_column(position, position, column['width']) + + def _write_report_title(self, title): + """Write report title on current line using all defined columns width. + Columns are defined with `_get_report_columns` method. + """ + self.sheet.merge_range( + self.row_pos, 0, self.row_pos, len(self.columns) - 1, + title, self.format_bold + ) + self.row_pos += 3 + + def _write_filters(self, filters): + """Write one line per filters on starting on current line. + Columns number for filter name is defined + with `_get_col_count_filter_name` method. + Columns number for filter value is define + with `_get_col_count_filter_value` method. + """ + col_name = 1 + col_count_filter_name = self._get_col_count_filter_name() + col_count_filter_value = self._get_col_count_filter_value() + col_value = col_name + col_count_filter_name + 1 + for title, value in filters: + self.sheet.merge_range( + self.row_pos, col_name, + self.row_pos, col_name + col_count_filter_name - 1, + title, self.format_header_left) + self.sheet.merge_range( + self.row_pos, col_value, + self.row_pos, col_value + col_count_filter_value - 1, + value) + self.row_pos += 1 + self.row_pos += 2 + + def write_array_title(self, title): + """Write array title on current line using all defined columns width. + Columns are defined with `_get_report_columns` method. + """ + self.sheet.merge_range( + self.row_pos, 0, self.row_pos, len(self.columns) - 1, + title, self.format_bold + ) + self.row_pos += 1 + + def write_array_header(self): + """Write array header on current line using all defined columns name. + Columns are defined with `_get_report_columns` method. + """ + for col_pos, column in self.columns.items(): + self.sheet.write(self.row_pos, col_pos, column['header'], + self.format_header_center) + self.row_pos += 1 + + def write_line(self, line_object): + """Write a line on current line using all defined columns field name. + Columns are defined with `_get_report_columns` method. + """ + for col_pos, column in self.columns.items(): + value = getattr(line_object, column['field']) + cell_type = column.get('type', 'string') + if cell_type == 'string': + self.sheet.write_string(self.row_pos, col_pos, value or '') + elif cell_type == 'amount': + self.sheet.write_number( + self.row_pos, col_pos, float(value), self.format_amount + ) + self.row_pos += 1 + + def write_initial_balance(self, my_object, label): + """Write a specific initial balance line on current line + using defined columns field_initial_balance name. + + Columns are defined with `_get_report_columns` method. + """ + col_pos_label = self._get_col_pos_initial_balance_label() + self.sheet.write(self.row_pos, col_pos_label, label, self.format_right) + for col_pos, column in self.columns.items(): + if column.get('field_initial_balance'): + value = getattr(my_object, column['field_initial_balance']) + cell_type = column.get('type', 'string') + if cell_type == 'string': + self.sheet.write_string(self.row_pos, col_pos, value or '') + elif cell_type == 'amount': + self.sheet.write_number( + self.row_pos, col_pos, float(value), self.format_amount + ) + self.row_pos += 1 + + def write_ending_balance(self, my_object, name, label): + """Write a specific ending balance line on current line + using defined columns field_final_balance name. + + Columns are defined with `_get_report_columns` method. + """ + for i in range(0, len(self.columns)): + self.sheet.write(self.row_pos, i, '', self.format_header_right) + row_count_name = self._get_col_count_final_balance_name() + col_pos_label = self._get_col_pos_final_balance_label() + self.sheet.merge_range( + self.row_pos, 0, self.row_pos, row_count_name - 1, name, + self.format_header_left + ) + self.sheet.write(self.row_pos, col_pos_label, label, + self.format_header_right) + for col_pos, column in self.columns.items(): + if column.get('field_final_balance'): + value = getattr(my_object, column['field_final_balance']) + cell_type = column.get('type', 'string') + if cell_type == 'string': + self.sheet.write_string(self.row_pos, col_pos, value or '', + self.format_header_right) + elif cell_type == 'amount': + self.sheet.write_number( + self.row_pos, col_pos, float(value), + self.format_header_amount + ) + self.row_pos += 1 + + def _generate_report_content(self, workbook, report): + pass + + def _get_report_name(self): + """ + Allow to define the report name. + Report name will be used as sheet name and as report title. + + :return: the report name + """ + raise NotImplementedError() + + def _get_report_columns(self, report): + """ + Allow to define the report columns + which will be used to generate report. + + :return: the report columns as dict + + :Example: + + { + 0: {'header': 'Simple column', + 'field': 'field_name_on_my_object', + 'width': 11}, + 1: {'header': 'Amount column', + 'field': 'field_name_on_my_object', + 'type': 'amount', + 'width': 14}, + } + """ + raise NotImplementedError() + + def _get_report_filters(self, report): + """ + :return: the report filters as list + + :Example: + + [ + ['first_filter_name', 'first_filter_value'], + ['second_filter_name', 'second_filter_value'] + ] + """ + raise NotImplementedError() + + def _get_col_count_filter_name(self): + """ + :return: the columns number used for filter names. + """ + raise NotImplementedError() + + def _get_col_count_filter_value(self): + """ + :return: the columns number used for filter values. + """ + raise NotImplementedError() + + def _get_col_pos_initial_balance_label(self): + """ + :return: the columns position used for initial balance label. + """ + raise NotImplementedError() + + def _get_col_count_final_balance_name(self): + """ + :return: the columns number used for final balance name. + """ + raise NotImplementedError() + + def _get_col_pos_final_balance_label(self): + """ + :return: the columns position used for final balance label. + """ + raise NotImplementedError() diff --git a/account_financial_report/report/aged_partner_balance.py b/account_financial_report/report/aged_partner_balance.py new file mode 100644 index 000000000000..382f6d5fc74f --- /dev/null +++ b/account_financial_report/report/aged_partner_balance.py @@ -0,0 +1,628 @@ +# ?? 2016 Julien Coux (Camptocamp) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import models, fields, api + + +class AgedPartnerBalanceReport(models.TransientModel): + """ Here, we just define class fields. + For methods, go more bottom at this file. + + The class hierarchy is : + * AgedPartnerBalanceReport + ** AgedPartnerBalanceReportAccount + *** AgedPartnerBalanceReportPartner + **** AgedPartnerBalanceReportLine + **** AgedPartnerBalanceReportMoveLine + If "show_move_line_details" is selected + """ + + _name = 'report_aged_partner_balance' + + # Filters fields, used for data computation + date_at = fields.Date() + only_posted_moves = fields.Boolean() + company_id = fields.Many2one(comodel_name='res.company') + filter_account_ids = fields.Many2many(comodel_name='account.account') + filter_partner_ids = fields.Many2many(comodel_name='res.partner') + show_move_line_details = fields.Boolean() + + # Open Items Report Data fields, used as base for compute the data reports + open_items_id = fields.Many2one(comodel_name='report_open_items') + + # Data fields, used to browse report data + account_ids = fields.One2many( + comodel_name='report_aged_partner_balance_account', + inverse_name='report_id' + ) + + +class AgedPartnerBalanceReportAccount(models.TransientModel): + _name = 'report_aged_partner_balance_account' + _order = 'code ASC' + + report_id = fields.Many2one( + comodel_name='report_aged_partner_balance', + ondelete='cascade', + index=True + ) + + # Data fields, used to keep link with real object + account_id = fields.Many2one( + 'account.account', + index=True + ) + + # Data fields, used for report display + code = fields.Char() + name = fields.Char() + + cumul_amount_residual = fields.Float(digits=(16, 2)) + cumul_current = fields.Float(digits=(16, 2)) + cumul_age_30_days = fields.Float(digits=(16, 2)) + cumul_age_60_days = fields.Float(digits=(16, 2)) + cumul_age_90_days = fields.Float(digits=(16, 2)) + cumul_age_120_days = fields.Float(digits=(16, 2)) + cumul_older = fields.Float(digits=(16, 2)) + + percent_current = fields.Float(digits=(16, 2)) + percent_age_30_days = fields.Float(digits=(16, 2)) + percent_age_60_days = fields.Float(digits=(16, 2)) + percent_age_90_days = fields.Float(digits=(16, 2)) + percent_age_120_days = fields.Float(digits=(16, 2)) + percent_older = fields.Float(digits=(16, 2)) + + # Data fields, used to browse report data + partner_ids = fields.One2many( + comodel_name='report_aged_partner_balance_partner', + inverse_name='report_account_id' + ) + + +class AgedPartnerBalanceReportPartner(models.TransientModel): + _name = 'report_aged_partner_balance_partner' + + report_account_id = fields.Many2one( + comodel_name='report_aged_partner_balance_account', + ondelete='cascade', + index=True + ) + + # Data fields, used to keep link with real object + partner_id = fields.Many2one( + 'res.partner', + index=True + ) + + # Data fields, used for report display + name = fields.Char() + + # Data fields, used to browse report data + move_line_ids = fields.One2many( + comodel_name='report_aged_partner_balance_move_line', + inverse_name='report_partner_id' + ) + line_ids = fields.One2many( + comodel_name='report_aged_partner_balance_line', + inverse_name='report_partner_id' + ) + + @api.model + def _generate_order_by(self, order_spec, query): + """Custom order to display "No partner allocated" at last position.""" + return """ +ORDER BY + CASE + WHEN + "report_aged_partner_balance_partner"."partner_id" IS NOT NULL + THEN 0 + ELSE 1 + END, + "report_aged_partner_balance_partner"."name" + """ + + +class AgedPartnerBalanceReportLine(models.TransientModel): + _name = 'report_aged_partner_balance_line' + + report_partner_id = fields.Many2one( + comodel_name='report_aged_partner_balance_partner', + ondelete='cascade', + index=True + ) + + # Data fields, used for report display + partner = fields.Char() + amount_residual = fields.Float(digits=(16, 2)) + current = fields.Float(digits=(16, 2)) + age_30_days = fields.Float(digits=(16, 2)) + age_60_days = fields.Float(digits=(16, 2)) + age_90_days = fields.Float(digits=(16, 2)) + age_120_days = fields.Float(digits=(16, 2)) + older = fields.Float(digits=(16, 2)) + + +class AgedPartnerBalanceReportMoveLine(models.TransientModel): + _name = 'report_aged_partner_balance_move_line' + + report_partner_id = fields.Many2one( + comodel_name='report_aged_partner_balance_partner', + ondelete='cascade', + index=True + ) + + # Data fields, used to keep link with real object + move_line_id = fields.Many2one('account.move.line') + + # Data fields, used for report display + date = fields.Date() + date_due = fields.Date() + entry = fields.Char() + journal = fields.Char() + account = fields.Char() + partner = fields.Char() + label = fields.Char() + + amount_residual = fields.Float(digits=(16, 2)) + current = fields.Float(digits=(16, 2)) + age_30_days = fields.Float(digits=(16, 2)) + age_60_days = fields.Float(digits=(16, 2)) + age_90_days = fields.Float(digits=(16, 2)) + age_120_days = fields.Float(digits=(16, 2)) + older = fields.Float(digits=(16, 2)) + + +class AgedPartnerBalanceReportCompute(models.TransientModel): + """ Here, we just define methods. + For class fields, go more top at this file. + """ + + _inherit = 'report_aged_partner_balance' + + @api.multi + def print_report(self, report_type): + self.ensure_one() + if report_type == 'xlsx': + report_name = 'a_f_r.report_aged_partner_balance_xlsx' + else: + report_name = 'account_financial_report.' \ + 'report_aged_partner_balance_qweb' + report = self.env['ir.actions.report'].search( + [('report_name', '=', report_name), + ('report_type', '=', report_type)], limit=1) + return report.report_action(self) + + def _get_html(self): + result = {} + rcontext = {} + context = dict(self.env.context) + report = self.browse(context.get('active_id')) + if report: + rcontext['o'] = report + result['html'] = self.env.ref( + 'account_financial_report.report_aged_partner_balance').render( + rcontext) + return result + + @api.model + def get_html(self, given_context=None): + return self._get_html() + + def _prepare_report_open_items(self): + self.ensure_one() + return { + 'date_at': self.date_at, + 'only_posted_moves': self.only_posted_moves, + 'company_id': self.company_id.id, + 'filter_account_ids': [(6, 0, self.filter_account_ids.ids)], + 'filter_partner_ids': [(6, 0, self.filter_partner_ids.ids)], + } + + @api.multi + def compute_data_for_report(self): + self.ensure_one() + # Compute Open Items Report Data. + # The data of Aged Partner Balance Report + # are based on Open Items Report data. + model = self.env['report_open_items'] + self.open_items_id = model.create(self._prepare_report_open_items()) + self.open_items_id.compute_data_for_report() + + # Compute report data + self._inject_account_values() + self._inject_partner_values() + self._inject_line_values() + self._inject_line_values(only_empty_partner_line=True) + if self.show_move_line_details: + self._inject_move_line_values() + self._inject_move_line_values(only_empty_partner_line=True) + self._compute_accounts_cumul() + # Refresh cache because all data are computed with SQL requests + self.refresh() + + def _inject_account_values(self): + """Inject report values for report_aged_partner_balance_account""" + query_inject_account = """ +INSERT INTO + report_aged_partner_balance_account + ( + report_id, + create_uid, + create_date, + account_id, + code, + name + ) +SELECT + %s AS report_id, + %s AS create_uid, + NOW() AS create_date, + rao.account_id, + rao.code, + rao.name +FROM + report_open_items_account rao +WHERE + rao.report_id = %s + """ + query_inject_account_params = ( + self.id, + self.env.uid, + self.open_items_id.id, + ) + self.env.cr.execute(query_inject_account, query_inject_account_params) + + def _inject_partner_values(self): + """Inject report values for report_aged_partner_balance_partner""" + query_inject_partner = """ +INSERT INTO + report_aged_partner_balance_partner + ( + report_account_id, + create_uid, + create_date, + partner_id, + name + ) +SELECT + ra.id AS report_account_id, + %s AS create_uid, + NOW() AS create_date, + rpo.partner_id, + rpo.name +FROM + report_open_items_partner rpo +INNER JOIN + report_open_items_account rao ON rpo.report_account_id = rao.id +INNER JOIN + report_aged_partner_balance_account ra ON rao.code = ra.code +WHERE + rao.report_id = %s +AND ra.report_id = %s + """ + query_inject_partner_params = ( + self.env.uid, + self.open_items_id.id, + self.id, + ) + self.env.cr.execute(query_inject_partner, query_inject_partner_params) + + def _inject_line_values(self, only_empty_partner_line=False): + """ Inject report values for report_aged_partner_balance_line. + + The "only_empty_partner_line" value is used + to compute data without partner. + """ + query_inject_line = """ +WITH + date_range AS + ( + SELECT + %s AS date_current, + DATE %s - INTEGER '30' AS date_less_30_days, + DATE %s - INTEGER '60' AS date_less_60_days, + DATE %s - INTEGER '90' AS date_less_90_days, + DATE %s - INTEGER '120' AS date_less_120_days, + DATE %s - INTEGER '150' AS date_older + ) +INSERT INTO + report_aged_partner_balance_line + ( + report_partner_id, + create_uid, + create_date, + partner, + amount_residual, + current, + age_30_days, + age_60_days, + age_90_days, + age_120_days, + older + ) +SELECT + rp.id AS report_partner_id, + %s AS create_uid, + NOW() AS create_date, + rp.name, + SUM(rlo.amount_residual) AS amount_residual, + SUM( + CASE + WHEN rlo.date_due > date_range.date_less_30_days + THEN rlo.amount_residual + END + ) AS current, + SUM( + CASE + WHEN + rlo.date_due > date_range.date_less_60_days + AND rlo.date_due <= date_range.date_less_30_days + THEN rlo.amount_residual + END + ) AS age_30_days, + SUM( + CASE + WHEN + rlo.date_due > date_range.date_less_90_days + AND rlo.date_due <= date_range.date_less_60_days + THEN rlo.amount_residual + END + ) AS age_60_days, + SUM( + CASE + WHEN + rlo.date_due > date_range.date_less_120_days + AND rlo.date_due <= date_range.date_less_90_days + THEN rlo.amount_residual + END + ) AS age_90_days, + SUM( + CASE + WHEN + rlo.date_due > date_range.date_older + AND rlo.date_due <= date_range.date_less_120_days + THEN rlo.amount_residual + END + ) AS age_120_days, + SUM( + CASE + WHEN rlo.date_due <= date_range.date_older + THEN rlo.amount_residual + END + ) AS older +FROM + date_range, + report_open_items_move_line rlo +INNER JOIN + report_open_items_partner rpo ON rlo.report_partner_id = rpo.id +INNER JOIN + report_open_items_account rao ON rpo.report_account_id = rao.id +INNER JOIN + report_aged_partner_balance_account ra ON rao.code = ra.code +INNER JOIN + report_aged_partner_balance_partner rp + ON + ra.id = rp.report_account_id + """ + if not only_empty_partner_line: + query_inject_line += """ + AND rpo.partner_id = rp.partner_id + """ + elif only_empty_partner_line: + query_inject_line += """ + AND rpo.partner_id IS NULL + AND rp.partner_id IS NULL + """ + query_inject_line += """ +WHERE + rao.report_id = %s +AND ra.report_id = %s +GROUP BY + rp.id + """ + query_inject_line_params = (self.date_at,) * 6 + query_inject_line_params += ( + self.env.uid, + self.open_items_id.id, + self.id, + ) + self.env.cr.execute(query_inject_line, query_inject_line_params) + + def _inject_move_line_values(self, only_empty_partner_line=False): + """ Inject report values for report_aged_partner_balance_move_line + + The "only_empty_partner_line" value is used + to compute data without partner. + """ + query_inject_move_line = """ +WITH + date_range AS + ( + SELECT + %s AS date_current, + DATE %s - INTEGER '30' AS date_less_30_days, + DATE %s - INTEGER '60' AS date_less_60_days, + DATE %s - INTEGER '90' AS date_less_90_days, + DATE %s - INTEGER '120' AS date_less_120_days, + DATE %s - INTEGER '150' AS date_older + ) +INSERT INTO + report_aged_partner_balance_move_line + ( + report_partner_id, + create_uid, + create_date, + date, + date_due, + entry, + journal, + account, + partner, + label, + amount_residual, + current, + age_30_days, + age_60_days, + age_90_days, + age_120_days, + older + ) +SELECT + rp.id AS report_partner_id, + %s AS create_uid, + NOW() AS create_date, + rlo.date, + rlo.date_due, + rlo.entry, + rlo.journal, + rlo.account, + rlo.partner, + rlo.label, + rlo.amount_residual AS amount_residual, + CASE + WHEN rlo.date_due > date_range.date_less_30_days + THEN rlo.amount_residual + END AS current, + CASE + WHEN + rlo.date_due > date_range.date_less_60_days + AND rlo.date_due <= date_range.date_less_30_days + THEN rlo.amount_residual + END AS age_30_days, + CASE + WHEN + rlo.date_due > date_range.date_less_90_days + AND rlo.date_due <= date_range.date_less_60_days + THEN rlo.amount_residual + END AS age_60_days, + CASE + WHEN + rlo.date_due > date_range.date_less_120_days + AND rlo.date_due <= date_range.date_less_90_days + THEN rlo.amount_residual + END AS age_90_days, + CASE + WHEN + rlo.date_due > date_range.date_older + AND rlo.date_due <= date_range.date_less_120_days + THEN rlo.amount_residual + END AS age_120_days, + CASE + WHEN rlo.date_due <= date_range.date_older + THEN rlo.amount_residual + END AS older +FROM + date_range, + report_open_items_move_line rlo +INNER JOIN + report_open_items_partner rpo ON rlo.report_partner_id = rpo.id +INNER JOIN + report_open_items_account rao ON rpo.report_account_id = rao.id +INNER JOIN + report_aged_partner_balance_account ra ON rao.code = ra.code +INNER JOIN + report_aged_partner_balance_partner rp + ON + ra.id = rp.report_account_id + """ + if not only_empty_partner_line: + query_inject_move_line += """ + AND rpo.partner_id = rp.partner_id + """ + elif only_empty_partner_line: + query_inject_move_line += """ + AND rpo.partner_id IS NULL + AND rp.partner_id IS NULL + """ + query_inject_move_line += """ +WHERE + rao.report_id = %s +AND ra.report_id = %s + """ + query_inject_move_line_params = (self.date_at,) * 6 + query_inject_move_line_params += ( + self.env.uid, + self.open_items_id.id, + self.id, + ) + self.env.cr.execute(query_inject_move_line, + query_inject_move_line_params) + + def _compute_accounts_cumul(self): + """ Compute cumulative amount for + report_aged_partner_balance_account. + """ + query_compute_accounts_cumul = """ +WITH + cumuls AS + ( + SELECT + ra.id AS report_account_id, + SUM(rl.amount_residual) AS cumul_amount_residual, + SUM(rl.current) AS cumul_current, + SUM(rl.age_30_days) AS cumul_age_30_days, + SUM(rl.age_60_days) AS cumul_age_60_days, + SUM(rl.age_90_days) AS cumul_age_90_days, + SUM(rl.age_120_days) AS cumul_age_120_days, + SUM(rl.older) AS cumul_older + FROM + report_aged_partner_balance_line rl + INNER JOIN + report_aged_partner_balance_partner rp + ON rl.report_partner_id = rp.id + INNER JOIN + report_aged_partner_balance_account ra + ON rp.report_account_id = ra.id + WHERE + ra.report_id = %s + GROUP BY + ra.id + ) +UPDATE + report_aged_partner_balance_account +SET + cumul_amount_residual = c.cumul_amount_residual, + cumul_current = c.cumul_current, + cumul_age_30_days = c.cumul_age_30_days, + cumul_age_60_days = c.cumul_age_60_days, + cumul_age_90_days = c.cumul_age_90_days, + cumul_age_120_days = c.cumul_age_120_days, + cumul_older = c.cumul_older, + percent_current = + CASE + WHEN c.cumul_amount_residual != 0 + THEN 100 * c.cumul_current / c.cumul_amount_residual + END, + percent_age_30_days = + CASE + WHEN c.cumul_amount_residual != 0 + THEN 100 * c.cumul_age_30_days / c.cumul_amount_residual + END, + percent_age_60_days = + CASE + WHEN c.cumul_amount_residual != 0 + THEN 100 * c.cumul_age_60_days / c.cumul_amount_residual + END, + percent_age_90_days = + CASE + WHEN c.cumul_amount_residual != 0 + THEN 100 * c.cumul_age_90_days / c.cumul_amount_residual + END, + percent_age_120_days = + CASE + WHEN c.cumul_amount_residual != 0 + THEN 100 * c.cumul_age_120_days / c.cumul_amount_residual + END, + percent_older = + CASE + WHEN c.cumul_amount_residual != 0 + THEN 100 * c.cumul_older / c.cumul_amount_residual + END +FROM + cumuls c +WHERE + id = c.report_account_id + """ + params_compute_accounts_cumul = (self.id,) + self.env.cr.execute(query_compute_accounts_cumul, + params_compute_accounts_cumul) diff --git a/account_financial_report/report/aged_partner_balance_xlsx.py b/account_financial_report/report/aged_partner_balance_xlsx.py new file mode 100644 index 000000000000..10ca6a2cf8a2 --- /dev/null +++ b/account_financial_report/report/aged_partner_balance_xlsx.py @@ -0,0 +1,257 @@ + +# Author: Julien Coux +# Copyright 2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import _, models + + +class AgedPartnerBalanceXslx(models.AbstractModel): + _name = 'report.a_f_r.report_aged_partner_balance_xlsx' + _inherit = 'report.account_financial_report.abstract_report_xlsx' + + def _get_report_name(self): + return _('Aged Partner Balance') + + def _get_report_columns(self, report): + if not report.show_move_line_details: + return { + 0: {'header': _('Partner'), 'field': 'partner', 'width': 70}, + 1: {'header': _('Residual'), + 'field': 'amount_residual', + 'field_footer_total': 'cumul_amount_residual', + 'type': 'amount', + 'width': 14}, + 2: {'header': _('Current'), + 'field': 'current', + 'field_footer_total': 'cumul_current', + 'field_footer_percent': 'percent_current', + 'type': 'amount', + 'width': 14}, + 3: {'header': _(u'Age ??? 30 d.'), + 'field': 'age_30_days', + 'field_footer_total': 'cumul_age_30_days', + 'field_footer_percent': 'percent_age_30_days', + 'type': 'amount', + 'width': 14}, + 4: {'header': _(u'Age ??? 60 d.'), + 'field': 'age_60_days', + 'field_footer_total': 'cumul_age_60_days', + 'field_footer_percent': 'percent_age_60_days', + 'type': 'amount', + 'width': 14}, + 5: {'header': _(u'Age ??? 90 d.'), + 'field': 'age_90_days', + 'field_footer_total': 'cumul_age_90_days', + 'field_footer_percent': 'percent_age_90_days', + 'type': 'amount', + 'width': 14}, + 6: {'header': _(u'Age ??? 120 d.'), + 'field': 'age_120_days', + 'field_footer_total': 'cumul_age_120_days', + 'field_footer_percent': 'percent_age_120_days', + 'type': 'amount', + 'width': 14}, + 7: {'header': _('Older'), + 'field': 'older', + 'field_footer_total': 'cumul_older', + 'field_footer_percent': 'percent_older', + 'type': 'amount', + 'width': 14}, + } + else: + return { + 0: {'header': _('Date'), 'field': 'date', 'width': 11}, + 1: {'header': _('Entry'), 'field': 'entry', 'width': 18}, + 2: {'header': _('Journal'), 'field': 'journal', 'width': 8}, + 3: {'header': _('Account'), 'field': 'account', 'width': 9}, + 4: {'header': _('Partner'), 'field': 'partner', 'width': 25}, + 5: {'header': _('Ref - Label'), 'field': 'label', 'width': 40}, + 6: {'header': _('Due date'), 'field': 'date_due', 'width': 11}, + 7: {'header': _('Residual'), + 'field': 'amount_residual', + 'field_footer_total': 'cumul_amount_residual', + 'field_final_balance': 'amount_residual', + 'type': 'amount', + 'width': 14}, + 8: {'header': _('Current'), + 'field': 'current', + 'field_footer_total': 'cumul_current', + 'field_footer_percent': 'percent_current', + 'field_final_balance': 'current', + 'type': 'amount', + 'width': 14}, + 9: {'header': _(u'Age ??? 30 d.'), + 'field': 'age_30_days', + 'field_footer_total': 'cumul_age_30_days', + 'field_footer_percent': 'percent_age_30_days', + 'field_final_balance': 'age_30_days', + 'type': 'amount', + 'width': 14}, + 10: {'header': _(u'Age ??? 60 d.'), + 'field': 'age_60_days', + 'field_footer_total': 'cumul_age_60_days', + 'field_footer_percent': 'percent_age_60_days', + 'field_final_balance': 'age_60_days', + 'type': 'amount', + 'width': 14}, + 11: {'header': _(u'Age ??? 90 d.'), + 'field': 'age_90_days', + 'field_footer_total': 'cumul_age_90_days', + 'field_footer_percent': 'percent_age_90_days', + 'field_final_balance': 'age_90_days', + 'type': 'amount', + 'width': 14}, + 12: {'header': _(u'Age ??? 120 d.'), + 'field': 'age_120_days', + 'field_footer_total': 'cumul_age_120_days', + 'field_footer_percent': 'percent_age_120_days', + 'field_final_balance': 'age_120_days', + 'type': 'amount', + 'width': 14}, + 13: {'header': _('Older'), + 'field': 'older', + 'field_footer_total': 'cumul_older', + 'field_footer_percent': 'percent_older', + 'field_final_balance': 'older', + 'type': 'amount', + 'width': 14}, + } + + def _get_report_filters(self, report): + return [ + [_('Date at filter'), report.date_at], + [_('Target moves filter'), + _('All posted entries') if report.only_posted_moves + else _('All entries')], + ] + + def _get_col_count_filter_name(self): + return 2 + + def _get_col_count_filter_value(self): + return 3 + + def _get_col_pos_footer_label(self, report): + return 0 if not report.show_move_line_details else 5 + + def _get_col_count_final_balance_name(self): + return 5 + + def _get_col_pos_final_balance_label(self): + return 5 + + def _generate_report_content(self, workbook, report): + if not report.show_move_line_details: + # For each account + for account in report.account_ids: + # Write account title + self.write_array_title(account.code + ' - ' + account.name) + + # Display array header for partners lines + self.write_array_header() + + # Display partner lines + for partner in account.partner_ids: + self.write_line(partner.line_ids) + + # Display account lines + self.write_account_footer(report, + account, + _('Total'), + 'field_footer_total', + self.format_header_right, + self.format_header_amount, + False) + self.write_account_footer(report, + account, + _('Percents'), + 'field_footer_percent', + self.format_right_bold_italic, + self.format_percent_bold_italic, + True) + + # 2 lines break + self.row_pos += 2 + else: + # For each account + for account in report.account_ids: + # Write account title + self.write_array_title(account.code + ' - ' + account.name) + + # For each partner + for partner in account.partner_ids: + # Write partner title + self.write_array_title(partner.name) + + # Display array header for move lines + self.write_array_header() + + # Display account move lines + for line in partner.move_line_ids: + self.write_line(line) + + # Display ending balance line for partner + self.write_ending_balance(partner.line_ids) + + # Line break + self.row_pos += 1 + + # Display account lines + self.write_account_footer(report, + account, + _('Total'), + 'field_footer_total', + self.format_header_right, + self.format_header_amount, + False) + self.write_account_footer(report, + account, + _('Percents'), + 'field_footer_percent', + self.format_right_bold_italic, + self.format_percent_bold_italic, + True) + + # 2 lines break + self.row_pos += 2 + + def write_ending_balance(self, my_object): + """ + Specific function to write ending partner balance + for Aged Partner Balance + """ + name = None + label = _('Partner cumul aged balance') + super(AgedPartnerBalanceXslx, self).write_ending_balance( + my_object, name, label + ) + + def write_account_footer(self, report, account, label, field_name, + string_format, amount_format, amount_is_percent): + """ + Specific function to write account footer for Aged Partner Balance + """ + col_pos_footer_label = self._get_col_pos_footer_label(report) + for col_pos, column in self.columns.items(): + if col_pos == col_pos_footer_label or column.get(field_name): + if col_pos == col_pos_footer_label: + value = label + else: + value = getattr(account, column[field_name]) + cell_type = column.get('type', 'string') + if cell_type == 'string' or col_pos == col_pos_footer_label: + self.sheet.write_string(self.row_pos, col_pos, value or '', + string_format) + elif cell_type == 'amount': + number = float(value) + if amount_is_percent: + number /= 100 + self.sheet.write_number(self.row_pos, col_pos, + number, + amount_format) + else: + self.sheet.write_string(self.row_pos, col_pos, '', + string_format) + + self.row_pos += 1 diff --git a/account_financial_report/report/general_ledger.py b/account_financial_report/report/general_ledger.py new file mode 100644 index 000000000000..bed83cc90a2f --- /dev/null +++ b/account_financial_report/report/general_ledger.py @@ -0,0 +1,1431 @@ + +# ?? 2016 Julien Coux (Camptocamp) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import models, fields, api, _ + + +class GeneralLedgerReport(models.TransientModel): + """ Here, we just define class fields. + For methods, go more bottom at this file. + + The class hierarchy is : + * GeneralLedgerReport + ** GeneralLedgerReportAccount + *** GeneralLedgerReportMoveLine + For non receivable/payable accounts + For receivable/payable centralized accounts + *** GeneralLedgerReportPartner + For receivable/payable and not centralized accounts + **** GeneralLedgerReportMoveLine + For receivable/payable and not centralized accounts + """ + + _name = 'report_general_ledger' + + # Filters fields, used for data computation + date_from = fields.Date() + date_to = fields.Date() + fy_start_date = fields.Date() + only_posted_moves = fields.Boolean() + hide_account_balance_at_0 = fields.Boolean() + company_id = fields.Many2one(comodel_name='res.company') + filter_account_ids = fields.Many2many(comodel_name='account.account') + filter_partner_ids = fields.Many2many(comodel_name='res.partner') + filter_cost_center_ids = fields.Many2many( + comodel_name='account.analytic.account' + ) + centralize = fields.Boolean() + + # Flag fields, used for report display + has_second_currency = fields.Boolean() + show_cost_center = fields.Boolean( + default=lambda self: self.env.user.has_group( + 'analytic.group_analytic_accounting' + ) + ) + + # Data fields, used to browse report data + account_ids = fields.One2many( + comodel_name='report_general_ledger_account', + inverse_name='report_id' + ) + + # Compute of unaffected earnings account + @api.depends('company_id') + def _compute_unaffected_earnings_account(self): + account_type = self.env.ref('account.data_unaffected_earnings') + self.unaffected_earnings_account = self.env['account.account'].search( + [ + ('user_type_id', '=', account_type.id), + ('company_id', '=', self.company_id.id) + ]) + + unaffected_earnings_account = fields.Many2one( + comodel_name='account.account', + compute='_compute_unaffected_earnings_account', + store=True + ) + + +class GeneralLedgerReportAccount(models.TransientModel): + + _name = 'report_general_ledger_account' + _order = 'code ASC' + + report_id = fields.Many2one( + comodel_name='report_general_ledger', + ondelete='cascade', + index=True + ) + + # Data fields, used to keep link with real object + account_id = fields.Many2one( + 'account.account', + index=True + ) + + # Data fields, used for report display + code = fields.Char() + name = fields.Char() + initial_debit = fields.Float(digits=(16, 2)) + initial_credit = fields.Float(digits=(16, 2)) + initial_balance = fields.Float(digits=(16, 2)) + final_debit = fields.Float(digits=(16, 2)) + final_credit = fields.Float(digits=(16, 2)) + final_balance = fields.Float(digits=(16, 2)) + + # Flag fields, used for report display and for data computation + is_partner_account = fields.Boolean() + + # Data fields, used to browse report data + move_line_ids = fields.One2many( + comodel_name='report_general_ledger_move_line', + inverse_name='report_account_id' + ) + partner_ids = fields.One2many( + comodel_name='report_general_ledger_partner', + inverse_name='report_account_id' + ) + + +class GeneralLedgerReportPartner(models.TransientModel): + + _name = 'report_general_ledger_partner' + + report_account_id = fields.Many2one( + comodel_name='report_general_ledger_account', + ondelete='cascade', + index=True + ) + + # Data fields, used to keep link with real object + partner_id = fields.Many2one( + 'res.partner', + index=True + ) + + # Data fields, used for report display + name = fields.Char() + initial_debit = fields.Float(digits=(16, 2)) + initial_credit = fields.Float(digits=(16, 2)) + initial_balance = fields.Float(digits=(16, 2)) + final_debit = fields.Float(digits=(16, 2)) + final_credit = fields.Float(digits=(16, 2)) + final_balance = fields.Float(digits=(16, 2)) + + # Data fields, used to browse report data + move_line_ids = fields.One2many( + comodel_name='report_general_ledger_move_line', + inverse_name='report_partner_id' + ) + + @api.model + def _generate_order_by(self, order_spec, query): + """Custom order to display "No partner allocated" at last position.""" + return """ +ORDER BY + CASE + WHEN "report_general_ledger_partner"."partner_id" IS NOT NULL + THEN 0 + ELSE 1 + END, + "report_general_ledger_partner"."name" + """ + + +class GeneralLedgerReportMoveLine(models.TransientModel): + + _name = 'report_general_ledger_move_line' + + report_account_id = fields.Many2one( + comodel_name='report_general_ledger_account', + ondelete='cascade', + index=True + ) + report_partner_id = fields.Many2one( + comodel_name='report_general_ledger_partner', + ondelete='cascade', + index=True + ) + + # Data fields, used to keep link with real object + move_line_id = fields.Many2one('account.move.line') + + # Data fields, used for report display + date = fields.Date() + entry = fields.Char() + journal = fields.Char() + account = fields.Char() + partner = fields.Char() + label = fields.Char() + cost_center = fields.Char() + matching_number = fields.Char() + debit = fields.Float(digits=(16, 2)) + credit = fields.Float(digits=(16, 2)) + cumul_balance = fields.Float(digits=(16, 2)) + currency_name = fields.Char() + amount_currency = fields.Float(digits=(16, 2)) + + +class GeneralLedgerReportCompute(models.TransientModel): + """ Here, we just define methods. + For class fields, go more top at this file. + """ + + _inherit = 'report_general_ledger' + + @api.multi + def print_report(self, report_type): + self.ensure_one() + if report_type == 'xlsx': + report_name = 'a_f_r.report_general_ledger_xlsx' + else: + report_name = 'account_financial_report.' \ + 'report_general_ledger_qweb' + return self.env['ir.actions.report'].search( + [('report_name', '=', report_name), + ('report_type', '=', report_type)], limit=1).report_action(self) + + def _get_html(self): + result = {} + rcontext = {} + context = dict(self.env.context) + report = self.browse(context.get('active_id')) + if report: + rcontext['o'] = report + result['html'] = self.env.ref( + 'account_financial_report.report_general_ledger').render( + rcontext) + return result + + @api.model + def get_html(self, given_context=None): + return self._get_html() + + @api.multi + def compute_data_for_report(self, + with_line_details=True, + with_partners=True + ): + self.ensure_one() + # Compute report data + self._inject_account_values() + + if with_partners: + self._inject_partner_values() + if not self.filter_partner_ids: + self._inject_partner_values(only_empty_partner=True) + + # Add unaffected earnings account + if (not self.filter_account_ids or + self.unaffected_earnings_account.id in + self.filter_account_ids.ids): + self._inject_unaffected_earnings_account_values() + + # Call this function even if we don't want line details because, + # we need to compute + # at least the values for unaffected earnings account + # In this case, only unaffected earnings account values are computed + only_unaffected_earnings_account = not with_line_details + self._inject_line_not_centralized_values( + only_unaffected_earnings_account=only_unaffected_earnings_account + ) + + if with_line_details: + self._inject_line_not_centralized_values( + is_account_line=False, + is_partner_line=True) + + self._inject_line_not_centralized_values( + is_account_line=False, + is_partner_line=True, + only_empty_partner_line=True) + + if self.centralize: + self._inject_line_centralized_values() + + # Complete unaffected earnings account + if (not self.filter_account_ids or + self.unaffected_earnings_account.id in + self.filter_account_ids.ids): + self._complete_unaffected_earnings_account_values() + + if with_line_details: + # Compute display flag + self._compute_has_second_currency() + + # Refresh cache because all data are computed with SQL requests + self.refresh() + + def _get_account_sub_subquery_sum_amounts( + self, include_initial_balance, date_included): + """ Return subquery used to compute sum amounts on accounts """ + sub_subquery_sum_amounts = """ + SELECT + a.id AS account_id, + SUM(ml.debit) AS debit, + SUM(ml.credit) AS credit, + SUM(ml.balance) AS balance + FROM + accounts a + INNER JOIN + account_account_type at ON a.user_type_id = at.id + INNER JOIN + account_move_line ml + ON a.id = ml.account_id + """ + + if date_included: + sub_subquery_sum_amounts += """ + AND ml.date <= %s + """ + else: + sub_subquery_sum_amounts += """ + AND ml.date < %s + """ + + if not include_initial_balance: + sub_subquery_sum_amounts += """ + AND at.include_initial_balance != TRUE AND ml.date >= %s + """ + else: + sub_subquery_sum_amounts += """ + AND at.include_initial_balance = TRUE + """ + + if self.only_posted_moves: + sub_subquery_sum_amounts += """ + INNER JOIN + account_move m ON ml.move_id = m.id AND m.state = 'posted' + """ + if self.filter_cost_center_ids: + sub_subquery_sum_amounts += """ + INNER JOIN + account_analytic_account aa + ON + ml.analytic_account_id = aa.id + AND aa.id IN %s + """ + sub_subquery_sum_amounts += """ + GROUP BY + a.id + """ + return sub_subquery_sum_amounts + + def _get_final_account_sub_subquery_sum_amounts(self, date_included): + """ Return final subquery used to compute sum amounts on accounts """ + subquery_sum_amounts = """ + SELECT + sub.account_id AS account_id, + SUM(COALESCE(sub.debit, 0.0)) AS debit, + SUM(COALESCE(sub.credit, 0.0)) AS credit, + SUM(COALESCE(sub.balance, 0.0)) AS balance + FROM + ( + """ + subquery_sum_amounts += self._get_account_sub_subquery_sum_amounts( + include_initial_balance=False, date_included=date_included + ) + subquery_sum_amounts += """ + UNION + """ + subquery_sum_amounts += self._get_account_sub_subquery_sum_amounts( + include_initial_balance=True, date_included=date_included + ) + subquery_sum_amounts += """ + ) sub + GROUP BY + sub.account_id + """ + return subquery_sum_amounts + + def _inject_account_values(self): + """Inject report values for report_general_ledger_account.""" + query_inject_account = """ +WITH + accounts AS + ( + SELECT + a.id, + a.code, + a.name, + a.internal_type IN ('payable', 'receivable') + AS is_partner_account, + a.user_type_id + FROM + account_account a + """ + if self.filter_partner_ids or self.filter_cost_center_ids: + query_inject_account += """ + INNER JOIN + account_move_line ml ON a.id = ml.account_id + """ + if self.filter_partner_ids: + query_inject_account += """ + INNER JOIN + res_partner p ON ml.partner_id = p.id + """ + if self.filter_cost_center_ids: + query_inject_account += """ + INNER JOIN + account_analytic_account aa + ON + ml.analytic_account_id = aa.id + AND aa.id IN %s + """ + query_inject_account += """ + WHERE + a.company_id = %s + AND a.id != %s + """ + if self.filter_account_ids: + query_inject_account += """ + AND + a.id IN %s + """ + if self.filter_partner_ids: + query_inject_account += """ + AND + p.id IN %s + """ + if self.filter_partner_ids or self.filter_cost_center_ids: + query_inject_account += """ + GROUP BY + a.id + """ + + init_subquery = self._get_final_account_sub_subquery_sum_amounts( + date_included=False + ) + final_subquery = self._get_final_account_sub_subquery_sum_amounts( + date_included=True + ) + + query_inject_account += """ + ), + initial_sum_amounts AS ( """ + init_subquery + """ ), + final_sum_amounts AS ( """ + final_subquery + """ ) +INSERT INTO + report_general_ledger_account + ( + report_id, + create_uid, + create_date, + account_id, + code, + name, + initial_debit, + initial_credit, + initial_balance, + final_debit, + final_credit, + final_balance, + is_partner_account + ) +SELECT + %s AS report_id, + %s AS create_uid, + NOW() AS create_date, + a.id AS account_id, + a.code, + a.name, + COALESCE(i.debit, 0.0) AS initial_debit, + COALESCE(i.credit, 0.0) AS initial_credit, + COALESCE(i.balance, 0.0) AS initial_balance, + COALESCE(f.debit, 0.0) AS final_debit, + COALESCE(f.credit, 0.0) AS final_credit, + COALESCE(f.balance, 0.0) AS final_balance, + a.is_partner_account +FROM + accounts a +LEFT JOIN + initial_sum_amounts i ON a.id = i.account_id +LEFT JOIN + final_sum_amounts f ON a.id = f.account_id +WHERE + ( + i.debit IS NOT NULL AND i.debit != 0 + OR i.credit IS NOT NULL AND i.credit != 0 + OR i.balance IS NOT NULL AND i.balance != 0 + OR f.debit IS NOT NULL AND f.debit != 0 + OR f.credit IS NOT NULL AND f.credit != 0 + OR f.balance IS NOT NULL AND f.balance != 0 + ) + """ + if self.hide_account_balance_at_0: + query_inject_account += """ +AND + f.balance IS NOT NULL AND f.balance != 0 + """ + query_inject_account_params = () + if self.filter_cost_center_ids: + query_inject_account_params += ( + tuple(self.filter_cost_center_ids.ids), + ) + query_inject_account_params += ( + self.company_id.id, + self.unaffected_earnings_account.id, + ) + if self.filter_account_ids: + query_inject_account_params += ( + tuple(self.filter_account_ids.ids), + ) + if self.filter_partner_ids: + query_inject_account_params += ( + tuple(self.filter_partner_ids.ids), + ) + query_inject_account_params += ( + self.date_from, + self.fy_start_date, + ) + if self.filter_cost_center_ids: + query_inject_account_params += ( + tuple(self.filter_cost_center_ids.ids), + ) + query_inject_account_params += ( + self.date_from, + ) + if self.filter_cost_center_ids: + query_inject_account_params += ( + tuple(self.filter_cost_center_ids.ids), + ) + query_inject_account_params += ( + self.date_to, + self.fy_start_date, + ) + if self.filter_cost_center_ids: + query_inject_account_params += ( + tuple(self.filter_cost_center_ids.ids), + ) + query_inject_account_params += ( + self.date_to, + ) + if self.filter_cost_center_ids: + query_inject_account_params += ( + tuple(self.filter_cost_center_ids.ids), + ) + query_inject_account_params += ( + self.id, + self.env.uid, + ) + self.env.cr.execute(query_inject_account, query_inject_account_params) + + def _get_partner_sub_subquery_sum_amounts( + self, only_empty_partner, include_initial_balance, date_included + ): + """ Return subquery used to compute sum amounts on partners """ + sub_subquery_sum_amounts = """ + SELECT + ap.account_id AS account_id, + ap.partner_id AS partner_id, + SUM(ml.debit) AS debit, + SUM(ml.credit) AS credit, + SUM(ml.balance) AS balance + FROM + accounts_partners ap + INNER JOIN + account_move_line ml + ON ap.account_id = ml.account_id + """ + if date_included: + sub_subquery_sum_amounts += """ + AND ml.date <= %s + """ + else: + sub_subquery_sum_amounts += """ + AND ml.date < %s + """ + if not only_empty_partner: + sub_subquery_sum_amounts += """ + AND ap.partner_id = ml.partner_id + """ + else: + sub_subquery_sum_amounts += """ + AND ap.partner_id IS NULL AND ml.partner_id IS NULL + """ + if not include_initial_balance: + sub_subquery_sum_amounts += """ + AND ap.include_initial_balance != TRUE AND ml.date >= %s + """ + else: + sub_subquery_sum_amounts += """ + AND ap.include_initial_balance = TRUE + """ + if self.only_posted_moves: + sub_subquery_sum_amounts += """ + INNER JOIN + account_move m ON ml.move_id = m.id AND m.state = 'posted' + """ + if self.filter_cost_center_ids: + sub_subquery_sum_amounts += """ + INNER JOIN + account_analytic_account aa + ON + ml.analytic_account_id = aa.id + AND aa.id IN %s + """ + sub_subquery_sum_amounts += """ + GROUP BY + ap.account_id, ap.partner_id + """ + return sub_subquery_sum_amounts + + def _get_final_partner_sub_subquery_sum_amounts(self, only_empty_partner, + date_included): + """Return final subquery used to compute sum amounts on partners""" + + subquery_sum_amounts = """ + + SELECT + sub.account_id AS account_id, + sub.partner_id AS partner_id, + SUM(COALESCE(sub.debit, 0.0)) AS debit, + SUM(COALESCE(sub.credit, 0.0)) AS credit, + SUM(COALESCE(sub.balance, 0.0)) AS balance + FROM + ( + """ + subquery_sum_amounts += self._get_partner_sub_subquery_sum_amounts( + only_empty_partner, + include_initial_balance=False, + date_included=date_included + ) + subquery_sum_amounts += """ + UNION + """ + subquery_sum_amounts += self._get_partner_sub_subquery_sum_amounts( + only_empty_partner, + include_initial_balance=True, + date_included=date_included + ) + subquery_sum_amounts += """ + ) sub + GROUP BY + sub.account_id, sub.partner_id + """ + return subquery_sum_amounts + + def _inject_partner_values(self, only_empty_partner=False): + """ Inject report values for report_general_ledger_partner. + + Only for "partner" accounts (payable and receivable). + """ + # pylint: disable=sql-injection + query_inject_partner = """ +WITH + accounts_partners AS + ( + SELECT + ra.id AS report_account_id, + a.id AS account_id, + at.include_initial_balance AS include_initial_balance, + p.id AS partner_id, + COALESCE( + CASE + WHEN + NULLIF(p.name, '') IS NOT NULL + AND NULLIF(p.ref, '') IS NOT NULL + THEN p.name || ' (' || p.ref || ')' + ELSE p.name + END, + '""" + _('No partner allocated') + """' + ) AS partner_name + FROM + report_general_ledger_account ra + INNER JOIN + account_account a ON ra.account_id = a.id + INNER JOIN + account_account_type at ON a.user_type_id = at.id + INNER JOIN + account_move_line ml ON a.id = ml.account_id + LEFT JOIN + res_partner p ON ml.partner_id = p.id + """ + if self.filter_cost_center_ids: + query_inject_partner += """ + INNER JOIN + account_analytic_account aa + ON + ml.analytic_account_id = aa.id + AND aa.id IN %s + """ + query_inject_partner += """ + WHERE + ra.report_id = %s + AND + ra.is_partner_account = TRUE + """ + if not only_empty_partner: + query_inject_partner += """ + AND + p.id IS NOT NULL + """ + else: + query_inject_partner += """ + AND + p.id IS NULL + """ + query_inject_partner += """ + """ + if self.centralize: + query_inject_partner += """ + AND (a.centralized IS NULL OR a.centralized != TRUE) + """ + if self.filter_partner_ids: + query_inject_partner += """ + AND + p.id IN %s + """ + + init_subquery = self._get_final_partner_sub_subquery_sum_amounts( + only_empty_partner, + date_included=False + ) + final_subquery = self._get_final_partner_sub_subquery_sum_amounts( + only_empty_partner, + date_included=True + ) + + query_inject_partner += """ + GROUP BY + ra.id, + a.id, + p.id, + at.include_initial_balance + ), + initial_sum_amounts AS ( """ + init_subquery + """ ), + final_sum_amounts AS ( """ + final_subquery + """ ) +INSERT INTO + report_general_ledger_partner + ( + report_account_id, + create_uid, + create_date, + partner_id, + name, + initial_debit, + initial_credit, + initial_balance, + final_debit, + final_credit, + final_balance + ) +SELECT + ap.report_account_id, + %s AS create_uid, + NOW() AS create_date, + ap.partner_id, + ap.partner_name, + COALESCE(i.debit, 0.0) AS initial_debit, + COALESCE(i.credit, 0.0) AS initial_credit, + COALESCE(i.balance, 0.0) AS initial_balance, + COALESCE(f.debit, 0.0) AS final_debit, + COALESCE(f.credit, 0.0) AS final_credit, + COALESCE(f.balance, 0.0) AS final_balance +FROM + accounts_partners ap +LEFT JOIN + initial_sum_amounts i + ON + ( + """ + if not only_empty_partner: + query_inject_partner += """ + ap.partner_id = i.partner_id + """ + else: + query_inject_partner += """ + ap.partner_id IS NULL AND i.partner_id IS NULL + """ + query_inject_partner += """ + ) + AND ap.account_id = i.account_id +LEFT JOIN + final_sum_amounts f + ON + ( + """ + if not only_empty_partner: + query_inject_partner += """ + ap.partner_id = f.partner_id + """ + else: + query_inject_partner += """ + ap.partner_id IS NULL AND f.partner_id IS NULL + """ + query_inject_partner += """ + ) + AND ap.account_id = f.account_id +WHERE + ( + i.debit IS NOT NULL AND i.debit != 0 + OR i.credit IS NOT NULL AND i.credit != 0 + OR i.balance IS NOT NULL AND i.balance != 0 + OR f.debit IS NOT NULL AND f.debit != 0 + OR f.credit IS NOT NULL AND f.credit != 0 + OR f.balance IS NOT NULL AND f.balance != 0 + ) + """ + if self.hide_account_balance_at_0: + query_inject_partner += """ +AND + f.balance IS NOT NULL AND f.balance != 0 + """ + query_inject_partner_params = () + if self.filter_cost_center_ids: + query_inject_partner_params += ( + tuple(self.filter_cost_center_ids.ids), + ) + query_inject_partner_params += ( + self.id, + ) + if self.filter_partner_ids: + query_inject_partner_params += ( + tuple(self.filter_partner_ids.ids), + ) + query_inject_partner_params += ( + self.date_from, + self.fy_start_date, + ) + if self.filter_cost_center_ids: + query_inject_partner_params += ( + tuple(self.filter_cost_center_ids.ids), + ) + query_inject_partner_params += ( + self.date_from, + ) + if self.filter_cost_center_ids: + query_inject_partner_params += ( + tuple(self.filter_cost_center_ids.ids), + ) + query_inject_partner_params += ( + self.date_to, + self.fy_start_date, + ) + if self.filter_cost_center_ids: + query_inject_partner_params += ( + tuple(self.filter_cost_center_ids.ids), + ) + query_inject_partner_params += ( + self.date_to, + ) + if self.filter_cost_center_ids: + query_inject_partner_params += ( + tuple(self.filter_cost_center_ids.ids), + ) + query_inject_partner_params += ( + self.env.uid, + ) + self.env.cr.execute(query_inject_partner, query_inject_partner_params) + + def _inject_line_not_centralized_values( + self, + is_account_line=True, + is_partner_line=False, + only_empty_partner_line=False, + only_unaffected_earnings_account=False): + """ Inject report values for report_general_ledger_move_line. + + If centralized option have been chosen, + only non centralized accounts are computed. + + In function of `is_account_line` and `is_partner_line` values, + the move_line link is made either with account or either with partner. + + The "only_empty_partner_line" value is used + to compute data without partner. + """ + query_inject_move_line = """ +INSERT INTO + report_general_ledger_move_line + ( + """ + if is_account_line: + query_inject_move_line += """ + report_account_id, + """ + elif is_partner_line: + query_inject_move_line += """ + report_partner_id, + """ + query_inject_move_line += """ + create_uid, + create_date, + move_line_id, + date, + entry, + journal, + account, + partner, + label, + cost_center, + matching_number, + debit, + credit, + cumul_balance, + currency_name, + amount_currency + ) +SELECT + """ + if is_account_line: + query_inject_move_line += """ + ra.id AS report_account_id, + """ + elif is_partner_line: + query_inject_move_line += """ + rp.id AS report_partner_id, + """ + query_inject_move_line += """ + %s AS create_uid, + NOW() AS create_date, + ml.id AS move_line_id, + ml.date, + m.name AS entry, + j.code AS journal, + a.code AS account, + """ + if not only_empty_partner_line: + query_inject_move_line += """ + CASE + WHEN + NULLIF(p.name, '') IS NOT NULL + AND NULLIF(p.ref, '') IS NOT NULL + THEN p.name || ' (' || p.ref || ')' + ELSE p.name + END AS partner, + """ + elif only_empty_partner_line: + query_inject_move_line += """ + '""" + _('No partner allocated') + """' AS partner, + """ + query_inject_move_line += """ + CONCAT_WS(' - ', NULLIF(ml.ref, ''), NULLIF(ml.name, '')) AS label, + aa.name AS cost_center, + fr.name AS matching_number, + ml.debit, + ml.credit, + """ + if is_account_line: + query_inject_move_line += """ + ra.initial_balance + ( + SUM(ml.balance) + OVER (PARTITION BY a.code + ORDER BY a.code, ml.date, ml.id) + ) AS cumul_balance, + """ + elif is_partner_line and not only_empty_partner_line: + query_inject_move_line += """ + rp.initial_balance + ( + SUM(ml.balance) + OVER (PARTITION BY a.code, p.name + ORDER BY a.code, p.name, ml.date, ml.id) + ) AS cumul_balance, + """ + elif is_partner_line and only_empty_partner_line: + query_inject_move_line += """ + rp.initial_balance + ( + SUM(ml.balance) + OVER (PARTITION BY a.code + ORDER BY a.code, ml.date, ml.id) + ) AS cumul_balance, + """ + query_inject_move_line += """ + c.name AS currency_name, + ml.amount_currency +FROM + """ + if is_account_line: + query_inject_move_line += """ + report_general_ledger_account ra + """ + elif is_partner_line: + query_inject_move_line += """ + report_general_ledger_partner rp +INNER JOIN + report_general_ledger_account ra ON rp.report_account_id = ra.id + """ + query_inject_move_line += """ +INNER JOIN + account_move_line ml ON ra.account_id = ml.account_id +INNER JOIN + account_move m ON ml.move_id = m.id +INNER JOIN + account_journal j ON ml.journal_id = j.id +INNER JOIN + account_account a ON ml.account_id = a.id + """ + if is_account_line: + query_inject_move_line += """ +LEFT JOIN + res_partner p ON ml.partner_id = p.id + """ + elif is_partner_line and not only_empty_partner_line: + query_inject_move_line += """ +INNER JOIN + res_partner p + ON ml.partner_id = p.id AND rp.partner_id = p.id + """ + query_inject_move_line += """ +LEFT JOIN + account_full_reconcile fr ON ml.full_reconcile_id = fr.id +LEFT JOIN + res_currency c ON ml.currency_id = c.id + """ + if self.filter_cost_center_ids: + query_inject_move_line += """ +INNER JOIN + account_analytic_account aa + ON + ml.analytic_account_id = aa.id + AND aa.id IN %s + """ + else: + query_inject_move_line += """ +LEFT JOIN + account_analytic_account aa ON ml.analytic_account_id = aa.id + """ + query_inject_move_line += """ +WHERE + ra.report_id = %s +AND + """ + if only_unaffected_earnings_account: + query_inject_move_line += """ + a.id = %s +AND + """ + if is_account_line: + query_inject_move_line += """ + (ra.is_partner_account IS NULL OR ra.is_partner_account != TRUE) + """ + elif is_partner_line: + query_inject_move_line += """ + ra.is_partner_account = TRUE + """ + if self.centralize: + query_inject_move_line += """ +AND + (a.centralized IS NULL OR a.centralized != TRUE) + """ + query_inject_move_line += """ +AND + ml.date BETWEEN %s AND %s + """ + if self.only_posted_moves: + query_inject_move_line += """ +AND + m.state = 'posted' + """ + if only_empty_partner_line: + query_inject_move_line += """ +AND + ml.partner_id IS NULL +AND + rp.partner_id IS NULL + """ + if is_account_line: + query_inject_move_line += """ +ORDER BY + a.code, ml.date, ml.id + """ + elif is_partner_line and not only_empty_partner_line: + query_inject_move_line += """ +ORDER BY + a.code, p.name, ml.date, ml.id + """ + elif is_partner_line and only_empty_partner_line: + query_inject_move_line += """ +ORDER BY + a.code, ml.date, ml.id + """ + + query_inject_move_line_params = ( + self.env.uid, + ) + if self.filter_cost_center_ids: + query_inject_move_line_params += ( + tuple(self.filter_cost_center_ids.ids), + ) + query_inject_move_line_params += ( + self.id, + ) + if only_unaffected_earnings_account: + query_inject_move_line_params += ( + self.unaffected_earnings_account.id, + ) + query_inject_move_line_params += ( + self.date_from, + self.date_to, + ) + self.env.cr.execute( + query_inject_move_line, + query_inject_move_line_params + ) + + def _inject_line_centralized_values(self): + """ Inject report values for report_general_ledger_move_line. + + Only centralized accounts are computed. + """ + query_inject_move_line_centralized = """ +WITH + move_lines AS + ( + SELECT + ml.account_id, + ( + DATE_TRUNC('month', ml.date) + interval '1 month' + - interval '1 day' + )::date AS date, + SUM(ml.debit) AS debit, + SUM(ml.credit) AS credit, + SUM(ml.balance) AS balance, + ml.currency_id AS currency_id + FROM + report_general_ledger_account ra + INNER JOIN + account_move_line ml ON ra.account_id = ml.account_id + INNER JOIN + account_move m ON ml.move_id = m.id + INNER JOIN + account_account a ON ml.account_id = a.id + """ + if self.filter_cost_center_ids: + query_inject_move_line_centralized += """ + INNER JOIN + account_analytic_account aa + ON + ml.analytic_account_id = aa.id + AND aa.id IN %s + """ + query_inject_move_line_centralized += """ + WHERE + ra.report_id = %s + AND + a.centralized = TRUE + AND + ml.date BETWEEN %s AND %s + """ + if self.only_posted_moves: + query_inject_move_line_centralized += """ + AND + m.state = 'posted' + """ + query_inject_move_line_centralized += """ + GROUP BY + ra.id, ml.account_id, a.code, 2, ml.currency_id + ) +INSERT INTO + report_general_ledger_move_line + ( + report_account_id, + create_uid, + create_date, + date, + account, + label, + debit, + credit, + cumul_balance + ) +SELECT + ra.id AS report_account_id, + %s AS create_uid, + NOW() AS create_date, + ml.date, + a.code AS account, + '""" + _('Centralized Entries') + """' AS label, + ml.debit AS debit, + ml.credit AS credit, + ra.initial_balance + ( + SUM(ml.balance) + OVER (PARTITION BY a.code ORDER BY ml.date) + ) AS cumul_balance +FROM + report_general_ledger_account ra +INNER JOIN + move_lines ml ON ra.account_id = ml.account_id +INNER JOIN + account_account a ON ml.account_id = a.id +LEFT JOIN + res_currency c ON ml.currency_id = c.id +WHERE + ra.report_id = %s +AND + (a.centralized IS NOT NULL AND a.centralized = TRUE) +ORDER BY + a.code, ml.date + """ + + query_inject_move_line_centralized_params = () + if self.filter_cost_center_ids: + query_inject_move_line_centralized_params += ( + tuple(self.filter_cost_center_ids.ids), + ) + query_inject_move_line_centralized_params += ( + self.id, + self.date_from, + self.date_to, + self.env.uid, + self.id, + ) + self.env.cr.execute( + query_inject_move_line_centralized, + query_inject_move_line_centralized_params + ) + + def _compute_has_second_currency(self): + """ Compute "has_second_currency" flag which will used for display.""" + query_update_has_second_currency = """ +UPDATE + report_general_ledger +SET + has_second_currency = + ( + SELECT + TRUE + FROM + report_general_ledger_move_line l + INNER JOIN + report_general_ledger_account a + ON l.report_account_id = a.id + WHERE + a.report_id = %s + AND l.currency_name IS NOT NULL + LIMIT 1 + ) + OR + ( + SELECT + TRUE + FROM + report_general_ledger_move_line l + INNER JOIN + report_general_ledger_partner p + ON l.report_partner_id = p.id + INNER JOIN + report_general_ledger_account a + ON p.report_account_id = a.id + WHERE + a.report_id = %s + AND l.currency_name IS NOT NULL + LIMIT 1 + ) +WHERE id = %s + """ + params = (self.id,) * 3 + self.env.cr.execute(query_update_has_second_currency, params) + + def _get_unaffected_earnings_account_sub_subquery_sum_amounts( + self, include_initial_balance + ): + """ Return subquery used to compute sum amounts on + unaffected earnings accounts """ + if not include_initial_balance: + sub_subquery_sum_amounts = """ + SELECT + -SUM(ml.balance) AS balance + """ + else: + sub_subquery_sum_amounts = """ + SELECT + SUM(ml.balance) AS balance + """ + sub_subquery_sum_amounts += """ + FROM + account_account a + INNER JOIN + account_account_type at ON a.user_type_id = at.id + INNER JOIN + account_move_line ml + ON a.id = ml.account_id + AND ml.date < %s + """ + + if not include_initial_balance: + sub_subquery_sum_amounts += """ + AND NOT(at.include_initial_balance != TRUE AND ml.date >= %s) + """ + else: + sub_subquery_sum_amounts += """ + AND at.include_initial_balance = FALSE + """ + if self.only_posted_moves: + sub_subquery_sum_amounts += """ + INNER JOIN + account_move m ON ml.move_id = m.id AND m.state = 'posted' + """ + if self.filter_cost_center_ids: + sub_subquery_sum_amounts += """ + INNER JOIN + account_analytic_account aa + ON + ml.analytic_account_id = aa.id + AND aa.id IN %s + """ + sub_subquery_sum_amounts += """ + WHERE + a.company_id =%s + AND a.id != %s + """ + return sub_subquery_sum_amounts + + def _inject_unaffected_earnings_account_values(self): + """Inject the report values of the unaffected earnings account + for report_general_ledger_account.""" + subquery_sum_amounts = """ + SELECT + SUM(COALESCE(sub.balance, 0.0)) AS balance + FROM + ( + """ + subquery_sum_amounts += \ + self._get_unaffected_earnings_account_sub_subquery_sum_amounts( + include_initial_balance=False + ) + subquery_sum_amounts += """ + UNION + """ + subquery_sum_amounts += \ + self._get_unaffected_earnings_account_sub_subquery_sum_amounts( + include_initial_balance=True + ) + subquery_sum_amounts += """ + ) sub + """ + + # pylint: disable=sql-injection + query_inject_account = """ + WITH + initial_sum_amounts AS ( """ + subquery_sum_amounts + """ ) + INSERT INTO + report_general_ledger_account + ( + report_id, + create_uid, + create_date, + account_id, + code, + name, + is_partner_account, + initial_balance + ) + SELECT + %s AS report_id, + %s AS create_uid, + NOW() AS create_date, + a.id AS account_id, + a.code, + a.name, + False AS is_partner_account, + COALESCE(i.balance, 0.0) AS initial_balance + FROM + account_account a, + initial_sum_amounts i + WHERE + a.company_id = %s + AND a.id = %s + """ + query_inject_account_params = ( + self.date_from, + self.fy_start_date, + ) + if self.filter_cost_center_ids: + query_inject_account_params += ( + tuple(self.filter_cost_center_ids.ids), + ) + query_inject_account_params += ( + self.company_id.id, + self.unaffected_earnings_account.id, + ) + query_inject_account_params += ( + self.date_from, + ) + if self.filter_cost_center_ids: + query_inject_account_params += ( + tuple(self.filter_cost_center_ids.ids), + ) + query_inject_account_params += ( + self.company_id.id, + self.unaffected_earnings_account.id, + ) + query_inject_account_params += ( + self.id, + self.env.uid, + self.company_id.id, + self.unaffected_earnings_account.id, + ) + self.env.cr.execute(query_inject_account, + query_inject_account_params) + + def _complete_unaffected_earnings_account_values(self): + """Complete the report values of the unaffected earnings account + for report_general_ledger_account.""" + query_update_unaffected_earnings_account_values = """ + WITH + sum_amounts AS + ( + SELECT + SUM(COALESCE(rml.debit, 0.0)) AS debit, + SUM(COALESCE(rml.credit, 0.0)) AS credit, + SUM( + COALESCE(rml.debit, 0.0) - + COALESCE(rml.credit, 0.0) + ) + ra.initial_balance AS balance + FROM + report_general_ledger_account ra + LEFT JOIN + report_general_ledger_move_line rml + ON ra.id = rml.report_account_id + WHERE + ra.report_id = %s + AND ra.account_id = %s + GROUP BY + ra.id + ) + UPDATE + report_general_ledger_account ra + SET + initial_debit = 0.0, + initial_credit = 0.0, + final_debit = sum_amounts.debit, + final_credit = sum_amounts.credit, + final_balance = sum_amounts.balance + FROM + sum_amounts + WHERE + ra.report_id = %s + AND ra.account_id = %s + """ + params = ( + self.id, + self.unaffected_earnings_account.id, + self.id, + self.unaffected_earnings_account.id, + ) + self.env.cr.execute( + query_update_unaffected_earnings_account_values, + params + ) diff --git a/account_financial_report/report/general_ledger_xlsx.py b/account_financial_report/report/general_ledger_xlsx.py new file mode 100644 index 000000000000..9dee4a3abba9 --- /dev/null +++ b/account_financial_report/report/general_ledger_xlsx.py @@ -0,0 +1,137 @@ + +# Author: Damien Crier +# Author: Julien Coux +# Copyright 2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import _, models + + +class GeneralLedgerXslx(models.AbstractModel): + _name = 'report.a_f_r.report_general_ledger_xlsx' + _inherit = 'report.account_financial_report.abstract_report_xlsx' + + def _get_report_name(self): + return _('General Ledger') + + def _get_report_columns(self, report): + return { + 0: {'header': _('Date'), 'field': 'date', 'width': 11}, + 1: {'header': _('Entry'), 'field': 'entry', 'width': 18}, + 2: {'header': _('Journal'), 'field': 'journal', 'width': 8}, + 3: {'header': _('Account'), 'field': 'account', 'width': 9}, + 4: {'header': _('Partner'), 'field': 'partner', 'width': 25}, + 5: {'header': _('Ref - Label'), 'field': 'label', 'width': 40}, + 6: {'header': _('Cost center'), + 'field': 'cost_center', + 'width': 15}, + 7: {'header': _('Rec.'), 'field': 'matching_number', 'width': 5}, + 8: {'header': _('Debit'), + 'field': 'debit', + 'field_initial_balance': 'initial_debit', + 'field_final_balance': 'final_debit', + 'type': 'amount', + 'width': 14}, + 9: {'header': _('Credit'), + 'field': 'credit', + 'field_initial_balance': 'initial_credit', + 'field_final_balance': 'final_credit', + 'type': 'amount', + 'width': 14}, + 10: {'header': _('Cumul. Bal.'), + 'field': 'cumul_balance', + 'field_initial_balance': 'initial_balance', + 'field_final_balance': 'final_balance', + 'type': 'amount', + 'width': 14}, + 11: {'header': _('Cur.'), 'field': 'currency_name', 'width': 7}, + 12: {'header': _('Amount cur.'), + 'field': 'amount_currency', + 'type': 'amount', + 'width': 14}, + } + + def _get_report_filters(self, report): + return [ + [_('Date range filter'), + _('From: %s To: %s') % (report.date_from, report.date_to)], + [_('Target moves filter'), + _('All posted entries') if report.only_posted_moves + else _('All entries')], + [_('Account balance at 0 filter'), + _('Hide') if report.hide_account_balance_at_0 else _('Show')], + [_('Centralize filter'), + _('Yes') if report.centralize else _('No')], + ] + + def _get_col_count_filter_name(self): + return 2 + + def _get_col_count_filter_value(self): + return 2 + + def _get_col_pos_initial_balance_label(self): + return 5 + + def _get_col_count_final_balance_name(self): + return 5 + + def _get_col_pos_final_balance_label(self): + return 5 + + def _generate_report_content(self, workbook, report): + # For each account + for account in report.account_ids: + # Write account title + self.write_array_title(account.code + ' - ' + account.name) + + if not account.partner_ids: + # Display array header for move lines + self.write_array_header() + + # Display initial balance line for account + self.write_initial_balance(account, _('Initial balance')) + + # Display account move lines + for line in account.move_line_ids: + self.write_line(line) + + else: + # For each partner + for partner in account.partner_ids: + # Write partner title + self.write_array_title(partner.name) + + # Display array header for move lines + self.write_array_header() + + # Display initial balance line for partner + self.write_initial_balance(partner, _('Initial balance')) + + # Display account move lines + for line in partner.move_line_ids: + self.write_line(line) + + # Display ending balance line for partner + self.write_ending_balance(partner, 'partner') + + # Line break + self.row_pos += 1 + + # Display ending balance line for account + self.write_ending_balance(account, 'account') + + # 2 lines break + self.row_pos += 2 + + def write_ending_balance(self, my_object, type_object): + """Specific function to write ending balance for General Ledger""" + if type_object == 'partner': + name = my_object.name + label = _('Partner ending balance') + elif type_object == 'account': + name = my_object.code + ' - ' + my_object.name + label = _('Ending balance') + super(GeneralLedgerXslx, self).write_ending_balance( + my_object, name, label + ) diff --git a/account_financial_report/report/open_items.py b/account_financial_report/report/open_items.py new file mode 100644 index 000000000000..f647bf5a217f --- /dev/null +++ b/account_financial_report/report/open_items.py @@ -0,0 +1,776 @@ + +# ?? 2016 Julien Coux (Camptocamp) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import models, fields, api, _ + + +class OpenItemsReport(models.TransientModel): + """ Here, we just define class fields. + For methods, go more bottom at this file. + + The class hierarchy is : + * OpenItemsReport + ** OpenItemsReportAccount + *** OpenItemsReportPartner + **** OpenItemsReportMoveLine + """ + + _name = 'report_open_items' + + # Filters fields, used for data computation + date_at = fields.Date() + only_posted_moves = fields.Boolean() + hide_account_balance_at_0 = fields.Boolean() + company_id = fields.Many2one(comodel_name='res.company') + filter_account_ids = fields.Many2many(comodel_name='account.account') + filter_partner_ids = fields.Many2many(comodel_name='res.partner') + + # Flag fields, used for report display + has_second_currency = fields.Boolean() + + # Data fields, used to browse report data + account_ids = fields.One2many( + comodel_name='report_open_items_account', + inverse_name='report_id' + ) + + +class OpenItemsReportAccount(models.TransientModel): + + _name = 'report_open_items_account' + _order = 'code ASC' + + report_id = fields.Many2one( + comodel_name='report_open_items', + ondelete='cascade', + index=True + ) + + # Data fields, used to keep link with real object + account_id = fields.Many2one( + 'account.account', + index=True + ) + + # Data fields, used for report display + code = fields.Char() + name = fields.Char() + final_amount_residual = fields.Float(digits=(16, 2)) + + # Data fields, used to browse report data + partner_ids = fields.One2many( + comodel_name='report_open_items_partner', + inverse_name='report_account_id' + ) + + +class OpenItemsReportPartner(models.TransientModel): + + _name = 'report_open_items_partner' + + report_account_id = fields.Many2one( + comodel_name='report_open_items_account', + ondelete='cascade', + index=True + ) + + # Data fields, used to keep link with real object + partner_id = fields.Many2one( + 'res.partner', + index=True + ) + + # Data fields, used for report display + name = fields.Char() + final_amount_residual = fields.Float(digits=(16, 2)) + + # Data fields, used to browse report data + move_line_ids = fields.One2many( + comodel_name='report_open_items_move_line', + inverse_name='report_partner_id' + ) + + @api.model + def _generate_order_by(self, order_spec, query): + """Custom order to display "No partner allocated" at last position.""" + return """ +ORDER BY + CASE + WHEN "report_open_items_partner"."partner_id" IS NOT NULL + THEN 0 + ELSE 1 + END, + "report_open_items_partner"."name" + """ + + +class OpenItemsReportMoveLine(models.TransientModel): + + _name = 'report_open_items_move_line' + + report_partner_id = fields.Many2one( + comodel_name='report_open_items_partner', + ondelete='cascade', + index=True + ) + + # Data fields, used to keep link with real object + move_line_id = fields.Many2one('account.move.line') + + # Data fields, used for report display + date = fields.Date() + date_due = fields.Date() + entry = fields.Char() + journal = fields.Char() + account = fields.Char() + partner = fields.Char() + label = fields.Char() + amount_total_due = fields.Float(digits=(16, 2)) + amount_residual = fields.Float(digits=(16, 2)) + currency_name = fields.Char() + amount_total_due_currency = fields.Float(digits=(16, 2)) + amount_residual_currency = fields.Float(digits=(16, 2)) + + +class OpenItemsReportCompute(models.TransientModel): + """ Here, we just define methods. + For class fields, go more top at this file. + """ + + _inherit = 'report_open_items' + + @api.multi + def print_report(self, report_type): + self.ensure_one() + if report_type == 'xlsx': + report_name = 'a_f_r.report_open_items_xlsx' + else: + report_name = 'account_financial_report.' \ + 'report_open_items_qweb' + return self.env['ir.actions.report'].search( + [('report_name', '=', report_name), + ('report_type', '=', report_type)], limit=1).report_action(self) + + def _get_html(self): + result = {} + rcontext = {} + context = dict(self.env.context) + report = self.browse(context.get('active_id')) + if report: + rcontext['o'] = report + result['html'] = self.env.ref( + 'account_financial_report.report_open_items').render( + rcontext) + return result + + @api.model + def get_html(self, given_context=None): + return self._get_html() + + @api.multi + def compute_data_for_report(self): + self.ensure_one() + # Compute report data + self._inject_account_values() + self._inject_partner_values() + self._inject_line_values() + self._inject_line_values(only_empty_partner_line=True) + self._clean_partners_and_accounts() + self._compute_partners_and_accounts_cumul() + if self.hide_account_balance_at_0: + self._clean_partners_and_accounts( + only_delete_account_balance_at_0=True + ) + # Compute display flag + self._compute_has_second_currency() + # Refresh cache because all data are computed with SQL requests + self.refresh() + + def _inject_account_values(self): + """Inject report values for report_open_items_account.""" + query_inject_account = """ +WITH + accounts AS + ( + SELECT + a.id, + a.code, + a.name, + a.user_type_id + FROM + account_account a + INNER JOIN + account_move_line ml ON a.id = ml.account_id AND ml.date <= %s + """ + if self.filter_partner_ids: + query_inject_account += """ + INNER JOIN + res_partner p ON ml.partner_id = p.id + """ + if self.only_posted_moves: + query_inject_account += """ + INNER JOIN + account_move m ON ml.move_id = m.id AND m.state = 'posted' + """ + query_inject_account += """ + WHERE + a.company_id = %s + AND a.reconcile IS true + """ + if self.filter_account_ids: + query_inject_account += """ + AND + a.id IN %s + """ + if self.filter_partner_ids: + query_inject_account += """ + AND + p.id IN %s + """ + query_inject_account += """ + GROUP BY + a.id + ) +INSERT INTO + report_open_items_account + ( + report_id, + create_uid, + create_date, + account_id, + code, + name + ) +SELECT + %s AS report_id, + %s AS create_uid, + NOW() AS create_date, + a.id AS account_id, + a.code, + a.name +FROM + accounts a + """ + query_inject_account_params = ( + self.date_at, + self.company_id.id, + ) + if self.filter_account_ids: + query_inject_account_params += ( + tuple(self.filter_account_ids.ids), + ) + if self.filter_partner_ids: + query_inject_account_params += ( + tuple(self.filter_partner_ids.ids), + ) + query_inject_account_params += ( + self.id, + self.env.uid, + ) + self.env.cr.execute(query_inject_account, query_inject_account_params) + + def _inject_partner_values(self): + """ Inject report values for report_open_items_partner. """ + # pylint: disable=sql-injection + query_inject_partner = """ +WITH + accounts_partners AS + ( + SELECT + ra.id AS report_account_id, + a.id AS account_id, + at.include_initial_balance AS include_initial_balance, + p.id AS partner_id, + COALESCE( + CASE + WHEN + NULLIF(p.name, '') IS NOT NULL + AND NULLIF(p.ref, '') IS NOT NULL + THEN p.name || ' (' || p.ref || ')' + ELSE p.name + END, + '""" + _('No partner allocated') + """' + ) AS partner_name + FROM + report_open_items_account ra + INNER JOIN + account_account a ON ra.account_id = a.id + INNER JOIN + account_account_type at ON a.user_type_id = at.id + INNER JOIN + account_move_line ml ON a.id = ml.account_id AND ml.date <= %s + """ + if self.only_posted_moves: + query_inject_partner += """ + INNER JOIN + account_move m ON ml.move_id = m.id AND m.state = 'posted' + """ + query_inject_partner += """ + LEFT JOIN + res_partner p ON ml.partner_id = p.id + WHERE + ra.report_id = %s + """ + if self.filter_partner_ids: + query_inject_partner += """ + AND + p.id IN %s + """ + query_inject_partner += """ + GROUP BY + ra.id, + a.id, + p.id, + at.include_initial_balance + ) +INSERT INTO + report_open_items_partner + ( + report_account_id, + create_uid, + create_date, + partner_id, + name + ) +SELECT + ap.report_account_id, + %s AS create_uid, + NOW() AS create_date, + ap.partner_id, + ap.partner_name +FROM + accounts_partners ap + """ + query_inject_partner_params = ( + self.date_at, + self.id, + ) + if self.filter_partner_ids: + query_inject_partner_params += ( + tuple(self.filter_partner_ids.ids), + ) + query_inject_partner_params += ( + self.env.uid, + ) + self.env.cr.execute(query_inject_partner, query_inject_partner_params) + + def _get_line_sub_query_move_lines(self, + only_empty_partner_line=False, + positive_balance=True): + """ Return subquery used to compute sum amounts on lines """ + sub_query = """ + SELECT + ml.id, + ml.balance, + SUM( + CASE + WHEN ml_past.id IS NOT NULL + THEN pr.amount + ELSE NULL + END + ) AS partial_amount, + ml.amount_currency, + SUM( + CASE + WHEN ml_past.id IS NOT NULL + THEN pr.amount_currency + ELSE NULL + END + ) AS partial_amount_currency, + ml.currency_id + FROM + report_open_items_partner rp + INNER JOIN + report_open_items_account ra + ON rp.report_account_id = ra.id + INNER JOIN + account_move_line ml + ON ra.account_id = ml.account_id + """ + if not only_empty_partner_line: + sub_query += """ + AND rp.partner_id = ml.partner_id + """ + elif only_empty_partner_line: + sub_query += """ + AND ml.partner_id IS NULL + """ + if not positive_balance: + sub_query += """ + LEFT JOIN + account_partial_reconcile pr + ON ml.balance < 0 AND pr.credit_move_id = ml.id + LEFT JOIN + account_move_line ml_future + ON ml.balance < 0 AND pr.debit_move_id = ml_future.id + AND ml_future.date >= %s + LEFT JOIN + account_move_line ml_past + ON ml.balance < 0 AND pr.debit_move_id = ml_past.id + AND ml_past.date < %s + """ + else: + sub_query += """ + LEFT JOIN + account_partial_reconcile pr + ON ml.balance > 0 AND pr.debit_move_id = ml.id + LEFT JOIN + account_move_line ml_future + ON ml.balance > 0 AND pr.credit_move_id = ml_future.id + AND ml_future.date >= %s + LEFT JOIN + account_move_line ml_past + ON ml.balance > 0 AND pr.credit_move_id = ml_past.id + AND ml_past.date < %s + """ + sub_query += """ + WHERE + ra.report_id = %s + GROUP BY + ml.id, + ml.balance, + ml.amount_currency + HAVING + ( + ml.full_reconcile_id IS NULL + OR MAX(ml_future.id) IS NOT NULL + ) + """ + return sub_query + + def _inject_line_values(self, only_empty_partner_line=False): + """ Inject report values for report_open_items_move_line. + + The "only_empty_partner_line" value is used + to compute data without partner. + """ + query_inject_move_line = """ +WITH + move_lines_amount AS + ( + """ + query_inject_move_line += self._get_line_sub_query_move_lines( + only_empty_partner_line=only_empty_partner_line, + positive_balance=True + ) + query_inject_move_line += """ + UNION + """ + query_inject_move_line += self._get_line_sub_query_move_lines( + only_empty_partner_line=only_empty_partner_line, + positive_balance=False + ) + query_inject_move_line += """ + ), + move_lines AS + ( + SELECT + id, + CASE + WHEN SUM(partial_amount) > 0 + THEN + CASE + WHEN balance > 0 + THEN balance - SUM(partial_amount) + ELSE balance + SUM(partial_amount) + END + ELSE balance + END AS amount_residual, + CASE + WHEN SUM(partial_amount_currency) > 0 + THEN + CASE + WHEN amount_currency > 0 + THEN amount_currency - SUM(partial_amount_currency) + ELSE amount_currency + SUM(partial_amount_currency) + END + ELSE amount_currency + END AS amount_residual_currency, + currency_id + FROM + move_lines_amount + GROUP BY + id, + balance, + amount_currency, + currency_id + ) +INSERT INTO + report_open_items_move_line + ( + report_partner_id, + create_uid, + create_date, + move_line_id, + date, + date_due, + entry, + journal, + account, + partner, + label, + amount_total_due, + amount_residual, + currency_name, + amount_total_due_currency, + amount_residual_currency + ) +SELECT + rp.id AS report_partner_id, + %s AS create_uid, + NOW() AS create_date, + ml.id AS move_line_id, + ml.date, + ml.date_maturity, + m.name AS entry, + j.code AS journal, + a.code AS account, + """ + if not only_empty_partner_line: + query_inject_move_line += """ + CASE + WHEN + NULLIF(p.name, '') IS NOT NULL + AND NULLIF(p.ref, '') IS NOT NULL + THEN p.name || ' (' || p.ref || ')' + ELSE p.name + END AS partner, + """ + elif only_empty_partner_line: + query_inject_move_line += """ + '""" + _('No partner allocated') + """' AS partner, + """ + query_inject_move_line += """ + CONCAT_WS(' - ', NULLIF(ml.ref, ''), NULLIF(ml.name, '')) AS label, + ml.balance, + ml2.amount_residual, + c.name AS currency_name, + ml.amount_currency, + ml2.amount_residual_currency +FROM + report_open_items_partner rp +INNER JOIN + report_open_items_account ra ON rp.report_account_id = ra.id +INNER JOIN + account_move_line ml ON ra.account_id = ml.account_id +INNER JOIN + move_lines ml2 + ON ml.id = ml2.id + AND ml2.amount_residual IS NOT NULL + AND ml2.amount_residual != 0 +INNER JOIN + account_move m ON ml.move_id = m.id +INNER JOIN + account_journal j ON ml.journal_id = j.id +INNER JOIN + account_account a ON ml.account_id = a.id + """ + if not only_empty_partner_line: + query_inject_move_line += """ +INNER JOIN + res_partner p + ON ml.partner_id = p.id AND rp.partner_id = p.id + """ + query_inject_move_line += """ +LEFT JOIN + account_full_reconcile fr ON ml.full_reconcile_id = fr.id +LEFT JOIN + res_currency c ON ml2.currency_id = c.id +WHERE + ra.report_id = %s +AND + ml.date <= %s + """ + if self.only_posted_moves: + query_inject_move_line += """ +AND + m.state = 'posted' + """ + if only_empty_partner_line: + query_inject_move_line += """ +AND + ml.partner_id IS NULL +AND + rp.partner_id IS NULL + """ + if not only_empty_partner_line: + query_inject_move_line += """ +ORDER BY + a.code, p.name, ml.date, ml.id + """ + elif only_empty_partner_line: + query_inject_move_line += """ +ORDER BY + a.code, ml.date, ml.id + """ + self.env.cr.execute( + query_inject_move_line, + (self.date_at, + self.date_at, + self.id, + self.date_at, + self.date_at, + self.id, + self.env.uid, + self.id, + self.date_at,) + ) + + def _compute_partners_and_accounts_cumul(self): + """ Compute cumulative amount for + report_open_items_partner and report_open_items_account. + """ + query_compute_partners_cumul = """ +UPDATE + report_open_items_partner +SET + final_amount_residual = + ( + SELECT + SUM(rml.amount_residual) AS final_amount_residual + FROM + report_open_items_move_line rml + WHERE + rml.report_partner_id = report_open_items_partner.id + ) +WHERE + id IN + ( + SELECT + rp.id + FROM + report_open_items_account ra + INNER JOIN + report_open_items_partner rp + ON ra.id = rp.report_account_id + WHERE + ra.report_id = %s + ) + """ + params_compute_partners_cumul = (self.id,) + self.env.cr.execute(query_compute_partners_cumul, + params_compute_partners_cumul) + query_compute_accounts_cumul = """ +UPDATE + report_open_items_account +SET + final_amount_residual = + ( + SELECT + SUM(rp.final_amount_residual) AS final_amount_residual + FROM + report_open_items_partner rp + WHERE + rp.report_account_id = report_open_items_account.id + ) +WHERE + report_id = %s + """ + params_compute_accounts_cumul = (self.id,) + self.env.cr.execute(query_compute_accounts_cumul, + params_compute_accounts_cumul) + + def _clean_partners_and_accounts(self, + only_delete_account_balance_at_0=False): + """ Delete empty data for + report_open_items_partner and report_open_items_account. + + The "only_delete_account_balance_at_0" value is used + to delete also the data with cumulative amounts at 0. + """ + query_clean_partners = """ +DELETE FROM + report_open_items_partner +WHERE + id IN + ( + SELECT + DISTINCT rp.id + FROM + report_open_items_account ra + INNER JOIN + report_open_items_partner rp + ON ra.id = rp.report_account_id + LEFT JOIN + report_open_items_move_line rml + ON rp.id = rml.report_partner_id + WHERE + ra.report_id = %s + """ + if not only_delete_account_balance_at_0: + query_clean_partners += """ + AND rml.id IS NULL + """ + elif only_delete_account_balance_at_0: + query_clean_partners += """ + AND ( + rp.final_amount_residual IS NULL + OR rp.final_amount_residual = 0 + ) + """ + query_clean_partners += """ + ) + """ + params_clean_partners = (self.id,) + self.env.cr.execute(query_clean_partners, params_clean_partners) + query_clean_accounts = """ +DELETE FROM + report_open_items_account +WHERE + id IN + ( + SELECT + DISTINCT ra.id + FROM + report_open_items_account ra + LEFT JOIN + report_open_items_partner rp + ON ra.id = rp.report_account_id + WHERE + ra.report_id = %s + """ + if not only_delete_account_balance_at_0: + query_clean_accounts += """ + AND rp.id IS NULL + """ + elif only_delete_account_balance_at_0: + query_clean_accounts += """ + AND ( + ra.final_amount_residual IS NULL + OR ra.final_amount_residual = 0 + ) + """ + query_clean_accounts += """ + ) + """ + params_clean_accounts = (self.id,) + self.env.cr.execute(query_clean_accounts, params_clean_accounts) + + def _compute_has_second_currency(self): + """ Compute "has_second_currency" flag which will used for display.""" + query_update_has_second_currency = """ +UPDATE + report_open_items +SET + has_second_currency = + ( + SELECT + TRUE + FROM + report_open_items_move_line l + INNER JOIN + report_open_items_partner p + ON l.report_partner_id = p.id + INNER JOIN + report_open_items_account a + ON p.report_account_id = a.id + WHERE + a.report_id = %s + AND l.currency_name IS NOT NULL + LIMIT 1 + ) +WHERE id = %s + """ + params = (self.id,) * 2 + self.env.cr.execute(query_update_has_second_currency, params) diff --git a/account_financial_report/report/open_items_xlsx.py b/account_financial_report/report/open_items_xlsx.py new file mode 100644 index 000000000000..a5447aea98d8 --- /dev/null +++ b/account_financial_report/report/open_items_xlsx.py @@ -0,0 +1,105 @@ + +# Author: Julien Coux +# Copyright 2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import _, models + + +class OpenItemsXslx(models.AbstractModel): + _name = 'report.a_f_r.report_open_items_xlsx' + _inherit = 'report.account_financial_report.abstract_report_xlsx' + + def _get_report_name(self): + return _('Open Items') + + def _get_report_columns(self, report): + return { + 0: {'header': _('Date'), 'field': 'date', 'width': 11}, + 1: {'header': _('Entry'), 'field': 'entry', 'width': 18}, + 2: {'header': _('Journal'), 'field': 'journal', 'width': 8}, + 3: {'header': _('Account'), 'field': 'account', 'width': 9}, + 4: {'header': _('Partner'), 'field': 'partner', 'width': 25}, + 5: {'header': _('Ref - Label'), 'field': 'label', 'width': 40}, + 6: {'header': _('Due date'), 'field': 'date_due', 'width': 11}, + 7: {'header': _('Original'), + 'field': 'amount_total_due', + 'type': 'amount', + 'width': 14}, + 8: {'header': _('Residual'), + 'field': 'amount_residual', + 'field_final_balance': 'final_amount_residual', + 'type': 'amount', + 'width': 14}, + 9: {'header': _('Cur.'), 'field': 'currency_name', 'width': 7}, + 10: {'header': _('Cur. Original'), + 'field': 'amount_total_due_currency', + 'type': 'amount', + 'width': 14}, + 11: {'header': _('Cur. Residual'), + 'field': 'amount_residual_currency', + 'type': 'amount', + 'width': 14}, + } + + def _get_report_filters(self, report): + return [ + [_('Date at filter'), report.date_at], + [_('Target moves filter'), + _('All posted entries') if report.only_posted_moves + else _('All entries')], + [_('Account balance at 0 filter'), + _('Hide') if report.hide_account_balance_at_0 else _('Show')], + ] + + def _get_col_count_filter_name(self): + return 2 + + def _get_col_count_filter_value(self): + return 2 + + def _get_col_count_final_balance_name(self): + return 5 + + def _get_col_pos_final_balance_label(self): + return 5 + + def _generate_report_content(self, workbook, report): + # For each account + for account in report.account_ids: + # Write account title + self.write_array_title(account.code + ' - ' + account.name) + + # For each partner + for partner in account.partner_ids: + # Write partner title + self.write_array_title(partner.name) + + # Display array header for move lines + self.write_array_header() + + # Display account move lines + for line in partner.move_line_ids: + self.write_line(line) + + # Display ending balance line for partner + self.write_ending_balance(partner, 'partner') + + # Line break + self.row_pos += 1 + + # Display ending balance line for account + self.write_ending_balance(account, 'account') + + # 2 lines break + self.row_pos += 2 + + def write_ending_balance(self, my_object, type_object): + """Specific function to write ending balance for Open Items""" + if type_object == 'partner': + name = my_object.name + label = _('Partner ending balance') + elif type_object == 'account': + name = my_object.code + ' - ' + my_object.name + label = _('Ending balance') + super(OpenItemsXslx, self).write_ending_balance(my_object, name, label) diff --git a/account_financial_report/report/templates/aged_partner_balance.xml b/account_financial_report/report/templates/aged_partner_balance.xml new file mode 100644 index 000000000000..4c74656df3c1 --- /dev/null +++ b/account_financial_report/report/templates/aged_partner_balance.xml @@ -0,0 +1,432 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/account_financial_report/report/templates/general_ledger.xml b/account_financial_report/report/templates/general_ledger.xml new file mode 100644 index 000000000000..c7934a40cc24 --- /dev/null +++ b/account_financial_report/report/templates/general_ledger.xml @@ -0,0 +1,263 @@ + + + + + + + + + + + + + + diff --git a/account_financial_report/report/templates/layouts.xml b/account_financial_report/report/templates/layouts.xml new file mode 100644 index 000000000000..704c7aadc17c --- /dev/null +++ b/account_financial_report/report/templates/layouts.xml @@ -0,0 +1,36 @@ + + + + + + diff --git a/account_financial_report/report/templates/open_items.xml b/account_financial_report/report/templates/open_items.xml new file mode 100644 index 000000000000..b7b77ddaec78 --- /dev/null +++ b/account_financial_report/report/templates/open_items.xml @@ -0,0 +1,233 @@ + + + + + + + + + + + + + + diff --git a/account_financial_report/report/templates/trial_balance.xml b/account_financial_report/report/templates/trial_balance.xml new file mode 100644 index 000000000000..6d82d0966607 --- /dev/null +++ b/account_financial_report/report/templates/trial_balance.xml @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + diff --git a/account_financial_report/report/trial_balance.py b/account_financial_report/report/trial_balance.py new file mode 100644 index 000000000000..0ff7896c7249 --- /dev/null +++ b/account_financial_report/report/trial_balance.py @@ -0,0 +1,263 @@ +# ?? 2016 Julien Coux (Camptocamp) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import models, fields, api + + +class TrialBalanceReport(models.TransientModel): + """ Here, we just define class fields. + For methods, go more bottom at this file. + + The class hierarchy is : + * TrialBalanceReport + ** TrialBalanceReportAccount + *** TrialBalanceReportPartner + If "show_partner_details" is selected + """ + + _name = 'report_trial_balance' + + # Filters fields, used for data computation + date_from = fields.Date() + date_to = fields.Date() + fy_start_date = fields.Date() + only_posted_moves = fields.Boolean() + hide_account_balance_at_0 = fields.Boolean() + company_id = fields.Many2one(comodel_name='res.company') + filter_account_ids = fields.Many2many(comodel_name='account.account') + filter_partner_ids = fields.Many2many(comodel_name='res.partner') + show_partner_details = fields.Boolean() + + # General Ledger Report Data fields, + # used as base for compute the data reports + general_ledger_id = fields.Many2one( + comodel_name='report_general_ledger' + ) + + # Data fields, used to browse report data + account_ids = fields.One2many( + comodel_name='report_trial_balance_account', + inverse_name='report_id' + ) + + +class TrialBalanceReportAccount(models.TransientModel): + _name = 'report_trial_balance_account' + _order = 'code ASC' + + report_id = fields.Many2one( + comodel_name='report_trial_balance', + ondelete='cascade', + index=True + ) + + # Data fields, used to keep link with real object + account_id = fields.Many2one( + 'account.account', + index=True + ) + + # Data fields, used for report display + code = fields.Char() + name = fields.Char() + + initial_balance = fields.Float(digits=(16, 2)) + debit = fields.Float(digits=(16, 2)) + credit = fields.Float(digits=(16, 2)) + final_balance = fields.Float(digits=(16, 2)) + + # Data fields, used to browse report data + partner_ids = fields.One2many( + comodel_name='report_trial_balance_partner', + inverse_name='report_account_id' + ) + + +class TrialBalanceReportPartner(models.TransientModel): + _name = 'report_trial_balance_partner' + + report_account_id = fields.Many2one( + comodel_name='report_trial_balance_account', + ondelete='cascade', + index=True + ) + + # Data fields, used to keep link with real object + partner_id = fields.Many2one( + 'res.partner', + index=True + ) + + # Data fields, used for report display + name = fields.Char() + + initial_balance = fields.Float(digits=(16, 2)) + debit = fields.Float(digits=(16, 2)) + credit = fields.Float(digits=(16, 2)) + final_balance = fields.Float(digits=(16, 2)) + + @api.model + def _generate_order_by(self, order_spec, query): + """Custom order to display "No partner allocated" at last position.""" + return """ +ORDER BY + CASE + WHEN "report_trial_balance_partner"."partner_id" IS NOT NULL + THEN 0 + ELSE 1 + END, + "report_trial_balance_partner"."name" + """ + + +class TrialBalanceReportCompute(models.TransientModel): + """ Here, we just define methods. + For class fields, go more top at this file. + """ + + _inherit = 'report_trial_balance' + + @api.multi + def print_report(self, report_type): + self.ensure_one() + if report_type == 'xlsx': + report_name = 'a_f_r.report_trial_balance_xlsx' + else: + report_name = 'account_financial_report.' \ + 'report_trial_balance_qweb' + return self.env['ir.actions.report'].search( + [('report_name', '=', report_name), + ('report_type', '=', report_type)], limit=1).report_action(self) + + def _get_html(self): + result = {} + rcontext = {} + context = dict(self.env.context) + report = self.browse(context.get('active_id')) + if report: + rcontext['o'] = report + result['html'] = self.env.ref( + 'account_financial_report.report_trial_balance').render( + rcontext) + return result + + @api.model + def get_html(self, given_context=None): + return self._get_html() + + def _prepare_report_general_ledger(self): + self.ensure_one() + return { + 'date_from': self.date_from, + 'date_to': self.date_to, + 'only_posted_moves': self.only_posted_moves, + 'hide_account_balance_at_0': self.hide_account_balance_at_0, + 'company_id': self.company_id.id, + 'filter_account_ids': [(6, 0, self.filter_account_ids.ids)], + 'filter_partner_ids': [(6, 0, self.filter_partner_ids.ids)], + 'fy_start_date': self.fy_start_date, + } + + @api.multi + def compute_data_for_report(self): + self.ensure_one() + # Compute General Ledger Report Data. + # The data of Trial Balance Report + # are based on General Ledger Report data. + model = self.env['report_general_ledger'] + self.general_ledger_id = model.create( + self._prepare_report_general_ledger() + ) + self.general_ledger_id.compute_data_for_report( + with_line_details=False, with_partners=self.show_partner_details + ) + + # Compute report data + self._inject_account_values() + if self.show_partner_details: + self._inject_partner_values() + # Refresh cache because all data are computed with SQL requests + self.refresh() + + def _inject_account_values(self): + """Inject report values for report_trial_balance_account""" + query_inject_account = """ +INSERT INTO + report_trial_balance_account + ( + report_id, + create_uid, + create_date, + account_id, + code, + name, + initial_balance, + debit, + credit, + final_balance + ) +SELECT + %s AS report_id, + %s AS create_uid, + NOW() AS create_date, + rag.account_id, + rag.code, + rag.name, + rag.initial_balance AS initial_balance, + rag.final_debit - rag.initial_debit AS debit, + rag.final_credit - rag.initial_credit AS credit, + rag.final_balance AS final_balance +FROM + report_general_ledger_account rag +WHERE + rag.report_id = %s + """ + query_inject_account_params = ( + self.id, + self.env.uid, + self.general_ledger_id.id, + ) + self.env.cr.execute(query_inject_account, query_inject_account_params) + + def _inject_partner_values(self): + """Inject report values for report_trial_balance_partner""" + query_inject_partner = """ +INSERT INTO + report_trial_balance_partner + ( + report_account_id, + create_uid, + create_date, + partner_id, + name, + initial_balance, + debit, + credit, + final_balance + ) +SELECT + ra.id AS report_account_id, + %s AS create_uid, + NOW() AS create_date, + rpg.partner_id, + rpg.name, + rpg.initial_balance AS initial_balance, + rpg.final_debit - rpg.initial_debit AS debit, + rpg.final_credit - rpg.initial_credit AS credit, + rpg.final_balance AS final_balance +FROM + report_general_ledger_partner rpg +INNER JOIN + report_general_ledger_account rag ON rpg.report_account_id = rag.id +INNER JOIN + report_trial_balance_account ra ON rag.code = ra.code +WHERE + rag.report_id = %s +AND ra.report_id = %s + """ + query_inject_partner_params = ( + self.env.uid, + self.general_ledger_id.id, + self.id, + ) + self.env.cr.execute(query_inject_partner, query_inject_partner_params) diff --git a/account_financial_report/report/trial_balance_xlsx.py b/account_financial_report/report/trial_balance_xlsx.py new file mode 100644 index 000000000000..b06d80456671 --- /dev/null +++ b/account_financial_report/report/trial_balance_xlsx.py @@ -0,0 +1,122 @@ + +# Author: Julien Coux +# Copyright 2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + + +from odoo import _, models + + +class TrialBalanceXslx(models.AbstractModel): + _name = 'report.a_f_r.report_trial_balance_xlsx' + _inherit = 'report.account_financial_report.abstract_report_xlsx' + + def _get_report_name(self): + return _('Trial Balance') + + def _get_report_columns(self, report): + if not report.show_partner_details: + return { + 0: {'header': _('Code'), 'field': 'code', 'width': 10}, + 1: {'header': _('Account'), 'field': 'name', 'width': 60}, + 2: {'header': _('Initial balance'), + 'field': 'initial_balance', + 'type': 'amount', + 'width': 14}, + 3: {'header': _('Debit'), + 'field': 'debit', + 'type': 'amount', + 'width': 14}, + 4: {'header': _('Credit'), + 'field': 'credit', + 'type': 'amount', + 'width': 14}, + 5: {'header': _('Ending balance'), + 'field': 'final_balance', + 'type': 'amount', + 'width': 14}, + } + else: + return { + 0: {'header': _('Partner'), 'field': 'name', 'width': 70}, + 1: {'header': _('Initial balance'), + 'field': 'initial_balance', + 'type': 'amount', + 'width': 14}, + 2: {'header': _('Debit'), + 'field': 'debit', + 'type': 'amount', + 'width': 14}, + 3: {'header': _('Credit'), + 'field': 'credit', + 'type': 'amount', + 'width': 14}, + 4: {'header': _('Ending balance'), + 'field': 'final_balance', + 'type': 'amount', + 'width': 14}, + } + + def _get_report_filters(self, report): + return [ + [_('Date range filter'), + _('From: %s To: %s') % (report.date_from, report.date_to)], + [_('Target moves filter'), + _('All posted entries') if report.only_posted_moves + else _('All entries')], + [_('Account balance at 0 filter'), + _('Hide') if report.hide_account_balance_at_0 else _('Show')], + ] + + def _get_col_count_filter_name(self): + return 2 + + def _get_col_count_filter_value(self): + return 3 + + def _generate_report_content(self, workbook, report): + + if not report.show_partner_details: + # Display array header for account lines + self.write_array_header() + + # For each account + for account in report.account_ids: + if not report.show_partner_details: + # Display account lines + self.write_line(account) + + else: + # Write account title + self.write_array_title(account.code + ' - ' + account.name) + + # Display array header for partner lines + self.write_array_header() + + # For each partner + for partner in account.partner_ids: + # Display partner lines + self.write_line(partner) + + # Display account footer line + self.write_account_footer(account, + account.code + ' - ' + account.name) + + # Line break + self.row_pos += 2 + + def write_account_footer(self, account, name_value): + """Specific function to write account footer for Trial Balance""" + for col_pos, column in self.columns.items(): + if column['field'] == 'name': + value = name_value + else: + value = getattr(account, column['field']) + cell_type = column.get('type', 'string') + if cell_type == 'string': + self.sheet.write_string(self.row_pos, col_pos, value or '', + self.format_header_left) + elif cell_type == 'amount': + self.sheet.write_number(self.row_pos, col_pos, float(value), + self.format_header_amount) + self.row_pos += 1 diff --git a/account_financial_report/reports.xml b/account_financial_report/reports.xml new file mode 100644 index 000000000000..54ccfa6a034c --- /dev/null +++ b/account_financial_report/reports.xml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Account financial report qweb paperformat + + custom + 297 + 210 + Portrait + 12 + 8 + 5 + 5 + + 10 + 110 + + + + + + + + + + + + + + + + + + + + + + General Ledger XLSX + report_general_ledger + ir.actions.report + a_f_r.report_general_ledger_xlsx + xlsx + report_general_ledger + + + + Trial Balance XLSX + report_trial_balance + ir.actions.report + a_f_r.report_trial_balance_xlsx + xlsx + report_trial_balance + + + + Open Items XLSX + report_open_items + ir.actions.report + a_f_r.report_open_items_xlsx + xlsx + report_open_items + + + + Aged Partner Balance XLSX + report_aged_partner_balance + ir.actions.report + a_f_r.report_aged_partner_balance_xlsx + xlsx + report_aged_partner_balance + + + diff --git a/account_financial_report/static/description/icon.png b/account_financial_report/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..272d0294347c7c2fda3bc37cf726a9079c3f6313 GIT binary patch literal 15230 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 F{udP(AcX(` literal 0 HcmV?d00001 diff --git a/account_financial_report/static/src/css/report.css b/account_financial_report/static/src/css/report.css new file mode 100644 index 000000000000..b38df775b078 --- /dev/null +++ b/account_financial_report/static/src/css/report.css @@ -0,0 +1,105 @@ +body, table, td, span, div { + font-family: Helvetica, Arial; +} +.act_as_table { + display: table !important; +} +.act_as_row { + display: table-row !important; + page-break-inside: avoid; +} +.act_as_cell { + display: table-cell !important; + page-break-inside: avoid; +} +.act_as_thead { + display: table-header-group !important; +} +.act_as_tbody { + display: table-row-group !important; +} +.list_table, .data_table, .totals_table{ + width: 100% !important; + table-layout: fixed !important; +} +.act_as_row.labels { + background-color:#F0F0F0 !important; +} +.list_table, .data_table, .totals_table, .list_table .act_as_row { + border-left:0px; + border-right:0px; + text-align:left; + font-size:10px; + padding-right:3px; + padding-left:3px; + padding-top:2px; + padding-bottom:2px; + border-collapse:collapse; +} +.totals_table { + font-weight: bold; + text-align: center; +} +.list_table .act_as_row.labels, .list_table .act_as_row.initial_balance, .list_table .act_as_row.lines { + border-color:grey !important; + border-bottom:1px solid lightGrey !important; +} +.data_table .act_as_cell{ + border: 1px solid lightGrey; + text-align: center; +} +.data_table .act_as_cell, .list_table .act_as_cell, .totals_table .act_as_cell { + word-wrap: break-word; +} +.data_table .act_as_row.labels, .totals_table .act_as_row.labels { + font-weight: bold; +} +.initial_balance .act_as_cell { + font-style:italic; +} +.account_title { + font-size:11px; + font-weight:bold; +} +.account_title.labels { + background-color:#F0F0F0 !important; +} +.act_as_cell.amount { + word-wrap:normal; + text-align:right; +} +.act_as_cell.left { + text-align:left; +} +.act_as_cell.right { + text-align:right; +} +.list_table .act_as_cell{ + padding-left: 5px; +/* border-right:1px solid lightGrey; uncomment to active column lines */ +} +.list_table .act_as_cell.first_column { + padding-left: 0px; +/* border-left:1px solid lightGrey; uncomment to active column lines */ +} +.overflow_ellipsis { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +} +.custom_footer { + font-size:7px !important; +} +.page_break { + page-break-inside: avoid; +} + +.button_row { + padding-bottom: 10px; +} + +.o_account_financial_reports_page { + background-color: @odoo-view-background-color; + color: @odoo-main-text-color; + padding-top: 10px; +} diff --git a/account_financial_report/static/src/js/account_financial_report_backend.js b/account_financial_report/static/src/js/account_financial_report_backend.js new file mode 100644 index 000000000000..b1c39c23501f --- /dev/null +++ b/account_financial_report/static/src/js/account_financial_report_backend.js @@ -0,0 +1,109 @@ +odoo.define('account_financial_report.account_financial_report_backend', function (require) { +'use strict'; + +var core = require('web.core'); +var Widget = require('web.Widget'); +var ControlPanelMixin = require('web.ControlPanelMixin'); +var session = require('web.session'); +var ReportWidget = require('account_financial_report.account_financial_report_widget'); +var framework = require('web.framework'); +var crash_manager = require('web.crash_manager'); + +var QWeb = core.qweb; + +var report_backend = Widget.extend(ControlPanelMixin, { + // Stores all the parameters of the action. + events: { + 'click .o_account_financial_reports_print': 'print', + 'click .o_account_financial_reports_export': 'export', + }, + init: function(parent, action) { + this.actionManager = parent; + this.given_context = {}; + this.odoo_context = action.context; + this.controller_url = action.context.url; + if (action.context.context) { + this.given_context = action.context.context; + } + this.given_context.active_id = action.context.active_id || action.params.active_id; + this.given_context.model = action.context.active_model || false; + this.given_context.ttype = action.context.ttype || false; + return this._super.apply(this, arguments); + }, + willStart: function() { + return $.when(this.get_html()); + }, + set_html: function() { + var self = this; + var def = $.when(); + if (!this.report_widget) { + this.report_widget = new ReportWidget(this, this.given_context); + def = this.report_widget.appendTo(this.$el); + } + def.then(function () { + self.report_widget.$el.html(self.html); + }); + }, + start: function() { + this.set_html(); + return this._super(); + }, + // Fetches the html and is previous report.context if any, else create it + get_html: function() { + var self = this; + var defs = []; + return this._rpc({ + model: this.given_context.model, + method: 'get_html', + args: [self.given_context], + context: self.odoo_context, + }) + .then(function (result) { + self.html = result.html; + defs.push(self.update_cp()); + return $.when.apply($, defs); + }); + }, + // Updates the control panel and render the elements that have yet to be rendered + update_cp: function() { + if (!this.$buttons) { + + } + var status = { + breadcrumbs: this.actionManager.get_breadcrumbs(), + cp_content: {$buttons: this.$buttons}, + }; + return this.update_control_panel(status); + }, + do_show: function() { + this._super(); + this.update_cp(); + }, + print: function(e) { + var self = this; + this._rpc({ + model: this.given_context.model, + method: 'print_report', + args: [this.given_context.active_id, 'qweb-pdf'], + context: self.odoo_context, + }).then(function(result){ + self.do_action(result); + }); + }, + export: function(e) { + var self = this; + this._rpc({ + model: this.given_context.model, + method: 'print_report', + args: [this.given_context.active_id, 'xlsx'], + context: self.odoo_context, + }) + .then(function(result){ + self.do_action(result); + }); + }, +}); + +core.action_registry.add("account_financial_report_backend", report_backend); +return report_backend; +}); diff --git a/account_financial_report/static/src/js/account_financial_report_widgets.js b/account_financial_report/static/src/js/account_financial_report_widgets.js new file mode 100644 index 000000000000..12ee84ffa378 --- /dev/null +++ b/account_financial_report/static/src/js/account_financial_report_widgets.js @@ -0,0 +1,37 @@ +odoo.define('account_financial_report.account_financial_report_widget', function +(require) { +'use strict'; + +var core = require('web.core'); +var Widget = require('web.Widget'); + +var QWeb = core.qweb; + +var _t = core._t; + +var accountFinancialReportWidget = Widget.extend({ + events: { + 'click .o_account_financial_reports_web_action': 'boundLink', + }, + init: function(parent) { + this._super.apply(this, arguments); + }, + start: function() { + return this._super.apply(this, arguments); + }, + boundLink: function(e) { + var res_model = $(e.target).data('res-model') + var res_id = $(e.target).data('active-id') + return this.do_action({ + type: 'ir.actions.act_window', + res_model: res_model, + res_id: res_id, + views: [[false, 'form']], + target: 'current' + }); + }, +}); + +return accountFinancialReportWidget; + +}); diff --git a/account_financial_report/tests/__init__.py b/account_financial_report/tests/__init__.py new file mode 100644 index 000000000000..9f431dfd6b6b --- /dev/null +++ b/account_financial_report/tests/__init__.py @@ -0,0 +1,9 @@ + +# ?? 2016 Julien Coux (Camptocamp) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).- + +from . import abstract_test +from . import test_aged_partner_balance +from . import test_general_ledger +from . import test_open_items +from . import test_trial_balance diff --git a/account_financial_report/tests/abstract_test.py b/account_financial_report/tests/abstract_test.py new file mode 100644 index 000000000000..15d687b18c1f --- /dev/null +++ b/account_financial_report/tests/abstract_test.py @@ -0,0 +1,246 @@ + +# Author: Julien Coux +# Copyright 2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo.tests.common import TransactionCase +import logging +_logger = logging.getLogger(__name__) + +try: + from xlrd import open_workbook +except ImportError: + _logger.debug('Can not import xlsxwriter`.') + + +class AbstractTest(TransactionCase): + """Common technical tests for all reports.""" + + def setUp(cls): + super(AbstractTest, cls).setUp() + + cls.model = cls._getReportModel() + + cls.qweb_report_name = cls._getQwebReportName() + cls.xlsx_report_name = cls._getXlsxReportName() + cls.xlsx_action_name = cls._getXlsxReportActionName() + + cls.report_title = cls._getReportTitle() + + cls.base_filters = cls._getBaseFilters() + cls.additional_filters = cls._getAdditionalFiltersToBeTested() + + cls.report = cls.model.create(cls.base_filters) + cls.report.compute_data_for_report() + + def test_01_generation_report_qweb(self): + """Check if report PDF/HTML is correctly generated""" + + # Check if returned report action is correct + report_type = 'qweb-pdf' + report_action = self.report.print_report(report_type) + self.assertDictContainsSubset( + { + 'type': 'ir.actions.report', + 'report_name': self.qweb_report_name, + 'report_type': 'qweb-pdf', + }, + report_action + ) + + # Check if report template is correct + report = self.env['ir.actions.report'].search( + [('report_name', '=', self.qweb_report_name), + ('report_type', '=', report_type)], limit=1) + self.assertEqual(report.report_type, 'qweb-pdf') + + rep = report.render(self.report.ids, {}) + + self.assertTrue(self.report_title.encode('utf8') in rep[0]) + + self.assertTrue( + self.report.account_ids[0].name.encode('utf8') in rep[0] + ) + + def test_02_generation_report_html(self): + """Check if report HTML is correctly generated""" + + # Check if returned report action is correct + report_type = 'qweb-html' + report_action = self.report.print_report(report_type) + self.assertDictContainsSubset( + { + 'type': 'ir.actions.report', + 'report_name': self.qweb_report_name, + 'report_type': 'qweb-html', + }, + report_action + ) + + # Check if report template is correct + report = self.env['ir.actions.report'].search( + [('report_name', '=', self.qweb_report_name), + ('report_type', '=', report_type)], limit=1) + self.assertEqual(report.report_type, 'qweb-html') + + rep = report.render(self.report.ids, {}) + + self.assertTrue(self.report_title.encode('utf8') in rep[0]) + self.assertTrue( + self.report.account_ids[0].name.encode('utf8') in rep[0] + ) + + def test_03_generation_report_xlsx(self): + """Check if report XLSX is correctly generated""" + report_object = self.env['ir.actions.report'] + # Check if returned report action is correct + report_type = 'xlsx' + report_action = self.report.print_report(report_type) + self.assertDictContainsSubset( + { + 'type': 'ir.actions.report', + 'report_name': self.xlsx_report_name, + 'report_type': 'xlsx', + }, + report_action + ) + report = report_object._get_report_from_name(self.xlsx_report_name) + self.assertEqual(report.report_type, 'xlsx') + report_xlsx = report.render(self.report.ids, {}) + wb = open_workbook(file_contents=report_xlsx[0]) + sheet = wb.sheet_by_index(0) + class_name = 'report.%s' % report.report_name + self.assertEqual(sheet.cell(0, 0).value, + self.env[class_name]._get_report_name()) + + def test_04_compute_data(self): + """Check that the SQL queries work with all filters options""" + + for filters in [{}] + self.additional_filters: + current_filter = self.base_filters.copy() + current_filter.update(filters) + + report = self.model.create(current_filter) + report.compute_data_for_report() + + self.assertGreaterEqual(len(report.account_ids), 1) + + # Same filters with only one account + current_filter = self.base_filters.copy() + current_filter.update(filters) + current_filter.update({ + 'filter_account_ids': + [(6, 0, report.account_ids[0].account_id.ids)], + }) + + report2 = self.model.create(current_filter) + report2.compute_data_for_report() + + self.assertEqual(len(report2.account_ids), 1) + self.assertEqual(report2.account_ids.name, + report.account_ids[0].name) + + if self._partner_test_is_possible(filters): + # Same filters with only one partner + report_partner_ids = report.account_ids.mapped('partner_ids') + partner_ids = report_partner_ids.mapped('partner_id') + + current_filter = self.base_filters.copy() + current_filter.update(filters) + current_filter.update({ + 'filter_partner_ids': [(6, 0, partner_ids[0].ids)], + }) + + report3 = self.model.create(current_filter) + report3.compute_data_for_report() + + self.assertGreaterEqual(len(report3.account_ids), 1) + + report_partner_ids3 = report3.account_ids.mapped('partner_ids') + partner_ids3 = report_partner_ids3.mapped('partner_id') + + self.assertEqual(len(partner_ids3), 1) + self.assertEqual( + partner_ids3.name, + partner_ids[0].name + ) + + # Same filters with only one partner and one account + report_partner_ids = report3.account_ids.mapped('partner_ids') + report_account_id = report_partner_ids.filtered( + lambda p: p.partner_id + )[0].report_account_id + + current_filter = self.base_filters.copy() + current_filter.update(filters) + current_filter.update({ + 'filter_account_ids': + [(6, 0, report_account_id.account_id.ids)], + 'filter_partner_ids': [(6, 0, partner_ids[0].ids)], + }) + + report4 = self.model.create(current_filter) + report4.compute_data_for_report() + + self.assertEqual(len(report4.account_ids), 1) + self.assertEqual(report4.account_ids.name, + report_account_id.account_id.name) + + report_partner_ids4 = report4.account_ids.mapped('partner_ids') + partner_ids4 = report_partner_ids4.mapped('partner_id') + + self.assertEqual(len(partner_ids4), 1) + self.assertEqual( + partner_ids4.name, + partner_ids[0].name + ) + + def _partner_test_is_possible(self, filters): + """ + :return: + a boolean to indicate if partner test is possible + with current filters + """ + return True + + def _getReportModel(self): + """ + :return: the report model name + """ + raise NotImplementedError() + + def _getQwebReportName(self): + """ + :return: the qweb report name + """ + raise NotImplementedError() + + def _getXlsxReportName(self): + """ + :return: the xlsx report name + """ + raise NotImplementedError() + + def _getXlsxReportActionName(self): + """ + :return: the xlsx report action name + """ + raise NotImplementedError() + + def _getReportTitle(self): + """ + :return: the report title displayed into the report + """ + raise NotImplementedError() + + def _getBaseFilters(self): + """ + :return: the minimum required filters to generate report + """ + raise NotImplementedError() + + def _getAdditionalFiltersToBeTested(self): + """ + :return: the additional filters to generate report variants + """ + raise NotImplementedError() diff --git a/account_financial_report/tests/test_aged_partner_balance.py b/account_financial_report/tests/test_aged_partner_balance.py new file mode 100644 index 000000000000..7debd2919f76 --- /dev/null +++ b/account_financial_report/tests/test_aged_partner_balance.py @@ -0,0 +1,42 @@ + +# Author: Julien Coux +# Copyright 2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import time +from . import abstract_test + + +class TestAgedPartnerBalance(abstract_test.AbstractTest): + """ + Technical tests for Aged Partner Balance Report. + """ + + def _getReportModel(self): + return self.env['report_aged_partner_balance'] + + def _getQwebReportName(self): + return 'account_financial_report.report_aged_partner_balance_qweb' + + def _getXlsxReportName(self): + return 'a_f_r.report_aged_partner_balance_xlsx' + + def _getXlsxReportActionName(self): + return 'account_financial_report.' \ + 'action_report_aged_partner_balance_xlsx' + + def _getReportTitle(self): + return 'Odoo Report' + + def _getBaseFilters(self): + return { + 'date_at': time.strftime('%Y-12-31'), + 'company_id': self.env.ref('base.main_company').id, + } + + def _getAdditionalFiltersToBeTested(self): + return [ + {'only_posted_moves': True}, + {'show_move_line_details': True}, + {'only_posted_moves': True, 'show_move_line_details': True}, + ] diff --git a/account_financial_report/tests/test_general_ledger.py b/account_financial_report/tests/test_general_ledger.py new file mode 100644 index 000000000000..9bb00ee095e1 --- /dev/null +++ b/account_financial_report/tests/test_general_ledger.py @@ -0,0 +1,466 @@ + +# Author: Julien Coux +# Copyright 2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import time +from . import abstract_test +from odoo.tests.common import TransactionCase + + +class TestGeneralLedger(abstract_test.AbstractTest): + """ + Technical tests for General Ledger Report. + """ + + def _getReportModel(self): + return self.env['report_general_ledger'] + + def _getQwebReportName(self): + return 'account_financial_report.report_general_ledger_qweb' + + def _getXlsxReportName(self): + return 'a_f_r.report_general_ledger_xlsx' + + def _getXlsxReportActionName(self): + return 'account_financial_report.' \ + 'action_report_general_ledger_xlsx' + + def _getReportTitle(self): + return 'Odoo Report' + + def _getBaseFilters(self): + return { + 'date_from': time.strftime('%Y-01-01'), + 'date_to': time.strftime('%Y-12-31'), + 'company_id': self.env.ref('base.main_company').id, + 'fy_start_date': time.strftime('%Y-01-01'), + } + + def _getAdditionalFiltersToBeTested(self): + return [ + {'only_posted_moves': True}, + {'hide_account_balance_at_0': True}, + {'centralize': True}, + {'only_posted_moves': True, 'hide_account_balance_at_0': True}, + {'only_posted_moves': True, 'centralize': True}, + {'hide_account_balance_at_0': True, 'centralize': True}, + { + 'only_posted_moves': True, + 'hide_account_balance_at_0': True, + 'centralize': True + }, + ] + + +class TestGeneralLedgerReport(TransactionCase): + + def setUp(self): + super(TestGeneralLedgerReport, self).setUp() + self.before_previous_fy_year = '2014-05-05' + self.previous_fy_date_start = '2015-01-01' + self.previous_fy_date_end = '2015-12-31' + self.fy_date_start = '2016-01-01' + self.fy_date_end = '2016-12-31' + self.receivable_account = self.env['account.account'].search([ + ('user_type_id.name', '=', 'Receivable') + ], limit=1) + self.income_account = self.env['account.account'].search([ + ('user_type_id.name', '=', 'Income') + ], limit=1) + self.unaffected_account = self.env['account.account'].search([ + ( + 'user_type_id', + '=', + self.env.ref('account.data_unaffected_earnings').id + )], limit=1) + + def _add_move( + self, + date, + receivable_debit, + receivable_credit, + income_debit, + income_credit, + unaffected_debit=0, + unaffected_credit=0 + ): + move_name = 'expense accrual' + journal = self.env['account.journal'].search([ + ('code', '=', 'MISC')]) + partner = self.env.ref('base.res_partner_12') + move_vals = { + 'journal_id': journal.id, + 'partner_id': partner.id, + 'name': move_name, + 'date': date, + 'line_ids': [ + (0, 0, { + 'name': move_name, + 'debit': receivable_debit, + 'credit': receivable_credit, + 'account_id': self.receivable_account.id}), + (0, 0, { + 'name': move_name, + 'debit': income_debit, + 'credit': income_credit, + 'account_id': self.income_account.id}), + (0, 0, { + 'name': move_name, + 'debit': unaffected_debit, + 'credit': unaffected_credit, + 'account_id': self.unaffected_account.id}), + ]} + move = self.env['account.move'].create(move_vals) + move.post() + + def _get_report_lines(self, with_partners=False): + company = self.env.ref('base.main_company') + general_ledger = self.env['report_general_ledger'].create({ + 'date_from': self.fy_date_start, + 'date_to': self.fy_date_end, + 'only_posted_moves': True, + 'hide_account_balance_at_0': False, + 'company_id': company.id, + 'fy_start_date': self.fy_date_start, + }) + general_ledger.compute_data_for_report( + with_line_details=True, with_partners=with_partners + ) + lines = {} + report_account_model = self.env['report_general_ledger_account'] + lines['receivable'] = report_account_model.search([ + ('report_id', '=', general_ledger.id), + ('account_id', '=', self.receivable_account.id), + ]) + lines['income'] = report_account_model.search([ + ('report_id', '=', general_ledger.id), + ('account_id', '=', self.income_account.id), + ]) + lines['unaffected'] = report_account_model.search([ + ('report_id', '=', general_ledger.id), + ('account_id', '=', self.unaffected_account.id), + ]) + if with_partners: + report_partner_model = self.env[ + 'report_general_ledger_partner' + ] + lines['partner_receivable'] = report_partner_model.search([ + ('report_account_id', '=', lines['receivable'].id), + ('partner_id', '=', self.env.ref('base.res_partner_12').id), + ]) + return lines + + def test_01_account_balance(self): + # Generate the general ledger line + lines = self._get_report_lines() + self.assertEqual(len(lines['receivable']), 0) + self.assertEqual(len(lines['income']), 0) + + # Add a move at the previous day of the first day of fiscal year + # to check the initial balance + self._add_move( + date=self.previous_fy_date_end, + receivable_debit=1000, + receivable_credit=0, + income_debit=0, + income_credit=1000 + ) + + # Re Generate the general ledger line + lines = self._get_report_lines() + self.assertEqual(len(lines['receivable']), 1) + self.assertEqual(len(lines['income']), 0) + + # Check the initial and final balance + self.assertEqual(lines['receivable'].initial_debit, 1000) + self.assertEqual(lines['receivable'].initial_credit, 0) + self.assertEqual(lines['receivable'].initial_balance, 1000) + self.assertEqual(lines['receivable'].final_debit, 1000) + self.assertEqual(lines['receivable'].final_credit, 0) + self.assertEqual(lines['receivable'].final_balance, 1000) + + # Add reversale move of the initial move the first day of fiscal year + # to check the first day of fiscal year is not used + # to compute the initial balance + self._add_move( + date=self.fy_date_start, + receivable_debit=0, + receivable_credit=1000, + income_debit=1000, + income_credit=0 + ) + + # Re Generate the general ledger line + lines = self._get_report_lines() + self.assertEqual(len(lines['receivable']), 1) + self.assertEqual(len(lines['income']), 1) + + # Check the initial and final balance + self.assertEqual(lines['receivable'].initial_debit, 1000) + self.assertEqual(lines['receivable'].initial_credit, 0) + self.assertEqual(lines['receivable'].initial_balance, 1000) + self.assertEqual(lines['receivable'].final_debit, 1000) + self.assertEqual(lines['receivable'].final_credit, 1000) + self.assertEqual(lines['receivable'].final_balance, 0) + + self.assertEqual(lines['income'].initial_debit, 0) + self.assertEqual(lines['income'].initial_credit, 0) + self.assertEqual(lines['income'].initial_balance, 0) + self.assertEqual(lines['income'].final_debit, 1000) + self.assertEqual(lines['income'].final_credit, 0) + self.assertEqual(lines['income'].final_balance, 1000) + + # Add another move at the end day of fiscal year + # to check that it correctly used on report + self._add_move( + date=self.fy_date_end, + receivable_debit=0, + receivable_credit=1000, + income_debit=1000, + income_credit=0 + ) + + # Re Generate the general ledger line + lines = self._get_report_lines() + self.assertEqual(len(lines['receivable']), 1) + self.assertEqual(len(lines['income']), 1) + + # Check the initial and final balance + self.assertEqual(lines['receivable'].initial_debit, 1000) + self.assertEqual(lines['receivable'].initial_credit, 0) + self.assertEqual(lines['receivable'].initial_balance, 1000) + self.assertEqual(lines['receivable'].final_debit, 1000) + self.assertEqual(lines['receivable'].final_credit, 2000) + self.assertEqual(lines['receivable'].final_balance, -1000) + + self.assertEqual(lines['income'].initial_debit, 0) + self.assertEqual(lines['income'].initial_credit, 0) + self.assertEqual(lines['income'].initial_balance, 0) + self.assertEqual(lines['income'].final_debit, 2000) + self.assertEqual(lines['income'].final_credit, 0) + self.assertEqual(lines['income'].final_balance, 2000) + + def test_02_partner_balance(self): + # Generate the general ledger line + lines = self._get_report_lines(with_partners=True) + self.assertEqual(len(lines['partner_receivable']), 0) + + # Add a move at the previous day of the first day of fiscal year + # to check the initial balance + self._add_move( + date=self.previous_fy_date_end, + receivable_debit=1000, + receivable_credit=0, + income_debit=0, + income_credit=1000 + ) + + # Re Generate the general ledger line + lines = self._get_report_lines(with_partners=True) + self.assertEqual(len(lines['partner_receivable']), 1) + + # Check the initial and final balance + self.assertEqual(lines['partner_receivable'].initial_debit, 1000) + self.assertEqual(lines['partner_receivable'].initial_credit, 0) + self.assertEqual(lines['partner_receivable'].initial_balance, 1000) + self.assertEqual(lines['partner_receivable'].final_debit, 1000) + self.assertEqual(lines['partner_receivable'].final_credit, 0) + self.assertEqual(lines['partner_receivable'].final_balance, 1000) + + # Add reversale move of the initial move the first day of fiscal year + # to check the first day of fiscal year is not used + # to compute the initial balance + self._add_move( + date=self.fy_date_start, + receivable_debit=0, + receivable_credit=1000, + income_debit=1000, + income_credit=0 + ) + + # Re Generate the general ledger line + lines = self._get_report_lines(with_partners=True) + self.assertEqual(len(lines['partner_receivable']), 1) + + # Check the initial and final balance + self.assertEqual(lines['partner_receivable'].initial_debit, 1000) + self.assertEqual(lines['partner_receivable'].initial_credit, 0) + self.assertEqual(lines['partner_receivable'].initial_balance, 1000) + self.assertEqual(lines['partner_receivable'].final_debit, 1000) + self.assertEqual(lines['partner_receivable'].final_credit, 1000) + self.assertEqual(lines['partner_receivable'].final_balance, 0) + + # Add another move at the end day of fiscal year + # to check that it correctly used on report + self._add_move( + date=self.fy_date_end, + receivable_debit=0, + receivable_credit=1000, + income_debit=1000, + income_credit=0 + ) + + # Re Generate the general ledger line + lines = self._get_report_lines(with_partners=True) + self.assertEqual(len(lines['partner_receivable']), 1) + + # Check the initial and final balance + self.assertEqual(lines['partner_receivable'].initial_debit, 1000) + self.assertEqual(lines['partner_receivable'].initial_credit, 0) + self.assertEqual(lines['partner_receivable'].initial_balance, 1000) + self.assertEqual(lines['partner_receivable'].final_debit, 1000) + self.assertEqual(lines['partner_receivable'].final_credit, 2000) + self.assertEqual(lines['partner_receivable'].final_balance, -1000) + + def test_03_unaffected_account_balance(self): + # Generate the general ledger line + lines = self._get_report_lines() + self.assertEqual(len(lines['unaffected']), 1) + + # Check the initial and final balance + self.assertEqual(lines['unaffected'].initial_debit, 0) + self.assertEqual(lines['unaffected'].initial_credit, 0) + self.assertEqual(lines['unaffected'].initial_balance, 0) + self.assertEqual(lines['unaffected'].final_debit, 0) + self.assertEqual(lines['unaffected'].final_credit, 0) + self.assertEqual(lines['unaffected'].final_balance, 0) + + # Add a move at the previous day of the first day of fiscal year + # to check the initial balance + self._add_move( + date=self.previous_fy_date_end, + receivable_debit=1000, + receivable_credit=0, + income_debit=0, + income_credit=1000 + ) + + # Re Generate the general ledger line + lines = self._get_report_lines() + self.assertEqual(len(lines['unaffected']), 1) + + # Check the initial and final balance + self.assertEqual(lines['unaffected'].initial_debit, 0) + self.assertEqual(lines['unaffected'].initial_credit, 0) + self.assertEqual(lines['unaffected'].initial_balance, -1000) + self.assertEqual(lines['unaffected'].final_debit, -0) + self.assertEqual(lines['unaffected'].final_credit, 0) + self.assertEqual(lines['unaffected'].final_balance, -1000) + + # Add reversale move of the initial move the first day of fiscal year + # to check the first day of fiscal year is not used + # to compute the initial balance + self._add_move( + date=self.fy_date_start, + receivable_debit=0, + receivable_credit=0, + income_debit=0, + income_credit=1000, + unaffected_debit=1000, + unaffected_credit=0 + ) + + # Re Generate the general ledger line + lines = self._get_report_lines() + self.assertEqual(len(lines['unaffected']), 1) + + # Check the initial and final balance + self.assertEqual(lines['unaffected'].initial_debit, 0) + self.assertEqual(lines['unaffected'].initial_credit, 0) + self.assertEqual(lines['unaffected'].initial_balance, -1000) + self.assertEqual(lines['unaffected'].final_debit, 1000) + self.assertEqual(lines['unaffected'].final_credit, 0) + self.assertEqual(lines['unaffected'].final_balance, 0) + + # Add another move at the end day of fiscal year + # to check that it correctly used on report + self._add_move( + date=self.fy_date_end, + receivable_debit=3000, + receivable_credit=0, + income_debit=0, + income_credit=0, + unaffected_debit=0, + unaffected_credit=3000 + ) + + # Re Generate the general ledger line + lines = self._get_report_lines() + self.assertEqual(len(lines['unaffected']), 1) + + # Check the initial and final balance + self.assertEqual(lines['unaffected'].initial_debit, 0) + self.assertEqual(lines['unaffected'].initial_credit, 0) + self.assertEqual(lines['unaffected'].initial_balance, -1000) + self.assertEqual(lines['unaffected'].final_debit, 1000) + self.assertEqual(lines['unaffected'].final_credit, 3000) + self.assertEqual(lines['unaffected'].final_balance, -3000) + + def test_04_unaffected_account_balance_2_years(self): + # Generate the general ledger line + lines = self._get_report_lines() + self.assertEqual(len(lines['unaffected']), 1) + + # Check the initial and final balance + self.assertEqual(lines['unaffected'].initial_debit, 0) + self.assertEqual(lines['unaffected'].initial_credit, 0) + self.assertEqual(lines['unaffected'].initial_balance, 0) + self.assertEqual(lines['unaffected'].final_debit, 0) + self.assertEqual(lines['unaffected'].final_credit, 0) + self.assertEqual(lines['unaffected'].final_balance, 0) + + # Add a move at any date 2 years before the balance + # (to create an historic) + self._add_move( + date=self.before_previous_fy_year, + receivable_debit=0, + receivable_credit=1000, + income_debit=1000, + income_credit=0 + ) + + # Re Generate the general ledger line + lines = self._get_report_lines() + self.assertEqual(len(lines['unaffected']), 1) + + # Check the initial and final balance + self.assertEqual(lines['unaffected'].initial_debit, 0) + self.assertEqual(lines['unaffected'].initial_credit, 0) + self.assertEqual(lines['unaffected'].initial_balance, 1000) + self.assertEqual(lines['unaffected'].final_debit, 0) + self.assertEqual(lines['unaffected'].final_credit, 0) + self.assertEqual(lines['unaffected'].final_balance, 1000) + + # Affect the company's result last year + self._add_move( + date=self.previous_fy_date_start, + receivable_debit=1000, + receivable_credit=0, + income_debit=0, + income_credit=0, + unaffected_debit=0, + unaffected_credit=1000 + ) + + # Add another move last year to test the initial balance this year + self._add_move( + date=self.previous_fy_date_start, + receivable_debit=0, + receivable_credit=500, + income_debit=500, + income_credit=0, + unaffected_debit=0, + unaffected_credit=0 + ) + # Re Generate the general ledger line + lines = self._get_report_lines() + self.assertEqual(len(lines['unaffected']), 1) + + # Check the initial and final balance + self.assertEqual(lines['unaffected'].initial_debit, 0) + self.assertEqual(lines['unaffected'].initial_credit, 0) + self.assertEqual(lines['unaffected'].initial_balance, 500) + self.assertEqual(lines['unaffected'].final_debit, 0) + self.assertEqual(lines['unaffected'].final_credit, 0) + self.assertEqual(lines['unaffected'].final_balance, 500) diff --git a/account_financial_report/tests/test_open_items.py b/account_financial_report/tests/test_open_items.py new file mode 100644 index 000000000000..f505ab31181a --- /dev/null +++ b/account_financial_report/tests/test_open_items.py @@ -0,0 +1,41 @@ + +# Author: Julien Coux +# Copyright 2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import time +from . import abstract_test + + +class TestOpenItems(abstract_test.AbstractTest): + """ + Technical tests for Open Items Report. + """ + + def _getReportModel(self): + return self.env['report_open_items'] + + def _getQwebReportName(self): + return 'account_financial_report.report_open_items_qweb' + + def _getXlsxReportName(self): + return 'a_f_r.report_open_items_xlsx' + + def _getXlsxReportActionName(self): + return 'account_financial_report.action_report_open_items_xlsx' + + def _getReportTitle(self): + return 'Odoo Report' + + def _getBaseFilters(self): + return { + 'date_at': time.strftime('%Y-12-31'), + 'company_id': self.env.ref('base.main_company').id, + } + + def _getAdditionalFiltersToBeTested(self): + return [ + {'only_posted_moves': True}, + {'hide_account_balance_at_0': True}, + {'only_posted_moves': True, 'hide_account_balance_at_0': True}, + ] diff --git a/account_financial_report/tests/test_trial_balance.py b/account_financial_report/tests/test_trial_balance.py new file mode 100644 index 000000000000..0b18e80be666 --- /dev/null +++ b/account_financial_report/tests/test_trial_balance.py @@ -0,0 +1,54 @@ + +# Author: Julien Coux +# Copyright 2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import time +from . import abstract_test + + +class TestTrialBalance(abstract_test.AbstractTest): + """ + Technical tests for Trial Balance Report. + """ + + def _getReportModel(self): + return self.env['report_trial_balance'] + + def _getQwebReportName(self): + return 'account_financial_report.report_trial_balance_qweb' + + def _getXlsxReportName(self): + return 'a_f_r.report_trial_balance_xlsx' + + def _getXlsxReportActionName(self): + return 'account_financial_report.action_report_trial_balance_xlsx' + + def _getReportTitle(self): + return 'Odoo Report' + + def _getBaseFilters(self): + return { + 'date_from': time.strftime('%Y-01-01'), + 'date_to': time.strftime('%Y-12-31'), + 'company_id': self.env.ref('base.main_company').id, + 'fy_start_date': time.strftime('%Y-01-01'), + } + + def _getAdditionalFiltersToBeTested(self): + return [ + {'only_posted_moves': True}, + {'hide_account_balance_at_0': True}, + {'show_partner_details': True}, + {'only_posted_moves': True, 'hide_account_balance_at_0': True}, + {'only_posted_moves': True, 'show_partner_details': True}, + {'hide_account_balance_at_0': True, 'show_partner_details': True}, + { + 'only_posted_moves': True, + 'hide_account_balance_at_0': True, + 'show_partner_details': True + }, + ] + + def _partner_test_is_possible(self, filters): + return 'show_partner_details' in filters diff --git a/account_financial_report/view/account_view.xml b/account_financial_report/view/account_view.xml new file mode 100644 index 000000000000..8d312b32632c --- /dev/null +++ b/account_financial_report/view/account_view.xml @@ -0,0 +1,42 @@ + + + + + account.account.form.inherit + + account.account + form + + + + + + + + + General Ledger Report + report_general_ledger + + + + + Trial Balance Report + report_trial_balance + + + + + Open Items Report + report_open_items + + + + + Aged Partner Balance Report + report_aged_partner_balance + + + + + + diff --git a/account_financial_report/view/report_aged_partner_balance.xml b/account_financial_report/view/report_aged_partner_balance.xml new file mode 100644 index 000000000000..c6bf80e6adaf --- /dev/null +++ b/account_financial_report/view/report_aged_partner_balance.xml @@ -0,0 +1,9 @@ + + + + diff --git a/account_financial_report/view/report_general_ledger.xml b/account_financial_report/view/report_general_ledger.xml new file mode 100644 index 000000000000..f172317f742d --- /dev/null +++ b/account_financial_report/view/report_general_ledger.xml @@ -0,0 +1,9 @@ + + + + diff --git a/account_financial_report/view/report_open_items.xml b/account_financial_report/view/report_open_items.xml new file mode 100644 index 000000000000..29dd0220ca81 --- /dev/null +++ b/account_financial_report/view/report_open_items.xml @@ -0,0 +1,9 @@ + + + + diff --git a/account_financial_report/view/report_template.xml b/account_financial_report/view/report_template.xml new file mode 100644 index 000000000000..440399437e21 --- /dev/null +++ b/account_financial_report/view/report_template.xml @@ -0,0 +1,45 @@ + + + +