diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0df3bc3cc0..d9d5893071 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -15,6 +15,10 @@ Change Log Unreleased ---------- +[4.13.8] +--------- +* feat: adding an activated_at value to group membership records + [4.13.7] --------- * fix: adding get_queryset for fix of integrated channel api logs loading diff --git a/enterprise/__init__.py b/enterprise/__init__.py index f85bffb4cc..a0194205ae 100644 --- a/enterprise/__init__.py +++ b/enterprise/__init__.py @@ -2,4 +2,4 @@ Your project description goes here. """ -__version__ = "4.13.7" +__version__ = "4.13.8" diff --git a/enterprise/api/v1/views/enterprise_group.py b/enterprise/api/v1/views/enterprise_group.py index 8c324b1225..951aace252 100644 --- a/enterprise/api/v1/views/enterprise_group.py +++ b/enterprise/api/v1/views/enterprise_group.py @@ -17,6 +17,7 @@ from enterprise.api.v1 import serializers from enterprise.api.v1.views.base_views import EnterpriseReadWriteModelViewSet from enterprise.logging import getEnterpriseLogger +from enterprise.utils import localized_utcnow LOGGER = getEnterpriseLogger(__name__) @@ -195,6 +196,7 @@ def assign_learners(self, request, group_uuid): # Extend the list of memberships that need to be created associated with existing Users ent_customer_users = [ models.EnterpriseGroupMembership( + activated_at=localized_utcnow(), enterprise_customer_user=ecu, group=group ) diff --git a/enterprise/migrations/0203_auto_20240312_1527.py b/enterprise/migrations/0203_auto_20240312_1527.py new file mode 100644 index 0000000000..29635c9c74 --- /dev/null +++ b/enterprise/migrations/0203_auto_20240312_1527.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.23 on 2024-03-12 15:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('enterprise', '0202_enterprisegroup_applies_to_all_contexts_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='enterprisegroupmembership', + name='activated_at', + field=models.DateTimeField(blank=True, default=None, help_text='The moment at which the membership record is written with an Enterprise Customer User record.', null=True), + ), + migrations.AddField( + model_name='historicalenterprisegroupmembership', + name='activated_at', + field=models.DateTimeField(blank=True, default=None, help_text='The moment at which the membership record is written with an Enterprise Customer User record.', null=True), + ), + ] diff --git a/enterprise/models.py b/enterprise/models.py index c47b9caf05..9dbb8d5b3b 100644 --- a/enterprise/models.py +++ b/enterprise/models.py @@ -1484,6 +1484,7 @@ def fulfill_pending_group_memberships(self, enterprise_customer_user): enterprise_customer_user: a EnterpriseCustomerUser instance """ self.memberships.update( + activated_at=localized_utcnow(), pending_enterprise_customer_user=None, enterprise_customer_user=enterprise_customer_user ) @@ -4308,6 +4309,14 @@ class EnterpriseGroupMembership(TimeStampedModel, SoftDeletableModel): related_name='memberships', on_delete=models.deletion.CASCADE, ) + activated_at = models.DateTimeField( + default=None, + blank=True, + null=True, + help_text=_( + "The moment at which the membership record is written with an Enterprise Customer User record." + ), + ) history = HistoricalRecords() class Meta: diff --git a/tests/test_enterprise/api/test_views.py b/tests/test_enterprise/api/test_views.py index c08a05d97c..5ae7d75132 100644 --- a/tests/test_enterprise/api/test_views.py +++ b/tests/test_enterprise/api/test_views.py @@ -7690,6 +7690,28 @@ def test_group_applies_to_all_contexts_learner_list(self): for result in results: assert (result.get('pending_learner_id') == pending_user.id) or (result.get('learner_id') == new_user.id) + def test_group_assign_realized_learner_adds_activated_at(self): + """ + Test that newly created membership records associated with an existing user have an activated at value written + but records associated with pending memberships do not. + """ + url = settings.TEST_SERVER + reverse( + 'enterprise-group-assign-learners', + kwargs={'group_uuid': self.group_2.uuid}, + ) + request_data = {'learner_emails': f"{UserFactory().email},email@example.com"} + self.client.post(url, data=request_data) + membership = EnterpriseGroupMembership.objects.filter( + group=self.group_2, + pending_enterprise_customer_user__isnull=True + ).first() + assert membership.activated_at + pending_membership = EnterpriseGroupMembership.objects.filter( + group=self.group_2, + enterprise_customer_user__isnull=True + ).first() + assert not pending_membership.activated_at + @mark.django_db class TestEnterpriseCustomerSsoConfigurationViewSet(APITest): diff --git a/tests/test_enterprise/test_signals.py b/tests/test_enterprise/test_signals.py index e93f14e758..ef5e6c47ff 100644 --- a/tests/test_enterprise/test_signals.py +++ b/tests/test_enterprise/test_signals.py @@ -256,10 +256,11 @@ def test_handle_user_post_save_fulfills_pending_group_memberships(self): email = "jackie.chan@hollywood.com" user = UserFactory(id=1, email=email) pending_user = PendingEnterpriseCustomerUserFactory(user_email=email) - EnterpriseGroupMembershipFactory( + new_membership = EnterpriseGroupMembershipFactory( pending_enterprise_customer_user=pending_user, enterprise_customer_user=None ) + assert not new_membership.activated_at parameters = {"instance": user, "created": False} handle_user_post_save(mock.Mock(), **parameters) # Should delete pending link @@ -267,8 +268,10 @@ def test_handle_user_post_save_fulfills_pending_group_memberships(self): assert len(EnterpriseGroupMembership.objects.all()) == 1 new_enterprise_user = EnterpriseCustomerUser.objects.get(user_id=user.id) - assert EnterpriseGroupMembership.objects.first().pending_enterprise_customer_user is None - assert EnterpriseGroupMembership.objects.first().enterprise_customer_user == new_enterprise_user + membership = EnterpriseGroupMembership.objects.first() + assert membership.pending_enterprise_customer_user is None + assert membership.enterprise_customer_user == new_enterprise_user + assert membership.activated_at @mark.django_db