From 3a1a05aa24dc2ad46218c6c00636e09bc861e771 Mon Sep 17 00:00:00 2001 From: rizwanisready Date: Tue, 5 Mar 2024 22:04:56 +0530 Subject: [PATCH] referral tracking, wallet, transaction --- accounts/api/serializers.py | 23 ++- accounts/api/views.py | 2 +- manage_referrals/forms.py | 40 ++++- manage_referrals/models.py | 8 +- manage_referrals/urls.py | 17 +++ manage_referrals/views.py | 111 +++++++++++++- manage_subscriptions/api/urls.py | 1 + manage_subscriptions/api/views.py | 35 ++++- manage_subscriptions/urls.py | 1 + manage_wallets/forms.py | 52 +++++++ manage_wallets/urls.py | 2 +- templates/manage_events/event_add.html | 2 +- .../manage_events/event_category_add.html | 2 +- templates/manage_events/event_master_add.html | 2 +- templates/manage_referrals/record_list.html | 1 + templates/manage_referrals/track_add.html | 135 +++++++++++++++++ templates/manage_referrals/track_list.html | 140 ++++++++++++++++++ templates/manage_subscriptions/plan_add.html | 2 +- .../principal_subscription_add.html | 2 +- .../principal_subscriptions_list.html | 4 +- .../subscription_add.html | 2 +- templates/manage_wallets/payment_list.html | 18 +-- templates/manage_wallets/wallet_list.html | 35 ++--- 23 files changed, 580 insertions(+), 57 deletions(-) create mode 100644 manage_wallets/forms.py create mode 100644 templates/manage_referrals/track_add.html create mode 100644 templates/manage_referrals/track_list.html diff --git a/accounts/api/serializers.py b/accounts/api/serializers.py index 008465d..51bf2dc 100644 --- a/accounts/api/serializers.py +++ b/accounts/api/serializers.py @@ -1,5 +1,5 @@ import re - +from django.utils import timezone from django.contrib.auth.hashers import make_password from rest_framework import serializers @@ -13,6 +13,7 @@ from manage_referrals.models import ( ReferralRecord, ) from goodtimes import constants, date_utils +from manage_subscriptions.models import PrincipalSubscription, SubscriptionStatus def clean_phone(number: str): @@ -138,6 +139,7 @@ class ProfileSerializer(serializers.ModelSerializer): email = serializers.CharField(read_only=True) invite_count = serializers.SerializerMethodField(read_only=True) principal_type_name = serializers.SerializerMethodField(read_only=True) + has_active_subscription = serializers.SerializerMethodField(read_only=True) class Meta: model = IAmPrincipal @@ -150,6 +152,7 @@ class ProfileSerializer(serializers.ModelSerializer): "email", "invite_count", "register_complete", + "has_active_subscription", ] def update(self, instance, validated_data): @@ -162,10 +165,7 @@ class ProfileSerializer(serializers.ModelSerializer): def get_invite_count(self, obj): if obj: - principal_type = self.context.get( - "principal_type" - ) # Retrieve the principal_type from the context - return ReferralRecord.get_invite_count(obj, principal_type) + return ReferralRecord.get_invite_count(obj) return 0 def get_principal_type_name(self, obj): @@ -177,6 +177,19 @@ class ProfileSerializer(serializers.ModelSerializer): return request.build_absolute_uri(image_field.url) return "" + def get_has_active_subscription(self, obj): + today = timezone.now().date() + try: + last_active_subscription = PrincipalSubscription.objects.filter( + principal=obj, + cancelled=False, + status=SubscriptionStatus.ACTIVE, + end_date__gte=today, + ).latest("end_date") + return True # If the query does not raise DoesNotExist, return True + except PrincipalSubscription.DoesNotExist: + return False # If no matching subscription is found, return False + def to_representation(self, instance): data = super().to_representation(instance) request = self.context.get("request") diff --git a/accounts/api/views.py b/accounts/api/views.py index 809d76a..fd83a2e 100644 --- a/accounts/api/views.py +++ b/accounts/api/views.py @@ -709,7 +709,6 @@ class GoogleLoginAPIView(APIView): principal, created = IAmPrincipal.objects.update_or_create( email=email, - username=email, defaults=defaults, ) principal_type_instance = IAmPrincipalType.objects.get(name=principal_type) @@ -718,6 +717,7 @@ class GoogleLoginAPIView(APIView): print("New Created") default_password = f"GTMES{email[::-1]}" principal.set_password(default_password) + principal.username = email principal.principal_type = principal_type_instance # Assuming principal_type should only be set on creation principal.principal_source = google_source principal.save() diff --git a/manage_referrals/forms.py b/manage_referrals/forms.py index 7971dbc..950f801 100644 --- a/manage_referrals/forms.py +++ b/manage_referrals/forms.py @@ -1,5 +1,10 @@ from django import forms -from .models import ReferralRecord, ReferralRecordReward, GoodTimeCoins +from .models import ( + ReferralRecord, + ReferralRecordReward, + GoodTimeCoins, + ReferralTracking, +) class ReferralRecordForm(forms.ModelForm): @@ -35,7 +40,32 @@ class GoodTimeCoinsForm(forms.ModelForm): class Meta: model = GoodTimeCoins fields = ["value_in_pound", "comments"] - # widgets = { - # "start_date": forms.DateInput(attrs={"type": "date"}), - # "end_date": forms.DateInput(attrs={"type": "date"}), - # } + + +class ReferralTrackingForm(forms.ModelForm): + class Meta: + model = ReferralTracking + fields = [ + "referral_record", + "referrer_subscription_id", + "referred_subscription_id", + "is_referrer_subscribed", + "ip_address", + "user_agent", + "device_model", + ] + widgets = { + "referral_record": forms.Select(attrs={"class": "form-control"}), + "referrer_subscription_id": forms.NumberInput( + attrs={"class": "form-control"} + ), + "referred_subscription_id": forms.NumberInput( + attrs={"class": "form-control"} + ), + "is_referrer_subscribed": forms.CheckboxInput( + attrs={"class": "form-check-input"} + ), + "ip_address": forms.TextInput(attrs={"class": "form-control"}), + "user_agent": forms.TextInput(attrs={"class": "form-control"}), + "device_model": forms.TextInput(attrs={"class": "form-control"}), + } diff --git a/manage_referrals/models.py b/manage_referrals/models.py index e54488b..1180741 100644 --- a/manage_referrals/models.py +++ b/manage_referrals/models.py @@ -117,15 +117,15 @@ class ReferralRecord(BaseModel): return f"Referral ID: {self.id}, Referrar name: {self.referrer_principal.first_name}, Type: {self.principal_type.name}" @classmethod - def filter_invite_records(cls, referrer_principal, principal_type): + def filter_invite_records(cls, referrer_principal): record_instance = cls.objects.filter( - referrer_principal=referrer_principal, principal_type__name=principal_type + referrer_principal=referrer_principal, ) return record_instance @classmethod - def get_invite_count(cls, referrer_principal, principal_type): - filter_record = cls.filter_invite_records(referrer_principal, principal_type) + def get_invite_count(cls, referrer_principal): + filter_record = cls.filter_invite_records(referrer_principal) return filter_record.count() diff --git a/manage_referrals/urls.py b/manage_referrals/urls.py index 67f0219..3d14636 100644 --- a/manage_referrals/urls.py +++ b/manage_referrals/urls.py @@ -67,4 +67,21 @@ urlpatterns = [ views.GTCoinsDeleteView.as_view(), name="coin_delete", ), + # Referral Tracking + path("track/list/", views.ReferralTrackingView.as_view(), name="track_list"), + # path( + # "track/add/", + # views.ReferralTrackingCreateOrUpdateView.as_view(), + # name="track_add", + # ), + path( + "track/edit//", + views.ReferralTrackingCreateOrUpdateView.as_view(), + name="track_edit", + ), + path( + "track/delete/", + views.ReferralTrackingDeleteView.as_view(), + name="track_delete", + ), ] diff --git a/manage_referrals/views.py b/manage_referrals/views.py index 32982cf..63cbca5 100644 --- a/manage_referrals/views.py +++ b/manage_referrals/views.py @@ -9,8 +9,14 @@ from manage_referrals.forms import ( ReferralRecordForm, ReferralRecordRewardForm, GoodTimeCoinsForm, + ReferralTrackingForm, +) +from manage_referrals.models import ( + ReferralRecord, + ReferralRecordReward, + GoodTimeCoins, + ReferralTracking, ) -from manage_referrals.models import ReferralRecord, ReferralRecordReward, GoodTimeCoins from django.contrib import messages @@ -336,3 +342,106 @@ class GTCoinsDeleteView(LoginRequiredMixin, generic.View): messages.success(request, self.error_message) return redirect(self.success_url) + + +class ReferralTrackingCreateOrUpdateView(LoginRequiredMixin, generic.View): + # Set the page_name and resource + page_name = resource_action.RESOURCE_MANAGE_REFERRALS + resource = resource_action.RESOURCE_MANAGE_REFERRALS + + # Initialize the action as ACTION_CREATE (can change based on logic) + action = resource_action.ACTION_CREATE # Default action + + template_name = "manage_referrals/track_add.html" + model = ReferralTracking + form_class = ReferralTrackingForm + success_url = reverse_lazy("manage_referrals:track_list") + error_message = "An error occurred while saving the data." + + # Determine the success message dynamically based on whether it's an update or create + def get_success_message(self): + self.success_message = ( + constants.RECORD_CREATED if not self.object else constants.RECORD_UPDATED + ) + return self.success_message + + # Get the object (if exists) based on URL parameter 'pk' + def get_object(self): + pk = self.kwargs.get("pk") + return get_object_or_404(self.model, pk=pk) if pk else None + + # Add page_name and operation to the context + def get_context_data(self, **kwargs): + context = { + "page_name": self.page_name, + "operation": "Add" if not self.object else "Edit", + } + context.update(kwargs) # Include any additional context data passed to the view + return context + + def get(self, request, *args, **kwargs): + self.object = self.get_object() + + # If an object is found, change action to ACTION_UPDATE + if self.object is not None: + self.action = resource_action.ACTION_UPDATE + print("get method of article is called") + form = self.form_class(instance=self.object) + context = self.get_context_data(form=form) + return render(request, self.template_name, context=context) + + def post(self, request, args, *kwargs): + self.object = self.get_object() + + # If an object is found, change action to ACTION_UPDATE + if self.object is not None: + self.action = resource_action.ACTION_UPDATE + print("post method is called") + form = self.form_class(request.POST, request.FILES, instance=self.object) + print("request with files", request.FILES) + if not form.is_valid(): + print(form.errors) + context = self.get_context_data(form=form) + return render(request, self.template_name, context=context) + form.save() + messages.success(self.request, self.get_success_message()) + return redirect(self.success_url) + + +class ReferralTrackingView(LoginRequiredMixin, generic.ListView): + page_name = resource_action.RESOURCE_MANAGE_REFERRALS + resource = resource_action.RESOURCE_MANAGE_REFERRALS + action = resource_action.ACTION_READ + model = ReferralTracking + template_name = "manage_referrals/track_list.html" + context_object_name = "tracks" + + def get_queryset(self): + return super().get_queryset().filter(active=True, deleted=False) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["page_name"] = self.page_name + return context + + +class ReferralTrackingDeleteView(LoginRequiredMixin, generic.View): + page_name = resource_action.RESOURCE_MANAGE_REFERRALS + resource = resource_action.RESOURCE_MANAGE_REFERRALS + action = resource_action.ACTION_DELETE + model = ReferralTracking + success_url = reverse_lazy("manage_referrals:track_list") + success_message = constants.RECORD_DELETED + error_message = constants.RECORD_NOT_FOUND + + def get(self, request, pk): + try: + type_obj = self.model.objects.get(id=pk) + type_obj.deleted = True + type_obj.active = False + type_obj.save() + messages.success(request, self.success_message) + except self.model.DoesNotExist: + messages.success(request, self.error_message) + + return redirect(self.success_url) diff --git a/manage_subscriptions/api/urls.py b/manage_subscriptions/api/urls.py index 280ce51..fb63bae 100644 --- a/manage_subscriptions/api/urls.py +++ b/manage_subscriptions/api/urls.py @@ -8,4 +8,5 @@ from rest_framework_simplejwt.views import ( urlpatterns = [ path('buy-subscription/', views.CreatePrincipalSubscriptionApi.as_view(), name='buy_subscription'), path('test-webhook/', views.StripeWebhookTest.as_view(), name='webhook_test'), + path('check-subscription/', views.LastActiveSubscriptionView.as_view(), name='check_subscription'), ] diff --git a/manage_subscriptions/api/views.py b/manage_subscriptions/api/views.py index 1bab6ba..539aba8 100644 --- a/manage_subscriptions/api/views.py +++ b/manage_subscriptions/api/views.py @@ -10,7 +10,11 @@ import stripe from accounts.models import IAmPrincipal import json from goodtimes import constants, services -from manage_subscriptions.models import Subscription, PrincipalSubscription +from manage_subscriptions.models import ( + Subscription, + PrincipalSubscription, + SubscriptionStatus, +) from goodtimes.utils import ApiResponse from accounts.resource_action import ( PRINCIPAL_TYPE_EVENT_USER, @@ -206,3 +210,32 @@ class StripeWebhookTest(APIView): message="Error processing event", errors=str(e), ) + + +class LastActiveSubscriptionView(APIView): + authentication_classes = [JWTAuthentication] + permission_classes = [IsAuthenticated] + + def get(self, request, *args, **kwargs): + today = timezone.now().date() + try: + last_active_subscription = PrincipalSubscription.objects.filter( + principal=request.user, + cancelled=False, + status=SubscriptionStatus.ACTIVE, + end_date__gte=today, + ).latest("end_date") + + serializer = PrincipalSubscriptionSerializer(last_active_subscription) + return ApiResponse.success( + status=status.HTTP_200_OK, + message=constants.SUCCESS, + data=serializer.data, + ) + + except PrincipalSubscription.DoesNotExist: + return ApiResponse.error( + status=status.HTTP_404_NOT_FOUND, + message="No Active Subscription Found", + errors="No Active Subscription Found", + ) diff --git a/manage_subscriptions/urls.py b/manage_subscriptions/urls.py index 16bf158..f26b649 100644 --- a/manage_subscriptions/urls.py +++ b/manage_subscriptions/urls.py @@ -39,6 +39,7 @@ urlpatterns = [ views.PlanDeleteView.as_view(), name="plan_delete", ), + # Principal Subscription path("principal_subscription/list/", views.PrincipalSubscriptionView.as_view(), name="principal_subscriptions_list"), path( "principal_subscription/add/", diff --git a/manage_wallets/forms.py b/manage_wallets/forms.py new file mode 100644 index 0000000..1971ace --- /dev/null +++ b/manage_wallets/forms.py @@ -0,0 +1,52 @@ +from django import forms +from .models import Wallet, Transaction + + +class WalletForm(forms.ModelForm): + class Meta: + model = Wallet + fields = [ + "principal", + "balance", + "deposit", + "earnings", + "coins", + "withdrawal_balance", + ] + widgets = { + "principal": forms.Select(attrs={"class": "form-control"}), + "balance": forms.NumberInput(attrs={"class": "form-control"}), + "deposit": forms.NumberInput(attrs={"class": "form-control"}), + "earnings": forms.NumberInput(attrs={"class": "form-control"}), + "coins": forms.NumberInput(attrs={"class": "form-control"}), + "withdrawal_balance": forms.NumberInput(attrs={"class": "form-control"}), + } + + +class TransactionForm(forms.ModelForm): + class Meta: + model = Transaction + fields = [ + "principal", + "principal_subscription", + "transaction_type", + "payment_method", + "transaction_status", + "amount", + "comment", + "order_id", + "product_id", + "reference_id", + ] + widgets = { + "principal": forms.Select(attrs={"class": "form-control"}), + "principal_subscription": forms.Select(attrs={"class": "form-control"}), + "transaction_type": forms.Select(attrs={"class": "form-control"}), + "payment_method": forms.Select(attrs={"class": "form-control"}), + "transaction_status": forms.Select(attrs={"class": "form-control"}), + "amount": forms.NumberInput(attrs={"class": "form-control"}), + "comment": forms.TextInput(attrs={"class": "form-control"}), + "order_id": forms.TextInput(attrs={"class": "form-control"}), + "product_id": forms.TextInput(attrs={"class": "form-control"}), + "reference_id": forms.TextInput(attrs={"class": "form-control"}), + } diff --git a/manage_wallets/urls.py b/manage_wallets/urls.py index 6b7780f..699dc92 100644 --- a/manage_wallets/urls.py +++ b/manage_wallets/urls.py @@ -11,6 +11,6 @@ urlpatterns = [ # for manage payment related url - path('peyment/', views.PaymentListView.as_view(), name='payment_list'), + path('payment/', views.PaymentListView.as_view(), name='payment_list'), ] diff --git a/templates/manage_events/event_add.html b/templates/manage_events/event_add.html index 7fbe5ed..2667314 100644 --- a/templates/manage_events/event_add.html +++ b/templates/manage_events/event_add.html @@ -16,7 +16,7 @@
-

