forked from Mapan/odoo17e
336 lines
18 KiB
Python
336 lines
18 KiB
Python
from odoo import api, models, _
|
|
|
|
|
|
class GeneralLedgerCustomHandler(models.AbstractModel):
|
|
_inherit = 'account.general.ledger.report.handler'
|
|
|
|
def _dynamic_lines_generator(self, report, options, all_column_groups_expression_totals, warnings=None):
|
|
if not options.get('l10n_co_reports_groupby_partner_id'):
|
|
return super()._dynamic_lines_generator(report, options, all_column_groups_expression_totals, warnings)
|
|
|
|
lines_groupby_account, total_line = self._l10n_co_reports_get_lines_group_by_account_and_partner(report, options)
|
|
|
|
# Order lines by account code
|
|
sorted_lines = [
|
|
line
|
|
for account_key in sorted(lines_groupby_account, key=lambda account: account.code)
|
|
for line in lines_groupby_account[account_key]
|
|
]
|
|
sorted_lines.append(total_line)
|
|
|
|
return [(0, line) for line in sorted_lines]
|
|
|
|
def _l10n_co_reports_get_lines_group_by_account_and_partner(self, report, options):
|
|
# Returns
|
|
# - A dict representing the lines that need to be added to the report, grouped by account
|
|
# - The final line with the total amounts
|
|
# Note: All moves without partner are grouped into a single line at the end of each list
|
|
|
|
def custom_sort_key(line):
|
|
# Order lines alphabetically by partner name, with the parent line on top and the line without partner at the bottom
|
|
custom_partner_id = line['l10n_co_partner_id']
|
|
custom_partner_id = 0 if custom_partner_id else 1
|
|
return not line['is_parent'], custom_partner_id, line['l10n_co_partner_name']
|
|
|
|
lines_groupby_account = {}
|
|
for (account, partner_id), column_group_results in self._l10n_co_reports_query_values(report, options):
|
|
eval_dict = {}
|
|
partner_name = ''
|
|
partner_vat = ''
|
|
for column_group_key, results in column_group_results.items():
|
|
account_sum = results.get('sum', {})
|
|
account_un_earn = results.get('unaffected_earnings', {})
|
|
|
|
if not partner_name:
|
|
partner_name = account_sum.get('partner_name', '') or account_un_earn.get('partner_name', '')
|
|
|
|
if not partner_vat:
|
|
partner_vat = account_sum.get('partner_vat', '') or account_un_earn.get('partner_vat', '')
|
|
|
|
eval_dict[column_group_key] = {
|
|
'amount_currency': account_sum.get('amount_currency', 0.0) + account_un_earn.get('amount_currency', 0.0),
|
|
'debit': account_sum.get('debit', 0.0) + account_un_earn.get('debit', 0.0),
|
|
'credit': account_sum.get('credit', 0.0) + account_un_earn.get('credit', 0.0),
|
|
'balance': account_sum.get('balance', 0.0) + account_un_earn.get('balance', 0.0),
|
|
}
|
|
|
|
lines_groupby_account.setdefault(account, []).append(
|
|
self._l10n_co_reports_get_account_title_line(report, options, account, partner_id, partner_name, partner_vat, eval_dict)
|
|
)
|
|
|
|
total_amounts = [0] * len(options['columns'])
|
|
|
|
for account, partner_lines in lines_groupby_account.items():
|
|
parent_line_col_values = [
|
|
sum(partner_line['columns'][i]['no_format'] or 0.0 for partner_line in partner_lines)
|
|
for i in range(len(options['columns']))
|
|
]
|
|
|
|
parent_line = self._l10n_co_reports_get_empty_parent_line(report, options, account, parent_line_col_values)
|
|
parent_line['id'] = report._get_generic_line_id('account.account', account.id)
|
|
|
|
for partner_line in partner_lines:
|
|
partner_line['parent_id'] = parent_line['id']
|
|
|
|
total_amounts = [x + y for x, y in zip(total_amounts, parent_line_col_values)]
|
|
|
|
partner_lines.append(parent_line)
|
|
partner_lines.sort(key=custom_sort_key)
|
|
|
|
total_line = self._l10n_co_reports_get_total_line(report, options, total_amounts)
|
|
|
|
return lines_groupby_account, total_line
|
|
|
|
@api.model
|
|
def _l10n_co_reports_get_empty_parent_line(self, report, options, account, parent_line_col_values):
|
|
line_columns = []
|
|
for i, column in enumerate(options['columns']):
|
|
if column['expression_label'] == 'partner_name':
|
|
line_columns.append({'name': '', 'no_format': 0.0, 'class': ''})
|
|
continue
|
|
if column['expression_label'] == 'partner_vat':
|
|
line_columns.append({'name': '', 'no_format': 0.0, 'class': ''})
|
|
continue
|
|
|
|
col_value = parent_line_col_values[i]
|
|
formatted_value = report.format_value(options, col_value, figure_type=column['figure_type'], blank_if_zero=True)
|
|
|
|
line_columns.append({
|
|
'name': formatted_value,
|
|
'no_format': col_value,
|
|
'class': 'number',
|
|
})
|
|
|
|
return {
|
|
'name': f'{account.code} {account.name}',
|
|
'search_key': account.code,
|
|
'columns': line_columns,
|
|
'level': 1,
|
|
'unfoldable': False,
|
|
'unfolded': False,
|
|
'l10n_co_partner_name': '',
|
|
'l10n_co_partner_id': '',
|
|
'is_parent': True,
|
|
}
|
|
|
|
@api.model
|
|
def _l10n_co_reports_get_account_title_line(self, report, options, account, partner_id, partner_name, partner_vat, eval_dict):
|
|
line_columns = []
|
|
for column in options['columns']:
|
|
if column['expression_label'] == 'partner_name':
|
|
line_columns.append({
|
|
'name': partner_name if partner_id else _('(None)'),
|
|
'no_format': 0.0,
|
|
})
|
|
continue
|
|
if column['expression_label'] == 'partner_vat':
|
|
line_columns.append({
|
|
'name': partner_vat,
|
|
'no_format': 0.0,
|
|
})
|
|
continue
|
|
|
|
col_value = eval_dict[column['column_group_key']].get(column['expression_label'])
|
|
formatted_value = report.format_value(options, col_value, figure_type=column['figure_type'], blank_if_zero=True)
|
|
|
|
line_columns.append({
|
|
'name': formatted_value,
|
|
'no_format': col_value,
|
|
'class': 'number',
|
|
})
|
|
|
|
line_id = report._get_generic_line_id('res.partner', partner_id, markup='account_id:' + str(account.id))
|
|
return {
|
|
'id': line_id,
|
|
'name': f'{account.code} {account.name}',
|
|
'search_key': account.code,
|
|
'columns': line_columns,
|
|
'level': 3,
|
|
'unfoldable': False,
|
|
'unfolded': False,
|
|
'l10n_co_partner_name': partner_name,
|
|
'l10n_co_partner_id': partner_id,
|
|
'is_parent': False,
|
|
}
|
|
|
|
@api.model
|
|
def _l10n_co_reports_get_total_line(self, report, options, total_amounts):
|
|
total_line_columns = []
|
|
for i, column in enumerate(options['columns']):
|
|
if column['expression_label'] in ('partner_name', 'partner_vat'):
|
|
total_line_columns.append({})
|
|
continue
|
|
|
|
col_value = total_amounts[i]
|
|
formatted_value = report.format_value(options, col_value, blank_if_zero=False, figure_type='monetary')
|
|
total_line_columns.append({
|
|
'name': formatted_value,
|
|
'no_format': col_value,
|
|
'class': 'number',
|
|
})
|
|
|
|
return {
|
|
'id': report._get_generic_line_id(None, None, markup='total'),
|
|
'name': _('Total'),
|
|
'class': 'total',
|
|
'level': 1,
|
|
'columns': total_line_columns,
|
|
}
|
|
|
|
def _l10n_co_reports_query_values(self, report, options):
|
|
accounts_partners_keys = set()
|
|
accounts_partners_map = {}
|
|
companies_map = {} # Changed from companies_partners_map
|
|
|
|
aml_query, aml_params = self._l10n_co_reports_get_query_amls(report, options)
|
|
self._cr.execute(aml_query, aml_params)
|
|
|
|
for res in self._cr.dictfetchall():
|
|
column_group_key = res['column_group_key']
|
|
key = res['key']
|
|
partner_id = res['partner_id']
|
|
res['partner_vat'] = res['partner_vat'] or ''
|
|
|
|
if key == 'sum':
|
|
if res['account_type'] == 'equity_unaffected':
|
|
partner_id = None # Remove partner details for unaffected earnings
|
|
res['partner_vat'] = ''
|
|
|
|
account_id = res['groupby']
|
|
groupby_key = (account_id, partner_id)
|
|
accounts_partners_keys.add(groupby_key)
|
|
accounts_partners_map.setdefault(groupby_key, {col_group_key: {} for col_group_key in options['column_groups']})
|
|
accounts_partners_map[groupby_key][column_group_key][key] = res
|
|
|
|
elif key == 'unaffected_earnings':
|
|
company_id = res['groupby']
|
|
companies_map.setdefault(company_id, {col_group_key: {} for col_group_key in options['column_groups']})
|
|
companies_map[company_id][column_group_key] = res
|
|
|
|
# Converts the unaffected earnings of the query to the proper unaffected account of the company.
|
|
# The subgroup per partner no longer applies for unaffected earnings
|
|
if companies_map:
|
|
company_unaffected_account_map = {
|
|
account.company_id.id: account
|
|
for account in self.env['account.account'].search([
|
|
('account_type', '=', 'equity_unaffected'),
|
|
('company_id', 'in', list(companies_map.keys())),
|
|
])
|
|
}
|
|
for company_id, company_data in companies_map.items():
|
|
account = company_unaffected_account_map[company_id]
|
|
groupby_key = (account.id, None) # Use None instead of partner_id
|
|
accounts_partners_map.setdefault(groupby_key, {col_group_key: {} for col_group_key in options['column_groups']})
|
|
for column_group_key in options['column_groups']:
|
|
if 'unaffected_earnings' not in accounts_partners_map[groupby_key][column_group_key]:
|
|
accounts_partners_map[groupby_key][column_group_key]['unaffected_earnings'] = companies_map[company_id][column_group_key]
|
|
accounts_partners_keys.add(groupby_key)
|
|
|
|
account_partner_keys = {(self.env['account.account'].browse(account_id), partner_id) for account_id, partner_id in accounts_partners_keys}
|
|
return [(account_partner_key, accounts_partners_map[account_partner_key[0].id, account_partner_key[1]]) for account_partner_key in account_partner_keys]
|
|
|
|
def _l10n_co_reports_get_query_amls(self, report, options):
|
|
options_by_column_group = report._split_options_per_column_group(options)
|
|
|
|
params = []
|
|
queries = []
|
|
|
|
# Create the currency table.
|
|
# As the currency table is the same whatever the comparisons, create it only once.
|
|
ct_query = report._get_query_currency_table(options)
|
|
|
|
# ===============================================================
|
|
# 1) Get sums for all (accounts, partners) existing combinations
|
|
# ===============================================================
|
|
for column_group_key, options_group in options_by_column_group.items():
|
|
if not options.get('general_ledger_strict_range'):
|
|
options_group = self._get_options_sum_balance(options_group)
|
|
|
|
# Sum is computed including the initial balance of the accounts configured to do so, unless a special option key is used
|
|
sum_date_scope = 'strict_range' if options_group.get('general_ledger_strict_range') else 'normal'
|
|
query_domain = []
|
|
|
|
if options.get('filter_search_bar'):
|
|
query_domain.append(('account_id', 'ilike', options['filter_search_bar']))
|
|
|
|
# Exclude move lines with P&L accounts from initial balance if they belong to a previous fiscal year
|
|
if options_group.get('include_current_year_in_unaff_earnings'):
|
|
query_domain += [('account_id.include_initial_balance', '=', True)]
|
|
|
|
tables, where_clause, where_params = report._query_get(options_group, sum_date_scope, domain=query_domain)
|
|
params.append(column_group_key)
|
|
params += where_params
|
|
queries.append(f"""
|
|
SELECT
|
|
account.id AS groupby,
|
|
account.account_type AS account_type,
|
|
partner.id AS partner_id,
|
|
'sum' AS key,
|
|
MAX(account_move_line.date) AS max_date,
|
|
%s AS column_group_key,
|
|
COALESCE(SUM(account_move_line.amount_currency), 0.0) AS amount_currency,
|
|
SUM(ROUND(account_move_line.debit * currency_table.rate, currency_table.precision)) AS debit,
|
|
SUM(ROUND(account_move_line.credit * currency_table.rate, currency_table.precision)) AS credit,
|
|
SUM(ROUND(account_move_line.balance * currency_table.rate, currency_table.precision)) AS balance,
|
|
account.code AS account_code,
|
|
account.name AS account_name,
|
|
partner.name AS partner_name,
|
|
partner.vat AS partner_vat
|
|
FROM {tables}
|
|
LEFT JOIN res_company company ON company.id = account_move_line.company_id
|
|
LEFT JOIN res_partner partner ON partner.id = account_move_line.partner_id
|
|
LEFT JOIN account_account account ON account.id = account_move_line.account_id
|
|
LEFT JOIN account_journal journal ON journal.id = account_move_line.journal_id
|
|
LEFT JOIN account_full_reconcile full_rec ON full_rec.id = account_move_line.full_reconcile_id
|
|
LEFT JOIN {ct_query} ON currency_table.company_id = account_move_line.company_id
|
|
WHERE {where_clause}
|
|
GROUP BY account.id, partner.id
|
|
""")
|
|
|
|
# ============================================
|
|
# 2) Unaffected earnings.
|
|
# ============================================
|
|
if not options_group.get('general_ledger_strict_range'):
|
|
# Apply to initial balance and end balance and only focus on unaffected earnings
|
|
|
|
unaff_earnings_domain = [('account_id.include_initial_balance', '=', False)]
|
|
|
|
# The period domain is expressed as:
|
|
# [
|
|
# ('date' <= fiscalyear['date_from'] - 1),
|
|
# ('account_id.include_initial_balance', '=', False),
|
|
# ]
|
|
|
|
new_options = self._get_options_unaffected_earnings(options_group)
|
|
tables, where_clause, where_params = report._query_get(new_options, 'strict_range', domain=unaff_earnings_domain)
|
|
params.append(column_group_key)
|
|
params += where_params
|
|
queries.append(f"""
|
|
SELECT
|
|
company.id AS groupby,
|
|
NULL AS account_type,
|
|
NULL AS partner_id,
|
|
'unaffected_earnings' AS key,
|
|
NULL AS max_date,
|
|
%s AS column_group_key,
|
|
COALESCE(SUM(account_move_line.amount_currency), 0.0) AS amount_currency,
|
|
SUM(ROUND(account_move_line.debit * currency_table.rate, currency_table.precision)) AS debit,
|
|
SUM(ROUND(account_move_line.credit * currency_table.rate, currency_table.precision)) AS credit,
|
|
SUM(ROUND(account_move_line.balance * currency_table.rate, currency_table.precision)) AS balance,
|
|
NULL AS account_code,
|
|
NULL AS account_name,
|
|
NULL AS partner_name,
|
|
NULL AS partner_vat
|
|
FROM {tables}
|
|
LEFT JOIN res_company company ON company.id = account_move_line.company_id
|
|
LEFT JOIN res_partner partner ON partner.id = account_move_line.partner_id
|
|
LEFT JOIN account_account account ON account.id = account_move_line.account_id
|
|
LEFT JOIN account_journal journal ON journal.id = account_move_line.journal_id
|
|
LEFT JOIN account_full_reconcile full_rec ON full_rec.id = account_move_line.full_reconcile_id
|
|
LEFT JOIN {ct_query} ON currency_table.company_id = account_move_line.company_id
|
|
WHERE {where_clause}
|
|
GROUP BY company.id
|
|
""")
|
|
|
|
return ' UNION ALL '.join(queries), params
|