Skip to content

Commit

Permalink
Feat: CalFresh re-enrollment error (#1992)
Browse files Browse the repository at this point in the history
  • Loading branch information
thekaveman authored May 2, 2024
2 parents 4227af6 + c9d4716 commit 7c45fec
Show file tree
Hide file tree
Showing 11 changed files with 167 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.0.3 on 2024-05-01 19:30

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("core", "0008_eligibilityverifier_unverified_template"),
]

operations = [
migrations.AddField(
model_name="eligibilitytype",
name="reenrollment_error_template",
field=models.TextField(blank=True, null=True),
),
]
4 changes: 4 additions & 0 deletions benefits/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ class EligibilityType(models.Model):
expiration_days = models.PositiveSmallIntegerField(null=True, blank=True)
expiration_reenrollment_days = models.PositiveSmallIntegerField(null=True, blank=True)
enrollment_index_template = models.TextField(default="enrollment/index.html")
reenrollment_error_template = models.TextField(null=True, blank=True)

def __str__(self):
return self.label
Expand Down Expand Up @@ -141,6 +142,7 @@ def clean(self):
supports_expiration = self.supports_expiration
expiration_days = self.expiration_days
expiration_reenrollment_days = self.expiration_reenrollment_days
reenrollment_error_template = self.reenrollment_error_template

if supports_expiration:
errors = {}
Expand All @@ -149,6 +151,8 @@ def clean(self):
errors.update(expiration_days=ValidationError(message))
if expiration_reenrollment_days is None or expiration_reenrollment_days <= 0:
errors.update(expiration_reenrollment_days=ValidationError(message))
if reenrollment_error_template is None:
errors.update(reenrollment_error_template=ValidationError("Required when supports expiration is True."))

if errors:
raise ValidationError(errors)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{% extends "enrollment/reenrollment-error-base.html" %}
{% load i18n %}

{% block error-message %}
<p>
{% translate "Your CalFresh Cardholder transit benefit does not expire until" %} {{ enrollment.expires|date }}.
{% translate "You can re-enroll for this benefit beginning on" %} {{ enrollment.reenrollment|date }}.
{% translate "Please try again then." %}
</p>
{% endblock error-message %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{% extends "core/base.html" %}
{% load i18n %}

{% block page-title %}
{% translate "Enrollment error" %}
{% endblock page-title %}

{% block main-content %}
{% if authentication and authentication.sign_out_link_template %}
{% include authentication.sign_out_link_template %}
{% endif %}

<div class="container">
<h1 class="h2 text-center">
<span class="icon d-block pb-5">{% include "core/includes/icon.html" with name="calendarcheck" %}</span>
{% translate "You are still enrolled in this benefit" %}
</h1>

<div class="row justify-content-center">
<div class="col-lg-8 pt-4">
{% block error-message %}
{% endblock error-message %}
</div>
</div>

<div class="row pt-8 justify-content-center">
<div class="col-lg-3 col-8">{% include "core/includes/button--index.html" %}</div>
</div>

</div>
{% endblock main-content %}
1 change: 1 addition & 0 deletions benefits/enrollment/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# /enrollment
path("", views.index, name="index"),
path("token", views.token, name="token"),
path("reenrollment-error", views.reenrollment_error, name="reenrollment-error"),
path("retry", views.retry, name="retry"),
path("success", views.success, name="success"),
]
28 changes: 22 additions & 6 deletions benefits/enrollment/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,13 @@
from requests.exceptions import HTTPError

from benefits.core import session
from benefits.core.middleware import (
EligibleSessionRequired,
VerifierSessionRequired,
pageview_decorator,
)
from benefits.core.middleware import EligibleSessionRequired, VerifierSessionRequired, pageview_decorator
from benefits.core.views import ROUTE_LOGGED_OUT
from . import analytics, forms

from . import analytics, forms

ROUTE_INDEX = "enrollment:index"
ROUTE_REENROLLMENT_ERROR = "enrollment:reenrollment-error"
ROUTE_RETRY = "enrollment:retry"
ROUTE_SUCCESS = "enrollment:success"
ROUTE_TOKEN = "enrollment:token"
Expand Down Expand Up @@ -122,6 +119,25 @@ def index(request):
return TemplateResponse(request, eligibility.enrollment_index_template, context)


@decorator_from_middleware(EligibleSessionRequired)
def reenrollment_error(request):
"""View handler for a re-enrollment attempt that is not yet within the re-enrollment window."""
eligibility = session.eligibility(request)
verifier = session.verifier(request)

if eligibility.reenrollment_error_template is None:
raise Exception(f"Re-enrollment error with null template on: {eligibility.label}")

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_error(request, "Re-enrollment error.")

return TemplateResponse(request, eligibility.reenrollment_error_template)


