Files
goodtimes/manage_subscriptions/models.py
2024-08-14 12:53:44 +05:30

187 lines
6.5 KiB
Python

from datetime import timedelta
from django.utils import timezone
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 _
# Create your models here.
class Plan(BaseModel):
title = models.CharField(max_length=255)
days = models.PositiveIntegerField()
class Meta:
db_table = "plan"
def __str__(self):
return self.title
class StripeProduct(BaseModel):
title = models.CharField(max_length=255)
product_id = models.CharField(max_length=255, blank=True, null=True)
description = models.TextField(blank=True, null=True)
metadata = models.JSONField(blank=True, null=True)
image_url = models.URLField(blank=True, null=True)
default_price_id = models.CharField(max_length=255, blank=True, null=True)
class Meta:
db_table = "stripe_product"
def __str__(self):
return self.title
class Subscription(BaseModel):
title = models.CharField(max_length=255)
price_id = models.CharField(max_length=255, blank=True, null=True)
stripe_product = models.ForeignKey(
StripeProduct,
related_name="subscription_product",
on_delete=models.CASCADE,
null=True,
blank=True,
)
short_description = models.CharField(max_length=255, null=True, blank=True)
long_description = models.TextField(null=True, blank=True)
image = models.ImageField(upload_to="subscription_img", null=True, blank=True)
plan = models.ForeignKey(
Plan, related_name="subscription_plan", on_delete=models.CASCADE
)
high_amount = models.DecimalField(max_digits=14, decimal_places=2, default=0.00)
amount = models.DecimalField(max_digits=14, decimal_places=2, default=0.00)
principal_types = models.ManyToManyField(
IAmPrincipalType, related_name="principal_type_subscriptions", blank=True
)
referral_percentage = models.DecimalField(max_digits=5, decimal_places=2)
is_free = models.BooleanField(
default=False,
help_text="Indicates whether this subscription is free and only accessible by administrators, not visible to regular users.",
)
class Meta:
db_table = "subscription"
def __str__(self):
return self.title
def clean(self):
# Ensure amount is greater than 1
if self.amount <= 1:
raise ValidationError({"amount": "Amount must be greater than 1."})
# Ensure high_amount is greater than amount
if self.high_amount <= self.amount:
raise ValidationError(
{"high_amount": "High amount must be greater than amount."}
)
# Ensure stripe_product is compulsory present
# if not self.stripe_product:
# raise ValidationError(
# {"stripe_product": "Please select stripe product to create subscription."}
# )
def save(self, *args, **kwargs):
self.clean() # Call clean before saving to ensure validation
super().save(*args, **kwargs)
class SubscriptionStatus(models.TextChoices):
ACTIVE = "active", _("Active")
EXPIRED = "expired", _("Expired")
INACTIVE = "inactive", _("Inactive")
class PrincipalSubscription(BaseModel):
subscription = models.ForeignKey(
Subscription, related_name="subscription_reference", on_delete=models.CASCADE
)
principal = models.ForeignKey(
IAmPrincipal, related_name="principal_subscription", on_delete=models.CASCADE
)
is_paid = models.BooleanField(default=False)
auto_renew = models.BooleanField(default=False)
status = models.CharField(
max_length=255,
choices=SubscriptionStatus.choices,
default=SubscriptionStatus.ACTIVE,
)
start_date = models.DateField()
end_date = models.DateField()
order_id = models.CharField(max_length=255, null=True, blank=True)
cancelled = models.BooleanField(default=False)
cancelled_date_time = models.DateTimeField(null=True, blank=True)
grace_period_end_date = models.DateField(null=True, blank=True)
stripe_customer_id = models.CharField(max_length=255, null=True, blank=True)
stripe_subscription_id = models.CharField(max_length=255, null=True, blank=True)
comments = models.CharField(max_length=255, null=True, blank=True)
is_stripe_subscription = models.BooleanField(default=False)
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
)
coupon_code = 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}"
def generate_order_id(email):
return f"order_{str(timezone.localtime().timestamp())}{str(email)}"
def generate_grace_period_end_date(date):
return date + timedelta(days=15)
@classmethod
def has_principal_subscription(cls, principal):
return cls.get_active_princial_subscription(principal).exists()
@classmethod
def get_active_princial_subscription(cls, principal):
return cls.objects.filter(
principal=principal,
is_paid=True,
# cancelled=False,
deleted=False,
active=True,
# status=SubscriptionStatus.ACTIVE,
grace_period_end_date__gt=timezone.now().date(),
)
@classmethod
def get_principal_subscription(cls, principal):
return cls.objects.filter(
principal=principal,
is_paid=True,
# cancelled=False,
deleted=False,
active=True,
# status=SubscriptionStatus.ACTIVE,
).order_by("-grace_period_end_date").first()
class WebhookEvent(BaseModel):
event_id = models.CharField(max_length=255, unique=True, db_index=True)
event_type = models.CharField(max_length=255, null=True, blank=True)
received_at = models.DateTimeField(auto_now_add=True)
processed_at = models.DateTimeField(null=True, blank=True)
status = models.CharField(
max_length=20, default="received"
) # e.g., 'received', 'processed', 'failed'
error_message = models.TextField(null=True, blank=True)
event_payload = models.JSONField(
null=True, blank=True
) # Optional: Store the payload for debugging.
def __str__(self):
return f"Webhook Event {self.event_id} - Status: {self.status}"
class Meta:
db_table = "webhook_event"