237 lines
11 KiB
Python
237 lines
11 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
from odoo import api, fields, models, _
|
|
from odoo.tools import SQL
|
|
|
|
from collections import OrderedDict
|
|
from datetime import timedelta
|
|
|
|
|
|
class L10n_ClReportHandler(models.AbstractModel):
|
|
_name = 'l10n_cl.report.handler'
|
|
_inherit = ['account.balance.sheet.report.handler']
|
|
_description = 'Chilean Report Custom Handler'
|
|
|
|
def _dynamic_lines_generator(self, report, options, all_column_groups_expression_totals, warnings=None):
|
|
# dict of the form {account_id: {column_group_key: {expression_label: value}}}
|
|
lines_dict = {}
|
|
|
|
# totals dictionaries: dicts of the form {column_group_key: {expression_label: total_value}}
|
|
subtotals_dict = {}
|
|
fiscalyear_result_dict = {}
|
|
previous_years_unallocated_earnings_dict = {}
|
|
totals_dict = {}
|
|
|
|
# Build query
|
|
query_list = []
|
|
for column_group_key, column_group_options in report._split_options_per_column_group(options).items():
|
|
query = self._prepare_query(report, column_group_options, column_group_key)
|
|
query_list.append(SQL("(%s)", query))
|
|
|
|
# Set defaults here since the results of the query for this column_group_key might be empty
|
|
subtotals_dict[column_group_key] = dict.fromkeys([col['expression_label'] for col in column_group_options['columns']], 0.0)
|
|
|
|
full_query = SQL(" UNION ALL ").join(query_list)
|
|
self.env.cr.execute(full_query)
|
|
|
|
# Fill lines and subtotals dictionaries
|
|
for result in self.env.cr.dictfetchall():
|
|
account_id = result['id']
|
|
column_group_key = result['column_group_key']
|
|
|
|
lines_dict.setdefault(account_id, {})
|
|
|
|
lines_dict[account_id]['full_name'] = f"{result['code']} {result['name']}" if result['code'] else result['name']
|
|
lines_dict[account_id][column_group_key] = result
|
|
|
|
for expression_label in subtotals_dict[column_group_key]:
|
|
subtotals_dict[column_group_key][expression_label] += result[expression_label]
|
|
|
|
# Compute other 'total' lines
|
|
for column_group_key, column_group_options in report._split_options_per_column_group(options).items():
|
|
subtotals = subtotals_dict[column_group_key]
|
|
|
|
fiscalyear_result = self._calculate_fiscalyear_result(column_group_options, subtotals)
|
|
fiscalyear_result_dict[column_group_key] = fiscalyear_result
|
|
|
|
previous_years_unallocated_earnings = self._calculate_previous_years_unallocated_earnings(report, column_group_options)
|
|
previous_years_unallocated_earnings_dict[column_group_key] = previous_years_unallocated_earnings
|
|
|
|
totals = self._calculate_totals(column_group_options, subtotals, fiscalyear_result, previous_years_unallocated_earnings)
|
|
totals_dict[column_group_key] = totals
|
|
|
|
lines = []
|
|
for account_id, line_vals in lines_dict.items():
|
|
line = self._create_report_line(report, options, line_vals, account_id)
|
|
lines.append((0, line))
|
|
|
|
# Subtotals line
|
|
lines.append((0, self._create_report_total_line(report, options, subtotals_dict, _("Subtotal"), 2, markup='subtotals')))
|
|
# Fiscal year results line
|
|
lines.append((0, self._create_report_total_line(report, options, fiscalyear_result_dict, _("Profit and Loss"), 3, markup='fiscalyear_result')))
|
|
# Previous year unallocated earnings line
|
|
unalloc_line = self._create_report_total_line(
|
|
report, options, previous_years_unallocated_earnings_dict, _("Previous years unallocated earnings"), 3, markup='previous_years_unallocated_earnings')
|
|
if any(not self.env.company.currency_id.is_zero(column['no_format']) for column in unalloc_line['columns']):
|
|
lines.append((0, unalloc_line))
|
|
# General total line
|
|
lines.append((0, self._create_report_total_line(report, options, totals_dict, _("Total"), 1, markup='general_total')))
|
|
|
|
return lines
|
|
|
|
@api.model
|
|
def _prepare_query(self, report, options, column_group_key) -> SQL:
|
|
domain = [
|
|
'|',
|
|
('date', '>=', options['date']['date_from']),
|
|
'&',
|
|
('account_id.include_initial_balance', '=', True),
|
|
('account_id.account_type', '!=', 'equity_unaffected'),
|
|
]
|
|
query = report._get_report_query(options, 'from_beginning', domain=domain)
|
|
query.add_join('JOIN', alias='aa', table='account_account', condition=SQL('account_move_line.account_id = aa.id'))
|
|
account_code = self.env['account.account']._field_to_sql('aa', 'code', query)
|
|
account_name = self.env['account.account']._field_to_sql('aa', 'name')
|
|
|
|
sql_query = SQL(
|
|
"""
|
|
SELECT %(column_group_key)s AS column_group_key,
|
|
aa.id, %(account_code)s AS code, %(account_name)s AS name,
|
|
SUM(account_move_line.debit) AS debit,
|
|
SUM(account_move_line.credit) AS credit,
|
|
GREATEST(SUM(account_move_line.balance), 0) AS debitor,
|
|
GREATEST(SUM(-account_move_line.balance), 0) AS creditor,
|
|
CASE WHEN %(bs)s THEN GREATEST(SUM(account_move_line.balance), 0) ELSE 0 END AS assets,
|
|
CASE WHEN %(bs)s THEN GREATEST(SUM(-account_move_line.balance), 0) ELSE 0 END AS liabilities,
|
|
CASE WHEN %(pnl)s THEN GREATEST(SUM(account_move_line.balance), 0) ELSE 0 END AS loss,
|
|
CASE WHEN %(pnl)s THEN GREATEST(SUM(-account_move_line.balance), 0) ELSE 0 END AS gain
|
|
FROM %(table_references)s
|
|
WHERE %(search_condition)s
|
|
GROUP BY aa.id, %(account_code)s, %(account_name)s
|
|
ORDER BY %(account_code)s
|
|
""",
|
|
column_group_key=column_group_key,
|
|
account_code=account_code,
|
|
account_name=account_name,
|
|
table_references=query.from_clause,
|
|
search_condition=query.where_clause,
|
|
bs=SQL("aa.account_type LIKE 'asset%%' OR aa.account_type LIKE 'liability%%' OR aa.account_type LIKE 'equity%%'"),
|
|
pnl=SQL("aa.account_type LIKE 'expense%%' OR aa.account_type LIKE 'income%%'"),
|
|
)
|
|
return sql_query
|
|
|
|
def _create_report_line(self, report, options, vals, vals_id):
|
|
""" Create a standard (non total) line for the report
|
|
:param options: report options
|
|
:param vals: values necessary for the line
|
|
:param vals_id: id of the account
|
|
"""
|
|
|
|
columns = []
|
|
for column in options['columns']:
|
|
col_value = vals.get(column['column_group_key'], {}).get(column['expression_label'], False)
|
|
columns.append(report._build_column_dict(col_value, column, options=options))
|
|
|
|
return {
|
|
'id': report._get_generic_line_id('account.account', vals_id),
|
|
'caret_options': 'account.account',
|
|
'name': vals['full_name'],
|
|
'columns': columns,
|
|
'level': 2,
|
|
}
|
|
|
|
def _create_report_total_line(self, report, options, total_vals, name, level=1, markup=''):
|
|
""" Create a total line for the report
|
|
:param options: report options
|
|
:param total_vals: values necessary for the line
|
|
:param name: name given to the total line
|
|
:param level: the level of the total line
|
|
"""
|
|
if not total_vals:
|
|
total_vals = {}
|
|
|
|
columns = []
|
|
for column in options['columns']:
|
|
col_value = total_vals.get(column['column_group_key'], {}).get(column['expression_label'], False)
|
|
columns.append(report._build_column_dict(col_value, column, options=options))
|
|
return {
|
|
'id': report._get_generic_line_id(None, None, markup=markup),
|
|
'name': name,
|
|
'level': level,
|
|
'columns': columns,
|
|
}
|
|
|
|
def _calculate_fiscalyear_result(self, options, subtotal_line):
|
|
exercise_result = OrderedDict.fromkeys([col['expression_label'] for col in options['columns']], 0)
|
|
if subtotal_line['gain'] >= subtotal_line['loss']:
|
|
exercise_result['loss'] = subtotal_line['gain'] - subtotal_line['loss']
|
|
exercise_result['liabilities'] = exercise_result['loss']
|
|
else:
|
|
exercise_result['gain'] = subtotal_line['loss'] - subtotal_line['gain']
|
|
exercise_result['assets'] = exercise_result['gain']
|
|
return exercise_result
|
|
|
|
def _calculate_unallocated_earnings_value(self, report, options):
|
|
"""
|
|
Get all the unallocated earnings value from the previous fiscal years.
|
|
The past moves that target Income and expense account (+ special type of expenses)
|
|
are summed, and the allocated earnings are removed by summing the balances
|
|
of the moves targeting the special account 'Undistributed Profits/Losses'.
|
|
"""
|
|
new_options = options.copy()
|
|
date_from_str = new_options.get('date', {}).get('date_from', '')
|
|
date_from = fields.Date.from_string(date_from_str) or fields.Date.today()
|
|
fiscal_dates = self.env.company.compute_fiscalyear_dates(date_from)
|
|
new_options['date'] = report._get_dates_period(
|
|
None,
|
|
fiscal_dates['date_from'] - timedelta(days=1),
|
|
'range',
|
|
period_type='custom')
|
|
query = report._get_report_query(new_options, 'strict_range')
|
|
account_types = (
|
|
'equity_unaffected',
|
|
'income',
|
|
'income_other',
|
|
'expense_direct_cost',
|
|
'expense',
|
|
'expense_depreciation'
|
|
)
|
|
sql_query = SQL(
|
|
"""
|
|
SELECT -SUM(account_move_line.balance) as unaffected_earnings
|
|
FROM account_account AS aa, %(table_references)s
|
|
WHERE %(search_condition)s
|
|
AND aa.id = account_move_line.account_id
|
|
AND aa.account_type IN %(account_types)s
|
|
""".strip('\n'),
|
|
table_references=query.from_clause,
|
|
search_condition=query.where_clause,
|
|
account_types=account_types,
|
|
)
|
|
self.env.cr.execute(sql_query)
|
|
value = self.env.cr.fetchone()[0] or 0.0
|
|
return self.env.company.currency_id.round(value)
|
|
|
|
def _calculate_previous_years_unallocated_earnings(self, report, options):
|
|
earning = self._calculate_unallocated_earnings_value(report, options)
|
|
if not earning:
|
|
return {}
|
|
|
|
sum_col, sold_col, liabilities_sign = {
|
|
True: ('debit', 'debitor', -1),
|
|
False: ('credit', 'creditor', 1),
|
|
}.get(earning < 0)
|
|
|
|
abs_earning = abs(earning)
|
|
row = OrderedDict.fromkeys([col['expression_label'] for col in options['columns']], 0)
|
|
row[sum_col] = abs_earning
|
|
row[sold_col] = abs_earning
|
|
row['liabilities'] = liabilities_sign * abs_earning
|
|
return row
|
|
|
|
def _calculate_totals(self, options, subtotal_line, exercise_result_line, previous_years_unallocated_earnings):
|
|
parts = [subtotal_line, exercise_result_line]
|
|
if previous_years_unallocated_earnings:
|
|
parts.append(previous_years_unallocated_earnings)
|
|
column_expression_labels = [col['expression_label'] for col in options['columns']]
|
|
return OrderedDict([(col, sum([part.get(col, 0) for part in parts])) for col in column_expression_labels])
|