diff --git a/benefits/core/migrations/0027_enrollmentflow_supported_methods.py b/benefits/core/migrations/0027_enrollmentflow_supported_methods.py new file mode 100644 index 000000000..038c2a32d --- /dev/null +++ b/benefits/core/migrations/0027_enrollmentflow_supported_methods.py @@ -0,0 +1,24 @@ +# Generated by Django 5.1 on 2024-09-19 20:45 + +import multiselectfield.db.fields +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("core", "0026_enrollmentevent"), + ] + + operations = [ + migrations.AddField( + model_name="enrollmentflow", + name="supported_enrollment_methods", + field=multiselectfield.db.fields.MultiSelectField( + choices=[("digital", "Digital"), ("in_person", "In-person")], + default=["digital", "in_person"], + help_text="If the flow is supported by digital enrollment, in-person enrollment, or both", + max_length=50, + ), + ), + ] diff --git a/benefits/core/migrations/local_fixtures.json b/benefits/core/migrations/local_fixtures.json index 48994f3e4..f23679537 100644 --- a/benefits/core/migrations/local_fixtures.json +++ b/benefits/core/migrations/local_fixtures.json @@ -61,7 +61,8 @@ "selection_label_template": "eligibility/includes/selection-label--senior.html", "eligibility_start_template": "eligibility/start--senior.html", "claims_scope": "verify:senior", - "claims_claim": "senior" + "claims_claim": "senior", + "supported_enrollment_methods": ["digital", "in_person"] } }, { @@ -77,7 +78,8 @@ "selection_label_template": "eligibility/includes/selection-label--veteran.html", "eligibility_start_template": "eligibility/start--veteran.html", "claims_scope": "verify:veteran", - "claims_claim": "veteran" + "claims_claim": "veteran", + "supported_enrollment_methods": ["digital", "in_person"] } }, { @@ -101,7 +103,8 @@ "eligibility_start_template": "eligibility/start--cst-agency-card.html", "eligibility_form_class": "benefits.eligibility.forms.CSTAgencyCard", "eligibility_unverified_template": "eligibility/unverified--cst-agency-card.html", - "help_template": "core/includes/help--cst-agency-card.html" + "help_template": "core/includes/help--cst-agency-card.html", + "supported_enrollment_methods": ["digital", "in_person"] } }, { @@ -122,7 +125,8 @@ "eligibility_start_template": "eligibility/start--calfresh.html", "help_template": "core/includes/help--calfresh.html", "claims_scope": "verify:calfresh", - "claims_claim": "calfresh" + "claims_claim": "calfresh", + "supported_enrollment_methods": ["digital", "in_person"] } }, { @@ -138,7 +142,35 @@ "selection_label_template": "eligibility/includes/selection-label--medicare.html", "eligibility_start_template": "eligibility/start--medicare.html", "claims_scope": "verify:medicare", - "claims_claim": "medicare" + "claims_claim": "medicare", + "supported_enrollment_methods": ["digital", "in_person"] + } + }, + { + "model": "core.enrollmentflow", + "pk": 6, + "fields": { + "system_name": "in_person_only", + "label": "(CST) In-person Only", + "group_id": "group123", + "supported_enrollment_methods": ["in_person"] + } + }, + { + "model": "core.enrollmentflow", + "pk": 7, + "fields": { + "system_name": "digital_only", + "label": "(CST) Digital Only", + "group_id": "group123", + "enrollment_success_template": "enrollment/success--cst.html", + "display_order": 5, + "claims_provider": 1, + "selection_label_template": "eligibility/includes/selection-label--senior.html", + "eligibility_start_template": "eligibility/start--senior.html", + "claims_scope": "verify:senior", + "claims_claim": "senior", + "supported_enrollment_methods": ["digital"] } }, { diff --git a/benefits/core/models.py b/benefits/core/models.py index c4e1cdee3..8eb869331 100644 --- a/benefits/core/models.py +++ b/benefits/core/models.py @@ -18,6 +18,7 @@ from benefits.routes import routes from benefits.secrets import NAME_VALIDATOR, get_secret_by_name +from multiselectfield import MultiSelectField logger = logging.getLogger(__name__) @@ -104,6 +105,17 @@ def __str__(self) -> str: return self.client_name +class EnrollmentMethods: + DIGITAL = "digital" + IN_PERSON = "in_person" + + +SUPPORTED_METHODS = ( + (EnrollmentMethods.DIGITAL, EnrollmentMethods.DIGITAL.capitalize()), + (EnrollmentMethods.IN_PERSON, EnrollmentMethods.IN_PERSON.replace("_", "-").capitalize()), +) + + class EnrollmentFlow(models.Model): """Represents a user journey through the Benefits app for a single eligibility type.""" @@ -216,6 +228,13 @@ class EnrollmentFlow(models.Model): enrollment_success_template = models.TextField( default="enrollment/success.html", help_text="Template for a successful enrollment associated with the enrollment flow" ) + supported_enrollment_methods = MultiSelectField( + choices=SUPPORTED_METHODS, + max_choices=2, + max_length=50, + default=[EnrollmentMethods.DIGITAL, EnrollmentMethods.IN_PERSON], + help_text="If the flow is supported by digital enrollment, in-person enrollment, or both", + ) class Meta: ordering = ["display_order"] @@ -437,11 +456,6 @@ def for_user(user: User): return None -class EnrollmentMethods: - DIGITAL = "digital" - IN_PERSON = "in_person" - - class EnrollmentEvent(models.Model): """A record of a successful enrollment.""" diff --git a/pyproject.toml b/pyproject.toml index c960d9b11..d8513f858 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,7 @@ dependencies = [ "requests==2.32.3", "sentry-sdk==2.14.0", "six==1.16.0", + "django-multiselectfield==0.1.13", ] [project.optional-dependencies] diff --git a/tests/pytest/core/test_models.py b/tests/pytest/core/test_models.py index 3f5a3fbf6..fa68fa1ab 100644 --- a/tests/pytest/core/test_models.py +++ b/tests/pytest/core/test_models.py @@ -186,6 +186,13 @@ def test_EnrollmentFlow_enrollment_success_template(): assert new_flow.enrollment_success_template == "enrollment/success.html" +@pytest.mark.django_db +def test_EnrollmentFlow_supported_enrollment_methods(): + new_flow = EnrollmentFlow.objects.create() + + assert new_flow.supported_enrollment_methods == ["digital", "in_person"] + + class SampleFormClass: """A class for testing EligibilityVerificationForm references."""