Files
enterprise-addons/hr_payroll/models/hr_rule_parameter.py
T

143 lines
6.2 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
from copy import deepcopy
from odoo.tools.safe_eval import safe_eval
from odoo import api, fields, models, _
from odoo.tools import ormcache
from odoo.tools.misc import format_date
from odoo.exceptions import UserError
_PARAMETER_NOT_FOUND = object()
class HrRuleParameterValue(models.Model):
_name = 'hr.rule.parameter.value'
_description = 'Salary Rule Parameter Value'
_order = 'date_from desc'
rule_parameter_id = fields.Many2one('hr.rule.parameter', required=True, index=True, ondelete='cascade', default=lambda self: self.env.context.get('active_id'))
rule_parameter_name = fields.Char(related="rule_parameter_id.name", readonly=True)
code = fields.Char(related="rule_parameter_id.code", index=True, store=True, readonly=True)
date_from = fields.Date(string="From", index=True, required=True)
parameter_value = fields.Text(help="Python data structure")
country_id = fields.Many2one(related="rule_parameter_id.country_id")
_unique_parameter = models.Constraint(
'unique (rule_parameter_id, date_from)',
"Two rules with the same code cannot start the same day",
)
@api.constrains('parameter_value')
def _check_parameter_value(self):
for value in self:
try:
safe_eval(value.parameter_value)
except Exception as e:
raise UserError(_('Wrong rule parameter value for %(rule_parameter_name)s at date %(date)s.\n%(error)s', rule_parameter_name=value.rule_parameter_name, date=format_date(self.env, value.date_from), error=str(e)))
@api.model_create_multi
def create(self, vals_list):
self.env.registry.clear_cache()
return super().create(vals_list)
def write(self, vals):
if 'date_from' in vals or 'parameter_value' in vals:
self.env.registry.clear_cache()
return super().write(vals)
def unlink(self):
self.env.registry.clear_cache()
return super().unlink()
class HrRuleParameter(models.Model):
_name = 'hr.rule.parameter'
_description = 'Salary Rule Parameter'
name = fields.Char(required=True)
code = fields.Char(required=True, help="This code is used in salary rules to refer to this parameter.")
description = fields.Html()
country_id = fields.Many2one('res.country', string='Country', default=lambda self: self.env.company.country_id)
parameter_version_ids = fields.One2many('hr.rule.parameter.value', 'rule_parameter_id', string='Versions')
current_value_one_line = fields.Text(string='Current Value (short)', compute='_compute_current_value')
valid_since = fields.Date(compute='_compute_current_value')
salary_rule_ids = fields.One2many('hr.salary.rule', compute='_compute_salary_rule', string='Salary Rules')
salary_rule_count = fields.Integer(compute='_compute_salary_rule')
_unique_code = models.Constraint(
'unique (code)',
"Two rule parameters cannot have the same code.",
)
@api.model
def _get_parameter_from_code(self, code, date=None, raise_if_not_found=True):
if not date:
date = fields.Date.today()
parameter_value = self._get_cached_parameter_from_code(code, date)
if parameter_value is not _PARAMETER_NOT_FOUND:
return deepcopy(parameter_value)
if raise_if_not_found:
raise UserError(_('No rule parameter with code "%(code)s" was found for %(date)s', code=code, date=date))
return None
@api.model
@ormcache('code', 'date', 'tuple(self.env.context.get("allowed_company_ids", []))')
def _get_cached_parameter_from_code(self, code, date):
# This should be quite fast as it uses a limit and fields are indexed
# moreover the method is cached
rule_parameter = self.env['hr.rule.parameter.value'].search([
('code', '=', code),
('date_from', '<=', date)], limit=1)
if rule_parameter:
return safe_eval(rule_parameter.parameter_value)
return _PARAMETER_NOT_FOUND
@api.depends('parameter_version_ids')
def _compute_current_value(self):
for rule_parameter in self:
rule_parameter.current_value_one_line = False
rule_parameter.valid_since = False
if not rule_parameter.parameter_version_ids:
continue
# All values are already order from most recent to oldest.
# Here we get the first value that is not in the future, i.e. the current value.
for value_id in rule_parameter.parameter_version_ids:
if value_id.date_from <= fields.Date.today():
parameter_value = value_id.parameter_value or ''
is_number = parameter_value.replace('-', '').replace('.', '').isnumeric()
rule_parameter.current_value_one_line = parameter_value if is_number else '(...)'
rule_parameter.valid_since = value_id.date_from
break
def _compute_salary_rule(self):
for rule_parameter in self:
rule_parameter.salary_rule_ids = self.env['hr.salary.rule'].search([
'|',
'|',
('condition_python', 'like', "'" + rule_parameter.code + "'"),
('amount_python_compute', 'like', "'" + rule_parameter.code + "'"),
'|',
('condition_python', 'like', '"' + rule_parameter.code + '"'),
('amount_python_compute', 'like', '"' + rule_parameter.code + '"'),
]) if rule_parameter.code else False
rule_parameter.salary_rule_count = len(rule_parameter.salary_rule_ids)
def action_open_salary_rules(self):
self.ensure_one()
action = self.env['ir.actions.actions']._for_xml_id('hr_payroll.action_salary_rule_form')
action.update({
'domain': [
'|',
'|',
('condition_python', 'like', "'" + self.code + "'"),
('amount_python_compute', 'like', "'" + self.code + "'"),
'|',
('condition_python', 'like', '"' + self.code + '"'),
('amount_python_compute', 'like', '"' + self.code + '"'),
],
})
return action