diff --git a/addons/gamification/data/goal_base.xml b/addons/gamification/data/goal_base.xml index 80694c4229f..06ba1f0d936 100644 --- a/addons/gamification/data/goal_base.xml +++ b/addons/gamification/data/goal_base.xml @@ -8,9 +8,12 @@ count boolean - [('id','=',user.id),('partner_id.tz', '!=', False)] + [('partner_id.tz', '!=', False)] user.id + True + + user.id @@ -30,7 +33,7 @@ count boolean - [('user_ids', 'in', user.id),('logo', '!=', False)] + [('user_ids', 'in', [user.id]),('logo', '!=', False)] user.company_id.id diff --git a/addons/gamification/models/challenge.py b/addons/gamification/models/challenge.py index 02b13f1ba89..637dbe1f03d 100644 --- a/addons/gamification/models/challenge.py +++ b/addons/gamification/models/challenge.py @@ -207,6 +207,10 @@ class Challenge(models.Model): - Create the missing goals (eg: modified the challenge to add lines) - Update every running challenge """ + # in cron mode, will do intermediate commits + # cannot be replaced by a parameter because it is intended to impact side-effects of + # write operations + self = self.with_context(commit_gamification=commit) # start scheduled challenges planned_challenges = self.search([ ('state', '=', 'draft'), @@ -225,9 +229,7 @@ class Challenge(models.Model): records = self.browse(ids) if ids else self.search([('state', '=', 'inprogress')]) - # in cron mode, will do intermediate commits - # FIXME: replace by parameter - return records.with_context(commit_gamification=commit)._update_all() + return records._update_all() def _update_all(self): """Update the challenges and related goals @@ -391,6 +393,9 @@ class Challenge(models.Model): to_update.update_goal() + if self.env.context.get('commit_gamification'): + self.env.cr.commit() + return True ##### JS utilities ##### diff --git a/addons/gamification/models/goal.py b/addons/gamification/models/goal.py index dad5b3b04fe..8491ee770e3 100644 --- a/addons/gamification/models/goal.py +++ b/addons/gamification/models/goal.py @@ -155,7 +155,7 @@ class Goal(models.Model): user_id = fields.Many2one('res.users', string="User", required=True, auto_join=True, ondelete="cascade") line_id = fields.Many2one('gamification.challenge.line', string="Challenge Line", ondelete="cascade") challenge_id = fields.Many2one( - related='line_id.challenge_id', store=True, readonly=True, + related='line_id.challenge_id', store=True, readonly=True, index=True, help="Challenge that generated the goal, assign challenge to users " "to generate goals with a value in this field.") start_date = fields.Date("Start Date", default=fields.Date.today) @@ -291,11 +291,11 @@ class Goal(models.Model): "of code for definition %s, expected a number", result, definition.name) - else: # count or sum + elif definition.computation_mode in ('count', 'sum'): # count or sum Obj = self.env[definition.model_id.model] field_date_name = definition.field_date_id.name - if definition.computation_mode == 'count' and definition.batch_mode: + if definition.batch_mode: # batch mode, trying to do as much as possible in one request general_domain = ast.literal_eval(definition.domain) field_name = definition.batch_distinctive_field.name @@ -314,12 +314,22 @@ class Goal(models.Model): if end_date: subquery_domain.append((field_date_name, '<=', end_date)) - if field_name == 'id': - # grouping on id does not work and is similar to search anyway - users = Obj.search(subquery_domain) - user_values = [{'id': user.id, 'id_count': 1} for user in users] - else: - user_values = Obj.read_group(subquery_domain, fields=[field_name], groupby=[field_name]) + if definition.computation_mode == 'count': + value_field_name = field_name + '_count' + if field_name == 'id': + # grouping on id does not work and is similar to search anyway + users = Obj.search(subquery_domain) + user_values = [{'id': user.id, value_field_name: 1} for user in users] + else: + user_values = Obj.read_group(subquery_domain, fields=[field_name], groupby=[field_name]) + + else: # sum + value_field_name = definition.field_id.name + if field_name == 'id': + user_values = Obj.search_read(subquery_domain, fields=['id', value_field_name]) + else: + user_values = Obj.read_group(subquery_domain, fields=[field_name, "%s:sum" % value_field_name], groupby=[field_name]) + # user_values has format of read_group: [{'partner_id': 42, 'partner_id_count': 3},...] for goal in [g for g in goals if g.id in query_goals]: for user_value in user_values: @@ -327,7 +337,7 @@ class Goal(models.Model): if isinstance(queried_value, tuple) and len(queried_value) == 2 and isinstance(queried_value[0], int): queried_value = queried_value[0] if queried_value == query_goals[goal.id]: - new_value = user_value.get(field_name+'_count', goal.current) + new_value = user_value.get(value_field_name, goal.current) goals_to_write.update(goal._get_write_values(new_value)) else: @@ -343,7 +353,6 @@ class Goal(models.Model): if definition.computation_mode == 'sum': field_name = definition.field_id.name - # TODO for master: group on user field in batch mode res = Obj.read_group(domain, [field_name], []) new_value = res and res[0][field_name] or 0.0 @@ -352,6 +361,11 @@ class Goal(models.Model): goals_to_write.update(goal._get_write_values(new_value)) + else: + _logger.error( + "Invalid computation mode '%s' in definition %s", + definition.computation_mode, definition.name) + for goal, values in goals_to_write.items(): if not values: continue diff --git a/addons/gamification/views/goal.xml b/addons/gamification/views/goal.xml index 2f7f3719290..bfba69b5c43 100644 --- a/addons/gamification/views/goal.xml +++ b/addons/gamification/views/goal.xml @@ -252,7 +252,7 @@ - +
In batch mode, the domain is evaluated globally. If enabled, do not use keyword 'user' in above filter domain.
- [('state','!=','cancel'),('invoice_user_id','=',user.id),('move_type','=','out_invoice')] + [('state','!=','cancel'),('move_type','=','out_invoice')] + True + + user.id @@ -21,7 +24,10 @@ - [('user_id','=',user.id), '|', ('type', '=', 'lead'), ('type', '=', 'opportunity')] + ['|', ('type', '=', 'lead'), ('type', '=', 'opportunity')] + True + + user.id @@ -33,7 +39,10 @@ - [('user_id','=',user.id),('type', '=', 'lead')] + [('type', '=', 'lead')] + True + + user.id @@ -45,7 +54,10 @@ - [('user_id','=',user.id)] + [] + True + + user.id @@ -56,7 +68,10 @@ opportunities - [('user_id','=',user.id),('type','=','opportunity')] + [('type','=','opportunity')] + True + + user.id @@ -66,7 +81,10 @@ orders - [('user_id','=',user.id),('state','not in',('draft', 'sent', 'cancel'))] + [('state','not in',('draft', 'sent', 'cancel'))] + True + + user.id @@ -76,7 +94,10 @@ orders - [('payment_state','in',('paid', 'in_payment')),('invoice_user_id','=',user.id),('move_type','=','out_invoice')] + [('payment_state','in',('paid', 'in_payment')),('move_type','=','out_invoice')] + True + + user.id Total Paid Sales Orders @@ -86,7 +107,10 @@ - [('payment_state','in',('paid', 'in_payment')),('invoice_user_id','=',user.id),('move_type','=','out_invoice')] + [('payment_state','in',('paid', 'in_payment')),('move_type','=','out_invoice')] + True + + user.id @@ -98,7 +122,10 @@ invoices - [('state','!=','cancel'),('invoice_user_id','=',user.id),('move_type','=','out_refund')] + [('state','!=','cancel'),('move_type','=','out_refund')] + True + + user.id Total Customer Credit Notes @@ -109,7 +136,10 @@ - [('state','!=','cancel'),('invoice_user_id','=',user.id),('move_type','=','out_refund')] + [('state','!=','cancel'),('move_type','=','out_refund')] + True + + user.id @@ -119,7 +149,7 @@ Monthly Sales Targets monthly ranking - + weekly @@ -127,7 +157,7 @@ Lead Acquisition monthly ranking - + weekly