Merge PR #1396 into 18.0

Signed-off-by pedrobaeza
This commit is contained in:
OCA-git-bot
2025-10-02 19:34:31 +00:00
10 changed files with 438 additions and 93 deletions

View File

@@ -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 will be able to set dynamic intervals that will appear on the Aged
Partner Balance. For further information, check CONFIGURE.rst 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** **Table of contents**
.. contents:: .. contents::

View File

@@ -897,6 +897,7 @@ msgstr ""
#. module: account_financial_report #. 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_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 #: model:ir.model.fields,field_description:account_financial_report.field_trial_balance_report_wizard__grouped_by
msgid "Grouped By" msgid "Grouped By"
msgstr "" msgstr ""
@@ -1114,6 +1115,12 @@ msgstr ""
msgid "Missing Partner" msgid "Missing Partner"
msgstr "" 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 #. module: account_financial_report
#: model:ir.model,name:account_financial_report.model_account_age_report_configuration_line #: model:ir.model,name:account_financial_report.model_account_age_report_configuration_line
msgid "Model to set interval lines for Age partner balance report" msgid "Model to set interval lines for Age partner balance report"
@@ -1306,6 +1313,12 @@ msgstr ""
msgid "Partner Initial balance" msgid "Partner Initial balance"
msgstr "" 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 #. module: account_financial_report
#. odoo-python #. odoo-python
#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:0 #: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:0
@@ -1328,6 +1341,7 @@ msgstr ""
#. module: account_financial_report #. 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__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" msgid "Partners"
msgstr "" msgstr ""

View File

@@ -927,6 +927,7 @@ msgstr "Agrupar por"
#. module: account_financial_report #. 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_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 #: model:ir.model.fields,field_description:account_financial_report.field_trial_balance_report_wizard__grouped_by
msgid "Grouped By" msgid "Grouped By"
msgstr "Agrupado por" 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/open_items.py:0
#: code:addons/account_financial_report/report/trial_balance.py:0 #: code:addons/account_financial_report/report/trial_balance.py:0
msgid "Missing Partner" 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 #. module: account_financial_report
#: model:ir.model,name:account_financial_report.model_account_age_report_configuration_line #: model:ir.model,name:account_financial_report.model_account_age_report_configuration_line
@@ -1348,6 +1355,12 @@ msgstr ""
msgid "Partner Initial balance" msgid "Partner Initial balance"
msgstr "Saldo Inicial de empresa" 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 #. module: account_financial_report
#. odoo-python #. odoo-python
#: code:addons/account_financial_report/report/aged_partner_balance_xlsx.py:0 #: 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 #. 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__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" msgid "Partners"
msgstr "Empresas" msgstr "Empresas"

View File

