[IMP] account_financial_report: analytic info + filters in the

open items and aged partner balance reports.

This is useful in project based companies where it is important
the assets & liabilities by projects. Long term projects my have
outstanding invoices for months.
pull/1138/head
AaronHForgeFlow 2024-03-25 16:49:27 +01:00
parent f37d54516d
commit 85ead5cd59
10 changed files with 272 additions and 49 deletions

View File

@ -6,6 +6,7 @@ import operator
from datetime import date, datetime, timedelta
from odoo import api, models
from odoo.osv import expression
from odoo.tools import float_is_zero
@ -148,10 +149,38 @@ class AgedPartnerBalanceReport(models.AbstractModel):
date_from,
only_posted_moves,
show_move_line_details,
analytic_account_ids,
no_analytic,
):
domain = self._get_move_lines_domain_not_reconciled(
company_id, account_ids, partner_ids, only_posted_moves, date_from
)
if no_analytic:
domain = expression.AND(
[
domain,
[
(
"analytic_account_id",
"=",
False,
)
],
]
)
elif analytic_account_ids:
domain = expression.AND(
[
domain,
[
(
"analytic_account_id",
"in",
analytic_account_ids,
)
],
]
)
ml_fields = self._get_ml_fields()
line_model = self.env["account.move.line"]
move_lines = line_model.search_read(domain=domain, fields=ml_fields)
@ -224,6 +253,10 @@ class AgedPartnerBalanceReport(models.AbstractModel):
ref_label = move_line["ref"]
else:
ref_label = move_line["ref"] + str(" - ") + move_line["name"]
if move_line["analytic_account_id"]:
analytic = move_line["analytic_account_id"][1]
else:
analytic = False
move_line_data.update(
{
"line_rec": line_model.browse(move_line["id"]),
@ -235,6 +268,7 @@ class AgedPartnerBalanceReport(models.AbstractModel):
"ref_label": ref_label,
"due_date": move_line["date_maturity"],
"residual": move_line["amount_residual"],
"analytic_account_id": analytic,
}
)
ag_pb_data[acc_id][prt_id]["move_lines"].append(move_line_data)
@ -416,6 +450,8 @@ class AgedPartnerBalanceReport(models.AbstractModel):
aged_partner_configuration = self.env[
"account.age.report.configuration"
].browse(data["age_partner_config_id"])
analytic_account_ids = data["analytic_account_ids"]
no_analytic = data["no_analytic"]
(ag_pb_data, accounts_data, partners_data, journals_data,) = self.with_context(
age_partner_config=aged_partner_configuration
)._get_move_lines_data(
@ -426,6 +462,8 @@ class AgedPartnerBalanceReport(models.AbstractModel):
date_from,
only_posted_moves,
show_move_line_details,
analytic_account_ids,
no_analytic,
)
aged_partner_data = self.with_context(
age_partner_config=aged_partner_configuration
@ -458,4 +496,5 @@ class AgedPartnerBalanceReport(models.AbstractModel):
"amount_residual",
"reconciled",
"date_maturity",
"analytic_account_id",
]

View File

