Coverage for benefits/core/admin/enrollment.py: 97%
56 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-08 19:50 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-08 19:50 +0000
1from adminsortable2.admin import SortableAdminMixin
2from django import forms
3from django.contrib import admin
4from django.core.exceptions import ValidationError
6from benefits.core import models
7from benefits.core.models.common import template_path
8from benefits.core.models.enrollment import EnrollmentFlow
10from .mixins import ProdReadOnlyPermissionMixin, StaffPermissionMixin, SuperuserPermissionMixin
13@admin.register(models.EnrollmentEvent)
14class EnrollmentEventAdmin(ProdReadOnlyPermissionMixin, admin.ModelAdmin):
15 list_display = ("enrollment_datetime", "transit_agency", "enrollment_flow", "enrollment_method", "verified_by")
16 ordering = ("-enrollment_datetime",)
19@admin.register(models.EligibilityApiVerificationRequest)
20class EligibilityApiVerificationRequestAdmin(SuperuserPermissionMixin, admin.ModelAdmin):
21 list_display = ("label", "api_url")
24class EnrollmentFlowForm(forms.ModelForm):
25 def has_field(self, field_name):
26 return self.fields.get(field_name) is not None
28 def get(self, cleaned_data, field_name):
29 """
30 If the field is present on the form, simply get the value from the cleaned_data.
32 If the field is not present on the form, that means the user doesn't have access to the field,
33 so get the value from the form's instance of the object.
34 """
35 return cleaned_data.get(field_name) if self.has_field(field_name) else getattr(self.instance, field_name)
37 def clean(self):
38 cleaned_data = super().clean()
40 field_errors = {}
41 non_field_errors = []
43 supports_expiration = cleaned_data.get("supports_expiration")
45 if supports_expiration:
46 expiration_days = cleaned_data.get("expiration_days")
47 expiration_reenrollment_days = cleaned_data.get("expiration_reenrollment_days")
49 message = "When support_expiration is True, this value must be greater than 0."
50 if expiration_days is None or expiration_days <= 0: 50 ↛ 52line 50 didn't jump to line 52 because the condition on line 50 was always true
51 field_errors.update(expiration_days=ValidationError(message))
52 if expiration_reenrollment_days is None or expiration_reenrollment_days <= 0: 52 ↛ 56line 52 didn't jump to line 56 because the condition on line 52 was always true
53 field_errors.update(expiration_reenrollment_days=ValidationError(message))
55 # these fields might not be on the form, so use helper method to correctly get the value
56 eligibility_api_request = self.get(cleaned_data, "api_request")
57 claims_request = self.get(cleaned_data, "claims_request")
59 supported_enrollment_methods = self.get(cleaned_data, "supported_enrollment_methods")
60 supports_self_service = (
61 supported_enrollment_methods and models.EnrollmentMethods.SELF_SERVICE in supported_enrollment_methods
62 )
64 if supports_self_service and not (claims_request or eligibility_api_request):
65 message = "Must configure either claims verification or Eligibility API verification."
66 non_field_errors.append(ValidationError(message))
68 system_name = self.get(cleaned_data, "system_name")
69 selection_label_template_path = EnrollmentFlow.get_selection_label_template(system_name)
70 if supports_self_service and not template_path(selection_label_template_path):
71 # we can't add a field-level validation error
72 # because the actual template for the self-service flow is derived from a pattern
73 non_field_errors.append(ValidationError(f"Template not found: {selection_label_template_path}"))
75 supports_in_person = (
76 supported_enrollment_methods and models.EnrollmentMethods.IN_PERSON in supported_enrollment_methods
77 )
78 in_person_policy = self.get(cleaned_data, "in_person_policy")
79 if supports_in_person and not in_person_policy:
80 field_errors.update(
81 in_person_policy=ValidationError(
82 "If the flow intends to support In-person Enrollment, it must be configured with associated policy language." # noqa: E501
83 )
84 )
86 for field_name, validation_error in field_errors.items():
87 self.add_error(field_name, validation_error)
88 for validation_error in non_field_errors:
89 self.add_error(None, validation_error)
92@admin.register(models.EnrollmentFlow)
93class SortableEnrollmentFlowAdmin(StaffPermissionMixin, SortableAdminMixin, admin.ModelAdmin):
94 list_display = ("label", "supported_enrollment_methods")
95 form = EnrollmentFlowForm