account_report_show_all/models/account_report.py

183 lines
8.4 KiB
Python

from odoo import models
class AccountReport(models.Model):
_inherit = 'account.report'
def _get_lines(self, options, all_column_groups_expression_totals=None, warnings=None):
# Get the standard lines
lines = super()._get_lines(options, all_column_groups_expression_totals, warnings=warnings)
# 1. Filter Logic & Collection of Seen Accounts
filtered_lines = []
seen_account_ids = set()
# We need a template line to construct missing account lines later
line_template = None
for line in lines:
keep_line = True
is_account_line = line.get('caret_options') in ('account.account', 'trial_balance')
if is_account_line:
# Store potential template (first valid account line we find)
if not line_template:
line_template = line
# Attempt to get the code
name = line.get('name', '').strip()
parts = name.split(' ', 1)
code_part = parts[0] if parts else ''
# Logic: Hide if code ends with '0' AND has no balance
if code_part.isdigit() and code_part.endswith('0'):
has_balance = False
for col in line.get('columns', []):
val = col.get('no_format')
if val is None or val == '':
val = 0.0
if not self.env.company.currency_id.is_zero(val):
has_balance = True
break
if not has_balance:
keep_line = False
# If we keep it, track the account ID
# Usually report lines for accounts have 'res_id' pointing to account.account id
if keep_line and line.get('res_model') == 'account.account':
seen_account_ids.add(line.get('res_id'))
elif keep_line:
# Fallback for complex IDs or different formats
line_id = line.get('id', '')
# 1. Standard Odoo 'account.account_ID'
if line_id.startswith('account.account_'):
try:
acc_id = int(line_id.split('_')[-1])
seen_account_ids.add(acc_id)
except: pass
# 2. Complex Report Engine ID: e.g. ~account.report~12|~account.account~3743
elif '~account.account~' in line_id:
try:
# Split by | and find the part with account.account
# This is a bit manual but robust enough for this format
parts = line_id.split('|')
for p in parts:
if '~account.account~' in p:
# p might be '~account.account~3743'
acc_id_str = p.split('~account.account~')[-1]
acc_id = int(acc_id_str)
seen_account_ids.add(acc_id)
except: pass
if keep_line:
filtered_lines.append(line)
# 2. Add Missing Accounts ("Show All")
# Find accounts that are NOT in seen_account_ids and do NOT end in '0'
# (Accounts ending in '0' are "view" accounts, we don't want to force show them if they are empty)
if line_template:
domain = [
('company_id', 'in', self.env.companies.ids),
('id', 'not in', list(seen_account_ids)),
('code', 'not like', '%0')
]
missing_accounts = self.env['account.account'].search(domain)
# Create a zero-column structure
zero_columns = []
for col in line_template.get('columns', []):
# reconstruct a zero column
zero_col = {'name': '', 'no_format': 0.0, 'class': 'number'}
zero_columns.append(zero_col)
# Separate existing lines into accounts and others (e.g. Total)
# The 'Total' line usually comes last.
# We want to insert our new accounts into the account list and sort them,
# but keep the Total at the bottom.
account_lines = []
other_lines = [] # Headers, Totals, Sections
for line in filtered_lines:
if line.get('caret_options') in ('account.account', 'trial_balance') and not line.get('class') == 'total':
account_lines.append(line)
else:
other_lines.append(line)
# Add missing accounts to account_lines
for account in missing_accounts:
new_line = {
'id': f'account.account_{account.id}',
'name': f'{account.code} {account.name}',
'columns': zero_columns,
'level': line_template.get('level', 2),
'caret_options': 'account.account',
'res_id': account.id,
'res_model': 'account.account',
'parent_id': line_template.get('parent_id'),
'unfoldable': False,
'unfolded': False,
}
account_lines.append(new_line)
# Sort account lines by code
def get_code(l):
n = l.get('name', '').strip()
return n.split(' ')[0]
account_lines.sort(key=get_code)
# Reassemble: Account Lines first, then Total/Other lines
# NOTE: If 'other_lines' contains headers that should be at top, this logic might be too simple.
# But usually Trial Balance is simple.
# If GL has sections, this whole "Show All" logic is risky without knowing section structure.
# Assuming Trial Balance for now as per user request context.
# If 'other_lines' are at the end (Total), appending is correct.
# If 'other_lines' are at start, they need to be prepended.
# Heuristic: Check where they came from.
# Re-construct based on original position roughly?
# Creating a list of (index, line) and sorting might work if we knew where to put new ones.
# But new ones belong in the "middle".
# Safer Approach for TB:
# - Group headers/top lines
# - Group account lines
# - Group total/bottom lines
# Simple Heuristic: 'Total' line usually has class='total' or similar.
# Let's assume other_lines are footer/total for TB.
# However, check if any 'other_lines' appeared BEFORE the first account line.
passed_first_account = False
top_lines = []
bottom_lines = []
# Re-scan filtered_lines to split into top/account/bottom
for line in filtered_lines:
is_acc = line.get('caret_options') in ('account.account', 'trial_balance') and not line.get('class') == 'total'
if is_acc:
passed_first_account = True
# account_lines already collected above (but we need to clear it and re-collect to be safe?
# No, we have account_lines populated above.
# Use the logic:
# If we haven't seen an account yet, it's a top line.
# If we have, and it's not an account, it's a bottom line? NOT ALWAYS (Sections).
pass
else:
if not passed_first_account:
top_lines.append(line)
else:
bottom_lines.append(line)
# Re-sort the account_lines (which includes the newly added ones + existing ones)
account_lines.sort(key=get_code)
filtered_lines = top_lines + account_lines + bottom_lines
return filtered_lines