correct discount model

This commit is contained in:
Tobias Brunner 2025-05-23 16:37:03 +02:00
parent f5f4ec8ac9
commit 3896636f9b
No known key found for this signature in database
6 changed files with 201 additions and 55 deletions

View file

@ -131,54 +131,98 @@ class ProgressiveDiscountModel(models.Model):
"""Calculate price using progressive percentage discounts."""
final_price = 0
remaining_units = units
processed_units = 0
# Get all tiers sorted by threshold
discount_tiers = self.tiers.all().order_by("threshold")
discount_tiers = self.tiers.all().order_by("min_units")
for i, tier in enumerate(discount_tiers):
# Calculate how many units fall into this tier
if i < discount_tiers.count() - 1:
next_threshold = discount_tiers[i + 1].threshold
tier_units = min(remaining_units, next_threshold - processed_units)
else:
# Last tier handles all remaining units
tier_units = remaining_units
# Calculate discounted rate for this tier
discounted_rate = base_rate * (1 - tier.discount_percent / 100)
# Add the price for units in this tier
final_price += discounted_rate * tier_units
# Update tracking variables
remaining_units -= tier_units
processed_units += tier_units
# Exit if all units have been processed
for tier in discount_tiers:
if remaining_units <= 0:
break
# Determine how many units fall into this tier
tier_min = tier.min_units
tier_max = tier.max_units if tier.max_units else float("inf")
# Skip if we haven't reached this tier yet
if units < tier_min:
continue
# Calculate units in this tier
units_start = max(0, units - remaining_units)
units_end = min(units, tier_max if tier.max_units else units)
tier_units = max(0, units_end - max(units_start, tier_min - 1))
if tier_units > 0:
discounted_rate = base_rate * (1 - tier.discount_percent / 100)
final_price += discounted_rate * tier_units
remaining_units -= tier_units
return final_price
def get_discount_breakdown(self, base_rate, units):
"""Get detailed breakdown of discount calculation."""
breakdown = []
remaining_units = units
discount_tiers = self.tiers.all().order_by("min_units")
for tier in discount_tiers:
if remaining_units <= 0:
break
tier_min = tier.min_units
tier_max = tier.max_units if tier.max_units else float("inf")
if units < tier_min:
continue
units_start = max(0, units - remaining_units)
units_end = min(units, tier_max if tier.max_units else units)
tier_units = max(0, units_end - max(units_start, tier_min - 1))
if tier_units > 0:
discounted_rate = base_rate * (1 - tier.discount_percent / 100)
tier_total = discounted_rate * tier_units
breakdown.append(
{
"tier_range": f"{tier_min}-{tier_max-1 if tier.max_units else ''}",
"units": tier_units,
"discount_percent": tier.discount_percent,
"rate": discounted_rate,
"subtotal": tier_total,
}
)
remaining_units -= tier_units
return breakdown
class DiscountTier(models.Model):
discount_model = models.ForeignKey(
ProgressiveDiscountModel, on_delete=models.CASCADE, related_name="tiers"
)
threshold = models.PositiveIntegerField(
help_text="Starting unit count for this tier"
min_units = models.PositiveIntegerField(
help_text="Minimum unit count for this tier (inclusive)", default=0
)
max_units = models.PositiveIntegerField(
null=True,
blank=True,
help_text="Maximum unit count for this tier (exclusive). Leave blank for unlimited.",
)
discount_percent = models.DecimalField(
max_digits=5, decimal_places=2, help_text="Percentage discount applied (0-100)"
)
class Meta:
ordering = ["threshold"]
unique_together = ["discount_model", "threshold"]
ordering = ["min_units"]
unique_together = ["discount_model", "min_units"]
def __str__(self):
return f"{self.discount_model.name}: {self.threshold}+ units → {self.discount_percent}% discount"
if self.max_units:
return f"{self.discount_model.name}: {self.min_units}-{self.max_units-1} units → {self.discount_percent}% discount"
else:
return f"{self.discount_model.name}: {self.min_units}+ units → {self.discount_percent}% discount"
class VSHNAppCatBaseFee(models.Model):