[FIX] im_livechat: fix operator assignation

Before this PR, trying to get an available operator could result in an
error.

When a live chat is created, an operator is selected based on various
criteria such as language, country, number of ongoing chats and
availability for calls. This selection process involves the
`_get_less_active_operator` method, which receives the status of all
operators and a list of operators to choose from.

Prior to this fix, an operator not included in the list of operators
to choose from could still be selected, resulting in errors when
trying to locate them in the operator list.

This PR resolves the issue by ensuring that operators are only
selected from the provided list.

opw-3874872

closes odoo/odoo#162357

Signed-off-by: Matthieu Stockbauer (tsm) <tsm@odoo.com>
This commit is contained in:
tsm-odoo
2024-04-19 23:49:50 +00:00
parent 5887847105
commit 09295d229d
2 changed files with 36 additions and 13 deletions
@@ -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
@@ -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(