diff --git a/.github/workflows/review_pipeline.yml b/.github/workflows/review_pipeline.yml index 440dddf5c..d5921e049 100644 --- a/.github/workflows/review_pipeline.yml +++ b/.github/workflows/review_pipeline.yml @@ -18,7 +18,7 @@ jobs: postgres: name: Provision postgres runs-on: ubuntu-latest - environment: staging + environment: review steps: - name: Install Cloud Foundry CLI @@ -44,7 +44,7 @@ jobs: redis: name: Provision redis runs-on: ubuntu-latest - environment: staging + environment: review steps: - name: Install Cloud Foundry CLI @@ -70,7 +70,7 @@ jobs: deploy: name: Deploy review app runs-on: ubuntu-latest - environment: staging + environment: review needs: [postgres, redis] permissions: issues: write @@ -125,7 +125,7 @@ jobs: cf set-env $APP_NAME IMPORT_PAAS_INSTANCE $IMPORT_PAAS_INSTANCE cf set-env $APP_NAME EXPORT_PAAS_INSTANCE "dluhc-core-review-export-bucket" cf set-env $APP_NAME S3_CONFIG $S3_CONFIG - cf set-env $APP_NAME CSV_DOWNLOAD_PAAS_INSTANCE "dluhc-core-staging-csv-bucket" + cf set-env $APP_NAME CSV_DOWNLOAD_PAAS_INSTANCE "dluhc-core-review-csv-bucket" cf set-env $APP_NAME SENTRY_DSN $SENTRY_DSN cf set-env $APP_NAME APP_HOST "https://dluhc-core-review-${{ github.event.pull_request.number }}.london.cloudapps.digital" diff --git a/.github/workflows/review_teardown_pipeline.yml b/.github/workflows/review_teardown_pipeline.yml index 08032596d..3962243c4 100644 --- a/.github/workflows/review_teardown_pipeline.yml +++ b/.github/workflows/review_teardown_pipeline.yml @@ -14,7 +14,7 @@ jobs: app: name: Teardown app runs-on: ubuntu-latest - environment: staging + environment: review steps: - name: Install Cloud Foundry CLI @@ -40,7 +40,7 @@ jobs: postgres: name: Teardown postgres runs-on: ubuntu-latest - environment: staging + environment: review needs: [app] steps: @@ -67,7 +67,7 @@ jobs: redis: name: Teardown redis runs-on: ubuntu-latest - environment: staging + environment: review needs: [app] steps: diff --git a/app/controllers/form_controller.rb b/app/controllers/form_controller.rb index e7bbf38ec..dbc5ba490 100644 --- a/app/controllers/form_controller.rb +++ b/app/controllers/form_controller.rb @@ -126,7 +126,7 @@ private next_page = form.get_page(next_page_id) previous_page = form.previous_page_id(@page, @log, current_user) - if next_page&.interruption_screen? || next_page_id == previous_page + if next_page&.interruption_screen? || next_page_id == previous_page || CONFIRMATION_PAGE_IDS.include?(next_page_id) return send("#{@log.class.name.underscore}_#{next_page_id}_path", @log, { referrer: "check_answers" }) else return send("#{@log.model_name.param_key}_#{form.subsection_for_page(@page).id}_check_answers_path", @log) @@ -180,4 +180,6 @@ private redirect_to lettings_log_path(@log) unless @log.collection_period_open? end + + CONFIRMATION_PAGE_IDS = %w[uprn_confirmation].freeze end diff --git a/app/controllers/merge_requests_controller.rb b/app/controllers/merge_requests_controller.rb new file mode 100644 index 000000000..3d1aa3f5f --- /dev/null +++ b/app/controllers/merge_requests_controller.rb @@ -0,0 +1,86 @@ +class MergeRequestsController < ApplicationController + before_action :find_resource, only: %i[update organisations update_organisations remove_merging_organisation] + before_action :authenticate_user! + before_action :authenticate_scope!, except: [:create] + + def create + ActiveRecord::Base.transaction do + @merge_request = MergeRequest.create!(merge_request_params.merge(status: :unsubmitted)) + MergeRequestOrganisation.create!({ merge_request: @merge_request, merging_organisation: @merge_request.requesting_organisation }) + end + redirect_to organisations_merge_request_path(@merge_request) + rescue ActiveRecord::RecordInvalid + render_not_found + end + + def organisations + @answer_options = organisations_answer_options + end + + def update + if @merge_request.update(merge_request_params) + redirect_to next_page_path + else + render previous_template, status: :unprocessable_entity + end + end + + def update_organisations + merge_request_organisation = MergeRequestOrganisation.new(merge_request_organisation_params) + @answer_options = organisations_answer_options + if merge_request_organisation.save + render :organisations + else + render :organisations, status: :unprocessable_entity + end + end + + def remove_merging_organisation + MergeRequestOrganisation.find_by(merge_request_organisation_params)&.destroy! + @answer_options = organisations_answer_options + render :organisations + end + +private + + def organisations_answer_options + answer_options = { "" => "Select an option" } + + Organisation.all.pluck(:id, :name).each do |organisation| + answer_options[organisation[0]] = organisation[1] + end + answer_options + end + + def merge_request_params + merge_params = params.fetch(:merge_request, {}).permit(:requesting_organisation_id, :other_merging_organisations, :status) + + if merge_params[:requesting_organisation_id].present? && (current_user.data_coordinator? || current_user.data_provider?) + merge_params[:requesting_organisation_id] = current_user.organisation.id + end + + merge_params + end + + def merge_request_organisation_params + { merge_request: @merge_request, merging_organisation_id: params[:merge_request][:merging_organisation] } + end + + def find_resource + @merge_request = MergeRequest.find(params[:id]) + end + + def next_page_path + absorbing_organisation_merge_request_path(@merge_request) + end + + def previous_template + :organisations + end + + def authenticate_scope! + if current_user.organisation != @merge_request.requesting_organisation && !current_user.support? + render_not_found + end + end +end diff --git a/app/controllers/organisations_controller.rb b/app/controllers/organisations_controller.rb index 1bd4694ec..70943063f 100644 --- a/app/controllers/organisations_controller.rb +++ b/app/controllers/organisations_controller.rb @@ -137,6 +137,10 @@ class OrganisationsController < ApplicationController end end + def merge_request + @merge_request = MergeRequest.new + end + private def org_params diff --git a/app/controllers/start_controller.rb b/app/controllers/start_controller.rb index d707d3e91..671b63805 100644 --- a/app/controllers/start_controller.rb +++ b/app/controllers/start_controller.rb @@ -28,4 +28,44 @@ class StartController < ApplicationController type: "application/pdf", ) end + + def download_23_24_lettings_bulk_upload_template + send_file( + Rails.root.join("public/files/bulk-upload-lettings-template-2023-24.xlsx"), + filename: "2023-24-lettings-bulk-upload-template.xlsx", + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + ) + end + + def download_23_24_lettings_bulk_upload_legacy_template + send_file( + Rails.root.join("public/files/bulk-upload-lettings-legacy-template-2023-24.xlsx"), + filename: "2023-24-lettings-bulk-upload-legacy-template.xlsx", + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + ) + end + + def download_23_24_lettings_bulk_upload_specification + send_file( + Rails.root.join("public/files/bulk-upload-lettings-specification-2023-24.xlsx"), + filename: "2023-24-lettings-bulk-upload-specification.xlsx", + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + ) + end + + def download_22_23_lettings_bulk_upload_template + send_file( + Rails.root.join("public/files/bulk-upload-lettings-template-2022-23.xlsx"), + filename: "2022-23-lettings-bulk-upload-template.xlsx", + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + ) + end + + def download_22_23_lettings_bulk_upload_specification + send_file( + Rails.root.join("public/files/bulk-upload-lettings-specification-2022-23.xlsx"), + filename: "2022-23-lettings-bulk-upload-specification.xlsx", + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + ) + end end diff --git a/app/helpers/tasklist_helper.rb b/app/helpers/tasklist_helper.rb index 6a12cff29..678ef044f 100644 --- a/app/helpers/tasklist_helper.rb +++ b/app/helpers/tasklist_helper.rb @@ -1,5 +1,6 @@ module TasklistHelper include GovukLinkHelper + include CollectionTimeHelper def get_next_incomplete_section(log) log.form.subsections.find { |subsection| subsection.is_incomplete?(log) } @@ -34,7 +35,9 @@ module TasklistHelper "You can #{govuk_link_to 'review and make changes to this log', link} until #{log.form.end_date.to_formatted_s(:govuk_date)}.".html_safe else - "This log is from the #{log.form.start_date.year}/#{log.form.start_date.year + 1} collection window, which is now closed." + start_year = log.startdate ? collection_start_year_for_date(log.startdate) : log.form.start_date.year + + "This log is from the #{start_year}/#{start_year + 1} collection window, which is now closed." end end diff --git a/app/models/form.rb b/app/models/form.rb index 80fed4444..cc73d2ca6 100644 --- a/app/models/form.rb +++ b/app/models/form.rb @@ -178,48 +178,78 @@ class Form pages.reject { |p| p.routed_to?(log, current_user) } end - def invalidated_questions(log) - invalidated_page_questions(log) + invalidated_conditional_questions(log) - end - - def invalidated_page_questions(log, current_user = nil) - # we're already treating these fields as a special case and reset their values upon saving a log - callback_questions = %w[postcode_known la ppcodenk previous_la_known prevloc postcode_full ppostcode_full location_id address_line1 address_line2 town_or_city county] - questions.reject { |q| q.page.routed_to?(log, current_user) || q.derived? || callback_questions.include?(q.id) } || [] - end + def reset_not_routed_questions_and_invalid_answers(log) + reset_checkbox_questions_if_not_routed(log) - def reset_not_routed_questions(log) - enabled_questions = enabled_page_questions(log) - enabled_question_ids = enabled_questions.map(&:id) + reset_radio_questions_if_not_routed_or_invalid_answers(log) - invalidated_page_questions(log).each do |question| - if %w[radio checkbox].include?(question.type) - enabled_answer_options = enabled_question_ids.include?(question.id) ? enabled_questions.find { |q| q.id == question.id }.answer_options : {} - current_answer_option_valid = enabled_answer_options.present? ? enabled_answer_options.key?(log.public_send(question.id).to_s) : false + reset_free_user_input_questions_if_not_routed(log) + end - if !current_answer_option_valid && log.respond_to?(question.id.to_s) - Rails.logger.debug("Cleared #{question.id} value") - log.public_send("#{question.id}=", nil) + def reset_checkbox_questions_if_not_routed(log) + checkbox_questions = routed_and_not_routed_questions_by_type(log, type: "checkbox") + checkbox_questions[:not_routed].each do |not_routed_question| + valid_options = checkbox_questions[:routed] + .select { |q| q.id == not_routed_question.id } + .flat_map { |q| q.answer_options.keys } + not_routed_question.answer_options.each_key do |invalid_option| + if !log.respond_to?(invalid_option) || valid_options.include?(invalid_option) || log.public_send(invalid_option).nil? + next else - - (question.answer_options.keys - enabled_answer_options.keys).map do |invalid_answer_option| - Rails.logger.debug("Cleared #{invalid_answer_option} value") - log.public_send("#{invalid_answer_option}=", nil) if log.respond_to?(invalid_answer_option) - end + clear_attribute(log, invalid_option) end + end + end + end + + def reset_radio_questions_if_not_routed_or_invalid_answers(log) + radio_questions = routed_and_not_routed_questions_by_type(log, type: "radio") + valid_radio_options = radio_questions[:routed] + .group_by(&:id) + .transform_values! { |q_array| q_array.flat_map { |q| q.answer_options.keys } } + radio_questions[:not_routed].each do |not_routed_question| + question_id = not_routed_question.id + if !log.respond_to?(question_id) || log.public_send(question_id).nil? || valid_radio_options.key?(question_id) + next + else + clear_attribute(log, question_id) + end + end + valid_radio_options.each do |question_id, valid_options| + if !log.respond_to?(question_id) || valid_options.include?(log.public_send(question_id).to_s) + next + else + clear_attribute(log, question_id) + end + end + end + + def reset_free_user_input_questions_if_not_routed(log) + non_radio_checkbox_questions = routed_and_not_routed_questions_by_type(log) + enabled_question_ids = non_radio_checkbox_questions[:routed].map(&:id) + non_radio_checkbox_questions[:not_routed].each do |not_routed_question| + question_id = not_routed_question.id + if log.public_send(question_id).nil? || enabled_question_ids.include?(question_id) + next else - Rails.logger.debug("Cleared #{question.id} value") - log.public_send("#{question.id}=", nil) unless enabled_question_ids.include?(question.id) + clear_attribute(log, question_id) end end end - def enabled_page_questions(log) - questions - invalidated_page_questions(log) + def routed_and_not_routed_questions_by_type(log, type: nil, current_user: nil) + questions_by_type = if type + questions.reject { |q| q.type != type || q.disable_clearing_if_not_routed_or_dynamic_answer_options } + else + questions.reject { |q| %w[radio checkbox].include?(q.type) || q.disable_clearing_if_not_routed_or_dynamic_answer_options } + end + routed, not_routed = questions_by_type.partition { |q| q.page.routed_to?(log, current_user) || q.derived? } + { routed:, not_routed: } end - def invalidated_conditional_questions(log) - questions.reject { |q| q.enabled?(log) } || [] + def clear_attribute(log, attribute) + Rails.logger.debug("Cleared #{attribute} value") + log.public_send("#{attribute}=", nil) end def readonly_questions @@ -230,6 +260,13 @@ class Form questions.select { |q| q.type == "numeric" } end + def previous_page(page_ids, page_index, log, current_user) + prev_page = get_page(page_ids[page_index - 1]) + return prev_page.id if prev_page.routed_to?(log, current_user) + + previous_page(page_ids, page_index - 1, log, current_user) + end + def send_chain(arr, log) Array(arr).inject(log) { |o, a| o.public_send(*a) } end diff --git a/app/models/form/lettings/pages/max_rent_value_check.rb b/app/models/form/lettings/pages/max_rent_value_check.rb index 8ae9b7d9c..956e07f44 100644 --- a/app/models/form/lettings/pages/max_rent_value_check.rb +++ b/app/models/form/lettings/pages/max_rent_value_check.rb @@ -1,7 +1,6 @@ class Form::Lettings::Pages::MaxRentValueCheck < ::Form::Page - def initialize(id, hsh, subsection) - super - @id = "max_rent_value_check" + def initialize(id, hsh, subsection, check_answers_card_number: nil) + super(id, hsh, subsection) @depends_on = [{ "rent_in_soft_max_range?" => true }] @title_text = { "translation" => "soft_validations.rent.outside_range_title", @@ -23,9 +22,10 @@ class Form::Lettings::Pages::MaxRentValueCheck < ::Form::Page }, ], } + @check_answers_card_number = check_answers_card_number end def questions - @questions ||= [Form::Lettings::Questions::RentValueCheck.new(nil, nil, self)] + @questions ||= [Form::Lettings::Questions::RentValueCheck.new(nil, nil, self, check_answers_card_number: @check_answers_card_number)] end end diff --git a/app/models/form/lettings/pages/min_rent_value_check.rb b/app/models/form/lettings/pages/min_rent_value_check.rb index eda8819ee..86b53951c 100644 --- a/app/models/form/lettings/pages/min_rent_value_check.rb +++ b/app/models/form/lettings/pages/min_rent_value_check.rb @@ -1,7 +1,6 @@ class Form::Lettings::Pages::MinRentValueCheck < ::Form::Page - def initialize(id, hsh, subsection) - super - @id = "min_rent_value_check" + def initialize(id, hsh, subsection, check_answers_card_number: nil) + super(id, hsh, subsection) @depends_on = [{ "rent_in_soft_min_range?" => true }] @title_text = { "translation" => "soft_validations.rent.outside_range_title", @@ -13,17 +12,16 @@ class Form::Lettings::Pages::MinRentValueCheck < ::Form::Page } @informative_text = { "translation" => "soft_validations.rent.min_hint_text", - "arguments" => [ - { - "key" => "field_formatted_as_currency", - "arguments_for_key" => "soft_min_for_period", - "i18n_template" => "soft_min_for_period", - }, - ], + "arguments" => [{ + "key" => "field_formatted_as_currency", + "arguments_for_key" => "soft_min_for_period", + "i18n_template" => "soft_min_for_period", + }], } + @check_answers_card_number = check_answers_card_number end def questions - @questions ||= [Form::Lettings::Questions::RentValueCheck.new(nil, nil, self)] + @questions ||= [Form::Lettings::Questions::RentValueCheck.new(nil, nil, self, check_answers_card_number: @check_answers_card_number)] end end diff --git a/app/models/form/lettings/questions/address_line1.rb b/app/models/form/lettings/questions/address_line1.rb index 4c8b4151b..ef197e4fd 100644 --- a/app/models/form/lettings/questions/address_line1.rb +++ b/app/models/form/lettings/questions/address_line1.rb @@ -7,6 +7,7 @@ class Form::Lettings::Questions::AddressLine1 < ::Form::Question @type = "text" @plain_label = true @check_answer_label = "Q12 - Address" + @disable_clearing_if_not_routed_or_dynamic_answer_options = true end def answer_label(log, _current_user = nil) diff --git a/app/models/form/lettings/questions/address_line2.rb b/app/models/form/lettings/questions/address_line2.rb index 16f7c8336..3b2c36dbc 100644 --- a/app/models/form/lettings/questions/address_line2.rb +++ b/app/models/form/lettings/questions/address_line2.rb @@ -5,6 +5,7 @@ class Form::Lettings::Questions::AddressLine2 < ::Form::Question @header = "Address line 2 (optional)" @type = "text" @plain_label = true + @disable_clearing_if_not_routed_or_dynamic_answer_options = true end def hidden_in_check_answers?(_log = nil, _current_user = nil) diff --git a/app/models/form/lettings/questions/county.rb b/app/models/form/lettings/questions/county.rb index 360c0966c..9f0dc7138 100644 --- a/app/models/form/lettings/questions/county.rb +++ b/app/models/form/lettings/questions/county.rb @@ -5,6 +5,7 @@ class Form::Lettings::Questions::County < ::Form::Question @header = "County (optional)" @type = "text" @plain_label = true + @disable_clearing_if_not_routed_or_dynamic_answer_options = true end def hidden_in_check_answers?(_log = nil, _current_user = nil) diff --git a/app/models/form/lettings/questions/earnings.rb b/app/models/form/lettings/questions/earnings.rb index 445710f09..fdf702740 100644 --- a/app/models/form/lettings/questions/earnings.rb +++ b/app/models/form/lettings/questions/earnings.rb @@ -10,7 +10,7 @@ class Form::Lettings::Questions::Earnings < ::Form::Question @min = 0 @guidance_partial = "what_counts_as_income" @hint_text = "" - @step = 1 + @step = 0.01 @prefix = "£" @suffix = [ { "label" => " every week", "depends_on" => { "incfreq" => 1 } }, diff --git a/app/models/form/lettings/questions/la.rb b/app/models/form/lettings/questions/la.rb index 3cafda054..f9d650918 100644 --- a/app/models/form/lettings/questions/la.rb +++ b/app/models/form/lettings/questions/la.rb @@ -5,9 +5,10 @@ class Form::Lettings::Questions::La < ::Form::Question @check_answer_label = "Local Authority" @header = "What is the property’s local authority?" @type = "select" - @check_answers_card_number = 0 + @check_answers_card_number = nil @hint_text = "" @question_number = 13 + @disable_clearing_if_not_routed_or_dynamic_answer_options = true end def answer_options diff --git a/app/models/form/lettings/questions/location_id.rb b/app/models/form/lettings/questions/location_id.rb index 94196534f..80c2516e8 100644 --- a/app/models/form/lettings/questions/location_id.rb +++ b/app/models/form/lettings/questions/location_id.rb @@ -1,6 +1,7 @@ class Form::Lettings::Questions::LocationId < ::Form::Question - def initialize(_id, hsh, page) - super("location_id", hsh, page) + def initialize(id, hsh, page) + super + @id = "location_id" @check_answer_label = "Location" @header = header_text @type = "radio" @@ -11,6 +12,7 @@ class Form::Lettings::Questions::LocationId < ::Form::Question }, } @question_number = 10 + @disable_clearing_if_not_routed_or_dynamic_answer_options = true end def answer_options diff --git a/app/models/form/lettings/questions/postcode_for_full_address.rb b/app/models/form/lettings/questions/postcode_for_full_address.rb index 015abc2e8..4f41867d7 100644 --- a/app/models/form/lettings/questions/postcode_for_full_address.rb +++ b/app/models/form/lettings/questions/postcode_for_full_address.rb @@ -17,6 +17,7 @@ class Form::Lettings::Questions::PostcodeForFullAddress < ::Form::Question }, } @plain_label = true + @disable_clearing_if_not_routed_or_dynamic_answer_options = true end def hidden_in_check_answers?(_log = nil, _current_user = nil) diff --git a/app/models/form/lettings/questions/postcode_full.rb b/app/models/form/lettings/questions/postcode_full.rb index 1f0a25c49..fc7d7691b 100644 --- a/app/models/form/lettings/questions/postcode_full.rb +++ b/app/models/form/lettings/questions/postcode_full.rb @@ -10,5 +10,6 @@ class Form::Lettings::Questions::PostcodeFull < ::Form::Question @check_answers_card_number = 0 @hint_text = "" @inferred_answers = { "la" => { "is_la_inferred" => true } } + @disable_clearing_if_not_routed_or_dynamic_answer_options = true end end diff --git a/app/models/form/lettings/questions/postcode_known.rb b/app/models/form/lettings/questions/postcode_known.rb index 6af30bb19..ea9adb06b 100644 --- a/app/models/form/lettings/questions/postcode_known.rb +++ b/app/models/form/lettings/questions/postcode_known.rb @@ -8,6 +8,7 @@ class Form::Lettings::Questions::PostcodeKnown < ::Form::Question @check_answers_card_number = 0 @hint_text = "" @answer_options = ANSWER_OPTIONS + @disable_clearing_if_not_routed_or_dynamic_answer_options = true @conditional_for = { "postcode_full" => [1] } @hidden_in_check_answers = { "depends_on" => [{ "postcode_known" => 0 }, { "postcode_known" => 1 }] } end diff --git a/app/models/form/lettings/questions/ppcodenk.rb b/app/models/form/lettings/questions/ppcodenk.rb index 33b03d959..25557a166 100644 --- a/app/models/form/lettings/questions/ppcodenk.rb +++ b/app/models/form/lettings/questions/ppcodenk.rb @@ -11,6 +11,7 @@ class Form::Lettings::Questions::Ppcodenk < ::Form::Question @conditional_for = { "ppostcode_full" => [1] } @hidden_in_check_answers = { "depends_on" => [{ "ppcodenk" => 0 }, { "ppcodenk" => 1 }] } @question_number = 80 + @disable_clearing_if_not_routed_or_dynamic_answer_options = true end ANSWER_OPTIONS = { "1" => { "value" => "Yes" }, "0" => { "value" => "No" } }.freeze diff --git a/app/models/form/lettings/questions/ppostcode_full.rb b/app/models/form/lettings/questions/ppostcode_full.rb index 0432d1b5b..86b4c7b31 100644 --- a/app/models/form/lettings/questions/ppostcode_full.rb +++ b/app/models/form/lettings/questions/ppostcode_full.rb @@ -11,5 +11,6 @@ class Form::Lettings::Questions::PpostcodeFull < ::Form::Question @hint_text = "" @inferred_answers = { "prevloc" => { "is_previous_la_inferred" => true } } @question_number = 80 + @disable_clearing_if_not_routed_or_dynamic_answer_options = true end end diff --git a/app/models/form/lettings/questions/previous_la_known.rb b/app/models/form/lettings/questions/previous_la_known.rb index a9ff11e55..eb80eda4a 100644 --- a/app/models/form/lettings/questions/previous_la_known.rb +++ b/app/models/form/lettings/questions/previous_la_known.rb @@ -11,6 +11,7 @@ class Form::Lettings::Questions::PreviousLaKnown < ::Form::Question @conditional_for = { "prevloc" => [1] } @hidden_in_check_answers = { "depends_on" => [{ "previous_la_known" => 0 }, { "previous_la_known" => 1 }] } @question_number = 81 + @disable_clearing_if_not_routed_or_dynamic_answer_options = true end ANSWER_OPTIONS = { "1" => { "value" => "Yes" }, "0" => { "value" => "No" } }.freeze diff --git a/app/models/form/lettings/questions/prevloc.rb b/app/models/form/lettings/questions/prevloc.rb index 229af2c18..e0082602e 100644 --- a/app/models/form/lettings/questions/prevloc.rb +++ b/app/models/form/lettings/questions/prevloc.rb @@ -9,6 +9,7 @@ class Form::Lettings::Questions::Prevloc < ::Form::Question @check_answers_card_number = 0 @hint_text = "Select ‘Northern Ireland’, ‘Scotland’, ‘Wales’ or ‘Outside the UK’ if the household’s last settled home was outside England." @question_number = 81 + @disable_clearing_if_not_routed_or_dynamic_answer_options = true end def answer_options diff --git a/app/models/form/lettings/questions/rent_value_check.rb b/app/models/form/lettings/questions/rent_value_check.rb index eac22908a..6bca43e30 100644 --- a/app/models/form/lettings/questions/rent_value_check.rb +++ b/app/models/form/lettings/questions/rent_value_check.rb @@ -1,11 +1,11 @@ class Form::Lettings::Questions::RentValueCheck < ::Form::Question - def initialize(id, hsh, page) - super + def initialize(id, hsh, page, check_answers_card_number:) + super(id, hsh, page) @id = "rent_value_check" @check_answer_label = "Total rent confirmation" @header = "Are you sure this is correct?" @type = "interruption_screen" - @check_answers_card_number = 0 + @check_answers_card_number = check_answers_card_number @answer_options = ANSWER_OPTIONS @hidden_in_check_answers = { "depends_on" => [{ "rent_value_check" => 0 }, { "rent_value_check" => 1 }] } end diff --git a/app/models/form/lettings/questions/town_or_city.rb b/app/models/form/lettings/questions/town_or_city.rb index f1eac8dff..501e9bec4 100644 --- a/app/models/form/lettings/questions/town_or_city.rb +++ b/app/models/form/lettings/questions/town_or_city.rb @@ -5,6 +5,7 @@ class Form::Lettings::Questions::TownOrCity < ::Form::Question @header = "Town or city" @type = "text" @plain_label = true + @disable_clearing_if_not_routed_or_dynamic_answer_options = true end def hidden_in_check_answers?(_log = nil, _current_user = nil) diff --git a/app/models/form/lettings/questions/uprn_known.rb b/app/models/form/lettings/questions/uprn_known.rb index 816a31ffb..95c53ec6a 100644 --- a/app/models/form/lettings/questions/uprn_known.rb +++ b/app/models/form/lettings/questions/uprn_known.rb @@ -9,7 +9,18 @@ class Form::Lettings::Questions::UprnKnown < ::Form::Question @hint_text = "The Unique Property Reference Number (UPRN) is a unique number system created by Ordnance Survey and used by housing providers and sectors UK-wide. For example 10010457355.

