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

139 lines
5.2 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import defaultdict
from datetime import datetime
import re
from odoo import api, models
from odoo.tools import SQL
class KpiProvider(models.AbstractModel):
_inherit = 'kpi.provider'
@api.model
def get_account_reports_kpi_summary(self):
return get_kpi_summary(self.env.cr, self.env.uid)
@api.model
def get_kpi_summary(self):
result = super().get_kpi_summary()
result.extend(self.get_account_reports_kpi_summary())
return result
def _tax_return_grouping_key(external_ids, return_info):
external_id = ''
# Determine the external identifier used to categorize the tax returns:
# - use the identifier of the root report if any,
# - otherwise use the identifier of the report,
# - otherwise use the identifier of the return type.
external_id = external_ids.get(('account.report', return_info['root_report_id']))
if not external_id:
external_id = external_ids.get(('account.report', return_info['report_id']))
if not external_id:
external_id = external_ids.get(('account.return.type', return_info['return_type_id']))
if not external_id:
external_id = return_info['tax_return_name']
# Special case for l10n_eu_oss_reports.* that are all merged together.
if external_id.startswith('l10n_eu_oss_reports.'):
external_id = 'l10n_eu_oss_reports'
# property names must match odoo.orm.utils.regex_alphanumeric
return re.sub(r'[^a-z0-9_]', '_', external_id.lower())
def _tax_returns_get_value(tax_returns):
for tax_return in tax_returns:
if tax_return['is_late']:
return 'late'
elif not tax_return['is_completed']:
if tax_return['state'] in ('reviewed', 'submitted'):
return 'to_submit'
elif tax_return['in_next_3_months']:
return 'to_do'
else:
return 'longterm'
return 'done'
def get_kpi_summary(cr, uid):
"""
Retrieve the status of pending tax returns grouped by report or return type.
The returned KPIs include:
- overdue tax returns;
- upcoming tax returns within the next 3 months;
- completion state of each tax return group.
This function intentionally bypasses the ORM so KPI summaries can be retrieved
without loading a registry, allowing multi-database servers to serve them faster.
"""
expected_columns = {
'account_return.date_deadline',
'account_return.is_completed',
'account_return.type_id',
'account_return_type.report_id',
'account_report.root_report_id',
}
cr.execute(SQL("""
SELECT table_name || '.' || column_name
FROM information_schema.columns
WHERE table_name || '.' || column_name IN %(columns)s
""", columns=tuple(expected_columns)))
existing_columns = {x[0] for x in cr.fetchall()}
if expected_columns - existing_columns:
# Needed columns are not present -> module is not installed
return []
cr.execute(SQL("""
SELECT model, res_id, module || '.' || name
FROM ir_model_data
WHERE model IN ('account.report', 'account.return.type')
AND module != '__export__'
"""))
external_ids = {
(model, res_id): complete_name
for model, res_id, complete_name in cr.fetchall()
}
now = datetime.now() # Using python-generated dates to respect what freezegun has frozen in tests
cr.execute(SQL("""
SELECT return.id return_id,
type.id return_type_id,
report.id report_id,
root_report.id root_report_id,
COALESCE(report.name->>partner.lang,
report.name->>'en_US',
type.name->>partner.lang,
type.name->>'en_US') tax_return_name,
return.date_deadline <= (%(now)s AT TIME ZONE COALESCE(partner.tz, 'UTC'))::date is_late,
return.date_deadline <= (%(now)s AT TIME ZONE COALESCE(partner.tz, 'UTC'))::date + INTERVAL '3 months' in_next_3_months,
return.is_completed,
return.state
FROM account_return return
JOIN res_users u ON u.id = %(uid)s
JOIN res_partner partner ON partner.id = u.partner_id
JOIN account_return_type type ON return.type_id = type.id
LEFT JOIN account_report report ON type.report_id = report.id
LEFT JOIN account_report root_report ON report.root_report_id = root_report.id
WHERE NOT return.is_completed
OR return.date_deadline >= (%(now)s AT TIME ZONE COALESCE(partner.tz, 'UTC'))::date
ORDER BY return.date_deadline NULLS LAST, return.id
""",
uid=uid,
now=now,
))
returns_by_external_id = defaultdict(list)
for return_info in cr.dictfetchall():
key = _tax_return_grouping_key(external_ids, return_info)
returns_by_external_id[key].append(return_info)
return [{
'id': f'account_return.{external_id}',
'name': tax_returns[0]['tax_return_name'],
'type': 'return_status',
'value': _tax_returns_get_value(tax_returns),
} for external_id, tax_returns in returns_by_external_id.items()]