Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 56 additions & 13 deletions app/controllers/event_registrations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,10 @@ def link_organization
# duplicate data), so we surface them all rather than silently picking one.
@submitted_entries = registration_submission_entries(@event_registration)
@form_submission = @submitted_entries.first&.fetch(:submission)
# The "primary" submitted org/title β€” the first submission that named an org
# (else the first submission) β€” drives the suggested-match and comparison logic.
primary = @submitted_entries.find { |entry| entry[:org_name].present? } || @submitted_entries.first
# The "primary" submitted org/title β€” the newest submission that named an org
# (else the newest submission) β€” drives the suggested-match and comparison logic,
# and is the same submission whose details linking applies to the org.
primary = primary_submission_entry(@submitted_entries)
@submitted_org_name = primary && primary[:org_name]
@submitted_position = primary && primary[:position]
# Each distinct submitted org name that isn't already in the database gets its
Expand Down Expand Up @@ -219,6 +220,8 @@ def select_organization
training_date: @event_registration.event.start_date
)

apply_submitted_organization_attributes(organization, @event_registration)

@event_registration.event_registration_organizations
.find_or_create_by!(organization: organization)

Expand Down Expand Up @@ -251,6 +254,9 @@ def create_organization
job_title: submitted_position(@event_registration),
training_date: @event_registration.event.start_date
)

apply_submitted_organization_attributes(organization, @event_registration)

@event_registration.event_registration_organizations.find_or_create_by!(organization: organization)

notice = existing ? "#{organization.name} linked." : "#{organization.name} created and linked."
Expand Down Expand Up @@ -375,17 +381,21 @@ def csv_export(registrations)
end
end

# Each registration-form submission with the org name and position the registrant
# entered on it: [{ submission:, org_name:, position: }], oldest first.
# Each registration-form submission with the org details the registrant entered on
# it: [{ submission:, org_name:, position:, agency_type:, agency_website: }], oldest
# first.
def registration_submission_entries(registration)
form = registration.event.registration_form
return [] unless form

field_ids = form.form_fields
.where(field_identifier: %w[agency_name agency_position])
.where(field_identifier: %w[agency_name agency_position agency_city agency_type agency_website])
.pluck(:field_identifier, :id).to_h
name_field_id = field_ids["agency_name"]
position_field_id = field_ids["agency_position"]
city_field_id = field_ids["agency_city"]
type_field_id = field_ids["agency_type"]
website_field_id = field_ids["agency_website"]

entries = registration.registrant.form_submissions
.where(form: form)
Expand All @@ -396,7 +406,10 @@ def registration_submission_entries(registration)
{
submission: submission,
org_name: name_field_id && answers[name_field_id]&.submitted_answer,
position: position_field_id && answers[position_field_id]&.submitted_answer
position: position_field_id && answers[position_field_id]&.submitted_answer,
agency_city: city_field_id && answers[city_field_id]&.submitted_answer,
agency_type: type_field_id && answers[type_field_id]&.submitted_answer,
agency_website: website_field_id && answers[website_field_id]&.submitted_answer
}
end

Expand All @@ -418,13 +431,43 @@ def submitted_agency_names(registration)
.uniq { |name| name.downcase }
end

# The "primary" entry among a registrant's submission entries β€” the one whose
# details we apply to a linked org and surface in the editor. Not every form asks
# for organization info, so the newest submission overall may carry none; we pick
# the NEWEST submission that actually collected an org address (agency_city present
# β€” the same signal public_registration uses to build the address), falling back to
# the newest that named an org, then the newest overall. Entries come oldest-first,
# so we scan from the end. link_organization picks the same one, so what we apply
# matches the editor.
def primary_submission_entry(entries)
newest_first = entries.reverse
newest_first.find { |entry| entry[:agency_city].present? } ||

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

πŸ€– From Claude: Primary now keys on agency_city (the signal public_registration uses for whether a submission carries an org address), not org name β€” a later submission from a form that did not ask for org info no longer wins. Falls back to newest-named, then newest overall, so name-only or org-less data still degrades gracefully.

newest_first.find { |entry| entry[:org_name].present? } ||
newest_first.first
end

