137 lines
6.9 KiB
Python
137 lines
6.9 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||
|
|
||
|
from odoo import api, fields, models, _, exceptions
|
||
|
from odoo.tools.safe_eval import safe_eval
|
||
|
|
||
|
DOMAIN_TEMPLATE = "[('store', '=', True), '|', ('model_id', '=', model_id), ('model_id', 'in', model_inherited_ids)%s]"
|
||
|
|
||
|
|
||
|
class GoalDefinition(models.Model):
|
||
|
"""Goal definition
|
||
|
|
||
|
A goal definition contains the way to evaluate an objective
|
||
|
Each module wanting to be able to set goals to the users needs to create
|
||
|
a new gamification_goal_definition
|
||
|
"""
|
||
|
_name = 'gamification.goal.definition'
|
||
|
_description = 'Gamification Goal Definition'
|
||
|
|
||
|
name = fields.Char("Goal Definition", required=True, translate=True)
|
||
|
description = fields.Text("Goal Description")
|
||
|
monetary = fields.Boolean("Monetary Value", default=False, help="The target and current value are defined in the company currency.")
|
||
|
suffix = fields.Char("Suffix", help="The unit of the target and current values", translate=True)
|
||
|
full_suffix = fields.Char("Full Suffix", compute='_compute_full_suffix', help="The currency and suffix field")
|
||
|
computation_mode = fields.Selection([
|
||
|
('manually', "Recorded manually"),
|
||
|
('count', "Automatic: number of records"),
|
||
|
('sum', "Automatic: sum on a field"),
|
||
|
('python', "Automatic: execute a specific Python code"),
|
||
|
], default='manually', string="Computation Mode", required=True,
|
||
|
help="Define how the goals will be computed. The result of the operation will be stored in the field 'Current'.")
|
||
|
display_mode = fields.Selection([
|
||
|
('progress', "Progressive (using numerical values)"),
|
||
|
('boolean', "Exclusive (done or not-done)"),
|
||
|
], default='progress', string="Displayed as", required=True)
|
||
|
model_id = fields.Many2one('ir.model', string='Model')
|
||
|
model_inherited_ids = fields.Many2many('ir.model', related='model_id.inherited_model_ids')
|
||
|
field_id = fields.Many2one(
|
||
|
'ir.model.fields', string='Field to Sum',
|
||
|
domain=DOMAIN_TEMPLATE % ''
|
||
|
)
|
||
|
field_date_id = fields.Many2one(
|
||
|
'ir.model.fields', string='Date Field', help='The date to use for the time period evaluated',
|
||
|
domain=DOMAIN_TEMPLATE % ", ('ttype', 'in', ('date', 'datetime'))"
|
||
|
)
|
||
|
domain = fields.Char(
|
||
|
"Filter Domain", required=True, default="[]",
|
||
|
help="Domain for filtering records. General rule, not user depending,"
|
||
|
" e.g. [('state', '=', 'done')]. The expression can contain"
|
||
|
" reference to 'user' which is a browse record of the current"
|
||
|
" user if not in batch mode.")
|
||
|
|
||
|
batch_mode = fields.Boolean("Batch Mode", help="Evaluate the expression in batch instead of once for each user")
|
||
|
batch_distinctive_field = fields.Many2one('ir.model.fields', string="Distinctive field for batch user", help="In batch mode, this indicates which field distinguishes one user from the other, e.g. user_id, partner_id...")
|
||
|
batch_user_expression = fields.Char("Evaluated expression for batch mode", help="The value to compare with the distinctive field. The expression can contain reference to 'user' which is a browse record of the current user, e.g. user.id, user.partner_id.id...")
|
||
|
compute_code = fields.Text("Python Code", help="Python code to be executed for each user. 'result' should contains the new current value. Evaluated user can be access through object.user_id.")
|
||
|
condition = fields.Selection([
|
||
|
('higher', "The higher the better"),
|
||
|
('lower', "The lower the better")
|
||
|
], default='higher', required=True, string="Goal Performance",
|
||
|
help="A goal is considered as completed when the current value is compared to the value to reach")
|
||
|
action_id = fields.Many2one('ir.actions.act_window', string="Action", help="The action that will be called to update the goal value.")
|
||
|
res_id_field = fields.Char("ID Field of user", help="The field name on the user profile (res.users) containing the value for res_id for action.")
|
||
|
|
||
|
@api.depends('suffix', 'monetary') # also depends of user...
|
||
|
def _compute_full_suffix(self):
|
||
|
for goal in self:
|
||
|
items = []
|
||
|
|
||
|
if goal.monetary:
|
||
|
items.append(self.env.company.currency_id.symbol or u'¤')
|
||
|
if goal.suffix:
|
||
|
items.append(goal.suffix)
|
||
|
|
||
|
goal.full_suffix = u' '.join(items)
|
||
|
|
||
|
def _check_domain_validity(self):
|
||
|
# take admin as should always be present
|
||
|
for definition in self:
|
||
|
if definition.computation_mode not in ('count', 'sum'):
|
||
|
continue
|
||
|
|
||
|
Obj = self.env[definition.model_id.model]
|
||
|
try:
|
||
|
domain = safe_eval(definition.domain, {
|
||
|
'user': self.env.user.with_user(self.env.user)
|
||
|
})
|
||
|
# dummy search to make sure the domain is valid
|
||
|
Obj.search_count(domain)
|
||
|
except (ValueError, SyntaxError) as e:
|
||
|
msg = e
|
||
|
if isinstance(e, SyntaxError):
|
||
|
msg = (e.msg + '\n' + e.text)
|
||
|
raise exceptions.UserError(_("The domain for the definition %s seems incorrect, please check it.\n\n%s", definition.name, msg))
|
||
|
return True
|
||
|
|
||
|
def _check_model_validity(self):
|
||
|
""" make sure the selected field and model are usable"""
|
||
|
for definition in self:
|
||
|
try:
|
||
|
if not (definition.model_id and definition.field_id):
|
||
|
continue
|
||
|
|
||
|
Model = self.env[definition.model_id.model]
|
||
|
field = Model._fields.get(definition.field_id.name)
|
||
|
if not (field and field.store):
|
||
|
raise exceptions.UserError(_(
|
||
|
"The model configuration for the definition %(name)s seems incorrect, please check it.\n\n%(field_name)s not stored",
|
||
|
name=definition.name,
|
||
|
field_name=definition.field_id.name
|
||
|
))
|
||
|
except KeyError as e:
|
||
|
raise exceptions.UserError(_(
|
||
|
"The model configuration for the definition %(name)s seems incorrect, please check it.\n\n%(error)s not found",
|
||
|
name=definition.name,
|
||
|
error=e
|
||
|
))
|
||
|
|
||
|
@api.model_create_multi
|
||
|
def create(self, vals_list):
|
||
|
definitions = super(GoalDefinition, self).create(vals_list)
|
||
|
definitions.filtered_domain([
|
||
|
('computation_mode', 'in', ['count', 'sum']),
|
||
|
])._check_domain_validity()
|
||
|
definitions.filtered_domain([
|
||
|
('field_id', '=', 'True'),
|
||
|
])._check_model_validity()
|
||
|
return definitions
|
||
|
|
||
|
def write(self, vals):
|
||
|
res = super(GoalDefinition, self).write(vals)
|
||
|
if vals.get('computation_mode', 'count') in ('count', 'sum') and (vals.get('domain') or vals.get('model_id')):
|
||
|
self._check_domain_validity()
|
||
|
if vals.get('field_id') or vals.get('model_id') or vals.get('batch_mode'):
|
||
|
self._check_model_validity()
|
||
|
return res
|