From b31e0d8de0681e9da064c7f4bf7aa3279b300af1 Mon Sep 17 00:00:00 2001 From: maebeale Date: Sun, 21 Jun 2026 23:48:29 -0400 Subject: [PATCH] Populate an org from the registrant's form on the linking page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When linking/creating an org from a registrant's submission on the org-linking page, optionally apply the org info the registrant typed, gated by a per-form "populate" toggle (zero JS): - Create-and-link (new org) and suggested matches default the toggle on (very likely the registrant's org); the manual search box defaults off (often an unrelated established org). - When applying: website and organization type overwrite the org's values (each logged to Ahoy update.organization); the address is additive — added as a new primary, or a same-street record refreshed instead of duplicated; demoted addresses stay active. - Create-and-link with the toggle off creates the org bare; an existing same-name org is only linked unless the toggle is on. Organization type is set via the OrganizationType association (PR #1886): the submitted type name is resolved to an OrganizationType record (case-insensitive). Website stored as typed (#1765). Phone is not populated (no org phone field). Also fixes a latent bug: PublicRegistration passed nil street/zip to NOT NULL address columns, 500-ing when a registrant filled a city but no zip; coerce to "". Co-Authored-By: Claude Opus 4.8 --- .../event_registrations_controller.rb | 147 +++++++++++- .../public_registration.rb | 16 +- .../_apply_form_details_checkbox.html.erb | 11 + .../link_organization.html.erb | 63 +++-- spec/requests/event_registrations_spec.rb | 227 ++++++++++++++++-- .../public_registration_spec.rb | 23 ++ 6 files changed, 430 insertions(+), 57 deletions(-) create mode 100644 app/views/event_registrations/_apply_form_details_checkbox.html.erb diff --git a/app/controllers/event_registrations_controller.rb b/app/controllers/event_registrations_controller.rb index 83f5022b71..48fb90917a 100644 --- a/app/controllers/event_registrations_controller.rb +++ b/app/controllers/event_registrations_controller.rb @@ -215,6 +215,12 @@ def select_organization @person = @event_registration.registrant organization = Organization.find(params[:organization_id]) + # Apply the org details the form captured only when the admin opted in — linking an + # established org shouldn't silently rewrite its address/website. + entries = registration_submission_entries(@event_registration) + answers = submitted_agency_answers(entries, organization.name) + apply_submitted_org_details(organization, answers) if params[:apply_form_details] == "1" + AffiliationServices::CreateFromRegistration.call( person: @person, organization: organization, @@ -236,7 +242,8 @@ def create_organization # button can't be used to create an arbitrary org — it only resolves a pending # submitted name. A specific name is passed when there are several submissions; # otherwise default to the first submitted name. - submitted_names = submitted_agency_names(@event_registration) + entries = registration_submission_entries(@event_registration) + submitted_names = entries.filter_map { |entry| entry[:org_name].presence&.strip }.uniq { |name| name.downcase } requested = params[:organization_name].presence name = requested ? submitted_names.find { |submitted| submitted.casecmp?(requested) } : submitted_names.first if name.blank? @@ -244,9 +251,24 @@ def create_organization return end - # Reuse an existing org with that name rather than creating a duplicate. + # The other org details the registrant typed on the same submission, applied when the + # admin leaves the populate toggle on (checked by default for create-and-link). + answers = submitted_agency_answers(entries, name) + apply = params[:apply_form_details] == "1" + + # Reuse an existing org with that name rather than creating a duplicate. A new org is + # built from the form when populating, else created bare; an existing org is populated + # only when the toggle is on (never silently). existing = Organization.where("LOWER(name) = ?", name.strip.downcase).first - organization = existing || Organization.create!(name: name.strip, organization_status: OrganizationStatus.find_by(name: "Active")) + organization = + if existing + existing + elsif apply + build_organization_from_answers(name, answers) + else + Organization.create!(name: name.strip, organization_status: OrganizationStatus.find_by(name: "Active")) + end + apply_submitted_org_details(organization, answers) if existing && apply AffiliationServices::CreateFromRegistration.call( person: @person, @@ -414,12 +436,15 @@ def registration_submission_entries(registration) entries end - # Distinct, non-blank org names the registrant typed across their registration-form - # submissions (case-insensitive dedupe, first spelling wins). - def submitted_agency_names(registration) - registration_submission_entries(registration) - .filter_map { |entry| entry[:org_name].presence&.strip } - .uniq { |name| name.downcase } + # The agency answers (org details + position) the registrant submitted for this org, + # keyed by field_identifier. Uses the submission that named this org, else the first + # that named any org (registrants normally have a single submission), so a resolved + # org and affiliation can carry what they typed. {} when nothing was submitted. + def submitted_agency_answers(entries, name) + entry = entries.find { |e| e[:org_name].present? && e[:org_name].strip.casecmp?(name.to_s.strip) } || + entries.find { |e| e[:org_name].present? } || + entries.first + entry&.fetch(:submission)&.answers_by_identifier || {} end # The job title/position the registrant typed for their organization on the @@ -431,4 +456,108 @@ def submitted_position(registration) primary = entries.find { |entry| entry[:org_name].present? } || entries.first primary && primary[:position] end + + # Apply the org info the registrant submitted to an org we already have, when the + # admin opts in (the apply_form_details checkbox). Website and organization type + # overwrite the org's values (each logged to Ahoy). The address is additive — a new + # record — unless one with the same street exists, which we refresh instead of duplicating. + def apply_submitted_org_details(organization, answers) + return if answers.blank? + + overwrite_org_field(organization, :website_url, answers["agency_website"]) + overwrite_org_type(organization, answers["agency_type"]) + apply_submitted_address(organization, answers) + end + + # Overwrite a single org column with the submitted value — an explicit admin opt-in — + # and log the change to Ahoy. No-op when the form had no value or it already matches. + def overwrite_org_field(organization, field, submitted) + value = submitted.to_s.strip + return if value.blank? || organization.public_send(field).to_s == value + + previous = organization.public_send(field) + organization.update!(field => value) + Ahoy::Tracker.new(user: current_user).track( + "update.organization", + resource_type: "Organization", + resource_id: organization.id, + resource_title: organization.name, + change: field.to_s, + from: previous, + to: value, + reason: "registration_form_apply" + ) + end + + # Overwrite the org's type from the submitted type name, resolving it to an + # OrganizationType record and logging the change to Ahoy. No-op when the form had no + # type, it names no known type, or it already matches. + def overwrite_org_type(organization, submitted) + type = submitted_organization_type(submitted) + return if type.nil? || organization.organization_type_id == type.id + + previous = organization.organization_type&.name + organization.update!(organization_type: type) + Ahoy::Tracker.new(user: current_user).track( + "update.organization", + resource_type: "Organization", + resource_id: organization.id, + resource_title: organization.name, + change: "organization_type", + from: previous, + to: type.name, + reason: "registration_form_apply" + ) + end + + # The OrganizationType matching a submitted type name (case-insensitive), or nil when + # blank or unrecognized. The form submits a type name from OrganizationType.published_names. + def submitted_organization_type(submitted) + name = submitted.to_s.strip + return if name.blank? + + OrganizationType.where("LOWER(name) = ?", name.downcase).first + end + + # Creates an Active org from a submitted name, carrying over the org-level details + # the registrant typed: type, website (stored as typed; see Organization#website_link_url), + # and a primary work address. + def build_organization_from_answers(name, answers) + organization = Organization.create!( + name: name.strip, + organization_status: OrganizationStatus.find_by(name: "Active"), + organization_type: submitted_organization_type(answers["agency_type"]), + website_url: answers["agency_website"].presence&.strip + ) + apply_submitted_address(organization, answers) + organization + end + + # Add the submitted address as a new primary "work" record with an "Unknown" locality + # (the form doesn't capture one). Additive — but if the org already has an address with + # the same street, refresh that one (e.g. fill in a city it was missing) rather than + # duplicating it. The chosen record becomes primary and any others are demoted, leaving + # them active so prior addresses aren't lost. Skipped unless city and state are present + # (the Address model requires them); street_address and zip_code are NOT NULL columns, + # so a missing one is stored as "" rather than nil. + def apply_submitted_address(organization, answers) + city = answers["agency_city"].presence&.strip + state = answers["agency_state"].presence&.strip + return if city.blank? || state.blank? + + street = answers["agency_street"].to_s.strip + zip = answers["agency_zip"].to_s.strip + match = street.present? && organization.addresses.find { |a| a.street_address.to_s.strip.casecmp?(street) } + + address = if match + match.update!(city: city, state: state, zip_code: zip, primary: true, inactive: false) + match + else + organization.addresses.create!( + street_address: street, city: city, state: state, zip_code: zip, + locality: "Unknown", address_type: "work", primary: true + ) + end + organization.addresses.where.not(id: address.id).update_all(primary: false) + end end diff --git a/app/services/event_registration_services/public_registration.rb b/app/services/event_registration_services/public_registration.rb index 6dc60d988a..13fc31ce3c 100644 --- a/app/services/event_registration_services/public_registration.rb +++ b/app/services/event_registration_services/public_registration.rb @@ -234,8 +234,8 @@ def create_mailing_address(person) if existing existing.update!( - street_address: field_value("mailing_street"), - zip_code: field_value("mailing_zip"), + street_address: field_value("mailing_street").to_s, + zip_code: field_value("mailing_zip").to_s, primary: true, inactive: false ) @@ -246,10 +246,10 @@ def create_mailing_address(person) person.addresses.where(primary: true).update_all(primary: false, inactive: true) person.addresses.create!( - street_address: field_value("mailing_street"), + street_address: field_value("mailing_street").to_s, city: new_city, state: new_state, - zip_code: field_value("mailing_zip"), + zip_code: field_value("mailing_zip").to_s, country: field_value("mailing_country")&.strip, locality: "Unknown", address_type: field_value("mailing_address_type")&.downcase || "unknown", @@ -327,8 +327,8 @@ def create_agency_address(organization) if existing existing.update!( - street_address: field_value("agency_street"), - zip_code: field_value("agency_zip"), + street_address: field_value("agency_street").to_s, + zip_code: field_value("agency_zip").to_s, primary: existing.primary? || make_primary, inactive: false ) @@ -337,10 +337,10 @@ def create_agency_address(organization) end organization.addresses.create!( - street_address: field_value("agency_street"), + street_address: field_value("agency_street").to_s, city: new_city, state: new_state, - zip_code: field_value("agency_zip"), + zip_code: field_value("agency_zip").to_s, country: field_value("agency_country")&.strip, locality: "Unknown", address_type: "work", diff --git a/app/views/event_registrations/_apply_form_details_checkbox.html.erb b/app/views/event_registrations/_apply_form_details_checkbox.html.erb new file mode 100644 index 0000000000..fb4b41c398 --- /dev/null +++ b/app/views/event_registrations/_apply_form_details_checkbox.html.erb @@ -0,0 +1,11 @@ +<%# Toggle: when checked, linking this org also applies the org info from the + registrant's form — overwrites the website and type, and adds the submitted address + as the new primary (refreshing a same-street one instead of duplicating). Defaults + on for a create-and-link or a suggested match (very likely the same org), off for the + manual search box (often an unrelated established org). The caller passes `checked`. + Lives inside each link form so it submits with that form without any JS. %> +<% checked = local_assigns.fetch(:checked, true) %> + diff --git a/app/views/event_registrations/link_organization.html.erb b/app/views/event_registrations/link_organization.html.erb index f4a0203bc9..fe51f92998 100644 --- a/app/views/event_registrations/link_organization.html.erb +++ b/app/views/event_registrations/link_organization.html.erb @@ -121,18 +121,23 @@ <%= @creatable_org_names.size > 1 ? "These organizations the registrant entered aren't in the database yet. Create each in the database and link it in one step:" : "The registrant entered this organization on their form, but it's not in the database yet. Create the organization in the database and link it in one step:" %>

<% @creatable_org_names.each do |creatable_name| %> -
-
- <%= creatable_name %> + <%= form_with url: create_organization_event_registration_path(@event_registration), + method: :post, + data: { turbo_frame: "_top" }, + class: "mb-2" do %> + <%= hidden_field_tag :organization_name, creatable_name %> + <%= hidden_field_tag :return_to, params[:return_to] %> +
+
+ <%= creatable_name %> +
+ <%= submit_tag "Create and link", + class: "shrink-0 w-48 text-center rounded-md px-6 py-2 font-semibold #{btn_solid} focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition cursor-pointer" %>
- <%= button_to create_organization_event_registration_path(@event_registration, return_to: params[:return_to]), - method: :post, - params: { organization_name: creatable_name }, - form: { class: "shrink-0", data: { turbo_frame: "_top" } }, - class: "shrink-0 w-48 text-center rounded-md px-6 py-2 font-semibold #{btn_solid} focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition cursor-pointer" do %> - Create and link - <% end %> -
+
+ <%= render "apply_form_details_checkbox", checked: true %> +
+ <% end %> <% end %>

Or, add an existing organization instead:

<% end %> @@ -144,13 +149,18 @@ <%= form_with url: select_organization_event_registration_path(@event_registration), method: :post, data: { turbo_frame: "_top" }, - class: "flex items-center justify-between bg-blue-50 border border-blue-200 rounded-lg p-3" do %> + class: "bg-blue-50 border border-blue-200 rounded-lg p-3" do %> <%= hidden_field_tag :organization_id, org.id %> <%= hidden_field_tag :return_to, params[:return_to] %> -
-

<%= org.name %><% if org.city_state.present? %> · <%= org.city_state %><% end %>

+
+
+

<%= org.name %><% if org.city_state.present? %> · <%= org.city_state %><% end %>

+
+ <%= submit_tag "Link organization", class: "shrink-0 rounded-md bg-blue-900 px-4 py-1.5 text-white text-sm font-medium hover:bg-blue-800 transition cursor-pointer" %> +
+
+ <%= render "apply_form_details_checkbox", checked: true %>
- <%= submit_tag "Link organization", class: "rounded-md bg-blue-900 px-4 py-1.5 text-white text-sm font-medium hover:bg-blue-800 transition cursor-pointer" %> <% end %> <% end %>
@@ -159,18 +169,21 @@ <%= form_with url: select_organization_event_registration_path(@event_registration), method: :post, data: { turbo_frame: "_top" }, - class: "flex items-stretch gap-2" do %> + class: "space-y-2" do %> <%= hidden_field_tag :return_to, params[:return_to] %> -
- <%= select_tag :organization_id, - options_from_collection_for_select([], :id, :name), - include_blank: true, - class: "w-full rounded-lg border border-gray-300 px-3 py-2 text-gray-800 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200 focus:outline-none", - data: { controller: "remote-select", remote_select_model_value: "organization" }, - prompt: "Type to search organizations…" %> +
+
+ <%= select_tag :organization_id, + options_from_collection_for_select([], :id, :name), + include_blank: true, + class: "w-full rounded-lg border border-gray-300 px-3 py-2 text-gray-800 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200 focus:outline-none", + data: { controller: "remote-select", remote_select_model_value: "organization" }, + prompt: "Type to search organizations…" %> +
+ <%= submit_tag "Link organization", + class: "shrink-0 w-48 text-center rounded-md px-6 py-2 font-semibold #{create_is_primary ? btn_outline : btn_solid} focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition cursor-pointer" %>
- <%= submit_tag "Link organization", - class: "shrink-0 w-48 text-center rounded-md px-6 py-2 font-semibold #{create_is_primary ? btn_outline : btn_solid} focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition cursor-pointer" %> + <%= render "apply_form_details_checkbox", checked: false %> <% end %>
diff --git a/spec/requests/event_registrations_spec.rb b/spec/requests/event_registrations_spec.rb index 58fc1c965a..f4d07a14a4 100644 --- a/spec/requests/event_registrations_spec.rb +++ b/spec/requests/event_registrations_spec.rb @@ -401,26 +401,36 @@ def unrequest(registration) describe "organization linking" do let(:organization) { create(:organization, name: "Helping Hands") } + # Records what the registrant "typed on the form" so the editor can show the + # submission, compare its position to an existing affiliation title, and carry + # the org details onto an org created from it. + def submit_form(org_name: nil, position: nil, street: nil, city: nil, state: nil, zip: nil, type: nil, website: nil) + reg_form = Form.find_by(name: "Reg form") || create(:form, name: "Reg form") + create(:event_form, :registration, event: event, form: reg_form) unless event.registration_form + submission = create(:form_submission, person: regular_user.person, form: reg_form) + { + "agency_name" => org_name, + "agency_position" => position, + "agency_street" => street, + "agency_city" => city, + "agency_state" => state, + "agency_zip" => zip, + "agency_type" => type, + "agency_website" => website + }.each do |identifier, value| + next if value.nil? + field = reg_form.form_fields.find_by(field_identifier: identifier) || + create(:form_field, form: reg_form, field_identifier: identifier) + create(:form_answer, form_submission: submission, form_field: field, submitted_answer: value) + end + submission + end + describe "GET /event_registrations/:id/link_organization" do before do create(:event_registration_organization, event_registration: existing_registration, organization: organization) end - # Records what the registrant "typed on the form" so the editor can show - # the submission and compare its position to an existing affiliation title. - def submit_form(org_name: nil, position: nil) - reg_form = Form.find_by(name: "Reg form") || create(:form, name: "Reg form") - create(:event_form, :registration, event: event, form: reg_form) unless event.registration_form - submission = create(:form_submission, person: regular_user.person, form: reg_form) - { "agency_name" => org_name, "agency_position" => position }.each do |identifier, value| - next if value.nil? - field = reg_form.form_fields.find_by(field_identifier: identifier) || - create(:form_field, form: reg_form, field_identifier: identifier) - create(:form_answer, form_submission: submission, form_field: field, submitted_answer: value) - end - submission - end - # Whether the
section whose text contains `heading` is expanded. def details_open?(body, heading) element = Nokogiri::HTML(body).css("details").find { |d| d.text.include?(heading) } @@ -649,6 +659,42 @@ def details_open?(body, heading) expect(response.body).to include(create_organization_event_registration_path(existing_registration)) end + # Finds the apply_form_details checkbox inside the form identified by `marker` + # (a CSS selector unique to that form), so each link form's default can be checked. + def populate_toggle_in_form(body, marker) + form = Nokogiri::HTML(body).css("form").find { |f| f.at_css(marker) } + form&.at_css('input[type="checkbox"][name="apply_form_details"]') + end + + it "checks the populate toggle by default on a create-and-link row" do + submit_form(org_name: "Totally New Org") + + get link_organization_event_registration_path(existing_registration) + + checkbox = populate_toggle_in_form(response.body, 'input[name="organization_name"]') + expect(checkbox).to be_present + expect(checkbox["checked"]).to be_present + end + + it "checks the populate toggle by default on a suggested match" do + create(:organization, name: "Helpers United") + submit_form(org_name: "Helpers United") + + get link_organization_event_registration_path(existing_registration) + + checkbox = populate_toggle_in_form(response.body, 'input[type="hidden"][name="organization_id"]') + expect(checkbox).to be_present + expect(checkbox["checked"]).to be_present + end + + it "leaves the populate toggle unchecked on the manual search form" do + get link_organization_event_registration_path(existing_registration) + + checkbox = populate_toggle_in_form(response.body, 'select[name="organization_id"]') + expect(checkbox).to be_present + expect(checkbox["checked"]).to be_nil + end + it "hides 'Create org & link' when an org with the submitted name already exists" do create(:organization, name: "Already Exists Org") submit_form(org_name: "Already Exists Org") @@ -729,6 +775,82 @@ def details_open?(body, heading) expect(regular_user.person.affiliations.where(organization: organization).pluck(:title)) .to contain_exactly("Facilitator") end + + it "populates address, website, and type from the form when the admin opts in" do + gov_type = create(:organization_type, name: "Government agency") + submit_form(org_name: organization.name, type: "Government agency", + website: "matchable.org", street: "9 Oak Ave", city: "Boise", state: "ID") + + post select_organization_event_registration_path(existing_registration), + params: { organization_id: organization.id, apply_form_details: "1" } + + organization.reload + expect(organization.organization_type).to eq(gov_type) + expect(organization.website_url).to eq("matchable.org") + expect(organization.addresses.active.first).to have_attributes(street_address: "9 Oak Ave", city: "Boise", state: "ID", primary: true) + end + + it "does not touch the org's details when the admin does not opt in" do + create(:organization_type, name: "Government agency") + submit_form(org_name: organization.name, type: "Government agency", + website: "matchable.org", city: "Boise", state: "ID") + + post select_organization_event_registration_path(existing_registration), + params: { organization_id: organization.id } + + organization.reload + expect(organization.organization_type).to be_nil + expect(organization.website_url).to be_blank + expect(organization.addresses).to be_empty + end + + it "overwrites the org's existing website and logs the change when opting in" do + organization.update!(website_url: "https://old.example") + submit_form(org_name: organization.name, website: "https://new.example") + + expect { + post select_organization_event_registration_path(existing_registration), + params: { organization_id: organization.id, apply_form_details: "1" } + }.to change { Ahoy::Event.where(name: "update.organization").count }.by(1) + + expect(organization.reload.website_url).to eq("https://new.example") + end + + it "adds the submitted address as a new primary, demoting but keeping the old one" do + old = create(:address, addressable: organization, street_address: "1 Old Rd", city: "Old City", state: "CA", primary: true) + submit_form(org_name: organization.name, street: "9 Oak Ave", city: "Boise", state: "ID") + + post select_organization_event_registration_path(existing_registration), + params: { organization_id: organization.id, apply_form_details: "1" } + + expect(organization.reload.addresses.where(primary: true).map(&:city)).to eq([ "Boise" ]) + expect(old.reload).to have_attributes(primary: false, inactive: false) + end + + it "refreshes a same-street address instead of duplicating it" do + existing = create(:address, addressable: organization, street_address: "9 Oak Ave", city: "Old City", state: "CA") + submit_form(org_name: organization.name, street: "9 Oak Ave", city: "Boise", state: "ID") + + expect { + post select_organization_event_registration_path(existing_registration), + params: { organization_id: organization.id, apply_form_details: "1" } + }.not_to change { organization.addresses.count } + + expect(existing.reload).to have_attributes(city: "Boise", state: "ID", primary: true) + end + + it "overwrites the org's existing type and logs the change when opting in" do + organization.update!(organization_type: create(:organization_type, name: "For-profit")) + gov_type = create(:organization_type, name: "Government agency") + submit_form(org_name: organization.name, type: "Government agency") + + expect { + post select_organization_event_registration_path(existing_registration), + params: { organization_id: organization.id, apply_form_details: "1" } + }.to change { Ahoy::Event.where(name: "update.organization").count }.by(1) + + expect(organization.reload.organization_type).to eq(gov_type) + end end describe "POST /event_registrations/:id/create_organization" do @@ -820,6 +942,81 @@ def details_open?(body, heading) expect(flash[:alert]).to be_present end + + it "carries the registrant's typed org details onto the new org" do + create(:organization_status, name: "Active") + for_profit = create(:organization_type, name: "For-profit") + submit_form(org_name: "Detailed Org", street: "100 Main St", city: "Austin", + state: "TX", zip: "78701", type: "For-profit", website: "https://detailed.org") + + post create_organization_event_registration_path(existing_registration), params: { apply_form_details: "1" } + + org = Organization.find_by(name: "Detailed Org") + expect(org.organization_type).to eq(for_profit) + expect(org.website_url).to eq("https://detailed.org") + address = org.addresses.first + expect(address).to have_attributes(street_address: "100 Main St", city: "Austin", + state: "TX", zip_code: "78701", address_type: "work") + end + + it "sets the registrant's affiliation title from the submitted position" do + create(:organization_status, name: "Active") + submit_form(org_name: "Titled Org", position: "Lead Facilitator") + + post create_organization_event_registration_path(existing_registration) + + affiliation = regular_user.person.affiliations.find_by(organization: Organization.find_by(name: "Titled Org")) + expect(affiliation.title).to eq("Lead Facilitator") + end + + it "stores a bare-domain website as typed (normalized at render)" do + create(:organization_status, name: "Active") + submit_form(org_name: "Schemeless Org", website: "example.com") + + post create_organization_event_registration_path(existing_registration), params: { apply_form_details: "1" } + + expect(Organization.find_by(name: "Schemeless Org").website_url).to eq("example.com") + end + + it "skips the address when no state was submitted" do + create(:organization_status, name: "Active") + submit_form(org_name: "Partial Address Org", city: "Austin") + + expect { + post create_organization_event_registration_path(existing_registration), params: { apply_form_details: "1" } + }.to change(Organization, :count).by(1) + + expect(Organization.find_by(name: "Partial Address Org").addresses).to be_empty + end + + it "creates a bare org when the populate toggle is off" do + create(:organization_status, name: "Active") + submit_form(org_name: "Bare Org", type: "For-profit", website: "bare.org", city: "Reno", state: "NV") + + post create_organization_event_registration_path(existing_registration) + + org = Organization.find_by(name: "Bare Org") + expect(org).to be_present + expect(org.organization_type).to be_nil + expect(org.website_url).to be_blank + expect(org.addresses).to be_empty + expect(existing_registration.organizations).to include(org) + end + + it "populates an existing org on create-and-link when the toggle is on" do + existing = create(:organization, name: "Sparse Org") + for_profit = create(:organization_type, name: "For-profit") + submit_form(org_name: "Sparse Org", type: "For-profit", website: "sparse.org", + street: "5 Elm St", city: "Reno", state: "NV", zip: "89501") + + post create_organization_event_registration_path(existing_registration), params: { apply_form_details: "1" } + + existing.reload + expect(existing.organization_type).to eq(for_profit) + expect(existing.website_url).to eq("sparse.org") + expect(existing.addresses.active.first).to have_attributes(street_address: "5 Elm St", city: "Reno", state: "NV") + expect(existing_registration.organizations).to include(existing) + end end describe "DELETE /event_registrations/:id/unlink_organization" do diff --git a/spec/services/event_registration_services/public_registration_spec.rb b/spec/services/event_registration_services/public_registration_spec.rb index abe26dd270..53eb757dae 100644 --- a/spec/services/event_registration_services/public_registration_spec.rb +++ b/spec/services/event_registration_services/public_registration_spec.rb @@ -358,6 +358,29 @@ def register_with_org(extra) end end + describe "an agency address submitted without a zip or street" do + # zip_code and street_address are NOT NULL columns. A registrant who fills the org + # city/state but leaves the zip/street blank must not hit a NotNullViolation 500. + let!(:organization) { create(:organization, name: "Helping Org") } + let(:params) do + base_form_params(first_name: "Sam", last_name: "Roe", email: "sam@example.com").merge( + field_id("agency_name") => "Helping Org", + field_id("agency_city") => "Reno", + field_id("agency_state") => "NV" + ) + end + + it "creates the address without raising" do + result = nil + expect { + result = described_class.call(event: event, form: form, form_params: params) + }.not_to raise_error + + expect(result.success?).to be true + expect(organization.addresses.last).to have_attributes(city: "Reno", state: "NV", zip_code: "", street_address: "") + end + end + describe "sector tagging" do let!(:primary_sector) { create(:sector, name: "Healthcare") } let!(:additional_sector) { create(:sector, name: "Education") }