From 55de7bd561dbe4bc16bde04172774b625f6d9e8d Mon Sep 17 00:00:00 2001 From: natdeanlewissoftwire Date: Thu, 7 Mar 2024 16:18:37 +0000 Subject: [PATCH] feat: add lettings behaviour to sales logs --- .../form/sales/pages/address_fallback.rb | 25 +++++++++++++ .../form/sales/pages/address_matcher.rb | 19 ++++++++++ .../form/sales/pages/address_selection.rb | 28 +++++++++++++++ .../form/sales/pages/no_address_found.rb | 26 ++++++++++++++ app/models/form/sales/pages/uprn.rb | 12 +++++-- .../address_line1_for_address_matcher.rb | 20 +++++++++++ .../form/sales/questions/address_selection.rb | 34 ++++++++++++++++++ .../form/sales/questions/no_address_found.rb | 9 +++++ .../questions/postcode_for_address_matcher.rb | 13 +++++++ .../form/sales/questions/uprn_confirmation.rb | 18 +++++++--- .../sales/subsections/property_information.rb | 36 +++++++++++++------ app/models/sales_log.rb | 5 +++ .../lettings/year2024/row_parser.rb | 4 +-- .../bulk_upload/sales/year2024/row_parser.rb | 17 +++++++++ ...ddress_search_value_check_to_sales_logs.rb | 5 +++ db/schema.rb | 3 +- 16 files changed, 254 insertions(+), 20 deletions(-) create mode 100644 app/models/form/sales/pages/address_fallback.rb create mode 100644 app/models/form/sales/pages/address_matcher.rb create mode 100644 app/models/form/sales/pages/address_selection.rb create mode 100644 app/models/form/sales/pages/no_address_found.rb create mode 100644 app/models/form/sales/questions/address_line1_for_address_matcher.rb create mode 100644 app/models/form/sales/questions/address_selection.rb create mode 100644 app/models/form/sales/questions/no_address_found.rb create mode 100644 app/models/form/sales/questions/postcode_for_address_matcher.rb create mode 100644 db/migrate/20240307161802_add_address_search_value_check_to_sales_logs.rb diff --git a/app/models/form/sales/pages/address_fallback.rb b/app/models/form/sales/pages/address_fallback.rb new file mode 100644 index 000000000..9c18543e0 --- /dev/null +++ b/app/models/form/sales/pages/address_fallback.rb @@ -0,0 +1,25 @@ +class Form::Sales::Pages::AddressFallback < ::Form::Page + def initialize(id, hsh, subsection) + super + @id = "address" + @header = "Q12 - What is the property's address?" + @depends_on = [ + { "is_supported_housing?" => false, "uprn_known" => nil, "address_selection" => 100 }, + { "is_supported_housing?" => false, "uprn_known" => 0, "address_selection" => 100 }, + { "is_supported_housing?" => false, "uprn_confirmed" => 0, "address_selection" => 100 }, + { "is_supported_housing?" => false, "uprn_known" => nil, "address_options_present?" => false }, + { "is_supported_housing?" => false, "uprn_known" => 0, "address_options_present?" => false }, + { "is_supported_housing?" => false, "uprn_confirmed" => 0, "address_options_present?" => false }, + ] + end + + def questions + @questions ||= [ + Form::Sales::Questions::AddressLine1.new(nil, nil, self), + Form::Sales::Questions::AddressLine2.new(nil, nil, self), + Form::Sales::Questions::TownOrCity.new(nil, nil, self), + Form::Sales::Questions::County.new(nil, nil, self), + Form::Sales::Questions::PostcodeForFullAddress.new(nil, nil, self), + ] + end +end diff --git a/app/models/form/sales/pages/address_matcher.rb b/app/models/form/sales/pages/address_matcher.rb new file mode 100644 index 000000000..a39bb7282 --- /dev/null +++ b/app/models/form/sales/pages/address_matcher.rb @@ -0,0 +1,19 @@ +class Form::Sales::Pages::AddressMatcher < ::Form::Page + def initialize(id, hsh, subsection) + super + @id = "address_matcher" + @header = "Find an address" + @depends_on = [ + { "is_supported_housing?" => false, "uprn_known" => nil }, + { "is_supported_housing?" => false, "uprn_known" => 0 }, + { "is_supported_housing?" => false, "uprn_confirmed" => 0 }, + ] + end + + def questions + @questions ||= [ + Form::Sales::Questions::AddressLine1ForAddressMatcher.new(nil, nil, self), + Form::Sales::Questions::PostcodeForAddressMatcher.new(nil, nil, self), + ] + end +end diff --git a/app/models/form/sales/pages/address_selection.rb b/app/models/form/sales/pages/address_selection.rb new file mode 100644 index 000000000..9b5f564f0 --- /dev/null +++ b/app/models/form/sales/pages/address_selection.rb @@ -0,0 +1,28 @@ +class Form::Sales::Pages::AddressSelection < ::Form::Page + def initialize(id, hsh, subsection) + super + @id = "address_selection" + @header = "We found some addresses that might be this property" + @depends_on = [{ "address_options_present?" => true }] + end + + def questions + @questions ||= [ + Form::Sales::Questions::AddressSelection.new(nil, nil, self), + ] + end + + def routed_to?(log, _current_user = nil) + (log.uprn_known.nil? || log.uprn_known.zero?) && log.address_line1_input.present? && log.postcode_full_input.present? && (1..10).cover?(log.address_options&.count) + end + + def skip_text + "Search for address again" + end + + def skip_href(log = nil) + return unless log + + "/#{log.model_name.param_key.dasherize}s/#{log.id}/address-matcher" + end +end diff --git a/app/models/form/sales/pages/no_address_found.rb b/app/models/form/sales/pages/no_address_found.rb new file mode 100644 index 000000000..a37408379 --- /dev/null +++ b/app/models/form/sales/pages/no_address_found.rb @@ -0,0 +1,26 @@ +class Form::Sales::Pages::NoAddressFound < ::Form::Page + def initialize(id, hsh, subsection) + super + @id = "no_address_found" + @type = "interruption_screen" + @title_text = { + "translation" => "soft_validations.no_address_found.title_text", + "arguments" => [], + } + @informative_text = { + "translation" => "soft_validations.no_address_found.informative_text", + "arguments" => [], + } + @depends_on = [{ "address_options_present?" => false }] + end + + def questions + @questions ||= [ + Form::Sales::Questions::NoAddressFound.new(nil, nil, self), + ] + end + + def interruption_screen_question_ids + %w[address_line1_input] + end +end diff --git a/app/models/form/sales/pages/uprn.rb b/app/models/form/sales/pages/uprn.rb index c4ae5b357..05727878e 100644 --- a/app/models/form/sales/pages/uprn.rb +++ b/app/models/form/sales/pages/uprn.rb @@ -12,12 +12,20 @@ class Form::Sales::Pages::Uprn < ::Form::Page end def skip_text - "Enter address instead" + if form.start_year_after_2024? + "Search for address instead" + else + "Enter address instead" + end end def skip_href(log = nil) return unless log - "/#{log.model_name.param_key.dasherize}s/#{log.id}/address" + if form.start_year_after_2024? + "/#{log.model_name.param_key.dasherize}s/#{log.id}/address-matcher" + else + "/#{log.model_name.param_key.dasherize}s/#{log.id}/address" + end end end diff --git a/app/models/form/sales/questions/address_line1_for_address_matcher.rb b/app/models/form/sales/questions/address_line1_for_address_matcher.rb new file mode 100644 index 000000000..fd1211b69 --- /dev/null +++ b/app/models/form/sales/questions/address_line1_for_address_matcher.rb @@ -0,0 +1,20 @@ +class Form::Sales::Questions::AddressLine1ForAddressMatcher < ::Form::Question + def initialize(id, hsh, page) + super + @id = "address_line1_input" + @header = "Address line 1" + @error_label = "Address line 1" + @type = "text" + @plain_label = true + @check_answer_label = "Find address" + @disable_clearing_if_not_routed_or_dynamic_answer_options = true + @hide_question_number_on_page = true + end + + def answer_label(log, _current_user = nil) + [ + log.address_line1_input, + log.postcode_full_input, + ].select(&:present?).join("\n") + end +end diff --git a/app/models/form/sales/questions/address_selection.rb b/app/models/form/sales/questions/address_selection.rb new file mode 100644 index 000000000..ce374cb7d --- /dev/null +++ b/app/models/form/sales/questions/address_selection.rb @@ -0,0 +1,34 @@ +class Form::Sales::Questions::AddressSelection < ::Form::Question + def initialize(id, hsh, page) + super + @id = "address_selection" + @header = "Select the correct address" + @type = "radio" + @check_answer_label = "Select the correct address" + @disable_clearing_if_not_routed_or_dynamic_answer_options = true + end + + def answer_options(log = nil, _user = nil) + answer_opts = { "100" => { "value" => "The address is not listed, I want to enter the address manually" } } + return answer_opts unless ActiveRecord::Base.connected? + return answer_opts unless log&.address_options + + answer_opts = {} + + (0...[log.address_options.count, 10].min).each do |i| + answer_opts[i.to_s] = { "value" => log.address_options[i] } + end + + answer_opts["divider"] = { "value" => true } + answer_opts["100"] = { "value" => "The address is not listed, I want to enter the address manually" } + answer_opts + end + + def displayed_answer_options(log, user = nil) + answer_options(log, user) + end + + def hidden_in_check_answers?(log, _current_user = nil) + (log.uprn_known == 1 || log.uprn_confirmed == 1) || !(1..10).cover?(log.address_options&.count) + end +end diff --git a/app/models/form/sales/questions/no_address_found.rb b/app/models/form/sales/questions/no_address_found.rb new file mode 100644 index 000000000..0db041111 --- /dev/null +++ b/app/models/form/sales/questions/no_address_found.rb @@ -0,0 +1,9 @@ +class Form::Sales::Questions::NoAddressFound < ::Form::Question + def initialize(id, hsh, page) + super + @id = "address_search_value_check" + @header = "No address found" + @type = "interruption_screen" + @hidden_in_check_answers = true + end +end diff --git a/app/models/form/sales/questions/postcode_for_address_matcher.rb b/app/models/form/sales/questions/postcode_for_address_matcher.rb new file mode 100644 index 000000000..421cdc4fc --- /dev/null +++ b/app/models/form/sales/questions/postcode_for_address_matcher.rb @@ -0,0 +1,13 @@ +class Form::Sales::Questions::PostcodeForAddressMatcher < ::Form::Question + def initialize(id, hsh, page) + super + @id = "postcode_full_input" + @header = "Postcode" + @type = "text" + @width = 5 + @plain_label = true + @disable_clearing_if_not_routed_or_dynamic_answer_options = true + @hide_question_number_on_page = true + @hidden_in_check_answers = true + end +end diff --git a/app/models/form/sales/questions/uprn_confirmation.rb b/app/models/form/sales/questions/uprn_confirmation.rb index 38ad9c938..6954a6ea5 100644 --- a/app/models/form/sales/questions/uprn_confirmation.rb +++ b/app/models/form/sales/questions/uprn_confirmation.rb @@ -4,14 +4,22 @@ class Form::Sales::Questions::UprnConfirmation < ::Form::Question @id = "uprn_confirmed" @header = "Is this the property address?" @type = "radio" - @answer_options = ANSWER_OPTIONS @check_answer_label = "Is this the right address?" end - ANSWER_OPTIONS = { - "1" => { "value" => "Yes" }, - "0" => { "value" => "No, I want to enter the address manually" }, - }.freeze + def answer_options + if form.start_year_after_2024? + { + "1" => { "value" => "Yes" }, + "0" => { "value" => "No, I want to search for the address instead" }, + }.freeze + else + { + "1" => { "value" => "Yes" }, + "0" => { "value" => "No, I want to enter the address manually" }, + }.freeze + end + end def notification_banner(log = nil) return unless log&.uprn diff --git a/app/models/form/sales/subsections/property_information.rb b/app/models/form/sales/subsections/property_information.rb index 302dfa7c7..073c1b056 100644 --- a/app/models/form/sales/subsections/property_information.rb +++ b/app/models/form/sales/subsections/property_information.rb @@ -22,16 +22,32 @@ class Form::Sales::Subsections::PropertyInformation < ::Form::Subsection end def uprn_questions - [ - Form::Sales::Pages::Uprn.new(nil, nil, self), - Form::Sales::Pages::UprnConfirmation.new(nil, nil, self), - Form::Sales::Pages::Address.new(nil, nil, self), - Form::Sales::Pages::PropertyLocalAuthority.new(nil, nil, self), - Form::Sales::Pages::Buyer1IncomeMaxValueCheck.new("local_authority_buyer_1_income_max_value_check", nil, self, check_answers_card_number: nil), - Form::Sales::Pages::Buyer2IncomeMaxValueCheck.new("local_authority_buyer_2_income_max_value_check", nil, self, check_answers_card_number: nil), - Form::Sales::Pages::CombinedIncomeMaxValueCheck.new("local_authority_combined_income_max_value_check", nil, self, check_answers_card_number: nil), - Form::Sales::Pages::AboutPriceValueCheck.new("about_price_la_value_check", nil, self), - ] + if form.start_year_after_2024? + [ + Form::Sales::Pages::Uprn.new(nil, nil, self), + Form::Sales::Pages::UprnConfirmation.new(nil, nil, self), + Form::Sales::Pages::AddressMatcher.new(nil, nil, self), + Form::Sales::Pages::NoAddressFound.new(nil, nil, self), + Form::Sales::Pages::AddressSelection.new(nil, nil, self), + Form::Sales::Pages::AddressFallback.new(nil, nil, self), + Form::Sales::Pages::PropertyLocalAuthority.new(nil, nil, self), + Form::Sales::Pages::Buyer1IncomeMaxValueCheck.new("local_authority_buyer_1_income_max_value_check", nil, self, check_answers_card_number: nil), + Form::Sales::Pages::Buyer2IncomeMaxValueCheck.new("local_authority_buyer_2_income_max_value_check", nil, self, check_answers_card_number: nil), + Form::Sales::Pages::CombinedIncomeMaxValueCheck.new("local_authority_combined_income_max_value_check", nil, self, check_answers_card_number: nil), + Form::Sales::Pages::AboutPriceValueCheck.new("about_price_la_value_check", nil, self), + ] + else + [ + Form::Sales::Pages::Uprn.new(nil, nil, self), + Form::Sales::Pages::UprnConfirmation.new(nil, nil, self), + Form::Sales::Pages::Address.new(nil, nil, self), + Form::Sales::Pages::PropertyLocalAuthority.new(nil, nil, self), + Form::Sales::Pages::Buyer1IncomeMaxValueCheck.new("local_authority_buyer_1_income_max_value_check", nil, self, check_answers_card_number: nil), + Form::Sales::Pages::Buyer2IncomeMaxValueCheck.new("local_authority_buyer_2_income_max_value_check", nil, self, check_answers_card_number: nil), + Form::Sales::Pages::CombinedIncomeMaxValueCheck.new("local_authority_combined_income_max_value_check", nil, self, check_answers_card_number: nil), + Form::Sales::Pages::AboutPriceValueCheck.new("about_price_la_value_check", nil, self), + ] + end end def postcode_and_la_questions diff --git a/app/models/sales_log.rb b/app/models/sales_log.rb index b17956b3a..d68d5bd82 100644 --- a/app/models/sales_log.rb +++ b/app/models/sales_log.rb @@ -32,6 +32,7 @@ class SalesLog < Log before_validation :reset_previous_location_fields!, unless: :previous_postcode_known? before_validation :set_derived_fields! before_validation :process_uprn_change!, if: :should_process_uprn_change? + before_validation :process_address_change!, if: :should_process_address_change? belongs_to :managing_organisation, class_name: "Organisation", optional: true @@ -432,6 +433,10 @@ class SalesLog < Log uprn && saledate && (uprn_changed? || saledate_changed?) && collection_start_year_for_date(saledate) >= 2023 end + def should_process_address_change? + (address_selection || select_best_address_match) && saledate && ((address_selection_changed? || select_best_address_match) || saledate_changed?) && form.start_year_after_2024? + end + def value_with_discount return if value.blank? diff --git a/app/services/bulk_upload/lettings/year2024/row_parser.rb b/app/services/bulk_upload/lettings/year2024/row_parser.rb index aa5fcb2de..366d45bc9 100644 --- a/app/services/bulk_upload/lettings/year2024/row_parser.rb +++ b/app/services/bulk_upload/lettings/year2024/row_parser.rb @@ -543,7 +543,7 @@ private def validate_address_option_found if log.address_selection.nil? && field_16.blank? && (field_17.present? || field_19.present?) - %i[field_17 field_18 field_19 field_21 field_22].each do |field| + %i[field_17 field_18 field_19 field_20 field_21 field_22].each do |field| errors.add(field, I18n.t("validations.no_address_found")) end end @@ -1235,7 +1235,7 @@ private end def address_line1_input - [field_17, field_18, field_19, field_20].compact.join(", ") + [field_17, field_18, field_19].compact.join(", ") end def postcode_known diff --git a/app/services/bulk_upload/sales/year2024/row_parser.rb b/app/services/bulk_upload/sales/year2024/row_parser.rb index f5697135b..bba1174ac 100644 --- a/app/services/bulk_upload/sales/year2024/row_parser.rb +++ b/app/services/bulk_upload/sales/year2024/row_parser.rb @@ -450,6 +450,7 @@ class BulkUpload::Sales::Year2024::RowParser on: :after_log validate :validate_buyer1_economic_status, on: :before_log + validate :validate_address_option_found, on: :after_log validate :validate_nulls, on: :after_log validate :validate_valid_radio_option, on: :before_log @@ -597,6 +598,14 @@ private end end + def validate_address_option_found + if log.address_selection.nil? && field_22.blank? && (field_23.present? || field_25.present?) + %i[field_23 field_24 field_25 field_26 field_27 field_28].each do |field| + errors.add(field, I18n.t("validations.no_address_found")) + end + end + end + def validate_address_fields if field_22.blank? || log.errors.attribute_names.include?(:uprn) if field_23.blank? @@ -758,6 +767,7 @@ private address_line2: %i[field_24], town_or_city: %i[field_25], county: %i[field_26], + address_selection: [:field_23], ethnic_group2: %i[field_40], ethnicbuy2: %i[field_40], @@ -932,6 +942,9 @@ private attributes["address_line2"] = field_24 attributes["town_or_city"] = field_25 attributes["county"] = field_26 + attributes["address_line1_input"] = address_line1_input + attributes["postcode_full_input"] = postcode_full + attributes["select_best_address_match"] = true attributes["ethnic_group2"] = infer_buyer2_ethnic_group_from_ethnic attributes["ethnicbuy2"] = field_40 @@ -948,6 +961,10 @@ private attributes end + def address_line1_input + [field_23, field_24, field_25].compact.join(", ") + end + def saledate Date.new(field_6 + 2000, field_5, field_4) if field_6.present? && field_5.present? && field_4.present? rescue Date::Error diff --git a/db/migrate/20240307161802_add_address_search_value_check_to_sales_logs.rb b/db/migrate/20240307161802_add_address_search_value_check_to_sales_logs.rb new file mode 100644 index 000000000..84347e309 --- /dev/null +++ b/db/migrate/20240307161802_add_address_search_value_check_to_sales_logs.rb @@ -0,0 +1,5 @@ +class AddAddressSearchValueCheckToSalesLogs < ActiveRecord::Migration[7.0] + def change + add_column :sales_logs, :address_search_value_check, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index 4c371ef59..7b97cdf69 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: 2024_03_06_091659) do +ActiveRecord::Schema[7.0].define(version: 2024_03_07_161802) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -668,6 +668,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_03_06_091659) do t.integer "address_selection" t.string "address_line1_input" t.string "postcode_full_input" + t.integer "address_search_value_check" t.index ["bulk_upload_id"], name: "index_sales_logs_on_bulk_upload_id" t.index ["created_by_id"], name: "index_sales_logs_on_created_by_id" t.index ["managing_organisation_id"], name: "index_sales_logs_on_managing_organisation_id"