From a647703a31cc29ff792ebbaedda05d625658247f Mon Sep 17 00:00:00 2001 From: Rowan Seymour Date: Wed, 23 Oct 2024 22:16:08 +0000 Subject: [PATCH] Give every workspace a default team with access to all topics --- temba/orgs/models.py | 10 ++++++-- temba/orgs/tests.py | 6 +++-- .../migrations/0066_team_is_default.py | 18 ++++++++++++++ temba/tickets/models.py | 24 ++++++++++++++----- 4 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 temba/tickets/migrations/0066_team_is_default.py diff --git a/temba/orgs/models.py b/temba/orgs/models.py index ac0c18e2ce..b63357f7ae 100644 --- a/temba/orgs/models.py +++ b/temba/orgs/models.py @@ -949,6 +949,10 @@ def get_contact_count(self) -> int: def default_ticket_topic(self): return self.topics.get(is_default=True) + @cached_property + def default_ticket_team(self): + return self.teams.get(is_default=True) + def get_resthooks(self): """ Returns the resthooks configured on this Org @@ -1229,12 +1233,13 @@ def initialize(self, sample_flows=True): Initializes an organization, creating all the dependent objects we need for it to work properly. """ from temba.contacts.models import ContactField, ContactGroup - from temba.tickets.models import Topic + from temba.tickets.models import Team, Topic with transaction.atomic(): ContactGroup.create_system_groups(self) ContactField.create_system_fields(self) - Topic.create_default_topic(self) + Team.create_system(self) + Topic.create_system(self) # outside of the transaction as it's going to call out to mailroom for flow validation if sample_flows: @@ -1339,6 +1344,7 @@ def delete(self) -> dict: delete_in_batches(self.tickets.all()) delete_in_batches(self.ticket_counts.all()) delete_in_batches(self.topics.all()) + delete_in_batches(self.teams.all()) delete_in_batches(self.airtime_transfers.all()) # delete our contacts diff --git a/temba/orgs/tests.py b/temba/orgs/tests.py index ee4cf0d8e6..68b29a757d 100644 --- a/temba/orgs/tests.py +++ b/temba/orgs/tests.py @@ -36,7 +36,7 @@ from temba.templates.models import TemplateTranslation from temba.tests import CRUDLTestMixin, TembaTest, matchers, mock_mailroom from temba.tests.base import get_contact_search -from temba.tickets.models import TicketExport +from temba.tickets.models import Team, TicketExport, Topic from temba.triggers.models import Trigger from temba.utils import json, languages from temba.utils.uuid import uuid4 @@ -1311,10 +1311,12 @@ def _create_campaign_content(self, org, user, fields, groups, flows, contacts, a add(FlowStartCount.objects.create(start=start1, count=1)) def _create_ticket_content(self, org, user, contacts, flows, add): - ticket1 = add(self.create_ticket(contacts[0])) + topic = add(Topic.create(org, user, "Spam")) + ticket1 = add(self.create_ticket(contacts[0], topic)) ticket1.events.create(org=org, contact=contacts[0], event_type="N", note="spam", created_by=user) add(self.create_ticket(contacts[0], opened_in=flows[0])) + add(Team.create(org, user, "Spam Only", topics=[topic])) def _create_export_content(self, org, user, flows, groups, fields, labels, add): results = add( diff --git a/temba/tickets/migrations/0066_team_is_default.py b/temba/tickets/migrations/0066_team_is_default.py new file mode 100644 index 0000000000..9591a9ca47 --- /dev/null +++ b/temba/tickets/migrations/0066_team_is_default.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.2 on 2024-10-23 22:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("tickets", "0065_team_all_topics"), + ] + + operations = [ + migrations.AddField( + model_name="team", + name="is_default", + field=models.BooleanField(default=False), + ), + ] diff --git a/temba/tickets/models.py b/temba/tickets/models.py index abcf0cf496..3b036492a0 100644 --- a/temba/tickets/models.py +++ b/temba/tickets/models.py @@ -53,19 +53,17 @@ class Topic(TembaModel, DependencyMixin): The topic of a ticket which controls who can access that ticket. """ - DEFAULT_TOPIC = "General" - org = models.ForeignKey(Org, on_delete=models.PROTECT, related_name="topics") is_default = models.BooleanField(default=False) org_limit_key = Org.LIMIT_TOPICS @classmethod - def create_default_topic(cls, org): + def create_system(cls, org): assert not org.topics.filter(is_default=True).exists(), "org already has default topic" org.topics.create( - name=cls.DEFAULT_TOPIC, + name="General", is_default=True, is_system=True, created_by=org.created_by, @@ -109,9 +107,23 @@ class Team(TembaModel): org = models.ForeignKey(Org, on_delete=models.PROTECT, related_name="teams") topics = models.ManyToManyField(Topic, related_name="teams") all_topics = models.BooleanField(default=False) + is_default = models.BooleanField(default=False) org_limit_key = Org.LIMIT_TEAMS + @classmethod + def create_system(cls, org): + assert not org.teams.filter(is_default=True).exists(), "org already has default team" + + org.teams.create( + name="All Topics", + is_default=True, + is_system=True, + all_topics=True, + created_by=org.created_by, + modified_by=org.modified_by, + ) + @classmethod def create(cls, org, user, name: str, *, topics=(), all_topics: bool = False): assert cls.is_valid_name(name), f"'{name}' is not a valid team name" @@ -126,8 +138,8 @@ def get_users(self): return self.org.users.filter(orgmembership__team=self) def release(self, user): - # remove all users from this team - OrgMembership.objects.filter(org=self.org, team=self).update(team=None) + # re-assign agents in this team to the default team + OrgMembership.objects.filter(org=self.org, team=self).update(team=self.org.default_ticket_team) self.name = self._deleted_name() self.is_active = False