diff --git a/CHANGELOG b/CHANGELOG index 3af503c..a643065 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,12 @@ django-plans changelog 1.1.0 (unreleased) ------------------ * Add `AbstractOrder.return_order()` +* Add `AbstractRecurringUserPlan.renewal_trigger` +* Add `renewal_trigger` parameter to `AbstractUserPlan.set_plan_renewal` +* Deprecate `AbstractRecurringUserPlan.has_automatic_renewal`; use `AbstractRecurringUserPlan.renewal_trigger` instead +* Deprecate `AbstractRecurringUserPlan.renewal_trigger=None`; set an `AbstractRecurringUserPlan.RENEWAL_TRIGGER` instead +* Deprecate `has_automatic_renewal` parameter of `AbstractUserPlan.set_plan_renewal`; use `renewal_trigger` instead +* Deprecate `None` value of `renewal_trigger` parameter of `AbstractUserPlan.set_plan_renewal`; use an `AbstractRecurringUserPlan.RENEWAL_TRIGGER` instead 1.0.6 ----- diff --git a/demo/example/sample_plans/migrations/0001_initial.py b/demo/example/sample_plans/migrations/0001_initial.py index 1c558c8..a6cfb3b 100644 --- a/demo/example/sample_plans/migrations/0001_initial.py +++ b/demo/example/sample_plans/migrations/0001_initial.py @@ -334,12 +334,24 @@ class Migration(migrations.Migration): ), ), ("currency", models.CharField(max_length=3, verbose_name="currency")), + ( + "renewal_trigger", + models.IntegerField( + blank=True, + choices=[(1, "other"), (2, "user"), (3, "task")], + db_index=True, + default=None, + help_text="The source of the associated plan's renewal (USER = user-initiated renewal, TASK = autorenew_account-task-initiated renewal, OTHER = renewal is triggered using another mechanism, None = use has_automatic_renewal value (deprecated)). Overrides has_automatic_renewal, if not None.", + null=True, + verbose_name="renewal trigger", + ), + ), ( "has_automatic_renewal", models.BooleanField( default=False, - help_text="Automatic renewal is enabled for associated plan. If False, the plan renewal can be still initiated by user.", - verbose_name="has automatic plan renewal", + help_text="Deprecated. Automatic renewal is enabled for associated plan. If False, the plan renewal can be still initiated by user. Overriden by renewal_trigger, if not None.", + verbose_name="has automatic plan renewal (deprecated)", ), ), ( diff --git a/docs/source/plans_recurrence.rst b/docs/source/plans_recurrence.rst index 1c40412..7478242 100644 --- a/docs/source/plans_recurrence.rst +++ b/docs/source/plans_recurrence.rst @@ -3,13 +3,13 @@ Plans recurrence and automatic renewal To support renewal of plans, use ``RecurringUserPlan`` model to store information about the recurrence. -The plans can be renewed automatically, or the ``RecurringUserPlan`` information can be used only to store information for one-click user initiated renewal (with ``automatic_renewal=False``). +The plans can be renewed automatically using this app, the ``RecurringUserPlan`` information can be used only to store information for one-click user initiated renewal (with ``renewal_trigger=USER``), or the ``RecurringUserPlan`` can indicate that another mechanism is used to automatically renew the plans (``renewal_trigger=OTHER``). -For plans, that should be renewed automatically fill in information about the recurrence:: +For plans, that should be renewed automatically using this app fill in information about the recurrence:: self.order.user.userplan.set_plan_renewal( order=self.order, - automatic_renewal=True, + renewal_trigger=TASK, ... # Not required payment_provider='FooProvider', @@ -19,7 +19,7 @@ For plans, that should be renewed automatically fill in information about the re ... ) -Then all active ``UserPlan`` with ``RecurringUserPlan.has_automatic_renewal=True`` will be picked by ``autorenew_account`` task, that will send ``account_automatic_renewal`` signal. +Then all active ``UserPlan`` with ``RecurringUserPlan.renewal_trigger=TASK`` will be picked by ``autorenew_account`` task, that will send ``account_automatic_renewal`` signal. This signal can be used for your implementation of automatic plan renewal. You should implement following steps:: @receiver(account_automatic_renewal) diff --git a/plans/admin.py b/plans/admin.py index 3e88d15..08d7cb9 100644 --- a/plans/admin.py +++ b/plans/admin.py @@ -261,6 +261,7 @@ class UserPlanAdmin(UserLinkMixin, admin.ModelAdmin): "plan__name", "plan__available", "plan__visible", + "recurring__renewal_trigger", "recurring__has_automatic_renewal", "recurring__payment_provider", "recurring__token_verified", @@ -272,6 +273,7 @@ class UserPlanAdmin(UserLinkMixin, admin.ModelAdmin): "plan", "expire", "active", + "recurring__renewal_trigger", "recurring__automatic_renewal", "recurring__token_verified", "recurring__payment_provider", @@ -290,12 +292,18 @@ class UserPlanAdmin(UserLinkMixin, admin.ModelAdmin): "plan", ] + def recurring__renewal_trigger(self, obj): + return obj.recurring.renewal_trigger + + recurring__renewal_trigger.admin_order_field = "recurring__renewal_trigger" + recurring__renewal_trigger.short_description = "Renewal trigger" + def recurring__automatic_renewal(self, obj): return obj.recurring.has_automatic_renewal recurring__automatic_renewal.admin_order_field = "recurring__has_automatic_renewal" recurring__automatic_renewal.boolean = True - recurring__automatic_renewal.short_description = "Automatic renewal" + recurring__automatic_renewal.short_description = "Automatic renewal (deprecated)" def recurring__token_verified(self, obj): return obj.recurring.token_verified diff --git a/plans/base/models.py b/plans/base/models.py index f004eee..09853cd 100644 --- a/plans/base/models.py +++ b/plans/base/models.py @@ -2,6 +2,7 @@ import logging import re +import warnings from datetime import date, timedelta from decimal import Decimal @@ -297,9 +298,25 @@ def get_plan_extended_from(self, plan): return date.today() def has_automatic_renewal(self): + if not hasattr(self, "recurring"): + return False + + recurring_renewal_trigger = self.recurring.renewal_trigger + # TODO: recurring.renewal_trigger=None deprecated. Remove in the next major release. + if recurring_renewal_trigger is None: + warnings.warn( + "recurring.renewal_trigger=None is deprecated. " + "Set an AbstractRecurringUserPlan.RENEWAL_TRIGGER instead.", + DeprecationWarning, + ) + recurring_renewal_trigger = ( + self.recurring.RENEWAL_TRIGGER.TASK + if self.recurring.has_automatic_renewal + else self.recurring.RENEWAL_TRIGGER.USER + ) + return ( - hasattr(self, "recurring") - and self.recurring.has_automatic_renewal + recurring_renewal_trigger != self.recurring.RENEWAL_TRIGGER.USER and self.recurring.token_verified ) @@ -332,13 +349,39 @@ def plan_autorenew_at(self): days=plans_autorenew_before_days, hours=plans_autorenew_before_hours ) - def set_plan_renewal(self, order, has_automatic_renewal=True, **kwargs): + def set_plan_renewal( + self, + order, + # TODO: has_automatic_renewal deprecated. Remove in the next major release. + has_automatic_renewal=None, + # TODO: renewal_trigger=None deprecated. Set to TASK in the next major release. + renewal_trigger=None, + **kwargs, + ): """ Creates or updates plan renewal information for this userplan with given order """ if not hasattr(self, "recurring"): self.recurring = AbstractRecurringUserPlan.get_concrete_model()() + if has_automatic_renewal is None and renewal_trigger is None: + has_automatic_renewal = True + if has_automatic_renewal is not None: + warnings.warn( + "has_automatic_renewal is deprecated. Use renewal_trigger instead.", + DeprecationWarning, + ) + + if renewal_trigger is None: + warnings.warn( + "renewal_trigger=None is deprecated. Set an AbstractRecurringUserPlan.RENEWAL_TRIGGER instead.", + DeprecationWarning, + ) + else: + has_automatic_renewal = ( + renewal_trigger != self.recurring.RENEWAL_TRIGGER.USER + ) + # Erase values of all fields # We don't want to mix the old and new values self.recurring.set_all_fields_default() @@ -349,6 +392,7 @@ def set_plan_renewal(self, order, has_automatic_renewal=True, **kwargs): self.recurring.amount = order.amount self.recurring.tax = order.tax self.recurring.currency = order.currency + self.recurring.renewal_trigger = renewal_trigger self.recurring.has_automatic_renewal = has_automatic_renewal for k, v in kwargs.items(): setattr(self.recurring, k, v) @@ -509,6 +553,14 @@ class AbstractRecurringUserPlan(BaseMixin, models.Model): More about recurring payments in docs. """ + RENEWAL_TRIGGER = Enumeration( + [ + (1, "OTHER", pgettext_lazy("Renewal trigger", "other")), + (2, "USER", pgettext_lazy("Renewal trigger", "user")), + (3, "TASK", pgettext_lazy("Renewal trigger", "task")), + ] + ) + user_plan = models.OneToOneField( "UserPlan", on_delete=models.CASCADE, related_name="recurring" ) @@ -550,11 +602,28 @@ class AbstractRecurringUserPlan(BaseMixin, models.Model): _("tax"), max_digits=4, decimal_places=2, db_index=True, null=True, blank=True ) # Tax=None is when tax is not applicable currency = models.CharField(_("currency"), max_length=3) + renewal_trigger = models.IntegerField( + _("renewal trigger"), + choices=RENEWAL_TRIGGER, + help_text=_( + "The source of the associated plan's renewal (USER = user-initiated renewal, " + "TASK = autorenew_account-task-initiated renewal, OTHER = renewal is triggered using another mechanism, " + "None = use has_automatic_renewal value (deprecated)). Overrides has_automatic_renewal, if not None." + ), + # TODO: Nullable deprecated. Set to False in the next major release. + null=True, + # TODO: Blank deprecated. Set to False in the next major release. + blank=True, + default=None, + db_index=True, + ) + # TODO: has_automatic_renewal deprecated. Remove in the next major release. has_automatic_renewal = models.BooleanField( - _("has automatic plan renewal"), + _("has automatic plan renewal (deprecated)"), help_text=_( - "Automatic renewal is enabled for associated plan. " - "If False, the plan renewal can be still initiated by user.", + "Deprecated. Automatic renewal is enabled for associated plan. " + "If False, the plan renewal can be still initiated by user. " + "Overriden by renewal_trigger, if not None.", ), default=False, ) @@ -605,6 +674,8 @@ def set_all_fields_default(self): self.amount = None self.tax = None self.currency = None + if self.renewal_trigger is not None: + self.renewal_trigger = self.RENEWAL_TRIGGER.USER self.has_automatic_renewal = False self.token_verified = False self.card_expire_year = None diff --git a/plans/migrations/0013_recurringuserplan_renewal_trigger_and_more.py b/plans/migrations/0013_recurringuserplan_renewal_trigger_and_more.py new file mode 100644 index 0000000..720c28f --- /dev/null +++ b/plans/migrations/0013_recurringuserplan_renewal_trigger_and_more.py @@ -0,0 +1,35 @@ +# Generated by Django 4.2.11 on 2024-03-31 00:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("plans", "0012_planpricing_visible"), + ] + + operations = [ + migrations.AddField( + model_name="recurringuserplan", + name="renewal_trigger", + field=models.IntegerField( + blank=True, + choices=[(1, "other"), (2, "user"), (3, "task")], + db_index=True, + default=None, + help_text="The source of the associated plan's renewal (USER = user-initiated renewal, TASK = autorenew_account-task-initiated renewal, OTHER = renewal is triggered using another mechanism, None = use has_automatic_renewal value (deprecated)). Overrides has_automatic_renewal, if not None.", + null=True, + verbose_name="renewal trigger", + ), + ), + migrations.AlterField( + model_name="recurringuserplan", + name="has_automatic_renewal", + field=models.BooleanField( + default=False, + help_text="Deprecated. Automatic renewal is enabled for associated plan. If False, the plan renewal can be still initiated by user. Overriden by renewal_trigger, if not None.", + verbose_name="has automatic plan renewal (deprecated)", + ), + ), + ] diff --git a/plans/tasks.py b/plans/tasks.py index 7dfe895..6c8e65c 100644 --- a/plans/tasks.py +++ b/plans/tasks.py @@ -3,7 +3,9 @@ from django.conf import settings from django.contrib.auth import get_user_model +from django.db.models import Q +from .base.models import AbstractRecurringUserPlan from .signals import account_automatic_renewal User = get_user_model() @@ -24,11 +26,21 @@ def autorenew_account(providers=None): PLANS_AUTORENEW_BEFORE_HOURS = getattr(settings, "PLANS_AUTORENEW_BEFORE_HOURS", 0) accounts_for_renewal = get_active_plans().filter( - userplan__recurring__has_automatic_renewal=True, - userplan__recurring__token_verified=True, - userplan__expire__lt=datetime.date.today() - + datetime.timedelta( - days=PLANS_AUTORENEW_BEFORE_DAYS, hours=PLANS_AUTORENEW_BEFORE_HOURS + ( + Q( + userplan__recurring__renewal_trigger=AbstractRecurringUserPlan.RENEWAL_TRIGGER.TASK + ) + | Q( + userplan__recurring__renewal_trigger=None, + userplan__recurring__has_automatic_renewal=True, + ) + ) + & Q( + userplan__recurring__token_verified=True, + userplan__expire__lt=datetime.date.today() + + datetime.timedelta( + days=PLANS_AUTORENEW_BEFORE_DAYS, hours=PLANS_AUTORENEW_BEFORE_HOURS + ), ), ) diff --git a/plans/tests/test_autorenew_accounts.py b/plans/tests/test_autorenew_accounts.py index 53dbdfa..f74a920 100644 --- a/plans/tests/test_autorenew_accounts.py +++ b/plans/tests/test_autorenew_accounts.py @@ -6,6 +6,8 @@ from django.test import TestCase from model_bakery import baker +from plans.models import AbstractRecurringUserPlan + class ManagementCommandTests(TestCase): def test_command_output(self): @@ -14,21 +16,50 @@ def test_command_output(self): self.assertEqual(out.getvalue(), "Starting renewal\nNo accounts autorenewed\n") def test_renewal(self): - self.user = baker.make("User", username="testuser") - plan_pricing = baker.make( - "PlanPricing", plan=baker.make("Plan", name="Foo plan") - ) - baker.make( - "UserPlan", - user=self.user, - plan=plan_pricing.plan, - recurring__payment_provider="internal-payment-recurring", - recurring__amount=Decimal(123), - recurring__pricing=plan_pricing.pricing, - recurring__currency="USD", - recurring__has_automatic_renewal=True, - recurring__token_verified=True, - expire=datetime.date(2020, 1, 2), + _make_user( + userplan__expire=datetime.date(2020, 1, 2), + userplan__recurring__renewal_trigger=AbstractRecurringUserPlan.RENEWAL_TRIGGER.TASK, + # False to make sure that renewal_trigger takes precedence. + userplan__recurring__has_automatic_renewal=False, + userplan__recurring__token_verified=True, + ) + out = StringIO() + call_command("autorenew_accounts", stdout=out) + self.assertEqual( + out.getvalue().strip(), + "Starting renewal\nAccounts submitted to renewal:\n\tinternal-payment-recurring\t\ttestuser", + ) + + def test_renewal_trigger_user(self): + _make_user( + userplan__expire=datetime.date(2020, 1, 2), + userplan__recurring__renewal_trigger=AbstractRecurringUserPlan.RENEWAL_TRIGGER.USER, + # True to make sure that renewal_trigger takes precedence. + userplan__recurring__has_automatic_renewal=True, + userplan__recurring__token_verified=True, + ) + out = StringIO() + call_command("autorenew_accounts", stdout=out) + self.assertEqual(out.getvalue(), "Starting renewal\nNo accounts autorenewed\n") + + def test_renewal_trigger_other(self): + _make_user( + userplan__expire=datetime.date(2020, 1, 2), + userplan__recurring__renewal_trigger=AbstractRecurringUserPlan.RENEWAL_TRIGGER.OTHER, + # True to make sure that renewal_trigger takes precedence. + userplan__recurring__has_automatic_renewal=True, + userplan__recurring__token_verified=True, + ) + out = StringIO() + call_command("autorenew_accounts", stdout=out) + self.assertEqual(out.getvalue(), "Starting renewal\nNo accounts autorenewed\n") + + def test_renewal_trigger_none_has_automatic_renewal_true(self): + _make_user( + userplan__expire=datetime.date(2020, 1, 2), + userplan__recurring__renewal_trigger=None, + userplan__recurring__has_automatic_renewal=True, + userplan__recurring__token_verified=True, ) out = StringIO() call_command("autorenew_accounts", stdout=out) @@ -37,23 +68,49 @@ def test_renewal(self): "Starting renewal\nAccounts submitted to renewal:\n\tinternal-payment-recurring\t\ttestuser", ) + def test_renewal_trigger_none_has_automatic_renewal_false(self): + _make_user( + userplan__expire=datetime.date(2020, 1, 2), + userplan__recurring__renewal_trigger=None, + userplan__recurring__has_automatic_renewal=False, + userplan__recurring__token_verified=True, + ) + out = StringIO() + call_command("autorenew_accounts", stdout=out) + self.assertEqual(out.getvalue(), "Starting renewal\nNo accounts autorenewed\n") + def test_renewal_providers(self): - self.user = baker.make("User", username="testuser") - plan_pricing = baker.make( - "PlanPricing", plan=baker.make("Plan", name="Foo plan") - ) - baker.make( - "UserPlan", - user=self.user, - plan=plan_pricing.plan, - recurring__payment_provider="internal-payment-recurring", - recurring__amount=Decimal(123), - recurring__pricing=plan_pricing.pricing, - recurring__currency="USD", - recurring__has_automatic_renewal=True, - recurring__token_verified=True, - expire=datetime.date(2020, 1, 2), + _make_user( + userplan__expire=datetime.date(2020, 1, 2), + userplan__recurring__renewal_trigger=AbstractRecurringUserPlan.RENEWAL_TRIGGER.TASK, + userplan__recurring__has_automatic_renewal=True, + userplan__recurring__payment_provider="internal-payment-recurring", + userplan__recurring__token_verified=True, ) out = StringIO() call_command("autorenew_accounts", providers="foo", stdout=out) self.assertEqual(out.getvalue(), "Starting renewal\nNo accounts autorenewed\n") + + +def _make_user( + userplan__expire, + userplan__recurring__renewal_trigger, + userplan__recurring__has_automatic_renewal, + userplan__recurring__token_verified, + userplan__recurring__payment_provider="internal-payment-recurring", +): + user = baker.make("User", username="testuser") + plan_pricing = baker.make("PlanPricing", plan=baker.make("Plan", name="Foo plan")) + baker.make( + "UserPlan", + user=user, + plan=plan_pricing.plan, + recurring__payment_provider=userplan__recurring__payment_provider, + recurring__amount=Decimal(123), + recurring__pricing=plan_pricing.pricing, + recurring__currency="USD", + recurring__renewal_trigger=userplan__recurring__renewal_trigger, + recurring__has_automatic_renewal=userplan__recurring__has_automatic_renewal, + recurring__token_verified=userplan__recurring__token_verified, + expire=userplan__expire, + ) diff --git a/plans/tests/tests.py b/plans/tests/tests.py index 1121229..66414e7 100644 --- a/plans/tests/tests.py +++ b/plans/tests/tests.py @@ -1,5 +1,6 @@ import random import re +import warnings from datetime import date, datetime, time, timedelta from decimal import Decimal from io import StringIO @@ -31,6 +32,7 @@ AbstractPlan, AbstractPlanPricing, AbstractPricing, + AbstractRecurringUserPlan, AbstractUserPlan, ) from plans.plan_change import PlanChangePolicy, StandardPlanChangePolicy @@ -1490,16 +1492,151 @@ def test_set_plan_renewal(self): """Test that UserPlan.set_plan_renewal() method""" up = baker.make("UserPlan") o = baker.make("Order", amount=10) - up.set_plan_renewal(order=o, card_masked_number="1234") - self.assertEqual(up.recurring.amount, 10) - self.assertEqual(up.recurring.card_masked_number, "1234") - old_id = up.recurring.id - # test setting new values - up.set_plan_renewal(order=o) - self.assertEqual(up.recurring.amount, 10) - self.assertEqual(up.recurring.card_masked_number, None) - self.assertEqual(up.recurring.id, old_id) + with warnings.catch_warnings(record=True) as caught_warnings: + warnings.simplefilter("always") + + up.set_plan_renewal( + order=o, + renewal_trigger=AbstractRecurringUserPlan.RENEWAL_TRIGGER.TASK, + card_masked_number="1234", + ) + self.assertEqual(up.recurring.amount, 10) + self.assertEqual(up.recurring.card_masked_number, "1234") + self.assertFalse(caught_warnings) + old_id = up.recurring.id + + # test setting new values + up.set_plan_renewal( + order=o, renewal_trigger=AbstractRecurringUserPlan.RENEWAL_TRIGGER.TASK + ) + self.assertEqual(up.recurring.amount, 10) + self.assertEqual(up.recurring.card_masked_number, None) + self.assertEqual(up.recurring.id, old_id) + self.assertFalse(caught_warnings) + + # test renewal_trigger overrides has_automatic_renewal + up.set_plan_renewal( + order=o, + renewal_trigger=AbstractRecurringUserPlan.RENEWAL_TRIGGER.TASK, + has_automatic_renewal=False, + ) + self.assertEqual( + up.recurring.renewal_trigger, + AbstractRecurringUserPlan.RENEWAL_TRIGGER.TASK, + ) + self.assertTrue(up.recurring.has_automatic_renewal) + self.assertEqual(len(caught_warnings), 1) + caught_warning = caught_warnings.pop() + self.assertTrue(issubclass(caught_warning.category, DeprecationWarning)) + self.assertEqual( + str(caught_warning.message), + "has_automatic_renewal is deprecated. Use renewal_trigger instead.", + ) + up.set_plan_renewal( + order=o, + renewal_trigger=AbstractRecurringUserPlan.RENEWAL_TRIGGER.USER, + has_automatic_renewal=True, + ) + self.assertEqual( + up.recurring.renewal_trigger, + AbstractRecurringUserPlan.RENEWAL_TRIGGER.USER, + ) + self.assertFalse(up.recurring.has_automatic_renewal) + self.assertEqual(len(caught_warnings), 1) + caught_warning = caught_warnings.pop() + self.assertTrue(issubclass(caught_warning.category, DeprecationWarning)) + self.assertEqual( + str(caught_warning.message), + "has_automatic_renewal is deprecated. Use renewal_trigger instead.", + ) + up.set_plan_renewal( + order=o, + renewal_trigger=AbstractRecurringUserPlan.RENEWAL_TRIGGER.OTHER, + has_automatic_renewal=True, + ) + self.assertEqual( + up.recurring.renewal_trigger, + AbstractRecurringUserPlan.RENEWAL_TRIGGER.OTHER, + ) + self.assertTrue(up.recurring.has_automatic_renewal) + self.assertEqual(len(caught_warnings), 1) + caught_warning = caught_warnings.pop() + self.assertTrue(issubclass(caught_warning.category, DeprecationWarning)) + self.assertEqual( + str(caught_warning.message), + "has_automatic_renewal is deprecated. Use renewal_trigger instead.", + ) + + # test deprecated backwards compatibility + up.recurring.renewal_trigger = None + up.set_plan_renewal(order=o) + self.assertIsNone(up.recurring.renewal_trigger) + self.assertTrue(up.recurring.has_automatic_renewal) + self.assertEqual(len(caught_warnings), 2) + caught_warning = caught_warnings.pop() + self.assertTrue(issubclass(caught_warning.category, DeprecationWarning)) + self.assertEqual( + str(caught_warning.message), + "renewal_trigger=None is deprecated. Set an AbstractRecurringUserPlan.RENEWAL_TRIGGER instead.", + ) + caught_warning = caught_warnings.pop() + self.assertTrue(issubclass(caught_warning.category, DeprecationWarning)) + self.assertEqual( + str(caught_warning.message), + "has_automatic_renewal is deprecated. Use renewal_trigger instead.", + ) + up.set_plan_renewal(order=o, has_automatic_renewal=True) + self.assertIsNone(up.recurring.renewal_trigger) + self.assertTrue(up.recurring.has_automatic_renewal) + self.assertEqual(len(caught_warnings), 2) + caught_warning = caught_warnings.pop() + self.assertTrue(issubclass(caught_warning.category, DeprecationWarning)) + self.assertEqual( + str(caught_warning.message), + "renewal_trigger=None is deprecated. Set an AbstractRecurringUserPlan.RENEWAL_TRIGGER instead.", + ) + caught_warning = caught_warnings.pop() + self.assertTrue(issubclass(caught_warning.category, DeprecationWarning)) + self.assertEqual( + str(caught_warning.message), + "has_automatic_renewal is deprecated. Use renewal_trigger instead.", + ) + up.set_plan_renewal(order=o, has_automatic_renewal=False) + self.assertIsNone(up.recurring.renewal_trigger) + self.assertFalse(up.recurring.has_automatic_renewal) + self.assertEqual(len(caught_warnings), 2) + caught_warning = caught_warnings.pop() + self.assertTrue(issubclass(caught_warning.category, DeprecationWarning)) + self.assertEqual( + str(caught_warning.message), + "renewal_trigger=None is deprecated. Set an AbstractRecurringUserPlan.RENEWAL_TRIGGER instead.", + ) + caught_warning = caught_warnings.pop() + self.assertTrue(issubclass(caught_warning.category, DeprecationWarning)) + self.assertEqual( + str(caught_warning.message), + "has_automatic_renewal is deprecated. Use renewal_trigger instead.", + ) + up.recurring.renewal_trigger = ( + AbstractRecurringUserPlan.RENEWAL_TRIGGER.TASK + ) + up.set_plan_renewal(order=o) + self.assertIsNone(up.recurring.renewal_trigger) + self.assertTrue(up.recurring.has_automatic_renewal) + self.assertEqual(len(caught_warnings), 2) + caught_warning = caught_warnings.pop() + self.assertTrue(issubclass(caught_warning.category, DeprecationWarning)) + self.assertEqual( + str(caught_warning.message), + "renewal_trigger=None is deprecated. Set an AbstractRecurringUserPlan.RENEWAL_TRIGGER instead.", + ) + caught_warning = caught_warnings.pop() + self.assertTrue(issubclass(caught_warning.category, DeprecationWarning)) + self.assertEqual( + str(caught_warning.message), + "has_automatic_renewal is deprecated. Use renewal_trigger instead.", + ) def test_plan_autorenew_at(self): """Test that UserPlan.plan_autorenew_at() method""" @@ -1524,11 +1661,46 @@ def test_has_automatic_renewal(self): """Test UserPlan.has_automatic_renewal() method""" user_plan = baker.make("UserPlan") order = baker.make("Order", amount=10) - user_plan.set_plan_renewal(order=order, card_masked_number="1234") - self.assertEqual(user_plan.has_automatic_renewal(), False) - user_plan.recurring.token_verified = True - self.assertEqual(user_plan.has_automatic_renewal(), True) + with warnings.catch_warnings(record=True) as caught_warnings: + warnings.simplefilter("always") + + user_plan.set_plan_renewal( + order=order, + renewal_trigger=AbstractRecurringUserPlan.RENEWAL_TRIGGER.TASK, + card_masked_number="1234", + ) + self.assertEqual(user_plan.has_automatic_renewal(), False) + user_plan.recurring.has_automatic_renewal = True + self.assertEqual(user_plan.has_automatic_renewal(), False) + self.assertFalse(caught_warnings) + + user_plan.recurring.token_verified = True + self.assertEqual(user_plan.has_automatic_renewal(), True) + user_plan.recurring.has_automatic_renewal = False + self.assertEqual(user_plan.has_automatic_renewal(), True) + self.assertFalse(caught_warnings) + + user_plan.recurring.renewal_trigger = None + self.assertEqual(user_plan.has_automatic_renewal(), False) + self.assertEqual(len(caught_warnings), 1) + caught_warning = caught_warnings.pop() + self.assertTrue(issubclass(caught_warning.category, DeprecationWarning)) + self.assertEqual( + str(caught_warning.message), + "recurring.renewal_trigger=None is deprecated. " + "Set an AbstractRecurringUserPlan.RENEWAL_TRIGGER instead.", + ) + user_plan.recurring.has_automatic_renewal = True + self.assertEqual(user_plan.has_automatic_renewal(), True) + self.assertEqual(len(caught_warnings), 1) + caught_warning = caught_warnings.pop() + self.assertTrue(issubclass(caught_warning.category, DeprecationWarning)) + self.assertEqual( + str(caught_warning.message), + "recurring.renewal_trigger=None is deprecated. " + "Set an AbstractRecurringUserPlan.RENEWAL_TRIGGER instead.", + ) def test_create_new_order(self): rup = baker.make( @@ -1565,7 +1737,11 @@ def test_expire_account_task(self): userplan.active = True # If the automatic renewal didn't go through, even automatic renewal plans has to go - userplan.set_plan_renewal(order=order, card_masked_number="1234") + userplan.set_plan_renewal( + order=order, + renewal_trigger=AbstractRecurringUserPlan.RENEWAL_TRIGGER.TASK, + card_masked_number="1234", + ) userplan.save() tasks.expire_account() @@ -1585,7 +1761,11 @@ def test_expire_account_task_notify(self): userplan.active = True # If the automatic renewal didn't go through, even automatic renewal plans has to go - userplan.set_plan_renewal(order=order, card_masked_number="1234") + userplan.set_plan_renewal( + order=order, + renewal_trigger=AbstractRecurringUserPlan.RENEWAL_TRIGGER.TASK, + card_masked_number="1234", + ) userplan.save() tasks.expire_account()