Files
odoo_source/addons/analytic/models/analytic_mixin.py
T
gawa-odoo dba7056c71 [FIX] analytic: allow filtering based on analytic distribution
Currently, the field `analytic_distribution` does not appear in
the filters, while it should (to be able to filter `account_move_line`
for example).
We added Json to the filterable fields. If not overriden, it will
search on it like a String.

We also have to add a non-stored field to be able to define a search
function for `analytic_distribution`, to be able to search based
on the keys of the json.

closes odoo/odoo#121223

X-original-commit: 3d3e6847230d5cc9cd61a9bf83bef403a5b4b3ec
Signed-off-by: William André (wan) <wan@odoo.com>
2023-05-12 20:52:06 +02:00

106 lines
5.2 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models, fields, api, _
from odoo.tools.float_utils import float_round, float_compare
from odoo.exceptions import UserError, ValidationError
class AnalyticMixin(models.AbstractModel):
_name = 'analytic.mixin'
_description = 'Analytic Mixin'
analytic_distribution = fields.Json(
'Analytic Distribution',
compute="_compute_analytic_distribution", store=True, copy=True, readonly=False,
precompute=True
)
# Json non stored to be able to search on analytic_distribution.
analytic_distribution_search = fields.Json(
store=False,
search="_search_analytic_distribution"
)
analytic_precision = fields.Integer(
store=False,
default=lambda self: self.env['decimal.precision'].precision_get("Percentage Analytic"),
)
def init(self):
# Add a gin index for json search on the keys, on the models that actually have a table
query = ''' SELECT table_name
FROM information_schema.tables
WHERE table_name=%s '''
self.env.cr.execute(query, [self._table])
if self.env.cr.dictfetchone():
query = f"""
CREATE INDEX IF NOT EXISTS {self._table}_analytic_distribution_gin_index
ON {self._table} USING gin(analytic_distribution);
"""
self.env.cr.execute(query)
super().init()
@api.model
def fields_get(self, allfields=None, attributes=None):
""" Hide analytic_distribution_search from filterable/searchable fields"""
res = super().fields_get(allfields, attributes)
if res.get('analytic_distribution_search'):
res['analytic_distribution_search']['searchable'] = False
return res
def _compute_analytic_distribution(self):
pass
def _search_analytic_distribution(self, operator, value):
if operator not in ['=', '!=', 'ilike', 'not ilike'] or not isinstance(value, (str, bool)):
raise UserError(_('Operation not supported'))
operator_name_search = '=' if operator in ('=', '!=') else 'ilike'
account_ids = list(self.env['account.analytic.account']._name_search(name=value, operator=operator_name_search))
query = f"""
SELECT id
FROM {self._table}
WHERE analytic_distribution ?| array[%s]
"""
operator_inselect = 'inselect' if operator in ('=', 'ilike') else 'not inselect'
return [('id', operator_inselect, (query, [[str(account_id) for account_id in account_ids]]))]
@api.model
def _search(self, domain, offset=0, limit=None, order=None, access_rights_uid=None):
for arg in domain:
if isinstance(arg, (list, tuple)) and arg[0] == 'analytic_distribution' and isinstance(arg[2], str):
arg[0] = 'analytic_distribution_search'
return super()._search(domain, offset, limit, order, access_rights_uid)
def write(self, vals):
""" Format the analytic_distribution float value, so equality on analytic_distribution can be done """
decimal_precision = self.env['decimal.precision'].precision_get('Percentage Analytic')
vals = self._sanitize_values(vals, decimal_precision)
return super().write(vals)
@api.model_create_multi
def create(self, vals_list):
""" Format the analytic_distribution float value, so equality on analytic_distribution can be done """
decimal_precision = self.env['decimal.precision'].precision_get('Percentage Analytic')
vals_list = [self._sanitize_values(vals, decimal_precision) for vals in vals_list]
return super().create(vals_list)
def _validate_distribution(self, **kwargs):
if self.env.context.get('validate_analytic', False):
mandatory_plans_ids = [plan['id'] for plan in self.env['account.analytic.plan'].sudo().get_relevant_plans(**kwargs) if plan['applicability'] == 'mandatory']
if not mandatory_plans_ids:
return
decimal_precision = self.env['decimal.precision'].precision_get('Percentage Analytic')
distribution_by_root_plan = {}
for analytic_account_id, percentage in (self.analytic_distribution or {}).items():
root_plan = self.env['account.analytic.account'].browse(int(analytic_account_id)).root_plan_id
distribution_by_root_plan[root_plan.id] = distribution_by_root_plan.get(root_plan.id, 0) + percentage
for plan_id in mandatory_plans_ids:
if float_compare(distribution_by_root_plan.get(plan_id, 0), 100, precision_digits=decimal_precision) != 0:
raise ValidationError(_("One or more lines require a 100% analytic distribution."))
def _sanitize_values(self, vals, decimal_precision):
""" Normalize the float of the distribution """
if 'analytic_distribution' in vals:
vals['analytic_distribution'] = vals.get('analytic_distribution') and {
account_id: float_round(distribution, decimal_precision) for account_id, distribution in vals['analytic_distribution'].items()}
return vals