added withdrawal logic

This commit is contained in:
rizwanisready
2024-04-04 20:11:07 +05:30
parent 883a6fab8f
commit 65cee3e75f
20 changed files with 897 additions and 40 deletions

View File

@@ -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,

View File

@@ -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],
},
},
]

View File

@@ -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": [

View File

@@ -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

View File

@@ -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
),
),
]

View File

@@ -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:

View File

@@ -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}"

View File

@@ -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"]

View File

@@ -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'),
]

View File

@@ -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,
)

View File

@@ -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,
},
),
]

View File

@@ -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",
),
]

View File

@@ -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",
),
]

View File

@@ -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')}"

View File

@@ -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"),
]

View File

@@ -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)

View File

@@ -106,6 +106,22 @@
</div>
</a>
</li>
<li class="menu {% if page_name == resource_context.RESOURCE_MANAGE_BANK_ACCOUNTS %}active{% endif %}">
<a href="{% url 'manage_wallets:account_list'%}" aria-expanded="false" class="dropdown-toggle">
<div class="">
<span class="material-symbols-outlined">currency_rupee</span>
<span>Manage Accounts</span>
</div>
</a>
</li>
<li class="menu {% if page_name == resource_context.RESOURCE_MANAGE_WITHDRAWALS %}active{% endif %}">
<a href="{% url 'manage_wallets:withdrawal_list'%}" aria-expanded="false" class="dropdown-toggle">
<div class="">
<span class="material-symbols-outlined">currency_rupee</span>
<span>Manage Withdrawals</span>
</div>
</a>
</li>
<li class="menu {% if page_name == resource_context.RESOURCE_MANAGE_SUBSCRIPTIONS %}active{% endif %}">
<a href="{% url 'manage_subscriptions:subscription_list'%}" aria-expanded="false"
class="dropdown-toggle">

View File

@@ -149,10 +149,10 @@
{% csrf_token %}
<div class="modal-body">
<input type="hidden" name="id" value="" id="contactUs">
{% comment %} <div class="col-12">
<label for="subject" class="form-label">Subject</label>
<input type="text" class="form-control" name="subject" id="subject">
</div> {% endcomment %}
<div class="col-12">
<label for="status" class="form-label">Status</label>
<input type="text" class="form-control" name="status" id="status">
</div>
<div class="col-12">
<label for="message" class="form-label">Message</label>
<textarea id="message" class="form-control" name="message" rows="4" cols="50"></textarea>

View File

@@ -0,0 +1,113 @@
{% extends 'layout/base_template.html' %}
{% load static %}
{% block stylesheet %}
<!-- include required css cdn link through html here -->
{% include "cdn_through_html/datatable_cdn_css.html" %}
{% endblock %}
{% block content %}
<div class="row layout-top-spacing">
<div class="col-lg-12">
<div class="row mb-2">
<div class="col">
<h3>Manage Accounts</h3>
</div>
<div class="col text-end">
</div>
</div>
<div class="row layout-spacing">
<div class="col-lg-12">
<div class="statbox widget box box-shadow">
<div class="widget-content widget-content-area">
<div id="style-3_wrapper" class="dataTables_wrapper container-fluid dt-bootstrap4 no-footer">
<div class="table-responsive">
<table id="style-3" class="table style-3 dt-table-hover dataTable no-footer" role="grid"
aria-describedby="style-3_info">
<thead>
<tr role="row">
<th class="checkbox-column text-center sorting_asc" tabindex="0"
aria-controls="style-3" rowspan="1" colspan="1" aria-sort="ascending"
aria-label=" Record Id : activate to sort column descending"
style="width: 69.2656px;"> UCC ID </th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1"
colspan="1" aria-label="Image: activate to sort column ascending"
style="width: 44.2344px;">Principal</th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1"
colspan="1" aria-label="Image: activate to sort column ascending"
style="width: 44.2344px;">Principal Type</th>
<th class="sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
aria-label="Mobile No.: activate to sort column ascending"
style="width: 98.875px;">Account No</th>
<th class="sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
aria-label="Mobile No.: activate to sort column ascending"
style="width: 98.875px;">First Name</th>
<th class="sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
aria-label="Mobile No.: activate to sort column ascending"
style="width: 98.875px;">Last Name</th>
<th class="sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
aria-label="Mobile No.: activate to sort column ascending"
style="width: 98.875px;">Is Verified</th>
<th class="sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
aria-label="Mobile No.: activate to sort column ascending"
style="width: 98.875px;">Submitted</th>
</tr>
</thead>
<tbody>
{% for account_obj in account_objs %}
<tr role="row">
<td class="checkbox-column text-center sorting_1">{{account_obj.id}}</td>
<td>{{account_obj.principal}}</td>
<td>{{account_obj.principal.principal_type}}</td>
<td>{{account_obj.account_no}}</td>
<td>{{account_obj.first_name}}</td>
<td>{{account_obj.last_name}}</td>
<td>{{account_obj.is_verified}}</td>
<td>{{account_obj.created_on}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}
{% block javascript %}
<!-- include required js cdn link through html here -->
{% include "cdn_through_html/datatable_cdn_js.html" %}
<script>
c3 = $('#style-3').DataTable({
"dom": "<'dt--top-section'<'row'<'col-12 col-sm-6 d-flex justify-content-sm-start justify-content-center'l><'col-12 col-sm-6 d-flex justify-content-sm-end justify-content-center mt-sm-0 mt-3'f>>>" +
"<'table-responsive'tr>" +
"<'dt--bottom-section d-sm-flex justify-content-sm-between text-center'<'dt--pages-count mb-sm-0 mb-3'i><'dt--pagination'p>>",
"oLanguage": {
"oPaginate": { "sPrevious": '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-arrow-left"><line x1="19" y1="12" x2="5" y2="12"></line><polyline points="12 19 5 12 12 5"></polyline></svg>', "sNext": '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-arrow-right"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg>' },
"sInfo": "Showing page _PAGE_ of _PAGES_",
"sSearch": '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-search"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>',
"sSearchPlaceholder": "Search...",
"sLengthMenu": "Results : _MENU_",
},
"order": [[ 7, "desc" ]],
"stripeClasses": [],
"lengthMenu": [5, 10, 20, 50],
"pageLength": 10
});
multiCheck(c3);
</script>
{% endblock %}

