diff --git a/accounts/views.py b/accounts/views.py index 490ef43..24f052f 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -797,8 +797,18 @@ class CustomerUpdateView(LoginRequiredMixin, generic.View): return render(request, self.template_name, context=context) class CustomerDetailView(LoginRequiredMixin, generic.DetailView): + page_name = resource_action.RESOURCE_MANAGE_CUSTOMER + resource = resource_action.RESOURCE_MANAGE_CUSTOMER + action = resource_action.ACTION_READ template_name = 'accounts/customer/customer_detail.html' + def get_context_data(self, **kwargs): + context = { + "page_name": self.page_name, + } + context.update(kwargs) # Include any additional context data passed to the view + return context + def get(self, request, *args, **kwargs): principal_obj = IAmPrincipal.objects.get(pk=kwargs.get("pk")) try: @@ -806,7 +816,8 @@ class CustomerDetailView(LoginRequiredMixin, generic.DetailView): except Exception as e: principal_preference = None principal_subscription = PrincipalSubscription.objects.filter(principal=principal_obj).order_by("-start_date").first() - return render(request, self.template_name, locals()) + context = self.get_context_data(principal_obj=principal_obj,principal_preference=principal_preference,principal_subscription=principal_subscription) + return render(request, self.template_name, context=context) class CustomerListView(LoginRequiredMixin, generic.ListView): page_name = resource_action.RESOURCE_MANAGE_CUSTOMER diff --git a/manage_events/api/filters.py b/manage_events/api/filters.py index cb88f8c..e26a938 100644 --- a/manage_events/api/filters.py +++ b/manage_events/api/filters.py @@ -14,7 +14,7 @@ class EventFilter(filters.FilterSet): # end_date = filters.DateFilter(field_name="end_date", lookup_expr="lte") price_from = filters.NumberFilter(field_name="entry_fee", lookup_expr="gte") price_to = filters.NumberFilter(field_name="entry_fee", lookup_expr="lte") - age_group = filters.CharFilter(field_name="age_group", lookup_expr="icontains") + age_group = filters.CharFilter(method="filter_age_group") class Meta: model = Event @@ -33,6 +33,11 @@ class EventFilter(filters.FilterSet): category = value.split(',') return queryset.filter(category__title__in=category) + def filter_age_group(self, queryset, name, value): + age_group = value.split(',') + return queryset.filter(age_group__in=age_group) + + # def filter_queryset(self, queryset): # queryset = super().filter_queryset(queryset) # if 'price_from' in self.data or 'price_to' in self.data: diff --git a/manage_subscriptions/forms.py b/manage_subscriptions/forms.py index ececb47..d4afaee 100644 --- a/manage_subscriptions/forms.py +++ b/manage_subscriptions/forms.py @@ -11,6 +11,7 @@ class SubscriptionForm(forms.ModelForm): fields = [ "title", "short_description", + "long_description", "interval", "interval_count", "high_amount", @@ -20,6 +21,7 @@ class SubscriptionForm(forms.ModelForm): "active", "is_free", ] + exclude = [] def __init__(self, *args, **kwargs): super(SubscriptionForm, self).__init__(*args, **kwargs) @@ -29,6 +31,23 @@ class SubscriptionForm(forms.ModelForm): id__in=[event_user.id, event_manager.id] ) + if self.instance: + # If there is an instance (i.e. we're editing an existing subscription) + + # Use a dictionary comprehension to create a new dictionary of fields + # that excludes the readonly fields + self.fields = { + field_name: field + for field_name, field in self.fields.items() + if field_name not in [ + "interval", + "interval_count", + "amount", + "high_amount", + "principal_types", + ] + } + class PrincipalSubscriptionForm(forms.ModelForm): class Meta: diff --git a/manage_subscriptions/migrations/0014_alter_subscription_long_description.py b/manage_subscriptions/migrations/0014_alter_subscription_long_description.py new file mode 100644 index 0000000..51a85f9 --- /dev/null +++ b/manage_subscriptions/migrations/0014_alter_subscription_long_description.py @@ -0,0 +1,19 @@ +# Generated by Django 5.0.2 on 2024-08-25 10:39 + +import django_quill.fields +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('manage_subscriptions', '0013_remove_subscription_plan_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='subscription', + name='long_description', + field=django_quill.fields.QuillField(), + ), + ] diff --git a/manage_subscriptions/models.py b/manage_subscriptions/models.py index b14e238..70bba3b 100644 --- a/manage_subscriptions/models.py +++ b/manage_subscriptions/models.py @@ -4,6 +4,7 @@ from django.db import models from django.core.exceptions import ValidationError from accounts.models import BaseModel, IAmPrincipal, IAmPrincipalType from django.utils.translation import gettext_lazy as _ +from django_quill.fields import QuillField class Subscription(BaseModel): @@ -22,7 +23,7 @@ class Subscription(BaseModel): price_id = models.CharField(max_length=255, blank=True, null=True) product_id = models.CharField(max_length=255, blank=True, null=True) short_description = models.CharField(max_length=255, null=True, blank=True) - long_description = models.TextField(null=True, blank=True) + long_description = QuillField() image = models.ImageField(upload_to="subscription_img", null=True, blank=True) interval = models.CharField(max_length=10, choices=INTERVAL_TYPES) interval_count = models.IntegerField(default=1) @@ -56,23 +57,44 @@ class Subscription(BaseModel): def save(self, *args, **kwargs): from goodtimes.services import StripeService + if not self.delete: self.clean() - if not self.is_free: - if self.price_id: - # Stipe dont provide to update the price record except active and deactive + if self.is_free: + # If is_free is True, set amounts to 0 and remove Stripe price and product IDs + self.high_amount = 0.00 + self.amount = 0.00 + self.price_id = None + self.product_id = None + else: + if self.id and self.price_id: # Update existing subscription + # Retrieve existing price and product from Stripe price = StripeService.retrieve_price(self.price_id) if not price["success"]: raise Exception(price['message']) + # Update price active status if it differs from local active status if self.active != price["data"].active: StripeService.update_price(price_id=self.price_id, active=self.active) - else: + + # Retrieve existing product from Stripe + product = StripeService.retrive_product(self.product_id) + if not product["success"]: + raise Exception(product['message']) + + # Update product data if it has changed + if product["data"].name != self.title or product["data"].description != self.short_description: + StripeService.update_product( + product_id=self.product_id, + name=self.title, + description=self.short_description + ) + else: # Create new subscription # Create new product and price price = StripeService.create_price( product_data={ - "name": self.txitle, + "name": self.title, "description": self.short_description, }, unit_amount=int(self.amount * 100), @@ -89,7 +111,7 @@ class Subscription(BaseModel): if not price["success"]: raise Exception(price['message']) - # add the id in record + # Add the IDs to the record self.price_id = price["data"].id self.product_id = price["data"].product @@ -105,8 +127,6 @@ class Subscription(BaseModel): return count[self.interval] * self.interval_count - - class SubscriptionStatus(models.TextChoices): ACTIVE = "active", _("Active") EXPIRED = "expired", _("Expired") @@ -146,6 +166,17 @@ class PrincipalSubscription(BaseModel): def __str__(self): return f"{self.subscription} - {self.principal.first_name}" + + def save(self, *args, **kwargs): + # If the subscription status is expired or inactive, set the active flag to False + if self.status in [SubscriptionStatus.EXPIRED, SubscriptionStatus.INACTIVE]: + self.active = False + + # If the active flag is False, set the status to inactive + if not self.active: + self.status = SubscriptionStatus.INACTIVE + super.save(*args, **kwargs) + def generate_order_id(email): return f"order_{str(timezone.localtime().timestamp())}{str(email)}" diff --git a/manage_subscriptions/urls.py b/manage_subscriptions/urls.py index 6e69838..21c0d15 100644 --- a/manage_subscriptions/urls.py +++ b/manage_subscriptions/urls.py @@ -12,6 +12,11 @@ urlpatterns = [ views.SubscriptionCreateOrUpdateView.as_view(), name="subscription_add", ), + path( + "subscription/edit//", + views.SubscriptionCreateOrUpdateView.as_view(), + name="subscription_edit", + ), path("subscription//", views.SubscriptionDetailView.as_view(), name="subscription_detail"), path( "subscription/delete/", diff --git a/manage_subscriptions/views.py b/manage_subscriptions/views.py index ab5f782..1c5e00b 100644 --- a/manage_subscriptions/views.py +++ b/manage_subscriptions/views.py @@ -126,7 +126,7 @@ class SubscriptionView(LoginRequiredMixin, generic.ListView): queryset = ( super() .get_queryset() - .filter(deleted=False, active=True) + .filter(deleted=False) .prefetch_related("principal_types") ) return queryset.order_by("-created_on") @@ -178,8 +178,8 @@ class SubscriptionDeleteView(LoginRequiredMixin, generic.View): class PrincipalSubscriptionCreateOrUpdateView(LoginRequiredMixin, generic.View): # Set the page_name and resource - page_name = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS - resource = resource_action.RESOURCE_MANAGE_SUBSCRIPTIONS + page_name = resource_action.RESOURCE_PRINCIPAL_SUBSCRIPTIONS + resource = resource_action.RESOURCE_PRINCIPAL_SUBSCRIPTIONS # Initialize the action as ACTION_CREATE (can change based on logic) action = resource_action.ACTION_CREATE # Default action diff --git a/manage_wallets/api/views.py b/manage_wallets/api/views.py index f477a22..931bbcb 100644 --- a/manage_wallets/api/views.py +++ b/manage_wallets/api/views.py @@ -146,7 +146,7 @@ class TransactionView(APIView): models.TransactionStatus.SUCCESS, models.TransactionStatus.FAIL, ], - ) + ).order_by("-created_on") serializer = serializers.TransactionSerializer(queryset, many=True) response = { diff --git a/static/src/assets/css/payment/style.css b/static/src/assets/css/payment/style.css index b58d7c4..5fb6746 100644 --- a/static/src/assets/css/payment/style.css +++ b/static/src/assets/css/payment/style.css @@ -331,6 +331,7 @@ header nav ul li a:hover:after { font-weight: 600; padding: 10px 40px; border-radius: 5px; + width: 100%; } @@ -601,8 +602,6 @@ div#accordionExample { .gold-text { color: rgb(209 170 88); - margin: 0; - padding: 12px; } .bg_color { @@ -618,9 +617,35 @@ div#accordionExample { padding: 35px; border-radius: 6px; background-color: #00000080; - text-align: center; + text-align: start; + margin-bottom: 15px; } +/* New css */ +.feat-card .para { + font-size: 18px; + } + .currency{ + font-size: 36px; + color: #fff; + margin-right:5px + } + .interval{ + font-size: 14px; + color: gray; + } + + .actual-price { + text-decoration: line-through; + color: #ccc; +} + .offer-price { + font-size: 36px; + /* font-weight: bold; */ + color: #fff; +} + + /* mediascreen */ @media (max-width: 1199px) { .big-heading br { @@ -829,6 +854,7 @@ div#accordionExample { .common-btn { /* remove default button margins */ margin: 0; + width: 100%; } .feat-card input.form-control.coupon-code-input { border-radius: 5px; diff --git a/templates/manage_subscriptions/principal_subscription_add.html b/templates/manage_subscriptions/principal_subscription_add.html index 8da8df6..d524ae8 100644 --- a/templates/manage_subscriptions/principal_subscription_add.html +++ b/templates/manage_subscriptions/principal_subscription_add.html @@ -16,7 +16,7 @@
-

