# -*- coding: utf-8 -*- from odoo import models, api class GeneralLedgerCustomHandler(models.AbstractModel): _inherit = 'account.general.ledger.report.handler' def _query_values(self, report, options): # 1. Get existing values from super (accounts with moves/balances) results = super()._query_values(report, options) # 2. Filter out accounts ending in '0' that have 0.0 balance. filtered_results = [] possible_removals = 0 for account, values in results: if account.code and account.code.endswith('0'): has_balance = False # Check ALL column groups for col_group_key, col_group_values in values.items(): # For GL, we usually care if there is ANY activity or balance in the period/range displayed. # Check 'sum' (Period Activity/Balance for the column) sum_vals = col_group_values.get('sum', {}) if not self.env.company.currency_id.is_zero(sum_vals.get('balance', 0.0)) or \ not self.env.company.currency_id.is_zero(sum_vals.get('debit', 0.0)) or \ not self.env.company.currency_id.is_zero(sum_vals.get('credit', 0.0)): has_balance = True break # Check 'initial_balance' init_vals = col_group_values.get('initial_balance', {}) if not self.env.company.currency_id.is_zero(init_vals.get('balance', 0.0)): has_balance = True break # Check 'unaffected_earnings' (current year earnings usually) unaff_vals = col_group_values.get('unaffected_earnings', {}) if not self.env.company.currency_id.is_zero(unaff_vals.get('balance', 0.0)): has_balance = True break if not has_balance: continue # Skip this account if no relevant numbers found filtered_results.append((account, values)) results = filtered_results # Extract IDs of accounts already in results existing_account_ids = {account.id for account, _ in results} # 2. Find missing accounts # We need all accounts for the current company(ies) # We respect the search filter if present (handled in super via existing_account_ids check essentially, # but we need to re-apply filter to find *unused* accounts matching the filter) domain = [ *self.env['account.account']._check_company_domain(report.get_report_company_ids(options)), ('id', 'not in', list(existing_account_ids)), ('code', 'not like', '%0') # Filter out unused accounts ending with 0. Used accounts (with balance) are already included by super(). ] if options.get('filter_search_bar'): domain.append(('name', 'ilike', options['filter_search_bar'])) # 3. Create empty result structure for missing accounts # The structure expected is: # values_by_column_group is a dict {column_group_key: values} # values is a dict with keys like 'sum', 'initial_balance', 'unaffected_earnings' # 'sum': {'debit': 0.0, 'credit': 0.0, 'balance': 0.0, 'amount_currency': 0.0} missing_accounts = self.env['account.account'].search(domain) if not missing_accounts: return results # Construct the empty value dictionary structure # We need to replicate the structure for each column group empty_values_template = { 'sum': { 'debit': 0.0, 'credit': 0.0, 'balance': 0.0, 'amount_currency': 0.0, # 'max_date': None # Optional }, 'initial_balance': { 'debit': 0.0, 'credit': 0.0, 'balance': 0.0, 'amount_currency': 0.0, }, 'unaffected_earnings': { 'debit': 0.0, 'credit': 0.0, 'balance': 0.0, 'amount_currency': 0.0, } } for account in missing_accounts: # We need a separate copy of the values for each account/column_group to avoid reference issues # (though strictly speaking they are all 0 so it might not matter, but safer to copy) account_values = {} for column_group_key in options['column_groups']: # deeply copy the template account_values[column_group_key] = { key: val.copy() for key, val in empty_values_template.items() } results.append((account, account_values)) # Re-sort results by account code to ensure correct order # The original results are sorted, we appended new ones at the end. results.sort(key=lambda x: x[0].code) return results