Skip to content

Commit

Permalink
Revert "Feat: enrollments can expire (#1989)" (#2052)
Browse files Browse the repository at this point in the history
  • Loading branch information
angela-tran committed Apr 25, 2024
2 parents dd3bd7a + 5961eec commit ea0dbea
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 540 deletions.
Empty file.
118 changes: 14 additions & 104 deletions benefits/enrollment/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@
"""

import logging
from datetime import timedelta

from django.http import JsonResponse
from django.template.response import TemplateResponse
from django.urls import reverse
from django.utils import timezone
from django.utils.decorators import decorator_from_middleware
from littlepay.api.client import Client
from requests.exceptions import HTTPError
Expand All @@ -28,7 +26,6 @@
ROUTE_SUCCESS = "enrollment:success"
ROUTE_TOKEN = "enrollment:token"

TEMPLATE_REENROLLMENT_ERROR = "enrollment/reenrollment-error.html"
TEMPLATE_RETRY = "enrollment/retry.html"
TEMPLATE_SUCCESS = "enrollment/success.html"

Expand Down Expand Up @@ -72,6 +69,7 @@ def index(request):
if not form.is_valid():
raise Exception("Invalid card token form")

logger.debug("Read tokenized card")
card_token = form.cleaned_data.get("card_token")

client = Client(
Expand All @@ -83,73 +81,25 @@ def index(request):
client.oauth.ensure_active_token(client.token)

funding_source = client.get_funding_source_by_token(card_token)
group_id = eligibility.group_id

try:
group_funding_source = _get_group_funding_source(
client=client, group_id=group_id, funding_source_id=funding_source.id
)

already_enrolled = group_funding_source is not None

if eligibility.supports_expiration:
# set expiry on session
if already_enrolled and group_funding_source.expiry_date is not None:
session.update(request, enrollment_expiry=group_funding_source.expiry_date)
else:
session.update(request, enrollment_expiry=_calculate_expiry(eligibility.expiration_days))

if not already_enrolled:
# enroll user with an expiration date, return success
client.link_concession_group_funding_source(
group_id=group_id, funding_source_id=funding_source.id, expiry=session.enrollment_expiry(request)
)
return success(request)
else: # already_enrolled
if group_funding_source.expiry_date is None:
# update expiration of existing enrollment, return success
client.update_concession_group_funding_source_expiry(
group_id=group_id,
funding_source_id=funding_source.id,
expiry=session.enrollment_expiry(request),
)
return success(request)
else:
is_expired = _is_expired(group_funding_source.expiry_date)
is_within_reenrollment_window = _is_within_reenrollment_window(
group_funding_source.expiry_date, session.enrollment_reenrollment(request)
)

if is_expired or is_within_reenrollment_window:
# update expiration of existing enrollment, return success
client.update_concession_group_funding_source_expiry(
group_id=group_id,
funding_source_id=funding_source.id,
expiry=session.enrollment_expiry(request),
)
return success(request)
else:
# re-enrollment error, return enrollment error with expiration and reenrollment_date
return reenrollment_error(request)
else: # eligibility does not support expiration
if not already_enrolled:
# enroll user with no expiration date, return success
client.link_concession_group_funding_source(group_id=group_id, funding_source_id=funding_source.id)
return success(request)
else: # already_enrolled
if group_funding_source.expiry_date is None:
# no action, return success
return success(request)
else:
# remove expiration date, return success
raise NotImplementedError("Removing expiration date is currently not supported")

client.link_concession_group_funding_source(funding_source_id=funding_source.id, group_id=eligibility.group_id)
except HTTPError as e:
analytics.returned_error(request, str(e))
raise Exception(f"{e}: {e.response.json()}")
# 409 means that customer already belongs to a concession group.
# the response JSON will look like:
# {"errors":[{"detail":"Conflict (409) - Customer already belongs to a concession group."}]}
if e.response.status_code == 409:
analytics.returned_success(request, eligibility.group_id)
return success(request)
else:
analytics.returned_error(request, str(e))
raise Exception(f"{e}: {e.response.json()}")
except Exception as e:
analytics.returned_error(request, str(e))
raise e
else:
analytics.returned_success(request, eligibility.group_id)
return success(request)

# GET enrollment index
else:
Expand All @@ -172,43 +122,6 @@ def index(request):
return TemplateResponse(request, eligibility.enrollment_index_template, context)


def _get_group_funding_source(client: Client, group_id, funding_source_id):
group_funding_sources = client.get_concession_group_linked_funding_sources(group_id)
matching_group_funding_source = None
for group_funding_source in group_funding_sources:
if group_funding_source.id == funding_source_id:
matching_group_funding_source = group_funding_source
break

return matching_group_funding_source


def _is_expired(expiry_date):
"""Returns whether the passed in datetime is expired or not."""
return expiry_date <= timezone.now()


def _is_within_reenrollment_window(expiry_date, enrollment_reenrollment_date):
"""Returns if we are currently within the reenrollment window."""
return enrollment_reenrollment_date <= timezone.now() < expiry_date


def _calculate_expiry(expiration_days):
"""Returns the expiry datetime, which should be midnight in our configured timezone of the (N + 1)th day from now,
where N is expiration_days."""
default_time_zone = timezone.get_default_timezone()
expiry_date = timezone.localtime(timezone=default_time_zone) + timedelta(days=expiration_days + 1)
expiry_datetime = expiry_date.replace(hour=0, minute=0, second=0, microsecond=0)

return expiry_datetime


def reenrollment_error(request):
"""View handler for a re-enrollment attempt that is not yet within the re-enrollment window."""
analytics.returned_error(request, "Re-enrollment error")
return TemplateResponse(request, TEMPLATE_REENROLLMENT_ERROR)


@decorator_from_middleware(EligibleSessionRequired)
def retry(request):
"""View handler for a recoverable failure condition."""
Expand All @@ -217,7 +130,6 @@ def retry(request):


@pageview_decorator
@decorator_from_middleware(EligibleSessionRequired)
@decorator_from_middleware(VerifierSessionRequired)
def success(request):
"""View handler for the final success page."""
Expand All @@ -226,12 +138,10 @@ def success(request):

agency = session.agency(request)
verifier = session.verifier(request)
eligibility = session.eligibility(request)

if session.logged_in(request) and verifier.auth_provider.supports_sign_out:
# overwrite origin for a logged in user
# if they click the logout button, they are taken to the new route
session.update(request, origin=reverse(ROUTE_LOGGED_OUT))

analytics.returned_success(request, eligibility.group_id)
return TemplateResponse(request, agency.enrollment_success_template)
6 changes: 1 addition & 5 deletions benefits/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,7 @@ def RUNTIME_ENVIRONMENT():

USE_I18N = True

# See https://docs.djangoproject.com/en/5.0/ref/settings/#std-setting-TIME_ZONE
# > Note that this isn’t necessarily the time zone of the server.
# > When USE_TZ is True, this is the default time zone that Django will use to display datetimes in templates
# > and to interpret datetimes entered in forms.
TIME_ZONE = "America/Los_Angeles"
TIME_ZONE = "UTC"
USE_TZ = True

# Static files (CSS, JavaScript, Images)
Expand Down
10 changes: 0 additions & 10 deletions tests/pytest/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from unittest.mock import create_autospec
from django.contrib.sessions.middleware import SessionMiddleware
from django.middleware.locale import LocaleMiddleware
from django.utils import timezone

import pytest
from pytest_socket import disable_socket
Expand Down Expand Up @@ -257,15 +256,6 @@ def mocked_session_oauth_token(mocker):
return mocker.patch("benefits.core.session.oauth_token", autospec=True, return_value="token")


@pytest.fixture
def mocked_session_enrollment_expiry(mocker):
return mocker.patch(
"benefits.core.session.enrollment_expiry",
autospec=True,
return_value=timezone.make_aware(timezone.datetime(2024, 1, 1), timezone=timezone.get_default_timezone()),
)


@pytest.fixture
def mocked_session_verifier(mocker, model_EligibilityVerifier):
return mocker.patch("benefits.core.session.verifier", autospec=True, return_value=model_EligibilityVerifier)
Expand Down

0 comments on commit ea0dbea

Please sign in to comment.