@@ -1,5 +1,6 @@
# © 2016 Julien Coux (Camptocamp) # © 2016 Julien Coux (Camptocamp)
# Copyright 2020 ForgeFlow S.L. (https://www.forgeflow.com) # 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). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import operator import operator
@@ -66,6 +67,7 @@ class OpenItemsReport(models.AbstractModel):
only_posted_moves, only_posted_moves,
company_id, company_id,
date_from, date_from,
grouped_by,
): ):
domain = self._get_move_lines_domain_not_reconciled( domain = self._get_move_lines_domain_not_reconciled(
company_id, account_ids, partner_ids, only_posted_moves, date_from company_id, account_ids, partner_ids, only_posted_moves, date_from
@@ -75,7 +77,7 @@ class OpenItemsReport(models.AbstractModel):
domain=domain, fields=ml_fields domain=domain, fields=ml_fields
) )
journals_ids = set() journals_ids = set()
partners_ids = set() group_ids = set()
partners_data = {} partners_data = {}
if date_at_object < date.today(): if date_at_object < date.today():
( (
@@ -119,29 +121,27 @@ class OpenItemsReport(models.AbstractModel):
journals_ids.add(move_line["journal_id"][0]) journals_ids.add(move_line["journal_id"][0])
acc_id = move_line["account_id"][0] acc_id = move_line["account_id"][0]
# Partners data # Partners data
if move_line["partner_id"]: partner = self.env["res.partner"]
prt_id = move_line["partner_id"][0] if move_line.get("partner_id"):
prt_name = move_line["partner_id"][1] 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: else:
prt_id = 0 group_id = partner.id or 0
prt_name = _("Missing Partner") group_name = partner.name or _("Missing Partner")
if prt_id not in partners_ids: if group_id not in group_ids:
partners_data.update({prt_id: {"id": prt_id, "name": prt_name}}) partners_data.update({group_id: {"id": group_id, "name": group_name}})
partners_ids.add(prt_id) group_ids.add(group_id)
# Move line update # Move line update
original = 0
if not float_is_zero(move_line["credit"], precision_digits=2): if not float_is_zero(move_line["credit"], precision_digits=2):
original = move_line["credit"] * (-1) original = move_line["credit"] * (-1)
if not float_is_zero(move_line["debit"], precision_digits=2): else:
original = move_line["debit"] original = move_line["debit"]
if move_line["ref"] == move_line["name"]: if move_line["ref"] == move_line["name"]:
if move_line["ref"]: ref_label = move_line["ref"] or ""
ref_label = move_line["ref"]
else:
ref_label = ""
elif not move_line["ref"]: elif not move_line["ref"]:
ref_label = move_line["name"] ref_label = move_line["name"]
elif not move_line["name"]: elif not move_line["name"]:
@@ -155,8 +155,8 @@ class OpenItemsReport(models.AbstractModel):
"date_maturity": move_line["date_maturity"] "date_maturity": move_line["date_maturity"]
and move_line["date_maturity"].strftime("%d/%m/%Y"), and move_line["date_maturity"].strftime("%d/%m/%Y"),
"original": original, "original": original,
"partner_id": prt_id, "partner_id": partner.id or 0,
"partner_name": prt_name, "partner_name": partner.name or "",
"ref_label": ref_label, "ref_label": ref_label,
"journal_id": move_line["journal_id"][0], "journal_id": move_line["journal_id"][0],
"move_name": move_line["move_id"][1], "move_name": move_line["move_id"][1],
@@ -172,12 +172,12 @@ class OpenItemsReport(models.AbstractModel):
# Open Items Move Lines Data # Open Items Move Lines Data
if acc_id not in open_items_move_lines_data.keys(): 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: else:
if prt_id not in open_items_move_lines_data[acc_id].keys(): if group_id not in open_items_move_lines_data[acc_id].keys():
open_items_move_lines_data[acc_id][prt_id] = [move_line] open_items_move_lines_data[acc_id][group_id] = [move_line]
else: 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)) journals_data = self._get_journals_data(list(journals_ids))
accounts_data = self._get_accounts_data(open_items_move_lines_data.keys()) accounts_data = self._get_accounts_data(open_items_move_lines_data.keys())
return ( return (
@@ -236,7 +236,9 @@ class OpenItemsReport(models.AbstractModel):
move_lines = [] move_lines = []
for move_line in open_items_move_lines_data[acc_id][prt_id]: for move_line in open_items_move_lines_data[acc_id][prt_id]:
move_lines += [move_line] 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 new_open_items[acc_id][prt_id] = move_lines
return new_open_items return new_open_items
@@ -251,7 +253,7 @@ class OpenItemsReport(models.AbstractModel):
date_from = data["date_from"] date_from = data["date_from"]
only_posted_moves = data["only_posted_moves"] only_posted_moves = data["only_posted_moves"]
show_partner_details = data["show_partner_details"] show_partner_details = data["show_partner_details"]
grouped_by = data["grouped_by"]
( (
move_lines_data, move_lines_data,
partners_data, partners_data,
@@ -265,6 +267,7 @@ class OpenItemsReport(models.AbstractModel):
only_posted_moves, only_posted_moves,
company_id, company_id,
date_from, date_from,
grouped_by,
) )
total_amount = self._calculate_amounts(open_items_move_lines_data) total_amount = self._calculate_amounts(open_items_move_lines_data)
@@ -290,6 +293,7 @@ class OpenItemsReport(models.AbstractModel):
"accounts_data": accounts_data, "accounts_data": accounts_data,
"total_amount": total_amount, "total_amount": total_amount,
"Open_Items": open_items_move_lines_data, "Open_Items": open_items_move_lines_data,
"grouped_by": grouped_by,
} }
def _get_ml_fields(self): def _get_ml_fields(self):

