service level specific base fees

This commit is contained in:
Tobias Brunner 2025-06-20 15:39:26 +02:00
parent 150250bfb1
commit 033eea92cd
No known key found for this signature in database
9 changed files with 716 additions and 58 deletions

View file

@ -250,29 +250,6 @@ class DiscountTier(models.Model):
return f"{self.discount_model.name}: {self.min_units}+ units → {self.discount_percent}% discount"
class VSHNAppCatBaseFee(models.Model):
vshn_appcat_price_config = models.ForeignKey(
"VSHNAppCatPrice", 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 = "Service Base Fee"
unique_together = ("vshn_appcat_price_config", "currency")
ordering = ["currency"]
def __str__(self):
return f"{self.vshn_appcat_price_config.service.name} Base Fee - {self.amount} {self.currency}"
class VSHNAppCatPrice(models.Model):
class VariableUnit(models.TextChoices):
RAM = "RAM", "Memory (RAM)"
@ -325,12 +302,6 @@ class VSHNAppCatPrice(models.Model):
def __str__(self):
return f"{self.service.name} - {self.get_variable_unit_display()} based pricing"
def get_base_fee(self, currency_code: str):
try:
return self.base_fees.get(currency=currency_code).amount
except VSHNAppCatBaseFee.DoesNotExist:
return None
def get_unit_rate(self, currency_code: str, service_level: str):
try:
return self.unit_rates.get(
@ -346,7 +317,7 @@ class VSHNAppCatPrice(models.Model):
number_of_units: int,
addon_ids=None,
):
base_fee = self.get_base_fee(currency_code)
base_fee = self.get_base_fee(currency_code, service_level)
unit_rate = self.get_unit_rate(currency_code, service_level)
if base_fee is None or unit_rate is None:
@ -380,7 +351,7 @@ class VSHNAppCatPrice(models.Model):
for addon in addons:
addon_price = 0
if addon.addon_type == VSHNAppCatAddon.AddonType.BASE_FEE:
addon_price_value = addon.get_price(currency_code)
addon_price_value = addon.get_price(currency_code, service_level)
if addon_price_value:
addon_price = addon_price_value
elif addon.addon_type == VSHNAppCatAddon.AddonType.UNIT_RATE:
@ -408,6 +379,15 @@ class VSHNAppCatPrice(models.Model):
"addon_breakdown": addon_breakdown,
}
def get_base_fee(self, currency_code: str, service_level: str):
"""
Get the base fee for the given currency and service level.
"""
try:
return self.base_fees.get(currency=currency_code, service_level=service_level).amount
except VSHNAppCatBaseFee.DoesNotExist:
return None
class VSHNAppCatUnitRate(models.Model):
vshn_appcat_price_config = models.ForeignKey(
@ -477,10 +457,16 @@ class VSHNAppCatAddon(models.Model):
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"""
"""
Get the price for this addon in the specified currency and service level.
For base fee addons, service_level is required and used.
For unit rate addons, service_level is required and used.
"""
try:
if self.addon_type == self.AddonType.BASE_FEE:
return self.base_fees.get(currency=currency_code).amount
if not service_level:
raise ValueError("Service level is required for base fee addons")
return self.base_fees.get(currency=currency_code, service_level=service_level).amount
elif self.addon_type == self.AddonType.UNIT_RATE:
if not service_level:
raise ValueError("Service level is required for unit rate addons")
@ -495,8 +481,9 @@ class VSHNAppCatAddon(models.Model):
class VSHNAppCatAddonBaseFee(models.Model):
"""Base fee for an addon (fixed amount regardless of units)"""
"""
Base fee for an addon (fixed amount regardless of units), specified per currency and service level.
"""
addon = models.ForeignKey(
VSHNAppCatAddon, on_delete=models.CASCADE, related_name="base_fees"
)
@ -504,19 +491,27 @@ class VSHNAppCatAddonBaseFee(models.Model):
max_length=3,
choices=Currency.choices,
)
service_level = models.CharField(
max_length=2,
choices=VSHNAppCatPrice.ServiceLevel.choices,
default=VSHNAppCatPrice.ServiceLevel.BEST_EFFORT,
)
amount = models.DecimalField(
max_digits=10,
decimal_places=2,
help_text="Base fee in the specified currency, excl. VAT",
help_text="Base fee in the specified currency and service level, excl. VAT",
)
class Meta:
verbose_name = "Addon Base Fee"
unique_together = ("addon", "currency")
ordering = ["currency"]
unique_together = ("addon", "currency", "service_level")
ordering = ["currency", "service_level"]
def __str__(self):
return f"{self.addon.name} Base Fee - {self.amount} {self.currency}"
return f"{self.addon.name} Base Fee - {self.amount} {self.currency} ({self.get_service_level_display()})"
def get_service_level_display(self):
return dict(VSHNAppCatPrice.ServiceLevel.choices).get(self.service_level, self.service_level)
class VSHNAppCatAddonUnitRate(models.Model):
@ -548,6 +543,40 @@ class VSHNAppCatAddonUnitRate(models.Model):
return f"{self.addon.name} - {self.get_service_level_display()} Unit Rate - {self.amount} {self.currency}"
class VSHNAppCatBaseFee(models.Model):
"""
Base fee for a service, specified per currency and service level.
"""
vshn_appcat_price_config = models.ForeignKey(
"VSHNAppCatPrice", on_delete=models.CASCADE, related_name="base_fees"
)
currency = models.CharField(
max_length=3,
choices=Currency.choices,
)
service_level = models.CharField(
max_length=2,
choices=VSHNAppCatPrice.ServiceLevel.choices,
default=VSHNAppCatPrice.ServiceLevel.BEST_EFFORT,
)
amount = models.DecimalField(
max_digits=10,
decimal_places=2,
help_text="Base fee in the specified currency and service level, excl. VAT",
)
class Meta:
verbose_name = "Service Base Fee"
unique_together = ("vshn_appcat_price_config", "currency", "service_level")
ordering = ["currency", "service_level"]
def __str__(self):
return f"{self.vshn_appcat_price_config.service.name} Base Fee - {self.amount} {self.currency} ({self.get_service_level_display()})"
def get_service_level_display(self):
return dict(VSHNAppCatPrice.ServiceLevel.choices).get(self.service_level, self.service_level)
class ExternalPricePlans(models.Model):
plan_name = models.CharField()
description = models.CharField(max_length=200, blank=True, null=True)