# The job title/position the registrant typed for their organization on the
# registration form. Uses the same "primary" submission as link_organization
# (the first submission that named an org, else the first), so the title applied
# when linking matches what the editor shows.
# registration form, from the primary submission.
def submitted_position(registration)
entries = registration_submission_entries(registration)
primary = entries.find { |entry| entry[:org_name].present? } || entries.first
primary && primary[:position]
primary_submission_entry(registration_submission_entries(registration))&.dig(:position)
end

# The Organization Type and Website the registrant typed on the registration form,
# as Organization attributes ({ agency_type:, website_url: }) β€” only the values the
# registrant actually provided, so a blank answer never clobbers a curated value.
def submitted_organization_attributes(registration)
entry = primary_submission_entry(registration_submission_entries(registration))
return {} unless entry

{ agency_type: entry[:agency_type], website_url: entry[:agency_website] }
.select { |_, value| value.present? }

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

πŸ€– From Claude: This presence filter is the one judgment call: we override curated agency_type/website_url with the registrant's answer, but a blank answer is dropped so it can't wipe a curated value. If you ever want new-org vs. existing-org to differ (e.g. backfill-only for existing), this is the seam to branch on.

end

# Overwrite the org's Organization Type and Website with what the registrant typed.
# We intentionally override curated values here (admins asked for the registrant's
# answer to win); every change is captured as an Ahoy lifecycle event.
def apply_submitted_organization_attributes(organization, registration)
attributes = submitted_organization_attributes(registration)
organization.update!(attributes) if attributes.any?
end
end
108 changes: 108 additions & 0 deletions spec/requests/event_registrations_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,96 @@ def details_open?(body, heading)
expect(regular_user.person.affiliations.where(organization: organization).pluck(:title))
.to contain_exactly("Facilitator")
end

it "applies the submitted organization type and website to the linked org" do
reg_form = create(:form, name: "Reg form")
type_field = create(:form_field, form: reg_form, field_identifier: "agency_type")
website_field = create(:form_field, form: reg_form, field_identifier: "agency_website")
create(:event_form, :registration, event: event, form: reg_form)
submission = create(:form_submission, person: regular_user.person, form: reg_form)
create(:form_answer, form_submission: submission, form_field: type_field, submitted_answer: "For-profit")
create(:form_answer, form_submission: submission, form_field: website_field, submitted_answer: "example.org")

post select_organization_event_registration_path(existing_registration),
params: { organization_id: organization.id }

expect(organization.reload).to have_attributes(agency_type: "For-profit", website_url: "example.org")
end

it "overrides the existing org's curated type and website with the submitted values" do
organization.update!(agency_type: "Government agency", website_url: "curated.org")
reg_form = create(:form, name: "Reg form")
type_field = create(:form_field, form: reg_form, field_identifier: "agency_type")
website_field = create(:form_field, form: reg_form, field_identifier: "agency_website")
create(:event_form, :registration, event: event, form: reg_form)
submission = create(:form_submission, person: regular_user.person, form: reg_form)
create(:form_answer, form_submission: submission, form_field: type_field, submitted_answer: "For-profit")
create(:form_answer, form_submission: submission, form_field: website_field, submitted_answer: "new.org")

post select_organization_event_registration_path(existing_registration),
params: { organization_id: organization.id }

expect(organization.reload).to have_attributes(agency_type: "For-profit", website_url: "new.org")
end

it "applies the newest submission's type and website when there are several" do
reg_form = create(:form, name: "Reg form")
name_field = create(:form_field, form: reg_form, field_identifier: "agency_name")
type_field = create(:form_field, form: reg_form, field_identifier: "agency_type")
website_field = create(:form_field, form: reg_form, field_identifier: "agency_website")
create(:event_form, :registration, event: event, form: reg_form)