View File

@@ -101,11 +101,126 @@ class OpenItemsXslx(models.AbstractModel):
def _get_col_pos_final_balance_label(self): def _get_col_pos_final_balance_label(self):
return 5 return 5
def _generate_report_content(self, workbook, report, data, report_data): def _calculate_amounts_by_partner(self, account_id, open_items_move_lines_data):
res_data = self.env[ total_amount = {}
"report.account_financial_report.open_items" for line in open_items_move_lines_data:
]._get_report_values(report, data) partner_id_key = line["partner_id"]
# For each account 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"] Open_items = res_data["Open_Items"]
accounts_data = res_data["accounts_data"] accounts_data = res_data["accounts_data"]
partners_data = res_data["partners_data"] partners_data = res_data["partners_data"]
@@ -120,7 +235,6 @@ class OpenItemsXslx(models.AbstractModel):
+ accounts_data[account_id]["name"], + accounts_data[account_id]["name"],
report_data, report_data,
) )
# For each partner # For each partner
if Open_items[account_id]: if Open_items[account_id]:
if show_partner_details: if show_partner_details:
@@ -180,18 +294,33 @@ class OpenItemsXslx(models.AbstractModel):
) )
self.write_line_from_dict(line, report_data) self.write_line_from_dict(line, report_data)
# Display ending balance line for account # Display ending balance line for account
type_object = "account" type_object = "account"
self.write_ending_balance_from_dict( self.write_ending_balance_from_dict(
accounts_data[account_id], accounts_data[account_id],
type_object, type_object,
total_amount, total_amount,
report_data, report_data,
account_id=account_id, account_id=account_id,
) )
# 2 lines break # 2 lines break
report_data["row_pos"] += 2 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( def write_ending_balance_from_dict(
self, self,
@@ -211,6 +340,10 @@ class OpenItemsXslx(models.AbstractModel):
name = my_object["code"] + " - " + my_object["name"] name = my_object["code"] + " - " + my_object["name"]
my_object["residual"] = total_amount[account_id]["residual"] my_object["residual"] = total_amount[account_id]["residual"]
label = _("Ending balance") 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( return super().write_ending_balance_from_dict(
my_object, name, label, report_data my_object, name, label, report_data
) )

View File

@@ -9,6 +9,7 @@
</t> </t>
</t> </t>
</template> </template>
<template id="account_financial_report.report_open_items_base"> <template id="account_financial_report.report_open_items_base">
<!-- Saved flag fields into variables, used to define columns display --> <!-- Saved flag fields into variables, used to define columns display -->
<t t-set="foreign_currency" t-value="foreign_currency" /> <t t-set="foreign_currency" t-value="foreign_currency" />
@@ -28,51 +29,87 @@
style="text-align: center;" style="text-align: center;"
/> />
</div> </div>
<!-- Display filters --> <t t-if="grouped_by == 'salesperson' and show_partner_details">
<t t-call="account_financial_report.report_open_items_filters" /> <t t-foreach="partners_data.keys()" t-as="partner_id">
<t t-foreach="Open_Items.keys()" t-as="account_id"> <t t-call="account_financial_report.report_open_items_filters" />
<!-- Display account header --> <div class="act_as_caption account_title">
<div class="act_as_table list_table" style="margin-top: 10px;" /> <span t-esc="partners_data[partner_id]['name']" />
<div class="account_title" style="width: 100%;">
<span t-esc="accounts_data[account_id]['code']" />
-
<span t-esc="accounts_data[account_id]['name']" />
</div>
<t t-if="not show_partner_details">
<div class="act_as_table data_table" style="width: 100%;">
<t
t-call="account_financial_report.report_open_items_lines_header"
/>
<!-- Display account move lines -->
<t t-foreach="Open_Items[account_id]" t-as="line">
<t
t-call="account_financial_report.report_open_items_lines"
/>
</t>
</div> </div>
</t> <t t-foreach="Open_Items.keys()" t-as="account_id">
<t t-if="show_partner_details"> <t t-if="partner_id in Open_Items[account_id]">
<div class="page_break"> <div
<!-- Display account partners --> class="act_as_table list_table"
<t t-foreach="Open_Items[account_id]" t-as="partner_id"> style="margin-top: 10px;"
<div class="act_as_caption account_title"> />
<span t-esc="partners_data[partner_id]['name']" /> <div class="account_title" style="width: 100%;">
<span t-esc="accounts_data[account_id]['code']" />
-
<span t-esc="accounts_data[account_id]['name']" />
</div> </div>
<div class="act_as_table data_table" style="width: 100%;">
<!-- Display partner header --> <t t-if="Open_Items[account_id]">
<t <t
t-call="account_financial_report.report_open_items_lines_header" t-set="partner_totals"
t-value="o._calculate_amounts_by_partner(account_id,Open_Items[account_id][partner_id])"
/> />
<!-- Display partner move lines -->
<t <t
t-foreach="Open_Items[account_id][partner_id]" t-foreach="partner_totals.get(account_id, {})"
t-as="line" t-as="partner_id_key"
> >
<t <t t-set="has_lines" t-value="False" />
t-call="account_financial_report.report_open_items_lines" <div
/> class="act_as_table data_table"
style="width: 100%;"
>
<t
t-foreach="Open_Items[account_id][partner_id]"
t-as="line"
>
<t
t-if="line['partner_id'] == partner_id_key"
>
<t t-set="has_lines" t-value="True" />
</t>
</t>
<t t-if="has_lines">
<!-- Display partner header -->
<t
t-call="account_financial_report.report_open_items_lines_header"
/>
</t>
<t
t-foreach="Open_Items[account_id][partner_id]"
t-as="line"
>
<t
t-if="line['partner_id'] == partner_id_key"
>
<!-- Display partner move lines -->
<t
t-call="account_financial_report.report_open_items_lines"
/>
</t>
</t>
</div>
<!-- Check if there were any lines displayed for the partner -->
<t t-if="has_lines">
<!-- Calculate and display subtotal for current partner_id -->
<t
t-call="account_financial_report.report_open_items_ending_cumul"
>
<t
t-set="currency_id"
t-value="accounts_data[account_id]['currency_name']"
/>
<t
t-set="type"
t-value="'partner_subtotal_type'"
/>
</t>
</t>
</t> </t>
</div> </t>
<!-- Display account footer -->
<t <t
t-call="account_financial_report.report_open_items_ending_cumul" t-call="account_financial_report.report_open_items_ending_cumul"
> >
@@ -87,23 +124,92 @@
<t t-set="type" t-value='"partner_type"' /> <t t-set="type" t-value='"partner_type"' />
</t> </t>
</t> </t>
</div> </t>
<div style="page-break-after: always;" />
</t> </t>
<!-- Display account footer --> </t>
<t t-call="account_financial_report.report_open_items_ending_cumul"> <t t-else="">
<t <!-- Display filters -->
t-set="account_or_partner_id" <t t-call="account_financial_report.report_open_items_filters" />
t-value="accounts_data[account_id]" <t t-foreach="Open_Items.keys()" t-as="account_id">
/> <!-- Display account header -->
<t <div class="act_as_table list_table" style="margin-top: 10px;" />
t-set="currency_id" <div class="account_title" style="width: 100%;">
t-value="accounts_data[account_id]['currency_name']" <span t-esc="accounts_data[account_id]['code']" />
/> -
<t t-set="type" t-value='"account_type"' /> <span t-esc="accounts_data[account_id]['name']" />
</div>
<t t-if="not show_partner_details">
<div class="act_as_table data_table" style="width: 100%;">
<t
t-call="account_financial_report.report_open_items_lines_header"
/>
<!-- Display account move lines -->
<t t-foreach="Open_Items[account_id]" t-as="line">
<t
t-call="account_financial_report.report_open_items_lines"
/>
</t>
</div>
</t>
<t t-if="show_partner_details">
<div class="page_break">
<!-- Display account partners -->
<t t-foreach="Open_Items[account_id]" t-as="partner_id">
<div class="act_as_caption account_title">
<span t-esc="partners_data[partner_id]['name']" />
</div>
<div
class="act_as_table data_table"
style="width: 100%;"
>
<!-- Display partner header -->
<t
t-call="account_financial_report.report_open_items_lines_header"
/>
<!-- Display partner move lines -->
<t
t-foreach="Open_Items[account_id][partner_id]"
t-as="line"
>
<t
t-call="account_financial_report.report_open_items_lines"
/>
</t>
</div>
<t
t-call="account_financial_report.report_open_items_ending_cumul"
>
<t
t-set="account_or_partner_id"
t-value="partners_data[partner_id]"
/>
<t
t-set="currency_id"
t-value="accounts_data[account_id]['currency_name']"
/>
<t t-set="type" t-value='"partner_type"' />
</t>
</t>
</div>
</t>
<!-- Display account footer -->
<t t-call="account_financial_report.report_open_items_ending_cumul">
<t
t-set="account_or_partner_id"
t-value="accounts_data[account_id]"
/>
<t
t-set="currency_id"
t-value="accounts_data[account_id]['currency_name']"
/>
<t t-set="type" t-value='"account_type"' />
</t>
</t> </t>
</t> </t>
</div> </div>
</template> </template>
<template id="account_financial_report.report_open_items_filters"> <template id="account_financial_report.report_open_items_filters">
<div class="act_as_table data_table" style="width: 100%;"> <div class="act_as_table data_table" style="width: 100%;">
<div class="act_as_row labels"> <div class="act_as_row labels">
@@ -294,6 +400,20 @@
Partner ending balance Partner ending balance
</div> </div>
</t> </t>
<t t-if='type == "partner_subtotal_type"'>
<div class="act_as_cell first_column" style="width: 36.34%;" />
<t
t-set="partner"
t-value="env['res.partner'].browse(partner_id_key)"
/>
<t t-if="partner">
<span t-esc="partner.name" />
</t>
<div class="act_as_cell right" style="width: 28.66%;">
Ending
balance
</div>
</t>
<!--## date_due--> <!--## date_due-->
<div class="act_as_cell" style="width: 6.47%;" /> <div class="act_as_cell" style="width: 6.47%;" />
<!--## amount_total_due--> <!--## amount_total_due-->
@@ -312,6 +432,12 @@
t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"
/> />
</t> </t>
<t t-if='type == "partner_subtotal_type"'>
<span
t-esc="partner_totals[account_id][partner_id_key]['residual']"
t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"
/>
</t>
</div> </div>
<!--## amount_total_due_currency + amount_residual_currency --> <!--## amount_total_due_currency + amount_residual_currency -->
<t t-if="foreign_currency"> <t t-if="foreign_currency">

View File

@@ -394,6 +394,8 @@ foreign currency balances are not available.</p>
<p>Invoicing / Settings / Invoicing / OCA Aged Report Configuration you <p>Invoicing / Settings / Invoicing / OCA Aged Report Configuration you
will be able to set dynamic intervals that will appear on the Aged will be able to set dynamic intervals that will appear on the Aged
Partner Balance. For further information, check CONFIGURE.rst</p> Partner Balance. For further information, check CONFIGURE.rst</p>
<p>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.</p>
<p><strong>Table of contents</strong></p> <p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents"> <div class="contents local topic" id="contents">
<ul class="simple"> <ul class="simple">

View File

@@ -1,7 +1,9 @@
# Author: Julien Coux # Author: Julien Coux
# Copyright 2016 Camptocamp SA # Copyright 2016 Camptocamp SA
# Copyright 2024 Tecnativa - Carolina Fernandez
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo.fields import Date
from odoo.tests import tagged from odoo.tests import tagged
from odoo.addons.account.tests.common import AccountTestInvoicingCommon from odoo.addons.account.tests.common import AccountTestInvoicingCommon
@@ -22,6 +24,14 @@ class TestOpenItems(AccountTestInvoicingCommon):
tracking_disable=True, tracking_disable=True,
) )
) )
cls.account001 = cls.env["account.account"].create(
{
"code": "001",
"name": "Account 001",
"account_type": "income_other",
"reconcile": True,
}
)
def test_partner_filter(self): def test_partner_filter(self):
partner_1 = self.env.ref("base.res_partner_1") partner_1 = self.env.ref("base.res_partner_1")
@@ -39,3 +49,23 @@ class TestOpenItems(AccountTestInvoicingCommon):
wizard = self.env["open.items.report.wizard"].with_context(**context) wizard = self.env["open.items.report.wizard"].with_context(**context)
self.assertEqual(wizard._default_partners(), expected_list) self.assertEqual(wizard._default_partners(), expected_list)
def test_open_items_grouped_by(self):
open_item_wizard = self.env["open.items.report.wizard"]
all_accounts = self.env["account.account"].search(
[
("reconcile", "=", True),
],
order="code",
)
wizard = open_item_wizard.create(
{
"date_at": Date.today(),
"account_code_from": self.account001.id,
"account_code_to": all_accounts[-1].id,
"grouped_by": "salesperson",
}
)
wizard.on_change_account_range()
res = wizard._prepare_report_open_items()
self.assertEqual(res["grouped_by"], wizard.grouped_by)

