Skip to content

Commit

Permalink
add support for RecurringUserPlan.renewal_triggered_by
Browse files Browse the repository at this point in the history
  • Loading branch information
radekholy24 committed Apr 5, 2024
1 parent 780fa39 commit 4573c29
Show file tree
Hide file tree
Showing 3 changed files with 258 additions and 15 deletions.
9 changes: 9 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
History
-------

1.3.0 (Unreleased)
++++++++++++++++++

* add support for `RecurringUserPlan.renewal_triggered_by`
* add `renewal_triggered_by` parameter to `Payment.set_renew_token`
* deprecate support for `RecurringUserPlan.renewal_triggered_by=None`; set an `AbstractRecurringUserPlan.RENEWAL_TRIGGERED_BY` instead
* deprecate `automatic_renewal` parameter of `Payment.set_renew_token`; migrate to `AbstractRecurringUserPlan.renewal_triggered_by` and use `renewal_triggered_by` parameter instead
* deprecate `None` value of `renewal_triggered_by` parameter of `Payment.set_renew_token`; set an `AbstractRecurringUserPlan.RENEWAL_TRIGGERED_BY` instead

1.2.2 (2023-12-20)
++++++++++++++++++

Expand Down
58 changes: 55 additions & 3 deletions plans_payments/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json
import logging
import warnings
from decimal import Decimal
from urllib.parse import urljoin

Expand All @@ -12,6 +13,7 @@
from payments.core import get_base_url
from payments.models import BasePayment
from payments.signals import status_changed
from plans.base.models import AbstractRecurringUserPlan
from plans.contrib import get_user_language, send_template_email
from plans.models import Order
from plans.signals import account_automatic_renewal
Expand Down Expand Up @@ -125,21 +127,57 @@ def set_renew_token(
card_expire_year=None,
card_expire_month=None,
card_masked_number=None,
automatic_renewal=True,
# TODO: automatic_renewal deprecated. Remove in the next major release.
automatic_renewal=None,
# TODO: renewal_triggered_by=None deprecated. Set to TASK in the next major release.
renewal_triggered_by=None,
):
"""
Store the recurring payments renew token for user of this payment
The renew token is string defined by the provider
Used by PayU provider for now
"""
set_plan_renewal_kwargs = {}

if automatic_renewal is None and renewal_triggered_by is None:
automatic_renewal = True
if automatic_renewal is not None:
warnings.warn(
"automatic_renewal is deprecated. "
"Migrate to AbstractRecurringUserPlan.renewal_triggered_by and use renewal_triggered_by instead.",
DeprecationWarning,
)
set_plan_renewal_kwargs["has_automatic_renewal"] = automatic_renewal

if renewal_triggered_by == "user":
set_plan_renewal_kwargs["renewal_triggered_by"] = (
AbstractRecurringUserPlan.RENEWAL_TRIGGERED_BY.USER
)
elif renewal_triggered_by == "task":
set_plan_renewal_kwargs["renewal_triggered_by"] = (
AbstractRecurringUserPlan.RENEWAL_TRIGGERED_BY.TASK
)
elif renewal_triggered_by == "other":
set_plan_renewal_kwargs["renewal_triggered_by"] = (
AbstractRecurringUserPlan.RENEWAL_TRIGGERED_BY.OTHER
)
elif renewal_triggered_by is None:
warnings.warn(
"renewal_triggered_by=None is deprecated. "
"Set an AbstractRecurringUserPlan.RENEWAL_TRIGGERED_BY instead.",
DeprecationWarning,
)
else:
raise ValueError(f"Invalid renewal_triggered_by: {renewal_triggered_by}")

self.order.user.userplan.set_plan_renewal(
order=self.order,
token=token,
payment_provider=self.variant,
card_expire_year=card_expire_year,
card_expire_month=card_expire_month,
card_masked_number=card_masked_number,
has_automatic_renewal=automatic_renewal,
**set_plan_renewal_kwargs,
)


