add addons to services
This commit is contained in:
parent
b96b186875
commit
22e527bcd9
8 changed files with 1039 additions and 4 deletions
|
@ -1,4 +1,5 @@
|
|||
from django.db import models
|
||||
from django.db.models import Q
|
||||
|
||||
from .base import Currency, Term, Unit
|
||||
from .providers import CloudProvider
|
||||
|
@ -339,7 +340,11 @@ class VSHNAppCatPrice(models.Model):
|
|||
return None
|
||||
|
||||
def calculate_final_price(
|
||||
self, currency_code: str, service_level: str, number_of_units: int
|
||||
self,
|
||||
currency_code: str,
|
||||
service_level: str,
|
||||
number_of_units: int,
|
||||
addon_ids=None,
|
||||
):
|
||||
base_fee = self.get_base_fee(currency_code)
|
||||
unit_rate = self.get_unit_rate(currency_code, service_level)
|
||||
|
@ -359,7 +364,49 @@ class VSHNAppCatPrice(models.Model):
|
|||
else:
|
||||
total_price = base_fee + (unit_rate * number_of_units)
|
||||
|
||||
return total_price
|
||||
# Add prices for mandatory addons and selected addons
|
||||
addon_total = 0
|
||||
addon_breakdown = []
|
||||
|
||||
# Query all active addons related to this price config
|
||||
addons_query = self.addons.filter(active=True)
|
||||
|
||||
# Include mandatory addons and explicitly selected addons
|
||||
if addon_ids:
|
||||
addons = addons_query.filter(Q(mandatory=True) | Q(id__in=addon_ids))
|
||||
else:
|
||||
addons = addons_query.filter(mandatory=True)
|
||||
|
||||
for addon in addons:
|
||||
addon_price = 0
|
||||
if addon.addon_type == VSHNAppCatAddon.AddonType.BASE_FEE:
|
||||
addon_price_value = addon.get_price(currency_code)
|
||||
if addon_price_value:
|
||||
addon_price = addon_price_value
|
||||
elif addon.addon_type == VSHNAppCatAddon.AddonType.UNIT_RATE:
|
||||
addon_price_value = addon.get_price(currency_code, service_level)
|
||||
if addon_price_value:
|
||||
addon_price = addon_price_value * number_of_units
|
||||
|
||||
addon_total += addon_price
|
||||
addon_breakdown.append(
|
||||
{
|
||||
"id": addon.id,
|
||||
"name": addon.name,
|
||||
"description": addon.description,
|
||||
"commercial_description": addon.commercial_description,
|
||||
"mandatory": addon.mandatory,
|
||||
"price": addon_price,
|
||||
}
|
||||
)
|
||||
|
||||
total_price += addon_total
|
||||
|
||||
return {
|
||||
"total_price": total_price,
|
||||
"addon_total": addon_total,
|
||||
"addon_breakdown": addon_breakdown,
|
||||
}
|
||||
|
||||
|
||||
class VSHNAppCatUnitRate(models.Model):
|
||||
|
@ -389,6 +436,118 @@ class VSHNAppCatUnitRate(models.Model):
|
|||
return f"{self.vshn_appcat_price_config.service.name} - {self.get_service_level_display()} Unit Rate - {self.amount} {self.currency}"
|
||||
|
||||
|
||||
class VSHNAppCatAddon(models.Model):
|
||||
"""
|
||||
Addon pricing model for VSHNAppCatPrice. Can be added to a service price configuration
|
||||
to provide additional features or resources with their own pricing.
|
||||
"""
|
||||
|
||||
class AddonType(models.TextChoices):
|
||||
BASE_FEE = "BF", "Base Fee" # Fixed amount regardless of units
|
||||
UNIT_RATE = "UR", "Unit Rate" # Price per unit
|
||||
|
||||
vshn_appcat_price_config = models.ForeignKey(
|
||||
VSHNAppCatPrice, on_delete=models.CASCADE, related_name="addons"
|
||||
)
|
||||
name = models.CharField(max_length=100, help_text="Name of the addon")
|
||||
description = models.TextField(
|
||||
blank=True, help_text="Technical description of the addon"
|
||||
)
|
||||
commercial_description = models.TextField(
|
||||
blank=True, help_text="Commercial description displayed in the frontend"
|
||||
)
|
||||
addon_type = models.CharField(
|
||||
max_length=2,
|
||||
choices=AddonType.choices,
|
||||
help_text="Type of addon pricing (fixed fee or per-unit)",
|
||||
)
|
||||
mandatory = models.BooleanField(default=False, help_text="Is this addon mandatory?")
|
||||
active = models.BooleanField(
|
||||
default=True, help_text="Is this addon active and available for selection?"
|
||||
)
|
||||
order = models.IntegerField(default=0, help_text="Display order in the frontend")
|
||||
valid_from = models.DateTimeField(blank=True, null=True)
|
||||
valid_to = models.DateTimeField(blank=True, null=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Service Addon"
|
||||
ordering = ["order", "name"]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.vshn_appcat_price_config.service.name} - {self.name}"
|
||||
|
||||
def get_price(self, currency_code: str, service_level: str = None):
|
||||
"""Get the price for this addon in the specified currency and service level"""
|
||||
try:
|
||||
if self.addon_type == self.AddonType.BASE_FEE:
|
||||
return self.base_fees.get(currency=currency_code).amount
|
||||
elif self.addon_type == self.AddonType.UNIT_RATE:
|
||||
if not service_level:
|
||||
raise ValueError("Service level is required for unit rate addons")
|
||||
return self.unit_rates.get(
|
||||
currency=currency_code, service_level=service_level
|
||||
).amount
|
||||
except (
|
||||
VSHNAppCatAddonBaseFee.DoesNotExist,
|
||||
VSHNAppCatAddonUnitRate.DoesNotExist,
|
||||
):
|
||||
return None
|
||||
|
||||
|
||||
class VSHNAppCatAddonBaseFee(models.Model):
|
||||
"""Base fee for an addon (fixed amount regardless of units)"""
|
||||
|
||||
addon = models.ForeignKey(
|
||||
VSHNAppCatAddon, on_delete=models.CASCADE, related_name="base_fees"
|
||||
)
|
||||
currency = models.CharField(
|
||||
max_length=3,
|
||||
choices=Currency.choices,
|
||||
)
|
||||
amount = models.DecimalField(
|
||||
max_digits=10,
|
||||
decimal_places=2,
|
||||
help_text="Base fee in the specified currency, excl. VAT",
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Addon Base Fee"
|
||||
unique_together = ("addon", "currency")
|
||||
ordering = ["currency"]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.addon.name} Base Fee - {self.amount} {self.currency}"
|
||||
|
||||
|
||||
class VSHNAppCatAddonUnitRate(models.Model):
|
||||
"""Unit rate for an addon (price per unit)"""
|
||||
|
||||
addon = models.ForeignKey(
|
||||
VSHNAppCatAddon, on_delete=models.CASCADE, related_name="unit_rates"
|
||||
)
|
||||
currency = models.CharField(
|
||||
max_length=3,
|
||||
choices=Currency.choices,
|
||||
)
|
||||
service_level = models.CharField(
|
||||
max_length=2,
|
||||
choices=VSHNAppCatPrice.ServiceLevel.choices,
|
||||
)
|
||||
amount = models.DecimalField(
|
||||
max_digits=10,
|
||||
decimal_places=4,
|
||||
help_text="Price per unit in the specified currency and service level, excl. VAT",
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Addon Unit Rate"
|
||||
unique_together = ("addon", "currency", "service_level")
|
||||
ordering = ["currency", "service_level"]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.addon.name} - {self.get_service_level_display()} Unit Rate - {self.amount} {self.currency}"
|
||||
|
||||
|
||||
class ExternalPricePlans(models.Model):
|
||||
plan_name = models.CharField()
|
||||
description = models.CharField(max_length=200, blank=True, null=True)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue