[MERGE] Sync with trunk

bzr revid: tde@openerp.com-20140411155723-pqe8ofxh060afiuj
This commit is contained in:
Thibault Delavallée 2014-04-11 17:57:23 +02:00
commit f091e9d6e5
9 changed files with 196 additions and 119 deletions

View File

@ -13,8 +13,6 @@
</header>
<p>You have not updated your progress for the goal ${object.definition_id.name} (currently reached at ${object.completeness}%) for at least ${object.remind_update_delay} days. Do not forget to do it.</p>
<p>If you have not changed your score yet, you can use the button "The current value is up to date" to indicate so.</p>
]]></field>
</record>

View File

@ -40,7 +40,8 @@ class gamification_badge_user(osv.Model):
_columns = {
'user_id': fields.many2one('res.users', string="User", required=True),
'sender_id': fields.many2one('res.users', string="Sender", help="The user who has send the badge"),
'badge_id': fields.many2one('gamification.badge', string='Badge', required=True),
'badge_id': fields.many2one('gamification.badge', string='Badge', required=True, ondelete="cascade"),
'challenge_id': fields.many2one('gamification.challenge', string='Challenge originating', help="If this badge was rewarded through a challenge"),
'comment': fields.text('Comment'),
'badge_name': fields.related('badge_id', 'name', type="char", string="Badge Name"),
'create_date': fields.datetime('Created', readonly=True),
@ -264,7 +265,7 @@ class gamification_badge(osv.Model):
def check_progress(self, cr, uid, context=None):
try:
model, res_id = template_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'gamification', 'badge_hidden')
model, res_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'gamification', 'badge_hidden')
except ValueError:
return True
badge_user_obj = self.pool.get('gamification.badge.user')

View File

@ -83,7 +83,7 @@ class gamification_challenge(osv.Model):
:return: a string in DEFAULT_SERVER_DATE_FORMAT representing the date"""
res = {}
for challenge in self.browse(cr, uid, ids, context):
for challenge in self.browse(cr, uid, ids, context=context):
last = datetime.strptime(challenge.last_report_date, DF).date()
if challenge.report_message_frequency == 'daily':
next = last + timedelta(days=1)
@ -163,6 +163,8 @@ class gamification_challenge(osv.Model):
'reward_second_id': fields.many2one('gamification.badge', string="For 2nd user"),
'reward_third_id': fields.many2one('gamification.badge', string="For 3rd user"),
'reward_failure': fields.boolean('Reward Bests if not Succeeded?'),
'reward_realtime': fields.boolean('Reward as soon as every goal is reached',
help="With this option enabled, a user can receive a badge only once. The top 3 badges are still rewarded only at the end of the challenge."),
'visibility_mode': fields.selection([
('personal', 'Individual Goals'),
@ -257,7 +259,7 @@ class gamification_challenge(osv.Model):
elif vals.get('state') == 'draft':
# resetting progress
if self.pool.get('gamification.goal').search(cr, uid, [('challenge_id', 'in', ids), ('state', 'in', ['inprogress', 'inprogress_update'])], context=context):
if self.pool.get('gamification.goal').search(cr, uid, [('challenge_id', 'in', ids), ('state', '=', 'inprogress')], context=context):
raise osv.except_osv("Error", "You can not reset a challenge with unfinished goals.")
write_res = super(gamification_challenge, self).write(cr, uid, ids, vals, context=context)
@ -280,13 +282,13 @@ class gamification_challenge(osv.Model):
- Create the missing goals (eg: modified the challenge to add lines)
- Update every running challenge
"""
# start planned challenges
# start scheduled challenges
planned_challenge_ids = self.search(cr, uid, [
('state', '=', 'draft'),
('start_date', '<=', fields.date.today())])
self.write(cr, uid, planned_challenge_ids, {'state': 'inprogress'}, context=context)
# close planned challenges
# close scheduled challenges
planned_challenge_ids = self.search(cr, uid, [
('state', '=', 'inprogress'),
('end_date', '>=', fields.date.today())])
@ -312,7 +314,7 @@ class gamification_challenge(osv.Model):
goal_ids = goal_obj.search(cr, uid, [
('challenge_id', 'in', ids),
'|',
('state', 'in', ('inprogress', 'inprogress_update')),
('state', '=', 'inprogress'),
'&',
('state', 'in', ('reached', 'failed')),
'|',
@ -363,7 +365,7 @@ class gamification_challenge(osv.Model):
"""Manual report of a goal, does not influence automatic report frequency"""
if isinstance(ids, (int,long)):
ids = [ids]
for challenge in self.browse(cr, uid, ids, context):
for challenge in self.browse(cr, uid, ids, context=context):
self.report_progress(cr, uid, challenge, context=context)
return True
@ -377,7 +379,8 @@ class gamification_challenge(osv.Model):
can be called after each change in the list of users or lines.
:param list(int) ids: the list of challenge concerned"""
for challenge in self.browse(cr, uid, ids, context):
to_update = []
for challenge in self.browse(cr, uid, ids, context=context):
(start_date, end_date) = start_end_date_for_period(challenge.period)
# if no periodicity, use challenge dates
@ -403,7 +406,7 @@ class gamification_challenge(osv.Model):
canceled_goal_ids = goal_obj.search(cr, uid, domain, context=context)
if canceled_goal_ids:
goal_obj.write(cr, uid, canceled_goal_ids, {'state': 'inprogress'}, context=context)
goal_obj.update(cr, uid, canceled_goal_ids, context=context)
to_update.extend(canceled_goal_ids)
# skip to next user
continue
@ -424,9 +427,10 @@ class gamification_challenge(osv.Model):
if challenge.remind_update_delay:
values['remind_update_delay'] = challenge.remind_update_delay
new_goal_id = goal_obj.create(cr, uid, values, context)
new_goal_id = goal_obj.create(cr, uid, values, context=context)
to_update.append(new_goal_id)
goal_obj.update(cr, uid, [new_goal_id], context=context)
goal_obj.update(cr, uid, to_update, context=context)
return True
@ -460,7 +464,7 @@ class gamification_challenge(osv.Model):
'rank': <user ranking>,
'user_id': <res.users id>,
'name': <res.users name>,
'state': <gamification.goal state {draft,inprogress,inprogress_update,reached,failed,canceled}>,
'state': <gamification.goal state {draft,inprogress,reached,failed,canceled}>,
'completeness': <percentage>,
'current': <current value>,
}
@ -478,7 +482,7 @@ class gamification_challenge(osv.Model):
'action': <{True,False}>,
'display_mode': <{progress,boolean}>,
'target': <challenge line target>,
'state': <gamification.goal state {draft,inprogress,inprogress_update,reached,failed,canceled}>,
'state': <gamification.goal state {draft,inprogress,reached,failed,canceled}>,
'completeness': <percentage>,
'current': <current value>,
}
@ -545,7 +549,7 @@ class gamification_challenge(osv.Model):
if user_id and goal.user_id.id == user_id:
line_data['own_goal_id'] = goal.id
elif restrict_top and ranking > restrict_top:
# not own goal, over top, skipping
# not own goal and too low to be in top
continue
line_data['goals'].append({
@ -669,52 +673,63 @@ class gamification_challenge(osv.Model):
"""
if isinstance(ids, (int,long)):
ids = [ids]
context = context or {}
for challenge in self.browse(cr, uid, ids, context=context):
(start_date, end_date) = start_end_date_for_period(challenge.period, challenge.start_date, challenge.end_date)
yesterday = date.today() - timedelta(days=1)
if end_date == yesterday.strftime(DF) or force:
rewarded_users = []
challenge_ended = end_date == yesterday.strftime(DF) or force
if challenge.reward_id and challenge_ended or challenge.reward_realtime:
for user in challenge.user_ids:
reached_goal_ids = self.pool.get('gamification.goal').search(cr, uid, [
('challenge_id', '=', challenge.id),
('user_id', '=', user.id),
('start_date', '=', start_date),
('end_date', '=', end_date),
('state', '=', 'reached')
], context=context)
if len(reached_goal_ids) == len(challenge.line_ids):
# the user has succeeded every assigned goal
if challenge.reward_realtime:
badges = self.pool['gamification.badge.user'].search(cr, uid, [
('challenge_id', '=', challenge.id),
('badge_id', '=', challenge.reward_id.id),
('user_id', '=', user.id),
], count=True, context=context)
if badges > 0:
# has already recieved the badge for this challenge
continue
self.reward_user(cr, uid, user.id, challenge.reward_id.id, challenge.id, context=context)
rewarded_users.append(user)
if challenge_ended:
# open chatter message
message_body = _("The challenge %s is finished." % challenge.name)
# reward for everybody succeeding
rewarded_users = []
if challenge.reward_id:
for user in challenge.user_ids:
reached_goal_ids = self.pool.get('gamification.goal').search(cr, uid, [
('challenge_id', '=', challenge.id),
('user_id', '=', user.id),
('start_date', '=', start_date),
('end_date', '=', end_date),
('state', '=', 'reached')
], context=context)
if len(reached_goal_ids) == len(challenge.line_ids):
self.reward_user(cr, uid, user.id, challenge.reward_id.id, context)
rewarded_users.append(user)
if rewarded_users:
message_body += _("<br/>Reward (badge %s) for every succeeding user was sent to %s." % (challenge.reward_id.name, ", ".join([user.name for user in rewarded_users])))
else:
message_body += _("<br/>Nobody has succeeded to reach every goal, no badge is rewared for this challenge.")
if rewarded_users:
message_body += _("<br/>Reward (badge %s) for every succeeding user was sent to %s." % (challenge.reward_id.name, ", ".join([user.name for user in rewarded_users])))
else:
message_body += _("<br/>Nobody has succeeded to reach every goal, no badge is rewared for this challenge.")
# reward bests
if challenge.reward_first_id:
(first_user, second_user, third_user) = self.get_top3_users(cr, uid, challenge, context)
(first_user, second_user, third_user) = self.get_top3_users(cr, uid, challenge, context=context)
if first_user:
self.reward_user(cr, uid, first_user.id, challenge.reward_first_id.id, context)
self.reward_user(cr, uid, first_user.id, challenge.reward_first_id.id, challenge.id, context=context)
message_body += _("<br/>Special rewards were sent to the top competing users. The ranking for this challenge is :")
message_body += "<br/> 1. %s - %s" % (first_user.name, challenge.reward_first_id.name)
else:
message_body += _("Nobody reached the required conditions to receive special badges.")
if second_user and challenge.reward_second_id:
self.reward_user(cr, uid, second_user.id, challenge.reward_second_id.id, context)
self.reward_user(cr, uid, second_user.id, challenge.reward_second_id.id, challenge.id, context=context)
message_body += "<br/> 2. %s - %s" % (second_user.name, challenge.reward_second_id.name)
if third_user and challenge.reward_third_id:
self.reward_user(cr, uid, third_user.id, challenge.reward_second_id.id, context)
self.reward_user(cr, uid, third_user.id, challenge.reward_second_id.id, challenge.id, context=context)
message_body += "<br/> 3. %s - %s" % (third_user.name, challenge.reward_third_id.name)
self.message_post(cr, uid, challenge.id, body=message_body, context=context)
return True
def get_top3_users(self, cr, uid, challenge, context=None):
@ -768,14 +783,14 @@ class gamification_challenge(osv.Model):
return (sorted_challengers[0]['user'], sorted_challengers[1]['user'], False)
return (sorted_challengers[0]['user'], sorted_challengers[1]['user'], sorted_challengers[2]['user'])
def reward_user(self, cr, uid, user_id, badge_id, context=None):
def reward_user(self, cr, uid, user_id, badge_id, challenge_id=False, context=None):
"""Create a badge user and send the badge to him
:param user_id: the user to reward
:param badge_id: the concerned badge
"""
badge_user_obj = self.pool.get('gamification.badge.user')
user_badge_id = badge_user_obj.create(cr, uid, {'user_id': user_id, 'badge_id': badge_id}, context=context)
user_badge_id = badge_user_obj.create(cr, uid, {'user_id': user_id, 'badge_id': badge_id, 'challenge_id':challenge_id}, context=context)
return badge_user_obj._send_badge(cr, uid, [user_badge_id], context=context)

View File

@ -88,8 +88,16 @@ class gamification_goal_definition(osv.Model):
string='Date Field',
help='The date to use for the time period evaluated'),
'domain': fields.char("Filter Domain",
help="Domain for filtering records. The rule can contain reference to 'user' that is a browse record of the current user, e.g. [('user_id', '=', user.id)].",
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.",
required=True),
'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 distinct one user form the other, e.g. user_id, partner_id..."),
'batch_user_expression': fields.char("Evaluted 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([
@ -102,7 +110,7 @@ class gamification_goal_definition(osv.Model):
'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.")
help="The field name on the user profile (res.users) containing the value for res_id for action."),
}
_defaults = {
@ -158,7 +166,7 @@ class gamification_goal(osv.Model):
_columns = {
'definition_id': fields.many2one('gamification.goal.definition', string='Goal Definition', required=True, ondelete="cascade"),
'user_id': fields.many2one('res.users', string='User', required=True),
'line_id': fields.many2one('gamification.challenge.line', string='Goal Line', ondelete="cascade"),
'line_id': fields.many2one('gamification.challenge.line', string='Challenge Line', ondelete="cascade"),
'challenge_id': fields.related('line_id', 'challenge_id',
string="Challenge",
type='many2one',
@ -175,7 +183,6 @@ class gamification_goal(osv.Model):
'state': fields.selection([
('draft', 'Draft'),
('inprogress', 'In progress'),
('inprogress_update', 'In progress (to update)'),
('reached', 'Reached'),
('failed', 'Failed'),
('canceled', 'Canceled'),
@ -183,6 +190,8 @@ class gamification_goal(osv.Model):
string='State',
required=True,
track_visibility='always'),
'to_update': fields.boolean('To update'),
'closed': fields.boolean('Closed goal', help="These goals will not be recomputed."),
'computation_mode': fields.related('definition_id', 'computation_mode', type='char', string="Computation mode"),
'remind_update_delay': fields.integer('Remind delay',
@ -212,14 +221,14 @@ class gamification_goal(osv.Model):
if goal.remind_update_delay and goal.last_update:
delta_max = timedelta(days=goal.remind_update_delay)
last_update = datetime.strptime(goal.last_update, DF).date()
if date.today() - last_update > delta_max and goal.state == 'inprogress':
if date.today() - last_update > delta_max:
# generate a remind report
temp_obj = self.pool.get('email.template')
template_id = self.pool['ir.model.data'].get_object(cr, uid, 'gamification', 'email_template_goal_reminder', context)
body_html = temp_obj.render_template(cr, uid, template_id.body_html, 'gamification.goal', goal.id, context=context)
self.message_post(cr, uid, goal.id, body=body_html, partner_ids=[goal.user_id.partner_id.id], context=context, subtype='mail.mt_comment')
return {'state': 'inprogress_update'}
return {'to_update': True}
return {}
def update(self, cr, uid, ids, context=None):
@ -233,70 +242,122 @@ class gamification_goal(osv.Model):
if context is None:
context = {}
goals_by_definition = {}
goals_to_write = {}
all_goals = {}
for goal in self.browse(cr, uid, ids, context=context):
towrite = {}
if goal.state in ('draft', 'canceled'):
# skip if goal draft or canceled
# draft or canceled goals should not be recomputed
continue
if goal.definition_id.computation_mode == 'manually':
towrite.update(self._check_remind_delay(cr, uid, goal, context))
goals_by_definition.setdefault(goal.definition_id, []).append(goal)
goals_to_write[goal.id] = {}
all_goals[goal.id] = goal
elif goal.definition_id.computation_mode == 'python':
# execute the chosen method
cxt = {
'self': self.pool.get('gamification.goal'),
'object': goal,
'pool': self.pool,
'cr': cr,
'context': dict(context), # copy context to prevent side-effects of eval
'uid': uid,
'date': date, 'datetime': datetime, 'timedelta': timedelta, 'time': time
}
code = goal.definition_id.compute_code.strip()
safe_eval(code, cxt, mode="exec", nocopy=True)
# the result of the evaluated codeis put in the 'result' local variable, propagated to the context
result = cxt.get('result')
if result is not None and type(result) in (float, int, long):
if result != goal.current:
towrite['current'] = result
else:
_logger.exception(_('Invalid return content from the evaluation of %s' % code))
for definition, goals in goals_by_definition.items():
if definition.computation_mode == 'manually':
for goal in goals:
goals_to_write[goal.id].update(self._check_remind_delay(cr, uid, goal, context))
elif definition.computation_mode == 'python':
# TODO batch execution
for goal in goals:
# execute the chosen method
cxt = {
'self': self.pool.get('gamification.goal'),
'object': goal,
'pool': self.pool,
'cr': cr,
'context': dict(context), # copy context to prevent side-effects of eval
'uid': uid,
'date': date, 'datetime': datetime, 'timedelta': timedelta, 'time': time
}
code = definition.compute_code.strip()
safe_eval(code, cxt, mode="exec", nocopy=True)
# the result of the evaluated codeis put in the 'result' local variable, propagated to the context
result = cxt.get('result')
if result is not None and type(result) in (float, int, long):
if result != goal.current:
goals_to_write[goal.id]['current'] = result
else:
_logger.exception(_('Invalid return content from the evaluation of code for definition %s' % definition.name))
else: # count or sum
obj = self.pool.get(goal.definition_id.model_id.model)
field_date_name = goal.definition_id.field_date_id.name
# eval the domain with user replaced by goal user object
domain = safe_eval(goal.definition_id.domain, {'user': goal.user_id})
obj = self.pool.get(definition.model_id.model)
field_date_name = definition.field_date_id and definition.field_date_id.name or False
# add temporal clause(s) to the domain if fields are filled on the goal
if goal.start_date and field_date_name:
domain.append((field_date_name, '>=', goal.start_date))
if goal.end_date and field_date_name:
domain.append((field_date_name, '<=', goal.end_date))
if definition.computation_mode == 'count' and definition.batch_mode:
if goal.definition_id.computation_mode == 'sum':
field_name = goal.definition_id.field_id.name
res = obj.read_group(cr, uid, domain, [field_name], [field_name], context=context)
new_value = res and res[0][field_name] or 0.0
general_domain = safe_eval(definition.domain)
# goal_distinct_values = {goal.id: safe_eval(definition.batch_user_expression, {'user': goal.user_id}) for goal in goals}
field_name = definition.batch_distinctive_field.name
# general_domain.append((field_name, 'in', list(set(goal_distinct_values.keys()))))
subqueries = {}
for goal in goals:
start_date = field_date_name and goal.start_date or False
end_date = field_date_name and goal.end_date or False
subqueries.setdefault((start_date, end_date), {}).update({goal.id:safe_eval(definition.batch_user_expression, {'user': goal.user_id})})
else: # computation mode = count
new_value = obj.search(cr, uid, domain, context=context, count=True)
for (start_date, end_date), query_goals in subqueries.items():
subquery_domain = list(general_domain)
subquery_domain.append((field_name, 'in', list(set(query_goals.values()))))
if start_date:
subquery_domain.append((field_date_name, '>=', start_date))
if end_date:
subquery_domain.append((field_date_name, '>=', end_date))
# avoid useless write if the new value is the same as the old one
if new_value != goal.current:
towrite['current'] = new_value
user_values = obj.read_group(cr, uid, subquery_domain, fields=[field_name], groupby=[field_name], context=context)
for goal in [g for g in goals if g.id in query_goals.keys()]:
for user_value in user_values:
# return format of read_group: [{'partner_id': 42, 'partner_id_count': 3},...]
queried_value = field_name in user_value and user_value[field_name] or False
if isinstance(queried_value, tuple) and len(queried_value) == 2 and isinstance(queried_value[0], (int, long)):
queried_value = queried_value[0]
if queried_value == query_goals[goal.id]:
new_value = user_value.get(field_name+'_count', goal.current)
if new_value != goal.current:
goals_to_write[goal.id]['current'] = new_value
else:
for goal in goals:
# eval the domain with user replaced by goal user object
domain = safe_eval(definition.domain, {'user': goal.user_id})
# add temporal clause(s) to the domain if fields are filled on the goal
if goal.start_date and field_date_name:
domain.append((field_date_name, '>=', goal.start_date))
if goal.end_date and field_date_name:
domain.append((field_date_name, '<=', goal.end_date))
if definition.computation_mode == 'sum':
field_name = definition.field_id.name
res = obj.read_group(cr, uid, domain, [field_name], [field_name], context=context)
new_value = res and res[0][field_name] or 0.0
else: # computation mode = count
new_value = obj.search(cr, uid, domain, context=context, count=True)
# avoid useless write if the new value is the same as the old one
if new_value != goal.current:
goals_to_write[goal.id]['current'] = new_value
for goal_id, value in goals_to_write.items():
if not value:
continue
goal = all_goals[goal_id]
# check goal target reached
if (goal.definition_condition == 'higher' and towrite.get('current', goal.current) >= goal.target_goal) or (goal.definition_condition == 'lower' and towrite.get('current', goal.current) <= goal.target_goal):
towrite['state'] = 'reached'
if (goal.definition_condition == 'higher' and value.get('current', goal.current) >= goal.target_goal) \
or (goal.definition_condition == 'lower' and value.get('current', goal.current) <= goal.target_goal):
value['state'] = 'reached'
# check goal failure
elif goal.end_date and fields.date.today() > goal.end_date:
towrite['state'] = 'failed'
if towrite:
self.write(cr, uid, [goal.id], towrite, context=context)
value['state'] = 'failed'
value['closed'] = True
if value:
self.write(cr, uid, [goal.id], value, context=context)
return True
def action_start(self, cr, uid, ids, context=None):

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View File

@ -49,21 +49,6 @@ openerp.gamification = function(instance) {
self.get_goal_todo_info();
});
});
},
'click .oe_goal h4': function(event) {
var self = this;
this.kkeys = [];
$(document).on('keydown.klistener', function(event) {
if ("37,38,39,40,65,66".indexOf(event.keyCode) < 0) {
$(document).off('keydown.klistener');
} else {
self.kkeys.push(event.keyCode);
if (self.kkeys.toString().indexOf("38,38,40,40,37,39,37,39,66,65") >= 0) {
new instance.web.Model('gamification.badge').call('check_progress', []);
$(document).off('keydown.klistener');
}
}
});
}
},
start: function() {
@ -126,6 +111,13 @@ openerp.gamification = function(instance) {
}
});
instance.web.WebClient.include({
to_kitten: function() {
this._super();
new instance.web.Model('gamification.badge').call('check_progress', []);
}
});
instance.mail.Widget.include({
start: function() {
this._super();

View File

@ -86,6 +86,7 @@
<field name="reward_second_id" attrs="{'invisible': [('reward_first_id','=', False)]}" />
<field name="reward_third_id" attrs="{'invisible': ['|',('reward_first_id','=', False),('reward_second_id','=', False)]}" />
<field name="reward_failure" attrs="{'invisible': [('reward_first_id','=', False)]}" />
<field name="reward_realtime" attrs="{'readonly': [('reward_id','=', False)], 'required': [('reward_id','!=', False)]}" />
</group>
<div class="oe_grey">
<p>Badges are granted when a challenge is finished. This is either at the end of a running period (eg: end of the month for a monthly challenge), at the end date of a challenge (if no periodicity is set) or when the challenge is manually closed.</p>

View File

@ -45,8 +45,8 @@
<header>
<button string="Start goal" type="object" name="action_start" states="draft" class="oe_highlight"/>
<button string="Goal Reached" type="object" name="action_reach" states="inprogress,inprogress_update" />
<button string="Goal Failed" type="object" name="action_fail" states="inprogress,inprogress_update"/>
<button string="Goal Reached" type="object" name="action_reach" states="inprogress" />
<button string="Goal Failed" type="object" name="action_fail" states="inprogress"/>
<button string="Reset Completion" type="object" name="action_cancel" states="failed,reached" groups="base.group_no_one" />
<field name="state" widget="statusbar" statusbar_visible="draft,inprogress,reached" />
</header>
@ -105,7 +105,7 @@
<filter name="inprogress" string="Current"
domain="[
'|',
('state', 'in', ('inprogress', 'inprogress_update')),
('state', '=', 'inprogress'),
('end_date', '>=', context_today().strftime('%%Y-%%m-%%d'))
]"/>
<filter name="closed" string="Passed" domain="[('state', 'in', ('reached', 'failed'))]"/>
@ -157,7 +157,7 @@
<t t-if="record.definition_display.raw_value == 'boolean'">
<div class="oe_goal_state oe_e">
<t t-if="record.state.raw_value=='reached'"><span class="oe_green" title="Goal Reached">W</span></t>
<t t-if="record.state.raw_value=='inprogress' || record.state.raw_value=='inprogress_update'"><span title="Goal in Progress">N</span></t>
<t t-if="record.state.raw_value=='inprogress'"><span title="Goal in Progress">N</span></t>
<t t-if="record.state.raw_value=='failed'"><span class="oe_red" title="Goal Failed">X</span></t>
</div>
</t>
@ -241,15 +241,23 @@
<!-- Hide the fields below if manually -->
<field name="model_id" attrs="{'invisible':[('computation_mode','not in',('sum', 'count'))], 'required':[('computation_mode','in',('sum', 'count'))]}" class="oe_inline"/>
<field name="field_id" attrs="{'invisible':[('computation_mode','!=','sum')], 'required':[('computation_mode','=','sum')]}" domain="[('model_id','=',model_id)]" class="oe_inline"/>
<field name="field_date_id" attrs="{'invisible':[('computation_mode','not in',('sum', 'count'))]}" domain="[('ttype', 'in', ('date', 'datetime')), ('model_id','=',model_id)]" class="oe_inline"/>
<field name="field_id" attrs="{'invisible':[('computation_mode','!=','sum')], 'required':[('computation_mode','=','sum')]}" domain="[('model_id', '=', model_id)]" class="oe_inline"/>
<field name="field_date_id" attrs="{'invisible':[('computation_mode','not in',('sum', 'count'))]}" domain="[('ttype', 'in', ('date', 'datetime')), ('model_id', '=', model_id)]" class="oe_inline"/>
<field name="domain" attrs="{'invisible':[('computation_mode','not in',('sum', 'count'))], 'required':[('computation_mode','in',('sum', 'count'))]}" class="oe_inline"/>
<field name="compute_code" attrs="{'invisible':[('computation_mode','!=','python')], 'required':[('computation_mode','=','python')]}" placeholder="e.g. result = pool.get('mail.followers').search(cr, uid, [('res_model', '=', 'mail.group'), ('partner_id', '=', object.user_id.partner_id.id)], count=True, context=context)"/>
<field name="condition" widget="radio"/>
</group>
<group string="Optimisation" attrs="{'invisible': [('computation_mode', '!=', 'count')]}">
<field name="batch_mode" />
<div colspan="4">In batch mode, the domain is evaluated globally. If enabled, do not use keyword 'user' in above filter domain.</div>
<field name="batch_distinctive_field" attrs="{'invisible': [('batch_mode', '=', False)], 'required': [('batch_mode', '=', True)]}"
domain="[('model_id', '=', model_id)]" class="oe_inline" />
<field name="batch_user_expression" attrs="{'invisible': [('batch_mode', '=', False)], 'required': [('batch_mode', '=', True)]}" class="oe_inline"
placeholder="e.g. user.partner_id.id"/>
</group>
<group string="Formating Options">
<field name="display_mode" widget="radio" />
<field name="suffix" placeholder="e.g. days"/>
<field name="suffix" placeholder="e.g. days" class="oe_inline"/>
<field name="monetary"/>
</group>
<group string="Clickable Goals" attrs="{'invisible': [('computation_mode', '=', 'manually')]}">

View File

@ -38,6 +38,7 @@ class goal_manual_wizard(osv.TransientModel):
towrite = {
'current': wiz.current,
'goal_id': wiz.goal_id.id,
'to_update': False,
}
goal_obj.write(cr, uid, [wiz.goal_id.id], towrite, context=context)
goal_obj.update(cr, uid, [wiz.goal_id.id], context=context)