Coverage for benefits/enrollment/views.py: 97%
87 statements
« prev ^ index » next coverage.py v7.11.3, created at 2025-11-15 00:14 +0000
« prev ^ index » next coverage.py v7.11.3, created at 2025-11-15 00:14 +0000
1"""
2The enrollment application: view definitions for the benefits enrollment flow.
3"""
5from dataclasses import asdict, dataclass
6import logging
8from django.template.defaultfilters import date
9from django.template.response import TemplateResponse
10from django.urls import reverse
11from django.utils.decorators import decorator_from_middleware
12from django.views.generic import RedirectView, TemplateView
14from benefits.core.context.agency import AgencySlug
15from benefits.core.context.flow import SystemName
16from benefits.core.context import formatted_gettext_lazy as _
17from benefits.routes import routes
18from benefits.core import session
19from benefits.core.mixins import (
20 AgencySessionRequiredMixin,
21 EligibleSessionRequiredMixin,
22 FlowSessionRequiredMixin,
23 PageViewMixin,
24)
25from benefits.core.middleware import EligibleSessionRequired
26from . import analytics
28TEMPLATE_RETRY = "enrollment/retry.html"
29TEMPLATE_SYSTEM_ERROR = "enrollment/system_error.html"
32logger = logging.getLogger(__name__)
35class IndexView(AgencySessionRequiredMixin, EligibleSessionRequiredMixin, RedirectView):
36 """CBV for the enrollment landing page."""
38 route_origin = routes.ENROLLMENT_INDEX
40 def get_redirect_url(self, *args, **kwargs):
41 route_name = self.agency.enrollment_index_route
42 return reverse(route_name)
44 def get(self, request, *args, **kwargs):
45 session.update(request, origin=reverse(self.route_origin))
46 return super().get(request, *args, **kwargs)
49class ReenrollmentErrorView(FlowSessionRequiredMixin, EligibleSessionRequiredMixin, TemplateView):
50 """View handler for a re-enrollment attempt that is not yet within the re-enrollment window."""
52 template_name = "enrollment/reenrollment-error.html"
54 def get_context_data(self, **kwargs):
55 context = super().get_context_data(**kwargs)
57 request = self.request
59 flow = self.flow
60 expiry = session.enrollment_expiry(request)
61 reenrollment = session.enrollment_reenrollment(request)
63 if flow.system_name == SystemName.CALFRESH: 63 ↛ 72line 63 didn't jump to line 72 because the condition on line 63 was always true
64 does_not_expire_until = _("Your CalFresh Cardholder transit benefit does not expire until")
65 reenroll_on = _("You can re-enroll for this benefit beginning on")
66 try_again = _("Please try again then.")
68 context["paragraphs"] = [
69 f"{does_not_expire_until} {date(expiry)}. {reenroll_on} {date(reenrollment)}. {try_again}"
70 ]
72 return context
74 def get(self, request, *args, **kwargs):
75 flow = self.flow
77 if session.logged_in(request) and flow.supports_sign_out:
78 # overwrite origin for a logged in user
79 # if they click the logout button, they are taken to the new route
80 session.update(request, origin=reverse(routes.LOGGED_OUT))
82 return super().get(request, *args, **kwargs)
85@decorator_from_middleware(EligibleSessionRequired)
86def retry(request):
87 """View handler for a recoverable failure condition."""
88 flow = session.flow(request)
89 agency = session.agency(request)
90 analytics.returned_retry(request, enrollment_group=flow.group_id, transit_processor=agency.transit_processor)
91 return TemplateResponse(request, TEMPLATE_RETRY)
94@decorator_from_middleware(EligibleSessionRequired)
95def system_error(request):
96 """View handler for an enrollment system error."""
98 # overwrite origin so that CTA takes user to agency index
99 agency = session.agency(request)
100 session.update(request, origin=agency.index_url)
102 return TemplateResponse(request, TEMPLATE_SYSTEM_ERROR)
105@dataclass
106class EnrollmentSuccess:
107 success_message: str
108 thank_you_message: str
110 def dict(self):
111 return asdict(self)
114class DefaultEnrollmentSuccess(EnrollmentSuccess):
115 def __init__(self, transportation_type):
116 super().__init__(
117 success_message=_(
118 "You were not charged anything today. When boarding {transportation_type}, tap your contactless card and you "
119 "will be charged a reduced fare. You will need to re-enroll if you choose to change the card you use to "
120 "pay for transit service.",
121 transportation_type=transportation_type,
122 ),
123 thank_you_message=_("Thank you for using Cal-ITP Benefits!"),
124 )
127class AgencyCardEnrollmentSuccess(EnrollmentSuccess):
128 def __init__(self, transit_benefit, transportation_type):
129 super().__init__(
130 success_message=_(
131 "Your contactless card is now enrolled in {transit_benefit}. When boarding {transportation_type}, tap this "
132 "card and you will be charged a reduced fare. You will need to re-enroll if you choose to change the card you "
133 "use to pay for transit service.",
134 transit_benefit=transit_benefit,
135 transportation_type=transportation_type,
136 ),
137 thank_you_message=_("You were not charged anything today. Thank you for using Cal-ITP Benefits!"),
138 )
141class SuccessView(PageViewMixin, FlowSessionRequiredMixin, EligibleSessionRequiredMixin, TemplateView):
142 """View handler for the final success page."""
144 template_name = "enrollment/success.html"
146 def get_context_data(self, **kwargs):
147 context = super().get_context_data(**kwargs)
149 request = self.request
150 flow = self.flow
152 context = {"redirect_to": request.path}
153 copy = {
154 AgencySlug.CST.value: DefaultEnrollmentSuccess(
155 transportation_type=_("a CST bus"),
156 ),
157 SystemName.AGENCY_CARD.value: AgencyCardEnrollmentSuccess(
158 transit_benefit=_("a CST Agency Card transit benefit"), transportation_type=_("a CST bus")
159 ),
160 AgencySlug.EDCTA.value: DefaultEnrollmentSuccess(transportation_type=_("an EDCTA bus")),
161 AgencySlug.MST.value: DefaultEnrollmentSuccess(transportation_type=_("an MST bus")),
162 SystemName.COURTESY_CARD.value: AgencyCardEnrollmentSuccess(
163 transit_benefit=_("an MST Courtesy Card transit benefit"), transportation_type="an MST bus"
164 ),
165 AgencySlug.NEVCO.value: DefaultEnrollmentSuccess(transportation_type=_("a Nevada County Connects bus")),
166 AgencySlug.RABA.value: DefaultEnrollmentSuccess(transportation_type=_("a RABA bus")),
167 AgencySlug.SACRT.value: DefaultEnrollmentSuccess(transportation_type=_("a SacRT bus")),
168 AgencySlug.SBMTD.value: DefaultEnrollmentSuccess(transportation_type=_("an SBMTD bus")),
169 SystemName.REDUCED_FARE_MOBILITY_ID.value: AgencyCardEnrollmentSuccess(
170 transit_benefit=_("an SBMTD Reduced Fare Mobility ID transit benefit"), transportation_type=_("an SBMTD bus")
171 ),
172 AgencySlug.VCTC.value: DefaultEnrollmentSuccess(
173 transportation_type=_("a Ventura County Transportation Commission bus")
174 ),
175 }
177 if flow.uses_api_verification: 177 ↛ 178line 177 didn't jump to line 178 because the condition on line 177 was never true
178 copy_context = copy[flow.system_name].dict()
179 else:
180 copy_context = copy[flow.transit_agency.slug].dict()
182 context.update(copy_context)
184 return context
186 def get(self, request, *args, **kwargs):
187 session.update(request, origin=reverse(routes.ENROLLMENT_SUCCESS))
189 flow = self.flow
191 if session.logged_in(request) and flow.supports_sign_out:
192 # overwrite origin for a logged in user
193 # if they click the logout button, they are taken to the new route
194 session.update(request, origin=reverse(routes.LOGGED_OUT))
196 return super().get(request, *args, **kwargs)