View File

@@ -59,6 +59,10 @@ class OpenItemsReportWizard(models.TransientModel):
comodel_name="account.account", comodel_name="account.account",
help="Ending account in a range", help="Ending account in a range",
) )
grouped_by = fields.Selection(
selection=[("partners", "Partners"), ("salesperson", "Partner Salesperson")],
default="partners",
)
@api.onchange("account_code_from", "account_code_to") @api.onchange("account_code_from", "account_code_to")
def on_change_account_range(self): def on_change_account_range(self):
@@ -134,6 +138,19 @@ class OpenItemsReportWizard(models.TransientModel):
else: else:
self.account_ids = None self.account_ids = None
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 _print_report(self, report_type): def _print_report(self, report_type):
self.ensure_one() self.ensure_one()
data = self._prepare_report_open_items() data = self._prepare_report_open_items()
@@ -165,6 +182,7 @@ class OpenItemsReportWizard(models.TransientModel):
"account_ids": self.account_ids.ids, "account_ids": self.account_ids.ids,
"partner_ids": self.partner_ids.ids or [], "partner_ids": self.partner_ids.ids or [],
"account_financial_report_lang": self.env.lang, "account_financial_report_lang": self.env.lang,
"grouped_by": self.grouped_by,
} }
def _export(self, report_type): def _export(self, report_type):

View File

@@ -21,6 +21,7 @@
<group name="other_filters"> <group name="other_filters">
<field name="target_move" widget="radio" /> <field name="target_move" widget="radio" />
<field name="show_partner_details" /> <field name="show_partner_details" />
<field name="grouped_by" />
<field name="hide_account_at_0" /> <field name="hide_account_at_0" />
<field name="foreign_currency" /> <field name="foreign_currency" />
</group> </group>