Expand Down Expand Up @@ -169,9 +207,23 @@ def change_payment_status(sender, *args, **kwargs):
@receiver(account_automatic_renewal)
def renew_accounts(sender, user, *args, **kwargs):
userplan = user.userplan
userplan_recurring_renewal_triggered_by = userplan.recurring.renewal_triggered_by
# TODO: userplan.recurring.renewal_triggered_by=None deprecated. Remove in the next major release.
if userplan_recurring_renewal_triggered_by is None:
warnings.warn(
"Support for userplan.recurring.renewal_triggered_by=None is deprecated. "
"Set an AbstractRecurringUserPlan.RENEWAL_TRIGGERED_BY instead.",
DeprecationWarning,
)
userplan_recurring_renewal_triggered_by = (
AbstractRecurringUserPlan.RENEWAL_TRIGGERED_BY.TASK
if userplan.recurring.has_automatic_renewal
else AbstractRecurringUserPlan.RENEWAL_TRIGGERED_BY.USER
)
if (
userplan.recurring.payment_provider in settings.PAYMENT_VARIANTS
and userplan.recurring.has_automatic_renewal
and userplan_recurring_renewal_triggered_by
== AbstractRecurringUserPlan.RENEWAL_TRIGGERED_BY.TASK
):
order = userplan.recurring.create_renew_order()