Add Event

+

{{operation}} {{page_title}}

--> Referral Rewards + Referral Tracking Good Time Coins
diff --git a/templates/manage_referrals/track_add.html b/templates/manage_referrals/track_add.html new file mode 100644 index 0000000..c25fda4 --- /dev/null +++ b/templates/manage_referrals/track_add.html @@ -0,0 +1,135 @@ +{% extends 'layout/base_template.html' %} +{% load static %} +{% block stylesheet %} + + + {% include "cdn_through_html/filepond_cdn_css.html" %} + {% include "cdn_through_html/quill_cdn_css.html" %} + {% include "cdn_through_html/tagify_cdn_css.html" %} + {{form.media}} + +{% endblock %} + +{% block content %} + +
+
+
+
+

Add Referral Track

+
+
+ +
+
+
+
+
+
+ +
+ {% csrf_token %} + {% include 'includes/dynamic_template_form.html' with form=form %} +
+
+
+ {% comment %}
+ + +
We'll never share your email with anyone else.
+
+
+ +
+
+
+ +
+
    +
    +
    +
    + + +
    + {% endcomment %} +
    + +
    +
    +
    +
    +
    +
    + + +{% endblock content %} + +{% block javascript %} + + + {% include "cdn_through_html/filepond_cdn_js.html" %} + {% include "cdn_through_html/quill_cdn_js.html" %} + {% include "cdn_through_html/tagify_cdn_js.html" %} + + + +{% endblock %} \ No newline at end of file diff --git a/templates/manage_referrals/track_list.html b/templates/manage_referrals/track_list.html new file mode 100644 index 0000000..22f8304 --- /dev/null +++ b/templates/manage_referrals/track_list.html @@ -0,0 +1,140 @@ +{% extends 'layout/base_template.html' %} +{% load static %} +{% block stylesheet %} + +{% include "cdn_through_html/datatable_cdn_css.html" %} + +{% endblock %} + +{% block content %} + +
    +
    +
    +
    +

    Referral Tracking

    +
    + +
    + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + +{% endblock content %} + +{% block javascript %} + +{% include "cdn_through_html/datatable_cdn_js.html" %} + + +{% endblock %} \ No newline at end of file diff --git a/templates/manage_subscriptions/plan_add.html b/templates/manage_subscriptions/plan_add.html index 0ae2cdd..8da8df6 100644 --- a/templates/manage_subscriptions/plan_add.html +++ b/templates/manage_subscriptions/plan_add.html @@ -16,7 +16,7 @@
    -

    Add Plan

    +

    {{operation}} {{page_name}}

    @@ -64,12 +60,16 @@ {{transaction_obj.id}} - {{transaction_obj.principal.phone_no}} - {{transaction_obj.principal_type}} - {{transaction_obj.get_transaction_type_display}} - {{transaction_obj.get_transaction_status_display}} + {{transaction_obj.principal}} + {{transaction_obj.principal_subscription}} + {{transaction_obj.transaction_type}} + {{transaction_obj.payment_method}} + {{transaction_obj.transaction_status}} {{transaction_obj.amount}} {{transaction_obj.comment}} + {{transaction_obj.order_id}} + {{transaction_obj.product_id}} + {{transaction_obj.reference_id}} diff --git a/templates/manage_wallets/wallet_list.html b/templates/manage_wallets/wallet_list.html index 0207470..84351aa 100644 --- a/templates/manage_wallets/wallet_list.html +++ b/templates/manage_wallets/wallet_list.html @@ -15,11 +15,7 @@

    Manage Wallet

    - {% comment %} {% endcomment %} - {% comment %} Add Newsletter {% endcomment %} +
    @@ -39,26 +35,23 @@ style="width: 69.2656px;"> Record Id User + style="width: 44.2344px;">Principal Player Balance + style="width: 79.7969px;">Balance Player Deposit + style="width: 77.3281px;">Deposit Player Bonus + style="width: 143.516px;">Earnings Merchant Balance - Merchant Deposit + style="width: 98.875px;">Coins Merchant Bonus + style="width: 51.625px;">Withdrawal Balance @@ -67,14 +60,12 @@ {{wallet_obj.id}} - {{wallet_obj.principal.phone_no}} - {{wallet_obj.player_balance}} - {{wallet_obj.player_deposit}} - {{wallet_obj.player_bonus}} - {{wallet_obj.merchant_balance}} - {{wallet_obj.merchant_deposit}} - {{wallet_obj.merchant_bonus}} - + {{wallet_obj.principal}} + {{wallet_obj.balance}} + {{wallet_obj.deposit}} + {{wallet_obj.earnings}} + {{wallet_obj.coins}} + {{wallet_obj.withdrawal_balance}} {% endfor %}