Skip to content

Add scholarship decline flow with admin email and totals exclusion - #2178

Draft
maebeale wants to merge 9 commits into
mainfrom
maebeale/scholarship-decline-email
Draft

Add scholarship decline flow with admin email and totals exclusion#2178
maebeale wants to merge 9 commits into
mainfrom
maebeale/scholarship-decline-email

Conversation

@maebeale

@maebeale maebeale commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

🤖 suggested review level: 5 Inspect 🔬 new agreement state model + data migration + history model that alter financial totals across dashboards, grant budgets, revenue/scholarship reports, and many display surfaces

What is the goal of this PR and why is this important?

  • Recipients can say No to a scholarship agreement (with a reason) from their scholarship page, not just Agree. Declining emails the admin team an FYI, and removes the award from every total.
  • Agreement state is now a single tri-state status (pending → accepted → declined) instead of two hand-synced timestamps, so states can't contradict and reinstating is a one-field transition.
  • The accept ↔ decline back-and-forth is recorded as first-class history.
  • The scholarship page shows how much the registrant will owe after the scholarship is applied (event cost − all allocations).

How did you approach the change?

  • State model: replaced agreement_signed_at / agreement_declined_at / reason with agreement_response_status + agreement_responded_at + agreement_response_reason (data migration backfills existing rows). Predicates (agreement_signed?/agreement_declined?) are unchanged for the ~16 call sites that use them.
  • History: new ScholarshipAgreementResponse model — one append-only row per transition (status, reason, responder, amount at the time), seeded for existing decided awards. Shown as a collapsible timeline on the scholarship edit page.
  • Decline flow: native <details> reason box next to Agree; recording a decline zeroes the allocation (so all allocation-based totals self-correct) and appends a history row. New scholarship_agreement_declined_fyi Notification kind → NotificationMailer + views + preview.
  • Totals exclusion: .not_declined on the amount_cents/scope readers (grant budget/remaining, funding split, EventDashboard, EventScholarshipFigures, EventRevenueFigures, ScholarshipsGrouping, grant decorator counts, registrant badge).
  • Reinstate: editing the amount or ticking "Agreement signed" on a declined award cleanly reinstates it (clears the decline, re-funds the allocation). A scholarship-decline-guard Stimulus controller warns before either action discards a decline.
  • Declined shown everywhere: scholarships index, event registration card, grant scholarships table, event recipient card, onboarding row, admin show/edit.

Anything else to add?

  • Data migration replaces the agreement timestamps and adds scholarship_agreement_responses.
  • Note: the pre-push hook's bin/brakeman --ensure-latest fails only because installed Brakeman 8.0.5 is behind the just-released 8.0.6; bundle exec brakeman scans clean (0 warnings).

Copilot AI lite review requested due to automatic review settings August 12, 2026 16:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings August 12, 2026 17:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings August 13, 2026 09:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings August 13, 2026 10:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings August 13, 2026 11:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings August 13, 2026 11:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings August 13, 2026 11:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings August 13, 2026 12:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings August 13, 2026 12:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

maebeale and others added 9 commits August 13, 2026 11:45
Recipients can decline a scholarship agreement with a reason from their
scholarship page. Declining emails the admin team an FYI, records the
reason, zeroes the allocation so the award drops out of every total, and
shows a Declined badge everywhere scholarships appear. Editing the award
amount re-offers it and clears the decline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Editing the award amount re-offers the scholarship and discards the
recipient's recorded decline; warn the admin before that happens.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the two mutually-exclusive agreement timestamps with a single
agreement_response_status (pending/accepted/declined) + responded_at +
reason, so the states can't contradict and reinstating is a one-field
transition. Add a ScholarshipAgreementResponse audit log capturing each
accept/decline/re-offer, shown as a collapsible timeline on the edit page.
Signing a declined award now cleanly reinstates it; the decline guard warns
before either an amount change or a sign clears a decline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the redundant agreement_responded_at / agreement_response_reason
cache columns — the latest ScholarshipAgreementResponse is the source of
truth (only the status stays denormalized, since totals query it in SQL).
Link a decline's response row to the FYI notification it produced.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The agreement history preserves a decline through any reinstatement, so
the client-side "confirm before clearing a decline" guard is no longer
needed. Drop the Stimulus controller, its form hooks, and system spec.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Editing a declined award's amount no longer silently reactivates it.
Reinstating is now a deliberate server-side action: a Re-offer button
(button_to POST with Turbo's built-in confirm, no custom JS) sets the
award back to pending and re-funds the allocation. Consolidate the
declined banner onto the edit page beside the button.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…istration

On signing, email the recipient a confirmation with a link back to their
ticket and send the team an FYI (both once, on the first sign). The decline
page's "contact us" now links to the contact form threaded with the
registration, and that FYI email links the team to the registration.

Copy fixes and, when the decline reason box is open, hide the Agree button
via CSS :has() (no JS).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collapse the three iterative migrations (which added, reshaped, then trimmed
the agreement columns) into two clean ones: create scholarship_agreement_responses
(with the notification link), then replace agreement_signed_at with the tri-state
agreement_response_status — seeding each signed award's date onto an "accepted"
response row. Net schema is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
No signed agreements exist in production, so the status migration no longer
backfills or seeds history — it just adds agreement_response_status (default
pending) and drops agreement_signed_at. Zero DML.

Render the agreement history on the registrant scholarship callout too, but
only for admins and with sky "admin only" styling (registrants never see it).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@maebeale
maebeale force-pushed the maebeale/scholarship-decline-email branch from 7c8ef0a to bee8b4f Compare August 13, 2026 15:50
Copilot AI review requested due to automatic review settings August 13, 2026 15:50

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants