diff --git a/addons/gamification/goal.py b/addons/gamification/goal.py
index 8fce8d43753..60b39c2e5a0 100644
--- a/addons/gamification/goal.py
+++ b/addons/gamification/goal.py
@@ -282,6 +282,6 @@ class gamification_goal(osv.Model):
if goal.planline_id and goal.planline_id.plan_id.report_message_frequency == 'onchange':
plan_obj = self.pool.get('gamification.goal.plan')
- plan_obj.report_progress(cr, uid, [goal.planline_id.plan_id.id], users=[goal.user_id], context=context)
+ plan_obj.report_progress(cr, uid, goal.planline_id.plan_id, users=[goal.user_id], context=context)
return super(gamification_goal, self).write(cr, uid, ids, vals, context=context)
diff --git a/addons/gamification/plan.py b/addons/gamification/plan.py
index 2a5e955c828..881e08fdea5 100644
--- a/addons/gamification/plan.py
+++ b/addons/gamification/plan.py
@@ -69,6 +69,26 @@ class gamification_goal_plan(osv.Model):
_description = 'Gamification goal plan'
_inherit = 'mail.thread'
+ def _get_next_report_date(self, cr, uid, ids, field_name, arg, context=None):
+ """Return the next report date based on the last report date and report
+ period. Return a string in isoformat."""
+ res = {}
+ for plan in self.browse(cr, uid, ids, context):
+ last = datetime.strptime(plan.last_report_date,'%Y-%m-%d').date()
+ if plan.report_message_frequency == 'daily':
+ next = last + timedelta(days=1)
+ elif plan.report_message_frequency == 'weekly':
+ next = last + timedelta(days=7)
+ elif plan.report_message_frequency == 'monthly':
+ month_range = calendar.monthrange(last.year, last.month)
+ next = last.replace(day=month_range[1]) + timedelta(days=1)
+ elif plan.report_message_frequency == 'yearly':
+ next = last.replace(year=last.year + 1)
+ else: # frequency == 'once':
+ next = False
+ res[plan.id] = next.isoformat()
+ return res
+
_columns = {
'name' : fields.char('Plan Name', required=True, translate=True),
'user_ids' : fields.many2many('res.users',
@@ -121,7 +141,11 @@ class gamification_goal_plan(osv.Model):
help='Group that will receive a copy of the report in addition to the user'),
'report_header' : fields.text('Report Header'),
'remind_update_delay' : fields.integer('Remind delay',
- help="The number of days after which the user assigned to a manual goal will be reminded. Never reminded if no value or zero is specified.")
+ help="The number of days after which the user assigned to a manual goal will be reminded. Never reminded if no value or zero is specified."),
+ 'last_report_date': fields.date('Last Report Date'),
+ 'next_report_date': fields.function(_get_next_report_date,
+ type='date',
+ string='Next Report Date'),
}
_defaults = {
@@ -129,6 +153,7 @@ class gamification_goal_plan(osv.Model):
'state': 'draft',
'visibility_mode' : 'progressbar',
'report_message_frequency' : 'onchange',
+ 'last_report_date' : fields.date.today
}
def _check_nonzero_planline(self, cr, uid, ids, context=None):
@@ -167,25 +192,44 @@ class gamification_goal_plan(osv.Model):
Create the goals for planlines not linked to goals (eg: modified the
plan to add planlines)
:param list(int) ids: the ids of the plans to update, if False will
- update only goals in progress."""
+ update only plans in progress."""
if not context: context = {}
if not ids:
ids = self.search(cr, uid, [('state', '=', 'inprogress')])
self.generate_goals_from_plan(cr, uid, ids, context=context)
- # update goals
goal_obj = self.pool.get('gamification.goal')
+ # we use yesterday to update the goals that just ended
+ yesterday = date.today() - timedelta(days=1)
goal_ids = goal_obj.search(cr, uid, [
'&',
('state', 'in', ('inprogress','inprogress_update', 'reached')),
'|',
- ('end_date', '>=', fields.date.today()),
+ ('end_date', '>=', yesterday.isoformat()),
('end_date', '=', False)
], context=context)
goal_obj.update(cr, uid, goal_ids, context=context)
- self.report_progress(cr, uid, ids, context=context)
+ for plan in self.browse(cr, uid, ids, context=context):
+ # goals closed but still opened at the last report date
+ closed_goals_to_report = []
+ for planline in plan.planline_ids:
+ closed_goals_to_report.extend( goal_obj.search(cr, uid, [
+ ('planline_id','=',planline.id),
+ ('start_date', '>=', plan.last_report_date),
+ ('end_date', '<=', plan.last_report_date)
+ ]) )
+
+ if len(closed_goals_to_report) > 0:
+ # some goals need a final report
+ self.report_progress(cr, uid, plan, subset_goal_ids=closed_goals_to_report, context=context)
+ self.write(cr, uid, plan.id, {'last_report_date':fields.date.today}, context=context)
+
+ if fields.date.today() == plan.next_report_date:
+ self.report_progress(cr, uid, plan, context=context)
+ self.write(cr, uid, plan.id, {'last_report_date':fields.date.today}, context=context)
+
def action_start(self, cr, uid, ids, context=None):
"""Start a draft goal plan
@@ -199,7 +243,7 @@ class gamification_goal_plan(osv.Model):
Create goals that haven't been created yet (eg: if added users of planlines)
Recompute the current value for each goal related"""
- return self._update_all(cr, uid, ids, context=context)
+ return self._update_all(cr, uid, ids=ids, context=context)
def action_close(self, cr, uid, ids, context=None):
@@ -254,6 +298,12 @@ class gamification_goal_plan(osv.Model):
res['domain'] = [('id','in', related_goal_ids)]
return res
+ def action_report_progress(self, cr, uid, ids, context=None):
+ """Manual report of a goal, does not influence automatic report frequency"""
+ for plan in self.browse(cr, uid, ids, context):
+ self.report_progress(cr, uid, plan, context=context)
+ return True
+
def generate_goals_from_plan(self, cr, uid, ids, context=None):
"""Generate the list of goals linked to a plan.
@@ -324,55 +374,112 @@ class gamification_goal_plan(osv.Model):
return True
- def report_progress(self, cr, uid, ids, context=None, users=False):
+ def report_progress(self, cr, uid, plan, context=None, users=False, subset_goal_ids=False):
"""Post report about the progress of the goals
- :param list(int) ids: the list of plan ids that need to be reported
+ :param plan: the plan object that need to be reported
:param list(res.users) users: the list of users that are concerned by
the report. If False, will send the report to every user concerned
(goal users and group that recieves a copy). Only used for plan with
- a visibility mode set to 'personal'."""
+ a visibility mode set to 'personal'.
+ :param list(int) goal_ids: the list of goal ids linked to the plan for
+ the report. If not specified, use the goals for the current plan
+ period. This parameter can be used to produce report for previous plan
+ periods."""
context = context or {}
goal_obj = self.pool.get('gamification.goal')
template_env = TemplateHelper()
- for plan in self.browse(cr, uid, ids, context=context):
+ (start_date, end_date) = start_end_date_for_period(plan.period)
- if plan.visibility_mode == 'board':
- # generate a shared report
- planlines_boards = []
+ if plan.visibility_mode == 'board':
+ # generate a shared report
+ planlines_boards = []
+
+ for planline in plan.planline_ids:
+
+ domain = [
+ ('planline_id', '=', planline.id),
+ ('state', 'in', ('inprogress', 'inprogress_update',
+ 'reached', 'failed')),
+ ]
+
+ if subset_goal_ids:
+ goal_ids = goal_obj.search(cr, uid, domain, context=context)
+ common_goal_ids = [goal.id for goal in goal_ids if goal in subset_goal_ids]
+ else:
+ # if no subset goals, use the dates for restriction
+ if start_date:
+ domain.append(('start_date', '=', start_date.isoformat()))
+ if end_date:
+ domain.append(('end_date', '=', end_date.isoformat()))
+ common_goal_ids = goal_obj.search(cr, uid, domain, context=context)
+
+ board_goals = []
+ for goal in goal_obj.browse(cr, uid, common_goal_ids, context=context):
+ board_goals.append({
+ 'user': goal.user_id,
+ 'current':goal.current,
+ 'target_goal':goal.target_goal,
+ 'completeness':goal.completeness,
+ })
+
+ # most complete first, current if same percentage (eg: if several 100%)
+ sorted_board = enumerate(sorted(board_goals, key=lambda k: (k['completeness'], k['current']), reverse=True))
+ planlines_boards.append({'goal_type':planline.type_id.name, 'board_goals':sorted_board})
+
+ body_html = template_env.get_template('group_progress.mako').render({'object':plan, 'planlines_boards':planlines_boards})
+
+ self.message_post(cr, uid, plan.id,
+ body=body_html,
+ partner_ids=[(6, 0, [user.partner_id.id for user in plan.user_ids])],
+ context=context,
+ subtype='mail.mt_comment')
+ if plan.report_message_group_id:
+ self.pool.get('mail.group').message_post(cr, uid, plan.report_message_group_id.id,
+ body=body_html,
+ context=context,
+ subtype='mail.mt_comment')
+
+ else:
+ # generate individual reports
+ for user in users or plan.user_ids:
+ related_goal_ids = []
for planline in plan.planline_ids:
-
- (start_date, end_date) = start_end_date_for_period(plan.period)
domain = [
('planline_id', '=', planline.id),
+ ('user_id', '=', user.id),
('state', 'in', ('inprogress', 'inprogress_update',
'reached', 'failed')),
]
- if start_date:
- domain.append(('start_date', '=', start_date.isoformat()))
+ if subset_goal_ids:
+ goal_ids = goal_obj.search(cr, uid, domain, context=context)
+ related_goal_ids.extend( [goal.id for goal in goal_ids if goal in subset_goal_ids] )
- board_goals = []
- goal_ids = goal_obj.search(cr, uid, domain, context=context)
- for goal in goal_obj.browse(cr, uid, goal_ids, context=context):
- board_goals.append({
- 'user': goal.user_id,
- 'current':goal.current,
- 'target_goal':goal.target_goal,
- 'completeness':goal.completeness,
- })
+ else:
+ # if no subset goals, use the dates for restriction
+ if start_date:
+ domain.append(('start_date', '=', start_date.isoformat()))
+ if end_date:
+ domain.append(('end_date', '=', end_date.isoformat()))
- # most complete first, current if same percentage (eg: if several 100%)
- sorted_board = enumerate(sorted(board_goals, key=lambda k: (k['completeness'], k['current']), reverse=True))
- planlines_boards.append({'goal_type':planline.type_id.name, 'board_goals':sorted_board})
+ related_goal_ids.extend( goal_obj.search(cr, uid, domain, context=context) )
- body_html = template_env.get_template('group_progress.mako').render({'object':plan, 'planlines_boards':planlines_boards})
+ if len(related_goal_ids) == 0:
+ continue
+
+ variables = {
+ 'object':plan,
+ 'user':user,
+ 'goals':goal_obj.browse(cr, uid, related_goal_ids, context=context)
+ }
+ body_html = template_env.get_template('personal_progress.mako').render(variables)
self.message_post(cr, uid, plan.id,
body=body_html,
- partner_ids=[(6, 0, [user.partner_id.id for user in plan.user_ids])],
+ partner_ids=[(6, 0, [user.partner_id.id])],
context=context,
subtype='mail.mt_comment')
if plan.report_message_group_id:
@@ -380,60 +487,8 @@ class gamification_goal_plan(osv.Model):
body=body_html,
context=context,
subtype='mail.mt_comment')
-
- else:
- # generate individual reports
- for user in users or plan.user_ids:
- goal_ids = self.get_current_related_goals(cr, uid, plan.id, user.id, context=context)
- if len(goal_ids) == 0:
- continue
-
- variables = {
- 'object':plan,
- 'user':user,
- 'goals':goal_obj.browse(cr, uid, goal_ids, context=context)
- }
- body_html = template_env.get_template('personal_progress.mako').render(variables)
-
- self.message_post(cr, uid, plan.id,
- body=body_html,
- partner_ids=[(6, 0, [user.partner_id.id])],
- context=context,
- subtype='mail.mt_comment')
- if plan.report_message_group_id:
- self.pool.get('mail.group').message_post(cr, uid, plan.report_message_group_id.id,
- body=body_html,
- context=context,
- subtype='mail.mt_comment')
return True
-
-
- def get_current_related_goals(self, cr, uid, plan_id, user_id, context=None):
- """Get the ids of goals linked to a plan for the current instance
-
- If several goals are linked to the same planline and user, only the
- latest instance of the plan is checked (eg: if the plan is monthly,
- return the goals started the 1st of this month).
- """
-
- plan = self.browse(cr, uid, plan_id, context=context)
- (start_date, end_date) = start_end_date_for_period(plan.period)
-
- goal_obj = self.pool.get('gamification.goal')
- related_goal_ids = []
-
- for planline in plan.planline_ids:
- domain = [('planline_id', '=', planline.id),
- ('user_id', '=', user_id),
- ('state','in',('inprogress','inprogress_update','reached'))]
-
- if start_date:
- domain.append(('start_date', '=', start_date.isoformat()))
-
- goal_ids = goal_obj.search(cr, uid, domain, context=context)
- related_goal_ids.extend(goal_ids)
-
- return related_goal_ids
+
class gamification_goal_planline(osv.Model):
"""Gamification goal planline
@@ -477,4 +532,4 @@ class gamification_goal_planline(osv.Model):
store={
'gamification.goal.type': (_get_planline_types, ['sequence'], 10),
}),
- }
\ No newline at end of file
+ }
diff --git a/addons/gamification/plan_view.xml b/addons/gamification/plan_view.xml
index 2c9efc5342f..cf95b6f690f 100644
--- a/addons/gamification/plan_view.xml
+++ b/addons/gamification/plan_view.xml
@@ -49,7 +49,7 @@
-
+