diff --git a/accounts/context_processors.py b/accounts/context_processors.py index 755ece6..fe6810d 100644 --- a/accounts/context_processors.py +++ b/accounts/context_processors.py @@ -34,6 +34,8 @@ def compute_resource_action_constants(): 'RESOURCE_MANAGE_PAYMENT': resource_action.RESOURCE_MANAGE_PAYMENT, 'RESOURCE_MANAGE_EVENTS': resource_action.RESOURCE_MANAGE_EVENTS, 'RESOURCE_MANAGE_VENUES': resource_action.RESOURCE_MANAGE_VENUES, + 'RESOURCE_MANAGE_WITHDRAWALS': resource_action.RESOURCE_MANAGE_WITHDRAWALS, + 'RESOURCE_MANAGE_BANK_ACCOUNTS': resource_action.RESOURCE_MANAGE_BANK_ACCOUNTS, 'RESOURCE_MANAGE_CONTACT_US': resource_action.RESOURCE_MANAGE_CONTACT_US, 'RESOURCE_MANAGE_CMS': resource_action.RESOURCE_MANAGE_CMS, 'RESOURCE_MANAGE_REPORTS': resource_action.RESOURCE_MANAGE_REPORTS, diff --git a/accounts/fixture_script.py b/accounts/fixture_script.py index e5f99eb..663ee5b 100644 --- a/accounts/fixture_script.py +++ b/accounts/fixture_script.py @@ -26,6 +26,8 @@ from accounts.resource_action import ( RESOURCE_PRINCIPAL_SUBSCRIPTIONS, RESOURCE_MANAGE_REFERRALS, RESOURCE_MANAGE_FEEDBACK, + RESOURCE_MANAGE_WITHDRAWALS, + RESOURCE_MANAGE_BANK_ACCOUNTS ) # this variable store the data of model principaltype, action, resource fixture_data = [ @@ -308,4 +310,28 @@ fixture_data = [ "action": [1, 2, 3, 4], }, }, + { + "model": "accounts.iamappresource", + "pk": 16, + "fields": { + "name": RESOURCE_MANAGE_WITHDRAWALS, + "label": RESOURCE_MANAGE_WITHDRAWALS, + "slug": RESOURCE_MANAGE_WITHDRAWALS, + "created_on": "2023-09-28T16:17:42.815", + "modified_on": "2023-09-28T16:17:42.815", + "action": [1, 2, 3, 4], + }, + }, + { + "model": "accounts.iamappresource", + "pk": 17, + "fields": { + "name": RESOURCE_MANAGE_BANK_ACCOUNTS, + "label": RESOURCE_MANAGE_BANK_ACCOUNTS, + "slug": RESOURCE_MANAGE_BANK_ACCOUNTS, + "created_on": "2023-09-28T16:17:42.815", + "modified_on": "2023-09-28T16:17:42.815", + "action": [1, 2, 3, 4], + }, + }, ] diff --git a/accounts/fixtures/resource_action_fixture.json b/accounts/fixtures/resource_action_fixture.json index b414de9..df2e808 100644 --- a/accounts/fixtures/resource_action_fixture.json +++ b/accounts/fixtures/resource_action_fixture.json @@ -340,9 +340,43 @@ "model": "accounts.iamappresource", "pk": 15, "fields": { - "name": "principal_subscription", - "label": "principal_subscription", - "slug": "principal_subscription", + "name": "principal_subscriptions", + "label": "principal_subscriptions", + "slug": "principal_subscriptions", + "created_on": "2023-09-28T16:17:42.815", + "modified_on": "2023-09-28T16:17:42.815", + "action": [ + 1, + 2, + 3, + 4 + ] + } + }, + { + "model": "accounts.iamappresource", + "pk": 16, + "fields": { + "name": "manage_withdrawals", + "label": "manage_withdrawals", + "slug": "manage_withdrawals", + "created_on": "2023-09-28T16:17:42.815", + "modified_on": "2023-09-28T16:17:42.815", + "action": [ + 1, + 2, + 3, + 4 + ] + } + }, + { + "model": "accounts.iamappresource", + "pk": 17, + "fields": { + "name": "manage_bank_accounts", + "label": "manage_bank_accounts", + "slug": "manage_bank_accounts", "created_on": "2023-09-28T16:17:42.815", "modified_on": "2023-09-28T16:17:42.815", "action": [ diff --git a/accounts/resource_action.py b/accounts/resource_action.py index 9ac0e88..3e10751 100644 --- a/accounts/resource_action.py +++ b/accounts/resource_action.py @@ -26,6 +26,8 @@ RESOURCE_PRINCIPAL_SUBSCRIPTIONS = "principal_subscriptions" RESOURCE_MANAGE_FEEDBACK = "manage_feedback" RESOURCE_MANAGE_REFERRALS = "manage_referrals" RESOURCE_MANAGE_NOTIFICATIONS = "manage_notifications" +RESOURCE_MANAGE_WITHDRAWALS = "manage_withdrawals" +RESOURCE_MANAGE_BANK_ACCOUNTS = "manage_bank_accounts" # These constants are used solely for managing the active and inactive state of pages diff --git a/manage_referrals/migrations/0008_referralrecordreward_unique_token.py b/manage_referrals/migrations/0008_referralrecordreward_unique_token.py new file mode 100644 index 0000000..ff30111 --- /dev/null +++ b/manage_referrals/migrations/0008_referralrecordreward_unique_token.py @@ -0,0 +1,21 @@ +# Generated by Django 5.0.2 on 2024-04-04 07:14 + +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("manage_referrals", "0007_alter_referraltracking_ip_address_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="referralrecordreward", + name="unique_token", + field=models.UUIDField( + blank=True, default=uuid.uuid4, editable=False, null=True + ), + ), + ] diff --git a/manage_referrals/models.py b/manage_referrals/models.py index a844c87..6be8fc0 100644 --- a/manage_referrals/models.py +++ b/manage_referrals/models.py @@ -1,3 +1,4 @@ +import uuid from django.db import models import random import string @@ -136,6 +137,9 @@ class ReferralRecordReward(BaseModel): Subscription, on_delete=models.CASCADE, related_name="subscription_reward" ) coins = models.PositiveBigIntegerField(default=1) + unique_token = models.UUIDField( + default=uuid.uuid4, editable=False, null=True, blank=True + ) value = models.DecimalField(max_digits=14, decimal_places=2) class Meta: diff --git a/manage_subscriptions/models.py b/manage_subscriptions/models.py index 7e03594..cce114a 100644 --- a/manage_subscriptions/models.py +++ b/manage_subscriptions/models.py @@ -1,6 +1,7 @@ from django.db import models from accounts.models import BaseModel, IAmPrincipal, IAmPrincipalType from django.utils.translation import gettext_lazy as _ + # Create your models here. @@ -30,9 +31,9 @@ class Subscription(BaseModel): class SubscriptionStatus(models.TextChoices): - ACTIVE = 'active', _('Active') - EXPIRED = 'expired', _('Expired') - INACTIVE = 'inactive', _('Inactive') + ACTIVE = "active", _("Active") + EXPIRED = "expired", _("Expired") + INACTIVE = "inactive", _("Inactive") class PrincipalSubscription(BaseModel): @@ -57,10 +58,14 @@ class PrincipalSubscription(BaseModel): grace_period_end_date = models.DateField(null=True, blank=True) stripe_customer_id = models.CharField(max_length=255, null=True, blank=True) payment_intent_id = models.CharField(max_length=255, null=True, blank=True) - payment_intent_client_secret = models.CharField(max_length=255, null=True, blank=True) + payment_intent_client_secret = models.CharField( + max_length=255, null=True, blank=True + ) class Meta: db_table = "principal_subscription" def __str__(self): return f"{self.subscription} - {self.principal.first_name}" + + diff --git a/manage_wallets/api/serializers.py b/manage_wallets/api/serializers.py index 6daf261..d8b9739 100644 --- a/manage_wallets/api/serializers.py +++ b/manage_wallets/api/serializers.py @@ -20,33 +20,37 @@ class TransactionSerializer(serializers.ModelSerializer): class Meta: model = models.Transaction fields = [ - 'id', - 'principal', - 'principal_subscription', - 'transaction_type', - 'payment_method', - 'transaction_status', - 'amount', - 'coin', - 'comment', - 'order_id', - 'product_id', - 'reference_id', + "id", + "principal", + "principal_subscription", + "transaction_type", + "payment_method", + "transaction_status", + "amount", + "coin", + "comment", + "order_id", + "product_id", + "reference_id", ] extra_kwargs = { - 'principal': {'read_only': True}, - 'order_id': {'required': False}, - 'product_id': {'required': False}, - 'reference_id': {'required': False}, + "principal": {"read_only": True}, + "order_id": {"required": False}, + "product_id": {"required": False}, + "reference_id": {"required": False}, } def to_representation(self, instance): # Customize the representation of the serializer, if necessary representation = super().to_representation(instance) # For example, to display a human-readable version of the transaction type: - representation['transaction_type_display'] = instance.get_transaction_type_display() - representation['payment_method_display'] = instance.get_payment_method_display() - representation['transaction_status_display'] = instance.get_transaction_status_display() + representation["transaction_type_display"] = ( + instance.get_transaction_type_display() + ) + representation["payment_method_display"] = instance.get_payment_method_display() + representation["transaction_status_display"] = ( + instance.get_transaction_status_display() + ) return representation @@ -65,3 +69,28 @@ class MerchantDepositSerializer(serializers.Serializer): ] ) amount = serializers.DecimalField(max_digits=14, decimal_places=2) + + +class PrincipalBankAccountSerializer(serializers.ModelSerializer): + class Meta: + model = models.PrincipalBankAccount + fields = ["first_name", "last_name", "account_no", "sort_code"] + + def create(self, validated_data): + # Set 'principal', 'created_by' from 'request.user'. + user = self.context["request"].user + validated_data["principal"] = user + validated_data["created_by"] = user + return super().create(validated_data) + + def update(self, instance, validated_data): + # Set 'modified_by' from 'request.user' during update. + user = self.context["request"].user + instance.modified_by = user + return super().update(instance, validated_data) + + +class WithdrawalRequestSerializer(serializers.ModelSerializer): + class Meta: + model = models.WithdrawalRequest + fields = ["notes"] diff --git a/manage_wallets/api/urls.py b/manage_wallets/api/urls.py index 140d120..491cbec 100644 --- a/manage_wallets/api/urls.py +++ b/manage_wallets/api/urls.py @@ -15,4 +15,6 @@ urlpatterns = [ path("get-transaction/", views.TransactionView.as_view(), name="transactions"), path("create-stripe-connect/", views.CreateStripeConnectAccount.as_view(), name="stripe_connect"), + path("add-bank-details/", views.PrincipalBankAccountCreateAPIView.as_view(), name="add_bank_account"), + path('withdrawal-request/', views.WithdrawalRequestCreateAPI.as_view(), name='withdrawal-request-create'), ] diff --git a/manage_wallets/api/views.py b/manage_wallets/api/views.py index 79b96e5..01eeaa2 100644 --- a/manage_wallets/api/views.py +++ b/manage_wallets/api/views.py @@ -11,6 +11,7 @@ from goodtimes import services, constants from decimal import Decimal from manage_wallets import models from django.conf import settings +from django.utils import timezone from . import serializers from goodtimes.utils import ApiResponse @@ -322,3 +323,70 @@ class CreateStripeConnectAccount(APIView): "message": "Link Sent", } return ApiResponse.success(**intent_response) + + +class PrincipalBankAccountCreateAPIView(APIView): + authentication_classes = [JWTAuthentication] + permission_classes = [IsAuthenticated] + + def post(self, request, *args, **kwargs): + serializer = serializers.PrincipalBankAccountSerializer( + data=request.data, context={"request": request} + ) + if serializer.is_valid(): + serializer.save() + intent_response = { + "data": serializer.data, + "status": status.HTTP_200_OK, + "message": constants.SUCCESS, + } + return ApiResponse.success(**intent_response) + exception_response = { + "status": status.HTTP_400_BAD_REQUEST, + "message": constants.FAILURE, + "errors": serializer.errors, + } + return ApiResponse.error(**exception_response) + + +class WithdrawalRequestCreateAPI(APIView): + authentication_classes = [JWTAuthentication] + permission_classes = [IsAuthenticated] + + def post(self, request, *args, **kwargs): + user = request.user + wallet = models.Wallet.objects.filter(principal=user).first() + + # Check if the user has a wallet and sufficient coins + if wallet is None or wallet.coins <= 0: + return ApiResponse.error( + errors="Insufficient balance or no wallet found.", + status=status.HTTP_400_BAD_REQUEST, + message=constants.FAILURE, + ) + print("Request.DATA: ", request.data) + serializer = serializers.WithdrawalRequestSerializer(data=request.data) + + if serializer.is_valid(): + notes = serializer.validated_data.get("notes") or "No Notes by User" + # Save the withdrawal request with notes (either provided or default) + serializer.save( + principal=user, + created_on=timezone.now(), + coins=wallet.coins, + notes=notes, + ) + # Reset the wallet's coins to 0 + wallet.coins = 0 + wallet.save() + return ApiResponse.success( + data=serializer.data, + status=status.HTTP_201_CREATED, + message=constants.SUCCESS, + ) + else: + return ApiResponse.error( + errors=serializer.errors, + status=status.HTTP_400_BAD_REQUEST, + message=constants.FAILURE, + ) diff --git a/manage_wallets/migrations/0007_principalbankaccount_withdrawalrequest.py b/manage_wallets/migrations/0007_principalbankaccount_withdrawalrequest.py new file mode 100644 index 0000000..cfb90b9 --- /dev/null +++ b/manage_wallets/migrations/0007_principalbankaccount_withdrawalrequest.py @@ -0,0 +1,150 @@ +# Generated by Django 5.0.2 on 2024-04-04 07:14 + +import django.db.models.deletion +import uuid +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("manage_wallets", "0006_stripeconnectaccount"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="PrincipalBankAccount", + fields=[ + ("active", models.BooleanField(default=True)), + ("deleted", models.BooleanField(default=False)), + ("created_on", models.DateTimeField(auto_now_add=True)), + ("modified_on", models.DateTimeField(auto_now=True)), + ( + "id", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + ), + ), + ("first_name", models.CharField(max_length=255)), + ("last_name", models.CharField(max_length=255)), + ("account_no", models.CharField(max_length=255)), + ("sort_code", models.CharField(max_length=255)), + ("is_verified", models.BooleanField(default=False)), + ( + "created_by", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="%(class)s_created", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "modified_by", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="%(class)s_modified", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "principal", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "db_table": "bank_account", + }, + ), + migrations.CreateModel( + name="WithdrawalRequest", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("active", models.BooleanField(default=True)), + ("deleted", models.BooleanField(default=False)), + ("created_on", models.DateTimeField(auto_now_add=True)), + ("modified_on", models.DateTimeField(auto_now=True)), + ( + "amount", + models.DecimalField(decimal_places=2, default=0.0, max_digits=14), + ), + ( + "ref_image", + models.ImageField(blank=True, null=True, upload_to="withdrawal"), + ), + ( + "ref_id", + models.CharField( + blank=True, max_length=255, null=True, unique=True + ), + ), + ("notes", models.TextField(blank=True, null=True)), + ("reply", models.TextField(blank=True, null=True)), + ( + "status", + models.CharField( + choices=[ + ("submitted", "Submitted"), + ("review", "Review"), + ("processing", "Under Process"), + ("transferred", "Transferred"), + ("dispute", "Dispute"), + ("denied", "Denied"), + ], + default="submitted", + max_length=20, + ), + ), + ( + "created_by", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="%(class)s_created", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "modified_by", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="%(class)s_modified", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "principal", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="withdrawal_requests", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "abstract": False, + }, + ), + ] diff --git a/manage_wallets/migrations/0008_withdrawalrequest_coin_alter_withdrawalrequest_table.py b/manage_wallets/migrations/0008_withdrawalrequest_coin_alter_withdrawalrequest_table.py new file mode 100644 index 0000000..555bb06 --- /dev/null +++ b/manage_wallets/migrations/0008_withdrawalrequest_coin_alter_withdrawalrequest_table.py @@ -0,0 +1,22 @@ +# Generated by Django 5.0.2 on 2024-04-04 09:43 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("manage_wallets", "0007_principalbankaccount_withdrawalrequest"), + ] + + operations = [ + migrations.AddField( + model_name="withdrawalrequest", + name="coin", + field=models.IntegerField(default=0), + ), + migrations.AlterModelTable( + name="withdrawalrequest", + table="withdraw_request", + ), + ] diff --git a/manage_wallets/migrations/0009_rename_coin_transaction_coins_and_more.py b/manage_wallets/migrations/0009_rename_coin_transaction_coins_and_more.py new file mode 100644 index 0000000..b4a44ca --- /dev/null +++ b/manage_wallets/migrations/0009_rename_coin_transaction_coins_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 5.0.2 on 2024-04-04 11:07 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("manage_wallets", "0008_withdrawalrequest_coin_alter_withdrawalrequest_table"), + ] + + operations = [ + migrations.RenameField( + model_name="transaction", + old_name="coin", + new_name="coins", + ), + migrations.RenameField( + model_name="withdrawalrequest", + old_name="coin", + new_name="coins", + ), + ] diff --git a/manage_wallets/models.py b/manage_wallets/models.py index f7764b2..c027cf8 100644 --- a/manage_wallets/models.py +++ b/manage_wallets/models.py @@ -1,3 +1,4 @@ +import uuid from django.db import models from accounts.models import BaseModel, IAmPrincipal, IAmPrincipalType from django.db.models.signals import post_save @@ -72,7 +73,7 @@ class Transaction(BaseModel): default=TransactionStatus.INITIATE, ) amount = models.DecimalField(max_digits=14, decimal_places=2, default=0.00) - coin = models.IntegerField(default=0) + coins = models.IntegerField(default=0) comment = models.CharField(max_length=200, null=True, blank=True) order_id = models.CharField(unique=True, max_length=255, null=True, blank=True) product_id = models.CharField(unique=True, max_length=255, null=True, blank=True) @@ -102,3 +103,49 @@ class StripeConnectAccount(BaseModel): def __str__(self): return f"principal: {self.principal}, type: {self.stripe_connect_id}" + + +class PrincipalBankAccount(BaseModel): + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + principal = models.ForeignKey(IAmPrincipal, on_delete=models.CASCADE) + first_name = models.CharField(max_length=255) + last_name = models.CharField(max_length=255) + account_no = models.CharField(max_length=255) + sort_code = models.CharField(max_length=255) + is_verified = models.BooleanField(default=False) + + class Meta: + db_table = "bank_account" + + def __str__(self): + return f"principal: {self.principal}, account_no: {self.account_no}" + + +class WithdrawalRequest(BaseModel): + STATUS_CHOICES = [ + ("submitted", "Submitted"), + ("review", "Review"), + ("processing", "Under Process"), + ("transferred", "Transferred"), + ("dispute", "Dispute"), + ("denied", "Denied"), + ] + # Update this line to reference IAmPrincipal + principal = models.ForeignKey( + IAmPrincipal, on_delete=models.CASCADE, related_name="withdrawal_requests" + ) + coins = models.IntegerField(default=0) + 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) + notes = models.TextField(null=True, blank=True) + reply = models.TextField(null=True, blank=True) + status = models.CharField( + max_length=20, choices=STATUS_CHOICES, default="submitted" + ) + + class Meta: + db_table = "withdraw_request" + + def __str__(self): + return f"Withdrawal Request by {self.principal.get_full_name()} on {self.created_on.strftime('%Y-%m-%d')}" diff --git a/manage_wallets/urls.py b/manage_wallets/urls.py index 699dc92..465a0d5 100644 --- a/manage_wallets/urls.py +++ b/manage_wallets/urls.py @@ -4,13 +4,13 @@ from . import views app_name = "manage_wallets" urlpatterns = [ - # for manage wallet related url - path('wallet/', views.WalletListView.as_view(), name='wallet_list'), - - - + path("wallet/", views.WalletListView.as_view(), name="wallet_list"), # for manage payment related url - path('payment/', views.PaymentListView.as_view(), name='payment_list'), - + path("payment/", views.PaymentListView.as_view(), name="payment_list"), + # for manage account related url + path("bank-accounts/", views.BankAccountListView.as_view(), name="account_list"), + # for manage withdraw related url + path("withdrawals/", views.WithdrawalListView.as_view(), name="withdrawal_list"), + path("update-status/", views.StatusUpdateView.as_view(), name="update_status"), ] diff --git a/manage_wallets/views.py b/manage_wallets/views.py index 9f42dda..f62211a 100644 --- a/manage_wallets/views.py +++ b/manage_wallets/views.py @@ -1,8 +1,11 @@ -from django.shortcuts import render +from django.shortcuts import render, redirect from django.contrib.auth.mixins import LoginRequiredMixin +from django.urls import reverse_lazy from accounts import resource_action from django.views import generic -from .models import Wallet, Transaction +from django.contrib import messages +from goodtimes import constants +from .models import PrincipalBankAccount, Wallet, Transaction, WithdrawalRequest """Wallet Related VIew""" @@ -32,3 +35,62 @@ class PaymentListView(LoginRequiredMixin, generic.TemplateView): context["page_name"] = self.page_name context["transaction_objs"] = Transaction.objects.filter(deleted=False) return context + + +"""Withdrawals Related View""" + + +class WithdrawalListView(LoginRequiredMixin, generic.TemplateView): + page_name = resource_action.RESOURCE_MANAGE_WITHDRAWALS + template_name = "manage_wallets/withdrawal_list.html" + model = WithdrawalRequest + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["page_name"] = self.page_name + context["withdrawal_objs"] = WithdrawalRequest.objects.filter(deleted=False) + return context + + +"""Bank Accounts Related View""" + + +class BankAccountListView(LoginRequiredMixin, generic.TemplateView): + page_name = resource_action.RESOURCE_MANAGE_WITHDRAWALS + template_name = "manage_wallets/account_list.html" + model = PrincipalBankAccount + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["page_name"] = self.page_name + context["account_objs"] = PrincipalBankAccount.objects.filter(deleted=False) + return context + + +class StatusUpdateView(LoginRequiredMixin, generic.View): + page_name = resource_action.RESOURCE_MANAGE_WITHDRAWALS + model = WithdrawalRequest + success_message = constants.DATA_SAVED + success_url = reverse_lazy("manage_wallets:withdrawal_list") + + def post(self, request): + 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) + instance.reply = message + instance.status = status + instance.save() + messages.success(request, self.success_message) + except self.model.DoesNotExist: + messages.error(request, "Contact Us entry not found") + except Exception as e: + messages.error(request, str(e)) + else: + messages.error(request, "Missing 'id' or 'message' in the request") + + # Redirect to the desired URL after form submission + return redirect(self.success_url) diff --git a/templates/elements/sidebar.html b/templates/elements/sidebar.html index f3f56da..d6d031b 100644 --- a/templates/elements/sidebar.html +++ b/templates/elements/sidebar.html @@ -106,6 +106,22 @@ +