143 lines
6.2 KiB
Python
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
|