{{operation}} {{page_name}}

+

{{operation}} Customer Subscription

-

Plan: {{ subscription.plan.title }}

-

Price ID: {{ subscription.price_id|default:"Not a Stripe Subscription" }}

-

Stripe Product: {{ subscription.stripe_product|default:"None" }}

+

Customer type : + {% for principal_type in subscription.principal_types.all %} + {{ principal_type.name }} + {% endfor %} +

+

Plan: {{subscription.interval_count}} {{ subscription.interval }}

+

Stripe Price ID: {{ subscription.price_id|default:"Not a Stripe Subscription" }}

+

Stripe Product ID: {{ subscription.product_id|default:"None" }}

Amount: ${{ subscription.amount }}

High Amount: ${{ subscription.high_amount }}

Referral Percentage: {{ subscription.referral_percentage }}%

@@ -49,26 +54,11 @@

Short Description: {{ subscription.short_description|default:"Not Provided" }}

Long Description:

-

{{ subscription.long_description|default:"Not Provided" }}

+

{{ subscription.long_description.html|default:"Not Provided"|safe }}

- -
-
-
-
Principal Types
-
-
-
    - {% for principal_type in subscription.principal_types.all %} -
  • {{ principal_type.name }}
  • - {% endfor %} -
-
-
-
diff --git a/templates/manage_subscriptions/subscription_list.html b/templates/manage_subscriptions/subscription_list.html index ee40a60..c2a59d7 100644 --- a/templates/manage_subscriptions/subscription_list.html +++ b/templates/manage_subscriptions/subscription_list.html @@ -84,6 +84,26 @@ diff --git a/templates/stripe_html/index.html b/templates/stripe_html/index.html index 61bc410..0adc577 100644 --- a/templates/stripe_html/index.html +++ b/templates/stripe_html/index.html @@ -98,49 +98,45 @@ {% for subscription in subscriptions %}
-
- -
{{ subscription.title }} -
- {% if subscription.image %} - {{ subscription.title }} - {% endif %} -
-
- {% if subscription.short_description %} -

