transactions

This commit is contained in:
rizwanisready
2024-03-10 23:27:22 +05:30
parent 34d60bf735
commit 97255b2a60
14 changed files with 319 additions and 136 deletions

View File

@@ -1,5 +1,45 @@
from django.contrib import admin
from .models import TicketIssueType, TicketAttachment, Tickets
from .models import TicketIssueType, TicketAttachment, Tickets, Feedback, ContactUs
class FeedbackAdmin(admin.ModelAdmin):
list_display = ("id", "principal", "email", "rating", "short_comment")
list_filter = ("rating", "principal")
search_fields = ("email", "comment")
readonly_fields = ("email",)
def short_comment(self, obj):
"""Create a shortened version of the comment for display in the list."""
return obj.comment[:50] + "..." if len(obj.comment) > 50 else obj.comment
short_comment.short_description = "Comment"
class ContactUsAdmin(admin.ModelAdmin):
list_display = (
"name",
"email_address",
"mobile_number",
"subject",
"short_message",
"has_reply",
)
list_filter = ("subject",)
search_fields = ("name", "email_address", "mobile_number", "message")
readonly_fields = ("name", "email_address", "mobile_number", "subject", "message")
def short_message(self, obj):
"""Create a shortened version of the message for display in the list."""
return obj.message[:50] + "..." if len(obj.message) > 50 else obj.message
short_message.short_description = "Message"
def has_reply(self, obj):
"""Indicate whether the contact request has been replied to."""
return obj.reply is not None
has_reply.boolean = True
has_reply.short_description = "Replied?"
class TicketIssueTypeAdmin(admin.ModelAdmin):
@@ -26,6 +66,8 @@ class TicketsAdmin(admin.ModelAdmin):
filter_horizontal = ("attachments",) # For the many-to-many relationship
admin.site.register(ContactUs, ContactUsAdmin)
admin.site.register(Feedback, FeedbackAdmin)
admin.site.register(TicketIssueType, TicketIssueTypeAdmin)
admin.site.register(TicketAttachment, TicketAttachmentAdmin)
admin.site.register(Tickets, TicketsAdmin)

View File

@@ -9,7 +9,8 @@ from goodtimes import date_utils
class ContactUsSerializer(serializers.ModelSerializer):
class Meta:
model = ContactUs
fields = "__all__"
# Alternatively, you can specify fields explicitly:
fields = ['name', 'email_address', 'mobile_number', 'subject', 'message']
class TicketIssueTypeSerializer(serializers.ModelSerializer):
class Meta:

View File

@@ -4,12 +4,15 @@ from . import views
app_name = "manage_communications"
urlpatterns = [
path('contact_us/', views.ContactUsListView.as_view(), name='contact_us_list'),
path('contact_us/reply/', views.ContactUsReplyView.as_view(), name='contact_us_reply'),
path('tickets/', views.TicketListView.as_view(), name='ticket_list'),
path('feedback/', views.FeedbackListView.as_view(), name='feedback_list'),
path('feedback/delete/<int:pk>', views.FeedbackDeleteView.as_view(), name='feedback_delete'),
path("contact_us/", views.ContactUsListView.as_view(), name="contact_us_list"),
path(
"contact_us/reply/", views.ContactUsReplyView.as_view(), name="contact_us_reply"
),
path("tickets/", views.TicketListView.as_view(), name="ticket_list"),
path("feedback/", views.FeedbackListView.as_view(), name="feedback_list"),
path(
"feedback/delete/<int:pk>",
views.FeedbackDeleteView.as_view(),
name="feedback_delete",
),
]

View File

@@ -124,6 +124,7 @@ class FeedbackDeleteView(LoginRequiredMixin, generic.View):
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:

View File

