introduce service plans
This commit is contained in:
parent
f69f7fb755
commit
70f4a02db9
11 changed files with 362 additions and 20 deletions
|
@ -12,6 +12,63 @@ def validate_image_size(value):
|
|||
raise ValidationError("Maximum file size is 1MB")
|
||||
|
||||
|
||||
class Currency(models.Model):
|
||||
code = models.CharField(max_length=3, unique=True) # ISO 4217 currency code
|
||||
name = models.CharField(max_length=50)
|
||||
symbol = models.CharField(max_length=5)
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = "Currencies"
|
||||
ordering = ["code"]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.code} ({self.name})"
|
||||
|
||||
|
||||
class Plan(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
description = ProseEditorField()
|
||||
service = models.ForeignKey(
|
||||
"Service", on_delete=models.CASCADE, related_name="plans"
|
||||
)
|
||||
is_default = models.BooleanField(default=False)
|
||||
features = ProseEditorField(blank=True)
|
||||
order = models.IntegerField(default=0)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ["order", "name"]
|
||||
unique_together = [["service", "name"]]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.service.name} - {self.name}"
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.is_default:
|
||||
# Ensure only one default plan per service
|
||||
Plan.objects.filter(service=self.service).update(is_default=False)
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
# If this is the only plan, make it default
|
||||
if self.pk and self.service and self.service.plans.count() == 1:
|
||||
self.is_default = True
|
||||
|
||||
|
||||
class PlanPrice(models.Model):
|
||||
plan = models.ForeignKey(Plan, on_delete=models.CASCADE, related_name="prices")
|
||||
currency = models.ForeignKey(Currency, on_delete=models.PROTECT)
|
||||
price = models.DecimalField(max_digits=10, decimal_places=2)
|
||||
|
||||
class Meta:
|
||||
unique_together = [["plan", "currency"]]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.plan.name} - {self.currency.code} {self.price}"
|
||||
|
||||
|
||||
class Category(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
slug = models.SlugField(unique=True)
|
||||
|
@ -103,7 +160,6 @@ class Service(models.Model):
|
|||
ConsultingPartner, related_name="services", blank=True
|
||||
)
|
||||
categories = models.ManyToManyField(Category, related_name="services")
|
||||
price = models.DecimalField(max_digits=10, decimal_places=2)
|
||||
features = ProseEditorField()
|
||||
logo = models.ImageField(
|
||||
upload_to="service_logos/",
|
||||
|
@ -133,6 +189,9 @@ class Service(models.Model):
|
|||
def get_absolute_url(self):
|
||||
return reverse("services:service_detail", kwargs={"slug": self.slug})
|
||||
|
||||
def get_default_plan(self):
|
||||
return self.plans.filter(is_default=True).first() or self.plans.first()
|
||||
|
||||
|
||||
class Lead(models.Model):
|
||||
service = models.ForeignKey(Service, on_delete=models.CASCADE)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue