Tower: upload at_accounting 18.0.1.7 (via marketplace)
This commit is contained in:
744
addons/at_accounting/models/account_general_ledger.py
Normal file
744
addons/at_accounting/models/account_general_ledger.py
Normal file
@@ -0,0 +1,744 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import json
|
||||
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.tools.misc import format_date
|
||||
from odoo.tools import get_lang, SQL
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
from datetime import timedelta
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
class GeneralLedgerCustomHandler(models.AbstractModel):
|
||||
_name = 'account.general.ledger.report.handler'
|
||||
_inherit = 'account.report.custom.handler'
|
||||
_description = 'General Ledger Custom Handler'
|
||||
|
||||
def _get_custom_display_config(self):
|
||||
return {
|
||||
'templates': {
|
||||
'AccountReportLineName': 'at_accounting.GeneralLedgerLineName',
|
||||
},
|
||||
}
|
||||
|
||||
def _custom_options_initializer(self, report, options, previous_options):
|
||||
# Remove multi-currency columns if needed
|
||||
super()._custom_options_initializer(report, options, previous_options=previous_options)
|
||||
if self.env.user.has_group('base.group_multi_currency'):
|
||||
options['multi_currency'] = True
|
||||
else:
|
||||
options['columns'] = [
|
||||
column for column in options['columns']
|
||||
if column['expression_label'] != 'amount_currency'
|
||||
]
|
||||
|
||||
# Automatically unfold the report when printing it, unless some specific lines have been unfolded
|
||||
options['unfold_all'] = (options['export_mode'] == 'print' and not options.get('unfolded_lines')) or options['unfold_all']
|
||||
|
||||
def _dynamic_lines_generator(self, report, options, all_column_groups_expression_totals, warnings=None):
|
||||
lines = []
|
||||
date_from = fields.Date.from_string(options['date']['date_from'])
|
||||
company_currency = self.env.company.currency_id
|
||||
|
||||
totals_by_column_group = defaultdict(lambda: {'debit': 0, 'credit': 0, 'balance': 0})
|
||||
for account, column_group_results in self._query_values(report, options):
|
||||
eval_dict = {}
|
||||
has_lines = False
|
||||
for column_group_key, results in column_group_results.items():
|
||||
account_sum = results.get('sum', {})
|
||||
account_un_earn = results.get('unaffected_earnings', {})
|
||||
|
||||
account_debit = account_sum.get('debit', 0.0) + account_un_earn.get('debit', 0.0)
|
||||
account_credit = account_sum.get('credit', 0.0) + account_un_earn.get('credit', 0.0)
|
||||
account_balance = account_sum.get('balance', 0.0) + account_un_earn.get('balance', 0.0)
|
||||
|
||||
eval_dict[column_group_key] = {
|
||||
'amount_currency': account_sum.get('amount_currency', 0.0) + account_un_earn.get('amount_currency', 0.0),
|
||||
'debit': account_debit,
|
||||
'credit': account_credit,
|
||||
'balance': account_balance,
|
||||
}
|
||||
|
||||
max_date = account_sum.get('max_date')
|
||||
has_lines = has_lines or (max_date and max_date >= date_from)
|
||||
|
||||
totals_by_column_group[column_group_key]['debit'] += account_debit
|
||||
totals_by_column_group[column_group_key]['credit'] += account_credit
|
||||
totals_by_column_group[column_group_key]['balance'] += account_balance
|
||||
|
||||
lines.append(self._get_account_title_line(report, options, account, has_lines, eval_dict))
|
||||
|
||||
# Report total line.
|
||||
for totals in totals_by_column_group.values():
|
||||
totals['balance'] = company_currency.round(totals['balance'])
|
||||
|
||||
# Tax Declaration lines.
|
||||
journal_options = report._get_options_journals(options)
|
||||
if len(options['column_groups']) == 1 and len(journal_options) == 1 and journal_options[0]['type'] in ('sale', 'purchase'):
|
||||
lines += self._tax_declaration_lines(report, options, journal_options[0]['type'])
|
||||
|
||||
# Total line
|
||||
lines.append(self._get_total_line(report, options, totals_by_column_group))
|
||||
|
||||
return [(0, line) for line in lines]
|
||||
|
||||
def _custom_unfold_all_batch_data_generator(self, report, options, lines_to_expand_by_function):
|
||||
account_ids_to_expand = []
|
||||
for line_dict in lines_to_expand_by_function.get('_report_expand_unfoldable_line_general_ledger', []):
|
||||
model, model_id = report._get_model_info_from_id(line_dict['id'])
|
||||
if model == 'account.account':
|
||||
account_ids_to_expand.append(model_id)
|
||||
|
||||
limit_to_load = report.load_more_limit if report.load_more_limit and not options.get('export_mode') else None
|
||||
has_more_per_account_id = {}
|
||||
|
||||
unlimited_aml_results_per_account_id = self._get_aml_values(report, options, account_ids_to_expand)[0]
|
||||
if limit_to_load:
|
||||
# Apply the load_more_limit.
|
||||
# load_more_limit cannot be passed to the call to _get_aml_values, otherwise it won't be applied per account but on the whole result.
|
||||
# We gain perf from batching, but load every result ; then we need to filter them.
|
||||
|
||||
aml_results_per_account_id = {}
|
||||
for account_id, account_aml_results in unlimited_aml_results_per_account_id.items():
|
||||
account_values = {}
|
||||
for key, value in account_aml_results.items():
|
||||
if len(account_values) == limit_to_load:
|
||||
has_more_per_account_id[account_id] = True
|
||||
break
|
||||
account_values[key] = value
|
||||
aml_results_per_account_id[account_id] = account_values
|
||||
else:
|
||||
aml_results_per_account_id = unlimited_aml_results_per_account_id
|
||||
|
||||
return {
|
||||
'initial_balances': self._get_initial_balance_values(report, account_ids_to_expand, options),
|
||||
'aml_results': aml_results_per_account_id,
|
||||
'has_more': has_more_per_account_id,
|
||||
}
|
||||
|
||||
def _tax_declaration_lines(self, report, options, tax_type):
|
||||
labels_replacement = {
|
||||
'debit': _("Base Amount"),
|
||||
'credit': _("Tax Amount"),
|
||||
}
|
||||
|
||||
rslt = [{
|
||||
'id': report._get_generic_line_id(None, None, markup='tax_decl_header_1'),
|
||||
'name': _('Tax Declaration'),
|
||||
'columns': [{} for column in options['columns']],
|
||||
'level': 1,
|
||||
'unfoldable': False,
|
||||
'unfolded': False,
|
||||
}, {
|
||||
'id': report._get_generic_line_id(None, None, markup='tax_decl_header_2'),
|
||||
'name': _('Name'),
|
||||
'columns': [{'name': labels_replacement.get(col['expression_label'], '')} for col in options['columns']],
|
||||
'level': 3,
|
||||
'unfoldable': False,
|
||||
'unfolded': False,
|
||||
}]
|
||||
|
||||
# Call the generic tax report
|
||||
generic_tax_report = self.env.ref('account.generic_tax_report')
|
||||
tax_report_options = generic_tax_report.get_options({**options, 'selected_variant_id': generic_tax_report.id, 'forced_domain': [('tax_line_id.type_tax_use', '=', tax_type)]})
|
||||
tax_report_lines = generic_tax_report._get_lines(tax_report_options)
|
||||
tax_type_parent_line_id = generic_tax_report._get_generic_line_id(None, None, markup=tax_type)
|
||||
|
||||
for tax_report_line in tax_report_lines:
|
||||
if tax_report_line.get('parent_id') == tax_type_parent_line_id:
|
||||
original_columns = tax_report_line['columns']
|
||||
row_column_map = {
|
||||
'debit': original_columns[0],
|
||||
'credit': original_columns[1],
|
||||
}
|
||||
|
||||
tax_report_line['columns'] = [row_column_map.get(col['expression_label'], {}) for col in options['columns']]
|
||||
rslt.append(tax_report_line)
|
||||
|
||||
return rslt
|
||||
|
||||
def _query_values(self, report, options):
|
||||
""" Executes the queries, and performs all the computations.
|
||||
|
||||
:return: [(record, values_by_column_group), ...], where
|
||||
- record is an account.account record.
|
||||
- values_by_column_group is a dict in the form {column_group_key: values, ...}
|
||||
- column_group_key is a string identifying a column group, as in options['column_groups']
|
||||
- values is a list of dictionaries, one per period containing:
|
||||
- sum: {'debit': float, 'credit': float, 'balance': float}
|
||||
- (optional) initial_balance: {'debit': float, 'credit': float, 'balance': float}
|
||||
- (optional) unaffected_earnings: {'debit': float, 'credit': float, 'balance': float}
|
||||
"""
|
||||
# Execute the queries and dispatch the results.
|
||||
query = self._get_query_sums(report, options)
|
||||
|
||||
if not query:
|
||||
return []
|
||||
|
||||
groupby_accounts = {}
|
||||
groupby_companies = {}
|
||||
|
||||
self._cr.execute(query)
|
||||
for res in self._cr.dictfetchall():
|
||||
# No result to aggregate.
|
||||
if res['groupby'] is None:
|
||||
continue
|
||||
|
||||
column_group_key = res['column_group_key']
|
||||
key = res['key']
|
||||
if key == 'sum':
|
||||
groupby_accounts.setdefault(res['groupby'], {col_group_key: {} for col_group_key in options['column_groups']})
|
||||
groupby_accounts[res['groupby']][column_group_key][key] = res
|
||||
|
||||
elif key == 'initial_balance':
|
||||
groupby_accounts.setdefault(res['groupby'], {col_group_key: {} for col_group_key in options['column_groups']})
|
||||
groupby_accounts[res['groupby']][column_group_key][key] = res
|
||||
|
||||
elif key == 'unaffected_earnings':
|
||||
groupby_companies.setdefault(res['groupby'], {col_group_key: {} for col_group_key in options['column_groups']})
|
||||
groupby_companies[res['groupby']][column_group_key] = res
|
||||
|
||||
# Affect the unaffected earnings to the first fetched account of type 'account.data_unaffected_earnings'.
|
||||
# It's less costly to fetch all candidate accounts in a single search and then iterate it.
|
||||
if groupby_companies:
|
||||
unaffected_earnings_accounts = self.env['account.account'].search([
|
||||
('display_name', 'ilike', options.get('filter_search_bar')),
|
||||
*self.env['account.account']._check_company_domain(list(groupby_companies.keys())),
|
||||
('account_type', '=', 'equity_unaffected'),
|
||||
])
|
||||
for company_id, groupby_company in groupby_companies.items():
|
||||
if equity_unaffected_account := unaffected_earnings_accounts.filtered(lambda a: self.env['res.company'].browse(company_id).root_id in a.company_ids):
|
||||
for column_group_key in options['column_groups']:
|
||||
groupby_accounts.setdefault(
|
||||
equity_unaffected_account.id,
|
||||
{col_group_key: {'unaffected_earnings': {}} for col_group_key in options['column_groups']},
|
||||
)
|
||||
if unaffected_earnings := groupby_company.get(column_group_key):
|
||||
if groupby_accounts[equity_unaffected_account.id][column_group_key].get('unaffected_earnings'):
|
||||
for key in ['amount_currency', 'debit', 'credit', 'balance']:
|
||||
groupby_accounts[equity_unaffected_account.id][column_group_key]['unaffected_earnings'][key] += unaffected_earnings[key]
|
||||
else:
|
||||
groupby_accounts[equity_unaffected_account.id][column_group_key]['unaffected_earnings'] = unaffected_earnings
|
||||
|
||||
# Retrieve the accounts to browse.
|
||||
# groupby_accounts.keys() contains all account ids affected by:
|
||||
# - the amls in the current period.
|
||||
# - the amls affecting the initial balance.
|
||||
# - the unaffected earnings allocation.
|
||||
# Note a search is done instead of a browse to preserve the table ordering.
|
||||
if groupby_accounts:
|
||||
accounts = self.env['account.account'].search([('id', 'in', list(groupby_accounts.keys()))])
|
||||
else:
|
||||
accounts = []
|
||||
|
||||
return [(account, groupby_accounts[account.id]) for account in accounts]
|
||||
|
||||
def _get_query_sums(self, report, options) -> SQL:
|
||||
""" Construct a query retrieving all the aggregated sums to build the report. It includes:
|
||||
- sums for all accounts.
|
||||
- sums for the initial balances.
|
||||
- sums for the unaffected earnings.
|
||||
- sums for the tax declaration.
|
||||
:return: query as SQL object
|
||||
"""
|
||||
options_by_column_group = report._split_options_per_column_group(options)
|
||||
|
||||
queries = []
|
||||
|
||||
# ============================================
|
||||
# 1) Get sums for all accounts.
|
||||
# ============================================
|
||||
for column_group_key, options_group in options_by_column_group.items():
|
||||
|
||||
# Sum is computed including the initial balance of the accounts configured to do so, unless a special option key is used
|
||||
# (this is required for trial balance, which is based on general ledger)
|
||||
sum_date_scope = 'strict_range' if options_group.get('general_ledger_strict_range') else 'from_beginning'
|
||||
|
||||
query_domain = []
|
||||
|
||||
if not options_group.get('general_ledger_strict_range'):
|
||||
date_from = fields.Date.from_string(options_group['date']['date_from'])
|
||||
current_fiscalyear_dates = self.env.company.compute_fiscalyear_dates(date_from)
|
||||
query_domain += [
|
||||
'|',
|
||||
('date', '>=', current_fiscalyear_dates['date_from']),
|
||||
('account_id.include_initial_balance', '=', True),
|
||||
]
|
||||
|
||||
if options_group.get('export_mode') == 'print' and options_group.get('filter_search_bar'):
|
||||
query_domain.append(('account_id', 'ilike', options_group['filter_search_bar']))
|
||||
|
||||
if options_group.get('include_current_year_in_unaff_earnings'):
|
||||
query_domain += [('account_id.include_initial_balance', '=', True)]
|
||||
|
||||
query = report._get_report_query(options_group, sum_date_scope, domain=query_domain)
|
||||
queries.append(SQL(
|
||||
"""
|
||||
SELECT
|
||||
account_move_line.account_id AS groupby,
|
||||
'sum' AS key,
|
||||
MAX(account_move_line.date) AS max_date,
|
||||
%(column_group_key)s AS column_group_key,
|
||||
COALESCE(SUM(account_move_line.amount_currency), 0.0) AS amount_currency,
|
||||
SUM(%(debit_select)s) AS debit,
|
||||
SUM(%(credit_select)s) AS credit,
|
||||
SUM(%(balance_select)s) AS balance
|
||||
FROM %(table_references)s
|
||||
%(currency_table_join)s
|
||||
WHERE %(search_condition)s
|
||||
GROUP BY account_move_line.account_id
|
||||
""",
|
||||
column_group_key=column_group_key,
|
||||
table_references=query.from_clause,
|
||||
debit_select=report._currency_table_apply_rate(SQL("account_move_line.debit")),
|
||||
credit_select=report._currency_table_apply_rate(SQL("account_move_line.credit")),
|
||||
balance_select=report._currency_table_apply_rate(SQL("account_move_line.balance")),
|
||||
currency_table_join=report._currency_table_aml_join(options_group),
|
||||
search_condition=query.where_clause,
|
||||
))
|
||||
|
||||
# ============================================
|
||||
# 2) Get sums for the unaffected earnings.
|
||||
# ============================================
|
||||
if not options_group.get('general_ledger_strict_range'):
|
||||
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)
|
||||
query = report._get_report_query(new_options, 'strict_range', domain=unaff_earnings_domain)
|
||||
queries.append(SQL(
|
||||
"""
|
||||
SELECT
|
||||
account_move_line.company_id AS groupby,
|
||||
'unaffected_earnings' AS key,
|
||||
NULL AS max_date,
|
||||
%(column_group_key)s AS column_group_key,
|
||||
COALESCE(SUM(account_move_line.amount_currency), 0.0) AS amount_currency,
|
||||
SUM(%(debit_select)s) AS debit,
|
||||
SUM(%(credit_select)s) AS credit,
|
||||
SUM(%(balance_select)s) AS balance
|
||||
FROM %(table_references)s
|
||||
%(currency_table_join)s
|
||||
WHERE %(search_condition)s
|
||||
GROUP BY account_move_line.company_id
|
||||
""",
|
||||
column_group_key=column_group_key,
|
||||
table_references=query.from_clause,
|
||||
debit_select=report._currency_table_apply_rate(SQL("account_move_line.debit")),
|
||||
credit_select=report._currency_table_apply_rate(SQL("account_move_line.credit")),
|
||||
balance_select=report._currency_table_apply_rate(SQL("account_move_line.balance")),
|
||||
currency_table_join=report._currency_table_aml_join(options_group),
|
||||
search_condition=query.where_clause,
|
||||
))
|
||||
|
||||
return SQL(" UNION ALL ").join(queries)
|
||||
|
||||
def _get_options_unaffected_earnings(self, options):
|
||||
''' Create options used to compute the unaffected earnings.
|
||||
The unaffected earnings are the amount of benefits/loss that have not been allocated to
|
||||
another account in the previous fiscal years.
|
||||
The resulting dates domain will be:
|
||||
[
|
||||
('date' <= fiscalyear['date_from'] - 1),
|
||||
('account_id.include_initial_balance', '=', False),
|
||||
]
|
||||
:param options: The report options.
|
||||
:return: A copy of the options.
|
||||
'''
|
||||
new_options = options.copy()
|
||||
new_options.pop('filter_search_bar', None)
|
||||
fiscalyear_dates = self.env.company.compute_fiscalyear_dates(fields.Date.from_string(options['date']['date_from']))
|
||||
|
||||
# Trial balance uses the options key, general ledger does not
|
||||
new_date_to = fields.Date.from_string(new_options['date']['date_to']) if options.get('include_current_year_in_unaff_earnings') else fiscalyear_dates['date_from'] - timedelta(days=1)
|
||||
|
||||
new_options['date'] = self.env['account.report']._get_dates_period(None, new_date_to, 'single')
|
||||
|
||||
return new_options
|
||||
|
||||
def _get_aml_values(self, report, options, expanded_account_ids, offset=0, limit=None):
|
||||
rslt = {account_id: {} for account_id in expanded_account_ids}
|
||||
aml_query = self._get_query_amls(report, options, expanded_account_ids, offset=offset, limit=limit)
|
||||
self._cr.execute(aml_query)
|
||||
aml_results_number = 0
|
||||
has_more = False
|
||||
for aml_result in self._cr.dictfetchall():
|
||||
aml_results_number += 1
|
||||
if aml_results_number == limit:
|
||||
has_more = True
|
||||
break
|
||||
|
||||
# For asset_receivable the name will already contains the ref with the _compute_name
|
||||
if aml_result['ref'] and aml_result['account_type'] != 'asset_receivable':
|
||||
aml_result['communication'] = f"{aml_result['ref']} - {aml_result['name']}"
|
||||
else:
|
||||
aml_result['communication'] = aml_result['name']
|
||||
|
||||
# The same aml can return multiple results when using account_report_cash_basis module, if the receivable/payable
|
||||
# is reconciled with multiple payments. In this case, the date shown for the move lines actually corresponds to the
|
||||
# reconciliation date. In order to keep distinct lines in this case, we include date in the grouping key.
|
||||
aml_key = (aml_result['id'], aml_result['date'])
|
||||
|
||||
account_result = rslt[aml_result['account_id']]
|
||||
if not aml_key in account_result:
|
||||
account_result[aml_key] = {col_group_key: {} for col_group_key in options['column_groups']}
|
||||
|
||||
already_present_result = account_result[aml_key][aml_result['column_group_key']]
|
||||
if already_present_result:
|
||||
# In case the same move line gives multiple results at the same date, add them.
|
||||
# This does not happen in standard GL report, but could because of custom shadowing of account.move.line,
|
||||
# such as the one done in account_report_cash_basis (if the payable/receivable line is reconciled twice at the same date).
|
||||
already_present_result['debit'] += aml_result['debit']
|
||||
already_present_result['credit'] += aml_result['credit']
|
||||
already_present_result['balance'] += aml_result['balance']
|
||||
already_present_result['amount_currency'] += aml_result['amount_currency']
|
||||
else:
|
||||
account_result[aml_key][aml_result['column_group_key']] = aml_result
|
||||
|
||||
return rslt, has_more
|
||||
|
||||
def _get_query_amls(self, report, options, expanded_account_ids, offset=0, limit=None) -> SQL:
|
||||
""" Construct a query retrieving the account.move.lines when expanding a report line with or without the load
|
||||
more.
|
||||
:param options: The report options.
|
||||
:param expanded_account_ids: The account.account ids corresponding to consider. If None, match every account.
|
||||
:param offset: The offset of the query (used by the load more).
|
||||
:param limit: The limit of the query (used by the load more).
|
||||
:return: (query, params)
|
||||
"""
|
||||
additional_domain = [('account_id', 'in', expanded_account_ids)] if expanded_account_ids is not None else None
|
||||
queries = []
|
||||
journal_name = self.env['account.journal']._field_to_sql('journal', 'name')
|
||||
for column_group_key, group_options in report._split_options_per_column_group(options).items():
|
||||
# Get sums for the account move lines.
|
||||
# period: [('date' <= options['date_to']), ('date', '>=', options['date_from'])]
|
||||
query = report._get_report_query(group_options, domain=additional_domain, date_scope='strict_range')
|
||||
account_alias = query.join(lhs_alias='account_move_line', lhs_column='account_id', rhs_table='account_account', rhs_column='id', link='account_id')
|
||||
account_code = self.env['account.account']._field_to_sql(account_alias, 'code', query)
|
||||
account_name = self.env['account.account']._field_to_sql(account_alias, 'name')
|
||||
account_type = self.env['account.account']._field_to_sql(account_alias, 'account_type')
|
||||
|
||||
query = SQL(
|
||||
'''
|
||||
SELECT
|
||||
account_move_line.id,
|
||||
account_move_line.date,
|
||||
account_move_line.date_maturity,
|
||||
account_move_line.name,
|
||||
account_move_line.ref,
|
||||
account_move_line.company_id,
|
||||
account_move_line.account_id,
|
||||
account_move_line.payment_id,
|
||||
account_move_line.partner_id,
|
||||
account_move_line.currency_id,
|
||||
account_move_line.amount_currency,
|
||||
COALESCE(account_move_line.invoice_date, account_move_line.date) AS invoice_date,
|
||||
account_move_line.date AS date,
|
||||
%(debit_select)s AS debit,
|
||||
%(credit_select)s AS credit,
|
||||
%(balance_select)s AS balance,
|
||||
move.name AS move_name,
|
||||
company.currency_id AS company_currency_id,
|
||||
partner.name AS partner_name,
|
||||
move.move_type AS move_type,
|
||||
%(account_code)s AS account_code,
|
||||
%(account_name)s AS account_name,
|
||||
%(account_type)s AS account_type,
|
||||
journal.code AS journal_code,
|
||||
%(journal_name)s AS journal_name,
|
||||
full_rec.id AS full_rec_name,
|
||||
%(column_group_key)s AS column_group_key
|
||||
FROM %(table_references)s
|
||||
JOIN account_move move ON move.id = account_move_line.move_id
|
||||
%(currency_table_join)s
|
||||
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_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
|
||||
WHERE %(search_condition)s
|
||||
ORDER BY account_move_line.date, account_move_line.move_name, account_move_line.id
|
||||
''',
|
||||
account_code=account_code,
|
||||
account_name=account_name,
|
||||
account_type=account_type,
|
||||
journal_name=journal_name,
|
||||
column_group_key=column_group_key,
|
||||
table_references=query.from_clause,
|
||||
currency_table_join=report._currency_table_aml_join(group_options),
|
||||
debit_select=report._currency_table_apply_rate(SQL("account_move_line.debit")),
|
||||
credit_select=report._currency_table_apply_rate(SQL("account_move_line.credit")),
|
||||
balance_select=report._currency_table_apply_rate(SQL("account_move_line.balance")),
|
||||
search_condition=query.where_clause,
|
||||
)
|
||||
queries.append(query)
|
||||
|
||||
full_query = SQL(" UNION ALL ").join(SQL("(%s)", query) for query in queries)
|
||||
|
||||
if offset:
|
||||
full_query = SQL('%s OFFSET %s ', full_query, offset)
|
||||
if limit:
|
||||
full_query = SQL('%s LIMIT %s ', full_query, limit)
|
||||
|
||||
return full_query
|
||||
|
||||
def _get_initial_balance_values(self, report, account_ids, options):
|
||||
"""
|
||||
Get sums for the initial balance.
|
||||
"""
|
||||
queries = []
|
||||
for column_group_key, options_group in report._split_options_per_column_group(options).items():
|
||||
new_options = self._get_options_initial_balance(options_group)
|
||||
domain = [
|
||||
('account_id', 'in', account_ids),
|
||||
]
|
||||
if not new_options.get('general_ledger_strict_range'):
|
||||
domain += [
|
||||
'|',
|
||||
('date', '>=', new_options['date']['date_from']),
|
||||
('account_id.include_initial_balance', '=', True),
|
||||
]
|
||||
if new_options.get('include_current_year_in_unaff_earnings'):
|
||||
domain += [('account_id.include_initial_balance', '=', True)]
|
||||
query = report._get_report_query(new_options, 'from_beginning', domain=domain)
|
||||
queries.append(SQL(
|
||||
"""
|
||||
SELECT
|
||||
account_move_line.account_id AS groupby,
|
||||
'initial_balance' AS key,
|
||||
NULL AS max_date,
|
||||
%(column_group_key)s AS column_group_key,
|
||||
COALESCE(SUM(account_move_line.amount_currency), 0.0) AS amount_currency,
|
||||
SUM(%(debit_select)s) AS debit,
|
||||
SUM(%(credit_select)s) AS credit,
|
||||
SUM(%(balance_select)s) AS balance
|
||||
FROM %(table_references)s
|
||||
%(currency_table_join)s
|
||||
WHERE %(search_condition)s
|
||||
GROUP BY account_move_line.account_id
|
||||
""",
|
||||
column_group_key=column_group_key,
|
||||
table_references=query.from_clause,
|
||||
debit_select=report._currency_table_apply_rate(SQL("account_move_line.debit")),
|
||||
credit_select=report._currency_table_apply_rate(SQL("account_move_line.credit")),
|
||||
balance_select=report._currency_table_apply_rate(SQL("account_move_line.balance")),
|
||||
currency_table_join=report._currency_table_aml_join(options_group),
|
||||
search_condition=query.where_clause,
|
||||
))
|
||||
|
||||
self._cr.execute(SQL(" UNION ALL ").join(queries))
|
||||
|
||||
init_balance_by_col_group = {
|
||||
account_id: {column_group_key: {} for column_group_key in options['column_groups']}
|
||||
for account_id in account_ids
|
||||
}
|
||||
for result in self._cr.dictfetchall():
|
||||
init_balance_by_col_group[result['groupby']][result['column_group_key']] = result
|
||||
|
||||
accounts = self.env['account.account'].browse(account_ids)
|
||||
return {
|
||||
account.id: (account, init_balance_by_col_group[account.id])
|
||||
for account in accounts
|
||||
}
|
||||
|
||||
def _get_options_initial_balance(self, options):
|
||||
""" Create options used to compute the initial balances.
|
||||
The initial balances depict the current balance of the accounts at the beginning of
|
||||
the selected period in the report.
|
||||
The resulting dates domain will be:
|
||||
[
|
||||
('date' <= options['date_from'] - 1),
|
||||
'|',
|
||||
('date' >= fiscalyear['date_from']),
|
||||
('account_id.include_initial_balance', '=', True)
|
||||
]
|
||||
:param options: The report options.
|
||||
:return: A copy of the options.
|
||||
"""
|
||||
#pylint: disable=sql-injection
|
||||
new_options = options.copy()
|
||||
date_to = new_options['comparison']['periods'][-1]['date_from'] if new_options.get('comparison', {}).get('periods') else new_options['date']['date_from']
|
||||
new_date_to = fields.Date.from_string(date_to) - timedelta(days=1)
|
||||
|
||||
# Date from computation
|
||||
# We have two case:
|
||||
# 1) We are choosing a date that starts at the beginning of a fiscal year and we want the initial period to be
|
||||
# the previous fiscal year
|
||||
# 2) We are choosing a date that starts in the middle of a fiscal year and in that case we want the initial period
|
||||
# to be the beginning of the fiscal year
|
||||
date_from = fields.Date.from_string(new_options['date']['date_from'])
|
||||
current_fiscalyear_dates = self.env.company.compute_fiscalyear_dates(date_from)
|
||||
|
||||
if date_from == current_fiscalyear_dates['date_from']:
|
||||
# We want the previous fiscal year
|
||||
previous_fiscalyear_dates = self.env.company.compute_fiscalyear_dates(date_from - timedelta(days=1))
|
||||
new_date_from = previous_fiscalyear_dates['date_from']
|
||||
include_current_year_in_unaff_earnings = True
|
||||
else:
|
||||
# We want the current fiscal year
|
||||
new_date_from = current_fiscalyear_dates['date_from']
|
||||
include_current_year_in_unaff_earnings = False
|
||||
|
||||
new_options['date'] = self.env['account.report']._get_dates_period(
|
||||
new_date_from,
|
||||
new_date_to,
|
||||
'range',
|
||||
)
|
||||
new_options['include_current_year_in_unaff_earnings'] = include_current_year_in_unaff_earnings
|
||||
|
||||
return new_options
|
||||
|
||||
####################################################
|
||||
# COLUMN/LINE HELPERS
|
||||
####################################################
|
||||
def _get_account_title_line(self, report, options, account, has_lines, eval_dict):
|
||||
line_columns = []
|
||||
for column in options['columns']:
|
||||
col_value = eval_dict.get(column['column_group_key'], {}).get(column['expression_label'])
|
||||
col_expr_label = column['expression_label']
|
||||
|
||||
value = None if col_value is None or (col_expr_label == 'amount_currency' and not account.currency_id) else col_value
|
||||
|
||||
line_columns.append(report._build_column_dict(
|
||||
value,
|
||||
column,
|
||||
options=options,
|
||||
currency=account.currency_id if col_expr_label == 'amount_currency' else None,
|
||||
))
|
||||
|
||||
line_id = report._get_generic_line_id('account.account', account.id)
|
||||
is_in_unfolded_lines = any(
|
||||
report._get_res_id_from_line_id(line_id, 'account.account') == account.id
|
||||
for line_id in options.get('unfolded_lines')
|
||||
)
|
||||
return {
|
||||
'id': line_id,
|
||||
'name': account.display_name,
|
||||
'columns': line_columns,
|
||||
'level': 1,
|
||||
'unfoldable': has_lines,
|
||||
'unfolded': has_lines and (is_in_unfolded_lines or options.get('unfold_all')),
|
||||
'expand_function': '_report_expand_unfoldable_line_general_ledger',
|
||||
}
|
||||
|
||||
def _get_aml_line(self, report, parent_line_id, options, eval_dict, init_bal_by_col_group):
|
||||
line_columns = []
|
||||
for column in options['columns']:
|
||||
col_expr_label = column['expression_label']
|
||||
col_value = eval_dict[column['column_group_key']].get(col_expr_label)
|
||||
col_currency = None
|
||||
|
||||
if col_value is not None:
|
||||
if col_expr_label == 'amount_currency':
|
||||
col_currency = self.env['res.currency'].browse(eval_dict[column['column_group_key']]['currency_id'])
|
||||
col_value = None if col_currency == self.env.company.currency_id else col_value
|
||||
elif col_expr_label == 'balance':
|
||||
col_value += (init_bal_by_col_group[column['column_group_key']] or 0)
|
||||
|
||||
line_columns.append(report._build_column_dict(
|
||||
col_value,
|
||||
column,
|
||||
options=options,
|
||||
currency=col_currency,
|
||||
))
|
||||
|
||||
aml_id = None
|
||||
move_name = None
|
||||
caret_type = None
|
||||
for column_group_dict in eval_dict.values():
|
||||
aml_id = column_group_dict.get('id', '')
|
||||
if aml_id:
|
||||
if column_group_dict.get('payment_id'):
|
||||
caret_type = 'account.payment'
|
||||
else:
|
||||
caret_type = 'account.move.line'
|
||||
move_name = column_group_dict['move_name']
|
||||
date = str(column_group_dict.get('date', ''))
|
||||
break
|
||||
|
||||
return {
|
||||
'id': report._get_generic_line_id('account.move.line', aml_id, parent_line_id=parent_line_id, markup=date),
|
||||
'caret_options': caret_type,
|
||||
'parent_id': parent_line_id,
|
||||
'name': move_name,
|
||||
'columns': line_columns,
|
||||
'level': 3,
|
||||
}
|
||||
|
||||
@api.model
|
||||
def _get_total_line(self, report, options, eval_dict):
|
||||
line_columns = []
|
||||
for column in options['columns']:
|
||||
col_value = eval_dict[column['column_group_key']].get(column['expression_label'])
|
||||
col_value = None if col_value is None else col_value
|
||||
|
||||
line_columns.append(report._build_column_dict(col_value, column, options=options))
|
||||
|
||||
return {
|
||||
'id': report._get_generic_line_id(None, None, markup='total'),
|
||||
'name': _('Total'),
|
||||
'level': 1,
|
||||
'columns': line_columns,
|
||||
}
|
||||
|
||||
def caret_option_audit_tax(self, options, params):
|
||||
return self.env['account.generic.tax.report.handler'].caret_option_audit_tax(options, params)
|
||||
|
||||
def _report_expand_unfoldable_line_general_ledger(self, line_dict_id, groupby, options, progress, offset, unfold_all_batch_data=None):
|
||||
def init_load_more_progress(line_dict):
|
||||
return {
|
||||
column['column_group_key']: line_col.get('no_format', 0)
|
||||
for column, line_col in zip(options['columns'], line_dict['columns'])
|
||||
if column['expression_label'] == 'balance'
|
||||
}
|
||||
|
||||
report = self.env.ref('at_accounting.general_ledger_report')
|
||||
model, model_id = report._get_model_info_from_id(line_dict_id)
|
||||
|
||||
if model != 'account.account':
|
||||
raise UserError(_("Wrong ID for general ledger line to expand: %s", line_dict_id))
|
||||
|
||||
lines = []
|
||||
|
||||
# Get initial balance
|
||||
if offset == 0:
|
||||
if unfold_all_batch_data:
|
||||
account, init_balance_by_col_group = unfold_all_batch_data['initial_balances'][model_id]
|
||||
else:
|
||||
account, init_balance_by_col_group = self._get_initial_balance_values(report, [model_id], options)[model_id]
|
||||
|
||||
initial_balance_line = report._get_partner_and_general_ledger_initial_balance_line(options, line_dict_id, init_balance_by_col_group, account.currency_id)
|
||||
|
||||
if initial_balance_line:
|
||||
lines.append(initial_balance_line)
|
||||
|
||||
# For the first expansion of the line, the initial balance line gives the progress
|
||||
progress = init_load_more_progress(initial_balance_line)
|
||||
|
||||
# Get move lines
|
||||
limit_to_load = report.load_more_limit + 1 if report.load_more_limit and options['export_mode'] != 'print' else None
|
||||
if unfold_all_batch_data:
|
||||
aml_results = unfold_all_batch_data['aml_results'][model_id]
|
||||
has_more = unfold_all_batch_data['has_more'].get(model_id, False)
|
||||
else:
|
||||
aml_results, has_more = self._get_aml_values(report, options, [model_id], offset=offset, limit=limit_to_load)
|
||||
aml_results = aml_results[model_id]
|
||||
|
||||
next_progress = progress
|
||||
for aml_result in aml_results.values():
|
||||
new_line = self._get_aml_line(report, line_dict_id, options, aml_result, next_progress)
|
||||
lines.append(new_line)
|
||||
next_progress = init_load_more_progress(new_line)
|
||||
|
||||
return {
|
||||
'lines': lines,
|
||||
'offset_increment': report.load_more_limit,
|
||||
'has_more': has_more,
|
||||
'progress': next_progress,
|
||||
}
|
||||
Reference in New Issue
Block a user