@@ -6,6 +6,7 @@ from manage_events.models import (
Event,
EventCategory,
EventImage,
EventReview,
Favorites,
Venue,
PrincipalPreference,
@@ -44,6 +45,7 @@ class EventDetailSerializer(serializers.ModelSerializer):
venue = VenueSerializer(read_only=True) # Use VenueSerializer for the venue field
category = EventCategorySerializer(read_only=True)
is_favorited = serializers.SerializerMethodField()
reviews = serializers.SerializerMethodField()
class Meta:
model = Event
@@ -66,6 +68,7 @@ class EventDetailSerializer(serializers.ModelSerializer):
"age_group",
"images",
"is_favorited",
"reviews",
]
def get_images(self, obj):
@@ -81,6 +84,11 @@ class EventDetailSerializer(serializers.ModelSerializer):
return Favorites.objects.filter(event=obj, principal=user).exists()
return False
def get_reviews(self, obj):
# related_name is 'reviews' as defined in EventReview model
reviews = obj.reviews.all()
return EventReviewSerializer(reviews, many=True, context=self.context).data
class CreateEventSerializer(serializers.ModelSerializer):
images = serializers.ListField(
@@ -187,5 +195,20 @@ class EventMasterSerializer(serializers.ModelSerializer):
class EventDateRangeSerializer(serializers.Serializer):
start_date = serializers.DateField(format='%Y-%m-%d', input_formats=['%Y-%m-%d'])
end_date = serializers.DateField(format='%Y-%m-%d', input_formats=['%Y-%m-%d'])
start_date = serializers.DateField(format="%Y-%m-%d", input_formats=["%Y-%m-%d"])
end_date = serializers.DateField(format="%Y-%m-%d", input_formats=["%Y-%m-%d"])
class EventReviewSerializer(serializers.ModelSerializer):
principal = ProfileSerializer(read_only=True)
class Meta:
model = EventReview
fields = ["id", "event", "principal", "review_text", "rating"]
extra_kwargs = {
"principal": {"read_only": True},
}
def create(self, validated_data):
validated_data["principal"] = self.context["request"].user
return super().create(validated_data)

View File

@@ -91,4 +91,10 @@ urlpatterns = [
),
# My Events
path("my-events/", views.MyEventsAPIView.as_view(), name="my-events"),
# Events Review Add and Edit
path(
"event-reviews/",
views.EventReviewCreateAPIView.as_view(),
name="event-review-create",
),
]

View File

@@ -2,7 +2,7 @@ from datetime import timedelta
from django.db import transaction
from django.shortcuts import get_object_or_404
from django.utils import timezone
from rest_framework import status, generics
from rest_framework import status, generics, mixins
from rest_framework.views import APIView
from django.conf import settings
from accounts.models import IAmPrincipalLocation
@@ -22,6 +22,7 @@ from manage_events.api.serializers import (
CreateVenueSerializer,
EventCategorySerializer,
EventDetailSerializer,
EventReviewSerializer,
IAmPrincipalLocationSerializer,
PrincipalPreferenceSerializer,
VenueSerializer,
@@ -32,6 +33,7 @@ from manage_events.models import (
Event,
EventCategory,
EventPrincipalInteraction,
EventReview,
Favorites,
PrincipalPreference,
Venue,
@@ -659,3 +661,25 @@ class EventDateRangeAPIView(APIView):
message=constants.SUCCESS,
status=status.HTTP_200_OK,
)
class EventReviewCreateAPIView(mixins.CreateModelMixin, generics.GenericAPIView):
queryset = EventReview.objects.filter(active=True, deleted=False)
serializer_class = EventReviewSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return ApiResponse.success(
"Review created successfully.",
data=serializer.data,
status=status.HTTP_201_CREATED,
)
else:
return ApiResponse.error(
"Validation error.",
errors=serializer.errors,
status=status.HTTP_400_BAD_REQUEST,
)

View File

@@ -0,0 +1,127 @@
# Generated by Django 5.0.2 on 2024-03-10 14:49
import django.db.models.deletion
import django.utils.timezone
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("manage_events", "0003_alter_eventprincipalinteraction_unique_together"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name="favorites",
name="active",
field=models.BooleanField(default=True),
),
migrations.AddField(
model_name="favorites",
name="created_by",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="%(class)s_created",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AddField(
model_name="favorites",
name="created_on",
field=models.DateTimeField(
auto_now_add=True, default=django.utils.timezone.now
),
preserve_default=False,
),
migrations.AddField(
model_name="favorites",
name="deleted",
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name="favorites",
name="modified_by",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="%(class)s_modified",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AddField(
model_name="favorites",
name="modified_on",
field=models.DateTimeField(auto_now=True),
),
migrations.CreateModel(
name="EventReview",
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)),
("review_text", models.TextField()),
(
"rating",
models.PositiveSmallIntegerField(
choices=[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]
),
),
(
"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,
),
),
(
"event",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="reviews",
to="manage_events.event",
),
),
(
"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="event_reviews",
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"ordering": ["-created_on"],
"unique_together": {("event", "principal")},
},
),
]

