Skip to content

Refactor: Globalize enrollment flows - #3509

Merged
Scotchester merged 11 commits into
mainfrom
refactor/global-enrollment-flows
Feb 26, 2026
Merged

Refactor: Globalize enrollment flows#3509
Scotchester merged 11 commits into
mainfrom
refactor/global-enrollment-flows

Conversation

@Scotchester

@Scotchester Scotchester commented Feb 20, 2026

Copy link
Copy Markdown
Member

Closes #3502


I think this is most of the work that needs to be done, though we still need to figure out how to handle that bad record in the prod DB. (I left in some print statements I was using to debug that.)

As discussed, it turned out to be a bit more wide-reaching change than we thought, but in the end, I think the changes are pretty straightforward. I broke them up into 3 fairly isolated commits.

I haven't touched the tests at all yet, and they'll certainly need some major updates.

Successful local tests all the way through enrollment:

  • CST Older Adult / Littlepay
  • CST Courtesy Card / Littlepay
  • CST Older Adult / Switchio
  • CST Courtesy Card / Switchio

@github-actions github-actions Bot added migrations [auto] Review for potential model changes/needed data migrations updates back-end Django views, sessions, middleware, models, migrations etc. labels Feb 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This PR adds a Django migration without updating any fixtures. Is a corresponding fixture update needed?

@Scotchester
Scotchester force-pushed the refactor/global-enrollment-flows branch from e53ef82 to f9de81d Compare February 23, 2026 18:59
@github-actions

Copy link
Copy Markdown
Contributor

This PR updates local_fixtures.json. After merging, please be sure to make a corresponding update to the fixtures with secrets in our shared LastPass folder and let other developers know that they should update their local fixtures accordingly.

@Scotchester
Scotchester force-pushed the refactor/global-enrollment-flows branch from 4ed611a to c13bc05 Compare February 23, 2026 22:14
@Scotchester

Copy link
Copy Markdown
Member Author

I've pushed a commit that resolves almost all of the failing tests, but there are a few that remain that I'd like to discuss with the team. It's a series of five tests shown here:

https://github.com/cal-itp/benefits/actions/runs/22327070430/job/64600237234?pr=3509

test_enroll_success_flow_does_not_support_expiration_no_expiry
test_enroll_success_flow_supports_expiration
test_enroll_success_flow_supports_expiration_no_expiry
test_enroll_success_flow_supports_expiration_is_expired
test_enroll_success_flow_supports_expiration_is_within_reenrollment_window

The proximate cause is clear – they are trying to get the group_id from the test's EnrollmentFlow, which is no longer possible. I could simply get the group_id from model_LittlepayGroup, but then these tests' model_EnrollmentFlow_supports_expiration/model_EnrollmentFlow_does_not_support_expiration would be unused, which seems odd when the purpose of them is to test various expiring/not-expiring conditions.

Maybe that's OK, because they're also bringing in things like mocked_group_funding_source_with_expiry and mocked_session_enrollment_expiry, which seem to be doing the real work in these tests, but it still feels a little odd if we're not actually testing against an EnrollmentFlow that is configured with(out) expiration.

Or maybe we need some new/different assertions in these tests that factor in the EnrollmentFlow's expiration fields?

Other than those tests, I think this is ready for initial review!

@Scotchester
Scotchester marked this pull request as ready for review February 23, 2026 22:38
@Scotchester
Scotchester requested a review from a team as a code owner February 23, 2026 22:38
@Scotchester Scotchester changed the title [WIP] Refactor: Globalize enrollment flows Refactor: Globalize enrollment flows Feb 23, 2026
@thekaveman

Copy link
Copy Markdown
Member

I've pushed a commit that resolves almost all of the failing tests, but there are a few that remain that I'd like to discuss with the team. It's a series of five tests shown here:

https://github.com/cal-itp/benefits/actions/runs/22327070430/job/64600237234?pr=3509

test_enroll_success_flow_does_not_support_expiration_no_expiry
test_enroll_success_flow_supports_expiration
test_enroll_success_flow_supports_expiration_no_expiry
test_enroll_success_flow_supports_expiration_is_expired
test_enroll_success_flow_supports_expiration_is_within_reenrollment_window

The proximate cause is clear – they are trying to get the group_id from the test's EnrollmentFlow, which is no longer possible. I could simply get the group_id from model_LittlepayGroup, but then these tests' model_EnrollmentFlow_supports_expiration/model_EnrollmentFlow_does_not_support_expiration would be unused, which seems odd when the purpose of them is to test various expiring/not-expiring conditions.

Maybe that's OK, because they're also bringing in things like mocked_group_funding_source_with_expiry and mocked_session_enrollment_expiry, which seem to be doing the real work in these tests, but it still feels a little odd if we're not actually testing against an EnrollmentFlow that is configured with(out) expiration.