@decorator_from_middleware(EligibleSessionRequired)
def retry(request):
"""View handler for a recoverable failure condition."""
Expand Down
15 changes: 15 additions & 0 deletions benefits/locale/en/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,21 @@ msgstr ""
msgid "Enroll"
msgstr ""

msgid "Your CalFresh Cardholder transit benefit does not expire until"
msgstr ""

msgid "You can re-enroll for this benefit beginning on"
msgstr ""

msgid "Please try again then."
msgstr ""

msgid "Enrollment error"
msgstr ""

msgid "You are still enrolled in this benefit"
msgstr ""

msgid "Unable to register card"
msgstr ""

Expand Down
15 changes: 15 additions & 0 deletions benefits/locale/es/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,21 @@ msgstr "Espere por favor..."
msgid "Enroll"
msgstr "Inscribir"

msgid "Your CalFresh Cardholder transit benefit does not expire until"
msgstr ""

msgid "You can re-enroll for this benefit beginning on"
msgstr ""

msgid "Please try again then."
msgstr ""

msgid "Enrollment error"
msgstr ""

msgid "You are still enrolled in this benefit"
msgstr ""

msgid "Unable to register card"
msgstr "No se pudo registrar la tarjeta"

Expand Down
1 change: 1 addition & 0 deletions tests/pytest/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ def model_EligibilityType_supports_expiration(model_EligibilityType):
model_EligibilityType.supports_expiration = True
model_EligibilityType.expiration_days = 365
model_EligibilityType.expiration_reenrollment_days = 14
model_EligibilityType.reenrollment_error_template = "enrollment/reenrollment-error--calfresh.html"
model_EligibilityType.save()

return model_EligibilityType
Expand Down
13 changes: 13 additions & 0 deletions tests/pytest/core/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,19 @@ def test_EligibilityType_zero_expiration_reenrollment_days(model_EligibilityType
)


@pytest.mark.django_db
def test_EligibilityType_missing_reenrollment_template(model_EligibilityType_supports_expiration):
model_EligibilityType_supports_expiration.reenrollment_error_template = None
model_EligibilityType_supports_expiration.save()

with pytest.raises(ValidationError) as exception_info:
model_EligibilityType_supports_expiration.full_clean()

error_dict = exception_info.value.error_dict
assert len(error_dict["reenrollment_error_template"]) == 1
assert error_dict["reenrollment_error_template"][0].message == "Required when supports expiration is True."


@pytest.mark.django_db
def test_EligibilityType_supports_expiration(model_EligibilityType_supports_expiration):
# test will fail if any error is raised
Expand Down
43 changes: 37 additions & 6 deletions tests/pytest/enrollment/test_views.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
import time

import pytest
from django.urls import reverse

from littlepay.api.funding_sources import FundingSourceResponse
from requests import HTTPError
import pytest

import benefits.enrollment.views
from benefits.core.middleware import TEMPLATE_USER_ERROR
from benefits.core.views import ROUTE_LOGGED_OUT
from benefits.enrollment.views import (
ROUTE_INDEX,
ROUTE_TOKEN,
ROUTE_SUCCESS,
ROUTE_REENROLLMENT_ERROR,
ROUTE_RETRY,
ROUTE_SUCCESS,
ROUTE_TOKEN,
TEMPLATE_SUCCESS,
TEMPLATE_RETRY,
)

import benefits.enrollment.views


@pytest.fixture
def card_tokenize_form_data():
Expand Down Expand Up @@ -211,6 +210,38 @@ def test_index_ineligible(client):
assert response.template_name == TEMPLATE_USER_ERROR


@pytest.mark.django_db
def test_reenrollment_error_ineligible(client):
path = reverse(ROUTE_REENROLLMENT_ERROR)

response = client.get(path)

assert response.status_code == 200
assert response.template_name == TEMPLATE_USER_ERROR


@pytest.mark.django_db
@pytest.mark.usefixtures("mocked_session_agency", "mocked_session_verifier", "mocked_session_eligibility")
def test_reenrollment_error_eligibility_no_error_template(client):
path = reverse(ROUTE_REENROLLMENT_ERROR)

with pytest.raises(Exception, match="Re-enrollment error with null template"):
client.get(path)


@pytest.mark.django_db
@pytest.mark.usefixtures("mocked_session_agency", "mocked_session_verifier")
def test_reenrollment_error(client, model_EligibilityType_supports_expiration, mocked_session_eligibility):
mocked_session_eligibility.return_value = model_EligibilityType_supports_expiration

path = reverse(ROUTE_REENROLLMENT_ERROR)

response = client.get(path)

assert response.status_code == 200
assert response.template_name == model_EligibilityType_supports_expiration.reenrollment_error_template


@pytest.mark.django_db
def test_retry_ineligible(client):
path = reverse(ROUTE_RETRY)
Expand Down

0 comments on commit 7c45fec

Please sign in to comment.