View File

@@ -0,0 +1,17 @@
# Generated by Django 5.0.2 on 2024-03-10 17:08
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("manage_events", "0004_favorites_active_favorites_created_by_and_more"),
]
operations = [
migrations.AlterUniqueTogether(
name="eventreview",
unique_together=set(),
),
]

View File

@@ -126,7 +126,7 @@ class PrincipalPreference(BaseModel):
db_table = "user_preference"
class Favorites(models.Model):
class Favorites(BaseModel):
principal = models.ForeignKey(
IAmPrincipal, on_delete=models.CASCADE, related_name="favorited_by"
)
@@ -137,3 +137,27 @@ class Favorites(models.Model):
def __str__(self):
return f"{self.principal}'s favorite: {self.event.title}"
class EventReview(BaseModel):
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="reviews")
principal = models.ForeignKey(
IAmPrincipal, on_delete=models.CASCADE, related_name="event_reviews"
)
review_text = models.TextField()
rating = models.PositiveSmallIntegerField(
choices=[
(1, 1),
(2, 2),
(3, 3),
(4, 4),
(5, 5),
]
)
class Meta:
# sort reviews, to show latest first by default:
ordering = ["-created_on"]
def __str__(self):
return f"Review by {self.principal} on {self.event}"

View File

@@ -19,7 +19,34 @@ class WalletSerializer(serializers.ModelSerializer):
class TransactionSerializer(serializers.ModelSerializer):
class Meta:
model = models.Transaction
fields = "__all__"
fields = [
'id',
'principal',
'principal_subscription',
'transaction_type',
'payment_method',
'transaction_status',
'amount',
'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},
}
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()
return representation
class MerchantDepositSerializer(serializers.Serializer):

View File

@@ -4,14 +4,13 @@ from . import views
app_name = "manage_wallet_api"
urlpatterns = [
path('become-a-merchant/<str:principal_type>/', views.BecomeAMerchantView.as_view(), name='become_a_merchant'),
path('webhook-test/', views.StripeWebhookTest.as_view(), name='webhook_test'),
path('postman-test/', views.TestWebhookAPI.as_view(), name='postman_test'),
path('postman-test-withdraw/', views.TestWebhookAPIWithdraw.as_view(), name='postman_test_withdraw'),
path('get-wallet/', views.GetWallet.as_view(), name='get_wallet'),
path('is-merchant/', views.IsMerchant.as_view(), name='is_merchant'),
path('transactions/', views.TransactionView.as_view(), name='transactions'),
path('merchant-deposit/', views.MerchantDeposit.as_view(), name='merchant_deposit'),
path("webhook-test/", views.StripeWebhookTest.as_view(), name="webhook_test"),
path("postman-test/", views.TestWebhookAPI.as_view(), name="postman_test"),
path(
"postman-test-withdraw/",
views.TestWebhookAPIWithdraw.as_view(),
name="postman_test_withdraw",
),
path("get-wallet/", views.GetWallet.as_view(), name="get_wallet"),
path("get-transaction/", views.TransactionView.as_view(), name="transactions"),
]

View File