Or maybe we need some new/different assertions in these tests that factor in the EnrollmentFlow's expiration fields?

Other than those tests, I think this is ready for initial review!

We could consider modifying the asserts to check that session.update is called with the appropriate updates to expiration related data based on the flow. We may also want to use the fixture mocked_session_group (currently referenced but I think unused) instead of the model_LittlepayGroup directly, to make it more realistic.

@github-actions

github-actions Bot commented Feb 24, 2026

Copy link
Copy Markdown
Contributor

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  benefits/core
  mixins.py
  session.py
  benefits/core/admin
  enrollment.py
  benefits/core/models
  enrollment.py
  transit.py
  benefits/eligibility
  views.py
  benefits/enrollment
  enrollment.py
  views.py
  benefits/enrollment_littlepay
  enrollment.py
  models.py
  benefits/enrollment_switchio
  enrollment.py
  models.py
  views.py
Project Total  

This report was generated by python-coverage-comment-action

@Scotchester

Copy link
Copy Markdown
Member Author

We could consider modifying the asserts to check that session.update is called with the appropriate updates to expiration related data based on the flow. We may also want to use the fixture mocked_session_group (currently referenced but I think unused) instead of the model_LittlepayGroup directly, to make it more realistic.

Thanks for the pointer in the right direction! The latest commit updates these tests. LMK what you think.

@Scotchester
Scotchester marked this pull request as draft February 24, 2026 19:20
@Scotchester

Copy link
Copy Markdown
Member Author

Oops, need to rebase now.

@Scotchester
Scotchester force-pushed the refactor/global-enrollment-flows branch 3 times, most recently from 4c8ad5b to 163e386 Compare February 24, 2026 22:27
@Scotchester

Copy link
Copy Markdown
Member Author

Now fully tested!

@Scotchester
Scotchester marked this pull request as ready for review February 24, 2026 22:33
@thekaveman

Copy link
Copy Markdown
Member

Taking a look! 👀

@thekaveman thekaveman left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking really good. Couple of thoughts/questions.

Comment thread benefits/core/migrations/0079_remove_enrollmentflow_transit_agency.py Outdated
Comment thread benefits/enrollment_switchio/enrollment.py Outdated
Comment thread tests/pytest/core/test_session.py
Comment thread tests/pytest/conftest.py Outdated
@Scotchester
Scotchester force-pushed the refactor/global-enrollment-flows branch from 9ad973d to ddbeaff Compare February 25, 2026 17:10
@Scotchester

Copy link
Copy Markdown
Member Author

@thekaveman Changes pushed!

@thekaveman thekaveman left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice work on this, what a huge refactor!

I was able to go through all the following flows successfully:

✅ CST / Medicare / Switchio
✅ CST / Older Adult / Switchio
✅ CST / Agency card / Switchio
✅ CST / Medicare / Littlepay
✅ CST / Older Adult / Littlepay
✅ CST / Agency card / Littlepay

The Admin UI looks pretty good OOTB on an agency page, for selecting the supported flows:

Image

It might be nicer if we can use checkboxes here instead of this default and kinda weird multi-select box that relies on color/highlighting, but that certainly isn't blocking.

I do want to clean up the large commented sections in the migration, and deal with the corrupted EnrollmentEvent record in prod (or at least have a solid plan) before merging though.

Comment thread benefits/core/migrations/0078_global_enrollmentflows.py Outdated
Comment thread benefits/core/migrations/local_fixtures.json Outdated
@Scotchester

Scotchester commented Feb 25, 2026

Copy link
Copy Markdown
Member Author

I do want to clean up the large commented sections in the migration, and deal with the corrupted EnrollmentEvent record in prod (or at least have a solid plan) before merging though.

Agree 💯

Here's the plan as I see it:

  1. Today
    1. I figure out how to actually delete this record. Whatever bug prevents filtering on this row also seems to prevent deleting it. Still experimenting locally.
    2. Once I can do that, I test re-adding the record via the Admin (on the main branch), then switch back to this branch and test the migration.
    3. Figured out how to repair the corruption via SQLite's recovery API and tested running the migration against it.
  2. Tomorrow morning at 9 a.m. ET / 6 a.m. PT
    1. I download the latest copy of the prod DB to my local machine
    2. I repair the DB using the SQLite recovery API
    3. I upload the modified DB back to the prod server, overwriting the previous file
    4. I verify that the previously corrupt EnrollmentEvent can now be loaded in the prod Admin.
  3. Tomorrow at 1:00 p.m. ET / 10: a.m. PT
    1. We do the deployment and the migration should run without incident

Does that seem right?

@thekaveman

Copy link
Copy Markdown
Member

I do want to clean up the large commented sections in the migration, and deal with the corrupted EnrollmentEvent record in prod (or at least have a solid plan) before merging though.

Agree 💯

Here's the plan as I see it:

...

Does that seem right?