Expand Down
206 changes: 194 additions & 12 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
Tests for `django-plans-payments` models module.
"""
import json
import warnings
from datetime import datetime
from decimal import Decimal

Expand All @@ -16,7 +17,7 @@
from freezegun import freeze_time
from model_bakery import baker
from payments import PaymentStatus
from plans.models import Invoice, Order
from plans.models import Invoice, Order, RecurringUserPlan

from plans_payments import models

Expand Down Expand Up @@ -187,22 +188,119 @@ def test_get_renew_token_verified(self):
)
self.assertEqual(p.get_renew_token(), "token")

def test_set_renew_token(self):
def test_set_renew_token_task(self):
user = baker.make("User")
p = models.Payment(order=baker.make("Order", user=user))
userplan = baker.make("UserPlan", user=user)
p.set_renew_token(
"token",
card_expire_year=2020,
card_expire_month=12,
card_masked_number="1234",
automatic_renewal=True,
with warnings.catch_warnings(record=True) as caught_warnings:
warnings.simplefilter("always")
p.set_renew_token(
"token",
card_expire_year=2020,
card_expire_month=12,
card_masked_number="1234",
renewal_triggered_by="task",
)
self.assertEqual(userplan.recurring.token, "token")
self.assertEqual(userplan.recurring.card_expire_year, 2020)
self.assertEqual(userplan.recurring.card_expire_month, 12)
self.assertEqual(userplan.recurring.card_masked_number, "1234")
self.assertEqual(
userplan.recurring.renewal_triggered_by,
RecurringUserPlan.RENEWAL_TRIGGERED_BY.TASK,
)
self.assertEqual(userplan.recurring.has_automatic_renewal, True)
self.assertFalse(caught_warnings)

def test_set_renew_token_other(self):
user = baker.make("User")
p = models.Payment(order=baker.make("Order", user=user))
userplan = baker.make("UserPlan", user=user)
with warnings.catch_warnings(record=True) as caught_warnings:
warnings.simplefilter("always")
p.set_renew_token(
"token",
card_expire_year=2020,
card_expire_month=12,
card_masked_number="1234",
renewal_triggered_by="other",
)
self.assertEqual(userplan.recurring.token, "token")
self.assertEqual(userplan.recurring.card_expire_year, 2020)
self.assertEqual(userplan.recurring.card_expire_month, 12)
self.assertEqual(userplan.recurring.card_masked_number, "1234")
self.assertEqual(
userplan.recurring.renewal_triggered_by,
RecurringUserPlan.RENEWAL_TRIGGERED_BY.OTHER,
)
self.assertEqual(userplan.recurring.has_automatic_renewal, True)
self.assertFalse(caught_warnings)

def test_set_renew_token_user(self):
user = baker.make("User")
p = models.Payment(order=baker.make("Order", user=user))
userplan = baker.make("UserPlan", user=user)
with warnings.catch_warnings(record=True) as caught_warnings:
warnings.simplefilter("always")
p.set_renew_token(
"token",
card_expire_year=2020,
card_expire_month=12,
card_masked_number="1234",
renewal_triggered_by="user",
)
self.assertEqual(userplan.recurring.token, "token")
self.assertEqual(userplan.recurring.card_expire_year, 2020)
self.assertEqual(userplan.recurring.card_expire_month, 12)
self.assertEqual(userplan.recurring.card_masked_number, "1234")
self.assertEqual(
userplan.recurring.renewal_triggered_by,
RecurringUserPlan.RENEWAL_TRIGGERED_BY.USER,
)
self.assertEqual(userplan.recurring.has_automatic_renewal, False)
self.assertFalse(caught_warnings)

def test_set_renew_token_none(self):
user = baker.make("User")
p = models.Payment(order=baker.make("Order", user=user))
userplan = baker.make("UserPlan", user=user)
with warnings.catch_warnings(record=True) as caught_warnings:
warnings.simplefilter("always")
p.set_renew_token(
"token",
card_expire_year=2020,
card_expire_month=12,
card_masked_number="1234",
automatic_renewal=True,
)
self.assertEqual(userplan.recurring.token, "token")
self.assertEqual(userplan.recurring.card_expire_year, 2020)
self.assertEqual(userplan.recurring.card_expire_month, 12)
self.assertEqual(userplan.recurring.card_masked_number, "1234")
self.assertIsNone(userplan.recurring.renewal_triggered_by)
self.assertEqual(userplan.recurring.has_automatic_renewal, True)
self.assertEqual(len(caught_warnings), 4)
self.assertTrue(issubclass(caught_warnings[0].category, DeprecationWarning))
self.assertEqual(
str(caught_warnings[0].message),
"automatic_renewal is deprecated. "
"Migrate to AbstractRecurringUserPlan.renewal_triggered_by and use renewal_triggered_by instead.",
)
self.assertTrue(issubclass(caught_warnings[1].category, DeprecationWarning))
self.assertEqual(
str(caught_warnings[1].message),
"renewal_triggered_by=None is deprecated. Set an AbstractRecurringUserPlan.RENEWAL_TRIGGERED_BY instead.",
)
self.assertTrue(issubclass(caught_warnings[2].category, DeprecationWarning))
self.assertEqual(
str(caught_warnings[2].message),
"has_automatic_renewal is deprecated. Use renewal_triggered_by instead.",
)
self.assertTrue(issubclass(caught_warnings[3].category, DeprecationWarning))
self.assertEqual(
str(caught_warnings[3].message),
"renewal_triggered_by=None is deprecated. Set an AbstractRecurringUserPlan.RENEWAL_TRIGGERED_BY instead.",
)

def test_change_payment_status(self):
p = models.Payment(order=baker.make("Order", status=Order.STATUS.NEW))
Expand Down Expand Up @@ -268,15 +366,18 @@ def test_renew_accounts_no_variant(self):
baker.make(
"RecurringUserPlan",
user_plan=userplan,
renewal_trigger=RecurringUserPlan.RENEWAL_TRIGGER.TASK,
renewal_triggered_by=RecurringUserPlan.RENEWAL_TRIGGERED_BY.TASK,
has_automatic_renewal=True,
)
models.renew_accounts("sender", user, p)
with warnings.catch_warnings(record=True) as caught_warnings:
warnings.simplefilter("always")
models.renew_accounts("sender", user, p)
self.assertEqual(p.autorenewed_payment, False)
self.assertFalse(Order.objects.exists())
self.assertFalse(models.Payment.objects.exclude(id=p.id).exists())
self.assertFalse(caught_warnings)

def test_renew_accounts(self):
def test_renew_accounts_none(self):
p = baker.make("Payment", variant="default", order__amount=12)
user = baker.make("User")
userplan = baker.make("UserPlan", user=user)
Expand All @@ -286,11 +387,51 @@ def test_renew_accounts(self):
"RecurringUserPlan",
user_plan=userplan,
payment_provider="default",
renewal_triggered_by=None,
has_automatic_renewal=True,
amount=14,
pricing=plan_pricing.pricing,
)
models.renew_accounts("sender", user, p)
with warnings.catch_warnings(record=True) as caught_warnings:
warnings.simplefilter("always")
models.renew_accounts("sender", user, p)
(order_renewed,) = Order.objects.exclude(id=p.order.id)
(payment_renewed,) = models.Payment.objects.exclude(id=p.id)
self.assertEqual(p.autorenewed_payment, False)
self.assertEqual(order_renewed.plan, plan_pricing.plan)
self.assertEqual(order_renewed.pricing, plan_pricing.pricing)
self.assertEqual(order_renewed.amount, Decimal(14))
self.assertEqual(order_renewed.user, user)
self.assertEqual(payment_renewed.order, order_renewed)
self.assertEqual(payment_renewed.variant, "default")
self.assertTrue(payment_renewed.autorenewed_payment)
self.assertEqual(len(caught_warnings), 1)
self.assertTrue(issubclass(caught_warnings[0].category, DeprecationWarning))
self.assertEqual(
str(caught_warnings[0].message),
"Support for userplan.recurring.renewal_triggered_by=None is deprecated. "
"Set an AbstractRecurringUserPlan.RENEWAL_TRIGGERED_BY instead.",
)

def test_renew_accounts(self):
p = baker.make("Payment", variant="default", order__amount=12)
user = baker.make("User")
userplan = baker.make("UserPlan", user=user)
plan_pricing = baker.make("PlanPricing", plan=userplan.plan, price=12)
baker.make("BillingInfo", user=user)
baker.make(
"RecurringUserPlan",
user_plan=userplan,
payment_provider="default",
renewal_triggered_by=RecurringUserPlan.RENEWAL_TRIGGERED_BY.TASK,
# False to make sure that renewal_triggered_by takes precedence.
has_automatic_renewal=False,
amount=14,
pricing=plan_pricing.pricing,
)
with warnings.catch_warnings(record=True) as caught_warnings:
warnings.simplefilter("always")
models.renew_accounts("sender", user, p)
(order_renewed,) = Order.objects.exclude(id=p.order.id)
(payment_renewed,) = models.Payment.objects.exclude(id=p.id)
self.assertEqual(p.autorenewed_payment, False)
Expand All @@ -301,6 +442,47 @@ def test_renew_accounts(self):
self.assertEqual(payment_renewed.order, order_renewed)
self.assertEqual(payment_renewed.variant, "default")
self.assertTrue(payment_renewed.autorenewed_payment)
self.assertFalse(caught_warnings)

def test_renew_accounts_user(self):
p = models.Payment(variant="default")
user = baker.make("User")
userplan = baker.make("UserPlan", user=user)
baker.make(
"RecurringUserPlan",
user_plan=userplan,
payment_provider="default",
renewal_triggered_by=RecurringUserPlan.RENEWAL_TRIGGERED_BY.USER,
# True to make sure that renewal_triggered_by takes precedence.
has_automatic_renewal=True,
)
with warnings.catch_warnings(record=True) as caught_warnings:
warnings.simplefilter("always")
models.renew_accounts("sender", user, p)
self.assertEqual(p.autorenewed_payment, False)
self.assertFalse(Order.objects.exists())
self.assertFalse(models.Payment.objects.exclude(id=p.id).exists())
self.assertFalse(caught_warnings)

def test_renew_accounts_other(self):
p = models.Payment(variant="default")
user = baker.make("User")
userplan = baker.make("UserPlan", user=user)
baker.make(
"RecurringUserPlan",
user_plan=userplan,
payment_provider="default",
renewal_triggered_by=RecurringUserPlan.RENEWAL_TRIGGERED_BY.OTHER,
# True to make sure that renewal_triggered_by takes precedence.
has_automatic_renewal=True,
)
with warnings.catch_warnings(record=True) as caught_warnings:
warnings.simplefilter("always")
models.renew_accounts("sender", user, p)
self.assertEqual(p.autorenewed_payment, False)
self.assertFalse(Order.objects.exists())
self.assertFalse(models.Payment.objects.exclude(id=p.id).exists())
self.assertFalse(caught_warnings)

def test_change_payment_status_called(self):
"""test that change_payment_status receiver is executed when Payment.change_status is called
Expand Down

0 comments on commit 4573c29

Please sign in to comment.