From 20934792ee7a5824e4cfec0b26b54bd4276dcd39 Mon Sep 17 00:00:00 2001 From: mreficent Date: Tue, 18 Apr 2017 11:17:36 +0200 Subject: [PATCH] [ADD] Account Journal Report --- account_journal_report/README.rst | 67 +++++ account_journal_report/__init__.py | 12 + account_journal_report/__openerp__.py | 30 ++ account_journal_report/i18n/es.po | 259 ++++++++++++++++++ account_journal_report/report/__init__.py | 12 + .../report/journal_ledger_report.py | 50 ++++ .../report/journal_ledger_report_xlsx.py | 201 ++++++++++++++ account_journal_report/report/reports.xml | 40 +++ .../static/description/icon.png | Bin 0 -> 15230 bytes account_journal_report/tests/__init__.py | 6 + .../tests/test_account_journal_report.py | 114 ++++++++ account_journal_report/views/templates.xml | 85 ++++++ account_journal_report/wizard/__init__.py | 11 + .../wizard/wizard_print_journal_ledger.py | 86 ++++++ .../wizard_print_journal_ledger_view.xml | 51 ++++ 15 files changed, 1024 insertions(+) create mode 100644 account_journal_report/README.rst create mode 100644 account_journal_report/__init__.py create mode 100644 account_journal_report/__openerp__.py create mode 100644 account_journal_report/i18n/es.po create mode 100644 account_journal_report/report/__init__.py create mode 100644 account_journal_report/report/journal_ledger_report.py create mode 100644 account_journal_report/report/journal_ledger_report_xlsx.py create mode 100644 account_journal_report/report/reports.xml create mode 100644 account_journal_report/static/description/icon.png create mode 100644 account_journal_report/tests/__init__.py create mode 100644 account_journal_report/tests/test_account_journal_report.py create mode 100644 account_journal_report/views/templates.xml create mode 100644 account_journal_report/wizard/__init__.py create mode 100644 account_journal_report/wizard/wizard_print_journal_ledger.py create mode 100644 account_journal_report/wizard/wizard_print_journal_ledger_view.xml diff --git a/account_journal_report/README.rst b/account_journal_report/README.rst new file mode 100644 index 00000000..f843cdf4 --- /dev/null +++ b/account_journal_report/README.rst @@ -0,0 +1,67 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +============== +Journal Report +============== + +Adds the following report in PDF and XLSX format: + +* Journal Ledger. It groups the moves by their entries. + +Configuration +============= + +Users willing to access to this report should have proper Accounting & Finance rights: + +#. Go to *Settings / Users* and edit your user to add the corresponding access rights as follows. +#. In *Application / Accounting & Finance*, select *Accountant* or *Adviser* options. + +Usage +===== + +To use this module, you need to: + +#. Go to *Invoicing* or *Accounting* menu +#. Press 'Reporting > PDF Reports > Journal Ledger' +#. Press 'Print' to print in PDF or 'Export' to export in Excel. + + +.. 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/9.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 smash it by providing detailed and welcomed feedback. + +Credits +======= + +Contributors +------------ + +* Joaquin Gutierrez +* Pedro M. Baeza +* RGB Consulting SL (http://www.rgbconsulting.com) +* Miquel Raïch + +Maintainer +---------- + +.. image:: http://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_journal_report/__init__.py b/account_journal_report/__init__.py new file mode 100644 index 00000000..cae5318c --- /dev/null +++ b/account_journal_report/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# Copyright 2013 Joaquin Gutierrez (http://www.gutierrezweb.es) +# Copyright 2015 Tecnativa S.L. (http://www.tecnativa.com) +# Pedro M. Baeza +# Copyright 2017 RGB Consulting S.L. (http://www.rgbconsulting.com) +# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# Miquel Raich +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import report +from . import wizard diff --git a/account_journal_report/__openerp__.py b/account_journal_report/__openerp__.py new file mode 100644 index 00000000..b70fddb3 --- /dev/null +++ b/account_journal_report/__openerp__.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Copyright 2013 Joaquin Gutierrez (http://www.gutierrezweb.es) +# Copyright 2015 Tecnativa - Pedro M. Baeza +# Copyright 2017 RGB Consulting +# Copyright 2017 Eficent - Miquel Raich +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': "Journal Report", + 'version': "9.0.1.0.0", + 'depends': [ + "account", + "report_xlsx", + ], + 'license': "AGPL-3", + 'author': "J. Gutierrez, " + "Tecnativa, " + "RGB Consulting, " + "Eficent, " + "Odoo Community Association (OCA)", + 'website': "http://github.com/OCA/account-financial-reporting", + 'category': "Accounting", + 'summary': "Journal Report", + 'data': [ + "wizard/wizard_print_journal_ledger_view.xml", + "report/reports.xml", + "views/templates.xml", + ], + "installable": True, +} diff --git a/account_journal_report/i18n/es.po b/account_journal_report/i18n/es.po new file mode 100644 index 00000000..7c970981 --- /dev/null +++ b/account_journal_report/i18n/es.po @@ -0,0 +1,259 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_journal_report +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0c\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-05-02 14:48+0200\n" +"PO-Revision-Date: 2017-05-02 14:48+0200\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 1.8.7.1\n" +"Last-Translator: \n" +"Language: es\n" + +#. module: account_journal_report +#: code:addons/account_journal_report/report/journal_ledger_report_xlsx.py:74 +#, python-format +msgid " to " +msgstr " a " + +#. module: account_journal_report +#: model:ir.ui.view,arch_db:account_journal_report.journal_ledger +msgid "Account name" +msgstr "Nombre cuenta" + +#. module: account_journal_report +#: model:ir.ui.view,arch_db:account_journal_report.journal_ledger +msgid "Account" +msgstr "Cuenta" + +#. module: account_journal_report +#: model:ir.ui.view,arch_db:account_journal_report.journal_ledger +msgid "Description" +msgstr "Descripción" + +#. module: account_journal_report +#: code:addons/account_journal_report/report/journal_ledger_report_xlsx.py:95 +#, python-format +msgid "Account" +msgstr "Cuenta" + +#. module: account_journal_report +#: code:addons/account_journal_report/report/journal_ledger_report_xlsx.py:98 +#, python-format +msgid "Account name" +msgstr "Nombre cuenta" + +#. module: account_journal_report +#: selection:account.journal.entries.report,sort_selection:0 +msgid "By date" +msgstr "Por fecha" + +#. module: account_journal_report +#: selection:account.journal.entries.report,sort_selection:0 +msgid "By entry number" +msgstr "Por nº de entrada" + +#. module: account_journal_report +#: selection:account.journal.entries.report,sort_selection:0 +msgid "By reference number" +msgstr "Por referencia" + +#. module: account_journal_report +#: model:ir.ui.view,arch_db:account_journal_report.account_journal_ledger_report_view +msgid "Cancel" +msgstr "Cancelar" + +#. module: account_journal_report +#: model:ir.model.fields,field_description:account_journal_report.field_account_journal_entries_report_create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: account_journal_report +#: model:ir.model.fields,field_description:account_journal_report.field_account_journal_entries_report_create_date +msgid "Created on" +msgstr "Creado el" + +#. module: account_journal_report +#: code:addons/account_journal_report/report/journal_ledger_report_xlsx.py:107 +#: model:ir.ui.view,arch_db:account_journal_report.journal_ledger +#, python-format +msgid "Credit" +msgstr "Haber" + +#. module: account_journal_report +#: code:addons/account_journal_report/report/journal_ledger_report_xlsx.py:92 +#: model:ir.ui.view,arch_db:account_journal_report.journal_ledger +#, python-format +msgid "Date" +msgstr "Fecha" + +#. module: account_journal_report +#: model:ir.model.fields,field_description:account_journal_report.field_account_journal_entries_report_date_end +msgid "Date end" +msgstr "Fecha final" + +#. module: account_journal_report +#: model:ir.model.fields,field_description:account_journal_report.field_account_journal_entries_report_date_start +msgid "Date start" +msgstr "Fecha inicial" + +#. module: account_journal_report +#: code:addons/account_journal_report/report/journal_ledger_report_xlsx.py:104 +#: model:ir.ui.view,arch_db:account_journal_report.journal_ledger +#, python-format +msgid "Debit" +msgstr "Debe" + +#. module: account_journal_report +#: code:addons/account_journal_report/report/journal_ledger_report_xlsx.py:101 +#, python-format +msgid "Description" +msgstr "Descripción" + +#. module: account_journal_report +#: model:ir.model.fields,field_description:account_journal_report.field_account_journal_entries_report_display_name +#: model:ir.model.fields,field_description:account_journal_report.field_report_account_journal_report_journal_ledger_display_name +msgid "Display Name" +msgstr "Nombre mostrado" + +#. module: account_journal_report +#: model:ir.model.fields,field_description:account_journal_report.field_account_journal_entries_report_sort_selection +msgid "Entries Sorted By" +msgstr "Entradas ordenadas por" + +#. module: account_journal_report +#: code:addons/account_journal_report/report/journal_ledger_report_xlsx.py:89 +#: model:ir.ui.view,arch_db:account_journal_report.journal_ledger +#, python-format +msgid "Entry" +msgstr "Entrada" + +#. module: account_journal_report +#: model:ir.ui.view,arch_db:account_journal_report.account_journal_ledger_report_view +msgid "Export" +msgstr "Exportar" + +#. module: account_journal_report +#: model:ir.ui.view,arch_db:account_journal_report.journal_ledger +msgid "From" +msgstr "De" + +#. module: account_journal_report +#: code:addons/account_journal_report/report/journal_ledger_report_xlsx.py:74 +#, python-format +msgid "From " +msgstr "De " + +#. module: account_journal_report +#: model:ir.model.fields,field_description:account_journal_report.field_account_journal_entries_report_id +#: model:ir.model.fields,field_description:account_journal_report.field_report_account_journal_report_journal_ledger_id +msgid "ID" +msgstr "ID" + +#. module: account_journal_report +#: code:addons/account_journal_report/report/journal_ledger_report_xlsx.py:79 +#: model:ir.ui.view,arch_db:account_journal_report.journal_ledger +#, python-format +msgid "Journal" +msgstr "Diario" + +#. module: account_journal_report +#: code:addons/account_journal_report/report/journal_ledger_report_xlsx.py:180 +#: model:ir.actions.report.xml,name:account_journal_report.account_journal_ledger_report +#: model:ir.actions.report.xml,name:account_journal_report.action_account_journal_ledger_xlsx +#: model:ir.ui.menu,name:account_journal_report.menu_journal_ledger_report +#, python-format +msgid "Journal Ledger" +msgstr "Libro Diario" + +#. module: account_journal_report +#: model:ir.ui.view,arch_db:account_journal_report.journal_ledger +msgid "Journal Ledger -" +msgstr "Libro Diario -" + +#. module: account_journal_report +#: model:ir.model.fields,field_description:account_journal_report.field_account_journal_entries_report_journal_ids +msgid "Journals" +msgstr "Diarios" + +#. module: account_journal_report +#: model:ir.model.fields,field_description:account_journal_report.field_account_journal_entries_report_landscape +msgid "Landscape mode" +msgstr "Modo apaisado" + +#. module: account_journal_report +#: model:ir.model.fields,field_description:account_journal_report.field_account_journal_entries_report___last_update +#: model:ir.model.fields,field_description:account_journal_report.field_report_account_journal_report_journal_ledger___last_update +msgid "Last Modified on" +msgstr "Última modificación el" + +#. module: account_journal_report +#: model:ir.model.fields,field_description:account_journal_report.field_account_journal_entries_report_write_uid +msgid "Last Updated by" +msgstr "Última actualización por" + +#. module: account_journal_report +#: model:ir.model.fields,field_description:account_journal_report.field_account_journal_entries_report_write_date +msgid "Last Updated on" +msgstr "Última actualización el" + +#. module: account_journal_report +#: code:addons/account_journal_report/wizard/wizard_print_journal_ledger.py:57 +#: code:addons/account_journal_report/wizard/wizard_print_journal_ledger.py:76 +#, python-format +msgid "No data available" +msgstr "No hay datos disponibles" + +#. module: account_journal_report +#: code:addons/account_journal_report/wizard/wizard_print_journal_ledger.py:58 +#: code:addons/account_journal_report/wizard/wizard_print_journal_ledger.py:77 +#, python-format +msgid "No records found for your selection!" +msgstr "No se han encontrado registros para su selección" + +#. module: account_journal_report +#: code:addons/account_journal_report/report/journal_ledger_report_xlsx.py:82 +#: model:ir.ui.view,arch_db:account_journal_report.journal_ledger +#, python-format +msgid "Partner" +msgstr "Empresa" + +#. module: account_journal_report +#: model:ir.ui.view,arch_db:account_journal_report.account_journal_ledger_report_view +msgid "Print" +msgstr "Imprimir" + +#. module: account_journal_report +#: model:ir.actions.act_window,name:account_journal_report.wizard_account_journal_ledger_report_new +#: model:ir.model,name:account_journal_report.model_account_journal_entries_report +msgid "Print Journal Ledger" +msgstr "Imprimir Libro Diario" + +#. module: account_journal_report +#: model:ir.ui.view,arch_db:account_journal_report.account_journal_ledger_report_view +msgid "Print journal ledger" +msgstr "Imprimir libro diario" + +#. module: account_journal_report +#: code:addons/account_journal_report/report/journal_ledger_report_xlsx.py:85 +#: model:ir.ui.view,arch_db:account_journal_report.journal_ledger +#, python-format +msgid "Reference" +msgstr "Referencia" + +#. module: account_journal_report +#: model:ir.ui.view,arch_db:account_journal_report.account_journal_ledger_report_view +msgid "or" +msgstr "o" + +#. module: account_journal_report +#: model:ir.ui.view,arch_db:account_journal_report.journal_ledger +msgid "to" +msgstr "a" diff --git a/account_journal_report/report/__init__.py b/account_journal_report/report/__init__.py new file mode 100644 index 00000000..5f29cc02 --- /dev/null +++ b/account_journal_report/report/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# Copyright 2013 Joaquin Gutierrez (http://www.gutierrezweb.es) +# Copyright 2015 Tecnativa S.L. (http://www.tecnativa.com) +# Pedro M. Baeza +# Copyright 2017 RGB Consulting S.L. (http://www.rgbconsulting.com) +# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# Miquel Raich +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import journal_ledger_report +from . import journal_ledger_report_xlsx diff --git a/account_journal_report/report/journal_ledger_report.py b/account_journal_report/report/journal_ledger_report.py new file mode 100644 index 00000000..97eba2fa --- /dev/null +++ b/account_journal_report/report/journal_ledger_report.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# Copyright 2013 Joaquin Gutierrez (http://www.gutierrezweb.es) +# Copyright 2015 Tecnativa - Pedro M. Baeza +# Copyright 2017 RGB Consulting +# Copyright 2017 Eficent - Miquel Raich +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import api, fields, models + + +class JournalLedger(models.AbstractModel): + _name = 'report.account_journal_report.journal_ledger' + + def _format_date_to_lang(self, str_date): + if 'lang' not in self._context: + return str_date + lang_code = self._context['lang'] + lang_id = self.env['res.lang']._lang_get(lang_code) + lang = self.env['res.lang'].browse(lang_id) + date = fields.Date.from_string(str_date) + return date.strftime(lang.date_format) + + @api.multi + def render_html(self, data): + account_move_obj = self.env['account.move'] + report_obj = self.env['report'] + + date_start = data.get('date_start') + date_end = data.get('date_end') + journal_ids = data.get('journal_ids', []) + + move_ids = account_move_obj.search( + [('date', '<=', date_end), + ('date', '>=', date_start), + ('journal_id', 'in', journal_ids), + ('state', '!=', 'draft')], + order=data.get('sort_selection', 'date') + ', id') + + report = report_obj._get_report_from_name( + 'account_journal_report.journal_ledger') + docargs = { + 'doc_model': report.model, + 'docs': move_ids, + 'date_start': self._format_date_to_lang( + date_start), + 'date_end': self._format_date_to_lang( + date_end), + } + return report_obj.render( + 'account_journal_report.journal_ledger', docargs) diff --git a/account_journal_report/report/journal_ledger_report_xlsx.py b/account_journal_report/report/journal_ledger_report_xlsx.py new file mode 100644 index 00000000..bc768ea8 --- /dev/null +++ b/account_journal_report/report/journal_ledger_report_xlsx.py @@ -0,0 +1,201 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 RGB Consulting S.L. (http://www.rgbconsulting.com) +# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# Miquel Raich +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +try: + from openerp.addons.report_xlsx.report.report_xlsx import ReportXlsx +except ImportError: + ReportXlsx = object +from openerp.report import report_sxw +from openerp import _ + + +class JournalLedgerXlsx(ReportXlsx): + def __init__(self, name, table, rml=False, parser=False, header=True, + store=False): + super(ReportXlsx, self).__init__( + name, table, rml, parser, header, store) + + self.sheet = None + self.row_pos = None + + self.format_title = None + self.format_border_top = None + + def _define_formats(self, workbook): + """ Add cell formats to current workbook. + Available formats: + * format_title + * format_header + * format_header_right + * format_header_italic + * format_border_top + """ + self.format_title = workbook.add_format({ + 'bold': True, + 'align': 'center', + 'bg_color': '#46C646', + 'border': True + }) + self.format_header = workbook.add_format({ + 'bold': True, + 'bg_color': '#FFFFCC', + 'border': True + }) + self.format_header_right = workbook.add_format({ + 'bold': True, + 'bg_color': '#FFFFCC', + 'border': True, + 'align': 'right' + }) + self.format_header_italic = workbook.add_format({ + 'bold': True, + 'bg_color': '#FFFFCC', + 'border': True, + 'italic': True + }) + self.format_border_top = workbook.add_format({ + 'top': 1, + 'bg_color': '#eeeeee' + }) + + def _write_report_title(self, title): + self.sheet.merge_range( + self.row_pos, 0, self.row_pos, 6, title, self.format_title + ) + self.row_pos += 1 + + def _write_report_range(self, date1, date2): + self.sheet.merge_range( + self.row_pos, 0, self.row_pos, 1, + _('From ')+date1+_(' to ')+date2+_(':')) + self.row_pos += 1 + + def _set_headers(self): + # Journal + self.sheet.write_string(self.row_pos, 2, _('Journal'), + self.format_header) + # Partner + self.sheet.write_string(self.row_pos, 3, _('Partner'), + self.format_header) + # Reference + self.sheet.write_string(self.row_pos, 4, _('Reference'), + self.format_header) + self.row_pos += 1 + # Entry + self.sheet.write_string(self.row_pos, 0, _('Entry'), + self.format_header) + # Date + self.sheet.write_string(self.row_pos, 1, _('Date'), self.format_header) + + # Account + self.sheet.write_string(self.row_pos, 2, _('Account'), + self.format_header_italic) + # Account name + self.sheet.write_string(self.row_pos, 3, _('Account name'), + self.format_header_italic) + # Description + self.sheet.write_string(self.row_pos, 4, _('Description'), + self.format_header_italic) + # Debit + self.sheet.write_string(self.row_pos, 5, _('Debit'), + self.format_header_right) + # Credit + self.sheet.write_string(self.row_pos, 6, _('Credit'), + self.format_header_right) + self.sheet.freeze_panes(4, 0) + self.row_pos += 1 + + def _generate_report_content(self, report_data): + for move in report_data: + # Entry + self.sheet.write_string(self.row_pos, 0, move.name or '', + self.format_border_top) + self.sheet.set_column(0, 0, 18) + # Date + self.sheet.write_string(self.row_pos, 1, move.date or '', + self.format_border_top) + self.sheet.set_column(1, 1, 12) + # Journal + self.sheet.write_string(self.row_pos, 2, + move.journal_id.name or '', + self.format_border_top) + self.sheet.set_column(2, 2, 30) + # Partner + self.sheet.write_string(self.row_pos, 3, + move.partner_id.name or '', + self.format_border_top) + self.sheet.set_column(3, 3, 40) + # Reference + self.sheet.write_string(self.row_pos, 4, move.ref or '', + self.format_border_top) + self.sheet.set_column(4, 4, 40) + # Debit + self.sheet.write_number(self.row_pos, 5, move.amount or 0, + self.format_border_top) + self.sheet.set_column(5, 5, 12) + # Credit + self.sheet.write_number(self.row_pos, 6, move.amount or 0, + self.format_border_top) + self.sheet.set_column(6, 6, 12) + + self.row_pos += 1 + for line in move.line_ids: + # Account code + self.sheet.write_string(self.row_pos, 2, + line.account_id.code or '') + # Account name + self.sheet.write_string(self.row_pos, 3, + line.account_id.name or '') + # Line description + self.sheet.write_string(self.row_pos, 4, line.name or '') + # Debit + self.sheet.write_number(self.row_pos, 5, line.debit or 0) + # Credit + self.sheet.write_number(self.row_pos, 6, line.credit or 0) + self.row_pos += 1 + + def generate_xlsx_report(self, workbook, data, objects): + date_start = data.get('date_start') + date_end = data.get('date_end') + journal_ids = data.get('journal_ids', []) + + report_data = self.env['account.move'].search( + [('date', '<=', date_end), + ('date', '>=', date_start), + ('journal_id', 'in', journal_ids), + ('state', '!=', 'draft')], + order=data.get('sort_selection', 'date') + ', id') + + # Initial row + self.row_pos = 0 + + # Load formats to workbook + self._define_formats(workbook) + + # Set report name + report_name = _('Journal Ledger') + ' - ' + \ + self.env.user.company_id.name + self.sheet = workbook.add_worksheet(report_name[:31]) + if data.get('landscape'): + self.sheet.set_landscape() + self.sheet.fit_to_pages(1, 0) + self.sheet.set_zoom(80) + self._write_report_title(report_name) + self._write_report_range(date_start, date_end) + + # Set headers + self._set_headers() + + # Generate data + self._generate_report_content(report_data) + + +if ReportXlsx != object: + JournalLedgerXlsx( + 'report.account_journal_report.journal_ledger_xlsx', + 'account.journal', parser=report_sxw.rml_parse + ) diff --git a/account_journal_report/report/reports.xml b/account_journal_report/report/reports.xml new file mode 100644 index 00000000..f528e795 --- /dev/null +++ b/account_journal_report/report/reports.xml @@ -0,0 +1,40 @@ + + + + + + + Journal Ledger + account.journal + ir.actions.report.xml + account_journal_report.journal_ledger_xlsx + xlsx + + + + + Account Journal Ledger + + A4 + + 10 + 10 + 7 + 7 + 0 + 90 + + + + + + + diff --git a/account_journal_report/static/description/icon.png b/account_journal_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_journal_report/tests/__init__.py b/account_journal_report/tests/__init__.py new file mode 100644 index 00000000..1ace59a9 --- /dev/null +++ b/account_journal_report/tests/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from . import test_account_journal_report diff --git a/account_journal_report/tests/test_account_journal_report.py b/account_journal_report/tests/test_account_journal_report.py new file mode 100644 index 00000000..c2b9dbb9 --- /dev/null +++ b/account_journal_report/tests/test_account_journal_report.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from openerp import fields +from openerp.tests.common import SavepointCase + + +class TestAccountJournalReport(SavepointCase): + """ + Tests for Account Journal Report. + """ + def setUp(self): + super(TestAccountJournalReport, self).setUp() + + self.report_model = \ + self.env['report.account_journal_report.journal_ledger'] + self.wiz = self.env['account.journal.entries.report'] + self.report_name = 'account_journal_report.journal_ledger' + self.action_name = 'account_journal_report.' \ + 'account_journal_ledger_report' + self.report_xlsx_name = 'account_journal_report.journal_ledger_xlsx' + self.action_xlsx_name = 'account_journal_report.' \ + 'action_account_journal_ledger_xlsx' + self.report_title = 'Journal Ledger' + + self.today = fields.Date.today() + + self.account_type = self.env['account.account.type'].create({ + 'name': 'Test account type', + 'type': 'other', + }) + self.account = self.env['account.account'].create({ + 'name': 'Test account', + 'code': 'TEST', + 'user_type_id': self.account_type.id, + }) + self.journal = self.env['account.journal'].create({ + 'name': 'Test journal', + 'code': 'TEST', + 'type': 'general', + }) + self.move = self.env['account.move'].create({ + 'journal_id': self.journal.id, + 'date': fields.Date.today(), + 'line_ids': [ + (0, 0, { + 'name': 'Debit line', + 'account_id': self.account.id, + 'debit': 100, + }), + (0, 0, { + 'name': 'Credit line', + 'account_id': self.account.id, + 'credit': 100, + }), + ], + }) + self.move.post() + + def test_account_journal_report(self): + + wiz_id = self.wiz.create({ + 'journal_ids': [(6, 0, self.journal.ids)], + 'date_start': self.today, + 'date_end': self.today, + }) + report = wiz_id.print_report() + + self.assertDictContainsSubset( + { + 'type': 'ir.actions.report.xml', + 'report_name': self.report_name, + 'report_type': 'qweb-pdf', + }, + report, + 'There was an error and the PDF report was not generated.' + ) + + data = wiz_id.read()[0] + report = self.env.ref(self.action_name).\ + render_report( + wiz_id.ids, self.report_name, data + ) + self.assertGreaterEqual(len(report[0]), 1) + self.assertEqual(report[1], 'html') + + def test_account_journal_report_xlsx(self): + + wiz_id = self.wiz.create({ + 'journal_ids': [(6, 0, self.journal.ids)], + 'date_start': self.today, + 'date_end': self.today, + }) + report = wiz_id.print_report_xlsx() + + self.assertDictContainsSubset( + { + 'type': 'ir.actions.report.xml', + 'report_type': 'xlsx', + 'report_name': self.report_xlsx_name, + }, + report, + 'There was an error and the XLSX report was not generated.' + ) + + data = wiz_id.read()[0] + report_xlsx = self.env.ref(self.action_xlsx_name).\ + render_report( + wiz_id.ids, self.report_xlsx_name, data + ) + + self.assertGreaterEqual(len(report_xlsx[0]), 1) + self.assertEqual(report_xlsx[1], 'xlsx') diff --git a/account_journal_report/views/templates.xml b/account_journal_report/views/templates.xml new file mode 100644 index 00000000..6adb8aff --- /dev/null +++ b/account_journal_report/views/templates.xml @@ -0,0 +1,85 @@ + + + + + diff --git a/account_journal_report/wizard/__init__.py b/account_journal_report/wizard/__init__.py new file mode 100644 index 00000000..8d8781d8 --- /dev/null +++ b/account_journal_report/wizard/__init__.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +# Copyright 2013 Joaquin Gutierrez (http://www.gutierrezweb.es) +# Copyright 2015 Tecnativa S.L. (http://www.tecnativa.com) +# Pedro M. Baeza +# Copyright 2017 RGB Consulting S.L. (http://www.rgbconsulting.com) +# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# Miquel Raich +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import wizard_print_journal_ledger diff --git a/account_journal_report/wizard/wizard_print_journal_ledger.py b/account_journal_report/wizard/wizard_print_journal_ledger.py new file mode 100644 index 00000000..e850b289 --- /dev/null +++ b/account_journal_report/wizard/wizard_print_journal_ledger.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +# Copyright 2013 Joaquin Gutierrez (http://www.gutierrezweb.es) +# Copyright 2015 Tecnativa - Pedro M. Baeza +# Copyright 2017 RGB Consulting +# Copyright 2017 Eficent - Miquel Raich +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from datetime import date, timedelta +from openerp import api, fields, models, exceptions, _ + + +class AccountJournalLedgerReport(models.TransientModel): + _name = "account.journal.entries.report" + _description = "Print Journal Ledger" + + @api.model + def _default_journal_ids(self): + return self.env['account.journal'].search([]) + + journal_ids = fields.Many2many( + comodel_name='account.journal', + relation="account_journal_entries_report_journals_rel", + string='Journals', required=True, default=_default_journal_ids, + ) + date_start = fields.Date(required=True, + default=fields.Date.to_string( + date.today()-timedelta(days=90))) + date_end = fields.Date(required=True, + default=fields.Date.to_string(date.today())) + sort_selection = fields.Selection( + [('date', 'By date'), + ('name', 'By entry number'), + ('ref', 'By reference number')], + string='Entries Sorted By', required=True, default='name', + ) + landscape = fields.Boolean(string='Landscape mode', default=True) + + @api.multi + def _check_data(self): + if not self.journal_ids: + return False + journal_obj = self.env['account.journal'] + for journal in self.journal_ids: + ids_journal = journal_obj.search( + [('id', '=', journal.id)]) + if ids_journal: + return True + return False + + @api.multi + def print_report(self): + """Print report PDF""" + + # Check data + if not self._check_data(): + raise exceptions.Warning( + _('No data available'), + _('No records found for your selection!')) + + report_name = 'account_journal_report.journal_ledger' + + # Call report + data = self.read()[0] + return self.env['report'].with_context( + {'landscape': self.landscape}).get_action(self, + report_name=report_name, + data=data) + + @api.multi + def print_report_xlsx(self): + """Print report XLSX""" + + # Check data + if not self._check_data(): + raise exceptions.Warning( + _('No data available'), + _('No records found for your selection!')) + + # Call report + data = self.read()[0] + report_name = 'account_journal_report.journal_ledger_xlsx' + return { + 'type': 'ir.actions.report.xml', + 'report_name': report_name, + 'report_type': 'xlsx', + 'datas': data} diff --git a/account_journal_report/wizard/wizard_print_journal_ledger_view.xml b/account_journal_report/wizard/wizard_print_journal_ledger_view.xml new file mode 100644 index 00000000..9cb927d1 --- /dev/null +++ b/account_journal_report/wizard/wizard_print_journal_ledger_view.xml @@ -0,0 +1,51 @@ + + + + Print journal ledger + account.journal.entries.report + +
+ + + + + + + + + + + + + +
+ + + Print Journal Ledger + ir.actions.act_window + account.journal.entries.report + form + form + + new + + + +