diff --git a/app/models/lettings_log.rb b/app/models/lettings_log.rb index ae01be1c6..5dc998bbb 100644 --- a/app/models/lettings_log.rb +++ b/app/models/lettings_log.rb @@ -65,6 +65,7 @@ class LettingsLog < Log .or(filter_by_id(param)) } scope :after_date, ->(date) { where("lettings_logs.startdate >= ?", date) } + scope :before_date, ->(date) { where("lettings_logs.startdate < ?", date) } scope :unresolved, -> { where(unresolved: true) } scope :age1_answered, -> { where.not(age1: nil).or(where(age1_known: 1)) } scope :tcharge_answered, -> { where.not(tcharge: nil).or(where(household_charge: 1)).or(where(is_carehome: 1)) } diff --git a/app/services/bulk_update_from_csv/update_locations_from_csv_service.rb b/app/services/bulk_update_from_csv/update_locations_from_csv_service.rb new file mode 100644 index 000000000..4a2577fc3 --- /dev/null +++ b/app/services/bulk_update_from_csv/update_locations_from_csv_service.rb @@ -0,0 +1,183 @@ +class BulkUpdateFromCsv::UpdateLocationsFromCsvService + def initialize(original_file_name:, updated_file_name:) + @original_file_name = original_file_name + @updated_file_name = updated_file_name + end + + def call + s3_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["CSV_DOWNLOAD_PAAS_INSTANCE"]) + + original_locations_csv = csv_from_path(@original_file_name, s3_service) + updated_locations_csv = csv_from_path(@updated_file_name, s3_service) + + updated_locations_csv.each do |row| + updated_attributes = attributes_from_row(row) + + original_row = original_locations_csv.find { |original_locations_row| original_locations_row[1] == updated_attributes["location_code"] } + if original_row.blank? || original_row["location_code"].nil? + Rails.logger.info("Location with id #{updated_attributes['location_code']} is not in the original location csv") + next + end + + original_attributes = attributes_from_row(original_row) + + location = Location.find_by(id: original_attributes["location_code"]) + if location.blank? + Rails.logger.info("Location with id #{original_attributes['location_code']} is not in the database") + next + end + + updated_attributes.each do |key, value| + next unless value != original_attributes[key] && value.present? + + case key + when "location_admin_district" + update_location_admin_district(location, original_attributes, value) + when "postcode" + update_postcode(location, original_attributes, value) + when "scheme_code" + update_scheme(location, original_attributes, value) + when "name", "units", "type_of_unit", "mobility_type" + begin + location[key] = value + Rails.logger.info("Updating location #{original_attributes['location_code']} with #{key}: #{value}") + rescue ArgumentError => e + Rails.logger.info("Cannot update location #{original_attributes['location_code']} with #{key}: #{value}. #{e.message}") + end + when "location_code", "status", "active_dates" + Rails.logger.info("Cannot update location #{original_attributes['location_code']} with #{key} as it it not a permitted field") + end + end + + unless location.changed? + Rails.logger.info("No changes to location #{original_attributes['location_code']}.") + next + end + + save_location(location, original_attributes) + end + end + +private + + def csv_from_path(path, s3_service) + original_file_io = s3_service.get_file_io(path) + original_file_io.set_encoding_by_bom + CSV.parse(original_file_io, headers: true) + end + + def attributes_from_row(row) + attributes = {} + attributes["scheme_code"] = row[0] + attributes["location_code"] = row[1] + attributes["postcode"] = row[2] + attributes["name"] = row[3] + attributes["status"] = row[4] + attributes["location_admin_district"] = row[5] + attributes["units"] = row[6] + attributes["type_of_unit"] = row[7] + attributes["mobility_type"] = row[8] + attributes["active_dates"] = row[9] + attributes + end + + def update_location_admin_district(location, original_attributes, value) + location_code = Location.local_authorities_for_current_year.key(value) + if location_code.present? + location.location_code = location_code + location.location_admin_district = value + Rails.logger.info("Updating location #{original_attributes['location_code']} with location_code: #{location_code}") + else + Rails.logger.info("Cannot update location #{original_attributes['location_code']} with location_admin_district: #{value}. Location admin distrint #{value} is not a valid option") + end + end + + def update_postcode(location, original_attributes, value) + if !value&.match(POSTCODE_REGEXP) + Rails.logger.info("Cannot update location #{original_attributes['location_code']} with postcode: #{value}. #{I18n.t('validations.postcode')}") + else + location.postcode = PostcodeService.clean(value) + Rails.logger.info("Updating location #{original_attributes['location_code']} with postcode: #{value}") + end + end + + def update_scheme(location, original_attributes, value) + scheme = Scheme.find_by(id: value.delete("S")) + if scheme.present? + original_scheme = Scheme.find_by(id: original_attributes["scheme_code"].delete("S")) + if original_scheme.nil? || !([original_scheme.owning_organisation] + original_scheme.owning_organisation.parent_organisations + original_scheme.owning_organisation.child_organisations).include?(scheme.owning_organisation) + Rails.logger.info("Cannot update location #{original_attributes['location_code']} with scheme_code: #{value}. Scheme with id #{value} is not in organisation that does not have relationship with the original organisation") + else + location["scheme_id"] = scheme.id + Rails.logger.info("Updating location #{original_attributes['location_code']} with scheme: S#{scheme.id}") + editable_from_date = FormHandler.instance.earliest_open_for_editing_collection_start_date + editable_logs = LettingsLog.where(location_id: location.id).after_date(editable_from_date) + Rails.logger.info("Clearing location and scheme for logs with startdate and location #{location.id}. Log IDs: #{editable_logs.map(&:id).join(', ')}") + editable_logs.update!(location: nil, scheme: nil, values_updated_at: Time.zone.now) + + logs_without_start_date = LettingsLog.where(scheme_id: scheme.id).where(startdate: nil) + Rails.logger.info("Clearing location and scheme for logs without startdate and location #{location.id}. Log IDs: #{logs_without_start_date.map(&:id).join(', ')}") + logs_without_start_date.update!(location: nil, scheme: nil, values_updated_at: Time.zone.now) + + exportable_from_date = FormHandler.instance.previous_collection_start_date + remaining_logs_to_export = LettingsLog.where(location_id: location.id).after_date(exportable_from_date) + Rails.logger.info("Clearing location and scheme for non editable logs with location #{location.id}. Log IDs: #{remaining_logs_to_export.map(&:id).join(', ')}") + remaining_logs_to_export.update_all(location_id: nil, scheme_id: nil, values_updated_at: Time.zone.now) + end + else + Rails.logger.info("Cannot update location #{original_attributes['location_code']} with scheme_code: #{value}. Scheme with id #{value} is not in the database") + end + end + + def save_location(location, original_attributes) + location.save! + Rails.logger.info("Saved location #{original_attributes['location_code']}.") + exportable_from_date = FormHandler.instance.previous_collection_start_date + logs_to_export = LettingsLog.where(location_id: location.id).after_date(exportable_from_date) + if original_attributes["location_admin_district"] != location.location_admin_district + clear_invalid_rent_fields(logs_to_export) + else + logs_to_export.update_all(values_updated_at: Time.zone.now) + end + + logs_not_to_export = LettingsLog.where(location_id: location.id).before_date(exportable_from_date) + Rails.logger.info("Will not export log #{logs_not_to_export.map(&:id).join(',')} as it is before the exportable date") if logs_not_to_export.any? + rescue ActiveRecord::RecordInvalid => e + Rails.logger.error("Cannot update location #{original_attributes['location_code']}. #{e.message}") + end + + def clear_invalid_rent_fields(logs) + logs.each do |log| + if log.rent_in_soft_min_range? || log.rent_in_soft_max_range? + complete_or_log_soft_invalidated_log(log) + else + log.validate + if log.errors["brent"].any? + Rails.logger.info("Log #{log.id} went from completed to in progress.") if log.status == "completed" + log.brent = nil + log.scharge = nil + log.pscharge = nil + log.supcharg = nil + end + log.values_updated_at = Time.zone.now + log.save!(validate: false) + end + end + end + + def complete_or_log_soft_invalidated_log(log) + return if log.rent_value_check.present? + + editable_from_date = FormHandler.instance.earliest_open_for_editing_collection_start_date + if log.startdate < editable_from_date + log.rent_value_check = 0 + Rails.logger.info("Confirmed rent value check for log #{log.id}.") + elsif log.status == "completed" + Rails.logger.info("Log #{log.id} went from completed to in progress.") + else + Rails.logger.info("Log #{log.id} stayed in progress, triggering soft rent value check.") + end + log.values_updated_at = Time.zone.now + log.save!(validate: false) + end +end diff --git a/app/services/bulk_update_from_csv/update_schemes_from_csv_service.rb b/app/services/bulk_update_from_csv/update_schemes_from_csv_service.rb new file mode 100644 index 000000000..bb4a0c261 --- /dev/null +++ b/app/services/bulk_update_from_csv/update_schemes_from_csv_service.rb @@ -0,0 +1,121 @@ +class BulkUpdateFromCsv::UpdateSchemesFromCsvService + def initialize(original_file_name:, updated_file_name:) + @original_file_name = original_file_name + @updated_file_name = updated_file_name + end + + def call + s3_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["CSV_DOWNLOAD_PAAS_INSTANCE"]) + + original_schemes_csv = csv_from_path(@original_file_name, s3_service) + updated_schemes_csv = csv_from_path(@updated_file_name, s3_service) + + updated_schemes_csv.each do |row| + updated_attributes = attributes_from_row(row) + + original_row = original_schemes_csv.find { |original_schemes_row| original_schemes_row[0] == updated_attributes["scheme_code"] } + if original_row.blank? || original_row["scheme_code"].nil? + Rails.logger.info("Scheme with id #{updated_attributes['scheme_code']} is not in the original scheme csv") + next + end + + original_attributes = attributes_from_row(original_row) + + scheme = Scheme.find_by(id: original_attributes["scheme_code"].delete("S")) + if scheme.blank? + Rails.logger.info("Scheme with id #{original_attributes['scheme_code']} is not in the database") + next + end + + updated_attributes.each do |key, value| + next unless value != original_attributes[key] && value.present? + + case key + when "owning_organisation" + update_owning_organisation(scheme, original_attributes, value) + when "service_name", "sensitive", "scheme_type", "registered_under_care_act", "arrangement_type", "primary_client_group", "has_other_client_group", "secondary_client_group", "support_type", "intended_stay" + begin + scheme[key] = value + Rails.logger.info("Updating scheme #{original_attributes['scheme_code']} with #{key}: #{value}") + rescue ArgumentError => e + Rails.logger.info("Cannot update scheme #{original_attributes['scheme_code']} with #{key}: #{value}. #{e.message}") + end + when "scheme_code", "status", "created_at", "active_dates" + Rails.logger.info("Cannot update scheme #{original_attributes['scheme_code']} with #{key} as it it not a permitted field") + end + end + + unless scheme.changed? + Rails.logger.info("No changes to scheme #{original_attributes['scheme_code']}.") + next + end + + save_scheme(scheme, original_attributes) + end + end + +private + + def csv_from_path(path, s3_service) + original_file_io = s3_service.get_file_io(path) + original_file_io.set_encoding_by_bom + CSV.parse(original_file_io, headers: true) + end + + def attributes_from_row(row) + attributes = {} + + attributes["scheme_code"] = row[0] + attributes["service_name"] = row[1] + attributes["status"] = row[2] + attributes["sensitive"] = row[3] + attributes["scheme_type"] = row[4] + attributes["registered_under_care_act"] = row[5] + attributes["owning_organisation"] = row[6] + attributes["arrangement_type"] = row[7] + attributes["primary_client_group"] = row[8] + attributes["has_other_client_group"] = row[9] + attributes["secondary_client_group"] = row[10] + attributes["support_type"] = row[11] + attributes["intended_stay"] = row[12] + attributes["created_at"] = row[13] + attributes["active_dates"] = row[14] + attributes + end + + def update_owning_organisation(scheme, original_attributes, value) + current_organisation = scheme.owning_organisation + organisation = Organisation.find_by(name: value) + if organisation.present? && (organisation.child_organisations.include?(current_organisation) || organisation.parent_organisations.include?(current_organisation)) + if LettingsLog.where(scheme_id: scheme.id).before_date(FormHandler.instance.lettings_earliest_open_for_editing_collection_start_date).any? + Rails.logger.info("Cannot update scheme #{original_attributes['scheme_code']} with owning_organisation: #{value}. There are lettings logs from closed collection period using this scheme") + else + scheme["owning_organisation_id"] = organisation.id + Rails.logger.info("Updating scheme #{original_attributes['scheme_code']} with owning_organisation: #{organisation.name}") + editable_from_date = FormHandler.instance.earliest_open_for_editing_collection_start_date + + editable_logs_with_startdate = LettingsLog.where(scheme_id: scheme.id).after_date(editable_from_date) + Rails.logger.info("Clearing location and scheme for logs with startdate and scheme S#{scheme.id}. Log IDs: #{editable_logs_with_startdate.map(&:id).join(', ')}") + editable_logs_with_startdate.update!(location: nil, scheme: nil) + + logs_without_start_date = LettingsLog.where(scheme_id: scheme.id).where(startdate: nil) + Rails.logger.info("Clearing location and scheme for logs without startdate and scheme S#{scheme.id}. Log IDs: #{logs_without_start_date.map(&:id).join(', ')}") + logs_without_start_date.update!(location: nil, scheme: nil) + end + else + Rails.logger.info("Cannot update scheme #{original_attributes['scheme_code']} with owning_organisation: #{value}. Organisation with name #{value} is not in the database or is not related to current organisation") + end + end + + def save_scheme(scheme, original_attributes) + scheme.save! + Rails.logger.info("Saved scheme #{original_attributes['scheme_code']}.") + exportable_from_date = FormHandler.instance.previous_collection_start_date + LettingsLog.where(scheme_id: scheme.id).after_date(exportable_from_date).update_all(values_updated_at: Time.zone.now) + LettingsLog.where(scheme_id: scheme.id).where(startdate: nil).update_all(values_updated_at: Time.zone.now) + logs_not_to_export = LettingsLog.where(scheme_id: scheme.id).before_date(exportable_from_date) + Rails.logger.info("Will not export log #{logs_not_to_export.map(&:id).join(',')} as it is before the exportable date") if logs_not_to_export.any? + rescue ActiveRecord::RecordInvalid => e + Rails.logger.error("Cannot update scheme #{original_attributes['scheme_code']}. #{e.message}") + end +end diff --git a/lib/tasks/update_schemes_and_locations_from_csv.rake b/lib/tasks/update_schemes_and_locations_from_csv.rake new file mode 100644 index 000000000..645f1c57d --- /dev/null +++ b/lib/tasks/update_schemes_and_locations_from_csv.rake @@ -0,0 +1,21 @@ +namespace :bulk_update do + desc "Bulk update scheme data from a csv file" + task :update_schemes_from_csv, %i[original_file_name updated_file_name] => :environment do |_task, args| + original_file_name = args[:original_file_name] + updated_file_name = args[:updated_file_name] + + raise "Usage: rake bulk_update:update_schemes_from_csv['original_file_name','updated_file_name']" if original_file_name.blank? || updated_file_name.blank? + + BulkUpdateFromCsv::UpdateSchemesFromCsvService.new(original_file_name:, updated_file_name:).call + end + + desc "Bulk update location data from a csv file" + task :update_locations_from_csv, %i[original_file_name updated_file_name] => :environment do |_task, args| + original_file_name = args[:original_file_name] + updated_file_name = args[:updated_file_name] + + raise "Usage: rake bulk_update:update_locations_from_csv['original_file_name','updated_file_name']" if original_file_name.blank? || updated_file_name.blank? + + BulkUpdateFromCsv::UpdateLocationsFromCsvService.new(original_file_name:, updated_file_name:).call + end +end diff --git a/spec/factories/lettings_log.rb b/spec/factories/lettings_log.rb index 503808fa4..ba207c97d 100644 --- a/spec/factories/lettings_log.rb +++ b/spec/factories/lettings_log.rb @@ -178,7 +178,7 @@ FactoryBot.define do end trait :sh do needstype { 2 } - sheltered { 0 } + sheltered { 3 } household_charge { 0 } end trait :sheltered_housing do diff --git a/spec/fixtures/files/original_locations.csv b/spec/fixtures/files/original_locations.csv new file mode 100644 index 000000000..88e308b21 --- /dev/null +++ b/spec/fixtures/files/original_locations.csv @@ -0,0 +1,5 @@ +scheme_code,location_code,location_postcode,location_name,location_status,location_local_authority,location_units,location_type_of_unit,location_mobility_type,location_active_dates +{scheme_id1},{id1},SW1A 2AA,Downing Street,active,Wigan,20,Self-contained house,Fitted with equipment and adaptations,"Active from 1 April 2022" +{scheme_id2},{id2},SW1A 2AA,Downing Street,active,Wigan,20,Self-contained house,Fitted with equipment and adaptations,"Active from 1 April 2022" +{scheme_id3},{id3},SW1A 2AA,Downing Street,active,Wigan,20,Self-contained house,Fitted with equipment and adaptations,"Active from 1 April 2022" +1,SWrong_id,SW1A 2AA,Downing Street,active,Wigan,20,Self-contained house,Fitted with equipment and adaptations,"Active from 1 April 2022" diff --git a/spec/fixtures/files/original_schemes.csv b/spec/fixtures/files/original_schemes.csv new file mode 100644 index 000000000..840d7e7a4 --- /dev/null +++ b/spec/fixtures/files/original_schemes.csv @@ -0,0 +1,6 @@ +scheme_code,scheme_service_name,scheme_status,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_support_services_provided_by,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,scheme_active_dates +{id1},Test name,active,Yes,Housing for older people,Yes – registered care home providing nursing care,DLUHC,The same organisation that owns the housing stock,People with alcohol problems,Yes,Older people with support needs,High level,Medium stay,2021-04-01T00:00:00+01:00,"Active from 1 April 2020" +{id2},Test name,active,Yes,Housing for older people,Yes – registered care home providing nursing care,DLUHC,The same organisation that owns the housing stock,People with alcohol problems,Yes,Older people with support needs,High level,Medium stay,2021-04-01T00:00:00+01:00,"Active from 1 April 2020" +{id3},Test name,active,Yes,Housing for older people,Yes – registered care home providing nursing care,DLUHC,The same organisation that owns the housing stock,People with alcohol problems,Yes,Older people with support needs,High level,Medium stay,2021-04-01T00:00:00+01:00,"Active from 1 April 2020" +{id4},Incomplete scheme,incomplete,Yes,Housing for older people,Yes – registered care home providing nursing care,DLUHC,The same organisation that owns the housing stock,People with alcohol problems,,,,,2021-04-01T00:00:00+01:00,"Active from 1 April 2020" +SWrong_id,Incomplete scheme,incomplete,Yes,Housing for older people,Yes – registered care home providing nursing care,DLUHC,The same organisation that owns the housing stock,People with alcohol problems,,,,,2021-04-01T00:00:00+01:00,"Active from 1 April 2020" diff --git a/spec/fixtures/files/updated_locations.csv b/spec/fixtures/files/updated_locations.csv new file mode 100644 index 000000000..a04c51683 --- /dev/null +++ b/spec/fixtures/files/updated_locations.csv @@ -0,0 +1,6 @@ +scheme_code,location_code,location_postcode,location_name,location_status,location_local_authority,location_units,location_type_of_unit,location_mobility_type,location_active_dates +{scheme_id1},{id1},B11BB,Updated name,deactivating_soon,Westminster,10,Bungalow,Wheelchair-user standard,"Active from 1 April 2028" +{scheme_id2},{id2},SW1A 2AA,Downing Street,active,Wigan,20,Self-contained house,Fitted with equipment and adaptations,"Active from 1 April 2022" +{scheme_id3},{id3},SWAAA,,deactivating_soon,Westminst,,elf-contained house,55,"Active from 1 April 2022" +x,Wrong_id,SWAAA,,deactivating_soon,Westminst,,elf-contained house,55,"Active from 1 April 2022" +x,SWrong_id,SWAAA,,deactivating_soon,Westminst,,elf-contained house,55,"Active from 1 April 2022" diff --git a/spec/fixtures/files/updated_schemes.csv b/spec/fixtures/files/updated_schemes.csv new file mode 100644 index 000000000..e83bd7584 --- /dev/null +++ b/spec/fixtures/files/updated_schemes.csv @@ -0,0 +1,6 @@ +scheme_code,scheme_service_name,scheme_status,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_support_services_provided_by,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,scheme_active_dates +{id1},Updated test name,incomplete,No,Direct Access Hostel,No,Different organisation,Another registered stock owner,People with drug problems,No,Older people with support needs,Low level,Permanent,2022-04-01T00:00:00+01:00,"Active from 2 April 2020" +{id2},Test name,active,Yes,Housing for older people,Yes – registered care home providing nursing care,DLUHC,The same organisation that owns the housing stock,People with alcohol problems,Yes,Older people with support needs,High level,Medium stay,2021-04-01T00:00:00+01:00,"Active from 1 April 2020" +{id3}, ,active,Yse,Direct access Hostel,Yes – registered care home providing nursing care,non existing org,wrong answer,FD,no,lder people with support needs,high,Permanent ,2021-04-01T00:00:00+01:00,"Active from 1 April 2020" +Wrong_id,Incomplete scheme,incomplete,Yes,Housing for older people,No,DLUHC,The same organisation that owns the housing stock,People with alcohol problems,,,,,2021-04-01T00:00:00+01:00,"Active from 1 April 2020" +SWrong_id,Incomplete scheme,incomplete,Yes,Housing for older people,No,DLUHC,The same organisation that owns the housing stock,People with alcohol problems,,,,,2021-04-01T00:00:00+01:00,"Active from 1 April 2020" diff --git a/spec/lib/tasks/update_schemes_and_locations_from_csv_spec.rb b/spec/lib/tasks/update_schemes_and_locations_from_csv_spec.rb new file mode 100644 index 000000000..50dd98400 --- /dev/null +++ b/spec/lib/tasks/update_schemes_and_locations_from_csv_spec.rb @@ -0,0 +1,651 @@ +require "rails_helper" +require "rake" + +RSpec.describe "bulk_update" do + def replace_entity_ids(scheme_1, scheme_2, scheme_3, export_template) + export_template.sub!(/\{id1\}/, "S#{scheme_1.id}") + export_template.sub!(/\{id2\}/, "S#{scheme_2.id}") + export_template.sub!(/\{id3\}/, "S#{scheme_3.id}") + end + + def replace_entity_ids_for_locations(location_1, location_2, location_3, scheme_1, scheme_2, scheme_3, export_template) + export_template.sub!(/\{id1\}/, location_1.id.to_s) + export_template.sub!(/\{id2\}/, location_2.id.to_s) + export_template.sub!(/\{id3\}/, location_3.id.to_s) + export_template.sub!(/\{scheme_id1\}/, "S#{scheme_1['id']}") + export_template.sub!(/\{scheme_id2\}/, "S#{scheme_2['id']}") + export_template.sub!(/\{scheme_id3\}/, "S#{scheme_3['id']}") + end + + before do + allow(Storage::S3Service).to receive(:new).and_return(storage_service) + allow(Configuration::EnvConfigurationService).to receive(:new).and_return(env_config_service) + allow(ENV).to receive(:[]) + allow(ENV).to receive(:[]).with("CSV_DOWNLOAD_PAAS_INSTANCE").and_return(instance_name) + + WebMock.stub_request(:get, /api\.postcodes\.io/) + .to_return(status: 200, body: "{\"status\":404,\"error\":\"Postcode not found\"}", headers: {}) + WebMock.stub_request(:get, /api\.postcodes\.io\/postcodes\/B11BB/) + .to_return(status: 200, body: '{"status":200,"result":{"admin_district":"Westminster","codes":{"admin_district":"E09000033"}}}', headers: {}) + end + + describe ":update_schemes_from_csv", type: :task do + subject(:task) { Rake::Task["bulk_update:update_schemes_from_csv"] } + + let(:instance_name) { "import_instance" } + let(:storage_service) { instance_double(Storage::S3Service) } + let(:env_config_service) { instance_double(Configuration::EnvConfigurationService) } + + before do + Rake.application.rake_require("tasks/update_schemes_and_locations_from_csv") + Rake::Task.define_task(:environment) + task.reenable + end + + context "when the rake task is run" do + let(:original_schemes_csv_path) { "original_schemes.csv" } + let(:updated_schemes_csv_path) { "updated_schemes.csv" } + let(:wrong_file_path) { "/test/no_csv_here.csv" } + let!(:different_organisation) { FactoryBot.create(:organisation, name: "Different organisation") } + let(:schemes) do + create_list(:scheme, + 3, + service_name: "Test name", + sensitive: 1, + registered_under_care_act: 4, + support_type: 4, + scheme_type: 7, + arrangement_type: "D", + intended_stay: "M", + primary_client_group: "G", + secondary_client_group: "M", + has_other_client_group: 1, + owning_organisation: FactoryBot.create(:organisation), + confirmed: true, + created_at: Time.zone.local(2021, 4, 1), + total_units: 2) + end + let(:location) { FactoryBot.create(:location, scheme: schemes[0]) } + let(:location_2) { FactoryBot.create(:location, scheme: schemes[1]) } + let(:location_3) { FactoryBot.create(:location, scheme: schemes[2]) } + let!(:lettings_log) { FactoryBot.create(:lettings_log, :sh, scheme: schemes[0], location:, values_updated_at: nil, owning_organisation: schemes[0].owning_organisation) } + let!(:lettings_log_2) { FactoryBot.create(:lettings_log, :sh, scheme: schemes[1], location: location_2, values_updated_at: nil, owning_organisation: schemes[1].owning_organisation) } + let!(:lettings_log_3) { FactoryBot.create(:lettings_log, :sh, scheme: schemes[2], location: location_3, values_updated_at: nil, owning_organisation: schemes[2].owning_organisation) } + let!(:lettings_log_4) { FactoryBot.create(:lettings_log, :sh, scheme: schemes[0], location:, values_updated_at: nil, owning_organisation: schemes[0].owning_organisation) } + let!(:lettings_log_5) { FactoryBot.create(:lettings_log, :sh, scheme: schemes[0], location:, values_updated_at: nil, owning_organisation: schemes[0].owning_organisation) } + + before do + allow(storage_service).to receive(:get_file_io) + .with("original_schemes.csv") + .and_return(StringIO.new(replace_entity_ids(schemes[0], schemes[1], schemes[2], File.open("./spec/fixtures/files/original_schemes.csv").read))) + + allow(storage_service).to receive(:get_file_io) + .with("updated_schemes.csv") + .and_return(StringIO.new(replace_entity_ids(schemes[0], schemes[1], schemes[2], File.open("./spec/fixtures/files/updated_schemes.csv").read))) + end + + it "updates the allowed scheme fields if they have changed and doesn't update other fields" do + create(:organisation_relationship, parent_organisation: schemes[0].owning_organisation, child_organisation: different_organisation) + + task.invoke(original_schemes_csv_path, updated_schemes_csv_path) + schemes[0].reload + expect(schemes[0].service_name).to eq("Updated test name") + expect(schemes[0].sensitive).to eq("No") + expect(schemes[0].registered_under_care_act).to eq("No") + expect(schemes[0].support_type).to eq("Low level") + expect(schemes[0].scheme_type).to eq("Direct Access Hostel") + expect(schemes[0].arrangement_type).to eq("Another registered stock owner") + expect(schemes[0].intended_stay).to eq("Permanent") + expect(schemes[0].primary_client_group).to eq("People with drug problems") + expect(schemes[0].has_other_client_group).to eq("No") + expect(schemes[0].owning_organisation).to eq(different_organisation) + expect(schemes[0].created_at).to eq(Time.zone.local(2021, 4, 1)) + expect(schemes[0].total_units).to eq(2) + end + + it "updates the lettings log if scheme has changed owning organisation" do + create(:organisation_relationship, parent_organisation: schemes[0].owning_organisation, child_organisation: different_organisation) + + task.invoke(original_schemes_csv_path, updated_schemes_csv_path) + + lettings_log.reload + expect(lettings_log.scheme).to be_nil + expect(lettings_log.location).to be_nil + end + + it "does not update the scheme if it hasn't changed" do + task.invoke(original_schemes_csv_path, updated_schemes_csv_path) + schemes[1].reload + expect(schemes[1].service_name).to eq("Test name") + expect(schemes[1].sensitive).to eq("Yes") + expect(schemes[1].registered_under_care_act).to eq("Yes – registered care home providing nursing care") + expect(schemes[1].support_type).to eq("High level") + expect(schemes[1].scheme_type).to eq("Housing for older people") + expect(schemes[1].arrangement_type).to eq("The same organisation that owns the housing stock") + expect(schemes[1].intended_stay).to eq("Medium stay") + expect(schemes[1].primary_client_group).to eq("People with alcohol problems") + expect(schemes[1].secondary_client_group).to eq("Older people with support needs") + expect(schemes[1].has_other_client_group).to eq("Yes") + expect(schemes[1].owning_organisation).not_to eq(different_organisation) + expect(schemes[1].created_at).to eq(Time.zone.local(2021, 4, 1)) + expect(schemes[1].total_units).to eq(2) + end + + it "does not update the scheme with invalid values" do + task.invoke(original_schemes_csv_path, updated_schemes_csv_path) + schemes[2].reload + expect(schemes[2].service_name).to eq("Test name") + expect(schemes[2].sensitive).to eq("Yes") + expect(schemes[2].registered_under_care_act).to eq("Yes – registered care home providing nursing care") + expect(schemes[2].support_type).to eq("High level") + expect(schemes[2].scheme_type).to eq("Housing for older people") + expect(schemes[2].arrangement_type).to eq("The same organisation that owns the housing stock") + expect(schemes[2].intended_stay).to eq("Medium stay") + expect(schemes[2].primary_client_group).to eq("People with alcohol problems") + expect(schemes[2].secondary_client_group).to eq("Older people with support needs") + expect(schemes[2].has_other_client_group).to eq("Yes") + expect(schemes[2].owning_organisation).not_to eq(different_organisation) + expect(schemes[2].created_at).to eq(Time.zone.local(2021, 4, 1)) + expect(schemes[2].total_units).to eq(2) + end + + it "does not update the owning organisation if the new organisation is not related to current organisation" do + task.invoke(original_schemes_csv_path, updated_schemes_csv_path) + schemes[0].reload + expect(schemes[0].owning_organisation).not_to eq(different_organisation) + end + + it "does not update the owning organisation if there are logs from closed collection periods" do + create(:organisation_relationship, parent_organisation: schemes[0].owning_organisation, child_organisation: different_organisation) + lettings_log_4.startdate = Time.zone.local(2022, 4, 1) + lettings_log_4.save!(validate: false) + lettings_log_5.startdate = Time.zone.local(2021, 4, 1) + lettings_log_5.save!(validate: false) + task.invoke(original_schemes_csv_path, updated_schemes_csv_path) + + schemes[0].reload + expect(schemes[0].owning_organisation).not_to eq(different_organisation) + end + + it "does not update the lettings log if scheme owning organisation didn't change" do + task.invoke(original_schemes_csv_path, updated_schemes_csv_path) + + lettings_log.reload + expect(lettings_log.scheme).not_to be_nil + expect(lettings_log.location).not_to be_nil + end + + it "only re-exports the logs for the schemes that have been updated" do + lettings_log_4.startdate = Time.zone.local(2022, 4, 1) + lettings_log_4.save!(validate: false) + lettings_log_5.startdate = Time.zone.local(2021, 4, 1) + lettings_log_5.save!(validate: false) + task.invoke(original_schemes_csv_path, updated_schemes_csv_path) + + lettings_log.reload + expect(lettings_log.values_updated_at).not_to eq(nil) + + lettings_log_2.reload + expect(lettings_log_2.values_updated_at).to eq(nil) + + lettings_log_3.reload + expect(lettings_log_3.values_updated_at).to eq(nil) + + lettings_log_4.reload + expect(lettings_log_4.values_updated_at).not_to eq(nil) + + lettings_log_5.reload + expect(lettings_log_5.values_updated_at).to eq(nil) + end + + it "logs the progress of the update" do + create(:organisation_relationship, parent_organisation: schemes[0].owning_organisation, child_organisation: different_organisation) + + expect(Rails.logger).to receive(:info).with("Updating scheme S#{schemes[0].id} with service_name: Updated test name") + expect(Rails.logger).to receive(:info).with("Updating scheme S#{schemes[0].id} with sensitive: No") + expect(Rails.logger).to receive(:info).with("Updating scheme S#{schemes[0].id} with scheme_type: Direct Access Hostel") + expect(Rails.logger).to receive(:info).with("Clearing location and scheme for logs with startdate and scheme S#{schemes[0].id}. Log IDs: ") + expect(Rails.logger).to receive(:info).with("Clearing location and scheme for logs without startdate and scheme S#{schemes[0].id}. Log IDs: #{lettings_log.id}, #{lettings_log_4.id}, #{lettings_log_5.id}") + expect(Rails.logger).to receive(:info).with("Updating scheme S#{schemes[0].id} with arrangement_type: Another registered stock owner") + expect(Rails.logger).to receive(:info).with("Updating scheme S#{schemes[0].id} with primary_client_group: People with drug problems") + expect(Rails.logger).to receive(:info).with("Updating scheme S#{schemes[0].id} with has_other_client_group: No") + expect(Rails.logger).to receive(:info).with("Updating scheme S#{schemes[0].id} with support_type: Low level") + expect(Rails.logger).to receive(:info).with("Updating scheme S#{schemes[0].id} with intended_stay: Permanent") + expect(Rails.logger).to receive(:info).with("Updating scheme S#{schemes[0].id} with registered_under_care_act: No") + expect(Rails.logger).to receive(:info).with("Updating scheme S#{schemes[0].id} with owning_organisation: Different organisation") + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[0].id} with status as it it not a permitted field") + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[0].id} with created_at as it it not a permitted field") + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[0].id} with active_dates as it it not a permitted field") + expect(Rails.logger).to receive(:info).with("Saved scheme S#{schemes[0].id}.") + + expect(Rails.logger).to receive(:info).with("No changes to scheme S#{schemes[1].id}.") + + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[2].id} with sensitive: Yse. 'Yse' is not a valid sensitive") + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[2].id} with scheme_type: Direct access Hostel. 'Direct access Hostel' is not a valid scheme_type") + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[2].id} with owning_organisation: non existing org. Organisation with name non existing org is not in the database or is not related to current organisation") + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[2].id} with arrangement_type: wrong answer. 'wrong answer' is not a valid arrangement_type") + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[2].id} with primary_client_group: FD. 'FD' is not a valid primary_client_group") + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[2].id} with has_other_client_group: no. 'no' is not a valid has_other_client_group") + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[2].id} with secondary_client_group: lder people with support needs. 'lder people with support needs' is not a valid secondary_client_group") + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[2].id} with support_type: high. 'high' is not a valid support_type") + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[2].id} with intended_stay: Permanent . 'Permanent ' is not a valid intended_stay") + + expect(Rails.logger).to receive(:info).with("No changes to scheme S#{schemes[2].id}.") + + expect(Rails.logger).to receive(:info).with("Scheme with id Wrong_id is not in the original scheme csv") + expect(Rails.logger).to receive(:info).with("Scheme with id SWrong_id is not in the database") + + task.invoke(original_schemes_csv_path, updated_schemes_csv_path) + end + + it "raises an error when no paths are given" do + expect { task.invoke(nil) }.to raise_error(RuntimeError, "Usage: rake bulk_update:update_schemes_from_csv['original_file_name','updated_file_name']") + end + + it "raises an error when no original path is given" do + expect { task.invoke(nil, updated_schemes_csv_path) }.to raise_error(RuntimeError, "Usage: rake bulk_update:update_schemes_from_csv['original_file_name','updated_file_name']") + end + + it "raises an error when no updated path is given" do + expect { task.invoke(original_schemes_csv_path, nil) }.to raise_error(RuntimeError, "Usage: rake bulk_update:update_schemes_from_csv['original_file_name','updated_file_name']") + end + + it "logs an error if a validation fails and processes the rest of the rows" do + create(:organisation_relationship, parent_organisation: schemes[0].owning_organisation, child_organisation: different_organisation) + lettings_log_4.startdate = Time.zone.local(2022, 4, 1) + lettings_log_4.save!(validate: false) + lettings_log_5.startdate = Time.zone.local(2021, 4, 1) + lettings_log_5.save!(validate: false) + + schemes[1].support_type = nil + schemes[1].save!(validate: false) + expect(Rails.logger).to receive(:info).with("Updating scheme S#{schemes[0].id} with service_name: Updated test name") + expect(Rails.logger).to receive(:info).with("Updating scheme S#{schemes[0].id} with sensitive: No") + expect(Rails.logger).to receive(:info).with("Updating scheme S#{schemes[0].id} with scheme_type: Direct Access Hostel") + expect(Rails.logger).to receive(:info).with("Updating scheme S#{schemes[0].id} with arrangement_type: Another registered stock owner") + expect(Rails.logger).to receive(:info).with("Updating scheme S#{schemes[0].id} with primary_client_group: People with drug problems") + expect(Rails.logger).to receive(:info).with("Updating scheme S#{schemes[0].id} with has_other_client_group: No") + expect(Rails.logger).to receive(:info).with("Updating scheme S#{schemes[0].id} with support_type: Low level") + expect(Rails.logger).to receive(:info).with("Updating scheme S#{schemes[0].id} with intended_stay: Permanent") + expect(Rails.logger).to receive(:info).with("Updating scheme S#{schemes[0].id} with registered_under_care_act: No") + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[0].id} with status as it it not a permitted field") + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[0].id} with created_at as it it not a permitted field") + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[0].id} with active_dates as it it not a permitted field") + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[0].id} with owning_organisation: Different organisation. There are lettings logs from closed collection period using this scheme") + expect(Rails.logger).to receive(:info).with("Saved scheme S#{schemes[0].id}.") + expect(Rails.logger).to receive(:info).with("Will not export log #{lettings_log_5.id} as it is before the exportable date") + + expect(Rails.logger).to receive(:info).with("No changes to scheme S#{schemes[1].id}.") + + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[2].id} with sensitive: Yse. 'Yse' is not a valid sensitive") + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[2].id} with scheme_type: Direct access Hostel. 'Direct access Hostel' is not a valid scheme_type") + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[2].id} with owning_organisation: non existing org. Organisation with name non existing org is not in the database or is not related to current organisation") + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[2].id} with arrangement_type: wrong answer. 'wrong answer' is not a valid arrangement_type") + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[2].id} with primary_client_group: FD. 'FD' is not a valid primary_client_group") + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[2].id} with has_other_client_group: no. 'no' is not a valid has_other_client_group") + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[2].id} with secondary_client_group: lder people with support needs. 'lder people with support needs' is not a valid secondary_client_group") + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[2].id} with support_type: high. 'high' is not a valid support_type") + expect(Rails.logger).to receive(:info).with("Cannot update scheme S#{schemes[2].id} with intended_stay: Permanent . 'Permanent ' is not a valid intended_stay") + expect(Rails.logger).to receive(:info).with("No changes to scheme S#{schemes[2].id}.") + + expect(Rails.logger).to receive(:info).with("Scheme with id Wrong_id is not in the original scheme csv") + expect(Rails.logger).to receive(:info).with("Scheme with id SWrong_id is not in the database") + + task.invoke(original_schemes_csv_path, updated_schemes_csv_path) + end + end + end + + describe ":update_locations_from_csv", type: :task do + subject(:task) { Rake::Task["bulk_update:update_locations_from_csv"] } + + let(:instance_name) { "import_instance" } + let(:storage_service) { instance_double(Storage::S3Service) } + let(:env_config_service) { instance_double(Configuration::EnvConfigurationService) } + + before do + Rake.application.rake_require("tasks/update_schemes_and_locations_from_csv") + Rake::Task.define_task(:environment) + task.reenable + end + + context "when the rake task is run" do + let(:original_locations_csv_path) { "original_locations.csv" } + let(:updated_locations_csv_path) { "updated_locations.csv" } + let(:wrong_file_path) { "/test/no_csv_here.csv" } + let!(:scheme) { FactoryBot.create(:scheme, service_name: "Scheme 1") } + let!(:different_scheme) { FactoryBot.create(:scheme, service_name: "Different scheme", owning_organisation: scheme.owning_organisation) } + + let(:locations) do + create_list(:location, + 3, + postcode: "SW1A 2AA", + name: "Downing Street", + type_of_unit: "Self-contained house", + units: 20, + mobility_type: "Fitted with equipment and adaptations", + location_code: "E08000010", + location_admin_district: "Wigan", + startdate: Time.zone.local(2022, 4, 1), + confirmed: true, + updated_at: Time.zone.local(2022, 3, 1), + scheme:) + end + let!(:lettings_log) { FactoryBot.create(:lettings_log, :sh, location: locations[0], scheme:, values_updated_at: nil, startdate: Time.zone.local(2023, 4, 4)) } + let!(:lettings_log_2) { FactoryBot.create(:lettings_log, :sh, location: locations[1], scheme:, values_updated_at: nil, startdate: Time.zone.local(2023, 4, 4)) } + let!(:lettings_log_3) { FactoryBot.create(:lettings_log, :sh, location: locations[2], scheme:, values_updated_at: nil, startdate: Time.zone.local(2023, 4, 4)) } + let!(:lettings_log_4) { FactoryBot.create(:lettings_log, :sh, location: locations[0], scheme:, values_updated_at: nil, startdate: Time.zone.local(2023, 4, 4)) } + let!(:lettings_log_5) { FactoryBot.create(:lettings_log, :sh, location: locations[0], scheme:, values_updated_at: nil, startdate: Time.zone.local(2023, 4, 4)) } + + before do + allow(storage_service).to receive(:get_file_io) + .with("original_locations.csv") + .and_return(StringIO.new(replace_entity_ids_for_locations(locations[0], locations[1], locations[2], scheme, scheme, scheme, File.open("./spec/fixtures/files/original_locations.csv").read))) + + allow(storage_service).to receive(:get_file_io) + .with("updated_locations.csv") + .and_return(StringIO.new(replace_entity_ids_for_locations(locations[0], locations[1], locations[2], different_scheme, scheme, { id: "non existent scheme id" }, File.open("./spec/fixtures/files/updated_locations.csv").read))) + end + + it "updates the allowed location fields if they have changed and doesn't update other fields" do + task.invoke(original_locations_csv_path, updated_locations_csv_path) + locations[0].reload + expect(locations[0].postcode).to eq("B1 1BB") + expect(locations[0].name).to eq("Updated name") + expect(locations[0].type_of_unit).to eq("Bungalow") + expect(locations[0].units).to eq(10) + expect(locations[0].mobility_type).to eq("Wheelchair-user standard") + expect(locations[0].location_code).to eq("E09000033") + expect(locations[0].location_admin_district).to eq("Westminster") + expect(locations[0].scheme).to eq(different_scheme) + end + + it "does not update the location if it hasn't changed" do + task.invoke(original_locations_csv_path, updated_locations_csv_path) + locations[1].reload + expect(locations[1].postcode).to eq("SW1A 2AA") + expect(locations[1].name).to eq("Downing Street") + expect(locations[1].type_of_unit).to eq("Self-contained house") + expect(locations[1].units).to eq(20) + expect(locations[1].mobility_type).to eq("Fitted with equipment and adaptations") + expect(locations[1].location_code).to eq("E08000010") + expect(locations[1].location_admin_district).to eq("Wigan") + expect(locations[1].scheme).to eq(scheme) + end + + it "does not update the location with invalid values" do + task.invoke(original_locations_csv_path, updated_locations_csv_path) + locations[2].reload + expect(locations[2].postcode).to eq("SW1A 2AA") + expect(locations[2].name).to eq("Downing Street") + expect(locations[2].type_of_unit).to eq("Self-contained house") + expect(locations[2].units).to eq(20) + expect(locations[2].mobility_type).to eq("Fitted with equipment and adaptations") + expect(locations[2].location_code).to eq("E08000010") + expect(locations[2].location_admin_district).to eq("Wigan") + expect(locations[2].scheme).to eq(scheme) + end + + it "does not update the scheme id if the new scheme is not in the same or related organisation as the old scheme" do + different_scheme.update!(owning_organisation: FactoryBot.create(:organisation)) + task.invoke(original_locations_csv_path, updated_locations_csv_path) + locations[0].reload + expect(locations[0].scheme).not_to eq(different_scheme) + end + + it "only re-exports the logs for the locations that have been updated" do + lettings_log_4.startdate = Time.zone.local(2022, 4, 1) + lettings_log_4.save!(validate: false) + lettings_log_5.startdate = Time.zone.local(2021, 4, 1) + lettings_log_5.save!(validate: false) + + task.invoke(original_locations_csv_path, updated_locations_csv_path) + + lettings_log.reload + expect(lettings_log.values_updated_at).not_to eq(nil) + + lettings_log_2.reload + expect(lettings_log_2.values_updated_at).to eq(nil) + + lettings_log_3.reload + expect(lettings_log_3.values_updated_at).to eq(nil) + + lettings_log_4.reload + expect(lettings_log_4.values_updated_at).not_to eq(nil) + + lettings_log_5.reload + expect(lettings_log_5.values_updated_at).to eq(nil) + end + + it "logs the progress of the update" do + lettings_log_4.startdate = Time.zone.local(2022, 4, 1) + lettings_log_4.save!(validate: false) + lettings_log_5.startdate = Time.zone.local(2021, 4, 1) + lettings_log_5.save!(validate: false) + + expect(Rails.logger).to receive(:info).with("Updating location #{locations[0].id} with scheme: S#{different_scheme.id}") + expect(Rails.logger).to receive(:info).with("Clearing location and scheme for logs with startdate and location #{locations[0].id}. Log IDs: #{lettings_log.id}") + expect(Rails.logger).to receive(:info).with("Clearing location and scheme for logs without startdate and location #{locations[0].id}. Log IDs: ") + expect(Rails.logger).to receive(:info).with("Clearing location and scheme for non editable logs with location #{locations[0].id}. Log IDs: #{lettings_log_4.id}") + expect(Rails.logger).to receive(:info).with("Updating location #{locations[0].id} with postcode: B11BB") + expect(Rails.logger).to receive(:info).with("Updating location #{locations[0].id} with name: Updated name") + expect(Rails.logger).to receive(:info).with("Updating location #{locations[0].id} with location_code: E09000033") + expect(Rails.logger).to receive(:info).with("Updating location #{locations[0].id} with type_of_unit: Bungalow") + expect(Rails.logger).to receive(:info).with("Updating location #{locations[0].id} with units: 10") + expect(Rails.logger).to receive(:info).with("Updating location #{locations[0].id} with mobility_type: Wheelchair-user standard") + expect(Rails.logger).to receive(:info).with("Cannot update location #{locations[0].id} with status as it it not a permitted field") + expect(Rails.logger).to receive(:info).with("Cannot update location #{locations[0].id} with active_dates as it it not a permitted field") + expect(Rails.logger).to receive(:info).with("Saved location #{locations[0].id}.") + + expect(Rails.logger).to receive(:info).with("Will not export log #{lettings_log_5.id} as it is before the exportable date") + expect(Rails.logger).to receive(:info).with("No changes to location #{locations[1].id}.") + + expect(Rails.logger).to receive(:info).with("Cannot update location #{locations[2].id} with postcode: SWAAA. Enter a postcode in the correct format, for example AA1 1AA") + expect(Rails.logger).to receive(:info).with("Cannot update location #{locations[2].id} with scheme_code: S. Scheme with id S is not in the database") + expect(Rails.logger).to receive(:info).with("Cannot update location #{locations[2].id} with location_admin_district: Westminst. Location admin distrint Westminst is not a valid option") + expect(Rails.logger).to receive(:info).with("Cannot update location #{locations[2].id} with type_of_unit: elf-contained house. 'elf-contained house' is not a valid type_of_unit") + expect(Rails.logger).to receive(:info).with("Cannot update location #{locations[2].id} with mobility_type: 55. '55' is not a valid mobility_type") + expect(Rails.logger).to receive(:info).with("Cannot update location #{locations[2].id} with status as it it not a permitted field") + + expect(Rails.logger).to receive(:info).with("No changes to location #{locations[2].id}.") + + expect(Rails.logger).to receive(:info).with("Location with id Wrong_id is not in the original location csv") + expect(Rails.logger).to receive(:info).with("Location with id SWrong_id is not in the database") + + task.invoke(original_locations_csv_path, updated_locations_csv_path) + end + + it "raises an error when no paths are given" do + expect { task.invoke(nil) }.to raise_error(RuntimeError, "Usage: rake bulk_update:update_locations_from_csv['original_file_name','updated_file_name']") + end + + it "raises an error when no original path is given" do + expect { task.invoke(nil, updated_locations_csv_path) }.to raise_error(RuntimeError, "Usage: rake bulk_update:update_locations_from_csv['original_file_name','updated_file_name']") + end + + it "raises an error when no updated path is given" do + expect { task.invoke(original_locations_csv_path, nil) }.to raise_error(RuntimeError, "Usage: rake bulk_update:update_locations_from_csv['original_file_name','updated_file_name']") + end + + context "when updating LA" do + before do + LaRentRange.create!( + ranges_rent_id: "1", + la: "E09000033", + beds: 0, + lettype: 8, + soft_min: 12.41, + soft_max: 89.54, + hard_min: 9.87, + hard_max: 100.99, + start_year: 2023, + ) + end + + context "when new LA does not trigger hard rent ranges validations" do + let!(:lettings_log) do + FactoryBot.create(:lettings_log, + :completed, + :sh, + location: locations[0], + scheme:, + values_updated_at: nil, + owning_organisation: scheme.owning_organisation, + brent: 80, + scharge: 50, + pscharge: 50, + supcharg: 50, + beds: 4, + lettype: 1, + period: 1) + end + + before do + allow(storage_service).to receive(:get_file_io) + .with("updated_locations.csv") + .and_return(StringIO.new(replace_entity_ids_for_locations(locations[0], locations[1], locations[2], scheme, scheme, { id: "non existent scheme id" }, File.open("./spec/fixtures/files/updated_locations.csv").read))) + end + + it "does not clear the charges values" do + expect(lettings_log.status).to eq("completed") + task.invoke(original_locations_csv_path, updated_locations_csv_path) + lettings_log.reload + expect(lettings_log.status).to eq("completed") + expect(lettings_log.brent).to eq(80) + expect(lettings_log.scharge).to eq(50) + expect(lettings_log.pscharge).to eq(50) + expect(lettings_log.supcharg).to eq(50) + end + end + + context "when new LA triggers hard rent ranges validation" do + let!(:lettings_log) do + FactoryBot.create(:lettings_log, + :completed, + :sh, + location: locations[0], + scheme:, + values_updated_at: nil, + owning_organisation: scheme.owning_organisation, + brent: 200, + scharge: 50, + pscharge: 50, + supcharg: 50, + beds: 4, + lettype: 1, + period: 1) + end + + before do + allow(storage_service).to receive(:get_file_io) + .with("updated_locations.csv") + .and_return(StringIO.new(replace_entity_ids_for_locations(locations[0], locations[1], locations[2], scheme, scheme, { id: "non existent scheme id" }, File.open("./spec/fixtures/files/updated_locations.csv").read))) + end + + it "clears the charges values" do + expect(lettings_log.status).to eq("completed") + task.invoke(original_locations_csv_path, updated_locations_csv_path) + lettings_log.reload + expect(lettings_log.status).to eq("in_progress") + expect(lettings_log.brent).to be_nil + expect(lettings_log.scharge).to be_nil + expect(lettings_log.pscharge).to be_nil + expect(lettings_log.supcharg).to be_nil + end + end + + context "when new LA triggers soft rent ranges validations" do + let!(:lettings_log) do + FactoryBot.create(:lettings_log, + :completed, + :sh, + location: locations[0], + scheme:, + values_updated_at: nil, + owning_organisation: scheme.owning_organisation, + brent: 100, + scharge: 50, + pscharge: 50, + supcharg: 50, + beds: 4, + lettype: 1, + period: 1) + end + + before do + allow(storage_service).to receive(:get_file_io) + .with("updated_locations.csv") + .and_return(StringIO.new(replace_entity_ids_for_locations(locations[0], locations[1], locations[2], scheme, scheme, { id: "non existent scheme id" }, File.open("./spec/fixtures/files/updated_locations.csv").read))) + end + + it "does not clear the charges values and marks the log in progress" do + expect(lettings_log.status).to eq("completed") + task.invoke(original_locations_csv_path, updated_locations_csv_path) + lettings_log.reload + expect(lettings_log.status).to eq("in_progress") + expect(lettings_log.rent_value_check).to eq(nil) + expect(lettings_log.brent).to eq(100) + expect(lettings_log.scharge).to eq(50) + expect(lettings_log.pscharge).to eq(50) + expect(lettings_log.supcharg).to eq(50) + end + end + + context "when new LA triggers soft rent ranges validations for closed collection period" do + let!(:lettings_log) do + FactoryBot.create(:lettings_log, + :completed, + :sh, + location: locations[0], + scheme:, + values_updated_at: nil, + owning_organisation: scheme.owning_organisation, + brent: 100, + scharge: 50, + pscharge: 50, + supcharg: 50, + beds: 4, + lettype: 1, + voiddate: Time.zone.local(2020, 4, 1), + mrcdate: Time.zone.local(2020, 4, 1), + period: 1) + end + + before do + LaRentRange.create!( + ranges_rent_id: "1", + la: "E09000033", + beds: 0, + lettype: 8, + soft_min: 12.41, + soft_max: 89.54, + hard_min: 9.87, + hard_max: 100.99, + start_year: 2022, + ) + + allow(storage_service).to receive(:get_file_io) + .with("updated_locations.csv") + .and_return(StringIO.new(replace_entity_ids_for_locations(locations[0], locations[1], locations[2], scheme, scheme, { id: "non existent scheme id" }, File.open("./spec/fixtures/files/updated_locations.csv").read))) + + lettings_log.startdate = Time.zone.local(2022, 4, 1) + lettings_log.owning_organisation = scheme.owning_organisation + lettings_log.save!(validate: false) + end + + it "does not clear the charges values and confirms the rent value check" do + expect(lettings_log.status).to eq("completed") + task.invoke(original_locations_csv_path, updated_locations_csv_path) + lettings_log.reload + expect(lettings_log.status).to eq("completed") + expect(lettings_log.rent_value_check).to eq(0) + expect(lettings_log.brent).to eq(100) + expect(lettings_log.scharge).to eq(50) + expect(lettings_log.pscharge).to eq(50) + expect(lettings_log.supcharg).to eq(50) + end + end + end + end + end +end