View File

@@ -0,0 +1,231 @@
{% extends 'layout/base_template.html' %}
{% load static %}
{% block stylesheet %}
<!-- include required css cdn link through html here -->
{% include "cdn_through_html/datatable_cdn_css.html" %}
{% include "cdn_through_html/animate_cdn_css.html" %}
{% include "cdn_through_html/modal_cdn_css.html" %}
{% include "cdn_through_html/tabs_cdn_css.html" %}
{% endblock %}
{% block content %}
<div class="row layout-top-spacing">
<div class="col-lg-12">
<div class="row mb-2">
<div class="col">
<h3>Manage Withdrawals</h3>
</div>
<div class="col text-end">
{% comment %} <button class="btn btn-dark mb-2 me-4" onclick="history.back()">
<i class="fa fa-arrow-left"></i>
Back
</button> {% endcomment %}
{% comment %} <a class="btn btn-primary mb-2 me-4" href="">Add Newsletter</a> {% endcomment %}
</div>
</div>
<div class="row layout-spacing">
<div class="col-lg-12">
<div class="statbox widget box box-shadow">
<div class="widget-content widget-content-area">
<div id="contact-us_wrapper" class="dataTables_wrapper container-fluid dt-bootstrap4 no-footer">
<div class="table-responsive">
<table id="contact-us" class="table style-3 dt-table-hover dataTable no-footer"
role="grid" aria-describedby="style-3_info">
<thead>
<tr role="row">
<th class="text-center" tabindex="0" aria-controls="style-3" rowspan="1"
colspan="1" style="width: 69.2656px;"> Record Id </th>
<th tabindex="1" aria-controls="style-3" rowspan="1" colspan="1"
style="width: 44.2344px;">Principal</th>
<th tabindex="2" aria-controls="style-3" rowspan="1" colspan="1"
style="width: 79.7969px;">Coins</th>
<th tabindex="3" aria-controls="style-3" rowspan="1" colspan="1"
style="width: 77.3281px;">Amount</th>
<th tabindex="4" aria-controls="style-3" rowspan="1" colspan="1"
style="width: 143.516px;">Submitted</th>
<th tabindex="5" aria-controls="style-3" rowspan="1" colspan="1"
style="width: 98.875px;">Reply / Update</th>
<th tabindex="7" aria-controls="style-3" rowspan="1" colspan="1"
style="width: 51.625px;">Action</th>
</tr>
</thead>
<tbody>
{% for withdrawal_obj in withdrawal_objs%}
<tr role="row">
<td class="text-center"> {{withdrawal_obj.id}} </td>
<td>{{withdrawal_obj.principal}}</td>
<td>{{withdrawal_obj.coins}}</td>
<td>{{withdrawal_obj.amount}}</td>
<td>{{withdrawal_obj.created_on}}</td>
<td class="text-center">
<button type="button" class="btn btn-info mb-2 me-4"
data-bs-toggle="modal" data-bs-target="#tabsModalMessageReply"
onclick="MessageModal('{{withdrawal_obj.notes}}','{{withdrawal_obj.reply}}')">
View
</button>
</td>
<td class="text-center">
<button type="button" class="btn btn-success mb-2 me-4"
data-bs-toggle="modal" data-bs-target="#replyFormModal"
onclick="ReplyModal('{{withdrawal_obj.id}}','{{withdrawal_obj.status}}')">
Reply
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Message/ reply modal -->
<div class="modal fade" id="tabsModalMessageReply" tabindex="-1" role="dialog" aria-labelledby="tabsModalLabel"
aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="tabsModalLabel">Message / Reply</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">
</button>
</div>
<div class="modal-body">
<div class="simple-pill">
<ul class="nav nav-pills mb-3" id="pills-tab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="pills-message-tab" data-bs-toggle="pill"
data-bs-target="#pills-message" type="button" role="tab" aria-controls="pills-message"
aria-selected="true">Message</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="pills-reply-tab" data-bs-toggle="pill"
data-bs-target="#pills-reply" type="button" role="tab" aria-controls="pills-reply"
aria-selected="false">Reply</button>
</li>
</ul>
<div class="tab-content" id="pills-tabContent">
<div class="tab-pane fade show active" id="pills-message" role="tabpanel"
aria-labelledby="pills-message-tab" tabindex="0">
<div class="card">
<div class="card-body">
<p class="mt-3" id="messageData"></p>
</div>
</div>
</div>
<div class="tab-pane fade" id="pills-reply" role="tabpanel" aria-labelledby="pills-reply-tab"
tabindex="0">
<div class="card">
<div class="card-body">
<p class="mt-3" id="replyData"></p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-light-dark" data-bs-dismiss="modal">Discard</button>
</div>
</div>
</div>
</div>
<!-- reply column modal-->
<div class="modal fade inputForm-modal" id="replyFormModal" tabindex="-1" role="dialog"
aria-labelledby="inputFormModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header" id="inputFormModalLabel">
<h5 class="modal-title">Reply</b></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-hidden="true"></button>
</div>
<form class="mt-0" method="POST" action="{% url 'manage_wallets:update_status' %}">
{% csrf_token %}
<div class="modal-body">
<input type="hidden" name="id" value="" id="contactUs">
<div class="col-12">
<label for="status">Status</label>
<select id="status" name="status" class="form-control">
<option value="submitted">Submitted</option>
<option value="review">Review</option>
<option value="processing">Under Process</option>
<option value="transferred">Transferred</option>
<option value="dispute">Dispute</option>
<option value="denied">Denied</option>
</select>
</div>
<div class="col-12">
<label for="message" class="form-label">Message</label>
<textarea id="message" class="form-control" name="message" rows="4" cols="50"></textarea>
</div>
<button type="submit" class="btn btn-primary mt-2 mb-2 btn-no-effect"
data-bs-dismiss="modal">Send</button>
</div>
</form>
</div>
</div>
</div>
{% endblock content %}
{% block javascript %}
<!-- include required js cdn link through html here -->
{% include "cdn_through_html/datatable_cdn_js.html" %}
<script>
contact_us = $('#contact-us').DataTable({
"dom": "<'dt--top-section'<'row'<'col-12 col-sm-6 d-flex justify-content-sm-start justify-content-center'l><'col-12 col-sm-6 d-flex justify-content-sm-end justify-content-center mt-sm-0 mt-3'f>>>" +
"<'table-responsive'tr>" +
"<'dt--bottom-section d-sm-flex justify-content-sm-between text-center'<'dt--pages-count mb-sm-0 mb-3'i><'dt--pagination'p>>",
"oLanguage": {
"oPaginate": { "sPrevious": '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-arrow-left"><line x1="19" y1="12" x2="5" y2="12"></line><polyline points="12 19 5 12 12 5"></polyline></svg>', "sNext": '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-arrow-right"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg>' },
"sInfo": "Showing page _PAGE_ of _PAGES_",
"sSearch": '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-search"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>',
"sSearchPlaceholder": "Search...",
"sLengthMenu": "Results : _MENU_",
},
"stripeClasses": [],
"lengthMenu": [5, 10, 20, 50],
"pageLength": 10
});
// message modal show and set data
function MessageModal(message, reply) {
console.log("trigger")
// Set the data in the modal content
$("#messageData").text(message);
$("#replyData").text(reply);
// Show the modal
$('#tabsModalMessageReply').modal('show');
}
// reply modal show and set data
function ReplyModal(contact_us_id, status) {
console.log("trigger")
// Set the data in the modal content
$("#contactUs").val(contact_us_id);
$("#status").val(status);
// Show the modal
$('#replyFormModal').modal('show');
}
</script>
{% endblock %}