Files
Odoo-18.0-20251222/account_financial_report/report/trial_balance.py
Fekete Mihai 2547753dcc Update links in report, add account group file, update trial balance with hierarchy.
Update indentation, remove empty lines from header.

Update test.

Update pylint.

Remove company_id on computing accounts, since account.group is not a company based model, filtering accounts is done on trial balance report.

Update account variables.

Improve condition in padding on accounts.

Add option to print hierarchy based on defined accounts/computed accounts.

Add VAT report, hierarchy from tax tags ans taxes.

Fix pylint, xlsx report generation header.

Update code to select code_prefix or name.

Update code to select code_prefix or name.

Update code to select code_prefix or name.

Fix domain in base amounts in vat report.

Change trial balance code_prefix or name.

Update trail balance, add tests for vat report.

Update pylint, amounts as monetary, many2one option on generation excels.

Update pulint.

Add VAT Report in readme.

Add VAT Report in readme.

Update array_agg.

Update array_agg.

Update array_agg.

Add option in VAT Report to be printed on Tax Tags - Tax Groups.

Add widget to hierarchy_on on trial balance.
2024-11-29 15:38:40 +07:00

489 lines
16 KiB
Python

# ?? 2016 Julien Coux (Camptocamp)
# ?? 2018 Forest and Biomass Romania SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import models, fields, api
class TrialBalanceReport(models.TransientModel):
""" Here, we just define class fields.
For methods, go more bottom at this file.
The class hierarchy is :
* TrialBalanceReport
*** TrialBalanceReportAccount
**** TrialBalanceReportPartner
If "show_partner_details" is selected
"""
_name = 'report_trial_balance'
# Filters fields, used for data computation
date_from = fields.Date()
date_to = fields.Date()
fy_start_date = fields.Date()
only_posted_moves = fields.Boolean()
hide_account_balance_at_0 = fields.Boolean()
company_id = fields.Many2one(comodel_name='res.company')
filter_account_ids = fields.Many2many(comodel_name='account.account')
filter_partner_ids = fields.Many2many(comodel_name='res.partner')
show_partner_details = fields.Boolean()
hierarchy_on = fields.Selection([('computed', 'Computed Accounts'),
('relation', 'Child Accounts')],
string='Hierarchy On',
required=True,
default='computed')
# General Ledger Report Data fields,
# used as base for compute the data reports
general_ledger_id = fields.Many2one(
comodel_name='report_general_ledger'
)
# Data fields, used to browse report data
account_ids = fields.One2many(
comodel_name='report_trial_balance_account',
inverse_name='report_id'
)
class TrialBalanceReportAccount(models.TransientModel):
_name = 'report_trial_balance_account'
_order = 'sequence, code ASC, name'
report_id = fields.Many2one(
comodel_name='report_trial_balance',
ondelete='cascade',
index=True
)
# Data fields, used to keep link with real object
sequence = fields.Integer(index=True, default=0)
level = fields.Integer(index=True, default=0)
# Data fields, used to keep link with real object
account_id = fields.Many2one(
'account.account',
index=True
)
account_group_id = fields.Many2one(
'account.group',
index=True
)
parent_id = fields.Many2one(
'account.group',
index=True
)
child_account_ids = fields.Char(
string="Accounts")
compute_account_ids = fields.Many2many(
'account.account',
string="Accounts", store=True)
# Data fields, used for report display
code = fields.Char()
name = fields.Char()
initial_balance = fields.Float(digits=(16, 2))
debit = fields.Float(digits=(16, 2))
credit = fields.Float(digits=(16, 2))
final_balance = fields.Float(digits=(16, 2))
# Data fields, used to browse report data
partner_ids = fields.One2many(
comodel_name='report_trial_balance_partner',
inverse_name='report_account_id'
)
class TrialBalanceReportPartner(models.TransientModel):
_name = 'report_trial_balance_partner'
report_account_id = fields.Many2one(
comodel_name='report_trial_balance_account',
ondelete='cascade',
index=True
)
# Data fields, used to keep link with real object
partner_id = fields.Many2one(
'res.partner',
index=True
)
# Data fields, used for report display
name = fields.Char()
initial_balance = fields.Float(digits=(16, 2))
debit = fields.Float(digits=(16, 2))
credit = fields.Float(digits=(16, 2))
final_balance = fields.Float(digits=(16, 2))
@api.model
def _generate_order_by(self, order_spec, query):
"""Custom order to display "No partner allocated" at last position."""
return """
ORDER BY
CASE
WHEN "report_trial_balance_partner"."partner_id" IS NOT NULL
THEN 0
ELSE 1
END,
"report_trial_balance_partner"."name"
"""
class TrialBalanceReportCompute(models.TransientModel):
""" Here, we just define methods.
For class fields, go more top at this file.
"""
_inherit = 'report_trial_balance'
@api.multi
def print_report(self, report_type):
self.ensure_one()
if report_type == 'xlsx':
report_name = 'a_f_r.report_trial_balance_xlsx'
else:
report_name = 'account_financial_report.' \
'report_trial_balance_qweb'
return self.env['ir.actions.report'].search(
[('report_name', '=', report_name),
('report_type', '=', report_type)], limit=1).report_action(self)
def _get_html(self):
result = {}
rcontext = {}
context = dict(self.env.context)
report = self.browse(context.get('active_id'))
if report:
rcontext['o'] = report
result['html'] = self.env.ref(
'account_financial_report.report_trial_balance').render(
rcontext)
return result
@api.model
def get_html(self, given_context=None):
return self._get_html()
def _prepare_report_general_ledger(self, account_ids):
self.ensure_one()
return {
'date_from': self.date_from,
'date_to': self.date_to,
'only_posted_moves': self.only_posted_moves,
'hide_account_balance_at_0': self.hide_account_balance_at_0,
'company_id': self.company_id.id,
'filter_account_ids': [(6, 0, account_ids.ids)],
'filter_partner_ids': [(6, 0, self.filter_partner_ids.ids)],
'fy_start_date': self.fy_start_date,
}
@api.multi
def compute_data_for_report(self):
self.ensure_one()
# Compute General Ledger Report Data.
# The data of Trial Balance Report
# are based on General Ledger Report data.
model = self.env['report_general_ledger']
if self.filter_account_ids:
account_ids = self.filter_account_ids
else:
account_ids = self.env['account.account'].search(
[('company_id', '=', self.company_id.id)])
self.general_ledger_id = model.create(
self._prepare_report_general_ledger(account_ids)
)
self.general_ledger_id.compute_data_for_report(
with_line_details=False, with_partners=self.show_partner_details
)
# Compute report data
self._inject_account_values(account_ids)
if self.show_partner_details:
self._inject_partner_values()
if not self.filter_account_ids:
self._inject_account_group_values()
if self.hierarchy_on == 'computed':
self._update_account_group_computed_values()
else:
self._update_account_group_child_values()
self._update_account_sequence()
self._add_account_group_account_values()
self.refresh()
if not self.filter_account_ids:
self._compute_group_accounts()
else:
for line in self.account_ids:
line.write({'level': 0})
def _inject_account_values(self, account_ids):
"""Inject report values for report_trial_balance_account"""
query_inject_account = """
INSERT INTO
report_trial_balance_account
(
report_id,
create_uid,
create_date,
account_id,
parent_id,
code,
name,
initial_balance,
debit,
credit,
final_balance
)
SELECT
%s AS report_id,
%s AS create_uid,
NOW() AS create_date,
acc.id,
acc.group_id,
acc.code,
acc.name,
coalesce(rag.initial_balance, 0) AS initial_balance,
coalesce(rag.final_debit - rag.initial_debit, 0) AS debit,
coalesce(rag.final_credit - rag.initial_credit, 0) AS credit,
coalesce(rag.final_balance, 0) AS final_balance
FROM
account_account acc
LEFT OUTER JOIN report_general_ledger_account AS rag
ON rag.account_id = acc.id AND rag.report_id = %s
WHERE
acc.id in %s
"""
if self.hide_account_balance_at_0:
query_inject_account += """ AND
final_balance IS NOT NULL AND final_balance != 0"""
query_inject_account_params = (
self.id,
self.env.uid,
self.general_ledger_id.id,
account_ids._ids,
)
self.env.cr.execute(query_inject_account, query_inject_account_params)
def _inject_partner_values(self):
"""Inject report values for report_trial_balance_partner"""
query_inject_partner = """
INSERT INTO
report_trial_balance_partner
(
report_account_id,
create_uid,
create_date,
partner_id,
name,
initial_balance,
debit,
credit,
final_balance
)
SELECT
ra.id AS report_account_id,
%s AS create_uid,
NOW() AS create_date,
rpg.partner_id,
rpg.name,
rpg.initial_balance AS initial_balance,
rpg.final_debit - rpg.initial_debit AS debit,
rpg.final_credit - rpg.initial_credit AS credit,
rpg.final_balance AS final_balance
FROM
report_general_ledger_partner rpg
INNER JOIN
report_general_ledger_account rag ON rpg.report_account_id = rag.id
INNER JOIN
report_trial_balance_account ra ON rag.code = ra.code
WHERE
rag.report_id = %s
AND ra.report_id = %s
"""
query_inject_partner_params = (
self.env.uid,
self.general_ledger_id.id,
self.id,
)
self.env.cr.execute(query_inject_partner, query_inject_partner_params)
def _inject_account_group_values(self):
"""Inject report values for report_trial_balance_account"""
query_inject_account_group = """
INSERT INTO
report_trial_balance_account
(
report_id,
create_uid,
create_date,
account_group_id,
parent_id,
code,
name,
sequence,
level
)
SELECT
%s AS report_id,
%s AS create_uid,
NOW() AS create_date,
accgroup.id,
accgroup.parent_id,
coalesce(accgroup.code_prefix, accgroup.name),
accgroup.name,
accgroup.parent_left * 100000,
accgroup.level
FROM
account_group accgroup"""
query_inject_account_params = (
self.id,
self.env.uid,
)
self.env.cr.execute(query_inject_account_group,
query_inject_account_params)
def _update_account_group_child_values(self):
"""Compute values for report_trial_balance_account group in child."""
query_update_account_group = """
WITH computed AS (WITH RECURSIVE cte AS (
SELECT account_group_id, code, account_group_id AS parent_id,
initial_balance, debit, credit, final_balance
FROM report_trial_balance_account
WHERE report_id = %s
GROUP BY report_trial_balance_account.id
UNION ALL
SELECT c.account_group_id, c.code, p.account_group_id,
p.initial_balance, p.debit, p.credit, p.final_balance
FROM cte c
JOIN report_trial_balance_account p USING (parent_id)
WHERE p.report_id = %s
)
SELECT account_group_id, code,
sum(initial_balance) AS initial_balance, sum(debit) AS debit,
sum(credit) AS credit, sum(final_balance) AS final_balance
FROM cte
GROUP BY cte.account_group_id, cte.code
ORDER BY account_group_id
)
UPDATE report_trial_balance_account
SET initial_balance = computed.initial_balance,
debit = computed.debit,
credit = computed.credit,
final_balance = computed.final_balance
FROM computed
WHERE report_trial_balance_account.account_group_id = computed.account_group_id
AND report_trial_balance_account.report_id = %s
"""
query_update_account_params = (self.id, self.id, self.id,)
self.env.cr.execute(query_update_account_group,
query_update_account_params)
def _add_account_group_account_values(self):
"""Compute values for report_trial_balance_account group in child."""
query_update_account_group = """
DROP AGGREGATE IF EXISTS array_concat_agg(anyarray);
CREATE AGGREGATE array_concat_agg(anyarray) (
SFUNC = array_cat,
STYPE = anyarray
);
WITH aggr AS(WITH computed AS (WITH RECURSIVE cte AS (
SELECT account_group_id, account_group_id AS parent_id,
ARRAY[account_id]::int[] as child_account_ids
FROM report_trial_balance_account
WHERE report_id = %s
GROUP BY report_trial_balance_account.id
UNION ALL
SELECT c.account_group_id, p.account_group_id, ARRAY[p.account_id]::int[]
FROM cte c
JOIN report_trial_balance_account p USING (parent_id)
WHERE p.report_id = %s
)
SELECT account_group_id,
array_concat_agg(DISTINCT child_account_ids)::int[] as child_account_ids
FROM cte
GROUP BY cte.account_group_id, cte.child_account_ids
ORDER BY account_group_id
)
SELECT account_group_id,
array_concat_agg(DISTINCT child_account_ids)::int[]
AS child_account_ids from computed
GROUP BY account_group_id)
UPDATE report_trial_balance_account
SET child_account_ids = aggr.child_account_ids
FROM aggr
WHERE report_trial_balance_account.account_group_id = aggr.account_group_id
AND report_trial_balance_account.report_id = %s
"""
query_update_account_params = (self.id, self.id, self.id,)
self.env.cr.execute(query_update_account_group,
query_update_account_params)
def _update_account_group_computed_values(self):
"""Compute values for report_trial_balance_account group in compute."""
query_update_account_group = """
WITH RECURSIVE accgroup AS
(SELECT
accgroup.id,
coalesce(sum(ra.initial_balance),0) as initial_balance,
coalesce(sum(ra.debit),0) as debit,
coalesce(sum(ra.credit),0) as credit,
coalesce(sum(ra.final_balance),0) as final_balance
FROM
account_group accgroup
LEFT OUTER JOIN account_account AS acc
ON strpos(acc.code, accgroup.code_prefix) = 1
LEFT OUTER JOIN report_trial_balance_account AS ra
ON ra.account_id = acc.id
WHERE ra.report_id = %s
GROUP BY accgroup.id
)
UPDATE report_trial_balance_account
SET initial_balance = accgroup.initial_balance,
debit = accgroup.debit,
credit = accgroup.credit,
final_balance = accgroup.final_balance
FROM accgroup
WHERE report_trial_balance_account.account_group_id = accgroup.id
"""
query_update_account_params = (self.id,)
self.env.cr.execute(query_update_account_group,
query_update_account_params)
def _update_account_sequence(self):
"""Compute sequence, level for report_trial_balance_account account."""
query_update_account_group = """
UPDATE report_trial_balance_account
SET sequence = newline.sequence + 1,
level = newline.level + 1
FROM report_trial_balance_account as newline
WHERE newline.account_group_id = report_trial_balance_account.parent_id
AND report_trial_balance_account.report_id = newline.report_id
AND report_trial_balance_account.account_id is not null
AND report_trial_balance_account.report_id = %s"""
query_update_account_params = (self.id,)
self.env.cr.execute(query_update_account_group,
query_update_account_params)
def _compute_group_accounts(self):
groups = self.account_ids.filtered(
lambda a: a.account_group_id is not False)
for group in groups:
if self.hierarchy_on == 'compute':
group.compute_account_ids = \
group.account_group_id.compute_account_ids
else:
if group.child_account_ids:
chacc = group.child_account_ids.replace(
'}', '').replace('{', '').split(',')
if 'NULL' in chacc:
chacc.remove('NULL')
if chacc:
group.compute_account_ids = [
(6, 0, [int(g) for g in chacc])]