251 lines
14 KiB
Python
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,
|
|
}
|