126 lines
3.9 KiB
Python
126 lines
3.9 KiB
Python
|
"""
|
||
|
Test utilities and fixtures for pricing tests
|
||
|
"""
|
||
|
|
||
|
from decimal import Decimal
|
||
|
from django.test import TestCase
|
||
|
|
||
|
from ..models.base import Currency, Term, Unit
|
||
|
from ..models.providers import CloudProvider
|
||
|
from ..models.services import Service, Category
|
||
|
from ..models.pricing import (
|
||
|
ProgressiveDiscountModel,
|
||
|
DiscountTier,
|
||
|
VSHNAppCatPrice,
|
||
|
VSHNAppCatBaseFee,
|
||
|
VSHNAppCatUnitRate,
|
||
|
)
|
||
|
|
||
|
|
||
|
class PricingTestMixin:
|
||
|
"""Mixin providing common setup for pricing tests"""
|
||
|
|
||
|
def create_test_cloud_provider(self, name="Test Provider"):
|
||
|
"""Create a test cloud provider"""
|
||
|
return CloudProvider.objects.create(
|
||
|
name=name,
|
||
|
slug=name.lower().replace(" ", "-"),
|
||
|
description=f"{name} description",
|
||
|
website=f"https://{name.lower().replace(' ', '')}.com",
|
||
|
)
|
||
|
|
||
|
def create_test_service(self, name="Test Service"):
|
||
|
"""Create a test service"""
|
||
|
return Service.objects.create(
|
||
|
name=name,
|
||
|
slug=name.lower().replace(" ", "-"),
|
||
|
description=f"{name} description",
|
||
|
features=f"{name} features",
|
||
|
)
|
||
|
|
||
|
def create_test_discount_model(self, name="Test Discount", tiers=None):
|
||
|
"""Create a test discount model with optional tiers"""
|
||
|
discount_model = ProgressiveDiscountModel.objects.create(
|
||
|
name=name, description=f"{name} description", active=True
|
||
|
)
|
||
|
|
||
|
if tiers is None:
|
||
|
# Default tiers: 0-9 (0%), 10-49 (10%), 50+ (20%)
|
||
|
tiers = [
|
||
|
(0, 10, Decimal("0.00")),
|
||
|
(10, 50, Decimal("10.00")),
|
||
|
(50, None, Decimal("20.00")),
|
||
|
]
|
||
|
|
||
|
for min_units, max_units, discount_percent in tiers:
|
||
|
DiscountTier.objects.create(
|
||
|
discount_model=discount_model,
|
||
|
min_units=min_units,
|
||
|
max_units=max_units,
|
||
|
discount_percent=discount_percent,
|
||
|
)
|
||
|
|
||
|
return discount_model
|
||
|
|
||
|
def create_test_appcat_price(
|
||
|
self,
|
||
|
service,
|
||
|
discount_model=None,
|
||
|
base_fee=Decimal("50.00"),
|
||
|
unit_rate=Decimal("5.0000"),
|
||
|
):
|
||
|
"""Create a test AppCat price configuration"""
|
||
|
appcat_price = VSHNAppCatPrice.objects.create(
|
||
|
service=service,
|
||
|
variable_unit=VSHNAppCatPrice.VariableUnit.RAM,
|
||
|
term=Term.MTH,
|
||
|
discount_model=discount_model,
|
||
|
)
|
||
|
|
||
|
# Create base fee
|
||
|
VSHNAppCatBaseFee.objects.create(
|
||
|
vshn_appcat_price_config=appcat_price,
|
||
|
currency=Currency.CHF,
|
||
|
amount=base_fee,
|
||
|
)
|
||
|
|
||
|
# Create unit rate
|
||
|
VSHNAppCatUnitRate.objects.create(
|
||
|
vshn_appcat_price_config=appcat_price,
|
||
|
currency=Currency.CHF,
|
||
|
service_level=VSHNAppCatPrice.ServiceLevel.GUARANTEED,
|
||
|
amount=unit_rate,
|
||
|
)
|
||
|
|
||
|
return appcat_price
|
||
|
|
||
|
|
||
|
def calculate_expected_discounted_price(base_rate, units, discount_tiers):
|
||
|
"""Helper function to calculate expected discounted price manually"""
|
||
|
final_price = Decimal("0")
|
||
|
remaining_units = units
|
||
|
|
||
|
# Sort tiers by min_units
|
||
|
sorted_tiers = sorted(discount_tiers, key=lambda x: x[0])
|
||
|
|
||
|
for min_units, max_units, discount_percent in sorted_tiers:
|
||
|
if remaining_units <= 0:
|
||
|
break
|
||
|
|
||
|
# Skip if we haven't reached this tier yet
|
||
|
if units < min_units:
|
||
|
continue
|
||
|
|
||
|
# Calculate units in this tier
|
||
|
tier_max = max_units if max_units else float("inf")
|
||
|
units_start = max(0, units - remaining_units)
|
||
|
units_end = min(units, tier_max if max_units else units)
|
||
|
tier_units = max(0, units_end - max(units_start, min_units - 1))
|
||
|
|
||
|
if tier_units > 0:
|
||
|
discounted_rate = base_rate * (1 - discount_percent / 100)
|
||
|
final_price += discounted_rate * tier_units
|
||
|
remaining_units -= tier_units
|
||
|
|
||
|
return final_price
|