You can continue without the UPRN, but it means we will need you to enter the address of the property." @conditional_for = { "uprn" => [1] } - @hidden_in_check_answers = true + @inferred_check_answers_value = [ + { + "condition" => { "uprn_known" => 0 }, + "value" => "Not known", + }, + ] + @hidden_in_check_answers = { + "depends_on" => [ + { "uprn_known" => 0 }, + { "uprn_known" => 1 }, + ], + } end ANSWER_OPTIONS = { diff --git a/app/models/form/lettings/subsections/income_and_benefits.rb b/app/models/form/lettings/subsections/income_and_benefits.rb index 30ba3a7e0..25f5d0ce4 100644 --- a/app/models/form/lettings/subsections/income_and_benefits.rb +++ b/app/models/form/lettings/subsections/income_and_benefits.rb @@ -24,8 +24,8 @@ class Form::Lettings::Subsections::IncomeAndBenefits < ::Form::Subsection Form::Lettings::Pages::RentBiWeekly.new(nil, nil, self), Form::Lettings::Pages::Rent4Weekly.new(nil, nil, self), Form::Lettings::Pages::RentMonthly.new(nil, nil, self), - Form::Lettings::Pages::MinRentValueCheck.new(nil, nil, self), - Form::Lettings::Pages::MaxRentValueCheck.new(nil, nil, self), + Form::Lettings::Pages::MinRentValueCheck.new("brent_min_rent_value_check", nil, self, check_answers_card_number: 0), + Form::Lettings::Pages::MaxRentValueCheck.new("brent_max_rent_value_check", nil, self, check_answers_card_number: 0), Form::Lettings::Pages::Outstanding.new(nil, nil, self), Form::Lettings::Pages::OutstandingAmount.new(nil, nil, self), ].compact diff --git a/app/models/form/lettings/subsections/property_information.rb b/app/models/form/lettings/subsections/property_information.rb index 1290f7cf2..0f921d4a5 100644 --- a/app/models/form/lettings/subsections/property_information.rb +++ b/app/models/form/lettings/subsections/property_information.rb @@ -10,6 +10,8 @@ class Form::Lettings::Subsections::PropertyInformation < ::Form::Subsection @pages ||= [ uprn_questions, Form::Lettings::Pages::PropertyLocalAuthority.new(nil, nil, self), + Form::Lettings::Pages::MinRentValueCheck.new("local_authority_min_rent_value_check", nil, self, check_answers_card_number: nil), + Form::Lettings::Pages::MaxRentValueCheck.new("local_authority_max_rent_value_check", nil, self, check_answers_card_number: nil), Form::Lettings::Pages::FirstTimePropertyLetAsSocialHousing.new(nil, nil, self), Form::Lettings::Pages::PropertyLetType.new(nil, nil, self), Form::Lettings::Pages::PropertyVacancyReasonNotFirstLet.new(nil, nil, self), @@ -20,6 +22,8 @@ class Form::Lettings::Subsections::PropertyInformation < ::Form::Subsection Form::Lettings::Pages::PropertyBuildingType.new(nil, nil, self), Form::Lettings::Pages::PropertyWheelchairAccessible.new(nil, nil, self), Form::Lettings::Pages::PropertyNumberOfBedrooms.new(nil, nil, self), + Form::Lettings::Pages::MinRentValueCheck.new("beds_min_rent_value_check", nil, self, check_answers_card_number: 0), + Form::Lettings::Pages::MaxRentValueCheck.new("beds_max_rent_value_check", nil, self, check_answers_card_number: 0), Form::Lettings::Pages::VoidDate.new(nil, nil, self), Form::Lettings::Pages::VoidDateValueCheck.new(nil, nil, self), Form::Lettings::Pages::PropertyMajorRepairs.new(nil, nil, self), diff --git a/app/models/form/lettings/subsections/setup.rb b/app/models/form/lettings/subsections/setup.rb index e2dbb7f8b..312ed5255 100644 --- a/app/models/form/lettings/subsections/setup.rb +++ b/app/models/form/lettings/subsections/setup.rb @@ -10,14 +10,22 @@ class Form::Lettings::Subsections::Setup < ::Form::Subsection @pages ||= [ organisation_page, stock_owner_page, + Form::Lettings::Pages::MinRentValueCheck.new("stock_owner_min_rent_value_check", nil, self), + Form::Lettings::Pages::MaxRentValueCheck.new("stock_owner_max_rent_value_check", nil, self), managing_organisation_page, created_by_page, Form::Lettings::Pages::NeedsType.new(nil, nil, self), Form::Lettings::Pages::Scheme.new(nil, nil, self), Form::Lettings::Pages::Location.new(nil, nil, self), + Form::Lettings::Pages::MinRentValueCheck.new("needs_type_min_rent_value_check", nil, self), + Form::Lettings::Pages::MaxRentValueCheck.new("needs_type_max_rent_value_check", nil, self), Form::Lettings::Pages::Renewal.new(nil, nil, self), Form::Lettings::Pages::TenancyStartDate.new(nil, nil, self), + Form::Lettings::Pages::MinRentValueCheck.new("start_date_min_rent_value_check", nil, self), + Form::Lettings::Pages::MaxRentValueCheck.new("start_date_max_rent_value_check", nil, self), Form::Lettings::Pages::RentType.new(nil, nil, self), + Form::Lettings::Pages::MinRentValueCheck.new("rent_type_min_rent_value_check", nil, self), + Form::Lettings::Pages::MaxRentValueCheck.new("rent_type_max_rent_value_check", nil, self), Form::Lettings::Pages::TenantCode.new(nil, nil, self), Form::Lettings::Pages::PropertyReference.new(nil, nil, self), ].compact diff --git a/app/models/form/question.rb b/app/models/form/question.rb index 8e8c26739..d4b7c9d76 100644 --- a/app/models/form/question.rb +++ b/app/models/form/question.rb @@ -1,5 +1,5 @@ class Form::Question - attr_accessor :id, :header, :hint_text, :description, :questions, + attr_accessor :id, :header, :hint_text, :description, :questions, :disable_clearing_if_not_routed_or_dynamic_answer_options, :type, :min, :max, :step, :width, :fields_to_add, :result_field, :conditional_for, :readonly, :answer_options, :page, :check_answer_label, :inferred_answers, :hidden_in_check_answers, :inferred_check_answers_value, @@ -42,6 +42,7 @@ class Form::Question @unresolved_hint_text = hsh["unresolved_hint_text"] @question_number = hsh["question_number"] @plain_label = hsh["plain_label"] + @disable_clearing_if_not_routed_or_dynamic_answer_options = hsh["disable_clearing_if_not_routed_or_dynamic_answer_options"] end end diff --git a/app/models/form/sales/pages/extra_borrowing_value_check.rb b/app/models/form/sales/pages/extra_borrowing_value_check.rb index 18f975b8d..5fff74db2 100644 --- a/app/models/form/sales/pages/extra_borrowing_value_check.rb +++ b/app/models/form/sales/pages/extra_borrowing_value_check.rb @@ -9,8 +9,7 @@ class Form::Sales::Pages::ExtraBorrowingValueCheck < Form::Page @title_text = { "translation" => "soft_validations.extra_borrowing.title", } - @informative_text = { - } + @informative_text = {} end def questions diff --git a/app/models/form/sales/pages/uprn.rb b/app/models/form/sales/pages/uprn.rb index ad247207f..c4ae5b357 100644 --- a/app/models/form/sales/pages/uprn.rb +++ b/app/models/form/sales/pages/uprn.rb @@ -11,10 +11,6 @@ class Form::Sales::Pages::Uprn < ::Form::Page ] end - def routed_to?(log, _current_user = nil) - log.uprn_known == 1 - end - def skip_text "Enter address instead" end diff --git a/app/models/form/sales/questions/address_line1.rb b/app/models/form/sales/questions/address_line1.rb index a95051116..edee2e7ee 100644 --- a/app/models/form/sales/questions/address_line1.rb +++ b/app/models/form/sales/questions/address_line1.rb @@ -7,6 +7,7 @@ class Form::Sales::Questions::AddressLine1 < ::Form::Question @type = "text" @plain_label = true @check_answer_label = "Q15 - Address" + @disable_clearing_if_not_routed_or_dynamic_answer_options = true end def answer_label(log, _current_user = nil) diff --git a/app/models/form/sales/questions/address_line2.rb b/app/models/form/sales/questions/address_line2.rb index 0b4ff661c..94396a2af 100644 --- a/app/models/form/sales/questions/address_line2.rb +++ b/app/models/form/sales/questions/address_line2.rb @@ -5,6 +5,7 @@ class Form::Sales::Questions::AddressLine2 < ::Form::Question @header = "Address line 2 (optional)" @type = "text" @plain_label = true + @disable_clearing_if_not_routed_or_dynamic_answer_options = true end def hidden_in_check_answers?(_log = nil, _current_user = nil) diff --git a/app/models/form/sales/questions/county.rb b/app/models/form/sales/questions/county.rb index 080ac809f..6586cb9e6 100644 --- a/app/models/form/sales/questions/county.rb +++ b/app/models/form/sales/questions/county.rb @@ -5,6 +5,7 @@ class Form::Sales::Questions::County < ::Form::Question @header = "County (optional)" @type = "text" @plain_label = true + @disable_clearing_if_not_routed_or_dynamic_answer_options = true end def hidden_in_check_answers?(_log = nil, _current_user = nil) diff --git a/app/models/form/sales/questions/postcode.rb b/app/models/form/sales/questions/postcode.rb index 0f72a9585..55e33199a 100644 --- a/app/models/form/sales/questions/postcode.rb +++ b/app/models/form/sales/questions/postcode.rb @@ -17,5 +17,6 @@ class Form::Sales::Questions::Postcode < ::Form::Question "is_la_inferred" => true, }, } + @disable_clearing_if_not_routed_or_dynamic_answer_options = true end end diff --git a/app/models/form/sales/questions/postcode_for_full_address.rb b/app/models/form/sales/questions/postcode_for_full_address.rb index a1e6f8633..5d3b9f122 100644 --- a/app/models/form/sales/questions/postcode_for_full_address.rb +++ b/app/models/form/sales/questions/postcode_for_full_address.rb @@ -17,6 +17,7 @@ class Form::Sales::Questions::PostcodeForFullAddress < ::Form::Question }, } @plain_label = true + @disable_clearing_if_not_routed_or_dynamic_answer_options = true end def hidden_in_check_answers?(_log = nil, _current_user = nil) diff --git a/app/models/form/sales/questions/previous_la_known.rb b/app/models/form/sales/questions/previous_la_known.rb index 1a9a646f3..1a4a2f438 100644 --- a/app/models/form/sales/questions/previous_la_known.rb +++ b/app/models/form/sales/questions/previous_la_known.rb @@ -21,6 +21,7 @@ class Form::Sales::Questions::PreviousLaKnown < ::Form::Question "prevloc" => [1], } @question_number = 58 + @disable_clearing_if_not_routed_or_dynamic_answer_options = true end ANSWER_OPTIONS = { diff --git a/app/models/form/sales/questions/previous_postcode.rb b/app/models/form/sales/questions/previous_postcode.rb index 568ba66ea..5eb8e6d54 100644 --- a/app/models/form/sales/questions/previous_postcode.rb +++ b/app/models/form/sales/questions/previous_postcode.rb @@ -18,5 +18,6 @@ class Form::Sales::Questions::PreviousPostcode < ::Form::Question }, } @question_number = 57 + @disable_clearing_if_not_routed_or_dynamic_answer_options = true end end diff --git a/app/models/form/sales/questions/previous_postcode_known.rb b/app/models/form/sales/questions/previous_postcode_known.rb index e83fda8d3..f53f52bf0 100644 --- a/app/models/form/sales/questions/previous_postcode_known.rb +++ b/app/models/form/sales/questions/previous_postcode_known.rb @@ -21,6 +21,7 @@ class Form::Sales::Questions::PreviousPostcodeKnown < ::Form::Question ], } @question_number = 57 + @disable_clearing_if_not_routed_or_dynamic_answer_options = true end ANSWER_OPTIONS = { diff --git a/app/models/form/sales/questions/prevloc.rb b/app/models/form/sales/questions/prevloc.rb index 75bb50e81..71606aa34 100644 --- a/app/models/form/sales/questions/prevloc.rb +++ b/app/models/form/sales/questions/prevloc.rb @@ -12,6 +12,7 @@ class Form::Sales::Questions::Prevloc < ::Form::Question "value" => "Not known", }] @question_number = 58 + @disable_clearing_if_not_routed_or_dynamic_answer_options = true end def answer_options diff --git a/app/models/form/sales/questions/property_local_authority.rb b/app/models/form/sales/questions/property_local_authority.rb index 04dbed347..df3fe5e88 100644 --- a/app/models/form/sales/questions/property_local_authority.rb +++ b/app/models/form/sales/questions/property_local_authority.rb @@ -6,6 +6,7 @@ class Form::Sales::Questions::PropertyLocalAuthority < ::Form::Question @header = "What is the property’s local authority?" @type = "select" @question_number = 16 + @disable_clearing_if_not_routed_or_dynamic_answer_options = true end def answer_options diff --git a/app/models/form/sales/questions/purchase_price.rb b/app/models/form/sales/questions/purchase_price.rb index f88938777..62e42f27b 100644 --- a/app/models/form/sales/questions/purchase_price.rb +++ b/app/models/form/sales/questions/purchase_price.rb @@ -9,17 +9,23 @@ class Form::Sales::Questions::PurchasePrice < ::Form::Question @step = 0.01 @width = 5 @prefix = "£" - @hint_text = "For all schemes, including Right to Acquire (RTA), Right to Buy (RTB), Voluntary Right to Buy (VRTB) or Preserved Right to Buy (PRTB) sales, enter the full price of the property without any discount" + @hint_text = hint_text @ownership_sch = ownershipsch @question_number = question_number end def question_number case @ownership_sch - when 2 + when 2 # discounted ownership scheme 100 - when 3 + when 3 # outright sale 110 end end + + def hint_text + return if @ownership_sch == 3 # outright sale + + "For all schemes, including Right to Acquire (RTA), Right to Buy (RTB), Voluntary Right to Buy (VRTB) or Preserved Right to Buy (PRTB) sales, enter the full price of the property without any discount" + end end diff --git a/app/models/form/sales/questions/town_or_city.rb b/app/models/form/sales/questions/town_or_city.rb index 9dde3aeb8..25acfe036 100644 --- a/app/models/form/sales/questions/town_or_city.rb +++ b/app/models/form/sales/questions/town_or_city.rb @@ -5,6 +5,7 @@ class Form::Sales::Questions::TownOrCity < ::Form::Question @header = "Town or city" @type = "text" @plain_label = true + @disable_clearing_if_not_routed_or_dynamic_answer_options = true end def hidden_in_check_answers?(_log = nil, _current_user = nil) diff --git a/app/models/form/sales/questions/uprn_known.rb b/app/models/form/sales/questions/uprn_known.rb index 22438e662..09f3c54c9 100644 --- a/app/models/form/sales/questions/uprn_known.rb +++ b/app/models/form/sales/questions/uprn_known.rb @@ -15,7 +15,12 @@ class Form::Sales::Questions::UprnKnown < ::Form::Question "value" => "Not known", }, ] - @hidden_in_check_answers = true + @hidden_in_check_answers = { + "depends_on" => [ + { "uprn_known" => 0 }, + { "uprn_known" => 1 }, + ], + } end ANSWER_OPTIONS = { diff --git a/app/models/form/sales/subsections/property_information.rb b/app/models/form/sales/subsections/property_information.rb index 74a064dca..9efac327f 100644 --- a/app/models/form/sales/subsections/property_information.rb +++ b/app/models/form/sales/subsections/property_information.rb @@ -8,13 +8,13 @@ class Form::Sales::Subsections::PropertyInformation < ::Form::Subsection def pages @pages ||= [ - uprn_questions, Form::Sales::Pages::PropertyNumberOfBedrooms.new(nil, nil, self), Form::Sales::Pages::AboutPriceValueCheck.new("about_price_bedrooms_value_check", nil, self), Form::Sales::Pages::PropertyUnitType.new(nil, nil, self), Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_property_type_value_check", nil, self), Form::Sales::Pages::PercentageDiscountValueCheck.new("percentage_discount_proptype_value_check", nil, self), Form::Sales::Pages::PropertyBuildingType.new(nil, nil, self), + uprn_questions, postcode_and_la_questions, Form::Sales::Pages::AboutPriceValueCheck.new("about_price_la_value_check", nil, self), Form::Sales::Pages::PropertyWheelchairAccessible.new(nil, nil, self), diff --git a/app/models/forms/bulk_upload_lettings/prepare_your_file.rb b/app/models/forms/bulk_upload_lettings/prepare_your_file.rb index c30cf88ad..b5d28a7a2 100644 --- a/app/models/forms/bulk_upload_lettings/prepare_your_file.rb +++ b/app/models/forms/bulk_upload_lettings/prepare_your_file.rb @@ -26,7 +26,12 @@ module Forms end def old_template_path - "/files/bulk-upload-lettings-template-2022-23.xlsx" + case year + when 2022 + "/files/bulk-upload-lettings-template-2022-23.xlsx" + when 2023 + "/files/bulk-upload-lettings-legacy-template-2023-24.xlsx" + end end def template_path diff --git a/app/models/lettings_log.rb b/app/models/lettings_log.rb index 15055d4f4..2d91e2ad9 100644 --- a/app/models/lettings_log.rb +++ b/app/models/lettings_log.rb @@ -55,7 +55,7 @@ class LettingsLog < Log scope :filter_by_organisation, ->(org, _user = nil) { where(owning_organisation: org).or(where(managing_organisation: org)) } AUTOGENERATED_FIELDS = %w[id status created_at updated_at discarded_at].freeze - OPTIONAL_FIELDS = %w[first_time_property_let_as_social_housing tenancycode propcode chcharge].freeze + OPTIONAL_FIELDS = %w[rent_value_check first_time_property_let_as_social_housing tenancycode propcode chcharge].freeze RENT_TYPE_MAPPING_LABELS = { 1 => "Social Rent", 2 => "Affordable Rent", 3 => "Intermediate Rent" }.freeze HAS_BENEFITS_OPTIONS = [1, 6, 8, 7].freeze NUM_OF_WEEKS_FROM_PERIOD = { 2 => 26, 3 => 13, 4 => 12, 5 => 50, 6 => 49, 7 => 48, 8 => 47, 9 => 46, 1 => 52, 10 => 53 }.freeze @@ -435,12 +435,22 @@ class LettingsLog < Log end def soft_min_for_period - soft_min = LaRentRange.find_by(start_year: collection_start_year, la:, beds: beds_for_la_rent_range, lettype:).soft_min + soft_min = LaRentRange.find_by( + start_year: collection_start_year, + la:, + beds: beds_for_la_rent_range, + lettype:, + ).soft_min "#{soft_value_for_period(soft_min)} #{SUFFIX_FROM_PERIOD[period].presence || 'every week'}" end def soft_max_for_period - soft_max = LaRentRange.find_by(start_year: collection_start_year, la:, beds: beds_for_la_rent_range, lettype:).soft_max + soft_max = LaRentRange.find_by( + start_year: collection_start_year, + la:, + beds: beds_for_la_rent_range, + lettype:, + ).soft_max "#{soft_value_for_period(soft_max)} #{SUFFIX_FROM_PERIOD[period].presence || 'every week'}" end diff --git a/app/models/log.rb b/app/models/log.rb index 33d3c4547..4ee748eda 100644 --- a/app/models/log.rb +++ b/app/models/log.rb @@ -48,7 +48,7 @@ class Log < ApplicationRecord service = UprnClient.new(uprn) service.call - return errors.add(:uprn, service.error) if service.error.present? + return errors.add(:uprn, :uprn_error, message: service.error) if service.error.present? presenter = UprnDataPresenter.new(service.result) @@ -174,7 +174,7 @@ private def reset_invalidated_dependent_fields! return unless form - form.reset_not_routed_questions(self) + form.reset_not_routed_questions_and_invalid_answers(self) reset_created_by! end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb new file mode 100644 index 000000000..111d25321 --- /dev/null +++ b/app/models/merge_request.rb @@ -0,0 +1,11 @@ +class MergeRequest < ApplicationRecord + belongs_to :requesting_organisation, class_name: "Organisation" + has_many :merge_request_organisations + has_many :merging_organisations, through: :merge_request_organisations, source: :merging_organisation + + STATUS = { + "unsubmitted" => 0, + "submitted" => 1, + }.freeze + enum status: STATUS +end diff --git a/app/models/merge_request_organisation.rb b/app/models/merge_request_organisation.rb new file mode 100644 index 000000000..08c1d6d45 --- /dev/null +++ b/app/models/merge_request_organisation.rb @@ -0,0 +1,34 @@ +class MergeRequestOrganisation < ApplicationRecord + belongs_to :merge_request, class_name: "MergeRequest" + belongs_to :merging_organisation, class_name: "Organisation" + validates :merge_request, presence: { message: I18n.t("validations.merge_request.merge_request_id.blank") } + validates :merging_organisation, presence: { message: I18n.t("validations.merge_request.merging_organisation_id.blank") } + validate :validate_merging_organisations + + scope :not_unsubmitted, -> { joins(:merge_request).where.not(merge_requests: { status: "unsubmitted" }) } + scope :with_merging_organisation, ->(merging_organisation) { where(merging_organisation:) } + + has_paper_trail + +private + + def validate_merging_organisations + if MergeRequestOrganisation.where(merge_request:, merging_organisation:).count.positive? + errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_part_of_another_merge")) + end + + if MergeRequestOrganisation.not_unsubmitted.with_merging_organisation(merging_organisation).count.positive? + errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_part_of_another_merge")) + merge_request.errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_part_of_another_merge")) + end + + if MergeRequest.not_unsubmitted.where.not(id: merge_request_id).where(requesting_organisation: merging_organisation).count.positive? + errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_part_of_another_merge")) + merge_request.errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_part_of_another_merge")) + end + + if merging_organisation_id.blank? || !Organisation.where(id: merging_organisation_id).exists? + merge_request.errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_not_selected")) + end + end +end diff --git a/app/models/validations/setup_validations.rb b/app/models/validations/setup_validations.rb index bd12d3249..ff814ea35 100644 --- a/app/models/validations/setup_validations.rb +++ b/app/models/validations/setup_validations.rb @@ -3,7 +3,7 @@ module Validations::SetupValidations include CollectionTimeHelper def validate_startdate_setup(record) - return unless record.startdate && date_valid?("startdate", record) && FeatureToggle.startdate_collection_window_validation_enabled? + return unless record.startdate && date_valid?("startdate", record) unless record.startdate.between?(active_collection_start_date, current_collection_end_date) record.errors.add :startdate, startdate_validation_error_message diff --git a/app/models/validations/shared_validations.rb b/app/models/validations/shared_validations.rb index 0a0c3f63b..6a32563a7 100644 --- a/app/models/validations/shared_validations.rb +++ b/app/models/validations/shared_validations.rb @@ -7,6 +7,7 @@ module Validations::SharedValidations main_field_label = main_label || main_field.to_s.humanize(capitalize: false) other_field_label = other_label || other_field.to_s.humanize(capitalize: false) if record[main_field] == value_other && record[other_field].blank? + record.errors.add main_field.to_sym, I18n.t("validations.other_field_missing", main_field_label:, other_field_label:) record.errors.add other_field.to_sym, I18n.t("validations.other_field_missing", main_field_label:, other_field_label:) end diff --git a/app/models/validations/soft_validations.rb b/app/models/validations/soft_validations.rb index cfcfb643b..5c571af6a 100644 --- a/app/models/validations/soft_validations.rb +++ b/app/models/validations/soft_validations.rb @@ -40,7 +40,12 @@ module Validations::SoftValidations def rent_in_soft_max_range? return unless brent && weekly_value(brent) && startdate - rent_range = LaRentRange.find_by(start_year: collection_start_year, la:, beds: beds_for_la_rent_range, lettype: get_lettype) + rent_range = LaRentRange.find_by( + start_year: collection_start_year, + la:, + beds: beds_for_la_rent_range, + lettype: get_lettype, + ) if beds.present? && rent_range.present? && beds > LaRentRange::MAX_BEDS weekly_value(brent) > rent_range.soft_max elsif rent_range.present? diff --git a/app/services/bulk_upload/lettings/year2022/row_parser.rb b/app/services/bulk_upload/lettings/year2022/row_parser.rb index bfb7940ed..e7f212b55 100644 --- a/app/services/bulk_upload/lettings/year2022/row_parser.rb +++ b/app/services/bulk_upload/lettings/year2022/row_parser.rb @@ -278,58 +278,58 @@ class BulkUpload::Lettings::Year2022::RowParser attribute :field_134, :integer validates :field_1, presence: { message: I18n.t("validations.not_answered", question: "letting type") }, - inclusion: { in: (1..12).to_a, message: I18n.t("validations.invalid_option", question: "letting type") } - validates :field_4, presence: { if: proc { [2, 4, 6, 8, 10, 12].include?(field_1) } } - - validates :field_12, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 1 must be a number or the letter R" } - validates :field_13, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 2 must be a number or the letter R" }, allow_blank: true - validates :field_14, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 3 must be a number or the letter R" }, allow_blank: true - validates :field_15, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 4 must be a number or the letter R" }, allow_blank: true - validates :field_16, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 5 must be a number or the letter R" }, allow_blank: true - validates :field_17, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 6 must be a number or the letter R" }, allow_blank: true - validates :field_18, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 7 must be a number or the letter R" }, allow_blank: true - validates :field_19, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 8 must be a number or the letter R" }, allow_blank: true - - validates :field_96, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (day)") } - validates :field_97, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (month)") } - validates :field_98, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (year)") } - - validates :field_98, format: { with: /\A\d{2}\z/, message: I18n.t("validations.setup.startdate.year_not_two_digits") } - - validate :validate_data_types - validate :validate_nulls - validate :validate_relevant_collection_window - validate :validate_la_with_local_housing_referral - validate :validate_cannot_be_la_referral_if_general_needs_and_la - validate :validate_leaving_reason_for_renewal - validate :validate_lettings_type_matches_bulk_upload - validate :validate_only_one_housing_needs_type - validate :validate_no_disabled_needs_conjunction - validate :validate_dont_know_disabled_needs_conjunction - validate :validate_no_and_dont_know_disabled_needs_conjunction - - validate :validate_owning_org_data_given - validate :validate_owning_org_exists - validate :validate_owning_org_owns_stock - validate :validate_owning_org_permitted - - validate :validate_managing_org_data_given - validate :validate_managing_org_exists - validate :validate_managing_org_related - - validate :validate_scheme_related - validate :validate_scheme_exists - validate :validate_scheme_data_given - - validate :validate_location_related - validate :validate_location_exists - validate :validate_location_data_given - - validate :validate_created_by_exists - validate :validate_created_by_related - validate :validate_rent_type - - validate :validate_valid_radio_option + inclusion: { in: (1..12).to_a, message: I18n.t("validations.invalid_option", question: "letting type") }, on: :after_log + validates :field_4, presence: { if: proc { [2, 4, 6, 8, 10, 12].include?(field_1) } }, on: :after_log + + validates :field_12, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 1 must be a number or the letter R" }, on: :after_log + validates :field_13, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 2 must be a number or the letter R" }, allow_blank: true, on: :after_log + validates :field_14, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 3 must be a number or the letter R" }, allow_blank: true, on: :after_log + validates :field_15, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 4 must be a number or the letter R" }, allow_blank: true, on: :after_log + validates :field_16, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 5 must be a number or the letter R" }, allow_blank: true, on: :after_log + validates :field_17, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 6 must be a number or the letter R" }, allow_blank: true, on: :after_log + validates :field_18, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 7 must be a number or the letter R" }, allow_blank: true, on: :after_log + validates :field_19, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 8 must be a number or the letter R" }, allow_blank: true, on: :after_log + + validates :field_96, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (day)") }, on: :after_log + validates :field_97, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (month)") }, on: :after_log + validates :field_98, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (year)") }, on: :after_log + + validates :field_98, format: { with: /\A\d{2}\z/, message: I18n.t("validations.setup.startdate.year_not_two_digits") }, on: :after_log + + validate :validate_data_types, on: :after_log + validate :validate_nulls, on: :after_log + validate :validate_relevant_collection_window, on: :after_log + validate :validate_la_with_local_housing_referral, on: :after_log + validate :validate_cannot_be_la_referral_if_general_needs_and_la, on: :after_log + validate :validate_leaving_reason_for_renewal, on: :after_log + validate :validate_lettings_type_matches_bulk_upload, on: :after_log + validate :validate_only_one_housing_needs_type, on: :after_log + validate :validate_no_disabled_needs_conjunction, on: :after_log + validate :validate_dont_know_disabled_needs_conjunction, on: :after_log + validate :validate_no_and_dont_know_disabled_needs_conjunction, on: :after_log + + validate :validate_owning_org_data_given, on: :after_log + validate :validate_owning_org_exists, on: :after_log + validate :validate_owning_org_owns_stock, on: :after_log + validate :validate_owning_org_permitted, on: :after_log + + validate :validate_managing_org_data_given, on: :after_log + validate :validate_managing_org_exists, on: :after_log + validate :validate_managing_org_related, on: :after_log + + validate :validate_scheme_related, on: :after_log + validate :validate_scheme_exists, on: :after_log + validate :validate_scheme_data_given, on: :after_log + + validate :validate_location_related, on: :after_log + validate :validate_location_exists, on: :after_log + validate :validate_location_data_given, on: :after_log + + validate :validate_created_by_exists, on: :after_log + validate :validate_created_by_related, on: :after_log + validate :validate_rent_type, on: :after_log + + validate :validate_valid_radio_option, on: :before_log def self.question_for_field(field) QUESTIONS[field] @@ -340,9 +340,13 @@ class BulkUpload::Lettings::Year2022::RowParser return true if blank_row? + super(:before_log) + before_errors = errors.dup + log.valid? - super + super(:after_log) + errors.merge!(before_errors) log.errors.each do |error| fields = field_mapping_for_errors[error.attribute] || [] @@ -1089,6 +1093,8 @@ private def voiddate Date.new(field_91 + 2000, field_90, field_89) if field_91.present? && field_90.present? && field_89.present? + rescue Date::Error + Date.new end def majorrepairs @@ -1097,6 +1103,8 @@ private def mrcdate Date.new(field_94 + 2000, field_93, field_92) if field_94.present? && field_93.present? && field_92.present? + rescue Date::Error + Date.new end def prevloc diff --git a/app/services/bulk_upload/lettings/year2023/row_parser.rb b/app/services/bulk_upload/lettings/year2023/row_parser.rb index 43b229aa3..84f30cc7b 100644 --- a/app/services/bulk_upload/lettings/year2023/row_parser.rb +++ b/app/services/bulk_upload/lettings/year2023/row_parser.rb @@ -280,59 +280,59 @@ class BulkUpload::Lettings::Year2023::RowParser attribute :field_134, :decimal validates :field_5, presence: { message: I18n.t("validations.not_answered", question: "letting type") }, - inclusion: { in: (1..12).to_a, message: I18n.t("validations.invalid_option", question: "letting type") } - validates :field_16, presence: { if: proc { [2, 4, 6, 8, 10, 12].include?(field_5) } } - - validates :field_46, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 1 must be a number or the letter R" } - validates :field_52, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 2 must be a number or the letter R" }, allow_blank: true - validates :field_56, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 3 must be a number or the letter R" }, allow_blank: true - validates :field_60, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 4 must be a number or the letter R" }, allow_blank: true - validates :field_64, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 5 must be a number or the letter R" }, allow_blank: true - validates :field_68, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 6 must be a number or the letter R" }, allow_blank: true - validates :field_72, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 7 must be a number or the letter R" }, allow_blank: true - validates :field_76, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 8 must be a number or the letter R" }, allow_blank: true - - validates :field_6, presence: { message: I18n.t("validations.not_answered", question: "property renewal") } - validates :field_7, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (day)") } - validates :field_8, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (month)") } - validates :field_9, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (year)") } - - validates :field_9, format: { with: /\A\d{2}\z/, message: I18n.t("validations.setup.startdate.year_not_two_digits") } - - validate :validate_needs_type_present - validate :validate_data_types - validate :validate_nulls - validate :validate_relevant_collection_window - validate :validate_la_with_local_housing_referral - validate :validate_cannot_be_la_referral_if_general_needs_and_la - validate :validate_leaving_reason_for_renewal - validate :validate_lettings_type_matches_bulk_upload - validate :validate_only_one_housing_needs_type - validate :validate_no_disabled_needs_conjunction - validate :validate_dont_know_disabled_needs_conjunction - validate :validate_no_and_dont_know_disabled_needs_conjunction - - validate :validate_owning_org_data_given - validate :validate_owning_org_exists - validate :validate_owning_org_owns_stock - validate :validate_owning_org_permitted - - validate :validate_managing_org_data_given - validate :validate_managing_org_exists - validate :validate_managing_org_related - - validate :validate_scheme_related - validate :validate_scheme_exists - validate :validate_scheme_data_given - - validate :validate_location_related - validate :validate_location_exists - validate :validate_location_data_given - - validate :validate_created_by_exists - validate :validate_created_by_related - - validate :validate_valid_radio_option + inclusion: { in: (1..12).to_a, message: I18n.t("validations.invalid_option", question: "letting type") }, on: :after_log + validates :field_16, presence: { if: proc { [2, 4, 6, 8, 10, 12].include?(field_5) } }, on: :after_log + + validates :field_46, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 1 must be a number or the letter R" }, on: :after_log + validates :field_52, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 2 must be a number or the letter R" }, allow_blank: true, on: :after_log + validates :field_56, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 3 must be a number or the letter R" }, allow_blank: true, on: :after_log + validates :field_60, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 4 must be a number or the letter R" }, allow_blank: true, on: :after_log + validates :field_64, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 5 must be a number or the letter R" }, allow_blank: true, on: :after_log + validates :field_68, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 6 must be a number or the letter R" }, allow_blank: true, on: :after_log + validates :field_72, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 7 must be a number or the letter R" }, allow_blank: true, on: :after_log + validates :field_76, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 8 must be a number or the letter R" }, allow_blank: true, on: :after_log + + validates :field_6, presence: { message: I18n.t("validations.not_answered", question: "property renewal") }, on: :after_log + validates :field_7, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (day)") }, on: :after_log + validates :field_8, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (month)") }, on: :after_log + validates :field_9, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (year)") }, on: :after_log + + validates :field_9, format: { with: /\A\d{2}\z/, message: I18n.t("validations.setup.startdate.year_not_two_digits") }, on: :after_log + + validate :validate_needs_type_present, on: :after_log + validate :validate_data_types, on: :after_log + validate :validate_nulls, on: :after_log + validate :validate_relevant_collection_window, on: :after_log + validate :validate_la_with_local_housing_referral, on: :after_log + validate :validate_cannot_be_la_referral_if_general_needs_and_la, on: :after_log + validate :validate_leaving_reason_for_renewal, on: :after_log + validate :validate_lettings_type_matches_bulk_upload, on: :after_log + validate :validate_only_one_housing_needs_type, on: :after_log + validate :validate_no_disabled_needs_conjunction, on: :after_log + validate :validate_dont_know_disabled_needs_conjunction, on: :after_log + validate :validate_no_and_dont_know_disabled_needs_conjunction, on: :after_log + + validate :validate_owning_org_data_given, on: :after_log + validate :validate_owning_org_exists, on: :after_log + validate :validate_owning_org_owns_stock, on: :after_log + validate :validate_owning_org_permitted, on: :after_log + + validate :validate_managing_org_data_given, on: :after_log + validate :validate_managing_org_exists, on: :after_log + validate :validate_managing_org_related, on: :after_log + + validate :validate_scheme_related, on: :after_log + validate :validate_scheme_exists, on: :after_log + validate :validate_scheme_data_given, on: :after_log + + validate :validate_location_related, on: :after_log + validate :validate_location_exists, on: :after_log + validate :validate_location_data_given, on: :after_log + + validate :validate_created_by_exists, on: :after_log + validate :validate_created_by_related, on: :after_log + + validate :validate_valid_radio_option, on: :before_log def self.question_for_field(field) QUESTIONS[field] @@ -343,9 +343,13 @@ class BulkUpload::Lettings::Year2023::RowParser return true if blank_row? + super(:before_log) + before_errors = errors.dup + log.valid? - super + super(:after_log) + errors.merge!(before_errors) log.errors.each do |error| fields = field_mapping_for_errors[error.attribute] || [] @@ -1313,10 +1317,14 @@ private def mrcdate Date.new(field_38 + 2000, field_37, field_36) if field_38.present? && field_37.present? && field_36.present? + rescue Date::Error + Date.new end def voiddate Date.new(field_35 + 2000, field_34, field_33) if field_35.present? && field_34.present? && field_33.present? + rescue Date::Error + Date.new end def first_time_property_let_as_social_housing diff --git a/config/initializers/feature_toggle.rb b/app/services/feature_toggle.rb similarity index 89% rename from config/initializers/feature_toggle.rb rename to app/services/feature_toggle.rb index 9986af543..a25d99b84 100644 --- a/config/initializers/feature_toggle.rb +++ b/app/services/feature_toggle.rb @@ -4,10 +4,6 @@ class FeatureToggle Rails.env.production? || Rails.env.test? || Rails.env.staging? end - def self.startdate_collection_window_validation_enabled? - Rails.env.production? || Rails.env.test? - end - def self.startdate_two_week_validation_enabled? Rails.env.production? || Rails.env.test? || Rails.env.staging? end @@ -36,7 +32,11 @@ class FeatureToggle true end - def self.bulk_upload_logs? + def self.bulk_upload_lettings_logs? + true + end + + def self.bulk_upload_sales_logs? !Rails.env.production? end diff --git a/app/services/imports/lettings_logs_import_service.rb b/app/services/imports/lettings_logs_import_service.rb index 56fb0c0d2..1bc98925a 100644 --- a/app/services/imports/lettings_logs_import_service.rb +++ b/app/services/imports/lettings_logs_import_service.rb @@ -202,6 +202,14 @@ module Imports attributes["first_time_property_let_as_social_housing"] = first_time_let(attributes["rsnvac"]) attributes["declaration"] = declaration(xml_doc) + attributes["uprn"] = string_or_nil(xml_doc, "UPRN") + attributes["uprn_known"] = attributes["uprn"].present? ? 1 : 0 + attributes["uprn_confirmed"] = attributes["uprn"].present? ? 1 : 0 + attributes["address_line1"] = string_or_nil(xml_doc, "AddressLine1") + attributes["address_line2"] = string_or_nil(xml_doc, "AddressLine2") + attributes["town_or_city"] = string_or_nil(xml_doc, "TownCity") + attributes["county"] = string_or_nil(xml_doc, "County") + set_partial_charges_to_zero(attributes) # Supported Housing fields @@ -211,15 +219,19 @@ module Imports schemes = Scheme.where(old_visible_id: scheme_old_visible_id, owning_organisation_id: attributes["owning_organisation_id"]) location = Location.find_by(old_visible_id: location_old_visible_id, scheme: schemes) - raise "No matching location for scheme #{scheme_old_visible_id} and location #{location_old_visible_id} (visible IDs)" if location.nil? + if location.nil? && [location_old_visible_id, scheme_old_visible_id].all?(&:present?) && previous_status != "saved" + raise "No matching location for scheme #{scheme_old_visible_id} and location #{location_old_visible_id} (visible IDs)" + end - # Set the scheme via location, because the scheme old visible ID can be duplicated at import - attributes["location_id"] = location.id - attributes["scheme_id"] = location.scheme.id - attributes["sheltered"] = unsafe_string_as_integer(xml_doc, "Q1e") - attributes["chcharge"] = safe_string_as_decimal(xml_doc, "Q18b") - attributes["household_charge"] = household_charge(xml_doc) - attributes["is_carehome"] = is_carehome(location.scheme) + if location.present? + # Set the scheme via location, because the scheme old visible ID can be duplicated at import + attributes["location_id"] = location.id + attributes["scheme_id"] = location.scheme.id + attributes["sheltered"] = unsafe_string_as_integer(xml_doc, "Q1e") + attributes["chcharge"] = safe_string_as_decimal(xml_doc, "Q18b") + attributes["household_charge"] = household_charge(xml_doc) + attributes["is_carehome"] = is_carehome(location.scheme) + end end # Handles confidential schemes @@ -333,6 +345,13 @@ module Imports return save_lettings_log(attributes, previous_status) end + if lettings_log.errors.of_kind?(:uprn, :uprn_error) + @logger.warn("Log #{lettings_log.old_id}: Setting uprn_known to no with error: #{lettings_log.errors[:uprn].join(', ')}") + @logs_overridden << lettings_log.old_id + attributes["uprn_known"] = 0 + return save_lettings_log(attributes, previous_status) + end + @logger.error("Log #{lettings_log.old_id}: Failed to import") lettings_log.errors.each do |error| @logger.error("Validation error: Field #{error.attribute}:") @@ -360,7 +379,7 @@ module Imports end def fields_not_present_in_softwire_data - %w[majorrepairs illness_type_0 tshortfall_known pregnancy_value_check retirement_value_check rent_value_check net_income_value_check major_repairs_date_value_check void_date_value_check carehome_charges_value_check housingneeds_type housingneeds_other created_by] + %w[majorrepairs illness_type_0 tshortfall_known pregnancy_value_check retirement_value_check rent_value_check net_income_value_check major_repairs_date_value_check void_date_value_check carehome_charges_value_check housingneeds_type housingneeds_other created_by uprn_known uprn_confirmed] end def check_status_completed(lettings_log, previous_status) diff --git a/app/services/imports/sales_logs_import_service.rb b/app/services/imports/sales_logs_import_service.rb index 2d6551189..8702a8846 100644 --- a/app/services/imports/sales_logs_import_service.rb +++ b/app/services/imports/sales_logs_import_service.rb @@ -126,10 +126,6 @@ module Imports attributes["postcode_full"] = parse_postcode(string_or_nil(xml_doc, "Q14Postcode")) attributes["pcodenk"] = 0 if attributes["postcode_full"].present? # known if given attributes["soctenant"] = 0 if attributes["ownershipsch"] == 1 - attributes["ethnic_group2"] = nil # 23/24 variable - attributes["ethnicbuy2"] = nil # 23/24 variable - attributes["prevshared"] = nil # 23/24 variable - attributes["staircasesale"] = nil # 23/24 variable attributes["previous_la_known"] = 1 if attributes["prevloc"].present? if attributes["la"].present? @@ -158,6 +154,27 @@ module Imports attributes["buyer_livein_value_check"] = 0 attributes["percentage_discount_value_check"] = 0 + # 2023/34 attributes + attributes["uprn"] = string_or_nil(xml_doc, "UPRN") + attributes["uprn_known"] = attributes["uprn"].present? ? 1 : 0 + attributes["uprn_confirmed"] = attributes["uprn"].present? ? 1 : 0 + attributes["address_line1"] = string_or_nil(xml_doc, "AddressLine1") + attributes["address_line2"] = string_or_nil(xml_doc, "AddressLine2") + attributes["town_or_city"] = string_or_nil(xml_doc, "TownCity") + attributes["county"] = string_or_nil(xml_doc, "County") + + attributes["proplen_asked"] = 0 if attributes["proplen"]&.positive? + attributes["proplen_asked"] = 1 if attributes["proplen"]&.zero? + + attributes["prevshared"] = unsafe_string_as_integer(xml_doc, "PREVSHARED") + attributes["ethnicbuy2"] = unsafe_string_as_integer(xml_doc, "P2Eth") + attributes["ethnic_group2"] = ethnic_group(attributes["ethnicbuy2"]) + attributes["nationalbuy2"] = unsafe_string_as_integer(xml_doc, "P2Nat") + attributes["buy2living"] = unsafe_string_as_integer(xml_doc, "BUY2LIVEIN") + + attributes["staircasesale"] = unsafe_string_as_integer(xml_doc, "STAIRCASESALE") + attributes["prevtenbuy2"] = unsafe_string_as_integer(xml_doc, "PREVTENBUY2") + # Sets the log creator owner_id = meta_field_value(xml_doc, "owner-user-id").strip if owner_id.present? @@ -233,6 +250,11 @@ module Imports @logs_overridden << sales_log.old_id attributes.delete("mortgage") save_sales_log(attributes, previous_status) + elsif sales_log.errors.of_kind?(:uprn, :uprn_error) + @logger.warn("Log #{sales_log.old_id}: Setting uprn_known to no with error: #{sales_log.errors[:uprn].join(', ')}") + @logs_overridden << sales_log.old_id + attributes["uprn_known"] = 0 + save_sales_log(attributes, previous_status) else @logger.error("Log #{sales_log.old_id}: Failed to import") sales_log.errors.each do |error| @@ -282,7 +304,9 @@ module Imports student_not_child_value_check discounted_sale_value_check buyer_livein_value_check - percentage_discount_value_check] + percentage_discount_value_check + uprn_known + uprn_confirmed] end def check_status_completed(sales_log, previous_status) @@ -461,12 +485,14 @@ module Imports safe_string_as_decimal(xml_doc, "Q29MonthlyCharges") when 2 safe_string_as_decimal(xml_doc, "Q37MonthlyCharges") + when 3 + safe_string_as_decimal(xml_doc, "Q44MonthlyCharges") end end def ownership_from_type(attributes) case attributes["type"] - when 2, 24, 18, 16, 28, 31, 30 + when 2, 24, 18, 16, 28, 31, 30, 32 1 # shared ownership when 8, 14, 27, 9, 29, 21, 22 2 # discounted ownership @@ -562,6 +588,9 @@ module Imports attributes["relat2"] ||= "R" attributes["inc2mort"] ||= 3 attributes["buy2livein"] ||= 1 unless attributes["ownershipsch"] == 3 + attributes["ethnic_group2"] ||= 17 + attributes["ethnicbuy2"] ||= 17 + attributes["nationalbuy2"] ||= 13 end # other household members characteristics diff --git a/app/services/imports/scheme_location_import_service.rb b/app/services/imports/scheme_location_import_service.rb index a26139aec..93093e6da 100644 --- a/app/services/imports/scheme_location_import_service.rb +++ b/app/services/imports/scheme_location_import_service.rb @@ -77,7 +77,7 @@ module Imports attributes["scheme_type"] = safe_string_as_integer(xml_doc, "scheme-type") registered_under_care_act = safe_string_as_integer(xml_doc, "reg-home-type") attributes["registered_under_care_act"] = registered_under_care_act&.zero? ? nil : registered_under_care_act - attributes["support_type"] = safe_string_as_integer(xml_doc, "support-type") + attributes["support_type"] = support_type(xml_doc) attributes["intended_stay"] = string_or_nil(xml_doc, "intended-stay") attributes["mobility_type"] = string_or_nil(xml_doc, "mobility-type") attributes["primary_client_group"] = string_or_nil(xml_doc, "client-group-1") @@ -205,5 +205,12 @@ module Imports date = string_or_nil(xml_doc, attribute) Time.zone.parse(date) if date end + + def support_type(xml_doc) + type = safe_string_as_integer(xml_doc, "support-type") + return unless type + + Scheme::SUPPORT_TYPE.value?(type) ? type : 0 + end end end diff --git a/app/views/bulk_upload_lettings_results/summary.html.erb b/app/views/bulk_upload_lettings_results/summary.html.erb index 533e2af05..51aa652f8 100644 --- a/app/views/bulk_upload_lettings_results/summary.html.erb +++ b/app/views/bulk_upload_lettings_results/summary.html.erb @@ -20,7 +20,7 @@ <% end %> <% c.with_tab(label: "Full error report") do %> - <% @bulk_upload.bulk_upload_errors.group_by(&:row).each do |_row, errors_for_row| %> + <% @bulk_upload.bulk_upload_errors.order_by_cell.group_by(&:row).each do |_row, errors_for_row| %> <%= render BulkUploadErrorRowComponent.new(bulk_upload_errors: errors_for_row) %> <% end %> <% end %> diff --git a/app/views/form/_checkbox_question.html.erb b/app/views/form/_checkbox_question.html.erb index c855e2d88..2a9c33941 100644 --- a/app/views/form/_checkbox_question.html.erb +++ b/app/views/form/_checkbox_question.html.erb @@ -6,14 +6,14 @@ hint: { text: question.hint_text&.html_safe } do %> <% after_divider = false %> - <% question.displayed_answer_options(@log).map do |key, options| %> + <% question.displayed_answer_options(@log).map do |key, option| %> <% if key.starts_with?("divider") %> - <% after_divider = true %> - <%= f.govuk_check_box_divider %> + <% after_divider = true %> + <%= f.govuk_check_box_divider %> <% else %> <%= f.govuk_check_box question.id, key, - label: { text: options["value"] }, - hint: { text: options["hint"] }, + label: { text: option["value"] }, + hint: { text: option["hint"] }, checked: @log[key] == 1, exclusive: after_divider, **stimulus_html_attributes(question) %> diff --git a/app/views/layouts/_collection_resources.html.erb b/app/views/layouts/_collection_resources.html.erb index 37e9c4b94..f583bd813 100644 --- a/app/views/layouts/_collection_resources.html.erb +++ b/app/views/layouts/_collection_resources.html.erb @@ -9,6 +9,21 @@ href: download_23_24_lettings_form_path, metadata: "PDF, 278 KB, 8 pages", }, + { + name: "Lettings bulk upload template (2023/24) *new question ordering*", + href: download_23_24_lettings_bulk_upload_template_path, + metadata: "Microsoft Excel, 15 KB", + }, + { + name: "Lettings bulk upload template (2023/24)", + href: download_23_24_lettings_bulk_upload_legacy_template_path, + metadata: "Microsoft Excel, 15 KB", + }, + { + name: "Lettings bulk upload specification (2023/24)", + href: download_23_24_lettings_bulk_upload_specification_path, + metadata: "Microsoft Excel, 90 KB", + }, ]) %>