{{ subscription.short_description }}

- {% endif %} - {% if subscription.long_description %} -

{{ subscription.long_description|truncatewords:20 }}

- {% endif %} -
Subscription Amount
- {% if subscription.high_amount and subscription.high_amount > subscription.amount %} -

£ {{ subscription.high_amount }} £ {{ subscription.amount }} +

{{subscription.title}}

+ {% if subscription.high_amount and subscription.high_amount > subscription.amount %} +

+ £{{subscription.high_amount}} + £{{subscription.amount}} + {% if subscription.interval_count == 1 %} + / {{ subscription.interval| capfirst }} + {% else %} + / {{ subscription.interval_count }} {{ subscription.interval | capfirst }}s + {% endif %}

- {% else %} -

£ {{ subscription.amount }}

- {% endif %} -

Subscription Cycle: {{subscription.interval_count}} {{ subscription.interval | capfirst }}

- -
-
- {% comment %} {% endcomment %} - -
- - -
- - - - -
+ {% else %} +

+ £{{subscription.amount}} + {% if interval_count == 1 %} + / {{ subscription.interval }} + {% else %} + / {{ subscription.interval_count }} {{ subscription.interval | capfirst }}s + {% endif %} +

+ {% endif %} + {% if subscription.short_description %} +

{{ subscription.short_description }}

+ {% endif %} + {% if subscription.long_description %} +

{{ subscription.long_description.html|safe }}

+ {% endif %} +
+ + +
+ +
{% endfor %} - + @@ -551,8 +547,6 @@ return result.json(); }) .then((data) => { - console.log("data: ", data); - console.log("data.sessionId: ", data.sessionId); // Redirects to Stripe Checkout return stripe.redirectToCheckout({ sessionId: data.sessionId