older = create(:form_submission, person: regular_user.person, form: reg_form, created_at: 2.days.ago)
create(:form_answer, form_submission: older, form_field: name_field, submitted_answer: "Helping Hands")
create(:form_answer, form_submission: older, form_field: type_field, submitted_answer: "Government agency")
create(:form_answer, form_submission: older, form_field: website_field, submitted_answer: "old.org")
newer = create(:form_submission, person: regular_user.person, form: reg_form, created_at: 1.day.ago)
create(:form_answer, form_submission: newer, form_field: name_field, submitted_answer: "Helping Hands")
create(:form_answer, form_submission: newer, form_field: type_field, submitted_answer: "For-profit")
create(:form_answer, form_submission: newer, form_field: website_field, submitted_answer: "new.org")

post select_organization_event_registration_path(existing_registration),
params: { organization_id: organization.id }

expect(organization.reload).to have_attributes(agency_type: "For-profit", website_url: "new.org")
end

it "skips a newer submission that collected no org address, using the newest one that did" do
reg_form = create(:form, name: "Reg form")
city_field = create(:form_field, form: reg_form, field_identifier: "agency_city")
type_field = create(:form_field, form: reg_form, field_identifier: "agency_type")
website_field = create(:form_field, form: reg_form, field_identifier: "agency_website")
create(:event_form, :registration, event: event, form: reg_form)

# Older submission collected an org address (and its type/website).
with_address = create(:form_submission, person: regular_user.person, form: reg_form, created_at: 2.days.ago)
create(:form_answer, form_submission: with_address, form_field: city_field, submitted_answer: "Seattle")
create(:form_answer, form_submission: with_address, form_field: type_field, submitted_answer: "For-profit")
create(:form_answer, form_submission: with_address, form_field: website_field, submitted_answer: "right.org")
# Newer submission from a form that didn't ask for org info.
no_address = create(:form_submission, person: regular_user.person, form: reg_form, created_at: 1.day.ago)
create(:form_answer, form_submission: no_address, form_field: type_field, submitted_answer: "Government agency")
create(:form_answer, form_submission: no_address, form_field: website_field, submitted_answer: "wrong.org")

post select_organization_event_registration_path(existing_registration),
params: { organization_id: organization.id }

expect(organization.reload).to have_attributes(agency_type: "For-profit", website_url: "right.org")
end

it "leaves a curated value untouched when the registrant left that answer blank" do
organization.update!(agency_type: "Government agency", website_url: "curated.org")
reg_form = create(:form, name: "Reg form")
website_field = create(:form_field, form: reg_form, field_identifier: "agency_website")
create(:event_form, :registration, event: event, form: reg_form)
submission = create(:form_submission, person: regular_user.person, form: reg_form)
create(:form_answer, form_submission: submission, form_field: website_field, submitted_answer: "new.org")

post select_organization_event_registration_path(existing_registration),
params: { organization_id: organization.id }

expect(organization.reload).to have_attributes(agency_type: "Government agency", website_url: "new.org")
end
end

describe "POST /event_registrations/:id/create_organization" do
Expand Down Expand Up @@ -711,6 +801,24 @@ def details_open?(body, heading)
expect(existing_registration.organizations.pluck(:name)).not_to include("Alpha Agency")
end

it "applies the submitted organization type and website to the new org" do
create(:organization_status, name: "Active")
reg_form = create(:form, name: "Reg form")
name_field = create(:form_field, form: reg_form, field_identifier: "agency_name")
type_field = create(:form_field, form: reg_form, field_identifier: "agency_type")
website_field = create(:form_field, form: reg_form, field_identifier: "agency_website")
create(:event_form, :registration, event: event, form: reg_form)
submission = create(:form_submission, person: regular_user.person, form: reg_form)
create(:form_answer, form_submission: submission, form_field: name_field, submitted_answer: "Brand New Org")
create(:form_answer, form_submission: submission, form_field: type_field, submitted_answer: "501c3/nonprofit")
create(:form_answer, form_submission: submission, form_field: website_field, submitted_answer: "brandnew.org")

post create_organization_event_registration_path(existing_registration)

expect(Organization.find_by(name: "Brand New Org"))
.to have_attributes(agency_type: "501c3/nonprofit", website_url: "brandnew.org")
end

it "rejects creating an org name the registrant didn't submit" do
create(:organization_status, name: "Active")
reg_form = create(:form, name: "Reg form")
Expand Down