Lettings 22/23

@@ -18,6 +33,16 @@ href: "https://core.communities.gov.uk/public/download/guides-and-manuals/2022-23%20Lettings%20paper%20form.pdf?download-format=pdf", metadata: "PDF, 654 KB, 4 pages", }, + { + name: "Lettings bulk upload template (2022/23)", + href: download_22_23_lettings_bulk_upload_template_path, + metadata: "Microsoft Excel, 36 KB", + }, + { + name: "Lettings bulk upload specification (2022/23)", + href: download_22_23_lettings_bulk_upload_specification_path, + metadata: "Microsoft Excel, 53 KB", + }, ]) %>

Sales 23/24

diff --git a/app/views/logs/index.html.erb b/app/views/logs/index.html.erb index 2d88c9bd6..04209be73 100644 --- a/app/views/logs/index.html.erb +++ b/app/views/logs/index.html.erb @@ -55,8 +55,12 @@ <%= govuk_button_to "Create a new sales log", sales_logs_path, class: "govuk-!-margin-right-6" %> <% end %> - <% if FeatureToggle.bulk_upload_logs? %> - <%= govuk_button_link_to "Upload #{log_type_for_controller(controller)} logs in bulk", bulk_upload_path_for_controller(controller, id: "start"), secondary: true %> + <% if FeatureToggle.bulk_upload_lettings_logs? && log_type_for_controller(controller) == "lettings" %> + <%= govuk_button_link_to "Upload lettings logs in bulk", bulk_upload_path_for_controller(controller, id: "start"), secondary: true %> + <% end %> + + <% if FeatureToggle.bulk_upload_sales_logs? && log_type_for_controller(controller) == "sales" %> + <%= govuk_button_link_to "Upload sales logs in bulk", bulk_upload_path_for_controller(controller, id: "start"), secondary: true %> <% end %> <% end %> diff --git a/app/views/merge_requests/organisations.html.erb b/app/views/merge_requests/organisations.html.erb new file mode 100644 index 000000000..14b70c17c --- /dev/null +++ b/app/views/merge_requests/organisations.html.erb @@ -0,0 +1,49 @@ +<% content_for :before_content do %> + + <% title = "Tell us if your organisation is merging" %> + <% content_for :title, title %> + <%= govuk_back_link href: merge_request_organisation_path(id: @merge_request.requesting_organisation_id) %> +<% end %> + +

Which organisations are merging?

+ +
+
+

Add all organisations to be merged - we have already added your own.

+ +<%= form_with model: @merge_request, url: organisations_merge_request_path, method: :patch do |f| %> + <%= f.govuk_error_summary %> +

Start typing to search

+ <%= render partial: "organisation_relationships/related_organisation_select_question", locals: { + field: :merging_organisation, + question: Form::Question.new("", { "answer_options" => @answer_options }, nil), + f:, + } %> + <%= f.govuk_submit "Add organisation", classes: "govuk-button--secondary" %> + <%= govuk_table do |table| %> + <% @merge_request.merging_organisations.each do |merging_organisation| %> + <%= table.body do |body| %> + <%= body.row do |row| %> + <% row.cell(text: merging_organisation.name) %> + <% row.cell(html_attributes: { + scope: "row", + class: "govuk-!-text-align-right", + }) do %> + <% if @merge_request.requesting_organisation != merging_organisation %> + <%= govuk_link_to("Remove", organisations_remove_merge_request_path(merge_request: { merging_organisation: merging_organisation.id })) %> + <% end %> + <% end %> + <% end %> + <% end %> + <% end %> + <% end %> +<% end %> +<%= form_with model: @merge_request, url: merge_request_path(id: @merge_request.id), method: :patch do |f| %> + <%= govuk_details(summary_text: "I cannot find an organisation on the list") do %> + <%= f.govuk_text_area :other_merging_organisations, label: { text: "Other organisations" }, hint: { text: "List other organisations that are part of the merge but not registered on CORE." }, rows: 9 %> + <% end %> + <% if @merge_request.merging_organisations.count > 1 %> + <%= f.govuk_submit "Continue" %> + <% end %> +<% end %> +
diff --git a/app/views/organisations/merge.html.erb b/app/views/organisations/merge_request.html.erb similarity index 89% rename from app/views/organisations/merge.html.erb rename to app/views/organisations/merge_request.html.erb index 7d72d08b7..d8602b46c 100644 --- a/app/views/organisations/merge.html.erb +++ b/app/views/organisations/merge_request.html.erb @@ -40,9 +40,10 @@ <%= govuk_warning_text text: "You will not be able to submit your request without the above information. Do not start the form until you have obtained all of the information. " %> - <%= govuk_start_button( - text: "Start now", - href: "#", - ) %> + + <%= form_for @merge_request, url: merge_requests_path do |f| %> + <%= f.hidden_field :requesting_organisation_id, value: @organisation.id %> + <%= f.submit "Start now", class: "govuk-button govuk-button--start" %> + <% end %>
diff --git a/app/views/organisations/show.html.erb b/app/views/organisations/show.html.erb index 4561e985b..e2dbb9862 100644 --- a/app/views/organisations/show.html.erb +++ b/app/views/organisations/show.html.erb @@ -36,7 +36,7 @@ <% end %> <% end %> <% if FeatureToggle.merge_organisations_enabled? %> -

