diff --git a/account_financial_report/report/general_ledger.py b/account_financial_report/report/general_ledger.py index d2d6876c56d..ddb5a7b09ea 100644 --- a/account_financial_report/report/general_ledger.py +++ b/account_financial_report/report/general_ledger.py @@ -444,6 +444,37 @@ def _prepare_ml_items(self, move_line, grouped_by): res.append({"id": 0, "name": ""}) return res + def _get_aggregation_key(self, move_line): + return ( + move_line["move_id"][0], + move_line["account_internal_type"], + ) + + def _aggregate_move_lines(self, aggregated_ml, move_line): + aggregated_ml["credit"] += move_line["credit"] + aggregated_ml["debit"] += move_line["debit"] + aggregated_ml["balance"] += move_line["balance"] + aggregated_ml["amount_currency"] += move_line["amount_currency"] + aggregated_ml["aggregated_ml_ids"].append(move_line["id"]) + return aggregated_ml + + def _get_aggregated_move_lines(self, move_lines): + payment_term_line_internal_type = ("receivable", "payable") + aggregated_move_lines = dict() + for move_line in move_lines: + move_line_id = move_line["id"] + if move_line["account_internal_type"] in payment_term_line_internal_type: + aggregation_key = self._get_aggregation_key(move_line) + aggregated_ml = aggregated_move_lines.get(aggregation_key) + if aggregated_ml is None: + aggregated_ml = aggregated_move_lines[aggregation_key] = move_line + aggregated_ml["aggregated_ml_ids"] = [move_line_id] + else: + aggregated_ml = self._aggregate_move_lines(aggregated_ml, move_line) + else: + aggregated_move_lines[move_line_id] = move_line + return list(aggregated_move_lines.values()) + def _get_period_ml_data( self, account_ids, @@ -457,6 +488,7 @@ def _get_period_ml_data( cost_center_ids, extra_domain, grouped_by, + aggregated_payment_term_lines, ): domain = self._get_period_domain( account_ids, @@ -473,6 +505,8 @@ def _get_period_ml_data( move_lines = self.env["account.move.line"].search_read( domain=domain, fields=ml_fields, order="date,move_name" ) + if aggregated_payment_term_lines: + move_lines = self._get_aggregated_move_lines(move_lines) journal_ids = set() full_reconcile_ids = set() taxes_ids = set() @@ -778,20 +812,22 @@ def _get_centralized_ml(self, account, date_to, grouped_by): # flake8: noqa: C901 def _get_report_values(self, docids, data): wizard_id = data["wizard_id"] - company = self.env["res.company"].browse(data["company_id"]) - company_id = data["company_id"] - date_to = data["date_to"] - date_from = data["date_from"] - partner_ids = data["partner_ids"] - account_ids = data["account_ids"] - cost_center_ids = data["cost_center_ids"] - grouped_by = data["grouped_by"] - hide_account_at_0 = data["hide_account_at_0"] - foreign_currency = data["foreign_currency"] - only_posted_moves = data["only_posted_moves"] - unaffected_earnings_account = data["unaffected_earnings_account"] - fy_start_date = data["fy_start_date"] - extra_domain = data["domain"] + wizard = self.env["general.ledger.report.wizard"].browse(wizard_id) + company_id = wizard.company_id.id + company = wizard.company_id + date_to = wizard.date_to + date_from = wizard.date_from + partner_ids = wizard.partner_ids.ids + account_ids = wizard.account_ids.ids + cost_center_ids = wizard.cost_center_ids.ids + grouped_by = wizard.grouped_by + aggregated_payment_term_lines = wizard.aggregated_payment_term_lines + hide_account_at_0 = wizard.hide_account_at_0 + foreign_currency = wizard.foreign_currency + only_posted_moves = wizard.target_move == "posted" + unaffected_earnings_account = wizard.unaffected_earnings_account.id + fy_start_date = wizard.fy_start_date + extra_domain = wizard._get_account_move_lines_domain() gen_ld_data = self._get_initial_balance_data( account_ids, partner_ids, @@ -826,6 +862,7 @@ def _get_report_values(self, docids, data): cost_center_ids, extra_domain, grouped_by, + aggregated_payment_term_lines, ) general_ledger = self._create_general_ledger( gen_ld_data, @@ -943,4 +980,5 @@ def _get_ml_fields(self): "tax_ids", "move_name", "matching_number", + "account_internal_type", ] diff --git a/account_financial_report/tests/test_general_ledger.py b/account_financial_report/tests/test_general_ledger.py index 808155bf7dd..9edde80199c 100644 --- a/account_financial_report/tests/test_general_ledger.py +++ b/account_financial_report/tests/test_general_ledger.py @@ -6,8 +6,10 @@ import time from datetime import date +from dateutil.relativedelta import relativedelta + from odoo import api, fields -from odoo.tests import tagged +from odoo.tests import Form, tagged from odoo.addons.account.tests.common import AccountTestInvoicingCommon @@ -731,3 +733,72 @@ def test_validate_date_range(self): wizard.onchange_date_range_id() self.assertEqual(wizard.date_from, date(2018, 1, 1)) self.assertEqual(wizard.date_to, date(2018, 12, 31)) + + def test_account_type_filter(self): + company_id = self.env.user.company_id + account_model = self.env["account.account"] + account = account_model.search([], limit=1) + account_type = account.user_type_id + accounts = account_model.search( + [ + ("company_id", "=", company_id.id), + ("user_type_id", "in", account_type.ids), + ] + ) + wizard = self.env["general.ledger.report.wizard"].create( + {"account_type_ids": account_type, "company_id": company_id.id} + ) + wizard.onchange_company_id() + self.assertEqual(wizard.account_ids, accounts) + + wizard.account_type_ids = False + wizard._onchange_account_type_ids() + self.assertEqual(wizard.account_ids, account_model) + + def test_aggregate_payment_term_lines(self): + """If the report is configured to aggregate payment term lines, + invoices having multiple maturity dates only show one line. + """ + # Arrange + day = relativedelta(days=1) + invoice = self.init_invoice( + "out_invoice", + amounts=[100], + ) + payment_term = self.env.ref("account.account_payment_term_advance_60days") + with Form(invoice) as invoice_form: + invoice_form.invoice_payment_term_id = payment_term + invoice.action_post() + receivable_lines = invoice.line_ids.filtered( + lambda line: line.account_internal_type == "receivable" + ) + wizard = self.env["general.ledger.report.wizard"].create( + { + "date_from": invoice.date - day, + "date_to": invoice.date + day, + "aggregated_payment_term_lines": True, + } + ) + wizard_data = wizard._prepare_report_general_ledger() + # pre-condition + self.assertGreater(len(payment_term.line_ids), 1) + self.assertGreater(len(receivable_lines), 1) + self.assertTrue(wizard.aggregated_payment_term_lines) + + # Act + report_data = self.env[ + "report.account_financial_report.general_ledger" + ]._get_report_values(wizard, wizard_data) + + # Assert + found_move_line = None + for account_data in report_data["general_ledger"]: + for grouped_data in account_data.get("list_grouped", []): + for move_line in grouped_data["move_lines"]: + if move_line["id"] in receivable_lines.ids: + if not found_move_line: + found_move_line = move_line + else: + self.fail("Invoice showing multiple lines") + + self.assertEqual(found_move_line["balance"], invoice.amount_total) diff --git a/account_financial_report/wizard/general_ledger_wizard.py b/account_financial_report/wizard/general_ledger_wizard.py index edce54c7bc2..9cbf5f725b0 100644 --- a/account_financial_report/wizard/general_ledger_wizard.py +++ b/account_financial_report/wizard/general_ledger_wizard.py @@ -88,6 +88,11 @@ class GeneralLedgerReportWizard(models.TransientModel): default=[], help="This domain will be used to select specific domain for Journal " "Items", ) + aggregated_payment_term_lines = fields.Boolean( + string="Aggregate payment term lines", + help="""Move lines that are generated by a payment term will be shown as one + line.""", + ) def _get_account_move_lines_domain(self): domain = literal_eval(self.domain) if self.domain else [] diff --git a/account_financial_report/wizard/general_ledger_wizard_view.xml b/account_financial_report/wizard/general_ledger_wizard_view.xml index c4d75719564..7d3baabc111 100644 --- a/account_financial_report/wizard/general_ledger_wizard_view.xml +++ b/account_financial_report/wizard/general_ledger_wizard_view.xml @@ -24,6 +24,7 @@ +