Skip to content

Commit b2e7a97

Browse files
Don't synchronize new monthly subscriptions to start of month.
1 parent 5798750 commit b2e7a97

3 files changed

Lines changed: 25 additions & 15 deletions

File tree

perma_web/perma/models/base.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
pp_date_from_post,
1919
prep_for_perma_payments,
2020
process_perma_payments_transmission,
21+
today_next_month,
2122
today_next_year,
2223
)
2324

@@ -315,13 +316,20 @@ def annotate_tier(self, tier, current_subscription, now, next_month, next_year):
315316
'''
316317

317318
# Calculate when, after today, the customer will/should next be charged.
318-
# Calculate what fraction of the current subscription period remains,
319-
# to use when determining how much to charge them today.
320319
if tier['period'] == 'monthly':
321-
# monthly subscriptions are paid on the first of the next month
322-
next_payment = next_month
323-
days_in_month = calendar.monthrange(now.year, now.month)[1]
324-
prorated_ratio = Decimal((next_payment - now).days / days_in_month)
320+
# montly subscriptions are now paid on the anniversary of their creation.
321+
# historically, monthly subscriptions were all paid on the first of the month.
322+
if current_subscription:
323+
# n.b. these values are nonsensical if the current subscription is not active.
324+
# there is no good answer in that case.... so updating a non-active
325+
# subscription is forbidden below. continuing to calculate the nonsensical values
326+
# for these fields since.... that at least avoids type errors.
327+
next_payment = current_subscription['paid_through']
328+
days_in_month = calendar.monthrange(now.year, now.month)[1]
329+
prorated_ratio = Decimal((next_payment - now).days / days_in_month)
330+
else:
331+
next_payment = next_month
332+
prorated_ratio = Decimal(1)
325333
elif tier['period'] == 'annually':
326334
# annual subscriptions are paid on the anniversary of their creation
327335
if current_subscription:
@@ -409,7 +417,7 @@ def annotate_tier(self, tier, current_subscription, now, next_month, next_year):
409417

410418
def get_subscription_info(self, now):
411419
timestamp = now.timestamp()
412-
next_month = first_day_of_next_month(now)
420+
next_month = today_next_month(now)
413421
next_year = today_next_year(now)
414422
subscription = self.get_subscription()
415423

perma_web/perma/tests/test_models.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
most_active_org_in_time_period,
1818
subscription_is_active
1919
)
20-
from perma.utils import pp_date_from_post, tz_datetime, first_day_of_next_month, today_next_year, years_ago_today
20+
from perma.utils import pp_date_from_post, tz_datetime, first_day_of_next_month, today_next_month, today_next_year, years_ago_today
2121

2222
from conftest import GENESIS
2323
import pytest
@@ -527,7 +527,7 @@ def test_annotate_tier_monthly_no_subscription_first_of_month(customers):
527527

528528
def test_annotate_tier_monthly_no_subscription_mid_month(customers):
529529
now = GENESIS.replace(day=16)
530-
next_month = first_day_of_next_month(now)
530+
next_month = today_next_month(now)
531531
next_year = today_next_year(now)
532532
subscription = None
533533
for customer in customers:
@@ -539,8 +539,8 @@ def test_annotate_tier_monthly_no_subscription_mid_month(customers):
539539
customer.annotate_tier(tier, subscription, now, next_month, next_year)
540540
assert tier['type'] == 'upgrade'
541541
assert tier['link_limit_effective_timestamp'] == now.timestamp()
542-
assert Decimal(tier['todays_charge']) == (customer.base_rate * tier['rate_ratio'] / 31 * 16).quantize(Decimal('.01'))
543-
assert tier['recurring_amount'] != tier['todays_charge']
542+
assert Decimal(tier['todays_charge']) == (customer.base_rate * tier['rate_ratio']).quantize(Decimal('.01'))
543+
assert tier['recurring_amount'] == tier['todays_charge']
544544
assert tier['next_payment'] == next_month
545545

546546

@@ -549,7 +549,7 @@ def test_annotate_tier_monthly_no_subscription_last_of_month(is_active, customer
549549
is_active.return_value = True
550550

551551
now = GENESIS.replace(day=31)
552-
next_month = first_day_of_next_month(now)
552+
next_month = today_next_month(now)
553553
next_year = today_next_year(now)
554554
subscription = None
555555
for customer in customers:
@@ -561,8 +561,8 @@ def test_annotate_tier_monthly_no_subscription_last_of_month(is_active, customer
561561
customer.annotate_tier(tier, subscription, now, next_month, next_year)
562562
assert tier['type'] == 'upgrade'
563563
assert tier['link_limit_effective_timestamp'] == now.timestamp()
564-
assert Decimal(tier['todays_charge']) == (customer.base_rate * tier['rate_ratio'] / 31).quantize(Decimal('.01'))
565-
assert tier['recurring_amount'] != tier['todays_charge']
564+
assert Decimal(tier['todays_charge']) == (customer.base_rate * tier['rate_ratio']).quantize(Decimal('.01'))
565+
assert tier['recurring_amount'] == tier['todays_charge']
566566
assert tier['next_payment'] == next_month
567567

568568

perma_web/perma/utils.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,12 +293,14 @@ def get_client_ip(request):
293293
def tz_datetime(*args, **kwargs):
294294
return timezone.make_aware(datetime(*args, **kwargs))
295295

296-
297296
def first_day_of_next_month(now):
298297
# use first of month instead of today to avoid issues w/ variable length months
299298
first_of_month = now.replace(day=1)
300299
return first_of_month + relativedelta(months=1)
301300

301+
def today_next_month(now):
302+
# relativedelta handles leap years: 2/29 -> 2/28
303+
return now + relativedelta(months=1)
302304

303305
def today_next_year(now):
304306
# relativedelta handles leap years: 2/29 -> 2/28

0 commit comments

Comments
 (0)