Is your organisation merging with another? <%= govuk_link_to "Let us know using this form", merge_organisation_path %>

+

Is your organisation merging with another? <%= govuk_link_to "Let us know using this form", merge_request_organisation_path %>

<% end %> diff --git a/config/forms/2021_2022.json b/config/forms/2021_2022.json index cf8ac4cd9..84065e019 100644 --- a/config/forms/2021_2022.json +++ b/config/forms/2021_2022.json @@ -24,6 +24,7 @@ "header": "Do you know the property’s postcode?", "hint_text": "", "type": "radio", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "answer_options": { "1": { "value": "Yes" @@ -54,6 +55,7 @@ "hint_text": "", "type": "text", "width": 5, + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "inferred_answers": { "la": { "is_la_inferred": true @@ -82,6 +84,7 @@ "header": "What is the local authority of the property?", "hint_text": "", "type": "select", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "answer_options": { "": "Select an option", "E07000223": "Adur", @@ -6482,6 +6485,7 @@ "header": "Do you know the postcode of the household’s last settled accommodation?", "hint_text": "This is also known as the household’s ‘last settled home’.", "type": "radio", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "answer_options": { "1": { "value": "Yes" @@ -6512,6 +6516,7 @@ "hint_text": "", "type": "text", "width": 5, + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "inferred_answers": { "prevloc": { "is_previous_la_inferred": true @@ -6535,6 +6540,7 @@ "header": "Do you know the local authority of the household’s last settled accommodation?", "hint_text": "This is also known as the household’s ‘last settled home’.", "type": "radio", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "hidden_in_check_answers": { "depends_on": [ { @@ -6564,6 +6570,7 @@ "header": "Select a local authority", "hint_text": "Select ‘Northern Ireland’, ‘Scotland’, ‘Wales’ or ‘Outside the UK’ if the household’s last settled home was outside England.", "type": "select", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "answer_options": { "": "Select an option", "S12000033": "Aberdeen City", diff --git a/config/forms/2022_2023.json b/config/forms/2022_2023.json index 181d8a19c..c3b4e3341 100644 --- a/config/forms/2022_2023.json +++ b/config/forms/2022_2023.json @@ -24,6 +24,7 @@ "header": "Do you know the property’s postcode?", "hint_text": "", "type": "radio", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "answer_options": { "1": { "value": "Yes" @@ -54,6 +55,7 @@ "hint_text": "", "type": "text", "width": 5, + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "inferred_answers": { "la": { "is_la_inferred": true @@ -82,6 +84,7 @@ "header": "What is the local authority of the property?", "hint_text": "", "type": "select", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "answer_options": { "": "Select an option", "E07000223": "Adur", @@ -6435,6 +6438,7 @@ "header": "Do you know the postcode of the household’s last settled accommodation?", "hint_text": "This is also known as the household’s ‘last settled home’.", "type": "radio", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "answer_options": { "1": { "value": "Yes" @@ -6465,6 +6469,7 @@ "hint_text": "", "type": "text", "width": 5, + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "inferred_answers": { "prevloc": { "is_previous_la_inferred": true @@ -6488,6 +6493,7 @@ "header": "Do you know the local authority of the household’s last settled accommodation?", "hint_text": "This is also known as the household’s ‘last settled home’.", "type": "radio", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "hidden_in_check_answers": { "depends_on": [ { @@ -6517,6 +6523,7 @@ "header": "Select a local authority", "hint_text": "Select ‘Northern Ireland’, ‘Scotland’, ‘Wales’ or ‘Outside the UK’ if the household’s last settled home was outside England.", "type": "select", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "answer_options": { "": "Select an option", "S12000033": "Aberdeen City", diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb index 71d184ad6..928c8f671 100644 --- a/config/initializers/filter_parameter_logging.rb +++ b/config/initializers/filter_parameter_logging.rb @@ -2,5 +2,5 @@ # Configure sensitive parameters which will be filtered from the log file. Rails.application.config.filter_parameters += %i[ - passw secret token crypt salt certificate otp ssn + passw secret token crypt salt certificate otp ssn reset_password_token ] diff --git a/config/locales/en.yml b/config/locales/en.yml index 6d6a3558f..fd0a8296a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -369,7 +369,7 @@ en: over_20: "The lead tenant must be under 20 as you told us their housing situation immediately before this letting was a children’s home or foster care" ecstat: retired_over_70: "Person %{person_num} must be retired if over 70" - child_under_16: "Person’s %{person_num} working situation must be ’child under 16‘ as you told us they’re under 16" + child_under_16: "Person %{person_num}’s working situation must be ‘child under 16’ as you told us they’re under 16" child_over_16: "Answer cannot be ‘child under 16’ as you told us the person %{person_num} is older than 16" not_student_16_19: "Person’s %{person_num} working situation must be full-time student or prefers not to say as you told us they’re between 16 and 19." student_16_19: @@ -508,6 +508,9 @@ en: discounted_ownership_value: "Mortgage, deposit, and grant total must equal %{value_with_discount}" monthly_rent: higher_than_expected: "Basic monthly rent must be between £0.00 and £9,999.00" + merge_request: + organisation_part_of_another_merge: "This organisation is part of another merge - select a different one" + organisation_not_selected: "Select an organisation from the search list" soft_validations: net_income: diff --git a/config/routes.rb b/config/routes.rb index f6e00083b..e9542ebec 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -35,9 +35,17 @@ Rails.application.routes.draw do get "/accessibility-statement", to: "content#accessibility_statement" get "/privacy-notice", to: "content#privacy_notice" get "/data-sharing-agreement", to: "content#data_sharing_agreement" + + get "/download-23-24-lettings-form", to: "start#download_23_24_lettings_form" + get "/download-23-24-lettings-bulk-upload-template", to: "start#download_23_24_lettings_bulk_upload_template" + get "/download-23-24-lettings-bulk-upload-legacy-template", to: "start#download_23_24_lettings_bulk_upload_legacy_template" + get "/download-23-24-lettings-bulk-upload-specification", to: "start#download_23_24_lettings_bulk_upload_specification" + + get "/download-22-23-lettings-bulk-upload-template", to: "start#download_22_23_lettings_bulk_upload_template" + get "/download-22-23-lettings-bulk-upload-specification", to: "start#download_22_23_lettings_bulk_upload_specification" + get "/download-23-24-sales-form", to: "start#download_23_24_sales_form" get "/download-22-23-sales-form", to: "start#download_22_23_sales_form" - get "/download-23-24-lettings-form", to: "start#download_23_24_lettings_form" resource :account, only: %i[show edit], controller: "users" do get "edit/password", to: "users#edit_password" @@ -119,7 +127,16 @@ Rails.application.routes.draw do get "managing-agents/remove", to: "organisation_relationships#remove_managing_agent" post "managing-agents", to: "organisation_relationships#create_managing_agent" delete "managing-agents", to: "organisation_relationships#delete_managing_agent" - get "merge", to: "organisations#merge" + get "merge-request", to: "organisations#merge_request" + end + end + + resources :merge_requests, path: "/merge-request" do + member do + get "organisations" + patch "organisations", to: "merge_requests#update_organisations" + get "organisations/remove", to: "merge_requests#remove_merging_organisation" + get "absorbing-organisation" end end diff --git a/db/migrate/20230412111338_add_merge_requests_table.rb b/db/migrate/20230412111338_add_merge_requests_table.rb new file mode 100644 index 000000000..2aaeb2989 --- /dev/null +++ b/db/migrate/20230412111338_add_merge_requests_table.rb @@ -0,0 +1,9 @@ +class AddMergeRequestsTable < ActiveRecord::Migration[7.0] + def change + create_table :merge_requests do |t| + t.integer :requesting_organisation_id + t.text :other_merging_organisations + t.timestamps + end + end +end diff --git a/db/migrate/20230413135407_add_merge_organisations.rb b/db/migrate/20230413135407_add_merge_organisations.rb new file mode 100644 index 000000000..97178f973 --- /dev/null +++ b/db/migrate/20230413135407_add_merge_organisations.rb @@ -0,0 +1,9 @@ +class AddMergeOrganisations < ActiveRecord::Migration[7.0] + def change + create_table :merge_request_organisations do |t| + t.integer :merge_request_id, foreign_key: true + t.integer :merging_organisation_id, foreign_key: true + t.timestamps + end + end +end diff --git a/db/migrate/20230418095819_add_status_to_merge_request.rb b/db/migrate/20230418095819_add_status_to_merge_request.rb new file mode 100644 index 000000000..a6a3b01b9 --- /dev/null +++ b/db/migrate/20230418095819_add_status_to_merge_request.rb @@ -0,0 +1,5 @@ +class AddStatusToMergeRequest < ActiveRecord::Migration[7.0] + def change + add_column :merge_requests, :status, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index 9040aba7c..e87364403 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_04_12_143245) do +ActiveRecord::Schema[7.0].define(version: 2023_04_18_095819) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -354,6 +354,21 @@ ActiveRecord::Schema[7.0].define(version: 2023_04_12_143245) do t.string "collection" end + create_table "merge_request_organisations", force: :cascade do |t| + t.integer "merge_request_id" + t.integer "merging_organisation_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "merge_requests", force: :cascade do |t| + t.integer "requesting_organisation_id" + t.text "other_merging_organisations" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "status" + end + create_table "organisation_relationships", force: :cascade do |t| t.integer "child_organisation_id" t.integer "parent_organisation_id" diff --git a/public/files/bulk-upload-lettings-legacy-template-2023-24.xlsx b/public/files/bulk-upload-lettings-legacy-template-2023-24.xlsx new file mode 100644 index 000000000..22b630cd3 Binary files /dev/null and b/public/files/bulk-upload-lettings-legacy-template-2023-24.xlsx differ diff --git a/spec/factories/lettings_log.rb b/spec/factories/lettings_log.rb index 311e5fecc..58c81e8f3 100644 --- a/spec/factories/lettings_log.rb +++ b/spec/factories/lettings_log.rb @@ -158,6 +158,12 @@ FactoryBot.define do sheltered { 0 } household_charge { 0 } end + trait :sheltered_housing do + needstype { 2 } + end + trait :startdate_today do + startdate { Time.zone.today } + end created_at { Time.zone.today } updated_at { Time.zone.today } end diff --git a/spec/factories/sales_log.rb b/spec/factories/sales_log.rb index 4bea77a65..c61b0cd93 100644 --- a/spec/factories/sales_log.rb +++ b/spec/factories/sales_log.rb @@ -10,6 +10,30 @@ FactoryBot.define do type { 8 } saledate { Time.utc(2023, 2, 2, 10, 36, 49) } end + trait :shared_ownership do + ownershipsch { 1 } + type { 30 } + end + trait :privacy_notice_seen do + privacynotice { 1 } + end + trait :saledate_today do + saledate { Time.zone.today } + end + trait :shared_ownership_setup_complete do + saledate_today + ownershipsch { 1 } + type { 30 } + jointpur { 2 } + end + trait :outright_sale_setup_complete do + saledate_today + ownershipsch { 3 } + type { 10 } + companybuy { 2 } + buylivein { 1 } + jointpur { 2 } + end trait :completed do ownershipsch { 2 } type { 8 } diff --git a/spec/fixtures/files/lettings_logs_download.csv b/spec/fixtures/files/lettings_logs_download.csv index b9aefc3c9..075792363 100644 --- a/spec/fixtures/files/lettings_logs_download.csv +++ b/spec/fixtures/files/lettings_logs_download.csv @@ -1,2 +1,2 @@ -id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,needstype,renewal,startdate,rent_type_detail,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,hhmemb,relat2,age2,sex2,retirement_value_check,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,is_previous_la_inferred,prevloc_label,prevloc,illness_type_1,illness_type_2,is_la_inferred,la_label,la,postcode_known,postcode_full,previous_la_known,wchair,preg_occ,cbl,earnings,incfreq,net_income_value_check,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,renttype,lettype,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,tshortfall,chcharge,ppcodenk,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,has_benefits,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,rent_value_check,old_form_id,lar,irproduct,old_id,joint,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,unresolved,updated_by_id,uprn,uprn_known,uprn_confirmed,address_line1,address_line2,town_or_city,county,carehome_charges_value_check,status_cache,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate -{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,No,DLUHC,DLUHC,2021,Supported housing,,2 October 2021,London Affordable Rent,,,,,,,,,,,,,,,,,,,,No,,,,,No,Westminster,E09000033,,SE1 1TE,,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,8,0,0,0,,0,,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,0,,,,,,,,,,,,,,,,,,,9,1,,,,,,,,,,,,,,,,not_started,6,{scheme_code},{scheme_service_name},{scheme_sensitive},Missing,No,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,Bungalow,Fitted with equipment and adaptations,Westminster,{location_startdate} +id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,rent_value_check,needstype,renewal,startdate,rent_type_detail,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,hhmemb,relat2,age2,sex2,retirement_value_check,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,is_previous_la_inferred,prevloc_label,prevloc,illness_type_1,illness_type_2,is_la_inferred,la_label,la,postcode_known,postcode_full,previous_la_known,wchair,preg_occ,cbl,earnings,incfreq,net_income_value_check,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,renttype,lettype,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,tshortfall,chcharge,ppcodenk,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,has_benefits,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,old_form_id,lar,irproduct,old_id,joint,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,unresolved,updated_by_id,uprn,uprn_known,uprn_confirmed,address_line1,address_line2,town_or_city,county,carehome_charges_value_check,status_cache,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate +{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,No,DLUHC,DLUHC,2021,,Supported housing,,2 October 2021,London Affordable Rent,,,,,,,,,,,,,,,,,,,,No,,,,,No,Westminster,E09000033,,SE1 1TE,,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,8,0,0,0,,0,,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,0,,,,,,,,,,,,,,,,,,9,1,,,,,,,,,,,,,,,,not_started,6,{scheme_code},{scheme_service_name},{scheme_sensitive},Missing,No,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,Bungalow,Fitted with equipment and adaptations,Westminster,{location_startdate} diff --git a/spec/fixtures/files/lettings_logs_download_codes_only.csv b/spec/fixtures/files/lettings_logs_download_codes_only.csv index dec2664b5..76dde6a7b 100644 --- a/spec/fixtures/files/lettings_logs_download_codes_only.csv +++ b/spec/fixtures/files/lettings_logs_download_codes_only.csv @@ -1,2 +1,2 @@ -id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,needstype,renewal,startdate,rent_type_detail,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,hhmemb,relat2,age2,sex2,retirement_value_check,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,is_previous_la_inferred,prevloc_label,prevloc,illness_type_1,illness_type_2,is_la_inferred,la_label,la,postcode_known,postcode_full,previous_la_known,wchair,preg_occ,cbl,earnings,incfreq,net_income_value_check,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,renttype,lettype,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,tshortfall,chcharge,ppcodenk,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,has_benefits,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,rent_value_check,old_form_id,lar,irproduct,old_id,joint,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,unresolved,updated_by_id,uprn,uprn_known,uprn_confirmed,address_line1,address_line2,town_or_city,county,carehome_charges_value_check,status_cache,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate -{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,false,DLUHC,DLUHC,2021,2,,2 October 2021,2,,,,,,,,,,,,,,,,,,,,false,,,,,false,Westminster,E09000033,,SE1 1TE,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,8,0,0,0,,0,,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,0,,,,,,,,,,,,,,,,,,,9,1,,,,,,,,,,,,,,,,not_started,6,{scheme_code},{scheme_service_name},{scheme_sensitive},0,1,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,6,A,Westminster,{location_startdate} +id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,rent_value_check,needstype,renewal,startdate,rent_type_detail,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,hhmemb,relat2,age2,sex2,retirement_value_check,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,is_previous_la_inferred,prevloc_label,prevloc,illness_type_1,illness_type_2,is_la_inferred,la_label,la,postcode_known,postcode_full,previous_la_known,wchair,preg_occ,cbl,earnings,incfreq,net_income_value_check,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,renttype,lettype,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,tshortfall,chcharge,ppcodenk,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,has_benefits,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,old_form_id,lar,irproduct,old_id,joint,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,unresolved,updated_by_id,uprn,uprn_known,uprn_confirmed,address_line1,address_line2,town_or_city,county,carehome_charges_value_check,status_cache,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate +{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,false,DLUHC,DLUHC,2021,,2,,2 October 2021,2,,,,,,,,,,,,,,,,,,,,false,,,,,false,Westminster,E09000033,,SE1 1TE,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,8,0,0,0,,0,,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,0,,,,,,,,,,,,,,,,,,9,1,,,,,,,,,,,,,,,,not_started,6,{scheme_code},{scheme_service_name},{scheme_sensitive},0,1,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,6,A,Westminster,{location_startdate} diff --git a/spec/fixtures/forms/2021_2022.json b/spec/fixtures/forms/2021_2022.json index d25b575cc..139ac8399 100644 --- a/spec/fixtures/forms/2021_2022.json +++ b/spec/fixtures/forms/2021_2022.json @@ -411,6 +411,7 @@ "hint_text": "Type ahead to filter the options", "type": "select", "check_answer_label": "Accessible Select", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "answer_options": { "": "Select an option", "E07000223": "Adur", @@ -473,6 +474,7 @@ "hint_text": "Type ahead to filter the options", "type": "select", "check_answer_label": "Accessible Select", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "answer_options": { "": "Select an option", "E07000223": "Adur", @@ -500,6 +502,7 @@ "header": "Do you know the property’s postcode?", "hint_text": "", "type": "radio", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "answer_options": { "1": { "value": "Yes" @@ -521,6 +524,7 @@ "hint_text": "", "type": "text", "width": 5, + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "inferred_answers": { "la": { "is_la_inferred": true @@ -544,6 +548,7 @@ "header": "Do you know what local authority the property is located in?", "hint_text": "", "type": "radio", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "answer_options": { "0": { "value": "No" @@ -1153,6 +1158,7 @@ "header": "Postcode for the previous accommodation", "hint_text": "If the household has moved from settled accommodation immediately prior to being re-housed", "type": "text", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "width": 5 } } diff --git a/spec/fixtures/imports/logs/00d2343e-d5fa-4c89-8400-ec3854b0f2b4.xml b/spec/fixtures/imports/logs/00d2343e-d5fa-4c89-8400-ec3854b0f2b4.xml index 8bc6a935c..cd0e03d74 100644 --- a/spec/fixtures/imports/logs/00d2343e-d5fa-4c89-8400-ec3854b0f2b4.xml +++ b/spec/fixtures/imports/logs/00d2343e-d5fa-4c89-8400-ec3854b0f2b4.xml @@ -131,6 +131,11 @@ <_2cYears/> + 12345678 + + + + SR8 3HF Durham E06000047 diff --git a/spec/fixtures/imports/sales_logs/outright_sale_sales_log.xml b/spec/fixtures/imports/sales_logs/outright_sale_sales_log.xml index 4bbdc9981..ce17ff3fc 100644 --- a/spec/fixtures/imports/sales_logs/outright_sale_sales_log.xml +++ b/spec/fixtures/imports/sales_logs/outright_sale_sales_log.xml @@ -32,6 +32,11 @@ 1 1 Flat or maisonette 1 Purpose built + + + + + SW1A 1AA Westminster E09000033 @@ -275,6 +280,7 @@ 300000 + 1 diff --git a/spec/fixtures/imports/sales_logs/shared_ownership_sales_log.xml b/spec/fixtures/imports/sales_logs/shared_ownership_sales_log.xml index 6e0c11174..8df5cc9a1 100644 --- a/spec/fixtures/imports/sales_logs/shared_ownership_sales_log.xml +++ b/spec/fixtures/imports/sales_logs/shared_ownership_sales_log.xml @@ -24,6 +24,7 @@ 2 No 1 Yes + 2 No 2 Yes @@ -32,6 +33,12 @@ 2 1 Flat or maisonette 1 Purpose built + + + + + + SW1A 1AA Westminster E09000033 @@ -47,6 +54,8 @@ + + diff --git a/spec/helpers/tasklist_helper_spec.rb b/spec/helpers/tasklist_helper_spec.rb index 3d4874632..ac6075fd2 100644 --- a/spec/helpers/tasklist_helper_spec.rb +++ b/spec/helpers/tasklist_helper_spec.rb @@ -131,10 +131,19 @@ RSpec.describe TasklistHelper do it "returns relevant text" do Timecop.freeze(now + 1.year) do - expect(review_log_text(sales_log)).to eq("This log is from the 2021/2022 collection window, which is now closed.") + expect(review_log_text(sales_log)).to eq("This log is from the 2022/2023 collection window, which is now closed.") end end end + + context "when older_than_previous_collection_year" do + let(:now) { Time.utc(2023, 6, 1) } + let(:sales_log) { build(:sales_log, :completed, saledate: Time.utc(2022, 2, 1)) } + + it "returns relevant text" do + expect(review_log_text(sales_log)).to eq("This log is from the 2021/2022 collection window, which is now closed.") + end + end end context "with lettings log" do @@ -168,10 +177,19 @@ RSpec.describe TasklistHelper do it "returns relevant text" do Timecop.freeze(now + 1.year) do - expect(review_log_text(sales_log)).to eq("This log is from the 2021/2022 collection window, which is now closed.") + expect(review_log_text(sales_log)).to eq("This log is from the 2022/2023 collection window, which is now closed.") end end end + + context "when older_than_previous_collection_year" do + let(:now) { Time.utc(2023, 6, 1) } + let(:lettings_log) { build(:lettings_log, :completed, startdate: Time.utc(2022, 2, 1)) } + + it "returns relevant text" do + expect(review_log_text(lettings_log)).to eq("This log is from the 2021/2022 collection window, which is now closed.") + end + end end end end diff --git a/spec/models/form/lettings/pages/max_rent_value_check_spec.rb b/spec/models/form/lettings/pages/max_rent_value_check_spec.rb index 7a3a5544f..1d712d886 100644 --- a/spec/models/form/lettings/pages/max_rent_value_check_spec.rb +++ b/spec/models/form/lettings/pages/max_rent_value_check_spec.rb @@ -3,7 +3,7 @@ require "rails_helper" RSpec.describe Form::Lettings::Pages::MaxRentValueCheck, type: :model do subject(:page) { described_class.new(page_id, page_definition, subsection) } - let(:page_id) { "shared_ownership_deposit_value_check" } + let(:page_id) { "max_rent_value_check" } let(:page_definition) { nil } let(:subsection) { instance_double(Form::Subsection) } diff --git a/spec/models/form/lettings/pages/min_rent_value_check_spec.rb b/spec/models/form/lettings/pages/min_rent_value_check_spec.rb index 4e68a09c7..818599b06 100644 --- a/spec/models/form/lettings/pages/min_rent_value_check_spec.rb +++ b/spec/models/form/lettings/pages/min_rent_value_check_spec.rb @@ -1,8 +1,9 @@ require "rails_helper" RSpec.describe Form::Lettings::Pages::MinRentValueCheck, type: :model do - subject(:page) { described_class.new(nil, page_definition, subsection) } + subject(:page) { described_class.new(page_id, page_definition, subsection) } + let(:page_id) { "min_rent_value_check" } let(:page_definition) { nil } let(:subsection) { instance_double(Form::Subsection) } @@ -41,8 +42,14 @@ RSpec.describe Form::Lettings::Pages::MinRentValueCheck, type: :model do it "has the correct informative_text" do expect(page.informative_text).to eq({ - "arguments" => [{ "arguments_for_key" => "soft_min_for_period", "i18n_template" => "soft_min_for_period", "key" => "field_formatted_as_currency" }], "translation" => "soft_validations.rent.min_hint_text", + "arguments" => [ + { + "key" => "field_formatted_as_currency", + "arguments_for_key" => "soft_min_for_period", + "i18n_template" => "soft_min_for_period", + }, + ], }) end end diff --git a/spec/models/form/lettings/questions/uprn_known_spec.rb b/spec/models/form/lettings/questions/uprn_known_spec.rb index a79185462..b2c7f9d01 100644 --- a/spec/models/form/lettings/questions/uprn_known_spec.rb +++ b/spec/models/form/lettings/questions/uprn_known_spec.rb @@ -54,6 +54,20 @@ RSpec.describe Form::Lettings::Questions::UprnKnown, type: :model do end it "has the correct hidden_in_check_answers" do - expect(question.hidden_in_check_answers).to eq(true) + expect(question.hidden_in_check_answers).to eq({ + "depends_on" => [ + { "uprn_known" => 0 }, + { "uprn_known" => 1 }, + ], + }) + end + + it "has the correct inferred_check_answers_value" do + expect(question.inferred_check_answers_value).to eq([ + { + "condition" => { "uprn_known" => 0 }, + "value" => "Not known", + }, + ]) end end diff --git a/spec/models/form/lettings/subsections/income_and_benefits_spec.rb b/spec/models/form/lettings/subsections/income_and_benefits_spec.rb index 8dd4f88a3..e914ccc6a 100644 --- a/spec/models/form/lettings/subsections/income_and_benefits_spec.rb +++ b/spec/models/form/lettings/subsections/income_and_benefits_spec.rb @@ -30,8 +30,8 @@ RSpec.describe Form::Lettings::Subsections::IncomeAndBenefits, type: :model do rent_bi_weekly rent_4_weekly rent_monthly - min_rent_value_check - max_rent_value_check + brent_min_rent_value_check + brent_max_rent_value_check outstanding outstanding_amount ], diff --git a/spec/models/form/lettings/subsections/property_information_spec.rb b/spec/models/form/lettings/subsections/property_information_spec.rb index 0e0bc7397..0eac3a27a 100644 --- a/spec/models/form/lettings/subsections/property_information_spec.rb +++ b/spec/models/form/lettings/subsections/property_information_spec.rb @@ -20,6 +20,8 @@ RSpec.describe Form::Lettings::Subsections::PropertyInformation, type: :model do %w[ property_postcode property_local_authority + local_authority_min_rent_value_check + local_authority_max_rent_value_check first_time_property_let_as_social_housing property_let_type property_vacancy_reason_not_first_let @@ -30,6 +32,8 @@ RSpec.describe Form::Lettings::Subsections::PropertyInformation, type: :model do property_building_type property_wheelchair_accessible property_number_of_bedrooms + beds_min_rent_value_check + beds_max_rent_value_check void_date void_date_value_check property_major_repairs @@ -49,6 +53,8 @@ RSpec.describe Form::Lettings::Subsections::PropertyInformation, type: :model do uprn_confirmation address property_local_authority + local_authority_min_rent_value_check + local_authority_max_rent_value_check first_time_property_let_as_social_housing property_let_type property_vacancy_reason_not_first_let @@ -59,6 +65,8 @@ RSpec.describe Form::Lettings::Subsections::PropertyInformation, type: :model do property_building_type property_wheelchair_accessible property_number_of_bedrooms + beds_min_rent_value_check + beds_max_rent_value_check void_date void_date_value_check property_major_repairs diff --git a/spec/models/form/lettings/subsections/setup_spec.rb b/spec/models/form/lettings/subsections/setup_spec.rb index d7da750a8..00f9b5904 100644 --- a/spec/models/form/lettings/subsections/setup_spec.rb +++ b/spec/models/form/lettings/subsections/setup_spec.rb @@ -15,14 +15,22 @@ RSpec.describe Form::Lettings::Subsections::Setup, type: :model do expect(setup.pages.map(&:id)).to eq( %w[ stock_owner + stock_owner_min_rent_value_check + stock_owner_max_rent_value_check managing_organisation created_by needs_type scheme location + needs_type_min_rent_value_check + needs_type_max_rent_value_check renewal tenancy_start_date + start_date_min_rent_value_check + start_date_max_rent_value_check rent_type + rent_type_min_rent_value_check + rent_type_max_rent_value_check tenant_code property_reference ], @@ -42,14 +50,22 @@ RSpec.describe Form::Lettings::Subsections::Setup, type: :model do expect(setup.pages.map(&:id)).to eq( %w[ stock_owner + stock_owner_min_rent_value_check + stock_owner_max_rent_value_check managing_organisation created_by needs_type scheme location + needs_type_min_rent_value_check + needs_type_max_rent_value_check renewal tenancy_start_date + start_date_min_rent_value_check + start_date_max_rent_value_check rent_type + rent_type_min_rent_value_check + rent_type_max_rent_value_check tenant_code property_reference ], diff --git a/spec/models/form/sales/pages/uprn_spec.rb b/spec/models/form/sales/pages/uprn_spec.rb index a0cd5b7bc..b015ce877 100644 --- a/spec/models/form/sales/pages/uprn_spec.rb +++ b/spec/models/form/sales/pages/uprn_spec.rb @@ -35,25 +35,6 @@ RSpec.describe Form::Sales::Pages::Uprn, type: :model do expect(page.skip_text).to eq("Enter address instead") end - describe "has correct routed_to?" do - context "when uprn_known != 1" do - let(:log) { create(:sales_log, uprn_known: 0) } - - it "returns false" do - expect(page.routed_to?(log)).to eq(false) - end - end - - context "when uprn_known == 1" do - let(:log) { create(:sales_log) } - - it "returns true" do - log.uprn_known = 1 - expect(page.routed_to?(log)).to eq(true) - end - end - end - describe "has correct skip_href" do context "when log is nil" do it "is nil" do diff --git a/spec/models/form/sales/questions/age2_spec.rb b/spec/models/form/sales/questions/age2_spec.rb index e98f41f93..fc5dfa77a 100644 --- a/spec/models/form/sales/questions/age2_spec.rb +++ b/spec/models/form/sales/questions/age2_spec.rb @@ -59,4 +59,8 @@ RSpec.describe Form::Sales::Questions::Age2, type: :model do it "has the correct max" do expect(question.max).to eq(110) end + + it "has the correct step" do + expect(question.step).to be 1 + end end diff --git a/spec/models/form/sales/questions/person_age_spec.rb b/spec/models/form/sales/questions/person_age_spec.rb index 9f475f6b6..1358ec4d2 100644 --- a/spec/models/form/sales/questions/person_age_spec.rb +++ b/spec/models/form/sales/questions/person_age_spec.rb @@ -40,6 +40,10 @@ RSpec.describe Form::Sales::Questions::PersonAge, type: :model do expect(question.width).to eq(3) end + it "has the correct step" do + expect(question.step).to be 1 + end + context "with person 2" do let(:person_index) { 2 } let(:question_id) { "age2" } diff --git a/spec/models/form/sales/questions/purchase_price_spec.rb b/spec/models/form/sales/questions/purchase_price_spec.rb index e9e7ee3c3..a6a4f6cbe 100644 --- a/spec/models/form/sales/questions/purchase_price_spec.rb +++ b/spec/models/form/sales/questions/purchase_price_spec.rb @@ -37,6 +37,36 @@ RSpec.describe Form::Sales::Questions::PurchasePrice, type: :model do ) end + it "has the correct question_number" do + expect(question.question_number).to be_nil + end + + context "when discounted ownership scheme" do + subject(:question) { described_class.new(question_id, question_definition, page, ownershipsch: 2) } + + it "has the correct hint" do + expect(question.hint_text).to eq( + "For all schemes, including Right to Acquire (RTA), Right to Buy (RTB), Voluntary Right to Buy (VRTB) or Preserved Right to Buy (PRTB) sales, enter the full price of the property without any discount", + ) + end + + it "has the correct question_number" do + expect(question.question_number).to eq(100) + end + end + + context "when outright sale" do + subject(:question) { described_class.new(question_id, question_definition, page, ownershipsch: 3) } + + it "has the correct hint" do + expect(question.hint_text).to be_nil + end + + it "has the correct question_number" do + expect(question.question_number).to eq(110) + end + end + it "has correct width" do expect(question.width).to eq(5) end diff --git a/spec/models/form/sales/questions/uprn_confirmation_spec.rb b/spec/models/form/sales/questions/uprn_confirmation_spec.rb index 27f8ec125..d4917ee67 100644 --- a/spec/models/form/sales/questions/uprn_confirmation_spec.rb +++ b/spec/models/form/sales/questions/uprn_confirmation_spec.rb @@ -50,7 +50,7 @@ RSpec.describe Form::Sales::Questions::UprnConfirmation, type: :model do context "when address is present" do it "returns formatted value" do - log = create(:sales_log, address_line1: "1, Test Street", town_or_city: "Test Town", county: "Test County", postcode_full: "AA1 1AA", uprn: "1234", uprn_known: 1) + log = build(:sales_log, :outright_sale_setup_complete, address_line1: "1, Test Street", town_or_city: "Test Town", county: "Test County", postcode_full: "AA1 1AA", uprn: "1234", uprn_known: 1) expect(question.notification_banner(log)).to eq( { diff --git a/spec/models/form/sales/questions/uprn_known_spec.rb b/spec/models/form/sales/questions/uprn_known_spec.rb index 4016b6e8d..faac81698 100644 --- a/spec/models/form/sales/questions/uprn_known_spec.rb +++ b/spec/models/form/sales/questions/uprn_known_spec.rb @@ -54,6 +54,20 @@ RSpec.describe Form::Sales::Questions::UprnKnown, type: :model do end it "has the correct hidden_in_check_answers" do - expect(question.hidden_in_check_answers).to eq(true) + expect(question.hidden_in_check_answers).to eq({ + "depends_on" => [ + { "uprn_known" => 0 }, + { "uprn_known" => 1 }, + ], + }) + end + + it "has the correct inferred_check_answers_value" do + expect(question.inferred_check_answers_value).to eq([ + { + "condition" => { "uprn_known" => 0 }, + "value" => "Not known", + }, + ]) end end diff --git a/spec/models/form/sales/questions/uprn_spec.rb b/spec/models/form/sales/questions/uprn_spec.rb index c99a08d28..3d9a22a14 100644 --- a/spec/models/form/sales/questions/uprn_spec.rb +++ b/spec/models/form/sales/questions/uprn_spec.rb @@ -56,6 +56,7 @@ RSpec.describe Form::Sales::Questions::Uprn, type: :model do let(:log) do create( :sales_log, + :completed, address_line1: "1, Test Street", town_or_city: "Test Town", county: "Test County", diff --git a/spec/models/form/sales/subsections/property_information_spec.rb b/spec/models/form/sales/subsections/property_information_spec.rb index 559db5ea6..2ad141502 100644 --- a/spec/models/form/sales/subsections/property_information_spec.rb +++ b/spec/models/form/sales/subsections/property_information_spec.rb @@ -41,16 +41,16 @@ RSpec.describe Form::Sales::Subsections::PropertyInformation, type: :model do it "has correct pages" do expect(property_information.pages.map(&:id)).to eq( %w[ - uprn - uprn_confirmation - address - property_local_authority property_number_of_bedrooms about_price_bedrooms_value_check property_unit_type monthly_charges_property_type_value_check percentage_discount_proptype_value_check property_building_type + uprn + uprn_confirmation + address + property_local_authority about_price_la_value_check property_wheelchair_accessible ], diff --git a/spec/models/form_handler_spec.rb b/spec/models/form_handler_spec.rb index 2c4c83022..bbeb56e55 100644 --- a/spec/models/form_handler_spec.rb +++ b/spec/models/form_handler_spec.rb @@ -17,13 +17,13 @@ RSpec.describe FormHandler do it "is able to load a current lettings form" do form = form_handler.get_form("current_lettings") expect(form).to be_a(Form) - expect(form.pages.count).to eq(46) + expect(form.pages.count).to be_positive end it "is able to load a next lettings form" do form = form_handler.get_form("next_lettings") expect(form).to be_a(Form) - expect(form.pages.count).to eq(13) + expect(form.pages.count).to be_positive end end @@ -48,14 +48,14 @@ RSpec.describe FormHandler do it "is able to load a current lettings form" do form = form_handler.get_form("current_lettings") expect(form).to be_a(Form) - expect(form.pages.count).to eq(13) + expect(form.pages.count).to be_positive expect(form.name).to eq("2022_2023_lettings") end it "is able to load a previous lettings form" do form = form_handler.get_form("previous_lettings") expect(form).to be_a(Form) - expect(form.pages.count).to eq(46) + expect(form.pages.count).to be_positive expect(form.name).to eq("2021_2022_lettings") end diff --git a/spec/models/form_spec.rb b/spec/models/form_spec.rb index f2a80efe3..5045d0280 100644 --- a/spec/models/form_spec.rb +++ b/spec/models/form_spec.rb @@ -204,32 +204,140 @@ RSpec.describe Form, type: :model do end end - describe "invalidated_page_questions" do - let(:lettings_log) { FactoryBot.create(:lettings_log, :in_progress, needstype: 1) } - let(:expected_invalid) { %w[scheme_id retirement_value_check condition_effects cbl conditional_question_no_second_question net_income_value_check dependent_question offered layear declaration] } + describe "#reset_not_routed_questions_and_invalid_answers" do + around do |example| + Singleton.__init__(FormHandler) + Timecop.freeze(now) do + FormHandler.instance.use_real_forms! + example.run + end + FormHandler.instance.use_fake_forms! + end + + let(:now) { Time.zone.local(2023, 5, 5) } + + context "when there are multiple radio questions for attribute X" do + context "and attribute Y is changed such that a different question for X is routed to" do + let(:log) { FactoryBot.create(:lettings_log, :setup_completed, :sheltered_housing, startdate: now, renewal: 0, prevten:) } + + context "and the value of X remains valid" do + let(:prevten) { 36 } - context "when dependencies are not met" do - it "returns an array of question keys whose pages conditions are not met" do - expect(form.invalidated_page_questions(lettings_log).map(&:id).uniq).to eq(expected_invalid) + it "the value of this attribute is not cleared" do + log.renewal = 1 + log.form.reset_not_routed_questions_and_invalid_answers(log) + expect(log.prevten).to be 36 + end + end + + context "and the value of X is now invalid" do + let(:prevten) { 30 } + + it "the value of this attribute is cleared" do + log.renewal = 1 + log.form.reset_not_routed_questions_and_invalid_answers(log) + expect(log.prevten).to be nil + end + end end end - context "with two pages having the same question and only one has dependencies met" do - it "returns an array of question keys whose pages conditions are not met" do - lettings_log["preg_occ"] = "No" - expect(form.invalidated_page_questions(lettings_log).map(&:id).uniq).to eq(expected_invalid) + context "when there is one radio question for attribute X" do + context "and the start date or sale date is changed such that the collection year changes and there are different options" do + let(:log) { FactoryBot.create(:lettings_log, :setup_completed, :sheltered_housing, startdate: now, sheltered:) } + + context "and the value of X remains valid" do + let(:sheltered) { 2 } + + it "the value of this attribute is not cleared" do + log.update!(startdate: Time.zone.local(2023, 1, 1)) + expect(log.sheltered).to be 2 + end + end + + context "and the value of X is now invalid" do + let(:sheltered) { 5 } + + it "the value of this attribute is cleared" do + log.update!(startdate: Time.zone.local(2023, 1, 1)) + expect(log.sheltered).to be nil + end + end end end - context "when a question is marked as `derived` and `depends_on: false`" do - let(:lettings_log) { FactoryBot.build(:lettings_log, :in_progress, startdate: Time.utc(2022, 4, 2, 10, 36, 49)) } + context "when there is one free user input question for an attribute X" do + let(:log) { FactoryBot.create(:sales_log, :shared_ownership_setup_complete, staircase: 1, stairbought: 25) } - it "does not count it's questions as invalidated" do - expect(form.enabled_page_questions(lettings_log).map(&:id).uniq).to include("tshortfall_known") + context "and attribute Y is changed such that it is no longer routed to" do + it "the value of this attribute is cleared" do + expect(log.stairbought).to be 25 + log.staircase = 2 + log.form.reset_not_routed_questions_and_invalid_answers(log) + expect(log.stairbought).to be nil + end end + end + + context "when there are multiple free user input questions for attribute X" do + context "and attribute Y is changed such that a different question for X is routed to" do + let(:log) { FactoryBot.create(:sales_log, :saledate_today, :shared_ownership, :privacy_notice_seen, jointpur: 1, jointmore: 2, hholdcount: expected_hholdcount) } + let(:expected_hholdcount) { 2 } + + it "the value of this attribute is not cleared" do + log.jointpur = 2 + log.form.reset_not_routed_questions_and_invalid_answers(log) + expect(log.hholdcount).to eq expected_hholdcount + end + end + + context "and attribute Y is changed such that no questions for X are routed to" do + let(:log) { FactoryBot.create(:sales_log, :shared_ownership_setup_complete, value: initial_value) } + let(:initial_value) { 200_000.to_d } + + it "the value of this attribute is cleared" do + expect(log.value).to eq initial_value + log.ownershipsch = 2 + log.form.reset_not_routed_questions_and_invalid_answers(log) + expect(log.value).to be nil + end + end + end + + context "when a value is changed such that a checkbox question is no longer routed to" do + let(:log) { FactoryBot.create(:lettings_log, :setup_completed, startdate: now, reasonpref: 1, rp_homeless: 1, rp_medwel: 1, rp_hardship: 1) } + + it "all attributes relating to that checkbox question are cleared" do + expect(log.rp_homeless).to be 1 + log.reasonpref = 2 + log.form.reset_not_routed_questions_and_invalid_answers(log) + expect(log.rp_homeless).to be nil + expect(log.rp_medwel).to be nil + expect(log.rp_hardship).to be nil + end + end + + context "when an attribute is derived, but no questions for that attribute are routed to" do + let(:log) { FactoryBot.create(:sales_log, :outright_sale_setup_complete, value: 200_000) } + + it "the value of this attribute is not cleared" do + expect(log.deposit).to be nil + log.update!(mortgageused: 2) + expect(log.form.questions.any? { |q| q.id == "deposit" && q.page.routed_to?(log, nil) }).to be false + expect(log.deposit).not_to be nil + end + end + + context "when an attribute is related to a callback question with no set answer options, and no questions for that attribute are routed to" do + let(:location) { FactoryBot.create(:location) } + let(:log) { FactoryBot.create(:lettings_log, :startdate_today) } - it "does not route to the page" do - expect(form.invalidated_pages(lettings_log).map(&:id)).to include("outstanding_amount_known") + # Pages::PropertyPostcode and questions inside have been removed from form. do we not want to delete and migration delete_column? + it "the value of this attribute is not cleared" do + expect(log.form.questions.find { |q| q.id == "location_id" }.answer_options.keys).to be_empty + log.location_id = location.id + log.form.reset_not_routed_questions_and_invalid_answers(log) + expect(log.location_id).not_to be nil end end end diff --git a/spec/models/lettings_log_spec.rb b/spec/models/lettings_log_spec.rb index 481f7fbbe..05da0a5a8 100644 --- a/spec/models/lettings_log_spec.rb +++ b/spec/models/lettings_log_spec.rb @@ -2310,6 +2310,7 @@ RSpec.describe LettingsLog do it "returns optional fields" do expect(lettings_log.optional_fields).to eq(%w[ + rent_value_check first_time_property_let_as_social_housing tenancycode propcode @@ -2324,6 +2325,7 @@ RSpec.describe LettingsLog do it "returns optional fields" do expect(lettings_log.optional_fields).to eq(%w[ + rent_value_check first_time_property_let_as_social_housing tenancycode propcode diff --git a/spec/models/sales_log_spec.rb b/spec/models/sales_log_spec.rb index 1fc2cf73e..310fb48d1 100644 --- a/spec/models/sales_log_spec.rb +++ b/spec/models/sales_log_spec.rb @@ -181,7 +181,7 @@ RSpec.describe SalesLog, type: :model do let(:sales_log) { create(:sales_log, :completed) } it "correctly derives and saves exday, exmonth and exyear" do - sales_log.update!(exdate: Time.gm(2022, 5, 4), saledate: Time.gm(2022, 7, 4), ownershipsch: 1, staircase: 2, resale: 2) + sales_log.update!(exdate: Time.gm(2022, 5, 4), saledate: Time.gm(2022, 7, 4), ownershipsch: 1, type: 18, staircase: 2, resale: 2, proplen: 0) record_from_db = ActiveRecord::Base.connection.execute("select exday, exmonth, exyear from sales_logs where id=#{sales_log.id}").to_a[0] expect(record_from_db["exday"]).to eq(4) expect(record_from_db["exmonth"]).to eq(5) @@ -571,8 +571,8 @@ RSpec.describe SalesLog, type: :model do end end - context "when service errors" do - let(:sales_log) { create(:sales_log, uprn_known: 1, uprn: "123456789", uprn_confirmed: 1) } + context "when the API returns an error" do + let(:sales_log) { build(:sales_log, :outright_sale_setup_complete, uprn_known: 1, uprn: "123456789", uprn_confirmed: 1) } let(:error_message) { "error" } it "adds error to sales log" do diff --git a/spec/requests/merge_requests_controller_spec.rb b/spec/requests/merge_requests_controller_spec.rb new file mode 100644 index 000000000..ac7094df6 --- /dev/null +++ b/spec/requests/merge_requests_controller_spec.rb @@ -0,0 +1,278 @@ +require "rails_helper" + +RSpec.describe MergeRequestsController, type: :request do + let(:organisation) { user.organisation } + let(:other_organisation) { FactoryBot.create(:organisation, name: "Other Test Org") } + let(:headers) { { "Accept" => "text/html" } } + let(:page) { Capybara::Node::Simple.new(response.body) } + let(:user) { FactoryBot.create(:user, :data_coordinator) } + let(:support_user) { FactoryBot.create(:user, :support, organisation:) } + let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation) } + let(:other_merge_request) { MergeRequest.create!(requesting_organisation: other_organisation) } + + context "when user is signed in with a data coordinator user" do + before do + sign_in user + end + + describe "#organisations" do + let(:params) { { merge_request: { requesting_organisation_id: "9", status: "unsubmitted" } } } + + context "when creating a new merge request" do + before do + organisation.update!(name: "Test Org") + post "/merge-request", headers:, params: + end + + it "creates merge request with requesting organisation" do + follow_redirect! + expect(page).to have_content("Which organisations are merging?") + expect(page).to have_content("Test Org") + expect(page).not_to have_link("Remove") + end + + context "when passing a different requesting organisation id" do + let(:params) { { merge_request: { requesting_organisation_id: other_organisation.id, status: "unsubmitted" } } } + + it "creates merge request with current user organisation" do + follow_redirect! + expect(MergeRequest.count).to eq(1) + expect(MergeRequest.first.requesting_organisation_id).to eq(organisation.id) + expect(MergeRequest.first.merging_organisations.count).to eq(1) + expect(MergeRequest.first.merging_organisations.first.id).to eq(organisation.id) + end + end + end + + context "when viewing existing merge request" do + before do + organisation.update!(name: "Test Org") + get "/merge-request/#{merge_request.id}/organisations", headers:, params: + end + + it "shows merge request with requesting organisation" do + expect(page).to have_content("Which organisations are merging?") + expect(page).to have_content("Test Org") + end + end + + context "when viewing existing merge request of a different (unauthorised) organisation" do + before do + get "/merge-request/#{other_merge_request.id}/organisations", headers:, params: + end + + it "shows merge request with requesting organisation" do + expect(response).to have_http_status(:not_found) + end + end + end + + describe "#update_organisations" do + let(:params) { { merge_request: { merging_organisation: other_organisation.id } } } + + context "when updating a merge request with a new organisation" do + before do + patch "/merge-request/#{merge_request.id}/organisations", headers:, params: + end + + it "updates the merge request" do + merge_request.reload + expect(merge_request.merging_organisations.count).to eq(1) + expect(page).to have_content("Test Org") + expect(page).to have_content("Other Test Org") + expect(page).to have_link("Remove") + end + end + + context "when the user selects an organisation that requested another merge" do + let(:params) { { merge_request: { merging_organisation: other_organisation.id } } } + + before do + MergeRequest.create!(requesting_organisation_id: other_organisation.id, status: "submitted") + patch "/merge-request/#{merge_request.id}/organisations", headers:, params: + end + + it "does not update the merge request" do + merge_request.reload + expect(merge_request.merging_organisations.count).to eq(0) + expect(response).to have_http_status(:unprocessable_entity) + expect(page).to have_content(I18n.t("validations.merge_request.organisation_part_of_another_merge")) + end + end + + context "when the user selects an organisation that has another non submitted merge" do + let(:params) { { merge_request: { merging_organisation: other_organisation.id } } } + + before do + MergeRequest.create!(requesting_organisation_id: other_organisation.id, status: "unsubmitted") + patch "/merge-request/#{merge_request.id}/organisations", headers:, params: + end + + it "updates the merge request" do + merge_request.reload + expect(merge_request.merging_organisations.count).to eq(1) + expect(page).not_to have_content(I18n.t("validations.merge_request.organisation_part_of_another_merge")) + end + end + + context "when the user selects an organisation that is a part of another merge" do + let(:another_organisation) { FactoryBot.create(:organisation, name: "Other Test Org") } + let(:params) { { merge_request: { merging_organisation: another_organisation.id } } } + + before do + existing_merge_request = MergeRequest.create!(requesting_organisation_id: other_organisation.id, status: "submitted") + MergeRequestOrganisation.create!(merge_request_id: existing_merge_request.id, merging_organisation_id: another_organisation.id) + patch "/merge-request/#{merge_request.id}/organisations", headers:, params: + end + + it "does not update the merge request" do + merge_request.reload + expect(merge_request.merging_organisations.count).to eq(0) + expect(response).to have_http_status(:unprocessable_entity) + expect(page).to have_content(I18n.t("validations.merge_request.organisation_part_of_another_merge")) + end + end + + context "when the user selects an organisation that is a part of another unsubmitted merge" do + let(:another_organisation) { FactoryBot.create(:organisation, name: "Other Test Org") } + let(:params) { { merge_request: { merging_organisation: another_organisation.id } } } + + before do + existing_merge_request = MergeRequest.create!(requesting_organisation_id: other_organisation.id, status: "unsubmitted") + MergeRequestOrganisation.create!(merge_request_id: existing_merge_request.id, merging_organisation_id: another_organisation.id) + patch "/merge-request/#{merge_request.id}/organisations", headers:, params: + end + + it "does not update the merge request" do + merge_request.reload + expect(merge_request.merging_organisations.count).to eq(1) + expect(page).not_to have_content(I18n.t("validations.merge_request.organisation_part_of_another_merge")) + end + end + + context "when the user selects an organisation that is a part of current merge" do + let(:another_organisation) { FactoryBot.create(:organisation, name: "Other Test Org") } + let(:params) { { merge_request: { merging_organisation: another_organisation.id } } } + + before do + merge_request.merging_organisations << another_organisation + patch "/merge-request/#{merge_request.id}/organisations", headers:, params: + end + + it "does not update the merge request" do + merge_request.reload + expect(merge_request.merging_organisations.count).to eq(1) + end + end + + context "when the user selects an organisation that is requesting this merge" do + let(:params) { { merge_request: { merging_organisation: merge_request.requesting_organisation_id } } } + + before do + patch "/merge-request/#{merge_request.id}/organisations", headers:, params: + end + + it "does not update the merge request" do + merge_request.reload + expect(page).not_to have_content(I18n.t("validations.merge_request.organisation_part_of_another_merge")) + expect(merge_request.merging_organisations.count).to eq(1) + end + end + + context "when the user does not select an organisation" do + let(:params) { { merge_request: { merging_organisation: nil } } } + + before do + patch "/merge-request/#{merge_request.id}/organisations", headers:, params: + end + + it "does not update the merge request" do + merge_request.reload + expect(merge_request.merging_organisations.count).to eq(0) + expect(response).to have_http_status(:unprocessable_entity) + expect(page).to have_content(I18n.t("validations.merge_request.organisation_not_selected")) + end + end + + context "when the user selects non existent id" do + let(:params) { { merge_request: { merging_organisation: "clearly_not_an_id" } } } + + before do + patch "/merge-request/#{merge_request.id}/organisations", headers:, params: + end + + it "does not update the merge request" do + merge_request.reload + expect(merge_request.merging_organisations.count).to eq(0) + expect(response).to have_http_status(:unprocessable_entity) + expect(page).to have_content(I18n.t("validations.merge_request.organisation_not_selected")) + end + end + end + + describe "#remove_organisation" do + let(:params) { { merge_request: { merging_organisation: other_organisation.id } } } + + context "when removing an organisation from merge request" do + before do + MergeRequestOrganisation.create!(merge_request_id: merge_request.id, merging_organisation_id: other_organisation.id) + get "/merge-request/#{merge_request.id}/organisations/remove", headers:, params: + end + + it "updates the merge request" do + expect(merge_request.merging_organisations.count).to eq(0) + expect(page).not_to have_link("Remove") + end + end + + context "when removing an organisation that is not part of a merge from merge request" do + before do + get "/merge-request/#{merge_request.id}/organisations/remove", headers:, params: + end + + it "does not throw an error" do + expect(merge_request.merging_organisations.count).to eq(0) + expect(page).not_to have_link("Remove") + end + end + end + + describe "#other_merging_organisations" do + let(:params) { { merge_request: { other_merging_organisations: "A list of other merging organisations" } } } + + context "when adding other merging organisations" do + before do + MergeRequestOrganisation.create!(merge_request_id: merge_request.id, merging_organisation_id: other_organisation.id) + patch "/merge-request/#{merge_request.id}", headers:, params: + end + + it "updates the merge request" do + merge_request.reload + expect(merge_request.other_merging_organisations).to eq("A list of other merging organisations") + end + end + end + end + + context "when user is signed in as a support user" do + before do + allow(support_user).to receive(:need_two_factor_authentication?).and_return(false) + sign_in support_user + end + + describe "#organisations" do + let(:params) { { merge_request: { requesting_organisation_id: other_organisation.id, status: "unsubmitted" } } } + + before do + organisation.update!(name: "Test Org") + post "/merge-request", headers:, params: + end + + it "creates merge request with requesting organisation" do + follow_redirect! + expect(MergeRequest.count).to eq(1) + expect(MergeRequest.first.requesting_organisation_id).to eq(other_organisation.id) + end + end + end +end diff --git a/spec/requests/organisations_controller_spec.rb b/spec/requests/organisations_controller_spec.rb index 459987fdc..d526f218f 100644 --- a/spec/requests/organisations_controller_spec.rb +++ b/spec/requests/organisations_controller_spec.rb @@ -230,7 +230,7 @@ RSpec.describe OrganisationsController, type: :request do it "displays a link to merge organisations" do expect(page).to have_content("Is your organisation merging with another?") - expect(page).to have_link("Let us know using this form", href: "/organisations/#{organisation.id}/merge") + expect(page).to have_link("Let us know using this form", href: "/organisations/#{organisation.id}/merge-request") end end @@ -444,7 +444,7 @@ RSpec.describe OrganisationsController, type: :request do describe "#merge" do context "with an organisation that the user belongs to" do before do - get "/organisations/#{organisation.id}/merge", headers:, params: {} + get "/organisations/#{organisation.id}/merge-request", headers:, params: {} end it "shows the correct content" do @@ -455,14 +455,14 @@ RSpec.describe OrganisationsController, type: :request do expect(page).to have_link("Back", href: "/organisations/#{organisation.id}") end - it "has a correct start no button" do - expect(page).to have_link("Start now", href: "#") + it "has a correct start now button" do + expect(page).to have_button("Start now") end end context "with organisation that are not in scope for the user, i.e. that they do not belong to" do before do - get "/organisations/#{unauthorised_organisation.id}/merge", headers:, params: {} + get "/organisations/#{unauthorised_organisation.id}/merge-request", headers:, params: {} end it "returns not found 404 from org details route" do diff --git a/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb b/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb index a39f0969c..7b4f30c85 100644 --- a/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb +++ b/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb @@ -507,6 +507,14 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do expect(parser.errors[:field_52]).to be_present end end + + context "when not a valid option" do + let(:attributes) { setup_section_params.merge({ bulk_upload:, field_52: "99" }) } + + it "has error for invalid option" do + expect(parser.errors[:field_52]).to include("Enter a valid value for What is the tenant's main reason for the household leaving their last settled home?") + end + end end end @@ -852,16 +860,6 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do end end - describe "#field_134" do - context "when an unpermitted value" do - let(:attributes) { { bulk_upload:, field_134: "3" } } - - it "has errors on the field" do - expect(parser.errors[:field_134]).to be_present - end - end - end - describe "#field_103" do context "when null" do let(:attributes) { setup_section_params.merge({ field_103: nil }) } @@ -874,14 +872,6 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do expect(parser.errors[:field_103]).to eql(["You must answer type of building"]) end end - - context "when unpermitted values" do - let(:attributes) { setup_section_params.merge({ field_103: "4" }) } - - it "returns an error" do - expect(parser.errors[:field_103]).to be_present - end - end end end @@ -1356,10 +1346,20 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do end describe "#mrcdate" do - let(:attributes) { { bulk_upload:, field_92: "13", field_93: "12", field_94: "22" } } + context "when valid" do + let(:attributes) { { bulk_upload:, field_92: "13", field_93: "12", field_94: "22" } } - it "sets value given" do - expect(parser.log.mrcdate).to eq(Date.new(2022, 12, 13)) + it "sets value given" do + expect(parser.log.mrcdate).to eq(Date.new(2022, 12, 13)) + end + end + + context "when invalid" do + let(:attributes) { { bulk_upload:, field_92: "13", field_93: "13", field_94: "22" } } + + it "does not raise an error" do + expect { parser.log.mrcdate }.not_to raise_error + end end end @@ -1382,10 +1382,20 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do end describe "#voiddate" do - let(:attributes) { { bulk_upload:, field_89: "13", field_90: "12", field_91: "22" } } + context "when valid" do + let(:attributes) { { bulk_upload:, field_89: "13", field_90: "12", field_91: "22" } } - it "sets value given" do - expect(parser.log.voiddate).to eq(Date.new(2022, 12, 13)) + it "sets value given" do + expect(parser.log.voiddate).to eq(Date.new(2022, 12, 13)) + end + end + + context "when invalid" do + let(:attributes) { { bulk_upload:, field_89: "13", field_90: "13", field_91: "22" } } + + it "does not raise an error" do + expect { parser.log.voiddate }.not_to raise_error + end end end diff --git a/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb b/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb index 37d8e24a5..a94eaac64 100644 --- a/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb +++ b/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb @@ -509,7 +509,7 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do let(:attributes) { setup_section_params.merge({ field_102: "7" }) } it "returns an error" do - expect(parser.errors[:field_102]).to be_present + expect(parser.errors[:field_102]).to include("Enter a valid value for What is the tenant’s main reason for the household leaving their last settled home?") end end end @@ -793,14 +793,6 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do end describe "#field_6" do # renewal - context "when an unpermitted value" do - let(:attributes) { { bulk_upload:, field_6: "3" } } - - it "has errors on the field" do - expect(parser.errors[:field_6]).to be_present - end - end - context "when blank" do let(:attributes) { { bulk_upload:, field_1: owning_org.old_visible_id, field_6: "" } } @@ -842,14 +834,6 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do expect(parser.errors[:field_30]).to eql(["You must answer type of building"]) end end - - context "when unpermitted values" do - let(:attributes) { setup_section_params.merge({ field_30: "4" }) } - - it "returns an error" do - expect(parser.errors[:field_30]).to be_present - end - end end describe "#field_52" do # age2 @@ -1372,10 +1356,20 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do end describe "#mrcdate" do - let(:attributes) { { bulk_upload:, field_36: "13", field_37: "12", field_38: "22" } } + context "when valid" do + let(:attributes) { { bulk_upload:, field_36: "13", field_37: "12", field_38: "22" } } - it "sets value given" do - expect(parser.log.mrcdate).to eq(Date.new(2022, 12, 13)) + it "sets value given" do + expect(parser.log.mrcdate).to eq(Date.new(2022, 12, 13)) + end + end + + context "when invalid" do + let(:attributes) { { bulk_upload:, field_36: "13", field_37: "13", field_38: "22" } } + + it "does not raise an error" do + expect { parser.log.mrcdate }.not_to raise_error + end end end @@ -1398,10 +1392,20 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do end describe "#voiddate" do - let(:attributes) { { bulk_upload:, field_33: "13", field_34: "12", field_35: "22" } } + context "when valid" do + let(:attributes) { { bulk_upload:, field_33: "13", field_34: "12", field_35: "22" } } - it "sets value given" do - expect(parser.log.voiddate).to eq(Date.new(2022, 12, 13)) + it "sets value given" do + expect(parser.log.voiddate).to eq(Date.new(2022, 12, 13)) + end + end + + context "when invalid" do + let(:attributes) { { bulk_upload:, field_33: "13", field_34: "13", field_35: "22" } } + + it "does not raise an error" do + expect { parser.log.voiddate }.not_to raise_error + end end end diff --git a/spec/services/csv/lettings_log_csv_service_spec.rb b/spec/services/csv/lettings_log_csv_service_spec.rb index 1a752035d..1ee3b9df1 100644 --- a/spec/services/csv/lettings_log_csv_service_spec.rb +++ b/spec/services/csv/lettings_log_csv_service_spec.rb @@ -20,6 +20,7 @@ RSpec.describe Csv::LettingsLogCsvService do owning_organisation_name managing_organisation_name collection_start_year + rent_value_check needstype renewal startdate @@ -161,7 +162,6 @@ RSpec.describe Csv::LettingsLogCsvService do pscharge supcharg tcharge - rent_value_check hbrentshortfall tshortfall_known tshortfall diff --git a/spec/services/imports/lettings_logs_import_service_spec.rb b/spec/services/imports/lettings_logs_import_service_spec.rb index f144a798d..ec41ba366 100644 --- a/spec/services/imports/lettings_logs_import_service_spec.rb +++ b/spec/services/imports/lettings_logs_import_service_spec.rb @@ -323,6 +323,34 @@ RSpec.describe Imports::LettingsLogsImportService do end end + context "and is a other tenancy but missing tenancyother" do + let(:lettings_log_id) { "0b4a68df-30cc-474a-93c0-a56ce8fdad3b" } + + before do + lettings_log_xml.at_xpath("//meta:status").content = "saved" + lettings_log_xml.at_xpath("//xmlns:Q2b").content = "3" + lettings_log_xml.at_xpath("//xmlns:Q2ba").content = "" + end + + it "intercepts the relevant validation error" do + allow(logger).to receive(:warn) + + expect { lettings_log_service.send(:create_log, lettings_log_xml) } + .not_to raise_error + end + + it "clears out the invalid answers" do + allow(logger).to receive(:warn) + + lettings_log_service.send(:create_log, lettings_log_xml) + lettings_log = LettingsLog.find_by(old_id: lettings_log_id) + + expect(lettings_log).not_to be_nil + expect(lettings_log.tenancy).to be_nil + expect(lettings_log.tenancyother).to be_nil + end + end + context "and this is an internal transfer from a non social housing" do before do lettings_log_xml.at_xpath("//xmlns:Q11").content = "9 Residential care home" @@ -935,6 +963,27 @@ RSpec.describe Imports::LettingsLogsImportService do end end + context "and the scheme and location is not given" do + let(:lettings_log_id) { "0b4a68df-30cc-474a-93c0-a56ce8fdad3b" } + + before do + lettings_log_xml.at_xpath("//xmlns:_1cmangroupcode").content = "" + lettings_log_xml.at_xpath("//xmlns:_1cschemecode").content = "" + lettings_log_xml.at_xpath("//xmlns:Q25").content = "" + lettings_log_xml.at_xpath("//meta:status").content = "saved" + end + + it "saves log without location and scheme" do + expect(logger).not_to receive(:warn) + lettings_log_service.send(:create_log, lettings_log_xml) + lettings_log = LettingsLog.find_by(old_id: lettings_log_id) + + expect(lettings_log.scheme_id).to be_nil + expect(lettings_log.location_id).to be_nil + expect(lettings_log.status).to eq("in_progress") + end + end + context "and this is a supported housing log with a single location under a scheme" do let(:lettings_log_id) { "0b4a68df-30cc-474a-93c0-a56ce8fdad3b" } @@ -1123,5 +1172,96 @@ RSpec.describe Imports::LettingsLogsImportService do expect(lettings_log.hbrentshortfall).to be_nil end end + + context "when setting location fields for 23/24 logs" do + let(:lettings_log_id) { "00d2343e-d5fa-4c89-8400-ec3854b0f2b4" } + let(:lettings_log_file) { open_file(fixture_directory, lettings_log_id) } + let(:lettings_log_xml) { Nokogiri::XML(lettings_log_file) } + + around do |example| + Timecop.freeze(Time.zone.local(2023, 4, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + + before do + lettings_log_xml.at_xpath("//xmlns:DAY").content = "10" + lettings_log_xml.at_xpath("//xmlns:MONTH").content = "4" + lettings_log_xml.at_xpath("//xmlns:YEAR").content = "2023" + lettings_log_xml.at_xpath("//xmlns:UPRN").content = "123456781234" + lettings_log_xml.at_xpath("//xmlns:AddressLine1").content = "address 1" + lettings_log_xml.at_xpath("//xmlns:AddressLine2").content = "address 2" + lettings_log_xml.at_xpath("//xmlns:TownCity").content = "towncity" + lettings_log_xml.at_xpath("//xmlns:County").content = "county" + lettings_log_xml.at_xpath("//xmlns:POSTCODE").content = "A1" + lettings_log_xml.at_xpath("//xmlns:POSTCOD2").content = "1AA" + + body = { + results: [ + { + DPA: { + "POSTCODE": "LS16 6FT", + "POST_TOWN": "Westminster", + "PO_BOX_NUMBER": "321", + "DOUBLE_DEPENDENT_LOCALITY": "Double Dependent Locality", + }, + }, + ], + }.to_json + + stub_request(:get, "https://api.os.uk/search/places/v1/uprn?key=OS_DATA_KEY&uprn=123456781234") + .to_return(status: 200, body:, headers: {}) + stub_request(:get, "https://api.os.uk/search/places/v1/uprn?key=OS_DATA_KEY&uprn=123") + .to_return(status: 500, body: "{}", headers: {}) + + allow(logger).to receive(:warn).and_return(nil) + end + + it "correctly sets address if uprn is not given" do + lettings_log_xml.at_xpath("//xmlns:UPRN").content = "" + lettings_log_service.send(:create_log, lettings_log_xml) + + lettings_log = LettingsLog.find_by(old_id: lettings_log_id) + expect(lettings_log&.uprn_known).to eq(0) # no + expect(lettings_log&.uprn).to be_nil + expect(lettings_log&.address_line1).to eq("address 1") + expect(lettings_log&.address_line2).to eq("address 2") + expect(lettings_log&.town_or_city).to eq("towncity") + expect(lettings_log&.county).to eq("county") + expect(lettings_log&.postcode_full).to eq("A1 1AA") + end + + it "correctly sets address and uprn if uprn is given" do + lettings_log_service.send(:create_log, lettings_log_xml) + + lettings_log = LettingsLog.find_by(old_id: lettings_log_id) + expect(lettings_log&.uprn_known).to eq(1) + expect(lettings_log&.uprn).to eq("123456781234") + expect(lettings_log&.address_line1).to eq("321") + expect(lettings_log&.address_line2).to eq("Double Dependent Locality") + expect(lettings_log&.town_or_city).to eq("Westminster") + expect(lettings_log&.postcode_full).to eq("LS16 6FT") + expect(lettings_log&.la).to eq("E08000035") + end + + it "correctly sets address and uprn if uprn is given but not recognised" do + lettings_log_xml.at_xpath("//xmlns:UPRN").content = "123" + + lettings_log_service.send(:create_log, lettings_log_xml) + + lettings_log = LettingsLog.find_by(old_id: lettings_log_id) + expect(lettings_log&.uprn_known).to eq(0) + expect(lettings_log&.uprn).to be_nil + expect(lettings_log&.address_line1).to eq("address 1") + expect(lettings_log&.address_line2).to eq("address 2") + expect(lettings_log&.town_or_city).to eq("towncity") + expect(lettings_log&.county).to eq("county") + expect(lettings_log&.postcode_full).to eq("A1 1AA") + expect(lettings_log&.la).to eq("E06000047") + end + end end end diff --git a/spec/services/imports/sales_logs_import_service_spec.rb b/spec/services/imports/sales_logs_import_service_spec.rb index 5862efa23..c06b7bf42 100644 --- a/spec/services/imports/sales_logs_import_service_spec.rb +++ b/spec/services/imports/sales_logs_import_service_spec.rb @@ -220,6 +220,87 @@ RSpec.describe Imports::SalesLogsImportService do end end + context "with 23/24 logs" do + around do |example| + Timecop.freeze(Time.zone.local(2023, 4, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + + before do + sales_log_xml.at_xpath("//xmlns:DAY").content = "10" + sales_log_xml.at_xpath("//xmlns:MONTH").content = "4" + sales_log_xml.at_xpath("//xmlns:YEAR").content = "2023" + sales_log_xml.at_xpath("//xmlns:UPRN").content = "" + sales_log_xml.at_xpath("//xmlns:AddressLine1").content = "address 1" + sales_log_xml.at_xpath("//xmlns:AddressLine2").content = "address 2" + sales_log_xml.at_xpath("//xmlns:TownCity").content = "towncity" + sales_log_xml.at_xpath("//xmlns:County").content = "county" + sales_log_xml.at_xpath("//xmlns:Q14Postcode").content = "A1 1AA" + end + + context "with outright sale type" do + let(:sales_log_id) { "outright_sale_sales_log" } + + before do + sales_log_xml.at_xpath("//xmlns:Q44MonthlyCharges").content = "40" + end + + it "successfully creates a completed outright sale log" do + expect(logger).not_to receive(:error) + expect(logger).not_to receive(:warn) + expect(logger).not_to receive(:info) + expect { sales_log_service.send(:create_log, sales_log_xml) } + .to change(SalesLog, :count).by(1) + end + end + + context "with shared sale type" do + let(:sales_log_id) { "shared_ownership_sales_log" } + + before do + sales_log_xml.at_xpath("//xmlns:joint").content = "1 Yes" + sales_log_xml.at_xpath("//xmlns:JointMore").content = "2 No" + sales_log_xml.at_xpath("//xmlns:PREVSHARED").content = "1" + sales_log_xml.at_xpath("//xmlns:Q16aProplen2").content = "0" + sales_log_xml.at_xpath("//xmlns:Q20Bedrooms").content = "2" + sales_log_xml.at_xpath("//xmlns:P2Eth").content = "2 White: Irish" + sales_log_xml.at_xpath("//xmlns:P2Nat").content = "18 United Kingdom" + sales_log_xml.at_xpath("//xmlns:BUY2LIVEIN").content = "1" + end + + it "successfully creates a completed shared sale log" do + expect(logger).not_to receive(:error) + expect(logger).not_to receive(:warn) + expect(logger).not_to receive(:info) + expect { sales_log_service.send(:create_log, sales_log_xml) } + .to change(SalesLog, :count).by(1) + sales_log = SalesLog.find_by(old_id: sales_log_id) + expect(sales_log.proplen_asked).to eq(1) + end + end + + context "with discounted sale type" do + let(:sales_log_id) { "shared_ownership_sales_log" } + + before do + sales_log_xml.at_xpath("//xmlns:PREVSHARED").content = "1" + sales_log_xml.at_xpath("//xmlns:Q20Bedrooms").content = "2" + end + + it "successfully creates a completed shared sale log" do + expect(logger).not_to receive(:error) + expect(logger).not_to receive(:warn) + expect(logger).not_to receive(:info) + expect { sales_log_service.send(:create_log, sales_log_xml) } + .to change(SalesLog, :count).by(1) + end + end + end + context "and the mortgage soft validation is triggered (mortgage_value_check)" do let(:sales_log_id) { "discounted_ownership_sales_log" } @@ -1336,6 +1417,97 @@ RSpec.describe Imports::SalesLogsImportService do end end + context "when setting location fields for 23/24 logs" do + let(:sales_log_id) { "outright_sale_sales_log" } + + around do |example| + Timecop.freeze(Time.zone.local(2023, 4, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + + before do + sales_log_xml.at_xpath("//xmlns:DAY").content = "10" + sales_log_xml.at_xpath("//xmlns:MONTH").content = "4" + sales_log_xml.at_xpath("//xmlns:YEAR").content = "2023" + sales_log_xml.at_xpath("//xmlns:UPRN").content = "123456781234" + sales_log_xml.at_xpath("//xmlns:AddressLine1").content = "address 1" + sales_log_xml.at_xpath("//xmlns:AddressLine2").content = "address 2" + sales_log_xml.at_xpath("//xmlns:TownCity").content = "towncity" + sales_log_xml.at_xpath("//xmlns:County").content = "county" + sales_log_xml.at_xpath("//xmlns:Q14Postcode").content = "A1 1AA" + + body = { + results: [ + { + DPA: { + "POSTCODE": "LS16 6FT", + "POST_TOWN": "Westminster", + "PO_BOX_NUMBER": "321", + "DOUBLE_DEPENDENT_LOCALITY": "Double Dependent Locality", + }, + }, + ], + }.to_json + + stub_request(:get, "https://api.os.uk/search/places/v1/uprn?key=OS_DATA_KEY&uprn=123456781234") + .to_return(status: 200, body:, headers: {}) + stub_request(:get, "https://api.os.uk/search/places/v1/uprn?key=OS_DATA_KEY&uprn=123") + .to_return(status: 500, body: "{}", headers: {}) + + WebMock.stub_request(:get, /api.postcodes.io\/postcodes\/LS166FT/) + .to_return(status: 200, body: '{"status":200,"result":{"admin_district":"Westminster","codes":{"admin_district":"E08000035"}}}', headers: {}) + + allow(logger).to receive(:warn).and_return(nil) + end + + it "correctly sets address if uprn is not given" do + sales_log_xml.at_xpath("//xmlns:UPRN").content = "" + sales_log_service.send(:create_log, sales_log_xml) + + sales_log = SalesLog.find_by(old_id: sales_log_id) + expect(sales_log&.uprn_known).to eq(0) # no + expect(sales_log&.uprn).to be_nil + expect(sales_log&.address_line1).to eq("address 1") + expect(sales_log&.address_line2).to eq("address 2") + expect(sales_log&.town_or_city).to eq("towncity") + expect(sales_log&.county).to eq("county") + expect(sales_log&.postcode_full).to eq("A1 1AA") + end + + it "correctly sets address and uprn if uprn is given" do + sales_log_service.send(:create_log, sales_log_xml) + + sales_log = SalesLog.find_by(old_id: sales_log_id) + expect(sales_log&.uprn_known).to eq(1) + expect(sales_log&.uprn).to eq("123456781234") + expect(sales_log&.address_line1).to eq("321") + expect(sales_log&.address_line2).to eq("Double Dependent Locality") + expect(sales_log&.town_or_city).to eq("Westminster") + expect(sales_log&.postcode_full).to eq("LS16 6FT") + expect(sales_log&.la).to eq("E08000035") + end + + it "correctly sets address and uprn if uprn is given but not recognised" do + sales_log_xml.at_xpath("//xmlns:UPRN").content = "123" + + sales_log_service.send(:create_log, sales_log_xml) + + sales_log = SalesLog.find_by(old_id: sales_log_id) + expect(sales_log&.uprn_known).to eq(0) + expect(sales_log&.uprn).to be_nil + expect(sales_log&.address_line1).to eq("address 1") + expect(sales_log&.address_line2).to eq("address 2") + expect(sales_log&.town_or_city).to eq("towncity") + expect(sales_log&.county).to eq("county") + expect(sales_log&.postcode_full).to eq("A1 1AA") + expect(sales_log&.la).to eq("E09000033") + end + end + context "when setting default buyer 1 previous tenancy" do let(:sales_log_id) { "outright_sale_sales_log" } diff --git a/spec/services/imports/scheme_location_import_service_spec.rb b/spec/services/imports/scheme_location_import_service_spec.rb index 186fba818..5c45a92aa 100644 --- a/spec/services/imports/scheme_location_import_service_spec.rb +++ b/spec/services/imports/scheme_location_import_service_spec.rb @@ -208,5 +208,23 @@ RSpec.describe Imports::SchemeLocationImportService do expect(location.scheme.confirmed).to be_falsey end end + + context "and support_type is not a valid one" do + before { location_xml.at_xpath("//scheme:support-type").content = "1" } + + it "sets the support type to missing" do + location = location_service.create_scheme_location(location_xml) + expect(location.scheme.support_type).to eq("Missing") + end + end + + context "and support_type is not answered" do + before { location_xml.at_xpath("//scheme:support-type").content = "" } + + it "sets the support type to nil" do + location = location_service.create_scheme_location(location_xml) + expect(location.scheme.support_type).to eq(nil) + end + end end end diff --git a/spec/views/bulk_upload_lettings_results/summary.html.erb_spec.rb b/spec/views/bulk_upload_lettings_results/summary.html.erb_spec.rb new file mode 100644 index 000000000..25499133d --- /dev/null +++ b/spec/views/bulk_upload_lettings_results/summary.html.erb_spec.rb @@ -0,0 +1,20 @@ +require "rails_helper" + +RSpec.describe "bulk_upload_lettings_results/summary.html.erb" do + let(:bulk_upload) { create(:bulk_upload, :lettings) } + + before do + create(:bulk_upload_error, bulk_upload:, cell: "AA100", row: "100", col: "AA") + create(:bulk_upload_error, bulk_upload:, cell: "Z100", row: "100", col: "Z") + end + + it "renders errors ordered by cell" do + assign(:bulk_upload, bulk_upload) + + render + + fragment = Capybara::Node::Simple.new(rendered) + + expect(fragment.find_css("table tbody th").map(&:inner_text)).to eql(%w[Z100 AA100]) + end +end