diff --git a/accounts/forms.py b/accounts/forms.py index a6eab95..ccb045e 100644 --- a/accounts/forms.py +++ b/accounts/forms.py @@ -401,7 +401,7 @@ class CreateCustomerForm(forms.Form): super().__init__(*args, **kwargs) self.fields['preferences'].queryset = EventCategory.objects.all() -class UpdateCustomerForm(forms.Form): +class UpdateOnboardedEventManagerForm(forms.Form): first_name = forms.CharField(max_length=255, required=True, label='First Name') last_name = forms.CharField(max_length=255, required=True, label='Last Name') business_name = forms.CharField(max_length=200, required=True, label="Business Name") @@ -442,5 +442,70 @@ class UpdateCustomerForm(forms.Form): super().__init__(*args, **kwargs) self.fields['preferences'].queryset = EventCategory.objects.all() + +class UpdateEventManagerForm(forms.ModelForm): + first_name = forms.CharField(max_length=255, required=True, label='First Name') + last_name = forms.CharField(max_length=255, required=True, label='Last Name') + business_name = forms.CharField(max_length=200, required=True, label="Business Name") + email = forms.EmailField(required=True, label='Email', widget=forms.TextInput(attrs={'readonly': 'readonly'})) + phone_no = PhoneNumberField( + widget=forms.TextInput(), + label="Phone No" + ) + address_line1 = forms.CharField(widget=forms.Textarea(attrs={'rows': 4, 'cols': 40})) + city = forms.CharField(max_length=200, required=False, label="Region") + country = forms.CharField(max_length=200, required=False, label="Country") + website = forms.URLField(max_length=255, required=False, label="Website") + linkedin_profile = forms.URLField(max_length=200, required=False, label="LinkedIn") + facebook_profile = forms.URLField(max_length=200, required=False, label="Facebook") + instagram_profile = forms.URLField(max_length=200, required=False, label="Instagram") + twitter_profile = forms.URLField(max_length=200, required=False, label="Twitter") + is_active = forms.BooleanField(required=False, label='Active', help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",) + + class Meta: + model = models.IAmPrincipal + fields = [ + "first_name", + "last_name", + "business_name", + "email", + "phone_no", + 'address_line1', + 'city', + 'country', + "website", + "linkedin_profile", + "facebook_profile", + "instagram_profile", + "twitter_profile", + "is_active", + ] + +class UpdateEventUserForm(forms.ModelForm): + first_name = forms.CharField(max_length=255, required=True, label='First Name') + last_name = forms.CharField(max_length=255, required=True, label='Last Name') + email = forms.EmailField(required=True, label='Email', widget=forms.TextInput(attrs={'readonly': 'readonly'})) + phone_no = PhoneNumberField( + widget=forms.TextInput(), + label="Phone No" + ) + address_line1 = forms.CharField(widget=forms.Textarea(attrs={'rows': 4, 'cols': 40})) + city = forms.CharField(max_length=200, required=False, label="Region") + country = forms.CharField(max_length=200, required=False, label="Country") + is_active = forms.BooleanField(required=False, label='Active', help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",) + + class Meta: + model = models.IAmPrincipal + fields = [ + "first_name", + "last_name", + "email", + "phone_no", + 'address_line1', + 'city', + 'country', + "is_active", + ] + class UploadExcelForm(forms.Form): file = forms.FileField() diff --git a/accounts/views.py b/accounts/views.py index 24f052f..6aa85b4 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -21,6 +21,7 @@ from django.urls import reverse_lazy from django.views import generic from django.db import models, transaction, IntegrityError from django.utils import timezone +import phonenumbers from accounts import permission from goodtimes import constants from goodtimes.services import EmailService @@ -28,6 +29,8 @@ from goodtimes.utils import JsonResponseUtil from manage_events.models import EventCategory, PrincipalPreference from manage_referrals.models import ReferralCode from manage_subscriptions.models import PrincipalSubscription, Subscription +import datetime +from datetime import datetime from . import resource_action from .forms import ( @@ -39,7 +42,9 @@ from .forms import ( IAmPrincipalRoleAppResourceActionLinkForm, IAmPrincipalGroupLinkForm, ProfileEditForm, - UpdateCustomerForm, + UpdateEventManagerForm, + UpdateEventUserForm, + UpdateOnboardedEventManagerForm, UploadExcelForm, ) from .models import ( @@ -183,10 +188,6 @@ class PrincipalListView(LoginRequiredMixin, generic.ListView): context["page_name"] = self.page_name return context - -import datetime - - class PrincipalCreateOrUpdateView(LoginRequiredMixin, generic.View): page_name = resource_action.RESOURCE_IAM_PRINCIPAL model = IAmPrincipal @@ -680,29 +681,13 @@ class CustomerUpdateView(LoginRequiredMixin, generic.View): page_name = resource_action.RESOURCE_MANAGE_CUSTOMER resource = resource_action.RESOURCE_MANAGE_CUSTOMER model = IAmPrincipal - form_class = UpdateCustomerForm - template_name = "accounts/customer/customer_edit.html" + template_name = None success_url = reverse_lazy("accounts:customer_list") success_message = "Updated Successfully" error_message = "An error occurred while saving the data." - def get_context_data(self, **kwargs): - context = { - "page_name": self.page_name, - "operation": "Edit", - } - context.update(kwargs) # Include any additional context data passed to the view - return context - - def get(self, request, *args, **kwargs): - principal_id = kwargs.get("pk") - try: - principal_obj = IAmPrincipal.objects.get(pk=principal_id) - except Exception as e: - messages.error(request, f"No Record of id {principal_id} is found") - return redirect(self.success_url) - - print(f"principal address is {principal_obj.address_line1}") + def OnboardedEventManagerFormData(self, principal_obj): + form_class = UpdateOnboardedEventManagerForm initial_data = { "first_name": principal_obj.first_name, @@ -735,7 +720,40 @@ class CustomerUpdateView(LoginRequiredMixin, generic.View): initial_data["free_start_date"] = None initial_data["free_end_date"] = None - form = self.form_class(initial=initial_data) + return form_class(initial=initial_data) + + def get_context_data(self, **kwargs): + context = { + "page_name": self.page_name, + "operation": "Edit", + } + context.update(kwargs) # Include any additional context data passed to the view + return context + + def get(self, request, *args, **kwargs): + principal_id = kwargs.get("pk") + try: + principal_obj = IAmPrincipal.objects.get(pk=principal_id) + except Exception as e: + messages.error(request, f"No Record of id {principal_id} is found") + return redirect(self.success_url) + + print(f"principal address is {principal_obj.address_line1}") + + is_onboarded = False + if hasattr(principal_obj, 'extended_data') and principal_obj.extended_data: + is_onboarded = principal_obj.extended_data.is_onboarded + + if is_onboarded and principal_obj.principal_type.name == resource_action.PRINCIPAL_TYPE_EVENT_MANAGER: + form = self.OnboardedEventManagerFormData(principal_obj) + self.template_name = "accounts/customer/customer_onboard_manager_edit.html" + elif not is_onboarded and principal_obj.principal_type.name == resource_action.PRINCIPAL_TYPE_EVENT_MANAGER: + form = UpdateEventManagerForm(instance=principal_obj) + self.template_name = "accounts/customer/customer_manager_edit.html" + elif principal_obj.principal_type.name == resource_action.PRINCIPAL_TYPE_EVENT_USER: + form = UpdateEventUserForm(instance=principal_obj) + self.template_name = "accounts/customer/customer_user_edit.html" + context = self.get_context_data(form=form, principal_obj=principal_obj) print("context dta is ", context) return render(request, self.template_name, context=context) @@ -747,10 +765,31 @@ class CustomerUpdateView(LoginRequiredMixin, generic.View): except Exception as e: messages.error(request, f"No Record of customer id {principal_id} is found") return redirect(self.success_url) - form = self.form_class(request.POST) + + is_onboarded = False + if hasattr(principal_obj, 'extended_data') and principal_obj.extended_data: + is_onboarded = principal_obj.extended_data.is_onboarded + + # Dynamically choose the form based on the principal object's data + if is_onboarded and principal_obj.principal_type.name == resource_action.PRINCIPAL_TYPE_EVENT_MANAGER: + form_class = UpdateOnboardedEventManagerForm + self.template_name = "accounts/customer/customer_onboard_manager_edit.html" + elif not is_onboarded and principal_obj.principal_type.name == resource_action.PRINCIPAL_TYPE_EVENT_MANAGER: + form_class = UpdateEventManagerForm + self.template_name = "accounts/customer/customer_manager_edit.html" + elif principal_obj.principal_type.name == resource_action.PRINCIPAL_TYPE_EVENT_USER: + form_class = UpdateEventUserForm + self.template_name = "accounts/customer/customer_user_edit.html" + else: + messages.error(request, "Invalid principal type") + return redirect(self.success_url) + + form = form_class(request.POST, instance=principal_obj) + if not form.is_valid(): context = self.get_context_data(form=form) return render(request, self.template_name, context=context) + try: with transaction.atomic(): # update principal data @@ -768,26 +807,27 @@ class CustomerUpdateView(LoginRequiredMixin, generic.View): principal_obj.twitter_profile = form.cleaned_data.get("twitter_profile") principal_obj.save() - # update principal preferences record - principal_preference, _ = PrincipalPreference.objects.get_or_create(principal=principal_obj) - principal_preference.preferred_categories.set(form.cleaned_data.get("preferences")) + if is_onboarded: # only update preference and subscription if it is added by admin + # update principal preferences record + principal_preference, _ = PrincipalPreference.objects.get_or_create(principal=principal_obj) + principal_preference.preferred_categories.set(form.cleaned_data.get("preferences")) - # update principal subscription record - principal_subscription = PrincipalSubscription.objects.filter(principal=principal_obj).order_by('-end_date').first() - if principal_subscription: - principal_subscription.start_date = form.cleaned_data.get("free_start_date") - principal_subscription.end_date = form.cleaned_data.get("free_end_date") - principal_subscription.grace_period_end_date = form.cleaned_data.get("free_end_date") + datetime.timedelta(days=15) - principal_subscription.save() - else: - PrincipalSubscription.objects.create( - principal=principal_obj, - start_date=form.cleaned_data.get("free_start_date"), - end_date=form.cleaned_data.get("free_end_date"), - grace_period_end_date=PrincipalSubscription.generate_grace_period_end_date(form.cleaned_data.get("free_end_date")), - is_paid=True, - subscription=Subscription.objects.filter().first() # Assuming you want to link a default subscription - ) + # update principal subscription record + principal_subscription = PrincipalSubscription.objects.filter(principal=principal_obj).order_by('-end_date').first() + if principal_subscription: + principal_subscription.start_date = form.cleaned_data.get("free_start_date") + principal_subscription.end_date = form.cleaned_data.get("free_end_date") + principal_subscription.grace_period_end_date = form.cleaned_data.get("free_end_date") + datetime.timedelta(days=15) + principal_subscription.save() + else: + PrincipalSubscription.objects.create( + principal=principal_obj, + start_date=form.cleaned_data.get("free_start_date"), + end_date=form.cleaned_data.get("free_end_date"), + grace_period_end_date=PrincipalSubscription.generate_grace_period_end_date(form.cleaned_data.get("free_end_date")), + is_paid=True, + subscription=Subscription.objects.filter().first() # Assuming you want to link a default subscription + ) messages.success(self.request, self.success_message) return redirect(self.success_url) @@ -941,10 +981,10 @@ def export_excel_template(request): 'Last Name', 'Business Name', 'Email', - 'Phone No', + 'Phone No (+919999999999)', 'Preferences (should be separated by comma)', - 'Free period start date (YYYY-MM-DD)', - 'Free period end date (YYYY-MM-DD)', + 'Free period start date (DD-MM-YYYY)', + 'Free period end date (DD-MM-YYYY)', 'Address', 'Region', 'Country', @@ -976,10 +1016,10 @@ def export_excel_template(request): ['Last Name', 'The last name of the customer. This is a required field.'], ['Business Name', 'The official name of the customer\'s business or organization.'], ['Email', 'The email address of the customer. This must be a unique email not already used in the system. This is a required Field'], - ['Phone No', 'The business phone number. It should include the country code if applicable.'], + ['Phone No', 'The business phone number. It should include the country code if applicable (+919999999999).'], ['Category', 'A comma-separated list of event categories the customer is interested in. These should match existing categories in the system. This is a required Field'], - ['Free period start date', 'The start date of the customer\'s free trial period, formatted as YYYY-MM-DD.'], - ['Free period end date', 'The end date of the customer\'s free trial period, formatted as YYYY-MM-DD. This date must be later than the start date.'], + ['Free period start date', 'The start date of the customer\'s free trial period, formatted as DD-MM-YYYY.'], + ['Free period end date', 'The end date of the customer\'s free trial period, formatted as DD-MM-YYYY. This date must be later than the start date.'], ['Address', 'The complete business address, including street, city, and postal code.'], ['Region', 'The geographic region where the business operates.'], ['Country', 'The country where the business is based.'], @@ -1029,7 +1069,7 @@ class CustomerTransferView(LoginRequiredMixin, generic.View): "accounts/customer/account_transfer_email_template.html", locals() ) email_service.send() - + principal_preference = IAmPrincipalExtendedData.objects.get(principal=principal_obj) principal_preference.is_transferred = True principal_preference.save() @@ -1054,6 +1094,36 @@ class CustomerImportView(LoginRequiredMixin, generic.View): context.update(kwargs) # Include any additional context data passed to the view return context + def validate_date(self, date_str, row_num, error_log, field_name): + """function to validate the date format DD-MM-YYYY""" + # Check if the input is already a datetime object + if isinstance(date_str, datetime): + return date_str + + # If it's a string, attempt to validate it + if isinstance(date_str, str): + try: + return datetime.strptime(date_str, '%d-%m-%Y') + except ValueError: + error_log.append(f"Row {row_num}: {field_name} '{date_str}' is not in the format DD-MM-YYYY.") + return None + + # If it's neither a string nor a datetime object, log an error + error_log.append(f"Row {row_num}: {field_name} '{date_str}' is of invalid type.") + return None + + def validate_phone(self, phone_str, row_num, error_log): + """Helper function to validate phone number""" + try: + phone_number = phonenumbers.parse(phone_str, None) + if not phonenumbers.is_valid_number(phone_number): + error_log.append(f"Row {row_num}: Phone number '{phone_str}' is not valid.") + return None + return phone_number + except Exception as e: + error_log.append(f"Row {row_num}: Phone number '{phone_str}' could not be parsed.") + return None + def get(self, request, *args, **kwargs): form = self.form_class() context = self.get_context_data(form=form) @@ -1104,11 +1174,24 @@ class CustomerImportView(LoginRequiredMixin, generic.View): error_log.append(f"Row {idx}: Email {email} already exists.") continue + # Validate start_date and end_date formats + start_date = self.validate_date(start_date, idx, error_log, 'Start Date') + end_date = self.validate_date(end_date, idx, error_log, 'End Date') + + if not start_date or not end_date: + continue # Skip if dates are invalid + # validate date rnage if end_date < start_date: error_log.append(f"Row {idx}: End date {end_date} must greater then start date {start_date}.") continue + # Validate phone number + if phone_no: + phone_number = self.validate_phone(str(phone_no), idx, error_log) + if not phone_number: + continue # Skip if phone number is invalid + # validate preferences preference_list = [pref.strip() for pref in preferences.split(',')] event_categories = EventCategory.objects.filter(title__in=preference_list) diff --git a/goodtimes/services.py b/goodtimes/services.py index fbf3ef8..92848f2 100644 --- a/goodtimes/services.py +++ b/goodtimes/services.py @@ -568,7 +568,7 @@ class GoogleMapsservice: def search_addresses_containing(self, keyword): """ Search for a list of addresses containing the given keyword. - + :param keyword: Keyword to search for in addresses :return: List of matching addresses containing the keyword """ @@ -589,49 +589,51 @@ class GoogleMapsservice: Returns: QuerySet: The filtered and sorted queryset of events. """ - - # Set the origin to the provided latitude and longitude origins = [(latitude, longitude)] - # Create a list of destination coordinates for all events with valid venues - destinations = [ - (event.venue.latitude, event.venue.longitude) + # Create a list of destination coordinates and map them to the events + destinations_and_events = [ + ((event.venue.latitude, event.venue.longitude), event) for event in queryset if event.venue.latitude and event.venue.longitude ] - # If there is no destination coordinates - if not destinations: + if not destinations_and_events: return queryset - # Get the distance matrix from the Google Maps API - matrix = self.get_distance_matrix(origins, destinations) + # Batch size for Google Distance Matrix API (max 25 elements) + batch_size = 25 + distances = {} - # Create a dictionary of event IDs and their corresponding distances - distances = { - event.id: element["distance"]["value"] - for event, element in zip(queryset, matrix["rows"][0]["elements"]) - if element["status"] == "OK" and element["distance"]["value"] <= radius_km * 1000 # Convert km to meters - } + # Loop through batches of destinations + for i in range(0, len(destinations_and_events), batch_size): + batch = destinations_and_events[i:i + batch_size] + print(f"batch list count is {len(batch)}") + destinations = [coords for coords, _ in batch] + events_in_batch = [event for _, event in batch] - print(f"distance is {distances} and distances key is {distances.keys()}") + # Call the Google Maps API for the current batch + matrix = self.get_distance_matrix(origins, destinations) + + # Extract distances and associate them with events + for event, element in zip(events_in_batch, matrix["rows"][0]["elements"]): + if element["status"] == "OK" and element["distance"]["value"] <= radius_km * 1000: # Convert km to meters + distances[event.id] = element["distance"]["value"] + + if not distances: + return queryset.none() # Filter the queryset to include only events within the specified radius queryset = queryset.filter(id__in=distances.keys()) - print(f"query set after distance filter {queryset}") - # Sort the event IDs by their distances in ascending order event_ids_by_distance = sorted(distances, key=distances.get) - print(f"sort event by it distance {event_ids_by_distance}") # Create a Case/When expression to preserve the order of events by distance preserved_order = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(event_ids_by_distance)]) - print(f"preserved_order is {preserved_order}") + # Order the queryset based on the preserved order queryset = queryset.order_by(preserved_order) - for data in queryset: - print(f"queryset after preserverd order {data.id}") return queryset diff --git a/goodtimes/webhook/webhook_service.py b/goodtimes/webhook/webhook_service.py index ba8cd4f..c2b9f5a 100644 --- a/goodtimes/webhook/webhook_service.py +++ b/goodtimes/webhook/webhook_service.py @@ -22,7 +22,7 @@ class WebhookService: def _fetch_metadata(self): """Fetch metadata based on the event type.""" - if self._event_type == "checkout.session.completed": + if self._event_type in ["checkout.session.expired", "checkout.session.completed"]: return self._charge_data.get("metadata", {}) elif self._event_type == "invoice.payment_succeeded": subscription_id = self._charge_data.get("subscription") diff --git a/manage_subscriptions/api/views.py b/manage_subscriptions/api/views.py index cb32eab..a912406 100644 --- a/manage_subscriptions/api/views.py +++ b/manage_subscriptions/api/views.py @@ -121,48 +121,6 @@ class CreatePrincipalSubscriptionApi(APIView): return ApiResponse.error(**fail_response) -# class CreatePrincipalSubscriptionApi(APIView): -# authentication_classes = [JWTAuthentication] -# permission_classes = [IsAuthenticated] - -# def post(self, request): -# serializer = PrincipalSubscriptionSerializer(data=request.data) - -# if serializer.is_valid(): -# subscription_id = serializer.validated_data.get("subscription").id -# try: -# subscription = Subscription.objects.get(id=subscription_id) -# except Subscription.DoesNotExist: -# return ApiResponse.error( -# status=status.HTTP_404_NOT_FOUND, message="Subscription not found." -# ) - -# start_date = timezone.localtime().date() -# end_date = start_date + timedelta(days=subscription.plan.days) -# grace_period_end_date = end_date + timedelta(days=15) - -# # You can directly pass the additional fields as save method arguments -# instance = serializer.save( -# start_date=start_date, -# end_date=end_date, -# grace_period_end_date=grace_period_end_date, -# created_by=request.user, # Assuming your model has this field and you want to track who created the subscription -# ) - -# success_response = { -# "status": status.HTTP_201_CREATED, # Use 201 for successful resource creation -# "message": "Success", -# "data": serializer.data, -# } -# return ApiResponse.success(**success_response) - -# else: -# fail_response = { -# "status": status.HTTP_400_BAD_REQUEST, -# "message": "Validation Failed", -# "errors": serializer.errors, -# } -# return ApiResponse.error(**fail_response) @method_decorator(csrf_exempt, name="dispatch") @@ -177,28 +135,29 @@ class StripeWebhookTest(APIView): sig_header = request.META["HTTP_STRIPE_SIGNATURE"] endpoint_secret = "whsec_ccf1f87295603cdd1733995ee2d3c0d6f74c7ceaf28916ea45114a54b7ce1d0f" # Make sure to retrieve this from your settings event = None + webhook_event = None + try: + # Construct Stripe event event = stripe.Event.construct_from(json.loads(payload), stripe.api_key) event_id = event["id"] event_type = event["type"] stripe_subscription_id = event["data"]["object"].get("subscription") + # Retrieve subscription details if available stripe_subscription = ( stripe.Subscription.retrieve(stripe_subscription_id) if stripe_subscription_id else None ) - current_period_start = ( - stripe_subscription["current_period_start"] - if stripe_subscription - else None - ) - current_period_end = ( - stripe_subscription["current_period_end"] - if stripe_subscription - else None - ) + current_period_start = stripe_subscription["current_period_start"] if stripe_subscription else None + current_period_end = stripe_subscription["current_period_end"] if stripe_subscription else None + + # Log received event details + logger.info(f"Received event {event_type} with ID {event_id}") + + # Get or create WebhookEvent in DB webhook_event, created = WebhookEvent.objects.get_or_create( event_id=event_id, defaults={ @@ -208,59 +167,80 @@ class StripeWebhookTest(APIView): ) if not created and webhook_event.status == "processed": + logger.info(f"Event {event_id} already processed.") return ApiResponse.success( status=status.HTTP_208_ALREADY_REPORTED, - message="Event already processed", + message="Event already processed.", ) + # Process the event payment_service = PaymentProcessingService( webhook_data=event, stripe_subscription=stripe_subscription_id, current_period_start=current_period_start, current_period_end=current_period_end, ) - payment_service.process_event() + + # Mark event as successfully processed webhook_event.status = "processed" webhook_event.processed_at = timezone.now() webhook_event.save() + logger.info(f"Event {event_id} processed successfully.") return ApiResponse.success( - status=status.HTTP_200_OK, message="Event processed successfully" + status=status.HTTP_200_OK, message="Event processed successfully." ) except stripe.error.SignatureVerificationError as e: - logger.error(f"Invalid Stripe signature: {str(e)}") + logger.error(f"Invalid Stripe signature for event: {str(e)}") return ApiResponse.error( status=status.HTTP_400_BAD_REQUEST, - message="Invalid signature", + message="Invalid signature.", errors=str(e), ) + except ValueError as e: - logger.error(f"Invalid payload: {str(e)}") + logger.error(f"Invalid payload for event: {str(e)}") return ApiResponse.error( status=status.HTTP_400_BAD_REQUEST, - message="Invalid payload", + message="Invalid payload.", errors=str(e), ) - except Transaction.DoesNotExist as e: - logger.error(f"Transaction does not exist: {str(e)}") + + except stripe.error.InvalidRequestError as e: + logger.error(f"Invalid request for event: {str(e)}") return ApiResponse.error( - status=status.HTTP_404_NOT_FOUND, - message="Transaction not found", + status=status.HTTP_400_BAD_REQUEST, + message="Invalid request to Stripe.", errors=str(e), ) + + except stripe.error.StripeError as e: + logger.error(f"General Stripe error: {str(e)}") + return ApiResponse.error( + status=status.HTTP_500_INTERNAL_SERVER_ERROR, + message="Stripe error occurred.", + errors=str(e), + ) + except Exception as e: - logger.error(f"Error processing webhook event: {str(e)}") + logger.error(f"Unexpected error processing event {event_id}: {str(e)}") if "webhook_event" in locals(): webhook_event.status = "failed" webhook_event.error_message = str(e) webhook_event.save() + return ApiResponse.error( status=status.HTTP_500_INTERNAL_SERVER_ERROR, - message="Error processing event", + message="Unexpected error processing event.", errors=str(e), ) + finally: + print(f"finally is runn") + webhook_event.status = "processed" + webhook_event.processed_at = timezone.now() + webhook_event.save() class LastActiveSubscriptionView(APIView): diff --git a/manage_subscriptions/views.py b/manage_subscriptions/views.py index 75335bf..e2af70e 100644 --- a/manage_subscriptions/views.py +++ b/manage_subscriptions/views.py @@ -535,6 +535,7 @@ def create_checkout_session(request): "metadata": { "transaction_amount": str(subscription.amount), "principal": str(principal_id), + "principal_email": str(request.user.email), "subscription_id": str(subscription.id), "product_id": subscription.product_id, "couponCode": coupon_code if coupon_code else None, diff --git a/templates/accounts/customer/customer_list.html b/templates/accounts/customer/customer_list.html index 0f04305..caf4463 100644 --- a/templates/accounts/customer/customer_list.html +++ b/templates/accounts/customer/customer_list.html @@ -129,7 +129,6 @@ -->