@@ -24,38 +24,6 @@ from rest_framework_simplejwt.authentication import JWTAuthentication
import stripe
class BecomeAMerchantView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
stripe.api_key = settings.STRIPE_SECRET_KEY
def post(self, request, type):
print(request.data)
amount = 1000
principal_type = request.data["principal_type"]
try:
payment_intent = stripe.PaymentIntent.create(
amount=100000,
currency="INR",
description="Merchant Registration",
metadata={
"principal_id": request.user.id,
"principal_type": principal_type,
},
)
# payment_intent_id = payment_intent.id # Get the payment_intent ID
return Response(
{
"client_secret": payment_intent.client_secret,
"message": "Payment intent created successfully",
}
)
except stripe.error.StripeError as e:
# Handle Stripe-related errors
return Response({"error": str(e)}, status=400)
@method_decorator(csrf_exempt, name="dispatch")
class StripeWebhookTest(APIView):
authentication_classes = []
@@ -165,39 +133,6 @@ class GetWallet(APIView):
return ApiResponse.error(**error_response)
class IsMerchant(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
model = models.Wallet
def get(self, request):
try:
# Get the Wallet associated with the request.user
wallet_obj = models.Wallet.objects.get(principal_id=request.user.id)
merchant_deposit = wallet_obj.merchant_deposit
if merchant_deposit >= 1.00:
success_response = {
"status": status.HTTP_200_OK,
"message": constants.SUCCESS,
"data": {"is_merchant": True},
}
return ApiResponse.success(**success_response)
data_response = {
"status": status.HTTP_200_OK,
"message": constants.SUCCESS,
"data": {"is_merchant": False},
}
return ApiResponse.success(**data_response)
except Exception as e:
# Handle any exceptions and return an error response
error_response = {
"status": status.HTTP_500_INTERNAL_SERVER_ERROR,
"message": constants.INTERNAL_SERVER_ERROR,
"errors": str(e),
}
return ApiResponse.error(**error_response)
class TransactionView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
@@ -335,45 +270,3 @@ class TestWebhookAPIWithdraw(APIView):
"message": "Webhook received, but payment failed",
}
return ApiResponse.success(**intent_response)
class MerchantDeposit(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
def post(self, request):
# print(request.data)
serializer = serializers.MerchantDepositSerializer(data=request.data)
# print("serializer: ", serializer)
if serializer.is_valid():
print(serializer.validated_data)
wallet_manager = services.WalletManager(
principal=request.user,
principal_type=IAmPrincipalType.objects.filter(
name=serializer.validated_data["principal_type"]
).first(),
)
deposit_amount = serializer.validated_data["amount"]
deposit_field = serializer.validated_data["field"]
deposit_transaction = wallet_manager.deposit(deposit_amount, deposit_field)
print("Passed Through principal_wallet Object")
current_balance = models.Wallet.objects.get(principal=request.user)
success_response = {
"status": status.HTTP_200_OK,
"message": "Deposit Received",
"data": {
**serializer.data,
"merchant_deposit": current_balance.merchant_deposit,
},
}
return ApiResponse.success(**success_response)
else:
intent_response = {
"status": status.HTTP_400_BAD_REQUEST,
"message": "Deposit Failed",
"errors": serializer.errors,
}
return ApiResponse.error(**intent_response)

View File

@@ -78,9 +78,6 @@
<th class="sorting" tabindex="5" aria-controls="style-3"
style="width: 79.7969px;">Rating</th>
<th class="sorting" tabindex="5" aria-controls="style-3"
style="width: 79.7969px;">Reaction</th>
<th class="dt-no-sorting sorting" tabindex="6"
aria-controls="style-3"
style="width: 51.625px;">Action</th>
@@ -100,7 +97,6 @@
</span>
{% endfor %}
</td>
<td>{{data_obj.feedback_reaction}}</td>
<td class="text-center">
<ul class="table-controls">
<li><a href="{% url 'manage_communications:feedback_delete' data_obj.id %}" class="bs-tooltip"