Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic event registrations with custom form fields and payment options #2

Merged
merged 14 commits into from
Dec 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"files.associations": {
"*.html": "jinja-html"
}
"*.html": "django-html"
},
"python.languageServer": "None"
}
19 changes: 19 additions & 0 deletions src/donations/migrations/0002_alter_donationtier_visible.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 4.2.1 on 2024-12-27 19:11

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("donations", "0001_initial"),
]

operations = [
migrations.AlterField(
model_name="donationtier",
name="visible",
field=models.BooleanField(
default=False, verbose_name="Show on Donations Page ?"
),
),
]
2 changes: 1 addition & 1 deletion src/donations/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class DonationTier(models.Model):
name = models.CharField(max_length=256)
description = models.CharField(max_length=1024)
amount = models.PositiveIntegerField(validators=[MaxValueValidator(10000)])
visible = models.BooleanField(default=False)
visible = models.BooleanField(verbose_name="Show on Donations Page ?", default=False)

def __str__(self):
return self.name
Expand Down
4 changes: 2 additions & 2 deletions src/donations/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from .forms import DonationForm
from .models import DonationConfig, DonationTier, DonationReceived
from temple_web.myconfig import PaymentGatewayConfig
from .upi_gateway import create_order, check_order_status
from utils.payment.upi_gateway import create_order, check_order_status
from uuid import uuid4
from datetime import date
from utils.adirect import adirect
Expand All @@ -20,7 +20,7 @@

def donations(request):
donation_config = DonationConfig.get_solo()
donation_tiers = DonationTier.objects.all()
donation_tiers = DonationTier.objects.filter(visible=True)
context = {"donation_config": donation_config, "donation_tiers": donation_tiers}
return render(request, "donations/donations.html", context=context)

Expand Down
156 changes: 141 additions & 15 deletions src/haps/admin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
from django.contrib import admin
from .models import EventRegistration, Event
from .models import EventRegistration, Event, EventFormField


class EventFormFieldInline(admin.TabularInline):
model = EventFormField
extra = 1
ordering = ['order']


@admin.register(Event)
Expand All @@ -11,24 +17,144 @@ class EventAdmin(admin.ModelAdmin):
"start_time",
"accept_reg",
"show_on_home",
"login_required",
"registration_fee",
"event_page",
]
list_filter = ["show_on_home", "accept_reg"]
fields = [
"name",
"description",
"cover_image",
"venue",
"start_time",
"end_time",
"accept_reg",
"show_on_home",
"content",
list_filter = ["show_on_home", "accept_reg", "login_required"]
fieldsets = [
(None, {
'fields': [
"name",
"description",
"cover_image",
"venue",
"start_time",
"end_time",
]
}),
('Registration Settings', {
'fields': [
"accept_reg",
"login_required",
"registration_fee",
],
'description': 'Configure how users can register for this event'
}),
('Display Settings', {
'fields': [
"show_on_home",
"content",
]
})
]
inlines = [EventFormFieldInline]


@admin.register(EventRegistration)
class EventRegistrationAdmin(admin.ModelAdmin):
search_fields = ["event", "user"]
list_display = ["event", "user_name", "user_whatsapp", "user", "user_profile_link"]
list_filter = ["event__name"]
search_fields = ["event__name", "user__email", "order_id", "client_txn_id"]
list_display = [
"registration_number",
"event",
"user_name",
"user_whatsapp",
"datetime",
"amount",
"payment_status",
"order_id",
"payment_date_time",
]
list_filter = ["event__name", "payment_status"]
readonly_fields = [
"datetime",
"form_responses",
"payment_status",
"order_id",
"client_txn_id",
"payment_date_time",
"payment_data",
"get_payment_details"
]

fieldsets = [
(None, {
'fields': [
"event",
"user",
"datetime",
"amount",
"form_responses",
]
}),
('Payment Information', {
'fields': [
"payment_status",
"order_id",
"client_txn_id",
"payment_date_time",
],
'classes': ['collapse']
}),
('Payment Diagnostic Data', {
'fields': [
"get_payment_details",
],
'classes': ['collapse'],
'description': 'Detailed payment transaction data from UPI gateway'
})
]

def has_change_permission(self, request, obj=None):
return False

def has_delete_permission(self, request, obj=None):
return False

def has_add_permission(self, request):
return False

def get_queryset(self, request):
return super().get_queryset(request).select_related('event', 'user')

def registration_number(self, obj):
return obj.registration_number
registration_number.short_description = "Registration No."

def get_payment_details(self, obj):
if not obj.payment_data:
return "No payment data available"

# Format payment data for display
details = []
if obj.payment_data.get('customer_vpa'):
details.append(f"Customer UPI: {obj.payment_data['customer_vpa']}")
if obj.payment_data.get('upi_txn_id'):
details.append(f"UPI Transaction ID: {obj.payment_data['upi_txn_id']}")
if obj.payment_data.get('status'):
details.append(f"Status: {obj.payment_data['status']}")
if obj.payment_data.get('remark'):
details.append(f"Remark: {obj.payment_data['remark']}")
if obj.payment_data.get('txnAt'):
details.append(f"Transaction Time: {obj.payment_data['txnAt']}")

# Merchant details
merchant = obj.payment_data.get('merchant', {})
if merchant:
details.append("Merchant Details:")
if merchant.get('name'):
details.append(f" - Name: {merchant['name']}")
if merchant.get('upi_id'):
details.append(f" - UPI ID: {merchant['upi_id']}")

# User defined fields
for i in range(1, 4):
udf = obj.payment_data.get(f'udf{i}')
if udf:
details.append(f"UDF{i}: {udf}")

if obj.payment_data.get('createdAt'):
details.append(f"Created At: {obj.payment_data['createdAt']}")

return "\n".join(details)
get_payment_details.short_description = "Payment Details"
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Generated by Django 4.2.1 on 2024-12-27 19:11

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("haps", "0004_event_content"),
]

