From 57f237eee3aaad674d5a529ddd9776acbeb3792a Mon Sep 17 00:00:00 2001 From: Mae Beale Date: Mon, 2 Nov 2026 20:10:17 -0500 Subject: [PATCH 1/2] Keep intends-to-pay CE on timesheets past the payment deadline Staff track sign-ins from the "Paid or intends to pay" timesheet view, but registrants marked intending to pay vanished from it once the CE payment deadline passed, even though they can still open their sign-in sheet. The default view now lists exactly who can sign in again; the dashboard and revenue report keep dropping lapsed CE. Co-Authored-By: Claude Opus 5.5 (1M context) --- app/services/event_attendance_report.rb | 5 +++-- db/seeds/features/2026-11-02-ce-reports-drop-lapsed-ce.yml | 6 +++--- spec/services/event_attendance_report_spec.rb | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/services/event_attendance_report.rb b/app/services/event_attendance_report.rb index 14ebce83b9..9008da8025 100644 --- a/app/services/event_attendance_report.rb +++ b/app/services/event_attendance_report.rb @@ -38,11 +38,12 @@ def hours_awarded end def paying? - ce_registration.nil? || (ce_registration.counted? && ce_registration.payment_access_granted?) + ce_registration.nil? || ce_registration.access_granted? end end - # `paying_only:` narrows the CE report to counted licences paid or intending to pay. + # `paying_only:` narrows the CE report to licences that can sign in: paid or intending to + # pay and not denied, even past the CE payment deadline. def initialize(event, ce_only: false, paying_only: false) @event = event @ce_only = ce_only diff --git a/db/seeds/features/2026-11-02-ce-reports-drop-lapsed-ce.yml b/db/seeds/features/2026-11-02-ce-reports-drop-lapsed-ce.yml index 3d87bfc483..336b77e654 100644 --- a/db/seeds/features/2026-11-02-ce-reports-drop-lapsed-ce.yml +++ b/db/seeds/features/2026-11-02-ce-reports-drop-lapsed-ce.yml @@ -6,9 +6,9 @@ pr_number: 2934 action_path: "/events" summary: >- Once an event's CE payment deadline passes, CE that isn't paid in full stops - counting in reports: the CE timesheets, the dashboard's CE fees card and CE - pie, and the revenue report. Denied CE is left out the same way. + counting in reports: the dashboard's CE fees card and CE pie, and the revenue + report. Denied CE is left out the same way. pro_tips: - "Money already collected still counts as paid, even on CE that stopped counting." - - "Intending to pay keeps CE on the timesheets only until the deadline." + - "CE timesheets still list CE marked intending to pay after the deadline, since those registrants can still sign in." - "Events without a CE payment deadline keep counting unpaid CE." diff --git a/spec/services/event_attendance_report_spec.rb b/spec/services/event_attendance_report_spec.rb index 29b07e43aa..c7d9f20e51 100644 --- a/spec/services/event_attendance_report_spec.rb +++ b/spec/services/event_attendance_report_spec.rb @@ -182,10 +182,10 @@ def row_for(registration) expect(report.all_rows.map(&:license_number)).to eq(%w[AAA111 BBB222]) end - it "drops a licence still intending to pay once the CE payment deadline passes" do + it "keeps a licence intending to pay after the CE payment deadline passes" do bob.continuing_education_registrations.first.update!(intends_to_pay: true) event.update!(ce_payment_due_deadline: 1.day.ago) - expect(described_class.new(event, ce_only: true, paying_only: true).rows.map(&:license_number)).to eq([ "AAA111" ]) + expect(described_class.new(event, ce_only: true, paying_only: true).rows.map(&:license_number)).to eq(%w[AAA111 BBB222]) end it "keeps a person's paid licence when their second is unpaid" do From df63c6fc1b39c4adb634a16fa08ca64452f5933c Mon Sep 17 00:00:00 2001 From: Mae Beale Date: Mon, 2 Nov 2026 20:22:23 -0500 Subject: [PATCH 2/2] Cover intends-to-pay CE past the deadline on both timesheet pages The service spec alone didn't prove what staff see: these request specs load the per-event and cross-event timesheets and fail without the filter fix. Co-Authored-By: Claude Opus 5.5 (1M context) --- spec/requests/events/attendance_spec.rb | 14 ++++++++++++++ spec/requests/events/signins_spec.rb | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/spec/requests/events/attendance_spec.rb b/spec/requests/events/attendance_spec.rb index 7dc2b8578b..51bd4ae2b1 100644 --- a/spec/requests/events/attendance_spec.rb +++ b/spec/requests/events/attendance_spec.rb @@ -56,6 +56,20 @@ def log_ce_time! expect(response.body).to include("AAA111", "UNPAID1") end + it "keeps CE intending to pay on the default view after the CE payment deadline passes" do + event.update!(ce_payment_due_deadline: 1.day.ago) + intending = create(:professional_license, person: registration.registrant, number: "INTENDS1") + create(:continuing_education_registration, event_registration: registration, professional_license: intending, intends_to_pay: true) + unpaid = create(:professional_license, person: registration.registrant, number: "UNPAID1") + create(:continuing_education_registration, event_registration: registration, professional_license: unpaid) + + get attendance_event_path(event, ce: "true") + expect(response.body).to include("INTENDS1") + expect(response.body).not_to include("UNPAID1") + toggle = Capybara.string(response.body).find("nav[aria-label='CE payment filter']") + expect(toggle).to have_link("Paid or intends to pay (1)") + end + it "says when nobody has paid or intends to pay for CE yet" do license = create(:professional_license, person: registration.registrant, number: "UNPAID1") create(:continuing_education_registration, event_registration: registration, professional_license: license) diff --git a/spec/requests/events/signins_spec.rb b/spec/requests/events/signins_spec.rb index b9d08fabb6..46c3a33e03 100644 --- a/spec/requests/events/signins_spec.rb +++ b/spec/requests/events/signins_spec.rb @@ -102,6 +102,20 @@ def header_text expect(Capybara.string(response.body)).to have_link(href: attendance_event_path(ce_event, ce: "true", ce_payment: "all", return_to: "signins")) end + it "keeps CE intending to pay on the default view after the CE payment deadline passes" do + ce_event = training(title: "CE training", abbreviation: "CE-1", ce: true) + ce_event.update!(ce_payment_due_deadline: 1.day.ago) + log_time!(ce_event, name: "Erin", ce: true, paid: false) + .continuing_education_registrations.first.update!(intends_to_pay: true) + log_time!(ce_event, name: "Dana", ce: true, paid: false) + + get signins_events_path + expect(response.body).to include("LIC-Erin") + expect(response.body).not_to include("LIC-Dana") + toggle = Capybara.string(response.body).find("nav[aria-label='CE payment filter']") + expect(toggle).to have_link("Paid or intends to pay (1)") + end + it "says when no CE registration in scope is paid or intending to pay yet" do log_time!(training(title: "CE training", abbreviation: "CE-1", ce: true), name: "Dana", ce: true, paid: false) get signins_events_path