# -*- 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 for col_group_key, col_group_values in values.items(): # Check if this column group is strict range (period activity) or cumulative (balance sheet) col_group_options = options.get('column_groups', {}).get(col_group_key, {}) forced_options = col_group_options.get('forced_options', {}) is_strict_range = forced_options.get('general_ledger_strict_range') or options.get('general_ledger_strict_range') for key in ['sum', 'initial_balance', 'unaffected_earnings']: if key in col_group_values: # 1. Always check Balance if not self.env.company.currency_id.is_zero(col_group_values[key].get('balance', 0.0)): has_balance = True break # 2. If Strict Range (Period Activity), check Debit/Credit too # If Cumulative, we ignore Debit/Credit because they are lifetime sums which might be non-zero even if balance is zero. if is_strict_range: if not self.env.company.currency_id.is_zero(col_group_values[key].get('debit', 0.0)) or \ not self.env.company.currency_id.is_zero(col_group_values[key].get('credit', 0.0)): has_balance = True break if has_balance: break if not has_balance: continue # Skip this account 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