Sounds great. Lemme know if you want to chat about fixing the corrupted record at all.

@Scotchester

Copy link
Copy Markdown
Member Author

Updated plan in my comment above based on the DB repair research just completed.

thekaveman
thekaveman previously approved these changes Feb 25, 2026

@thekaveman thekaveman left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

Comment thread tests/pytest/conftest.py Outdated

@pytest.fixture
def model_EnrollmentFlow(model_TransitAgency):
# def model_EnrollmentFlow(model_TransitAgency):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this line is (harmless) detritus

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another good catch, thanks!

3,
4,
5
],

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prettier collapses this line for me. did you bypass it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I just hadn't gotten this current with main, as you now realize :) Will clean it up.

@jgravois jgravois Feb 25, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry, now i understand why @thekaveman suggested that we just merge this and untangle in #3216 🍝

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iojk;hasdfiokl;hjasdfiohkl;asdfIoH;ASFIO;HKKASDFGhIkl;

I've rebased locally but Prettier is still behaving the same way. Which makes sense, because your PR didn't change anything about how VS Code handles Prettier, only pre-commit.

But still! pre-commit didn't yell at me either. I dunno, man.

I gotta run, but I'll try to figure it out in the morning.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'll pull down your branch and play with it but personally i don't have a problem merging this PR as-is and solving the mystery when i rebase #3216.

@thekaveman thekaveman Feb 25, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the main issue I'm trying to avoid is: Scott's local working branch is now significantly out of date with this one.

In the best case, he can git fetch && git reset --hard origin/refactor/global-enrollment-flows to align with these updates.

In the worst case, he has some local work (either still in progress or committed locally but not pushed) that has to be dealt with to get in sync with this newly rebased branch.

Even if the underlying changes of the commits are the same, rebasing is still destructive to the commit history (SHAs change). We should try to avoid introducing additional merge conflicts or merge work for our colleagues unless they ask for help, is what I'm ultimately trying to say.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A good point in general, Kegan. Can definitely be annoying if a branch gets rebased out from under you. Hopefully this is the last change on this PR and I won't even have to update my local branch, though :)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

understood. it was overzealous for me to -f push onto this branch without getting a greenlight from @Scotchester first. won't happen again 💂

@jgravois jgravois Feb 26, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

epilogue: @Scotchester and i were able to verify this morning that the inconsistency in formatting on his machine had nothing to do with #3520.

he had a VS Code user setting pertaining to .json files specifically that sidestepped prettier entirely in favor of 'JSON Language Features'.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😖

Scotchester and others added 10 commits February 25, 2026 22:42
- Add `enrollment_flows` many-to-many field to `TransitAgency`
- Add `transit_agency` FK to `EnrollmentGroup`, since groups will now need to keep track of which agency they belong to (since the flow they belong to no longer will)
- Convert `EnrollmentGroup.enrollment_flow` from `OneToOneField` to `ForeignKey`, since flows will now be used by more than one group
- Remove `group_id` property from `EnrollmentFlow` since they will no longer relate to a single group
- Update `EnrollmentFlow.clean()` to reflect that it no longer has a `group_id` and soon will not have a `transit_agency`
- Schema and data migration to handle all of the above
Since it can no longer be retrieved from a flow.
Including taking the agency name out of the selection label template name.
Co-authored-by: Kegan Maher <kegan@compiler.la>
@jgravois
jgravois force-pushed the refactor/global-enrollment-flows branch from 46bcff5 to d80877b Compare February 25, 2026 22:48

@thekaveman thekaveman left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀

@lalver1 lalver1 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, this was a big refactor! Great job @Scotchester!

The code changes look good to me, and I tested locally Older Adult and Courtesy Card and saw no issues 👍

I'm almost finished reviewing, but I did notice something with the migration. As part of looking into the corrupted database row issue, I made a copy of the prod database. Since I had that copy, I ran this PR's migration on it to get a better view of how this PR changes the data model at the SQL table level. After doing that I noticed that SacRT, MST, and SBMTD were missing the Medicare flow; and NevCo was missing the Veteran flow. I may be mistaken, and I'll double check by looking at the migration code again, but wanted to see if you had noticed this too.

@lalver1

lalver1 commented Feb 26, 2026

Copy link
Copy Markdown
Member

Sorry, it was my mistake 🤦‍♂️. I was using the test database so that's why the flows were missing from those agencies.

@Scotchester

Copy link
Copy Markdown
Member Author

@lalver1 Thank you for your diligent testing, regardless!

@Scotchester
Scotchester merged commit a1d4e97 into main Feb 26, 2026
19 checks passed
@Scotchester
Scotchester deleted the refactor/global-enrollment-flows branch February 26, 2026 14:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

back-end Django views, sessions, middleware, models, migrations etc. migrations [auto] Review for potential model changes/needed data migrations updates

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make Enrollment Flows global and eliminate ageny-specific flows

4 participants