Skip to content

Commit ccf9cb6

Browse files
committed
applies coupon after advance discount (i.e. inverse order)
1 parent 1b98c97 commit ccf9cb6

File tree

3 files changed

+77
-21
lines changed

3 files changed

+77
-21
lines changed

saas/mixins.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2025, DjaoDjin inc.
1+
# Copyright (c) 2026, DjaoDjin inc.
22
# All rights reserved.
33
#
44
# Redistribution and use in source and binary forms, with or without
@@ -179,8 +179,7 @@ def get_cart_options(subscription, created_at=None,
179179
coupon_discount_amount = 0
180180
if coupon:
181181
coupon_discount_amount = coupon.get_discount_amount(
182-
prorated_amount=prorated_amount,
183-
period_amount=plan.period_amount)
182+
amount=full_amount, period_amount=plan.period_amount)
184183
discount_by_types[coupon.discount_type] = coupon.discount_value
185184
amount = max(full_amount - coupon_discount_amount, 0)
186185

@@ -220,24 +219,24 @@ def get_cart_options(subscription, created_at=None,
220219
prorated_amount + advance_discount.full_periods_amount)
221220
advance_discount_amount = advance_discount.get_discount_amount(
222221
prorated_amount=prorated_amount)
222+
amount = full_amount - advance_discount_amount
223223
discount_by_types = {}
224224
discount_by_types[advance_discount.discount_type] = \
225225
advance_discount.discount_value
226-
coupon_discount_amount = 0
227226
if coupon:
228227
coupon_discount_amount = coupon.get_discount_amount(
229-
prorated_amount=prorated_amount,
230-
period_amount=plan.period_amount,
231-
advance_amount=(advance_discount.full_periods_amount
232-
- plan.period_amount))
228+
amount=amount, period_amount=plan.period_amount)
229+
amount = max(amount - coupon_discount_amount, 0)
233230
if coupon.discount_type in discount_by_types:
234-
discount_by_types[coupon.discount_type] += \
235-
coupon.discount_value
231+
if coupon.discount_type == Coupon.PERCENTAGE:
232+
discount_by_types[coupon.discount_type] = (
233+
full_amount - amount) * 10000 // full_amount
234+
else:
235+
discount_by_types[coupon.discount_type] += \
236+
coupon.discount_value
236237
else:
237238
discount_by_types[coupon.discount_type] = \
238239
coupon.discount_value
239-
amount = (full_amount - advance_discount_amount
240-
- coupon_discount_amount)
241240
if amount <= 0:
242241
continue # never allow to be completely free here.
243242
nb_periods = (

saas/models.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3130,7 +3130,7 @@ def setup_price(self):
31303130

31313131
def get_discounted_period_amount(self, coupon):
31323132
return self.period_amount - coupon.get_discount_amount(
3133-
period_amount=self.period_amount)
3133+
amount=self.period_amount, period_amount=self.period_amount)
31343134

31353135
def get_discounted_period_price(self, coupon):
31363136
return Price(self.get_discounted_period_amount(coupon), self.unit)
@@ -3390,12 +3390,14 @@ def __str__(self):
33903390
def provider(self):
33913391
return self.organization
33923392

3393-
def get_discount_amount(self, prorated_amount=0, period_amount=0,
3394-
advance_amount=0, rounding=Plan.PRICE_ROUND_NONE):
3393+
def get_discount_amount(self, amount=0, period_amount=0,
3394+
rounding=Plan.PRICE_ROUND_NONE):
33953395
if self.discount_type == self.CURRENCY:
33963396
return self.discount_value
3397+
if self.discount_type == self.PERIOD:
3398+
return self.discount_value * period_amount
33973399
# discount percentage
3398-
full_amount = prorated_amount + period_amount + advance_amount
3400+
full_amount = amount
33993401
discount_percent = self.discount_value
34003402
discount_amount = (full_amount * discount_percent) // 10000
34013403
if rounding == Plan.PRICE_ROUND_WHOLE:

testsite/fixtures/170-billing.json

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,13 +106,27 @@
106106
},
107107
{
108108
"fields":{
109-
"code": "PROMO50",
110109
"created_at":"2026-01-01T00:00:00+00:00",
111110
"discount_type": 1,
112111
"discount_value": 5000,
113-
"organization": 170
114-
},
115-
"model" : "saas.Coupon", "pk": 170
112+
"organization": 170,
113+
"code": "PROMO50"}, "model" : "saas.Coupon", "pk": 170
114+
},
115+
{
116+
"fields":{
117+
"created_at":"2026-01-01T00:00:00+00:00",
118+
"discount_type": 2,
119+
"discount_value": 5000,
120+
"organization": 170,
121+
"code": "DOLLAR50"}, "model" : "saas.Coupon", "pk": 171
122+
},
123+
{
124+
"fields":{
125+
"created_at":"2026-01-01T00:00:00+00:00",
126+
"discount_type": 3,
127+
"discount_value": 1,
128+
"organization": 170,
129+
"code": "MONTH50"}, "model" : "saas.Coupon", "pk": 172
116130
},
117131
{
118132
"fields": {
@@ -351,4 +365,45 @@
351365
"user": 175
352366
},
353367
"model": "saas.Role", "pk": 175
354-
}]
368+
},
369+
370+
{
371+
"fields": {
372+
"username": "xia176",
373+
"date_joined": "2026-01-01T00:00:00+00:00",
374+
"last_login": "2026-01-01T00:00:00+00:00",
375+
"email": "xia+176@localhost.localdomain",
376+
"first_name": "Xia176",
377+
"last_name": "Subscriber",
378+
"password": "pbkdf2_sha256$10000$z0MBiWn0Rlem$iZdC6uHomlE07qGK/TqfcfxNzKJtFp03c0JILF1frRc=",
379+
"is_active": true,
380+
"username": "xia176"}, "model": "auth.User", "pk": 176
381+
},
382+
{
383+
"fields": {
384+
"last_signed": "2026-01-01T00:00:00+00:00",
385+
"agreement": 1,
386+
"user": 176}, "model": "saas.signature", "pk": 176
387+
},
388+
{
389+
"fields": {
390+
"full_name": "Xia176 Subscriber",
391+
"created_at": "2026-01-01T00:00:00+00:00",
392+
"email": "xia+176@localhost.localdomain",
393+
"phone": "555-555-5555",
394+
"street_address": "1 ABC loop",
395+
"locality": "San Francisco",
396+
"region": "CA",
397+
"postal_code": "94102",
398+
"country": "US",
399+
"processor": 1,
400+
"is_active": 1,
401+
"slug": "xia176"}, "model": "saas.Organization", "pk": 176
402+
},
403+
{
404+
"fields":{
405+
"created_at": "2026-01-01T00:00:00+00:00",
406+
"role_description": 1,
407+
"organization": 176, "user": 176}, "model": "saas.Role", "pk": 176
408+
}
409+
]

0 commit comments

Comments
 (0)