diff --git a/app/controllers/concerns/importable.rb b/app/controllers/concerns/importable.rb index f2bd1515bb..67e5644584 100644 --- a/app/controllers/concerns/importable.rb +++ b/app/controllers/concerns/importable.rb @@ -27,11 +27,15 @@ def import_csv data = File.read(params[:file].path, encoding: "BOM|UTF-8") csv = CSV.parse(data, headers: true) if csv.count.positive? && csv.first.headers.all? { |header| !header.nil? } - errors = resource_model.import_csv(csv, current_organization.id) - if errors.empty? + results = resource_model.import_csv(csv, current_organization.id) + if results[:errors].empty? flash[:notice] = "#{resource_model_humanized} were imported successfully!" else - flash[:error] = "The following #{resource_model_humanized} did not import successfully:\n#{errors.join("\n")}" + flash[:error] = "The following #{resource_model_humanized} did not import successfully:\n#{results[:errors].join("\n")}" + end + + if results[:warnings].present? + flash[:alert] = "The following #{resource_model_humanized} imported with warnings:\n#{results[:warnings].join("\n")}" end else flash[:error] = "Check headers in file!" diff --git a/app/models/concerns/provideable.rb b/app/models/concerns/provideable.rb index 5f25f61810..5105c8b770 100644 --- a/app/models/concerns/provideable.rb +++ b/app/models/concerns/provideable.rb @@ -15,7 +15,8 @@ def self.import_csv(csv, organization) loc.save! end - [] + + {errors: [], warnings: []} end def self.csv_export_headers diff --git a/app/models/donation_site.rb b/app/models/donation_site.rb index cc79d2f46e..83ab6382be 100644 --- a/app/models/donation_site.rb +++ b/app/models/donation_site.rb @@ -38,6 +38,7 @@ class DonationSite < ApplicationRecord def self.import_csv(csv, organization) errors = [] + csv.each_with_index do |row, index| loc = DonationSite.new(row.to_hash) loc.organization_id = organization @@ -47,7 +48,8 @@ def self.import_csv(csv, organization) errors << "Row #{index + 2}, #{row.to_hash["name"]} - #{loc.errors.full_messages.join(", ")}" end end - errors + + {errors: errors, warnings: []} end def self.csv_export_headers diff --git a/app/models/partner.rb b/app/models/partner.rb index cbbd55e71b..3bcf64307f 100644 --- a/app/models/partner.rb +++ b/app/models/partner.rb @@ -56,6 +56,7 @@ class Partner < ApplicationRecord validate :correct_document_mime_type before_save { email&.downcase! } + before_create :default_send_reminders_to_false, if: :send_reminders_nil? before_update :invite_new_partner, if: :should_invite_because_email_changed? scope :alphabetized, -> { order(:name) } @@ -103,18 +104,30 @@ def approvable? # better to extract this outside of the model def self.import_csv(csv, organization_id) errors = [] + warnings = [] organization = Organization.find(organization_id) csv.each do |row| hash_rows = Hash[row.to_hash.map { |k, v| [k.downcase, v] }] - svc = PartnerCreateService.new(organization: organization, partner_attrs: hash_rows) - svc.call - if svc.errors.present? - errors << "#{svc.partner.name}: #{svc.partner.errors.full_messages.to_sentence}" + partner_create_service = PartnerCreateService.new(organization: organization, partner_attrs: hash_rows) + partner_create_service.call + + if partner_create_service.errors.present? + formatted_errors = partner_create_service.errors.map do |error| + "#{error.attribute.to_s.humanize} #{error.message.downcase}" + end + errors << "#{partner_create_service.partner.name}: #{formatted_errors.to_sentence}" + + elsif partner_create_service.warnings.present? + formatted_warnings = partner_create_service.warnings.map do |warning| + "#{warning.attribute.to_s.humanize} #{warning.message.downcase}" + end + warnings << "#{partner_create_service.partner.name}: #{formatted_warnings.to_sentence}" end end - errors + + {errors: errors, warnings: warnings} end def partials_to_show @@ -165,6 +178,14 @@ def correct_document_mime_type end end + def default_send_reminders_to_false + self.send_reminders = false + end + + def send_reminders_nil? + send_reminders.nil? + end + def invite_new_partner UserInviteService.invite(email: email, roles: [Role::PARTNER], resource: self) end diff --git a/app/models/storage_location.rb b/app/models/storage_location.rb index 450002fa57..62ba6fd5c5 100644 --- a/app/models/storage_location.rb +++ b/app/models/storage_location.rb @@ -124,7 +124,8 @@ def self.import_csv(csv, organization) loc.organization_id = organization loc.save! end - [] + + {errors: [], warnings: []} end # NOTE: We should generalize this elsewhere -- Importable concern? diff --git a/app/services/partner_create_service.rb b/app/services/partner_create_service.rb index 6b81bfae59..bfc2b29b8c 100644 --- a/app/services/partner_create_service.rb +++ b/app/services/partner_create_service.rb @@ -9,6 +9,8 @@ def initialize(organization:, partner_attrs:) end def call + process_default_storage_location + @partner = organization.partners.build(partner_attrs) if @partner.valid? @@ -37,5 +39,27 @@ def call private + def process_default_storage_location + return unless partner_attrs.has_key?("default_storage_location") + + if partner_attrs["default_storage_location"].blank? + partner_attrs.delete("default_storage_location") + else + default_storage_location_name = partner_attrs["default_storage_location"]&.titlecase + default_storage_location_id = StorageLocation.find_by( + name: default_storage_location_name, + organization: organization.id + )&.id + + if default_storage_location_id.nil? + add_warning(:default_storage_location, + "is not a storage location for this partner's organization") + end + + partner_attrs.delete("default_storage_location") + partner_attrs["default_storage_location_id"] = default_storage_location_id + end + end + attr_reader :organization, :partner_attrs end diff --git a/app/services/service_object_errors_mixin.rb b/app/services/service_object_errors_mixin.rb index 07338a62ee..714e2450db 100644 --- a/app/services/service_object_errors_mixin.rb +++ b/app/services/service_object_errors_mixin.rb @@ -31,6 +31,14 @@ def errors @errors ||= ActiveModel::Errors.new(self) end + def warnings + @warnings ||= ActiveModel::Errors.new(self) + end + + def add_warning(attribute, message) + warnings.add(attribute, message) + end + def read_attribute_for_validation(attr) send(attr) end @@ -45,4 +53,3 @@ def lookup_ancestors end end end - diff --git a/app/views/dashboard/_getting_started_prompt.html.erb b/app/views/dashboard/_getting_started_prompt.html.erb index 7c745cfef0..53f655dbcb 100644 --- a/app/views/dashboard/_getting_started_prompt.html.erb +++ b/app/views/dashboard/_getting_started_prompt.html.erb @@ -59,5 +59,17 @@ <%= button_to "Dismiss", "/manage", method: :patch, params: {organization: { bank_is_set_up: true }}, class: "btn btn-primary" %> + <%= render( + layout: "shared/csv_import_modal", + locals: { + import_type: "Partners", + csv_template_url: "/partners_template_updated.csv", + csv_import_url: import_csv_partners_path, + }, + ) do %> +
  • Open the CSV file with Excel or your favourite spreadsheet program.
  • +
  • Delete the sample data and enter your partner agency names and addresses in the appropriate columns.
  • +
  • Save the file as a CSV file.
  • + <% end %> <% end %> <% end %> diff --git a/app/views/partners/index.html.erb b/app/views/partners/index.html.erb index ce3fc6def8..7215f8ed00 100644 --- a/app/views/partners/index.html.erb +++ b/app/views/partners/index.html.erb @@ -56,7 +56,7 @@ layout: "shared/csv_import_modal", locals: { import_type: "Partners", - csv_template_url: "/partners_template.csv", + csv_template_url: "/partners_template_updated.csv", csv_import_url: import_csv_partners_path } ) do %> diff --git a/public/partners_template.csv b/public/partners_template.csv deleted file mode 100644 index bb62004176..0000000000 --- a/public/partners_template.csv +++ /dev/null @@ -1,4 +0,0 @@ -name,email -Partner 1,partner1@example.com -Partner 2,partner2@example.com -Partner 3,partner3@example.com \ No newline at end of file diff --git a/public/partners_template_updated.csv b/public/partners_template_updated.csv new file mode 100644 index 0000000000..17ac82af17 --- /dev/null +++ b/public/partners_template_updated.csv @@ -0,0 +1,4 @@ +name,email,default_storage_location,send_reminders,quota,notes +Partner 1,partner1@example.com,"Smithsonian Conservation Center",false,20,"test note 1" +Partner 2,partner2@example.com,"Smithsonian Conservation Center",false,30,"test note 2" +Partner 3,partner3@example.com,"Smithsonian Conservation Center",true,10,"test note 3" \ No newline at end of file diff --git a/spec/fixtures/files/partners.csv b/spec/fixtures/files/partners.csv deleted file mode 100644 index bb62004176..0000000000 --- a/spec/fixtures/files/partners.csv +++ /dev/null @@ -1,4 +0,0 @@ -name,email -Partner 1,partner1@example.com -Partner 2,partner2@example.com -Partner 3,partner3@example.com \ No newline at end of file diff --git a/spec/fixtures/files/partners_missing_default_storage_location_field.csv b/spec/fixtures/files/partners_missing_default_storage_location_field.csv new file mode 100644 index 0000000000..a3ad3a6abb --- /dev/null +++ b/spec/fixtures/files/partners_missing_default_storage_location_field.csv @@ -0,0 +1,3 @@ +name,email,default_storage_location,send_reminders,quota,notes +Partner 51,partner51@example.com,,false,50,"great partner" + diff --git a/spec/fixtures/files/partners_missing_default_storage_location_field_and_header.csv b/spec/fixtures/files/partners_missing_default_storage_location_field_and_header.csv new file mode 100644 index 0000000000..986cd8d6c0 --- /dev/null +++ b/spec/fixtures/files/partners_missing_default_storage_location_field_and_header.csv @@ -0,0 +1,2 @@ +name,email,send_reminders,quota,notes +Partner 71,partner71@example.com,false,50,"great partner" diff --git a/spec/fixtures/files/partners_missing_send_reminders_field.csv b/spec/fixtures/files/partners_missing_send_reminders_field.csv new file mode 100644 index 0000000000..33dc94979e --- /dev/null +++ b/spec/fixtures/files/partners_missing_send_reminders_field.csv @@ -0,0 +1,3 @@ +name,email,default_storage_location,send_reminders,quota,notes +Partner 51,partner51@example.com,"Smithsonian Conservation Center",,50,"great partner" + diff --git a/spec/fixtures/files/partners_missing_send_reminders_field_and_header.csv b/spec/fixtures/files/partners_missing_send_reminders_field_and_header.csv new file mode 100644 index 0000000000..b29569b895 --- /dev/null +++ b/spec/fixtures/files/partners_missing_send_reminders_field_and_header.csv @@ -0,0 +1,3 @@ +name,email,default_storage_location,quota,notes +Partner 51,partner51@example.com,"Smithsonian Conservation Center",50,"great partner" + diff --git a/spec/fixtures/files/partners_with_bom_encoding.csv b/spec/fixtures/files/partners_with_bom_encoding.csv index 36d82c5129..a2d24f2914 100644 --- a/spec/fixtures/files/partners_with_bom_encoding.csv +++ b/spec/fixtures/files/partners_with_bom_encoding.csv @@ -1,21 +1,21 @@ -name,email -Beaverton Police Department,krodriguez@beavertonoregon.gov -Catholic Charities,lcrombie@catholiccharitiesoregon.org -Clackamas Service Center,debramason@cscoregon.org -Healthy Familes of Clackamas County,bkersens@healthyfamiliescc.org -Dollar for Portland,jared@dollorfor.org -Emmanuel Housing Center,shauntemeyers@emmanuelpdx.org -Helensview School,hgandee@mesd.k12.or.us -JOIN,ccarroll@joinpdx.org -Job Corps (PIVOT),zutz.kayla@jobcorps.org -NARA,christman@naranw.org -NW Housing Alternatives,doty@nwhousing.org -Pregnancy Resource Center,debbie@portlandprc.org -Portland Homeless Family Solutions,emma@pdxhfs.org -Raphael House,lvold@raphaelhouse.com -The Rebecca Foundation's Cloth Diaper Closet,portland@clothforall.org -Rose Haven,adeol@rosehaven.org -Self Enhancement Inc.,stephaniep@selfenhancement.org -Teen Parent Services (PPS),lgovan@pps.net -Volunteers of America,cross@voaor.org -Central City Concern,lindsey.ramsey@ccconcern.org \ No newline at end of file +name,email,default_storage_location,send_reminders,quota,notes +Beaverton Police Department,krodriguez@beavertonoregon.gov,"Smithsonian Conservation Center",true,50,"great partner" +Catholic Charities,lcrombie@catholiccharitiesoregon.org,"Smithsonian Conservation Center",true,50,"great partner" +Clackamas Service Center,debramason@cscoregon.org,"Smithsonian Conservation Center",true,50,"great partner" +Healthy Familes of Clackamas County,bkersens@healthyfamiliescc.org,"Smithsonian Conservation Center",true,50,"great partner" +Dollar for Portland,jared@dollorfor.org,"Smithsonian Conservation Center",true,50,"great partner" +Emmanuel Housing Center,shauntemeyers@emmanuelpdx.org,"Smithsonian Conservation Center",true,50,"great partner" +Helensview School,hgandee@mesd.k12.or.us,"Smithsonian Conservation Center",true,50,"great partner" +JOIN,ccarroll@joinpdx.org,"Smithsonian Conservation Center",true,50,"great partner" +Job Corps (PIVOT),zutz.kayla@jobcorps.org,"Smithsonian Conservation Center",true,50,"great partner" +NARA,christman@naranw.org,"Smithsonian Conservation Center",true,50,"great partner" +NW Housing Alternatives,doty@nwhousing.org,"Smithsonian Conservation Center",true,50,"great partner" +Pregnancy Resource Center,debbie@portlandprc.org,"Smithsonian Conservation Center",true,50,"great partner" +Portland Homeless Family Solutions,emma@pdxhfs.org,"Smithsonian Conservation Center",true,50,"great partner" +Raphael House,lvold@raphaelhouse.com,"Smithsonian Conservation Center",true,50,"great partner" +The Rebecca Foundation's Cloth Diaper Closet,portland@clothforall.org,"Smithsonian Conservation Center",true,50,"great partner" +Rose Haven,adeol@rosehaven.org,"Smithsonian Conservation Center",true,50,"great partner" +Self Enhancement Inc.,stephaniep@selfenhancement.org,"Smithsonian Conservation Center",true,50,"great partner" +Teen Parent Services (PPS),lgovan@pps.net,"Smithsonian Conservation Center",true,50,"great partner" +Volunteers of America,cross@voaor.org,"Smithsonian Conservation Center",true,50,"great partner" +Central City Concern,lindsey.ramsey@ccconcern.org,"Smithsonian Conservation Center",true,50,"great partner" \ No newline at end of file diff --git a/spec/fixtures/files/partners_with_duplicates.csv b/spec/fixtures/files/partners_with_duplicates.csv deleted file mode 100644 index c4890e78af..0000000000 --- a/spec/fixtures/files/partners_with_duplicates.csv +++ /dev/null @@ -1,4 +0,0 @@ -name,email -Partner 1,partner1@example.com -Partner 2,partner2@example.com -Partner 4,partner4@example.com \ No newline at end of file diff --git a/spec/fixtures/files/partners_with_final_line_blank.csv b/spec/fixtures/files/partners_with_final_line_blank.csv new file mode 100644 index 0000000000..c8a10d842b --- /dev/null +++ b/spec/fixtures/files/partners_with_final_line_blank.csv @@ -0,0 +1,3 @@ +name,email,default_storage_location,send_reminders,quota,notes +Partner 31,partner31@example.com,"Smithsonian Conservation Center",true,50,"great partner" + diff --git a/spec/fixtures/files/partners_with_six_fields.csv b/spec/fixtures/files/partners_with_six_fields.csv new file mode 100644 index 0000000000..797b65f0f6 --- /dev/null +++ b/spec/fixtures/files/partners_with_six_fields.csv @@ -0,0 +1,4 @@ +name,email,default_storage_location,send_reminders,quota,notes +Partner 1,partner1@example.com,"Smithsonian Conservation Center",true,50,"great partner" +Partner 2,partner2@example.com,"Smithsonian Conservation Center",true,75,"such a great partner" +Partner 4,partner4@example.com,"Smithsonian Conservation Center",false,80,"really ten out of ten" diff --git a/spec/fixtures/files/partners_with_six_fields_and_duplicates.csv b/spec/fixtures/files/partners_with_six_fields_and_duplicates.csv new file mode 100644 index 0000000000..6250a90ae8 --- /dev/null +++ b/spec/fixtures/files/partners_with_six_fields_and_duplicates.csv @@ -0,0 +1,4 @@ +name,email,default_storage_location,send_reminders,quota,notes +Partner 1,partner1@example.com,"Smithsonian Conservation Center",true,50,"great partner" +Partner 2,partner2@example.com,"Smithsonian Conservation Center",true,75,"such a great partner" +Partner 3,partner3@example.com,"Smithsonian Conservation Center",true,80,"yay, a partner" diff --git a/spec/fixtures/files/partners_with_six_fields_capitalized_location.csv b/spec/fixtures/files/partners_with_six_fields_capitalized_location.csv new file mode 100644 index 0000000000..0f8cfd6856 --- /dev/null +++ b/spec/fixtures/files/partners_with_six_fields_capitalized_location.csv @@ -0,0 +1,4 @@ +name,email,default_storage_location,send_reminders,quota,notes +Partner 1,partner1@example.com,"SMITHSONIAN CONSERVATION CENTER",true,50,"great partner" +Partner 2,partner2@example.com,"Smithsonian Conservation Center",true,75,"such a great partner" +Partner 4,partner4@example.com,"Smithsonian Conservation Center",false,80,"really ten out of ten" diff --git a/spec/fixtures/files/partners_with_six_fields_invalid_location.csv b/spec/fixtures/files/partners_with_six_fields_invalid_location.csv new file mode 100644 index 0000000000..14daacbdd8 --- /dev/null +++ b/spec/fixtures/files/partners_with_six_fields_invalid_location.csv @@ -0,0 +1,2 @@ +name,email,default_storage_location,send_reminders,quota,notes +Partner 4,partner4@example.com,"Invalid",false,80,"really ten out of ten" \ No newline at end of file diff --git a/spec/models/donation_site_spec.rb b/spec/models/donation_site_spec.rb index 1f1a246b1b..25047cc758 100644 --- a/spec/models/donation_site_spec.rb +++ b/spec/models/donation_site_spec.rb @@ -43,7 +43,8 @@ data = File.read(duplicated_name_csv_path, encoding: "BOM|UTF-8") csv = CSV.parse(data, headers: true) - errors = DonationSite.import_csv(csv, organization.id) + response = DonationSite.import_csv(csv, organization.id) + errors = response[:errors] expect(errors).not_to be_empty expect(errors.first).to match(/Row/) expect(errors.first).to include("Name must be unique within the organization") @@ -55,7 +56,8 @@ data = File.read(valid_csv_path, encoding: "BOM|UTF-8") csv = CSV.parse(data, headers: true) - errors = DonationSite.import_csv(csv, organization.id) + response = DonationSite.import_csv(csv, organization.id) + errors = response[:errors] expect(errors).to be_empty expect(DonationSite.count).to eq 1 @@ -67,7 +69,8 @@ data = File.read(invalid_csv_path, encoding: "BOM|UTF-8") csv = CSV.parse(data, headers: true) - errors = DonationSite.import_csv(csv, organization.id) + response = DonationSite.import_csv(csv, organization.id) + errors = response[:errors] expect(errors).not_to be_empty expect(errors.first).to match(/Row/) expect(errors.first).to include("can't be blank") @@ -79,7 +82,9 @@ import_file_path = Rails.root.join("spec", "fixtures", "files", "donation_sites.csv") data = File.read(import_file_path, encoding: "BOM|UTF-8") csv = CSV.parse(data, headers: true) - DonationSite.import_csv(csv, organization.id) + response = DonationSite.import_csv(csv, organization.id) + errors = response[:errors] + expect(errors).to be_empty expect(DonationSite.count).to eq 1 end end diff --git a/spec/models/partner_spec.rb b/spec/models/partner_spec.rb index f0dbc2066a..d19accd69f 100644 --- a/spec/models/partner_spec.rb +++ b/spec/models/partner_spec.rb @@ -258,19 +258,27 @@ describe "import_csv" do let(:organization) { create(:organization) } + before do + create(:storage_location, organization: organization) + create(:storage_location, organization: organization, name: "shed") + create(:storage_location, organization: organization, name: "building") + create(:storage_location, organization: organization, name: "Smithsonian Conservation Center") + end it "imports partners from a csv file and prevents multiple imports" do - before_import = Partner.count - import_file_path = Rails.root.join("spec", "fixtures", "files", "partners.csv") + import_file_path = Rails.root.join("spec", "fixtures", "files", "partners_with_six_fields.csv") data = File.read(import_file_path, encoding: "BOM|UTF-8") csv = CSV.parse(data, headers: true) - Partner.import_csv(csv, organization.id) - expect(Partner.count).to eq before_import + 3 - import_file_path2 = Rails.root.join("spec", "fixtures", "files", "partners_with_duplicates.csv") + expect do + Partner.import_csv(csv, organization.id) + end.to change { Partner.count }.by(3) + + import_file_path2 = Rails.root.join("spec", "fixtures", "files", "partners_with_six_fields_and_duplicates.csv") data2 = File.read(import_file_path2, encoding: "BOM|UTF-8") csv2 = CSV.parse(data2, headers: true) - Partner.import_csv(csv2, organization.id) - expect(Partner.count).to eq before_import + 4 + expect do + Partner.import_csv(csv2, organization.id) + end.to change { Partner.count }.by(1) end it "imports partners from a csv file with BOM encodings" do @@ -281,6 +289,43 @@ Partner.import_csv(csv, organization.id) end.to change { Partner.count }.by(20) end + + it "imports partners with the correct values for fields" do + import_file_path = Rails.root.join("spec", "fixtures", "files", "partners_with_six_fields.csv") + data = File.read(import_file_path, encoding: "BOM|UTF-8") + csv = CSV.parse(data, headers: true) + response = Partner.import_csv(csv, organization.id) + + expect(response[:errors]).to be_empty + expect(response[:warnings]).to be_empty + partner = Partner.last + expect(StorageLocation.find(partner.default_storage_location_id).name).to eq("Smithsonian Conservation Center") + expect(partner.send_reminders).to eq(false) + expect(partner.quota).to eq(80) + expect(partner.notes).to eq("really ten out of ten") + end + + it "imports partners with all caps storage location" do + import_file_path = Rails.root.join("spec", "fixtures", "files", "partners_with_six_fields_capitalized_location.csv") + data = File.read(import_file_path, encoding: "BOM|UTF-8") + csv = CSV.parse(data, headers: true) + expect do + response = Partner.import_csv(csv, organization.id) + expect(response[:warnings]).to be_empty + end.to change { Partner.count }.by(3) + end + + context "when storage location is not found" do + it "returns a warning" do + import_file_path = Rails.root.join("spec", "fixtures", "files", "partners_with_six_fields_invalid_location.csv") + data = File.read(import_file_path, encoding: "BOM|UTF-8") + csv = CSV.parse(data, headers: true) + response = Partner.import_csv(csv, organization.id) + expect(response[:errors]).to be_empty + expect(response[:warnings]).to be_present + expect(response[:warnings].join).to include("Default storage location is not a storage location for this partner's organization") + end + end end describe '#quantity_year_to_date' do diff --git a/spec/requests/partners_requests_spec.rb b/spec/requests/partners_requests_spec.rb index 2e4255164f..9f333eb423 100644 --- a/spec/requests/partners_requests_spec.rb +++ b/spec/requests/partners_requests_spec.rb @@ -240,7 +240,7 @@ enable_quantity_based_requests: true) end - it "orders partners alphaetically" do + it "orders partners alphabetically" do get partners_path(partner, format: response_format) csv = CSV.parse(response.body) @@ -433,9 +433,12 @@ describe "POST #import_csv" do let(:model_class) { Partner } + let!(:outside_organization) { create(:organization) } + let!(:invalid_storage_location) { create(:storage_location, name: 'invalid', organization: outside_organization) } + let!(:valid_storage_location) { create(:storage_location, organization: organization) } context "with a csv file" do - let(:file) { fixture_file_upload("#{model_class.name.underscore.pluralize}.csv", "text/csv") } + let(:file) { fixture_file_upload("partners_with_six_fields.csv", "text/csv") } subject { post import_csv_partners_path, params: { file: file } } it "invokes .import_csv" do @@ -480,6 +483,46 @@ end end + context "csv file with send_reminders header and field missing" do + let(:file) { fixture_file_upload("partners_missing_send_reminders_field_and_header.csv", "text/csv") } + subject { post import_csv_partners_path, params: { file: file } } + + it "invokes .import_csv" do + expect(model_class).to respond_to(:import_csv).with(2).arguments + end + + it "redirects to :index" do + subject + expect(response).to be_redirect + end + + it "defaults send_reminders to false" do + subject + partner = Partner.find_by(name: "Partner 51") + expect(partner.send_reminders).to be(false) + end + end + + context "csv file with send_reminders field missing" do + let(:file) { fixture_file_upload("partners_missing_send_reminders_field.csv", "text/csv") } + subject { post import_csv_partners_path, params: { file: file } } + + it "invokes .import_csv" do + expect(model_class).to respond_to(:import_csv).with(2).arguments + end + + it "redirects to :index" do + subject + expect(response).to be_redirect + end + + it "defaults send_reminders to false" do + subject + partner = Partner.find_by(name: "Partner 51") + expect(partner.send_reminders).to be(false) + end + end + context "csv file with invalid email address" do let(:file) { fixture_file_upload("partners_with_invalid_email.csv", "text/csv") } subject { post import_csv_partners_path, params: { file: file } } @@ -499,6 +542,114 @@ expect(response).to have_error(/Partner 2: Email is invalid/) end end + + context "csv file with default storage location header and field missing" do + let(:file) { fixture_file_upload("partners_missing_default_storage_location_field_and_header.csv", "text/csv") } + subject { post import_csv_partners_path, params: { file: file } } + + it "invokes .import_csv" do + expect(model_class).to respond_to(:import_csv).with(2).arguments + end + + it "redirects to :index" do + subject + expect(response).to be_redirect + end + + it "presents a flash notice message" do + subject + expect(response).to have_notice "#{model_class.name.underscore.humanize.pluralize} were imported successfully!" + end + end + + context "csv file with default storage location field missing" do + let(:file) { fixture_file_upload("partners_missing_default_storage_location_field.csv", "text/csv") } + subject { post import_csv_partners_path, params: { file: file } } + + it "invokes .import_csv" do + expect(model_class).to respond_to(:import_csv).with(2).arguments + end + + it "redirects to :index" do + subject + expect(response).to be_redirect + end + + it "presents a flash notice message" do + subject + expect(response).to have_notice "#{model_class.name.underscore.humanize.pluralize} were imported successfully!" + end + end + + context "csv file with default storage location, email preferences, quota, and notes" do + let(:file) { fixture_file_upload("partners_with_six_fields.csv", "text/csv") } + subject { post import_csv_partners_path, params: { file: file } } + + it "invokes .import_csv" do + expect(model_class).to respond_to(:import_csv).with(2).arguments + end + + it "redirects to :index" do + subject + expect(response).to be_redirect + end + + it "presents a flash notice message" do + subject + expect(response).to have_notice "#{model_class.name.underscore.humanize.pluralize} were imported successfully!" + end + end + + context "csv file with an invalid storage location" do + let!(:current_organization) { create(:organization) } + let!(:outside_organization) { create(:organization) } + let(:file) { fixture_file_upload("partners_with_six_fields_invalid_location.csv", "text/csv") } + before do + allow(controller).to receive(:current_organization).and_return(current_organization) + end + + subject { post import_csv_partners_path, params: { file: file } } + + it "presents a flash error message" do + subject + expect(flash[:alert]).to be_present + expect(flash[:alert]).to match(/The following Partners imported with warnings/) + end + end + + context "csv file with a valid all-caps storage location" do + let(:file) { fixture_file_upload("partners_with_six_fields.csv", "text/csv") } + subject { post import_csv_partners_path, params: { file: file } } + + it "invokes .import_csv" do + expect(model_class).to respond_to(:import_csv).with(2).arguments + end + + it "redirects to :index" do + subject + expect(response).to be_redirect + end + + it "presents a flash notice message" do + subject + expect(response).to have_notice "#{model_class.name.underscore.humanize.pluralize} were imported successfully!" + end + end + + context "csv file with a blank line at the file's bottom" do + let(:file) { fixture_file_upload("partners_with_final_line_blank.csv", "text/csv") } + subject { post import_csv_partners_path, params: { file: file } } + + it "redirects to :index" do + subject + expect(response).to be_redirect + end + + it "presents a flash notice message" do + subject + expect(response).to have_notice "#{model_class.name.underscore.humanize.pluralize} were imported successfully!" + end + end end describe "POST #create" do diff --git a/spec/services/partner_create_service_spec.rb b/spec/services/partner_create_service_spec.rb index cc55d748ab..86eec31693 100644 --- a/spec/services/partner_create_service_spec.rb +++ b/spec/services/partner_create_service_spec.rb @@ -43,6 +43,32 @@ expect(query.first.enable_quantity_based_requests).to eq(true) end + context 'when send_reminders is nil' do + before do + partner_attrs.merge!(send_reminders: nil) + end + + it 'defaults send_reminders to false' do + subject + + partner = Partner.find_by(name: partner_attrs[:name]) + expect(partner.send_reminders).to be(false) + end + end + + context 'when send_reminders is missing' do + before do + partner_attrs.delete(:send_reminders) + end + + it 'defaults send_reminders to false' do + subject + + partner = Partner.find_by(name: partner_attrs[:name]) + expect(partner.send_reminders).to be(false) + end + end + context 'but there was an unexpected issue with saving the' do let(:error_message) { Faker::Games::ElderScrolls.dragon }