From 54ee2acb040ba3bde3cf140ea25156c877daba1f Mon Sep 17 00:00:00 2001 From: muhammad-ammar Date: Thu, 21 Sep 2023 11:14:12 +0500 Subject: [PATCH] feat: update license assign view to set source for assigned licenses --- license_manager/apps/api/serializers.py | 34 ++++++++++++++++- license_manager/apps/api/v1/views.py | 37 ++++++++++++++++++- license_manager/apps/subscriptions/admin.py | 19 +++++++++- .../apps/subscriptions/constants.py | 1 + 4 files changed, 87 insertions(+), 4 deletions(-) diff --git a/license_manager/apps/api/serializers.py b/license_manager/apps/api/serializers.py index 0f050696..29e0d6b5 100644 --- a/license_manager/apps/api/serializers.py +++ b/license_manager/apps/api/serializers.py @@ -1,7 +1,9 @@ from rest_framework import serializers from rest_framework.fields import SerializerMethodField -from license_manager.apps.subscriptions.constants import ACTIVATED, ASSIGNED +from django.core.validators import MinLengthValidator + +from license_manager.apps.subscriptions.constants import ACTIVATED, ASSIGNED, SALESFORCE_ID_LENGTH from license_manager.apps.subscriptions.models import ( CustomerAgreement, License, @@ -355,12 +357,42 @@ class Meta: fields = LicenseAdminBulkActionSerializer.Meta.fields + CustomTextSerializer.Meta.fields +class SalesforceIdsSerializer(serializers.Serializer): # pylint: disable=abstract-method + """ + Serializer for specifying salesforce ids + + Requires that a list of valid, non-empty salesforce ids are submitted. + """ + user_sfids = serializers.ListField( + child=serializers.CharField( + allow_blank=False, + write_only=True, + ), + allow_empty=False, + required=False + ) + + class Meta: + fields = [ + 'user_sfids', + ] + + class LicenseAdminAssignActionSerializer(CustomTextWithMultipleEmailsSerializer): # pylint: disable=abstract-method """ Serializer for the license admin assign action. """ notify_users = serializers.BooleanField(required=False) + user_sfids = user_sfids = serializers.ListField( + child=serializers.CharField( + allow_blank=False, + write_only=True, + validators=[MinLengthValidator(SALESFORCE_ID_LENGTH)] + ), + allow_empty=False, + required=False + ) class Meta: fields = CustomTextWithMultipleEmailsSerializer.Meta.fields + [ diff --git a/license_manager/apps/api/v1/views.py b/license_manager/apps/api/v1/views.py index 1400ac1c..abfc9b9c 100644 --- a/license_manager/apps/api/v1/views.py +++ b/license_manager/apps/api/v1/views.py @@ -55,6 +55,8 @@ License, SubscriptionPlan, SubscriptionsRoleAssignment, + SubscriptionLicenseSource, + SubscriptionLicenseSourceType, ) from license_manager.apps.subscriptions.utils import ( chunks, @@ -726,6 +728,26 @@ def _assign_new_licenses(self, subscription_plan, user_emails): ) return licenses + def _set_source_for_assigned_licenses(self, assigned_licenses, emails_and_sfids): + """ + Set source for each assigned license. + """ + license_source = SubscriptionLicenseSourceType.get_source_type(SubscriptionLicenseSourceType.AMT) + source_objects = [] + for assigned_license in assigned_licenses: + sf_opportunity_id = emails_and_sfids.get(assigned_license.user_email) + source = SubscriptionLicenseSource( + license=assigned_license, + source_id=sf_opportunity_id, + source_type=license_source + ) + source_objects.append(source) + + SubscriptionLicenseSource.objects.bulk_create( + source_objects, + batch_size=constants.LICENSE_SOURCE_BULK_OPERATION_BATCH_SIZE + ) + @action(detail=False, methods=['post']) def assign(self, request, subscription_uuid=None): # pylint: disable=unused-argument """ @@ -769,8 +791,17 @@ def _assign(self, request, subscription_plan): # Validate the user_emails and text sent in the data self._validate_data(request.data) - # Dedupe all lowercase emails before turning back into a list for indexing - user_emails = list({email.lower() for email in request.data.get('user_emails', [])}) + emails_and_sfids = None + # remove duplicate emails and convert to lowercase + if 'user_sfids' in request.data: + user_emails = map(str.lower, request.data.get('user_emails', [])) + user_sfids = request.data.get('user_sfids', []) + + emails_and_sfids = dict(zip(user_emails, user_sfids)) + user_emails = list(emails_and_sfids.keys()) + else: + # Dedupe all lowercase emails before turning back into a list for indexing + user_emails = list({email.lower() for email in request.data.get('user_emails', [])}) user_emails, already_associated_emails = self._trim_already_associated_emails( subscription_plan, @@ -795,6 +826,8 @@ def _assign(self, request, subscription_plan): assigned_licenses = self._assign_new_licenses( subscription_plan, user_emails, ) + if emails_and_sfids: + self._set_source_for_assigned_licenses(assigned_licenses, emails_and_sfids) except DatabaseError: error_message = 'Database error occurred while assigning licenses, no assignments were completed' logger.exception(error_message) diff --git a/license_manager/apps/subscriptions/admin.py b/license_manager/apps/subscriptions/admin.py index dac23505..7425a894 100644 --- a/license_manager/apps/subscriptions/admin.py +++ b/license_manager/apps/subscriptions/admin.py @@ -47,7 +47,9 @@ class LicenseAdmin(DjangoQLSearchMixin, admin.ModelAdmin): 'activation_key', 'get_renewed_to', 'get_renewed_from', - 'auto_applied' + 'auto_applied', + 'source_id', + 'source_type', ] exclude = ['history', 'renewed_to'] list_display = ( @@ -79,8 +81,23 @@ class LicenseAdmin(DjangoQLSearchMixin, admin.ModelAdmin): def get_queryset(self, request): return super().get_queryset(request).select_related( 'subscription_plan', + 'source' ) + @admin.display(description='Source ID') + def source_id(self, instance): + try: + return instance.source.source_id + except License.source.RelatedObjectDoesNotExist: + return '' + + @admin.display(description='Source Type') + def source_type(self, instance): + try: + return instance.source.source_type.slug + except License.source.RelatedObjectDoesNotExist: + return '' + @admin.display( description='Subscription Plan' ) diff --git a/license_manager/apps/subscriptions/constants.py b/license_manager/apps/subscriptions/constants.py index f896254a..be56b914 100644 --- a/license_manager/apps/subscriptions/constants.py +++ b/license_manager/apps/subscriptions/constants.py @@ -104,6 +104,7 @@ class SegmentEvents: # Bulk operation constants LICENSE_BULK_OPERATION_BATCH_SIZE = 100 PENDING_ACCOUNT_CREATION_BATCH_SIZE = 100 +LICENSE_SOURCE_BULK_OPERATION_BATCH_SIZE = 100 # Num distinct catalog query validation batch size VALIDATE_NUM_CATALOG_QUERIES_BATCH_SIZE = 100