From ef6d62632a269c264349c1c1124b3effd849f940 Mon Sep 17 00:00:00 2001 From: Benjamin Willig Date: Mon, 6 Nov 2017 07:58:28 +0100 Subject: [PATCH] [ADD] Qweb and xlsx journal report --- account_financial_report_qweb/README.rst | 1 + account_financial_report_qweb/__manifest__.py | 3 + account_financial_report_qweb/menuitems.xml | 8 + .../report/__init__.py | 2 + .../report/abstract_report_xlsx.py | 22 +- .../report/journal_report.py | 764 ++++++++++++++++++ .../report/journal_report_xlsx.py | 274 +++++++ .../report/templates/journal.xml | 445 ++++++++++ account_financial_report_qweb/reports.xml | 22 + .../tests/__init__.py | 1 + .../tests/test_journal.py | 292 +++++++ .../wizard/__init__.py | 1 + .../wizard/journal_report_wizard.py | 119 +++ .../wizard/journal_report_wizard.xml | 66 ++ 14 files changed, 2016 insertions(+), 4 deletions(-) create mode 100644 account_financial_report_qweb/report/journal_report.py create mode 100644 account_financial_report_qweb/report/journal_report_xlsx.py create mode 100644 account_financial_report_qweb/report/templates/journal.xml create mode 100644 account_financial_report_qweb/tests/test_journal.py create mode 100644 account_financial_report_qweb/wizard/journal_report_wizard.py create mode 100644 account_financial_report_qweb/wizard/journal_report_wizard.xml diff --git a/account_financial_report_qweb/README.rst b/account_financial_report_qweb/README.rst index 7836ebbb..b127e61d 100644 --- a/account_financial_report_qweb/README.rst +++ b/account_financial_report_qweb/README.rst @@ -52,6 +52,7 @@ Contributors * Julien Coux * Akim Juillerat * Alexis de Lattre +* Benjamin Willig Much of the work in this module was done at a sprint in Sorrento, Italy in April 2016. diff --git a/account_financial_report_qweb/__manifest__.py b/account_financial_report_qweb/__manifest__.py index 59c289f2..a5ed59c9 100644 --- a/account_financial_report_qweb/__manifest__.py +++ b/account_financial_report_qweb/__manifest__.py @@ -11,6 +11,7 @@ 'author': 'Camptocamp SA,' 'initOS GmbH,' 'redCOR AG,' + 'ACSONE SA/NV,' 'Odoo Community Association (OCA)', "website": "https://odoo-community.org/", 'depends': [ @@ -23,12 +24,14 @@ 'data': [ 'wizard/aged_partner_balance_wizard_view.xml', 'wizard/general_ledger_wizard_view.xml', + 'wizard/journal_report_wizard.xml', 'wizard/open_items_wizard_view.xml', 'wizard/trial_balance_wizard_view.xml', 'menuitems.xml', 'reports.xml', 'report/templates/aged_partner_balance.xml', 'report/templates/general_ledger.xml', + 'report/templates/journal.xml', 'report/templates/layouts.xml', 'report/templates/open_items.xml', 'report/templates/trial_balance.xml', diff --git a/account_financial_report_qweb/menuitems.xml b/account_financial_report_qweb/menuitems.xml index 4d36885f..e81877c7 100644 --- a/account_financial_report_qweb/menuitems.xml +++ b/account_financial_report_qweb/menuitems.xml @@ -15,6 +15,14 @@ sequence="10" /> + + = %s + AND + am.date <= %s + """ + if self.move_target != 'all': + where_clause += """ + AND + am.state = %s + """ + return where_clause + + @api.multi + def _get_inject_move_order_by(self): + self.ensure_one() + order_by = """ + ORDER BY + """ + if self.sort_option == 'move_name': + order_by += " am.name" + elif self.sort_option == 'date': + order_by += " am.date, am.name" + return order_by + + @api.multi + def _get_inject_move_params(self): + params = [ + self.env.uid, + self.id, + self.date_from, + self.date_to + ] + + if self.move_target != 'all': + params.append(self.move_target) + + return tuple(params) + + @api.multi + def _inject_move_line_values(self): + self.ensure_one() + sql = """ + INSERT INTO report_journal_qweb_move_line ( + create_uid, + create_date, + report_id, + report_journal_id, + report_move_id, + move_line_id, + account_id, + account, + account_code, + account_type, + partner_id, + partner, + date, + entry, + label, + debit, + credit, + company_currency_id, + amount_currency, + currency_id, + currency_name, + tax_id, + taxes_description, + company_id + ) + SELECT + %s as create_uid, + NOW() as create_date, + rjqm.report_id as report_id, + rjqm.report_journal_id as report_journal_id, + rjqm.id as report_move_id, + aml.id as move_line_id, + aml.account_id as account_id, + aa.name as account, + aa.code as account_code, + aa.internal_type as account_type, + aml.partner_id as partner_id, + p.name as partner, + aml.date as date, + rjqm.name as entry, + aml.name as label, + aml.debit as debit, + aml.credit as credit, + aml.company_currency_id as currency_id, + aml.amount_currency as amount_currency, + aml.currency_id as currency_id, + currency.name as currency_name, + aml.tax_line_id as tax_id, + CASE + WHEN + aml.tax_line_id is not null + THEN + COALESCE(at.description, at.name) + WHEN + aml.tax_line_id is null + THEN + (SELECT + array_to_string( + array_agg(COALESCE(at.description, at.name) + ), ', ') + FROM + account_move_line_account_tax_rel aml_at_rel + LEFT JOIN + account_tax at on (at.id = aml_at_rel.account_tax_id) + WHERE + aml_at_rel.account_move_line_id = aml.id) + ELSE + '' + END as taxes_description, + aml.company_id as company_id + FROM + account_move_line aml + INNER JOIN + report_journal_qweb_move rjqm + on (rjqm.move_id = aml.move_id) + LEFT JOIN + account_account aa + on (aa.id = aml.account_id) + LEFT JOIN + res_partner p + on (p.id = aml.partner_id) + LEFT JOIN + account_tax at + on (at.id = aml.tax_line_id) + LEFT JOIN + res_currency currency + on (currency.id = aml.currency_id) + WHERE + rjqm.report_id = %s + """ + params = ( + self.env.uid, + self.id, + ) + self.env.cr.execute(sql, params) + + @api.multi + def _inject_report_tax_values(self): + self.ensure_one() + sql_distinct_tax_id = """ + SELECT + distinct(jrqjtl.tax_id) + FROM + report_journal_qweb_journal_tax_line jrqjtl + WHERE + jrqjtl.report_id = %s + """ + self.env.cr.execute(sql_distinct_tax_id, (self.id,)) + rows = self.env.cr.fetchall() + tax_ids = set([row[0] for row in rows]) + + sql = """ + INSERT INTO report_journal_qweb_report_tax_line ( + create_uid, + create_date, + report_id, + tax_id, + tax_name, + tax_code, + base_debit, + base_credit, + tax_debit, + tax_credit + ) + SELECT + %s as create_uid, + NOW() as create_date, + %s as report_id, + %s as tax_id, + at.name as tax_name, + at.description as tax_code, + ( + SELECT sum(base_debit) + FROM report_journal_qweb_journal_tax_line jrqjtl2 + WHERE jrqjtl2.report_id = %s + AND jrqjtl2.tax_id = %s + ) as base_debit, + ( + SELECT sum(base_credit) + FROM report_journal_qweb_journal_tax_line jrqjtl2 + WHERE jrqjtl2.report_id = %s + AND jrqjtl2.tax_id = %s + ) as base_credit, + ( + SELECT sum(tax_debit) + FROM report_journal_qweb_journal_tax_line jrqjtl2 + WHERE jrqjtl2.report_id = %s + AND jrqjtl2.tax_id = %s + ) as tax_debit, + ( + SELECT sum(tax_credit) + FROM report_journal_qweb_journal_tax_line jrqjtl2 + WHERE jrqjtl2.report_id = %s + AND jrqjtl2.tax_id = %s + ) as tax_credit + FROM + report_journal_qweb_journal_tax_line jrqjtl + LEFT JOIN + account_tax at + on (at.id = jrqjtl.tax_id) + WHERE + jrqjtl.report_id = %s + AND + jrqjtl.tax_id = %s + """ + + for tax_id in tax_ids: + params = ( + self.env.uid, + self.id, + tax_id, + self.id, + tax_id, + self.id, + tax_id, + self.id, + tax_id, + self.id, + tax_id, + self.id, + tax_id, + ) + self.env.cr.execute(sql, params) + + @api.multi + def _inject_journal_tax_values(self): + self.ensure_one() + + sql_distinct_tax_id = """ + SELECT + distinct(jrqml.tax_id) + FROM + report_journal_qweb_move_line jrqml + WHERE + jrqml.report_journal_id = %s + """ + + tax_ids_by_journal_id = {} + for report_journal in self.report_journal_ids: + if report_journal.id not in tax_ids_by_journal_id: + tax_ids_by_journal_id[report_journal.id] = [] + self.env.cr.execute(sql_distinct_tax_id, (report_journal.id,)) + rows = self.env.cr.fetchall() + tax_ids_by_journal_id[report_journal.id].extend([ + row[0] for row in rows if row[0] + ]) + + sql = """ + INSERT INTO report_journal_qweb_journal_tax_line ( + create_uid, + create_date, + report_id, + report_journal_id, + tax_id, + tax_name, + tax_code, + base_debit, + base_credit, + tax_debit, + tax_credit + ) + SELECT + %s as create_uid, + NOW() as create_date, + %s as report_id, + %s as report_journal_id, + %s as tax_id, + at.name as tax_name, + at.description as tax_code, + ( + SELECT sum(debit) + FROM report_journal_qweb_move_line jrqml2 + WHERE jrqml2.report_journal_id = %s + AND ( + SELECT + count(*) + FROM + account_move_line_account_tax_rel aml_at_rel + WHERE + aml_at_rel.account_move_line_id = + jrqml2.move_line_id + AND + aml_at_rel.account_tax_id = %s + ) > 0 + ) as base_debit, + ( + SELECT sum(credit) + FROM report_journal_qweb_move_line jrqml2 + WHERE jrqml2.report_journal_id = %s + AND ( + SELECT + count(*) + FROM + account_move_line_account_tax_rel aml_at_rel + WHERE + aml_at_rel.account_move_line_id = + jrqml2.move_line_id + AND + aml_at_rel.account_tax_id = %s + ) > 0 + ) as base_credit, + ( + SELECT sum(debit) + FROM report_journal_qweb_move_line jrqml2 + WHERE jrqml2.report_journal_id = %s + AND jrqml2.tax_id = %s + ) as tax_debit, + ( + SELECT sum(credit) + FROM report_journal_qweb_move_line jrqml2 + WHERE jrqml2.report_journal_id = %s + AND jrqml2.tax_id = %s + ) as tax_credit + FROM + report_journal_qweb_journal rjqj + LEFT JOIN + account_tax at + on (at.id = %s) + WHERE + rjqj.id = %s + """ + + for report_journal_id in tax_ids_by_journal_id: + tax_ids = tax_ids_by_journal_id[report_journal_id] + for tax_id in tax_ids: + params = ( + self.env.uid, + self.id, + report_journal_id, + tax_id, + report_journal_id, + tax_id, + report_journal_id, + tax_id, + report_journal_id, + tax_id, + report_journal_id, + tax_id, + tax_id, + report_journal_id, + ) + self.env.cr.execute(sql, params) + + @api.multi + def _update_journal_report_total_values(self): + self.ensure_one() + sql = """ + UPDATE + report_journal_qweb_journal rjqj + SET + debit = ( + SELECT sum(rjqml.debit) + FROM report_journal_qweb_move_line rjqml + WHERE rjqml.report_journal_id = rjqj.id + ), + credit = ( + SELECT sum(rjqml.credit) + FROM report_journal_qweb_move_line rjqml + WHERE rjqml.report_journal_id = rjqj.id + ) + WHERE + rjqj.report_id = %s + """ + self.env.cr.execute(sql, (self.id,)) + + @api.multi + def print_report(self, xlsx_report=False): + self.ensure_one() + self.compute_data_for_report() + if xlsx_report: + report_name = 'account_financial_report_qweb.' \ + 'report_journal_xlsx' + else: + report_name = 'account_financial_report_qweb.' \ + 'report_journal_qweb' + return self.env['report'].get_action( + docids=self.ids, report_name=report_name) + + +class ReportJournalQwebJournal(models.TransientModel): + + _name = 'report_journal_qweb_journal' + + name = fields.Char( + required=True, + ) + code = fields.Char() + report_id = fields.Many2one( + comodel_name='report_journal_qweb', + required=True, + ondelete='cascade' + ) + journal_id = fields.Many2one( + comodel_name='account.journal', + required=True, + ondelete='cascade', + ) + report_move_ids = fields.One2many( + comodel_name='report_journal_qweb_move', + inverse_name='report_journal_id', + ) + report_tax_line_ids = fields.One2many( + comodel_name='report_journal_qweb_journal_tax_line', + inverse_name='report_journal_id', + ) + debit = fields.Float( + digits=DIGITS, + ) + credit = fields.Float( + digits=DIGITS, + ) + company_id = fields.Many2one( + comodel_name='res.company', + required=True, + ondelete='cascade' + ) + currency_id = fields.Many2one( + comodel_name='res.currency', + ) + + +class ReportJournalQwebMove(models.TransientModel): + + _name = 'report_journal_qweb_move' + + report_id = fields.Many2one( + comodel_name='report_journal_qweb', + required=True, + ondelete='cascade' + ) + report_journal_id = fields.Many2one( + comodel_name='report_journal_qweb_journal', + required=True, + ondelete='cascade', + ) + move_id = fields.Many2one( + comodel_name='account.move', + required=True, + ondelete='cascade', + ) + report_move_line_ids = fields.One2many( + comodel_name='report_journal_qweb_move_line', + inverse_name='report_move_id', + ) + name = fields.Char() + company_id = fields.Many2one( + comodel_name='res.company', + required=True, + ondelete='cascade' + ) + + +class ReportJournalQwebMoveLine(models.TransientModel): + + _name = 'report_journal_qweb_move_line' + _order = 'partner_id desc, account_id desc' + + report_id = fields.Many2one( + comodel_name='report_journal_qweb', + required=True, + ondelete='cascade' + ) + report_journal_id = fields.Many2one( + comodel_name='report_journal_qweb_journal', + required=True, + ondelete='cascade', + ) + report_move_id = fields.Many2one( + comodel_name='report_journal_qweb_move', + required=True, + ondelete='cascade', + ) + move_line_id = fields.Many2one( + comodel_name='account.move.line', + required=True, + ondelete='cascade', + ) + account_id = fields.Many2one( + comodel_name='account.account' + ) + account = fields.Char() + account_code = fields.Char() + account_type = fields.Char() + partner = fields.Char() + partner_id = fields.Many2one( + comodel_name='res.partner', + ) + date = fields.Date() + entry = fields.Char() + label = fields.Char() + debit = fields.Float( + digits=DIGITS, + ) + credit = fields.Float( + digits=DIGITS, + ) + company_currency_id = fields.Many2one( + comodel_name='res.currency', + ) + amount_currency = fields.Monetary( + currency_field='currency_id', + ) + currency_id = fields.Many2one( + comodel_name='res.currency', + ) + currency_name = fields.Char() + taxes_description = fields.Char() + tax_id = fields.Many2one( + comodel_name='account.tax' + ) + company_id = fields.Many2one( + comodel_name='res.company', + required=True, + ondelete='cascade' + ) + + +class ReportJournalQwebReportTaxLine(models.TransientModel): + + _name = 'report_journal_qweb_report_tax_line' + _order = 'tax_code' + + report_id = fields.Many2one( + comodel_name='report_journal_qweb', + required=True, + ondelete='cascade' + ) + tax_id = fields.Many2one( + comodel_name='account.tax' + ) + tax_name = fields.Char() + tax_code = fields.Char() + base_debit = fields.Float( + digits=DIGITS, + ) + base_credit = fields.Float( + digits=DIGITS, + ) + base_balance = fields.Float( + digits=DIGITS, + compute='_compute_base_balance', + ) + tax_debit = fields.Float( + digits=DIGITS, + ) + tax_credit = fields.Float( + digits=DIGITS, + ) + tax_balance = fields.Float( + digits=DIGITS, + compute='_compute_tax_balance' + ) + + @api.multi + def _compute_base_balance(self): + for rec in self: + rec.base_balance = rec.base_debit - rec.base_credit + + @api.multi + def _compute_tax_balance(self): + for rec in self: + rec.tax_balance = rec.tax_debit - rec.tax_credit + + +class ReportJournalQwebJournalTaxLine(models.TransientModel): + + _name = 'report_journal_qweb_journal_tax_line' + _inherit = 'report_journal_qweb_report_tax_line' + _order = 'tax_code' + + report_journal_id = fields.Many2one( + comodel_name='report_journal_qweb_journal', + required=True, + ondelete='cascade', + ) diff --git a/account_financial_report_qweb/report/journal_report_xlsx.py b/account_financial_report_qweb/report/journal_report_xlsx.py new file mode 100644 index 00000000..7a36204c --- /dev/null +++ b/account_financial_report_qweb/report/journal_report_xlsx.py @@ -0,0 +1,274 @@ +# -*- coding: utf-8 -*- +# Author: Damien Crier +# Author: Julien Coux +# Copyright 2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import abstract_report_xlsx +from odoo.report import report_sxw +from odoo import _ + + +class JournalXslx(abstract_report_xlsx.AbstractReportXslx): + + def __init__( + self, name, table, rml=False, parser=False, header=True, + store=False): + super(JournalXslx, self).__init__( + name, table, rml, parser, header, store) + + def _get_report_name(self): + return _('Journal') + + def _get_report_columns(self, report): + columns = [ + { + 'header': _('Entry'), + 'field': 'entry', + 'width': 18 + }, + { + 'header': _('Date'), + 'field': 'date', + 'width': 11 + }, + { + 'header': _('Account'), + 'field': 'account_code', + 'width': 9 + }, + ] + + if report.with_account_name: + columns.append({ + 'header': _('Account Name'), + 'field': 'account', + 'width': 15 + }) + + columns += [ + { + 'header': _('Partner'), + 'field': 'partner', + 'width': 25 + }, + { + 'header': _('Ref - Label'), + 'field': 'label', + 'width': 40 + }, + { + 'header': _('Taxes'), + 'field': 'taxes_description', + 'width': 11 + }, + { + 'header': _('Debit'), + 'field': 'debit', + 'type': 'amount', + 'width': 14, + }, + { + 'header': _('Credit'), + 'field': 'credit', + 'type': 'amount', + 'width': 14 + } + ] + + if report.with_currency: + columns += [ + { + 'header': _('Amount Currency'), + 'field': 'amount_currency', + 'type': 'amount', + 'width': 14 + }, + { + 'header': _('Currency'), + 'field': 'currency_name', + 'width': 14 + } + ] + + columns_as_dict = {} + for i, column in enumerate(columns): + columns_as_dict[i] = column + return columns_as_dict + + def _get_journal_tax_columns(self, report): + return { + 0: { + 'header': _('Name'), + 'field': 'tax_name', + 'width': 35 + }, + 1: { + 'header': _('Description'), + 'field': 'tax_code', + 'width': 18 + }, + 2: { + 'header': _('Base Debit'), + 'field': 'base_debit', + 'type': 'amount', + 'width': 14 + }, + 3: { + 'header': _('Base Credit'), + 'field': 'base_credit', + 'type': 'amount', + 'width': 14 + }, + 4: { + 'header': _('Base Balance'), + 'field': 'base_balance', + 'type': 'amount', + 'width': 14 + }, + 5: { + 'header': _('Tax Debit'), + 'field': 'tax_debit', + 'type': 'amount', + 'width': 14 + }, + 6: { + 'header': _('Tax Credit'), + 'field': 'tax_credit', + 'type': 'amount', + 'width': 14 + }, + 7: { + 'header': _('Tax Balance'), + 'field': 'tax_balance', + 'type': 'amount', + 'width': 14 + }, + } + + def _get_col_count_filter_name(self): + return 2 + + def _get_col_count_filter_value(self): + return 3 + + def _get_report_filters(self, report): + target_label_by_value = { + value: label + for value, label in + self.env['journal.report.wizard']._get_move_targets() + } + + sort_option_label_by_value = { + value: label + for value, label in + self.env['journal.report.wizard']._get_sort_options() + } + + return [ + [ + _('Company'), + report.company_id.name + ], + [ + _('Date range filter'), + _('From: %s To: %s') % (report.date_from, report.date_to) + ], + [ + _('Target moves filter'), + _("%s") % target_label_by_value[report.move_target], + ], + [ + _('Entries sorted by'), + _("%s") % sort_option_label_by_value[report.sort_option], + ], + [ + _('Journals'), + ', '.join([ + "%s - %s" % (report_journal.code, report_journal.name) + for report_journal in report.report_journal_ids + ]) + + ] + ] + + def _generate_report_content(self, workbook, report): + group_option = report.group_option + if group_option == 'journal': + for report_journal in report.report_journal_ids: + self._generate_journal_content(workbook, report_journal) + elif group_option == 'none': + self._generate_no_group_content(workbook, report) + + def _generate_no_group_content(self, workbook, report): + self._generate_moves_content( + workbook, report, "Report", report.report_move_ids) + self._generate_no_group_taxes_summary(workbook, report) + + def _generate_journal_content(self, workbook, report_journal): + sheet_name = "%s (%s) - %s" % ( + report_journal.code, + report_journal.currency_id.name, + report_journal.name, + ) + self._generate_moves_content( + workbook, report_journal.report_id, sheet_name, + report_journal.report_move_ids) + self._generate_journal_taxes_summary(workbook, report_journal) + + def _generate_no_group_taxes_summary(self, workbook, report): + self._generate_taxes_summary( + workbook, report, "Tax Report", report.report_tax_line_ids) + + def _generate_journal_taxes_summary(self, workbook, report_journal): + sheet_name = "Tax - %s (%s) - %s" % ( + report_journal.code, + report_journal.currency_id.name, + report_journal.name, + ) + report = report_journal.report_id + self._generate_taxes_summary( + workbook, report, sheet_name, report_journal.report_tax_line_ids) + + def _generate_moves_content(self, workbook, report, sheet_name, moves): + report_sheet = self.add_sheet(workbook, sheet_name) + self.set_sheet(report_sheet) + self._set_column_width() + + self.row_pos = 1 + + self.write_array_title(sheet_name) + self.row_pos += 2 + + self.write_array_header() + for move in moves: + for line in move.report_move_line_ids: + self.write_line(line) + self.row_pos += 1 + + def _generate_taxes_summary(self, workbook, report, sheet_name, tax_lines): + tax_journal_sheet = self.add_sheet(workbook, sheet_name) + self.set_sheet(tax_journal_sheet) + + self.row_pos = 1 + self.write_array_title(sheet_name) + self.row_pos += 2 + + tax_columns = self._get_journal_tax_columns(report) + self._set_columns_width(tax_columns) + + for col_pos, column in tax_columns.iteritems(): + self.sheet.write( + self.row_pos, col_pos, column['header'], + self.format_header_center + ) + self.row_pos += 1 + for tax_line in tax_lines: + self._write_line(tax_columns, tax_line) + + +JournalXslx( + 'report.account_financial_report_qweb.report_journal_xlsx', + 'report_journal_qweb', + parser=report_sxw.rml_parse +) diff --git a/account_financial_report_qweb/report/templates/journal.xml b/account_financial_report_qweb/report/templates/journal.xml new file mode 100644 index 00000000..c966f732 --- /dev/null +++ b/account_financial_report_qweb/report/templates/journal.xml @@ -0,0 +1,445 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/account_financial_report_qweb/reports.xml b/account_financial_report_qweb/reports.xml index 33330c0c..e2863a79 100644 --- a/account_financial_report_qweb/reports.xml +++ b/account_financial_report_qweb/reports.xml @@ -12,6 +12,15 @@ file="account_financial_report_qweb.report_general_ledger_qweb" /> + + + + + + @@ -84,6 +97,15 @@ + + Journal XLSX + report_journal_qweb + ir.actions.report.xml + account_financial_report_qweb.report_journal_xlsx + xlsx + + + Trial Balance XLSX report_trial_balance_qweb diff --git a/account_financial_report_qweb/tests/__init__.py b/account_financial_report_qweb/tests/__init__.py index af7f04a0..2d91b5cd 100644 --- a/account_financial_report_qweb/tests/__init__.py +++ b/account_financial_report_qweb/tests/__init__.py @@ -5,5 +5,6 @@ from . import abstract_test from . import test_aged_partner_balance from . import test_general_ledger +from . import test_journal from . import test_open_items from . import test_trial_balance diff --git a/account_financial_report_qweb/tests/test_journal.py b/account_financial_report_qweb/tests/test_journal.py new file mode 100644 index 00000000..fcc3b708 --- /dev/null +++ b/account_financial_report_qweb/tests/test_journal.py @@ -0,0 +1,292 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from datetime import datetime +from dateutil.relativedelta import relativedelta + +from odoo.fields import Date +from odoo.tests.common import TransactionCase + + +class TestJournalReport(TransactionCase): + + def setUp(self): + super(TestJournalReport, self).setUp() + self.AccountObj = self.env['account.account'] + self.InvoiceObj = self.env['account.invoice'] + self.JournalObj = self.env['account.journal'] + self.JournalReportObj = self.env['journal.report.wizard'] + self.MoveObj = self.env['account.move'] + self.ReportJournalQweb = self.env['report_journal_qweb'] + self.TaxObj = self.env['account.tax'] + + self.company = self.env.ref('base.main_company') + + today = datetime.today() + last_year = today - relativedelta(years=1) + + self.previous_fy_date_start = Date.to_string( + last_year.replace(month=1, day=1)) + self.previous_fy_date_end = Date.to_string( + last_year.replace(month=12, day=31)) + self.fy_date_start = Date.to_string( + today.replace(month=1, day=1)) + self.fy_date_end = Date.to_string( + today.replace(month=12, day=31)) + + self.receivable_account = self.AccountObj.search([ + ('user_type_id.name', '=', 'Receivable') + ], limit=1) + self.income_account = self.AccountObj.search([ + ('user_type_id.name', '=', 'Income') + ], limit=1) + self.payable_account = self.AccountObj.search([ + ('user_type_id.name', '=', 'Payable') + ], limit=1) + + self.journal_sale = self.JournalObj.create({ + 'name': "Test journal sale", + 'code': "TST-JRNL-S", + 'type': 'sale', + 'company_id': self.company.id, + }) + self.journal_purchase = self.JournalObj.create({ + 'name': "Test journal purchase", + 'code': "TST-JRNL-P", + 'type': 'sale', + 'company_id': self.company.id, + }) + + self.tax_15_s = self.TaxObj.create({ + 'sequence': 30, + 'name': 'Tax 15.0% (Percentage of Price)', + 'amount': 15.0, + 'amount_type': 'percent', + 'include_base_amount': False, + 'type_tax_use': 'sale', + }) + + self.tax_20_s = self.TaxObj.create({ + 'sequence': 30, + 'name': 'Tax 20.0% (Percentage of Price)', + 'amount': 20.0, + 'amount_type': 'percent', + 'include_base_amount': False, + 'type_tax_use': 'sale', + }) + + self.tax_15_p = self.TaxObj.create({ + 'sequence': 30, + 'name': 'Tax 15.0% (Percentage of Price)', + 'amount': 15.0, + 'amount_type': 'percent', + 'include_base_amount': False, + 'type_tax_use': 'purchase', + }) + + self.tax_20_p = self.TaxObj.create({ + 'sequence': 30, + 'name': 'Tax 20.0% (Percentage of Price)', + 'amount': 20.0, + 'amount_type': 'percent', + 'include_base_amount': False, + 'type_tax_use': 'purchase', + }) + + self.partner_2 = self.env.ref('base.res_partner_2') + + def _add_move( + self, date, journal, + receivable_debit, receivable_credit, income_debit, income_credit): + move_name = 'move name' + move_vals = { + 'journal_id': journal.id, + '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 + }), + ] + } + return self.MoveObj.create(move_vals) + + def check_report_journal_debit_credit( + self, report, expected_debit, expected_credit): + self.assertEqual( + expected_debit, + sum([journal.debit for journal in report.report_journal_ids]) + ) + + self.assertEqual( + expected_credit, + sum([journal.credit for journal in report.report_journal_ids]) + ) + + def check_report_journal_debit_credit_taxes( + self, report, + expected_base_debit, expected_base_credit, + expected_tax_debit, expected_tax_credit): + + self.assertEqual( + expected_base_debit, + sum([ + journal.base_debit + for journal in report.report_journal_tax_line_ids + ]) + ) + self.assertEqual( + expected_base_credit, + sum([ + journal.base_credit + for journal in report.report_journal_tax_line_ids + ]) + ) + self.assertEqual( + expected_tax_debit, + sum([ + journal.tax_debit + for journal in report.report_journal_tax_line_ids + ]) + ) + self.assertEqual( + expected_tax_credit, + sum([ + journal.tax_credit + for journal in report.report_journal_tax_line_ids + ]) + ) + + def test_01_test_total(self): + today_date = Date.today() + last_year_date = Date.to_string( + datetime.today() - relativedelta(years=1)) + + move1 = self._add_move( + today_date, self.journal_sale, + 0, 100, 100, 0) + move2 = self._add_move( + last_year_date, self.journal_sale, + 0, 100, 100, 0) + + report = self.ReportJournalQweb.create({ + 'date_from': self.fy_date_start, + 'date_to': self.fy_date_end, + 'company_id': self.company.id, + 'journal_ids': [(6, 0, [self.journal_sale.ids])] + }) + report.compute_data_for_report() + + self.check_report_journal_debit_credit(report, 100, 100) + + move3 = self._add_move( + today_date, self.journal_sale, + 0, 100, 100, 0) + + report.refresh() + self.check_report_journal_debit_credit(report, 200, 200) + + report.move_target = 'posted' + report.refresh() + self.check_report_journal_debit_credit(report, 0, 0) + + move1.post() + report.refresh() + self.check_report_journal_debit_credit(report, 100, 100) + + move2.post() + report.refresh() + self.check_report_journal_debit_credit(report, 100, 100) + + move3.post() + report.refresh() + self.check_report_journal_debit_credit(report, 200, 200) + + report.date_from = self.previous_fy_date_start + report.refresh() + self.check_report_journal_debit_credit(report, 300, 300) + + def test_02_test_taxes_out_invoice(self): + invoice_values = { + 'journal_id': self.journal_sale.id, + 'partner_id': self.partner_2.id, + 'type': 'out_invoice', + 'invoice_line_ids': [ + (0, 0, { + 'quantity': 1.0, + 'price_unit': 100, + 'account_id': self.receivable_account.id, + 'name': "Test", + 'invoice_line_tax_ids': [(6, 0, [self.tax_15_s.id])], + }), + (0, 0, { + 'quantity': 1.0, + 'price_unit': 100, + 'account_id': self.receivable_account.id, + 'name': "Test", + 'invoice_line_tax_ids': [(6, 0, [ + self.tax_15_s.id, self.tax_20_s.id + ])], + }) + ] + } + invoice = self.InvoiceObj.create(invoice_values) + invoice.action_invoice_open() + + report = self.ReportJournalQweb.create({ + 'date_from': self.fy_date_start, + 'date_to': self.fy_date_end, + 'company_id': self.company.id, + 'journal_ids': [(6, 0, [self.journal_sale.ids])] + }) + report.compute_data_for_report() + + self.check_report_journal_debit_credit(report, 250, 250) + self.check_report_journal_debit_credit_taxes(report, 0, 300, 0, 50) + + def test_03_test_taxes_in_invoice(self): + invoice_values = { + 'journal_id': self.journal_sale.id, + 'partner_id': self.partner_2.id, + 'type': 'in_invoice', + 'invoice_line_ids': [ + (0, 0, { + 'quantity': 1.0, + 'price_unit': 100, + 'account_id': self.payable_account.id, + 'name': "Test", + 'invoice_line_tax_ids': [(6, 0, [self.tax_15_p.id])], + }), + (0, 0, { + 'quantity': 1.0, + 'price_unit': 100, + 'account_id': self.payable_account.id, + 'name': "Test", + 'invoice_line_tax_ids': [(6, 0, [ + self.tax_15_p.id, self.tax_20_p.id + ])], + }) + ] + } + invoice = self.InvoiceObj.create(invoice_values) + invoice.action_invoice_open() + + report = self.ReportJournalQweb.create({ + 'date_from': self.fy_date_start, + 'date_to': self.fy_date_end, + 'company_id': self.company.id, + 'journal_ids': [(6, 0, [self.journal_sale.ids])] + }) + report.compute_data_for_report() + + self.check_report_journal_debit_credit(report, 250, 250) + self.check_report_journal_debit_credit_taxes(report, 300, 0, 50, 0) diff --git a/account_financial_report_qweb/wizard/__init__.py b/account_financial_report_qweb/wizard/__init__.py index f1b0812d..1f150b84 100644 --- a/account_financial_report_qweb/wizard/__init__.py +++ b/account_financial_report_qweb/wizard/__init__.py @@ -5,5 +5,6 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from . import aged_partner_balance_wizard from . import general_ledger_wizard +from . import journal_report_wizard from . import open_items_wizard from . import trial_balance_wizard diff --git a/account_financial_report_qweb/wizard/journal_report_wizard.py b/account_financial_report_qweb/wizard/journal_report_wizard.py new file mode 100644 index 00000000..e6e2e381 --- /dev/null +++ b/account_financial_report_qweb/wizard/journal_report_wizard.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models, _ + + +class JournalReportWizard(models.TransientModel): + + _name = 'journal.report.wizard' + + company_id = fields.Many2one( + comodel_name='res.company', + default=lambda self: self.env.user.company_id, + string='Company', + required=True, + ondelete='cascade', + ) + date_range_id = fields.Many2one( + comodel_name='date.range', + string='Date range', + domain="['|', " + "('company_id', '=', False)," + "('company_id', '=', company_id)]", + ) + date_from = fields.Date( + string="Start date", + required=True + ) + date_to = fields.Date( + string="End date", + required=True + ) + journal_ids = fields.Many2many( + comodel_name='account.journal', + string="Journals", + domain="[('company_id', '=', company_id)]", + required=True, + ) + move_target = fields.Selection( + selection='_get_move_targets', + default='all', + required=True, + ) + with_currency = fields.Boolean() + sort_option = fields.Selection( + selection='_get_sort_options', + string="Sort entries by", + default='move_name', + required=True, + ) + group_option = fields.Selection( + selection='_get_group_options', + string="Group entries by", + default='journal', + required=True, + ) + with_account_name = fields.Boolean( + default=False, + ) + + @api.model + def _get_move_targets(self): + return [ + ('all', _("All")), + ('posted', _("Posted")), + ('draft', _("Not Posted")) + ] + + @api.model + def _get_sort_options(self): + return [ + ('move_name', _("Entry number")), + ('date', _("Date")), + ] + + @api.model + def _get_group_options(self): + return [ + ('journal', _("Journal")), + ('none', _("No group")), + ] + + @api.onchange('date_range_id') + def onchange_date_range_id(self): + self.date_from = self.date_range_id.date_start + self.date_to = self.date_range_id.date_end + + @api.multi + def export_as_pdf(self): + self.ensure_one() + return self._export() + + @api.multi + def export_as_xlsx(self): + self.ensure_one() + return self._export(xlsx_report=True) + + @api.multi + def _prepare_report_journal(self): + self.ensure_one() + return { + 'date_from': self.date_from, + 'date_to': self.date_to, + 'move_target': self.move_target, + 'with_currency': self.with_currency, + 'company_id': self.company_id.id, + 'journal_ids': [(6, 0, self.journal_ids.ids)], + 'sort_option': self.sort_option, + 'group_option': self.group_option, + 'with_account_name': self.with_account_name, + } + + @api.multi + def _export(self, xlsx_report=False): + self.ensure_one() + model = self.env['report_journal_qweb'] + report = model.create(self._prepare_report_journal()) + return report.print_report(xlsx_report=xlsx_report) diff --git a/account_financial_report_qweb/wizard/journal_report_wizard.xml b/account_financial_report_qweb/wizard/journal_report_wizard.xml new file mode 100644 index 00000000..6cea7f31 --- /dev/null +++ b/account_financial_report_qweb/wizard/journal_report_wizard.xml @@ -0,0 +1,66 @@ + + + + + + + journal.report.wizard.form (in account_financial_report_qweb) + journal.report.wizard + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + Journal Ledger Report Wizard + journal.report.wizard + form + {} + new + + +