operations = [
migrations.AddField(
model_name="event",
name="login_required",
field=models.BooleanField(
default=True, verbose_name="Login required for registration?"
),
),
migrations.AddField(
model_name="event",
name="registration_fee",
field=models.PositiveIntegerField(
blank=True, help_text="Leave blank for free registration", null=True
),
),
migrations.AddField(
model_name="eventregistration",
name="amount",
field=models.PositiveIntegerField(blank=True, null=True),
),
migrations.AddField(
model_name="eventregistration",
name="form_responses",
field=models.JSONField(default=dict),
),
migrations.AddField(
model_name="eventregistration",
name="order_id",
field=models.CharField(blank=True, max_length=100, null=True),
),
migrations.AddField(
model_name="eventregistration",
name="payment_status",
field=models.CharField(
choices=[
("pending", "Pending"),
("success", "Success"),
("failure", "Failure"),
],
default="pending",
max_length=10,
),
),
migrations.AlterField(
model_name="eventregistration",
name="user",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
migrations.CreateModel(
name="EventFormField",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("field_label", models.CharField(max_length=100)),
(
"field_type",
models.CharField(
choices=[
("text", "Text Input"),
("number", "Number Input"),
("email", "Email Input"),
("textarea", "Text Area"),
],
max_length=20,
),
),
("required", models.BooleanField(default=True)),
("order", models.PositiveIntegerField(default=0)),
("help_text", models.CharField(blank=True, max_length=200)),
(
"event",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="form_fields",
to="haps.event",
),
),
],
options={
"ordering": ["order"],
"unique_together": {("event", "field_label")},
},
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Generated by Django 4.2.1 on 2024-12-29 19:34

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("haps", "0005_event_login_required_event_registration_fee_and_more"),
]

operations = [
migrations.AddField(
model_name="eventregistration",
name="client_txn_id",
field=models.CharField(
blank=True, db_index=True, max_length=128, null=True, unique=True
),
),
migrations.AddField(
model_name="eventregistration",
name="payment_data",
field=models.JSONField(
default=dict,
help_text="\n Stores payment-related data from UPI gateway including:\n - customer_vpa: Customer's UPI ID\n - upi_txn_id: UPI transaction ID\n - status: Detailed payment status\n - remark: Payment remarks/failure reason\n - txnAt: Transaction timestamp\n - merchant: Merchant details (name, upi_id)\n - udf1, udf2, udf3: User defined fields\n - redirect_url: Payment redirect URL\n - createdAt: Order creation time\n ",
),
),
migrations.AddField(
model_name="eventregistration",
name="payment_date_time",
field=models.DateTimeField(blank=True, null=True),
),
]
Loading
Loading