diff --git a/chats/apps/rooms/tests/test_viewsets_external.py b/chats/apps/rooms/tests/test_viewsets_external.py index 524cc589..4b736136 100644 --- a/chats/apps/rooms/tests/test_viewsets_external.py +++ b/chats/apps/rooms/tests/test_viewsets_external.py @@ -372,3 +372,44 @@ def test_add_agent_to_nonexistent_room(self): ) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + +class RoomsRoutingExternalTests(APITestCase): + fixtures = [ + "chats/fixtures/fixture_sector.json", + ] + + def _create_room(self, token: str, data: dict): + url = reverse("external_rooms-list") + client = self.client + client.credentials(HTTP_AUTHORIZATION=f"Bearer {token}") + + return client.post(url, data=data, format="json") + + def test_create_external_room_can_open_offline(self): + data = { + "queue_uuid": "8590ad29-5629-448c-bfb6-1bfd5219b8ec", + "contact": { + "external_id": "953fdcc9-1f6f-4abd-b90e-10a35c1cc825", + "name": "Foo Bar", + "email": "FooBar@weni.ai", + "phone": "+250788123123", + "custom_fields": {}, + }, + } + response = self._create_room("b5fab78a-4836-468c-96c4-f5b0bba3303a", data) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + def test_create_external_room_cannot_open_offline(self): + data = { + "queue_uuid": "605e21b0-4177-4eae-9cfb-529d9972a192", + "contact": { + "external_id": "a5ff0cd3-0bcd-4e91-8120-8718128cb1d9", + "name": "Foo Bar", + "email": "FooBar@weni.ai", + "phone": "+250788123123", + "custom_fields": {}, + }, + } + response = self._create_room("b5fab78a-4836-468c-96c4-f5b0bba3303a", data) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) diff --git a/chats/apps/sectors/migrations/0006_sector_open_offline.py b/chats/apps/sectors/migrations/0006_sector_open_offline.py new file mode 100644 index 00000000..0d253fdf --- /dev/null +++ b/chats/apps/sectors/migrations/0006_sector_open_offline.py @@ -0,0 +1,20 @@ +# Generated by Django 4.1.2 on 2023-06-27 13:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("sectors", "0005_remove_sector_rooms_limit_greater_than_zero"), + ] + + operations = [ + migrations.AddField( + model_name="sector", + name="open_offline", + field=models.BooleanField( + default=True, verbose_name="Open room when all agents are offline?" + ), + ), + ] diff --git a/chats/apps/sectors/models.py b/chats/apps/sectors/models.py index 20e3daec..a5544700 100644 --- a/chats/apps/sectors/models.py +++ b/chats/apps/sectors/models.py @@ -2,7 +2,7 @@ from django.contrib.auth import get_user_model from django.core.exceptions import ObjectDoesNotExist from django.db import models -from django.db.models import F, Q, Value +from django.db.models import Count, F, Q, Value from django.db.models.functions import Concat from django.utils.translation import gettext_lazy as _ @@ -31,6 +31,9 @@ class Sector(BaseSoftDeleteModel, BaseModel): default=False, ) is_deleted = models.BooleanField(_("is deleted?"), default=False) + open_offline = models.BooleanField( + _("Open room when all agents are offline?"), default=True + ) class Meta: verbose_name = _("Sector") @@ -123,6 +126,28 @@ def contact_count(self): # ) return 0 + def validate_agent_status(self, queue=None): + if self.open_offline: + return True + is_online = False + if queue: + is_online = queue.authorizations.filter( + permission__status="ONLINE" + ).exists() + else: + is_online = ( + self.queues.annotate( + online_count=Count( + "authorizations", + filter=Q(authorizations__permission__status="ONLINE"), + ) + ) + .filter(online_count__gt=0) + .exists() + ) + + return is_online + def is_attending(self, created_on): tz = pendulum.timezone(str(self.project.timezone)) created_on = pendulum.parse(str(created_on)).in_timezone(tz) diff --git a/chats/fixtures/fixture_sector.json b/chats/fixtures/fixture_sector.json index 85dfe081..c33998f0 100644 --- a/chats/fixtures/fixture_sector.json +++ b/chats/fixtures/fixture_sector.json @@ -10,6 +10,47 @@ "date_format": "D" } }, + { + "model": "projects.project", + "pk": "e7e5163d-4ee2-42da-a804-c252fda989b0", + "fields": { + "created_on": "2022-08-23T20:08:48.462Z", + "modified_on": "2022-08-23T20:08:48.462Z", + "name": "Weni routing", + "timezone": "America/Sao_Paulo", + "date_format": "D" + } + }, + { + "model": "sectors.sector", + "pk": "ae4b17b4-6ff1-44f4-b77b-182b65f0098a", + "fields": { + "created_on": "2022-08-24T20:59:49.115Z", + "modified_on": "2022-08-24T20:59:49.115Z", + "name": "Don't open offline", + "project": "e7e5163d-4ee2-42da-a804-c252fda989b0", + "rooms_limit": 5, + "open_offline": false, + "work_start": "08:00:00", + "work_end": "17:00:00", + "is_deleted": false + } + }, + { + "model": "sectors.sector", + "pk": "ab6e9380-a384-4f9f-ac21-c1131029c59b", + "fields": { + "created_on": "2022-08-24T20:59:49.115Z", + "modified_on": "2022-08-24T20:59:49.115Z", + "name": "Open offline", + "project": "e7e5163d-4ee2-42da-a804-c252fda989b0", + "rooms_limit": 5, + "open_offline": true, + "work_start": "08:00:00", + "work_end": "17:00:00", + "is_deleted": false + } + }, { "model": "sectors.sector", "pk": "21aecf8c-0c73-4059-ba82-4343e0cc627c", @@ -111,6 +152,17 @@ "user_permissions": [] } }, + { + "model": "projects.projectpermission", + "pk": "b5fab78a-4836-468c-96c4-f5b0bba3303a", + "fields": { + "created_on": "2022-08-24T20:28:05.281Z", + "modified_on": "2022-08-24T20:28:05.281Z", + "project": "e7e5163d-4ee2-42da-a804-c252fda989b0", + "user": "amywong@chats.weni.ai", + "role": 1 + } + }, { "model": "projects.projectpermission", "pk": "ce3f052c-e71d-402c-b02e-1dfaca8b3d45", @@ -144,6 +196,28 @@ "role": 1 } }, + { + "model": "sectors.sectorauthorization", + "pk": "c5378e06-ee3b-4cd4-a9c7-62410f6a45d4", + "fields": { + "created_on": "2022-08-24T21:13:48.359Z", + "modified_on": "2022-08-24T21:13:48.359Z", + "permission": "b5fab78a-4836-468c-96c4-f5b0bba3303a", + "sector": "ae4b17b4-6ff1-44f4-b77b-182b65f0098a", + "role": 1 + } + }, + { + "model": "sectors.sectorauthorization", + "pk": "2a2571be-294e-4bd7-ba3a-8db6b182de82", + "fields": { + "created_on": "2022-08-24T21:13:48.359Z", + "modified_on": "2022-08-24T21:13:48.359Z", + "permission": "b5fab78a-4836-468c-96c4-f5b0bba3303a", + "sector": "ab6e9380-a384-4f9f-ac21-c1131029c59b", + "role": 1 + } + }, { "model": "sectors.sectorauthorization", "pk": "00185928-ef97-47b8-9295-5462ff797a4e", @@ -166,6 +240,26 @@ "role": 1 } }, + { + "model": "queues.queue", + "pk": "605e21b0-4177-4eae-9cfb-529d9972a192", + "fields": { + "created_on": "2022-08-24T21:40:15.274Z", + "modified_on": "2022-08-24T21:40:15.274Z", + "sector": "ae4b17b4-6ff1-44f4-b77b-182b65f0098a", + "name": "DON'T OPEN OFFLINE" + } + }, + { + "model": "queues.queue", + "pk": "8590ad29-5629-448c-bfb6-1bfd5219b8ec", + "fields": { + "created_on": "2022-08-24T21:40:15.274Z", + "modified_on": "2022-08-24T21:40:15.274Z", + "sector": "ab6e9380-a384-4f9f-ac21-c1131029c59b", + "name": "OPEN OFFLINE" + } + }, { "model": "queues.queue", "pk": "f2519480-7e58-4fc4-9894-9ab1769e29cf", @@ -186,6 +280,28 @@ "name": "FRONTEND" } }, + { + "model": "queues.queueauthorization", + "pk": "355297c1-448f-4357-b4b1-97df80c240b4", + "fields": { + "created_on": "2022-08-25T04:55:18.502Z", + "modified_on": "2022-08-25T04:55:18.502Z", + "queue": "605e21b0-4177-4eae-9cfb-529d9972a192", + "role": 1, + "permission": "b5fab78a-4836-468c-96c4-f5b0bba3303a" + } + }, + { + "model": "queues.queueauthorization", + "pk": "45c6658e-966d-4222-b865-56710922335a", + "fields": { + "created_on": "2022-08-25T04:55:18.502Z", + "modified_on": "2022-08-25T04:55:18.502Z", + "queue": "8590ad29-5629-448c-bfb6-1bfd5219b8ec", + "role": 1, + "permission": "b5fab78a-4836-468c-96c4-f5b0bba3303a" + } + }, { "model": "queues.queueauthorization", "pk": "3717f056-7ea5-4d38-80f5-ba907132807c",