diff --git a/addons/im_livechat/models/im_livechat_channel.py b/addons/im_livechat/models/im_livechat_channel.py index 9881f0c4172..8af1e6e62a1 100644 --- a/addons/im_livechat/models/im_livechat_channel.py +++ b/addons/im_livechat/models/im_livechat_channel.py @@ -187,23 +187,32 @@ class ImLivechatChannel(models.Model): operator is in a call. The list is ordered by the number of active chats (ascending) and whether the operator is in a call (descending). - :param operators: list of operators to choose from + :param operators: recordset of :class:`ResUsers` operators to choose from. + :return: the :class:`ResUsers` record for the chosen operator """ if not operators: return False - active_operator_ids = set(map(lambda s: s['livechat_operator_id'], operator_statuses)) - inactive_operators = [ - operator for operator in operators if operator.partner_id.id not in active_operator_ids + + # 1) only consider operators in the list to choose from + operator_statuses = [ + s for s in operator_statuses if s['livechat_operator_id'] in set(operators.partner_id.ids) ] - if inactive_operators: - return random.choice(inactive_operators) - count, in_call = itemgetter('count', 'in_call')(operator_statuses[0]) - i = 1 - while i < len(operator_statuses) and operator_statuses[i]['in_call'] == in_call and operator_statuses[i]['count'] == count: - i += 1 - less_active_operator_id = random.choice(operator_statuses[:i])['livechat_operator_id'] - # convert the selected 'partner_id' to its corresponding res.users - return next(operator for operator in operators if operator.partner_id.id == less_active_operator_id) + + # 2) try to select an inactive op, i.e. one w/ no active status (no recent chat) + active_op_partner_ids = {s['livechat_operator_id'] for s in operator_statuses} + candidates = operators.filtered(lambda o: o.partner_id.id not in active_op_partner_ids) + if candidates: + return random.choice(candidates) + + # 3) otherwise select least active ops, based on status ordering (count + in_call) + best_status = operator_statuses[0] + best_status_op_partner_ids = { + s['livechat_operator_id'] + for s in operator_statuses + if (s['count'], s['in_call']) == (best_status['count'], best_status['in_call']) + } + candidates = operators.filtered(lambda o: o.partner_id.id in best_status_op_partner_ids) + return random.choice(candidates) def _get_operator(self, previous_operator_id=None, lang=None, country_id=None): """ Return an operator for a livechat. Try to return the previous diff --git a/addons/im_livechat/tests/test_get_operator.py b/addons/im_livechat/tests/test_get_operator.py index 26da74003b3..95bc97049da 100644 --- a/addons/im_livechat/tests/test_get_operator.py +++ b/addons/im_livechat/tests/test_get_operator.py @@ -66,6 +66,20 @@ class TestGetOperator(HttpCase): self.assertEqual(fr_operator, livechat_channel._get_operator(lang="fr_FR")) self.assertEqual(en_operator, livechat_channel._get_operator(lang="en_US")) + def test_get_by_lang_both_operator_active(self): + fr_operator = self._create_operator("fr_FR") + en_operator = self._create_operator("en_US") + livechat_channel = self.env["im_livechat.channel"].create( + { + "name": "Livechat Channel", + "user_ids": [fr_operator.id, en_operator.id], + } + ) + self._create_chat(livechat_channel, fr_operator) + self._create_chat(livechat_channel, en_operator) + self._create_chat(livechat_channel, en_operator) + self.assertEqual(en_operator, livechat_channel._get_operator(lang="en_US")) + def test_get_by_lang_no_operator_matching_lang(self): fr_operator = self._create_operator("fr_FR") livechat_channel = self.env["im_livechat.channel"].create(