diff --git a/goodtimes/services.py b/goodtimes/services.py index f4c824d..767ab50 100644 --- a/goodtimes/services.py +++ b/goodtimes/services.py @@ -207,26 +207,57 @@ class PaymentProcessingService: self.charge_data = webhook_data["data"]["object"] self.customer_id = self._get_customer_id() self.transaction = self._get_transaction_by_id() - self.principal_id = self.transaction.principal.id + self.principal = self.transaction.principal def _get_customer_id(self): # Access the customer ID from the charge object return self.charge_data.get("customer", None) def _get_transaction_by_id(self): - print("self.metadata: ", self.charge_data["metadata"]) + logger.debug("self.metadata: ", self.charge_data["metadata"]) + logger.debug("transaction_id: ", self.charge_data["metadata"]["transaction_id"]) transaction_id = self.charge_data["metadata"]["transaction_id"] - print("_get_transaction_by_id: ", transaction_id) - return Transaction.objects.get(id=int(transaction_id)) + if transaction_id: + try: + logger.debug("_get_transaction_by_id: ", transaction_id) + return Transaction.objects.get(id=int(transaction_id)) + except Transaction.DoesNotExist: + logger.error(f"Transaction ID {transaction_id} not found.") + return None + + def _get_subscription(self): + logger.debug("subscription_id: ", self.charge_data["metadata"]["subscription_id"]) + subscription_id = self.charge_data["metadata"]["subscription_id"] + if subscription_id: + try: + return Subscription.objects.get(id=int(subscription_id)) + except Subscription.DoesNotExist: + logger.error(f"Subscription ID {subscription_id} not found.") + return None def _get_principal_subscription_by_id(self): - # Extract transaction_id from webhook metadata - principal_subscription = self.charge_data["metadata"][ - "principal_subscription_id" - ] - print("principal_subscription: ", principal_subscription) - # Fetch the Transaction instance using the transaction_id - return PrincipalSubscription.objects.get(id=int(principal_subscription)) + order_id = self.charge_data["metadata"]["order_id"] + try: + subscription = self._get_subscription() + + subscription_days = subscription.plan.days + today = timezone.now().date() + last_date = today + timedelta(days=int(subscription_days)) + + principal_subscription = PrincipalSubscription.objects.create( + principal=self.principal, + subscription=subscription, + is_paid=True, + order_id=order_id, + start_date=today, + end_date=last_date, + grace_period_end_date=last_date + timedelta(days=15), + ) + return principal_subscription + except Subscription.DoesNotExist: + logger.error("SOmething Went Wrong inside _get_principal_subscription_by_id().") + + return None def process_event(self): if self.event_type == "checkout.session.completed": @@ -245,7 +276,7 @@ class PaymentProcessingService: referral_record = ReferralRecord.objects.filter( active=True, deleted=False, - referred_principal_id=self.principal_id, + referred_principal_id=self.principal.id, is_completed=True, ).first() @@ -263,9 +294,25 @@ class PaymentProcessingService: .order_by("-end_date") .first() ) - if active_subscription: - self._credit_good_time_coin(referral_record.referrer_principal) + subscription = self._get_principal_subscription_by_id() + if subscription: + # Calculate the reward value + percentage = ( + subscription.referral_percentage * subscription.amount / 100 + ) + + # Create a reward entry + ReferralRecordReward.objects.create( + referral_record=referral_record, + subscription=subscription, + coins=1, # Assuming this is a default or a calculated value + value=percentage, + ) + + self._credit_good_time_coin( + referral_record.referrer_principal, percentage + ) # Here's where you call _update_reward self._update_reward( referral_record=referral_record, @@ -282,16 +329,16 @@ class PaymentProcessingService: has_active_subscription=False, ) - def _credit_good_time_coin(self, referrer_principal): - wallet, created = Wallet.objects.get_or_create(principal=referrer_principal) - wallet.coins += 1 - wallet.save() + def _credit_good_time_coin(self, referrer_principal, percentage): + # wallet, created = Wallet.objects.get_or_create(principal=referrer_principal) + # wallet.coins += 1 + # wallet.save() Transaction.objects.create( principal=referrer_principal, transaction_type=TransactionType.CREDIT, payment_method="", transaction_status=TransactionStatus.SUCCESS, - amount=0, + amount=percentage, coins=1, comment="Referral reward", # Populate other fields as necessary, such as `order_id`, `product_id`, or `reference_id` if applicable diff --git a/manage_referrals/admin.py b/manage_referrals/admin.py index 1c6dea5..d7d6dc5 100644 --- a/manage_referrals/admin.py +++ b/manage_referrals/admin.py @@ -74,8 +74,9 @@ class ReferralRecordRewardAdmin(admin.ModelAdmin): "get_subscription_name", "coins", "value", + "unique_token", ) - search_fields = ("referral_record__id", "subscription__name", "coins") + search_fields = ("referral_record__id", "subscription__title", "coins") list_filter = ("subscription",) raw_id_fields = ("referral_record", "subscription") @@ -85,7 +86,7 @@ class ReferralRecordRewardAdmin(admin.ModelAdmin): get_referral_record_id.short_description = "Referral Record ID" def get_subscription_name(self, obj): - return obj.subscription.name + return obj.subscription.title get_subscription_name.short_description = "Subscription Name" diff --git a/manage_referrals/api/serializers.py b/manage_referrals/api/serializers.py index 3f4cbab..ff4dcf5 100644 --- a/manage_referrals/api/serializers.py +++ b/manage_referrals/api/serializers.py @@ -11,6 +11,7 @@ from accounts.models import ( from manage_referrals.models import ( ReferralCode, ReferralRecord, + ReferralRecordReward, ) from goodtimes import constants, date_utils @@ -37,3 +38,16 @@ class ReferralRecordSerializer(serializers.ModelSerializer): def get_join_at(self, obj): return date_utils.format_date_to_string(obj.created_on) + + +class ReferralRecordRewardSerializer(serializers.ModelSerializer): + class Meta: + model = ReferralRecordReward + fields = [ + "referral_record", + "subscription", + "redeem", + "coins", + "unique_token", + "value", + ] diff --git a/manage_referrals/api/urls.py b/manage_referrals/api/urls.py index 2811950..6d2f4c0 100644 --- a/manage_referrals/api/urls.py +++ b/manage_referrals/api/urls.py @@ -14,4 +14,14 @@ urlpatterns = [ views.ReferralRecordViews.as_view(), name="referral_record", ), + path( + "referral-record-rewards/", + views.RewardListView.as_view(), + name="referral_record_reward", + ), + path( + "redeem-reward//", + views.RedeemRewardView.as_view(), + name="redeem_reward", + ), ] diff --git a/manage_referrals/api/views.py b/manage_referrals/api/views.py index 7d84dba..0a42b81 100644 --- a/manage_referrals/api/views.py +++ b/manage_referrals/api/views.py @@ -1,12 +1,15 @@ from rest_framework import status from rest_framework.views import APIView from django.conf import settings -from manage_referrals.models import ReferralCode, ReferralRecord +from manage_referrals.models import ReferralCode, ReferralRecord, ReferralRecordReward +from django.shortcuts import get_object_or_404 from goodtimes import constants - +from django.db import transaction from goodtimes.utils import ApiResponse +from manage_wallets.models import WithdrawalRequest from .serializers import ( ReferralCodeSerializer, + ReferralRecordRewardSerializer, ReferralRecordSerializer, ) from rest_framework.permissions import IsAuthenticated @@ -49,3 +52,75 @@ class ReferralRecordViews(APIView): "data": {"count": referral_obj.count(), "record": serializer_obj.data}, } return ApiResponse.success(**success_message) + + +class RewardListView(APIView): + authentication_classes = [JWTAuthentication] + permission_classes = [IsAuthenticated] + model = ReferralRecordReward + serializer = ReferralRecordRewardSerializer + + def get(self, request): + # Filter rewards based on specified conditions + rewards_query = ReferralRecordReward.objects.filter( + referral_record__active=True, + referral_record__deleted=False, + redeem=False, + deleted=False, + active=True, + ) + # Serialize the results + rewards_serializer = ReferralRecordRewardSerializer(rewards_query, many=True) + # Get the count of unredeemed rewards + unredeemed_count = rewards_query.count() + success_message = { + "status": status.HTTP_200_OK, + "message": constants.SUCCESS, + "data": { + "reward_count": unredeemed_count, + "rewards": rewards_serializer.data, + }, + } + return ApiResponse.success(**success_message) + + +class RedeemRewardView(APIView): + authentication_classes = [JWTAuthentication] + permission_classes = [IsAuthenticated] + + def patch(self, request, unique_token): + # Retrieve the reward using the unique token + try: + reward = ReferralRecordReward.objects.get( + unique_token=unique_token, sell=False + ) + except ReferralRecordReward.DoesNotExist: + return ApiResponse.error( + status=status.HTTP_404_NOT_FOUND, + message=constants.FAILURE, + errors="No unsold token found.", + ) + + # Check if a withdrawal request already exists for this token + if WithdrawalRequest.objects.filter(token=unique_token).exists(): + return ApiResponse.error( + status=status.HTTP_400_BAD_REQUEST, + message=constants.FAILURE, + errors="A withdrawal request for this token already exists.", + ) + + with transaction.atomic(): + # Create a new withdrawal request + WithdrawalRequest.objects.create( + principal=request.user, token=unique_token, coins=1, amount=reward.value + ) + + # Update reward to mark it as sold + reward.sell = True + reward.save() + + return ApiResponse.success( + status=status.HTTP_200_OK, + message=constants.SUCCESS, + data="Token sold successfully.", + ) diff --git a/manage_referrals/forms.py b/manage_referrals/forms.py index 8427dbf..0cfef95 100644 --- a/manage_referrals/forms.py +++ b/manage_referrals/forms.py @@ -34,7 +34,7 @@ class ReferralRecordForm(forms.ModelForm): class ReferralRecordRewardForm(forms.ModelForm): class Meta: model = ReferralRecordReward - fields = ["referral_record", "subscription", "coins", "value"] + fields = ["referral_record", "subscription", "coins", "value", "sell"] class GoodTimeCoinsForm(forms.ModelForm): diff --git a/manage_referrals/migrations/0009_referralrecordreward_redeem.py b/manage_referrals/migrations/0009_referralrecordreward_redeem.py new file mode 100644 index 0000000..25cd7aa --- /dev/null +++ b/manage_referrals/migrations/0009_referralrecordreward_redeem.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-04-22 14:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("manage_referrals", "0008_referralrecordreward_unique_token"), + ] + + operations = [ + migrations.AddField( + model_name="referralrecordreward", + name="redeem", + field=models.BooleanField(default=False), + ), + ] diff --git a/manage_referrals/migrations/0010_alter_referralrecordreward_unique_token.py b/manage_referrals/migrations/0010_alter_referralrecordreward_unique_token.py new file mode 100644 index 0000000..f3f4e18 --- /dev/null +++ b/manage_referrals/migrations/0010_alter_referralrecordreward_unique_token.py @@ -0,0 +1,19 @@ +# Generated by Django 5.0.2 on 2024-04-23 07:50 + +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("manage_referrals", "0009_referralrecordreward_redeem"), + ] + + operations = [ + migrations.AlterField( + model_name="referralrecordreward", + name="unique_token", + field=models.UUIDField(default=uuid.uuid4, editable=False), + ), + ] diff --git a/manage_referrals/migrations/0011_rename_redeem_referralrecordreward_sell_and_more.py b/manage_referrals/migrations/0011_rename_redeem_referralrecordreward_sell_and_more.py new file mode 100644 index 0000000..f2e42bb --- /dev/null +++ b/manage_referrals/migrations/0011_rename_redeem_referralrecordreward_sell_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 5.0.2 on 2024-04-23 14:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("manage_referrals", "0010_alter_referralrecordreward_unique_token"), + ] + + operations = [ + migrations.RenameField( + model_name="referralrecordreward", + old_name="redeem", + new_name="sell", + ), + migrations.AddField( + model_name="referralrecordreward", + name="withdraw", + field=models.BooleanField(default=False), + ), + ] diff --git a/manage_referrals/models.py b/manage_referrals/models.py index 6be8fc0..3a47060 100644 --- a/manage_referrals/models.py +++ b/manage_referrals/models.py @@ -114,7 +114,7 @@ class ReferralRecord(BaseModel): db_table = "referral_record" def __str__(self): - return f"Referral ID: {self.id}, Referrar name: {self.referrer_principal.first_name}, Type: {self.principal_type.name}" + return f"Reward ID: {self.id}, Referrar: {self.referrer_principal.first_name}, Referred to: {self.referred_principal.first_name}" @classmethod def filter_invite_records(cls, referrer_principal): @@ -136,9 +136,11 @@ class ReferralRecordReward(BaseModel): subscription = models.ForeignKey( Subscription, on_delete=models.CASCADE, related_name="subscription_reward" ) + sell = models.BooleanField(default=False) + withdraw = models.BooleanField(default=False) coins = models.PositiveBigIntegerField(default=1) unique_token = models.UUIDField( - default=uuid.uuid4, editable=False, null=True, blank=True + default=uuid.uuid4, editable=False ) value = models.DecimalField(max_digits=14, decimal_places=2) diff --git a/manage_referrals/views.py b/manage_referrals/views.py index 3ac267e..10d9174 100644 --- a/manage_referrals/views.py +++ b/manage_referrals/views.py @@ -181,7 +181,7 @@ class ReferralRecordRewardCreateOrUpdateView(LoginRequiredMixin, generic.View): context.update(kwargs) # Include any additional context data passed to the view return context - def get(self, request, args, *kwargs): + def get(self, request, *args, **kwargs): self.object = self.get_object() # If an object is found, change action to ACTION_UPDATE @@ -192,7 +192,7 @@ class ReferralRecordRewardCreateOrUpdateView(LoginRequiredMixin, generic.View): context = self.get_context_data(form=form) return render(request, self.template_name, context=context) - def post(self, request, args, *kwargs): + def post(self, request, *args, **kwargs): self.object = self.get_object() # If an object is found, change action to ACTION_UPDATE diff --git a/manage_subscriptions/forms.py b/manage_subscriptions/forms.py index 8baf8ef..3579fc4 100644 --- a/manage_subscriptions/forms.py +++ b/manage_subscriptions/forms.py @@ -27,6 +27,7 @@ class SubscriptionForm(forms.ModelForm): # "long_description", # "image", "principal_types", + "referral_percentage", ] # Include all fields you want from the model diff --git a/manage_subscriptions/migrations/0006_subscription_referral_percentage.py b/manage_subscriptions/migrations/0006_subscription_referral_percentage.py new file mode 100644 index 0000000..86c5670 --- /dev/null +++ b/manage_subscriptions/migrations/0006_subscription_referral_percentage.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-04-22 13:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("manage_subscriptions", "0005_webhookevent_event_type"), + ] + + operations = [ + migrations.AddField( + model_name="subscription", + name="referral_percentage", + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=5), + ), + ] diff --git a/manage_subscriptions/migrations/0007_alter_subscription_referral_percentage.py b/manage_subscriptions/migrations/0007_alter_subscription_referral_percentage.py new file mode 100644 index 0000000..92fd275 --- /dev/null +++ b/manage_subscriptions/migrations/0007_alter_subscription_referral_percentage.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-04-22 13:13 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("manage_subscriptions", "0006_subscription_referral_percentage"), + ] + + operations = [ + migrations.AlterField( + model_name="subscription", + name="referral_percentage", + field=models.DecimalField(decimal_places=2, max_digits=5), + ), + ] diff --git a/manage_subscriptions/models.py b/manage_subscriptions/models.py index 71bf9ed..ae99b31 100644 --- a/manage_subscriptions/models.py +++ b/manage_subscriptions/models.py @@ -29,6 +29,7 @@ class Subscription(BaseModel): principal_types = models.ManyToManyField( IAmPrincipalType, related_name="principal_type_subscriptions", blank=True ) + referral_percentage = models.DecimalField(max_digits=5, decimal_places=2) class Meta: db_table = "subscription" diff --git a/manage_subscriptions/views.py b/manage_subscriptions/views.py index 87a1816..dbbdcec 100644 --- a/manage_subscriptions/views.py +++ b/manage_subscriptions/views.py @@ -437,20 +437,20 @@ def create_checkout_session(request): comment="Principal Subscription Initiated", ) - subscription_days = subscription.plan.days - today = timezone.now().date() - last_date = today + timedelta(days=int(subscription_days)) + # subscription_days = subscription.plan.days + # today = timezone.now().date() + # last_date = today + timedelta(days=int(subscription_days)) # To Avoid Duplicacy of Principal Subscription - principal_subscription = PrincipalSubscription.objects.create( - principal=request.user, - subscription=subscription, - is_paid=False, - order_id=order_id, - start_date=today, - end_date=last_date, - grace_period_end_date=last_date + timedelta(days=15), - ) + # principal_subscription = PrincipalSubscription.objects.create( + # principal=request.user, + # subscription=subscription, + # is_paid=False, + # order_id=order_id, + # start_date=today, + # end_date=last_date, + # grace_period_end_date=last_date + timedelta(days=15), + # ) try: # customer = stripe.Customer.create( @@ -491,9 +491,9 @@ def create_checkout_session(request): metadata={ "principal": str(request.user.id), "order_id": str(order_id), - # "subscription_id": str(subscription.id), + "subscription_id": str(subscription.id), "transaction_id": str(transaction.id), - "principal_subscription_id": str(principal_subscription.id), + # "principal_subscription_id": str(principal_subscription.id), }, ) return JsonResponse({"sessionId": checkout_session["id"]}) diff --git a/manage_wallets/admin.py b/manage_wallets/admin.py index 9a3aa56..4ac8c78 100644 --- a/manage_wallets/admin.py +++ b/manage_wallets/admin.py @@ -71,3 +71,48 @@ class StripeConnectAccountAdmin(admin.ModelAdmin): "details_submitted", ) search_fields = ("principal__name", "stripe_connect_id") + + +class WithdrawalRequestAdmin(admin.ModelAdmin): + list_display = ("id", "principal", "coins", "token", "amount", "status", "ref_id") + list_filter = ("status", "principal") + search_fields = ( + "ref_id", + "token", + "principal__user__username", + ) # Adjust if 'principal' has a different relation to User + readonly_fields = ("ref_image", "reply", "notes") + + fieldsets = ( + (None, {"fields": ("principal", "coins", "token", "amount", "status")}), + ( + "Reference Information", + { + "fields": ("ref_image", "ref_id", "notes", "reply"), + "classes": ("collapse",), + }, + ), + ) + + # def has_delete_permission(self, request, obj=None): + # # Disable delete if the status is not 'denied' or 'dispute' + # if obj is not None: + # if obj.status in ["denied", "dispute"]: + # return True + # return False + # return True + + def has_add_permission(self, request, obj=None): + # You can also control if adding is allowed + return True + + def has_change_permission(self, request, obj=None): + # Control the ability to change existing records + if obj is not None: + if obj.status in ["submitted", "review"]: + return True + return False + return True + + +admin.site.register(models.WithdrawalRequest, WithdrawalRequestAdmin) diff --git a/manage_wallets/api/urls.py b/manage_wallets/api/urls.py index 4420ef1..f5bed9a 100644 --- a/manage_wallets/api/urls.py +++ b/manage_wallets/api/urls.py @@ -33,4 +33,10 @@ urlpatterns = [ views.WithdrawalRequestView.as_view(), name="withdrawal-request-view", ), + # checking bank accounts + path( + "check-bank-accounts/", + views.PrincipalBankAccountCheck.as_view(), + name="check_bank_accounts", + ), ] diff --git a/manage_wallets/api/views.py b/manage_wallets/api/views.py index 496c8e4..85deee9 100644 --- a/manage_wallets/api/views.py +++ b/manage_wallets/api/views.py @@ -419,3 +419,26 @@ class WithdrawalRequestView(APIView): status=status.HTTP_200_OK, message=constants.SUCCESS, ) + + +class PrincipalBankAccountCheck(APIView): + authentication_classes = [JWTAuthentication] + permission_classes = [IsAuthenticated] + + def get(self, request): + queryset = models.PrincipalBankAccount.objects.filter(principal=request.user) + if queryset: + response = { + "status": status.HTTP_200_OK, + "message": constants.SUCCESS, + "data": True, + } + + return ApiResponse.success(**response) + false_response = { + "status": status.HTTP_200_OK, + "message": constants.SUCCESS, + "data": False, + } + + return ApiResponse.success(**false_response) diff --git a/manage_wallets/migrations/0010_withdrawalrequest_token.py b/manage_wallets/migrations/0010_withdrawalrequest_token.py new file mode 100644 index 0000000..77afcba --- /dev/null +++ b/manage_wallets/migrations/0010_withdrawalrequest_token.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-04-23 11:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("manage_wallets", "0009_rename_coin_transaction_coins_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="withdrawalrequest", + name="token", + field=models.CharField(blank=True, max_length=255, null=True), + ), + ] diff --git a/manage_wallets/models.py b/manage_wallets/models.py index c027cf8..6a91d1f 100644 --- a/manage_wallets/models.py +++ b/manage_wallets/models.py @@ -3,6 +3,7 @@ from django.db import models from accounts.models import BaseModel, IAmPrincipal, IAmPrincipalType from django.db.models.signals import post_save from django.dispatch import receiver +from manage_referrals.models import ReferralRecordReward from manage_subscriptions.models import PrincipalSubscription # Create your models here. @@ -135,6 +136,7 @@ class WithdrawalRequest(BaseModel): IAmPrincipal, on_delete=models.CASCADE, related_name="withdrawal_requests" ) coins = models.IntegerField(default=0) + token = models.CharField(max_length=255, blank=True, null=True) amount = models.DecimalField(max_digits=14, decimal_places=2, default=0.00) ref_image = models.ImageField(upload_to="withdrawal", null=True, blank=True) ref_id = models.CharField(unique=True, max_length=255, null=True, blank=True) diff --git a/manage_wallets/views.py b/manage_wallets/views.py index 2f7a48e..4d77168 100644 --- a/manage_wallets/views.py +++ b/manage_wallets/views.py @@ -78,10 +78,20 @@ class StatusUpdateView(LoginRequiredMixin, generic.View): id = request.POST.get("id") message = request.POST.get("message") status = request.POST.get("status") + print("Request.POST: ", request.POST) if id or message: try: instance = self.model.objects.get(id=id) + print("status: ", status) + if status == "transferred": + print("Hello") + bank_account = PrincipalBankAccount.objects.filter(principal=instance.principal) + print("bank_account: ", bank_account) + for account in bank_account: + account.delete() + print("Deleted all accounts") + instance.reply = message instance.status = status instance.save() diff --git a/templates/manage_referrals/code_list.html b/templates/manage_referrals/code_list.html index 1574337..134d116 100644 --- a/templates/manage_referrals/code_list.html +++ b/templates/manage_referrals/code_list.html @@ -21,10 +21,10 @@ Back --> - Coins + Tracking Referral Record - + Rewards diff --git a/templates/manage_referrals/reward_add.html b/templates/manage_referrals/reward_add.html index 7fbe5ed..f802fb2 100644 --- a/templates/manage_referrals/reward_add.html +++ b/templates/manage_referrals/reward_add.html @@ -16,7 +16,7 @@
-

Add Event

+

Rewards