Files
enterprise-addons/account_reports/models/account.py
T

251 lines
14 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models
from odoo.tools import SQL, OrderedSet, html2plaintext
from odoo.addons.account_reports.models.account_audit_account_status import STATUS_SELECTION
class AccountAccount(models.Model):
_inherit = "account.account"
exclude_provision_currency_ids = fields.Many2many('res.currency', relation='account_account_exclude_res_currency_provision', help="Whether or not we have to make provisions for the selected foreign currencies.")
budget_item_ids = fields.One2many(comodel_name='account.report.budget.item', inverse_name='account_id') # To use it in the domain when adding accounts from the report
audit_debit = fields.Monetary(string="Debit", compute="_compute_audit_period", currency_field="company_currency_id", search="_search_audit_debit")
audit_credit = fields.Monetary(string="Credit", compute="_compute_audit_period", currency_field="company_currency_id", search="_search_audit_credit")
audit_balance = fields.Monetary(string="Balance", compute="_compute_audit_period", currency_field="company_currency_id", search="_search_audit_balance")
audit_balance_show_warning = fields.Boolean(compute="_compute_audit_balance_show_warning")
audit_previous_balance = fields.Monetary(string="Balance N-1", compute="_compute_audit_period", currency_field="company_currency_id", search="_search_audit_previous_balance")
audit_previous_balance_show_warning = fields.Boolean(compute="_compute_audit_previous_balance_show_warning")
audit_var_n_1 = fields.Monetary(string="Var N-1", compute="_compute_audit_variation", currency_field="company_currency_id", search="_search_audit_var_n_1")
audit_var_percentage = fields.Float(string="Var %", compute="_compute_audit_variation", search="_search_var_percentage", default=False)
audit_status = fields.Selection(selection=STATUS_SELECTION, string="Status", compute="_compute_audit_status", inverse="_inverse_audit_status")
account_status = fields.One2many(string="Account Status", comodel_name='account.audit.account.status', inverse_name='account_id')
last_message = fields.Char(string="Last Message", compute='_compute_last_message')
def _common_audit_search(self, field_name: str, operator: str, value, previous=False):
if isinstance(value, OrderedSet):
value = tuple(value)
query = self._search([])
query.add_where(self._fields[field_name]._condition_to_sql(
field_name, operator, value,
self, query.table, query,
))
result = self.env.execute_query_dict(query.select())
return [('id', 'in', [row['id'] for row in result])]
def _search_audit_debit(self, operator, value):
return self._common_audit_search('audit_debit', operator, value)
def _search_audit_credit(self, operator, value):
return self._common_audit_search('audit_credit', operator, value)
def _search_audit_balance(self, operator, value):
return self._common_audit_search('audit_balance', operator, value)
def _search_audit_previous_balance(self, operator, value):
return self._common_audit_search('audit_previous_balance', operator, value)
def _search_audit_var_n_1(self, operator, value):
return self._common_audit_search('audit_var_n_1', operator, value)
def _search_var_percentage(self, operator, value):
return self._common_audit_search('audit_var_percentage', operator, value)
@api.depends_context('working_file_id')
def _compute_audit_period(self):
working_file = self.env['account.return'].browse(self.env.context.get('working_file_id'))
found_ids = set()
if working_file and self.ids:
query = self._as_query(ordered=False)
for account_id, debit, credit, balance, previous_balance in self.env.execute_query(query.select(*(
self._field_to_sql(query.table, field_name, query)
for field_name in ('id', 'audit_debit', 'audit_credit', 'audit_balance', 'audit_previous_balance')
))):
account = self.browse(account_id)
account.audit_debit = debit
account.audit_credit = credit
account.audit_balance = balance
account.audit_previous_balance = previous_balance
found_ids.add(account_id)
remaining = self - self.browse(found_ids)
remaining.audit_debit = remaining.audit_credit = remaining.audit_balance = remaining.audit_previous_balance = 0
@api.depends('audit_balance', 'audit_previous_balance')
def _compute_audit_variation(self):
for account in self:
account.audit_var_n_1 = account.audit_balance - account.audit_previous_balance
if self.env.company.currency_id.is_zero(account.audit_previous_balance):
account.audit_var_percentage = False
else:
account.audit_var_percentage = (account.audit_balance - account.audit_previous_balance) / account.audit_previous_balance
@api.depends_context('working_file_id')
def _compute_audit_status(self):
working_file = self.env['account.return'].browse(self.env.context.get('working_file_id'))
self.audit_status = 'todo'
if working_file:
create_vals = []
account_status_by_account = {status.account_id: status for status in working_file.audit_account_status_ids}
for account in self:
if account in account_status_by_account:
account.audit_status = account_status_by_account[account].status
else:
create_vals.append({
'account_id': account.id,
'audit_id': working_file.id,
})
if create_vals:
self.env['account.audit.account.status'].create(create_vals)
def _inverse_audit_status(self):
working_file = self.env['account.return'].browse(self.env.context.get('working_file_id'))
if working_file:
account_status_by_account = {status.account_id: status for status in working_file.audit_account_status_ids}
for account in self:
if account in account_status_by_account:
account_status_by_account[account].status = account.audit_status
def _compute_balance_warning(self, balance_field_name, warning_field_name):
for account in self:
if account.internal_group == 'asset':
account[warning_field_name] = account.company_currency_id.compare_amounts(account[balance_field_name], 0) == -1
elif account.internal_group == 'liability':
account[warning_field_name] = account.company_currency_id.compare_amounts(account[balance_field_name], 0) == 1
else:
account[warning_field_name] = False
@api.depends('audit_balance')
def _compute_audit_balance_show_warning(self):
self._compute_balance_warning('audit_balance', 'audit_balance_show_warning')
@api.depends('audit_previous_balance')
def _compute_audit_previous_balance_show_warning(self):
self._compute_balance_warning('audit_previous_balance', 'audit_previous_balance_show_warning')
@api.depends_context('working_file_id')
def _compute_last_message(self):
working_file = self.env['account.return'].browse(self.env.context.get('working_file_id'))
if not working_file:
for account in self:
account.last_message = False
return
self.env['mail.message'].flush_model(['model', 'res_id', 'body'])
self.env['account.report.annotation'].flush_model(['message_id'])
self.env.cr.execute("""
SELECT DISTINCT ON (message.res_id) message.res_id, message.body
FROM mail_message message
JOIN account_report_annotation annotation ON annotation.message_id = message.id
WHERE message.model = 'account.account' AND message.res_id = ANY(%s) AND annotation.date >= %s AND annotation.date <= %s
ORDER BY message.res_id, message.create_date DESC
""", (self.ids, working_file.date_from, working_file.date_to))
last_message_by_account = {
row[0]: html2plaintext(row[1])
for row in self.env.cr.fetchall()
}
for account in self:
account.last_message = last_message_by_account.get(account.id, False)
def _field_to_sql(self, alias, field_expr, query=None) -> SQL:
def add_aml_join(join_alias, date_from, date_to, company_ids):
self.env['account.move.line'].flush_model()
query.add_join(
'LEFT JOIN',
join_alias,
SQL("""
(SELECT SUM(COALESCE(debit, 0.0)) AS debit,
SUM(COALESCE(credit, 0.0)) AS credit,
SUM(COALESCE(balance, 0.0)) AS balance,
account_id
FROM (
SELECT aml.debit, aml.credit, aml.balance, aml.account_id
FROM account_move_line aml
JOIN account_account aml_account ON aml_account.id = aml.account_id
WHERE aml.date <= %(date_to)s
AND NOT aml_account.account_type ILIKE ANY(ARRAY['income%%', 'expense%%', 'equity_unaffected'])
AND aml.company_id = ANY(%(company_ids)s)
AND aml.parent_state = 'posted'
UNION ALL
SELECT aml.debit, aml.credit, aml.balance, aml.account_id
FROM account_move_line aml
JOIN account_account aml_account ON aml_account.id = aml.account_id
WHERE aml.date <= %(date_to)s
AND aml.date >= %(date_from)s
AND aml_account.account_type ILIKE ANY(ARRAY['income%%', 'expense%%', 'equity_unaffected'])
AND aml.company_id = ANY(%(company_ids)s)
AND aml.parent_state = 'posted'
) aml
GROUP BY account_id)
""",
date_from=date_from,
date_to=date_to,
company_ids=company_ids,
),
SQL("%s = %s", SQL.identifier(join_alias, 'account_id'), self._field_to_sql(alias, 'id', query))
)
if field_expr not in ('audit_debit', 'audit_credit', 'audit_balance', 'audit_previous_balance', 'audit_status', 'audit_var_n_1', 'audit_var_percentage'):
return super()._field_to_sql(alias, field_expr, query)
if field_expr == 'audit_status':
query.add_join(
'LEFT JOIN',
'account_audit_account_status',
'account_audit_account_status',
SQL("account_audit_account_status.audit_id = %s AND account_audit_account_status.account_id = %s", self.env.context.get('working_file_id'), self._field_to_sql(alias, 'id', query))
)
return SQL('account_audit_account_status.status')
working_file = self.env['account.return'].browse(self.env.context.get('working_file_id'))
if not working_file:
return SQL()
if field_expr in ('audit_debit', 'audit_credit', 'audit_balance'):
field_name = field_expr.replace('audit_', '')
add_aml_join('current_account_move_lines', working_file.date_from, working_file.date_to, working_file.company_ids.ids)
return SQL("COALESCE(%s, 0.0)", SQL.identifier('current_account_move_lines', field_name))
if field_expr == 'audit_previous_balance':
previous_period_start, previous_period_end = working_file.type_id._get_period_boundaries(working_file.company_id, (working_file.date_from or fields.Date.today()) - relativedelta(days=1))
add_aml_join('prev_account_move_line', previous_period_start, previous_period_end, working_file.company_ids.ids)
return SQL("COALESCE(prev_account_move_line.balance, 0.0)")
if field_expr in ('audit_var_n_1', 'audit_var_percentage'):
previous_period_start, previous_period_end = working_file.type_id._get_period_boundaries(working_file.company_id, (working_file.date_from or fields.Date.today()) - relativedelta(days=1))
add_aml_join('current_account_move_lines', working_file.date_from, working_file.date_to, working_file.company_ids.ids)
add_aml_join('prev_account_move_line', previous_period_start, previous_period_end, working_file.company_ids.ids)
if field_expr == 'audit_var_n_1':
return SQL("(COALESCE(current_account_move_lines.balance, 0) - COALESCE(prev_account_move_line.balance, 0))")
else:
return SQL("""
CASE WHEN prev_account_move_line.balance IS NULL THEN NULL
ELSE (COALESCE(current_account_move_lines.balance, 0) - COALESCE(prev_account_move_line.balance, 0)) / COALESCE(NULLIF(prev_account_move_line.balance, 0), 1) * 100
END
""")
def action_audit_account(self):
domain = [('account_id', 'in', self.ids)]
working_file = self.env['account.return'].browse(self.env.context.get('working_file_id'))
if working_file:
domain += [('date', '>=', working_file.date_from), ('date', '<=', working_file.date_to)]
return {
**self.env['ir.actions.act_window']._for_xml_id("account.action_account_moves_all"),
'domain': domain,
}