diff --git a/account_financial_report/README.rst b/account_financial_report/README.rst index 5c5900c0..4c057f4e 100644 --- a/account_financial_report/README.rst +++ b/account_financial_report/README.rst @@ -54,6 +54,9 @@ Invoicing / Settings / Invoicing / OCA Aged Report Configuration you will be able to set dynamic intervals that will appear on the Aged Partner Balance. For further information, check CONFIGURE.rst +Add new grouped by field to export Open items report grouped by partner salesperson. +If grouped by is empty or selected partner option it will grouped by Open items by partner. + **Table of contents** .. contents:: diff --git a/account_financial_report/i18n/account_financial_report.pot b/account_financial_report/i18n/account_financial_report.pot index edf6242d..cf093964 100644 --- a/account_financial_report/i18n/account_financial_report.pot +++ b/account_financial_report/i18n/account_financial_report.pot @@ -897,6 +897,7 @@ msgstr "" #. module: account_financial_report #: model:ir.model.fields,field_description:account_financial_report.field_general_ledger_report_wizard__grouped_by +#: model:ir.model.fields,field_description:account_financial_report.field_open_items_report_wizard__grouped_by #: model:ir.model.fields,field_description:account_financial_report.field_trial_balance_report_wizard__grouped_by msgid "Grouped By" msgstr "" @@ -1114,6 +1115,12 @@ msgstr "" msgid "Missing Partner" msgstr "" +#. module: account_financial_report +#: code:addons/account_financial_report/report/open_items.py:0 +#, python-format +msgid "Missing Salesperson" +msgstr "" + #. module: account_financial_report #: model:ir.model,name:account_financial_report.model_account_age_report_configuration_line msgid "Model to set interval lines for Age partner balance report" @@ -1306,6 +1313,12 @@ msgstr "" msgid "Partner Initial balance" msgstr "" +#. module: account_financial_report +#. odoo-python +#: model:ir.model.fields.selection,name:account_financial_report.selection__open_items_report_wizard__grouped_by__salesperson +msgid "Partner Salesperson" +msgstr "" + #. module: account_financial_report #. odoo-python #: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:0 @@ -1328,6 +1341,7 @@ msgstr "" #. module: account_financial_report #: model:ir.model.fields.selection,name:account_financial_report.selection__general_ledger_report_wizard__grouped_by__partners +#: model:ir.model.fields.selection,name:account_financial_report.selection__open_items_report_wizard__grouped_by__partners msgid "Partners" msgstr "" diff --git a/account_financial_report/i18n/es.po b/account_financial_report/i18n/es.po index 84c0c9b0..0679d6bc 100644 --- a/account_financial_report/i18n/es.po +++ b/account_financial_report/i18n/es.po @@ -927,6 +927,7 @@ msgstr "Agrupar por" #. module: account_financial_report #: model:ir.model.fields,field_description:account_financial_report.field_general_ledger_report_wizard__grouped_by +#: model:ir.model.fields,field_description:account_financial_report.field_open_items_report_wizard__grouped_by #: model:ir.model.fields,field_description:account_financial_report.field_trial_balance_report_wizard__grouped_by msgid "Grouped By" msgstr "Agrupado por" @@ -1150,7 +1151,13 @@ msgstr "Línea" #: code:addons/account_financial_report/report/open_items.py:0 #: code:addons/account_financial_report/report/trial_balance.py:0 msgid "Missing Partner" -msgstr "Falta el Socio" +msgstr "Falta la empresa" + +#. module: account_financial_report +#: code:addons/account_financial_report/report/open_items.py:0 +#, python-format +msgid "Missing Salesperson" +msgstr "Sin comercial" #. module: account_financial_report #: model:ir.model,name:account_financial_report.model_account_age_report_configuration_line @@ -1348,6 +1355,12 @@ msgstr "" msgid "Partner Initial balance" msgstr "Saldo Inicial de empresa" +#. module: account_financial_report +#. odoo-python +#: model:ir.model.fields.selection,name:account_financial_report.selection__open_items_report_wizard__grouped_by__salesperson +msgid "Partner Salesperson" +msgstr "Comercial de la empresa" + #. module: account_financial_report #. odoo-python #: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:0 @@ -1370,6 +1383,7 @@ msgstr "Saldo inicial de empresa" #. module: account_financial_report #: model:ir.model.fields.selection,name:account_financial_report.selection__general_ledger_report_wizard__grouped_by__partners +#: model:ir.model.fields.selection,name:account_financial_report.selection__open_items_report_wizard__grouped_by__partners msgid "Partners" msgstr "Empresas" diff --git a/account_financial_report/report/open_items.py b/account_financial_report/report/open_items.py index ae05d32f..20cb002e 100644 --- a/account_financial_report/report/open_items.py +++ b/account_financial_report/report/open_items.py @@ -1,5 +1,6 @@ # © 2016 Julien Coux (Camptocamp) # Copyright 2020 ForgeFlow S.L. (https://www.forgeflow.com) +# Copyright 2024 Tecnativa - Carolina Fernandez # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import operator @@ -66,6 +67,7 @@ class OpenItemsReport(models.AbstractModel): only_posted_moves, company_id, date_from, + grouped_by, ): domain = self._get_move_lines_domain_not_reconciled( company_id, account_ids, partner_ids, only_posted_moves, date_from @@ -75,7 +77,7 @@ class OpenItemsReport(models.AbstractModel): domain=domain, fields=ml_fields ) journals_ids = set() - partners_ids = set() + group_ids = set() partners_data = {} if date_at_object < date.today(): ( @@ -119,29 +121,27 @@ class OpenItemsReport(models.AbstractModel): journals_ids.add(move_line["journal_id"][0]) acc_id = move_line["account_id"][0] # Partners data - if move_line["partner_id"]: - prt_id = move_line["partner_id"][0] - prt_name = move_line["partner_id"][1] + partner = self.env["res.partner"] + if move_line.get("partner_id"): + partner = self.env["res.partner"].browse(move_line["partner_id"][0]) + if grouped_by == "salesperson": + user = partner.user_id + group_id = user.id or 0 + group_name = user.name or _("Missing Salesperson") else: - prt_id = 0 - prt_name = _("Missing Partner") - if prt_id not in partners_ids: - partners_data.update({prt_id: {"id": prt_id, "name": prt_name}}) - partners_ids.add(prt_id) - + group_id = partner.id or 0 + group_name = partner.name or _("Missing Partner") + if group_id not in group_ids: + partners_data.update({group_id: {"id": group_id, "name": group_name}}) + group_ids.add(group_id) # Move line update - original = 0 - if not float_is_zero(move_line["credit"], precision_digits=2): original = move_line["credit"] * (-1) - if not float_is_zero(move_line["debit"], precision_digits=2): + else: original = move_line["debit"] if move_line["ref"] == move_line["name"]: - if move_line["ref"]: - ref_label = move_line["ref"] - else: - ref_label = "" + ref_label = move_line["ref"] or "" elif not move_line["ref"]: ref_label = move_line["name"] elif not move_line["name"]: @@ -155,8 +155,8 @@ class OpenItemsReport(models.AbstractModel): "date_maturity": move_line["date_maturity"] and move_line["date_maturity"].strftime("%d/%m/%Y"), "original": original, - "partner_id": prt_id, - "partner_name": prt_name, + "partner_id": partner.id or 0, + "partner_name": partner.name or "", "ref_label": ref_label, "journal_id": move_line["journal_id"][0], "move_name": move_line["move_id"][1], @@ -172,12 +172,12 @@ class OpenItemsReport(models.AbstractModel): # Open Items Move Lines Data if acc_id not in open_items_move_lines_data.keys(): - open_items_move_lines_data[acc_id] = {prt_id: [move_line]} + open_items_move_lines_data[acc_id] = {group_id: [move_line]} else: - if prt_id not in open_items_move_lines_data[acc_id].keys(): - open_items_move_lines_data[acc_id][prt_id] = [move_line] + if group_id not in open_items_move_lines_data[acc_id].keys(): + open_items_move_lines_data[acc_id][group_id] = [move_line] else: - open_items_move_lines_data[acc_id][prt_id].append(move_line) + open_items_move_lines_data[acc_id][group_id].append(move_line) journals_data = self._get_journals_data(list(journals_ids)) accounts_data = self._get_accounts_data(open_items_move_lines_data.keys()) return ( @@ -236,7 +236,9 @@ class OpenItemsReport(models.AbstractModel): move_lines = [] for move_line in open_items_move_lines_data[acc_id][prt_id]: move_lines += [move_line] - move_lines = sorted(move_lines, key=lambda k: (k["date"])) + move_lines = sorted( + move_lines, key=lambda k: (k["date"], k["partner_id"]) + ) new_open_items[acc_id][prt_id] = move_lines return new_open_items @@ -251,7 +253,7 @@ class OpenItemsReport(models.AbstractModel): date_from = data["date_from"] only_posted_moves = data["only_posted_moves"] show_partner_details = data["show_partner_details"] - + grouped_by = data["grouped_by"] ( move_lines_data, partners_data, @@ -265,6 +267,7 @@ class OpenItemsReport(models.AbstractModel): only_posted_moves, company_id, date_from, + grouped_by, ) total_amount = self._calculate_amounts(open_items_move_lines_data) @@ -290,6 +293,7 @@ class OpenItemsReport(models.AbstractModel): "accounts_data": accounts_data, "total_amount": total_amount, "Open_Items": open_items_move_lines_data, + "grouped_by": grouped_by, } def _get_ml_fields(self): diff --git a/account_financial_report/report/open_items_xlsx.py b/account_financial_report/report/open_items_xlsx.py index 2f7d7a49..cf38673b 100644 --- a/account_financial_report/report/open_items_xlsx.py +++ b/account_financial_report/report/open_items_xlsx.py @@ -101,11 +101,126 @@ class OpenItemsXslx(models.AbstractModel): def _get_col_pos_final_balance_label(self): return 5 - def _generate_report_content(self, workbook, report, data, report_data): - res_data = self.env[ - "report.account_financial_report.open_items" - ]._get_report_values(report, data) - # For each account + def _calculate_amounts_by_partner(self, account_id, open_items_move_lines_data): + total_amount = {} + for line in open_items_move_lines_data: + partner_id_key = line["partner_id"] + if account_id not in total_amount: + total_amount[account_id] = {} + if partner_id_key not in total_amount[account_id]: + total_amount[account_id][partner_id_key] = {"residual": 0.0} + total_amount[account_id][partner_id_key]["residual"] += line[ + "amount_residual" + ] + return total_amount + + def _generate_report_content_by_salesperson( + self, workbook, report, data, report_data, res_data + ): + Open_items = res_data["Open_Items"] + accounts_data = res_data["accounts_data"] + partners_data = res_data["partners_data"] + journals_data = res_data["journals_data"] + total_amount = res_data["total_amount"] + + for partner_id in partners_data.keys(): + # Create a new sheet for each partner + partner_totals = {} + partner_name = partners_data[partner_id]["name"] + new_sheet = workbook.add_worksheet(partner_name[:31]) + report_data["sheet"] = new_sheet + report_data["row_pos"] = 0 + + for account_id in Open_items.keys(): + if partner_id in Open_items[account_id]: + self.write_array_title( + accounts_data[account_id]["code"] + + " - " + + accounts_data[account_id]["name"], + report_data, + ) + + # For each partner + if Open_items[account_id]: + type_object = "partner" + # Write partner title + self.write_array_title( + partners_data[partner_id]["name"], report_data + ) + + # Calculate totals by partner_id + partner_totals = self._calculate_amounts_by_partner( + account_id, Open_items[account_id][partner_id] + ) + # Display array header for move lines + self.write_array_header(report_data) + # Display account move lines + has_lines = False + for partner_id_key, total_amount_dict in partner_totals.get( + account_id, {} + ).items(): + for line in Open_items[account_id][partner_id]: + if line["partner_id"] == partner_id_key: + line.update( + { + "account": accounts_data[account_id][ + "code" + ], + "journal": journals_data[ + line["journal_id"] + ]["code"], + } + ) + self.write_line_from_dict(line, report_data) + has_lines = True + if has_lines: + partner = self.env["res.partner"].browse(partner_id_key) + # Display ending balance line for partner + partner_data = { + "id": partner_id_key, + "name": partner.name + if partner + else _("Missing Partner"), + "currency_id": accounts_data[account_id][ + "currency_id" + ], + "currency_name": accounts_data[account_id][ + "currency_name" + ], + "residual": total_amount_dict, + } + self.write_ending_balance_from_dict( + partner_data, + "partner_subtotal", + partner_totals, + report_data, + account_id=account_id, + partner_id=partner_id_key, + ) + has_lines = False + # Display ending balance line for salesperson + partners_data[partner_id].update( + { + "currency_id": accounts_data[account_id]["currency_id"], + "currency_name": accounts_data[account_id][ + "currency_name" + ], + } + ) + self.write_ending_balance_from_dict( + partners_data[partner_id], + type_object, + total_amount, + report_data, + account_id=account_id, + partner_id=partner_id, + ) + # Line break + report_data["row_pos"] += 1 + + def _generate_report_content_by_partner( + self, workbook, report, data, report_data, res_data + ): Open_items = res_data["Open_Items"] accounts_data = res_data["accounts_data"] partners_data = res_data["partners_data"] @@ -120,7 +235,6 @@ class OpenItemsXslx(models.AbstractModel): + accounts_data[account_id]["name"], report_data, ) - # For each partner if Open_items[account_id]: if show_partner_details: @@ -180,18 +294,33 @@ class OpenItemsXslx(models.AbstractModel): ) self.write_line_from_dict(line, report_data) - # Display ending balance line for account - type_object = "account" - self.write_ending_balance_from_dict( - accounts_data[account_id], - type_object, - total_amount, - report_data, - account_id=account_id, - ) + # Display ending balance line for account + type_object = "account" + self.write_ending_balance_from_dict( + accounts_data[account_id], + type_object, + total_amount, + report_data, + account_id=account_id, + ) - # 2 lines break - report_data["row_pos"] += 2 + # 2 lines break + report_data["row_pos"] += 2 + + def _generate_report_content(self, workbook, report, data, report_data): + res_data = self.env[ + "report.account_financial_report.open_items" + ]._get_report_values(report, data) + show_partner_details = res_data["show_partner_details"] + grouped_by = res_data["grouped_by"] + if grouped_by == "salesperson" and show_partner_details: + return self._generate_report_content_by_salesperson( + workbook, report, data, report_data, res_data + ) + else: + return self._generate_report_content_by_partner( + workbook, report, data, report_data, res_data + ) def write_ending_balance_from_dict( self, @@ -211,6 +340,10 @@ class OpenItemsXslx(models.AbstractModel): name = my_object["code"] + " - " + my_object["name"] my_object["residual"] = total_amount[account_id]["residual"] label = _("Ending balance") + elif type_object == "partner_subtotal": + name = my_object["name"] + my_object["residual"] = total_amount[account_id][partner_id]["residual"] + label = _("Ending balance") return super().write_ending_balance_from_dict( my_object, name, label, report_data ) diff --git a/account_financial_report/report/templates/open_items.xml b/account_financial_report/report/templates/open_items.xml index 1e410464..c58fd3e3 100644 --- a/account_financial_report/report/templates/open_items.xml +++ b/account_financial_report/report/templates/open_items.xml @@ -9,6 +9,7 @@ + +