@ -104,9 +104,14 @@ class AgedPartnerBalanceXslx(models.AbstractModel):
2: {"header": _("Journal"), "field": "journal", "width": 8},
3: {"header": _("Account"), "field": "account", "width": 9},
4: {"header": _("Partner"), "field": "partner", "width": 25},
5: {"header": _("Ref - Label"), "field": "ref_label", "width": 40},
6: {"header": _("Due date"), "field": "due_date", "width": 11},
7: {
5: {
"header": _("Analytic Account"),
"field": "analytic_account_id",
"width": 25,
},
6: {"header": _("Ref - Label"), "field": "ref_label", "width": 40},
7: {"header": _("Due date"), "field": "due_date", "width": 11},
8: {
"header": _("Residual"),
"field": "residual",
"field_footer_total": "residual",
@ -114,7 +119,7 @@ class AgedPartnerBalanceXslx(models.AbstractModel):
"type": "amount",
"width": 14,
},
8: {
9: {
"header": _("Current"),
"field": "current",
"field_footer_total": "current",

View File

@ -7,6 +7,7 @@ import operator
from datetime import date, datetime
from odoo import _, api, models
from odoo.osv import expression
from odoo.tools import float_is_zero
@ -68,10 +69,14 @@ class OpenItemsReport(models.AbstractModel):
company_id,
date_from,
grouped_by,
analytic_account_ids,
no_analytic,
):
domain = self._get_move_lines_domain_not_reconciled(
company_id, account_ids, partner_ids, only_posted_moves, date_from
)
# moved out to avoid too complex method error
domain = self.get_analytic_domain(domain, analytic_account_ids, no_analytic)
ml_fields = self._get_ml_fields()
move_lines = self.env["account.move.line"].search_read(
domain=domain, fields=ml_fields
@ -148,7 +153,10 @@ class OpenItemsReport(models.AbstractModel):
ref_label = move_line["ref"]
else:
ref_label = move_line["ref"] + str(" - ") + move_line["name"]
if analytic_account_ids and move_line["analytic_account_id"]:
analytic_account_id = move_line["analytic_account_id"][1]
else:
analytic_account_id = False
move_line.update(
{
"date": move_line["date"],
@ -167,6 +175,7 @@ class OpenItemsReport(models.AbstractModel):
"currency_name": move_line["currency_id"][1]
if move_line["currency_id"]
else False,
"analytic_account_id": analytic_account_id,
}
)
@ -247,6 +256,9 @@ class OpenItemsReport(models.AbstractModel):
only_posted_moves = data["only_posted_moves"]
show_partner_details = data["show_partner_details"]
grouped_by = data["grouped_by"]
analytic_account_ids = data["analytic_account_ids"]
no_analytic = data["no_analytic"]
(
move_lines_data,
partners_data,
@ -261,6 +273,8 @@ class OpenItemsReport(models.AbstractModel):
company_id,
date_from,
grouped_by,
analytic_account_ids,
no_analytic,
)
total_amount = self._calculate_amounts(open_items_move_lines_data)
@ -288,6 +302,7 @@ class OpenItemsReport(models.AbstractModel):
def _get_ml_fields(self):
return self.COMMON_ML_FIELDS + [
"analytic_account_id",
"amount_residual",
"reconciled",
"currency_id",
@ -297,3 +312,32 @@ class OpenItemsReport(models.AbstractModel):
"debit",
"amount_currency",
]
def get_analytic_domain(self, domain, analytic_account_ids, no_analytic):
if no_analytic:
domain = expression.AND(
[
domain,
[
(
"analytic_account_id",
"=",
False,
)
],
]
)
elif analytic_account_ids:
domain = expression.AND(
[
domain,
[
(
"analytic_account_id",
"in",
analytic_account_ids,
)
],
]
)
return domain

View File

@ -27,15 +27,20 @@ class OpenItemsXslx(models.AbstractModel):
2: {"header": _("Journal"), "field": "journal", "width": 8},
3: {"header": _("Account"), "field": "account", "width": 9},
4: {"header": _("Partner"), "field": "partner_name", "width": 25},
5: {"header": _("Ref - Label"), "field": "ref_label", "width": 40},
6: {"header": _("Due date"), "field": "date_maturity", "width": 11},
7: {
5: {
"header": _("Analytic Account"),
"field": "analytic_account_id",
"width": 25,
},
6: {"header": _("Ref - Label"), "field": "ref_label", "width": 40},
7: {"header": _("Due date"), "field": "date_maturity", "width": 11},
8: {
"header": _("Original"),
"field": "original",
"type": "amount",
"width": 14,
},
8: {
9: {
"header": _("Residual"),
"field": "amount_residual",
"field_final_balance": "residual",
@ -45,21 +50,21 @@ class OpenItemsXslx(models.AbstractModel):
}
if report.foreign_currency:
foreign_currency = {
9: {
10: {
"header": _("Cur."),
"field": "currency_name",
"field_currency_balance": "currency_name",
"type": "currency_name",
"width": 7,
},
10: {
11: {
"header": _("Cur. Original"),
"field": "amount_currency",
"field_final_balance": "amount_currency",
"type": "amount_currency",
"width": 14,
},
11: {
12: {
"header": _("Cur. Residual"),
"field": "amount_residual_currency",
"field_final_balance": "amount_currency",

View File

@ -218,13 +218,29 @@
<div class="act_as_cell" style="width: 5.00%;">Journal</div>
<!--## account code-->
<div class="act_as_cell" style="width: 6.00%;">Account</div>
<!--## partner-->
<div class="act_as_cell" style="width: 10.50%;">Partner</div>
<!--## ref - label-->
<div class="act_as_cell" style="width: 18.00%;">
Ref -
Label
</div>
<t t-if="analytic_account_ids">
<!--## partner-->
<div class="act_as_cell" style="width: 9.50%;">Partner</div>
<!--## analytic account -->
<div
class="act_as_cell"
style="width: 9.50%;"
>Analytic Account</div>
<!--## ref - label-->
<div class="act_as_cell" style="width: 9.50%;">
Ref -
Label
</div>
</t>
<t t-else="">
<!--## partner-->
<div class="act_as_cell" style="width: 10.50%;">Partner</div>
<!--## ref - label-->
<div class="act_as_cell" style="width: 18.00%;">
Ref -
Label
</div>
</t>
<!--## date_due-->
<div class="act_as_cell" style="width: 6.00%;">
Due
@ -311,26 +327,60 @@
<t t-out="line['account']" />
</span>
</div>
<!--## partner-->
<div class="act_as_cell left">
<span
t-att-res-id="line['line_rec'].partner_id.id"
res-model="res.partner"
view-type="form"
>
<t t-out="line['partner']" />
</span>
</div>
<!--## ref - label-->
<div class="act_as_cell left">
<span
t-att-res-id="line['line_rec'].id"
res-model="account.move.line"
view-type="form"
>
<t t-out="line['ref_label']" />
</span>
</div>
<t t-if="analytic_account_ids">
<!--## partner-->
<div class="act_as_cell left">
<span
t-att-res-id="line['line_rec'].partner_id.id"
res-model="res.partner"
view-type="form"
>
<t t-out="line['partner']" />
</span>
</div>
<!-- ANAL -->
<div class="act_as_cell left">
<span
t-att-res-id="line['line_rec'].analytic_account_id.id"
res-model="analytic_account_id"
view-type="form"
>
<t t-out="line['analytic_account_id']" />
</span>
</div>
<!--## ref - label-->
<div class="act_as_cell left">
<span
t-att-res-id="line['line_rec'].id"
res-model="account.move.line"
view-type="form"
>
<t t-out="line['ref_label']" />
</span>
</div>
</t>
<t t-else="">
<!--## partner-->
<div class="act_as_cell left">
<span
t-att-res-id="line['line_rec'].partner_id.id"
res-model="res.partner"
view-type="form"
>
<t t-out="line['partner']" />
</span>
</div>
<!--## ref - label-->
<div class="act_as_cell left">
<span
t-att-res-id="line['line_rec'].id"
res-model="account.move.line"
view-type="form"
>
<t t-out="line['ref_label']" />
</span>
</div>
</t>
<!--## date_due-->
<div class="act_as_cell left">
<span

View File

@ -245,13 +245,30 @@
<div class="act_as_cell" style="width: 4.78%;">Journal</div>
<!--## account code-->
<div class="act_as_cell" style="width: 5.38%;">Account</div>
<!--## partner-->
<div class="act_as_cell" style="width: 15.07%;">Partner</div>
<!--## ref - label-->
<div class="act_as_cell" style="width: 24.5%;">
Ref -
Label
</div>
<t t-if="analytic_account_ids">
<!--## partner-->
<div class="act_as_cell" style="width: 13.19%;">Partner</div>
<!--## analytic_account-->
<div
class="act_as_cell"
style="width: 13.19%;"
>Analytic Account</div>
<!--## ref - label-->
<div class="act_as_cell" style="width: 13.19%;">
Ref -
Label
</div>
</t>
<t t-else="">
<!--## partner-->
<div class="act_as_cell" style="width: 15.07%;">Partner</div>
<!--## ref - label-->
<div class="act_as_cell" style="width: 24.5%;">
Ref -
Label
</div>
</t>
<!--## date_due-->
<div class="act_as_cell" style="width: 6.47%;">
Due
@ -324,6 +341,20 @@
<t t-esc="line['partner_name']" />
</span>
</div>
<!--## cost_center-->
<t t-if="analytic_account_ids">
<div class="act_as_cell left">
<t t-if="line['analytic_account_id']">
<span
t-att-res-id="line['analytic_account_id']"
res-model="account.analytic.account"
view-type="form"
>
<t t-raw="line['analytic_account_id']" />
</span>
</t>
</div>
</t>
<!--## ref - label-->
<div class="act_as_cell left">
<span t-esc="line['ref_label']" />

View File

@ -43,6 +43,10 @@ class AgedPartnerBalanceWizard(models.TransientModel):
age_partner_config_id = fields.Many2one(
"account.age.report.configuration", string="Intervals configuration"
)
analytic_account_ids = fields.Many2many(
comodel_name="account.analytic.account", string="Filter analytic accounts"
)
no_analytic = fields.Boolean("Only no analytic items")
@api.onchange("account_code_from", "account_code_to")
def on_change_account_range(self):
@ -86,17 +90,27 @@ class AgedPartnerBalanceWizard(models.TransientModel):
self.account_ids = self.account_ids.filtered(
lambda a: a.company_id == self.company_id
)
res = {"domain": {"account_ids": [], "partner_ids": []}}
res = {
"domain": {"account_ids": [], "partner_ids": [], "analytic_account_ids": []}
}
if not self.company_id:
return res
else:
res["domain"]["account_ids"] += [("company_id", "=", self.company_id.id)]
res["domain"]["partner_ids"] += self._get_partner_ids_domain()
res["domain"]["analytic_account_ids"] += [
("company_id", "=", self.company_id.id)
]
return res
@api.onchange("account_ids")
def onchange_account_ids(self):
return {"domain": {"account_ids": [("reconcile", "=", True)]}}
return {
"domain": {
"account_ids": [("reconcile", "=", True)],
"analytic_account_ids": [],
}
}
@api.onchange("receivable_accounts_only", "payable_accounts_only")
def onchange_type_accounts_only(self):
@ -142,6 +156,8 @@ class AgedPartnerBalanceWizard(models.TransientModel):
"show_move_line_details": self.show_move_line_details,
"account_financial_report_lang": self.env.lang,
"age_partner_config_id": self.age_partner_config_id.id,
"analytic_account_ids": self.analytic_account_ids.ids or [],
"no_analytic": self.no_analytic,
}
def _export(self, report_type):

View File

@ -62,6 +62,17 @@
colspan="4"
/>
</group>
<group
name="Filter analytic accounts"
groups="analytic.group_analytic_accounting"
>
<field
name="analytic_account_ids"
widget="many2many_tags"
options="{'no_create': True}"
/>
<field name="no_analytic" />
</group>
<footer>
<button
name="button_export_html"

View File

@ -63,6 +63,10 @@ class OpenItemsReportWizard(models.TransientModel):
selection=[("partners", "Partners"), ("salesperson", "Partner Salesperson")],
default="partners",
)
analytic_account_ids = fields.Many2many(
comodel_name="account.analytic.account", string="Filter analytic accounts"
)
no_analytic = fields.Boolean("Only no analytic items")
@api.onchange("account_code_from", "account_code_to")
def on_change_account_range(self):
@ -109,12 +113,17 @@ class OpenItemsReportWizard(models.TransientModel):
self.account_ids = self.account_ids.filtered(
lambda a: a.company_id == self.company_id
)
res = {"domain": {"account_ids": [], "partner_ids": []}}
res = {
"domain": {"account_ids": [], "partner_ids": [], "analytic_account_ids": []}
}
if not self.company_id:
return res
else:
res["domain"]["account_ids"] += [("company_id", "=", self.company_id.id)]
res["domain"]["partner_ids"] += self._get_partner_ids_domain()
res["domain"]["analytic_account_ids"] += [
("company_id", "=", self.company_id.id)
]
return res
@api.onchange("account_ids")
@ -179,8 +188,10 @@ class OpenItemsReportWizard(models.TransientModel):
"target_move": self.target_move,
"account_ids": self.account_ids.ids,
"partner_ids": self.partner_ids.ids or [],
"analytic_account_ids": self.analytic_account_ids.ids or [],
"account_financial_report_lang": self.env.lang,
"grouped_by": self.grouped_by,
"no_analytic": self.no_analytic,
}
def _export(self, report_type):

View File

@ -62,6 +62,17 @@
colspan="4"
/>
</group>
<group
name="Filter analytic accounts"
groups="analytic.group_analytic_accounting"
>
<field
name="analytic_account_ids"
widget="many2many_tags"
options="{'no_create': True}"
/>
<field name="no_analytic" />
</group>
<footer>
<button
name="button_export_html"