Browse Source

CLDC-3787: Autocomplete address uprn search (#2967)

* Prototype

* Remove git from dockerfile

* UPRN search too

* Revert address client and use uprn client

* Add address search to lettings too

* Updates with lettings logs

* Update copy

* Move guidance to partial

* Fix uprn return

* Delete new db file, restore old

* Lint

* Remove old db file

* Lint

* Add new db file, remove old

* JS lint

* Update schema

* Add manual entry option

* Update derived variables

* Comment out old version of find address in 2024

* Remove db column

* Add new db columns

* Update guidance partial

* Add unless to migration

* Add migration files to remove and readd

* authenticate user

* Remove file

* Delete migration files

* Add search url

* Add search url

* Fix onConfirm

* Add manual entry button instead of change skip link

* Revert "Add manual entry button instead of change skip link"

This reverts commit 22577c801a.

* Revert "Revert "Add manual entry button instead of change skip link""

This reverts commit 9f0a2111a5.

* Replace uprn question

* Update question copy

* Allow changing the address search value

* Rename address autocomplete to address search

* Add buttons to switch between address questions

* Fix controller logic

* Enable adding question numbers to page headers

* Update skip links

* Add js disabled message to select

* Alternative way to handle js disabled users

* Revert "Alternative way to handle js disabled users"

This reverts commit 10da3d61e2.

* Fix typo

* Fix address options for address search question

* Reuse AddressDataPresenter where appropriate

* Lint

* Remove uprn selection question tests

* Reuse UprnDataPresenter where appropriate

* CSV export, exclude address_search

* Add address search to sales and lettings factory bots

* Exclude old address questions from routing, keep as exported values

* lint

* Update uprn value

* Add address search input boolean and switch between questions

* Reword copy, remove "Find" and "Search by"

* Align address questions, add question number and question text

* Remove old wip depends on

* Update some tests

* Update migration, move default value from db to model

* Update test

* Remove binding pry

* Lint

* Update test

* Lint

* Update test

* Update routes with underscores

* Remove debugging

* Limit visible logs to user

* Add manual address entry selected variable

* Change address search min length to 3chars

* Remove binding.pry

* Update factory bots, manual_address_entry_selected to true for preexisting tests

* Update model tests

* Update sales model tests excl E-code tests

* Update address search request test

* Reuse uprn id instead of address_search

* Set manual address entry selected as false when creating test logs

* Update model test

* Update request tests and remove old questions

* Add back test

* Update services

* Update more tests

Co-authored-by: kosiakkatrina <kosiakkatrina@users.noreply.github.com>

* Update request tests

* update model tests

* Also update sales log

* Update service csv uprn_selection values to 1

* Add tests for pages and questions

* Update test

* Update uprn_known

* Lint

* Add feature test

* Update test

* Update tests

* Remove test

* pre-consolidate migration files

* Indentation

* Controller method improvements

* Update question numbers for 2025/26

* Update question numbers tests

* consolidate and delete old migration files

* undo changes to schema.rb

* Update 2025 property information translation files

* Update answer options to show singular previously selected result if present

* Move buttons to bottom guidance partials

* Small improvements, make address search and existing search more similar

* Validate entered addresses as within England

* Update test

* Revert "Validate entered addresses as within England"

This reverts commit 2dbfbcc8a5.

* Add missing button to sales address page

* Change error code

* Clear invalid options

* Edit no results message method

* Keep no result logic just change text

* Display uprn value with address value

* Still show no results message when characters entered is less than 3 rather than nothing

* Fix uprn result when query is ambiguous

* Reduce min match for address search

* Hide no result found message just before results are populated

* Prevent changing logs to 2025 with invalid addresses

* Correct attribute name

* Handle nil

* Remove custom error message

* Remove unused variables from factory

* Update tests, remove address and postcode from old find address

* Fix bug clearing uprn from see all answers

* Revert "Fix bug clearing uprn from see all answers"

This reverts commit a66c47a1ab.

* Undo changes to validation method

* Fix unchanged uprn_selection when clearing or changing uprn

* Undo a change

* Update bulk upload 2025

* Fix typo

* Remove redundant line

---------

Co-authored-by: Kat <54268893+kosiakkatrina@users.noreply.github.com>
Co-authored-by: kosiakkatrina <kosiakkatrina@users.noreply.github.com>
pull/2948/head^2
Manny Dinssa 1 week ago committed by GitHub
parent
commit
5da13b6b29
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 70
      app/controllers/address_search_controller.rb
  2. 8
      app/controllers/test_data_controller.rb
  3. 73
      app/frontend/controllers/address_search_controller.js
  4. 3
      app/frontend/controllers/index.js
  5. 15
      app/models/derived_variables/lettings_log_variables.rb
  6. 49
      app/models/derived_variables/sales_log_variables.rb
  7. 9
      app/models/form/lettings/pages/address_fallback.rb
  8. 17
      app/models/form/lettings/pages/address_search.rb
  9. 44
      app/models/form/lettings/questions/address_search.rb
  10. 1
      app/models/form/lettings/questions/postcode_for_full_address.rb
  11. 6
      app/models/form/lettings/subsections/property_information.rb
  12. 11
      app/models/form/sales/pages/address_fallback.rb
  13. 23
      app/models/form/sales/pages/address_search.rb
  14. 1
      app/models/form/sales/pages/no_address_found.rb
  15. 44
      app/models/form/sales/questions/address_search.rb
  16. 1
      app/models/form/sales/questions/postcode_for_full_address.rb
  17. 6
      app/models/form/sales/subsections/property_information.rb
  18. 20
      app/models/log.rb
  19. 7
      app/services/address_client.rb
  20. 1
      app/services/bulk_upload/lettings/year2024/row_parser.rb
  21. 1
      app/services/bulk_upload/lettings/year2025/row_parser.rb
  22. 1
      app/services/bulk_upload/sales/year2024/row_parser.rb
  23. 1
      app/services/bulk_upload/sales/year2025/row_parser.rb
  24. 22
      app/services/csv/lettings_log_csv_service.rb
  25. 14
      app/services/csv/sales_log_csv_service.rb
  26. 8
      app/services/uprn_data_presenter.rb
  27. 24
      app/views/form/_address_search_question.html.erb
  28. 21
      app/views/form/_select_question.html.erb
  29. 3
      app/views/form/guidance/_address_fallback.html.erb
  30. 7
      app/views/form/guidance/_address_search.html.erb
  31. 7
      config/locales/forms/2024/lettings/guidance.en.yml
  32. 7
      config/locales/forms/2024/lettings/property_information.en.yml
  33. 7
      config/locales/forms/2024/sales/guidance.en.yml
  34. 7
      config/locales/forms/2024/sales/property_information.en.yml
  35. 7
      config/locales/forms/2025/lettings/guidance.en.yml
  36. 45
      config/locales/forms/2025/lettings/property_information.en.yml
  37. 7
      config/locales/forms/2025/sales/guidance.en.yml
  38. 45
      config/locales/forms/2025/sales/property_information.en.yml
  39. 14
      config/locales/validations/sales/property_information.en.yml
  40. 4
      config/routes.rb
  41. 6
      db/migrate/20250219122817_add_manual_address_entry_selected_to_logs.rb
  42. 1
      db/schema.rb
  43. 9
      spec/factories/lettings_log.rb
  44. 10
      spec/factories/sales_log.rb
  45. 45
      spec/features/form/address_search_spec.rb
  46. 60
      spec/features/form/form_navigation_spec.rb
  47. 375
      spec/features/lettings_log_spec.rb
  48. 375
      spec/features/sales_log_spec.rb
  49. 2
      spec/fixtures/files/sales_logs_csv_export_codes_24.csv
  50. 2
      spec/fixtures/files/sales_logs_csv_export_labels_24.csv
  51. 2
      spec/fixtures/files/sales_logs_csv_export_non_support_labels_24.csv
  52. 4
      spec/models/bulk_upload_spec.rb
  53. 9
      spec/models/form/lettings/pages/address_fallback_spec.rb
  54. 42
      spec/models/form/lettings/pages/address_search_spec.rb
  55. 68
      spec/models/form/lettings/questions/address_search_spec.rb
  56. 8
      spec/models/form/lettings/questions/uprn_selection_spec.rb
  57. 4
      spec/models/form/lettings/questions/uprn_spec.rb
  58. 12
      spec/models/form/lettings/subsections/property_information_spec.rb
  59. 9
      spec/models/form/sales/pages/address_fallback_spec.rb
  60. 42
      spec/models/form/sales/pages/address_search_spec.rb
  61. 4
      spec/models/form/sales/pages/uprn_confirmation_spec.rb
  62. 68
      spec/models/form/sales/questions/address_search_spec.rb
  63. 8
      spec/models/form/sales/questions/uprn_selection_spec.rb
  64. 12
      spec/models/form/sales/subsections/property_information_spec.rb
  65. 7
      spec/models/sales_log_spec.rb
  66. 4
      spec/models/validations/sales/property_validations_spec.rb
  67. 2
      spec/request_helper.rb
  68. 148
      spec/requests/address_search_controller_spec.rb
  69. 10
      spec/requests/duplicate_logs_controller_spec.rb
  70. 8
      spec/services/csv/sales_log_csv_service_spec.rb
  71. 2
      spec/services/exports/lettings_log_export_service_spec.rb
  72. 13
      spec/shared/shared_examples_for_derived_fields.rb
  73. 2
      spec/shared/shared_log_examples.rb

70
app/controllers/address_search_controller.rb

@ -0,0 +1,70 @@
class AddressSearchController < ApplicationController
before_action :authenticate_user!
before_action :set_log, only: %i[manual_input search_input]
def index
query = params[:query]
if query.match?(/\A\d+\z/) && query.length > 5
# Query is all numbers and greater than 5 digits, assume it's a UPRN
service = UprnClient.new(query)
service.call
if service.error.present?
render json: { error: service.error }, status: :not_found
else
presenter = UprnDataPresenter.new(service.result)
render json: [{ text: presenter.address, value: presenter.uprn }]
end
elsif query.match?(/[a-zA-Z]/)
# Query contains letters, assume it's an address
service = AddressClient.new(query, { minmatch: 0.2 })
service.call
if service.error.present?
render json: { error: service.error }, status: :not_found
else
results = service.result.map do |result|
presenter = AddressDataPresenter.new(result)
{ text: presenter.address, value: presenter.uprn }
end
render json: results
end
else
# Query is ambiguous, use both APIs and merge results
address_service = AddressClient.new(query, { minmatch: 0.2 })
uprn_service = UprnClient.new(query)
address_service.call
uprn_service.call
results = ([uprn_service.result] || []) + (address_service.result || [])
if address_service.error.present? && uprn_service.error.present?
render json: { error: "Address and UPRN are not recognised." }, status: :not_found
else
formatted_results = results.map do |result|
presenter = AddressDataPresenter.new(result)
{ text: presenter.address, value: presenter.uprn }
end
render json: formatted_results
end
end
end
def manual_input
@log.update!(manual_address_entry_selected: true)
redirect_to polymorphic_url([@log, :address])
end
def search_input
@log.update!(manual_address_entry_selected: false)
redirect_to polymorphic_url([@log, :address_search])
end
private
def set_log
@log = current_user.send("#{params[:log_type]}s").find(params[:log_id])
end
end

8
app/controllers/test_data_controller.rb

@ -4,14 +4,14 @@ class TestDataController < ApplicationController
def create_test_lettings_log def create_test_lettings_log
return render_not_found unless FeatureToggle.create_test_logs_enabled? return render_not_found unless FeatureToggle.create_test_logs_enabled?
log = FactoryBot.create(:lettings_log, :completed, assigned_to: current_user, ppostcode_full: "SW1A 1AA") log = FactoryBot.create(:lettings_log, :completed, assigned_to: current_user, ppostcode_full: "SW1A 1AA", manual_address_entry_selected: false)
redirect_to lettings_log_path(log) redirect_to lettings_log_path(log)
end end
def create_setup_test_lettings_log def create_setup_test_lettings_log
return render_not_found unless FeatureToggle.create_test_logs_enabled? return render_not_found unless FeatureToggle.create_test_logs_enabled?
log = FactoryBot.create(:lettings_log, :setup_completed, assigned_to: current_user) log = FactoryBot.create(:lettings_log, :setup_completed, assigned_to: current_user, manual_address_entry_selected: false)
redirect_to lettings_log_path(log) redirect_to lettings_log_path(log)
end end
@ -40,14 +40,14 @@ class TestDataController < ApplicationController
def create_test_sales_log def create_test_sales_log
return render_not_found unless FeatureToggle.create_test_logs_enabled? return render_not_found unless FeatureToggle.create_test_logs_enabled?
log = FactoryBot.create(:sales_log, :completed, assigned_to: current_user) log = FactoryBot.create(:sales_log, :completed, assigned_to: current_user, manual_address_entry_selected: false)
redirect_to sales_log_path(log) redirect_to sales_log_path(log)
end end
def create_setup_test_sales_log def create_setup_test_sales_log
return render_not_found unless FeatureToggle.create_test_logs_enabled? return render_not_found unless FeatureToggle.create_test_logs_enabled?
log = FactoryBot.create(:sales_log, :shared_ownership_setup_complete, assigned_to: current_user) log = FactoryBot.create(:sales_log, :shared_ownership_setup_complete, assigned_to: current_user, manual_address_entry_selected: false)
redirect_to sales_log_path(log) redirect_to sales_log_path(log)
end end

73
app/frontend/controllers/address_search_controller.js

@ -0,0 +1,73 @@
import { Controller } from '@hotwired/stimulus'
import accessibleAutocomplete from 'accessible-autocomplete'
import 'accessible-autocomplete/dist/accessible-autocomplete.min.css'
const options = []
const fetchOptions = async (query, searchUrl) => {
if (query.length < 2) {
throw new Error('Query must be at least 2 characters long.')
}
try {
const response = await fetch(`${searchUrl}?query=${encodeURIComponent(query.trim())}`)
return await response.json()
} catch (error) {
return error
}
}
const fetchAndPopulateSearchResults = async (query, populateResults, searchUrl, populateOptions, selectEl) => {
if (/\S/.test(query)) {
try {
const results = await fetchOptions(query, searchUrl)
if (results.length === 0) {
populateOptions([], selectEl)
populateResults([])
} else {
populateOptions(results, selectEl)
populateResults(Object.values(results).map((o) => `${o.text} (${o.value})`))
}
} catch (error) {
populateOptions([], selectEl)
populateResults([])
}
}
}
const populateOptions = (results, selectEl) => {
selectEl.innerHTML = ''
results.forEach((result) => {
const option = document.createElement('option')
option.value = result.value
option.innerHTML = `${result.text} (${result.value})`
selectEl.appendChild(option)
options.push(option)
})
}
export default class extends Controller {
connect () {
const searchUrl = JSON.parse(this.element.dataset.info).search_url
const selectEl = this.element
accessibleAutocomplete.enhanceSelectElement({
defaultValue: '',
selectElement: selectEl,
minLength: 2,
source: (query, populateResults) => {
fetchAndPopulateSearchResults(query, populateResults, searchUrl, populateOptions, selectEl)
},
autoselect: true,
showNoOptionsFound: true,
placeholder: 'Start typing to search',
templates: { suggestion: (value) => value },
onConfirm: (val) => {
const selectedResult = Array.from(selectEl.options).find(option => option.text === val)
if (selectedResult) {
selectedResult.selected = true
}
}
})
}
}

3
app/frontend/controllers/index.js

@ -19,6 +19,8 @@ import FilterLayoutController from './filter_layout_controller.js'
import TabsController from './tabs_controller.js' import TabsController from './tabs_controller.js'
import AddressSearchController from './address_search_controller.js'
application.register('accessible-autocomplete', AccessibleAutocompleteController) application.register('accessible-autocomplete', AccessibleAutocompleteController)
application.register('conditional-filter', ConditionalFilterController) application.register('conditional-filter', ConditionalFilterController)
application.register('conditional-question', ConditionalQuestionController) application.register('conditional-question', ConditionalQuestionController)
@ -27,3 +29,4 @@ application.register('numeric-question', NumericQuestionController)
application.register('filter-layout', FilterLayoutController) application.register('filter-layout', FilterLayoutController)
application.register('search', SearchController) application.register('search', SearchController)
application.register('tabs', TabsController) application.register('tabs', TabsController)
application.register('address-search', AddressSearchController)

15
app/models/derived_variables/lettings_log_variables.rb

@ -113,6 +113,21 @@ module DerivedVariables::LettingsLogVariables
self.previous_la_known = nil if is_renewal? self.previous_la_known = nil if is_renewal?
end end
if form.start_year_2024_or_later?
if manual_address_entry_selected
self.uprn_known = 0
self.uprn_selection = nil
self.uprn_confirmed = nil
else
self.uprn_confirmed = 1 if uprn.present?
self.uprn_known = 1 if uprn.present?
reset_address_fields! if uprn.blank?
if uprn_changed?
self.uprn_selection = uprn
end
end
end
if is_renewal? if is_renewal?
self.underoccupation_benefitcap = 2 if collection_start_year == 2021 self.underoccupation_benefitcap = 2 if collection_start_year == 2021
self.voiddate = startdate self.voiddate = startdate

49
app/models/derived_variables/sales_log_variables.rb

@ -55,27 +55,29 @@ module DerivedVariables::SalesLogVariables
if uprn_known&.zero? if uprn_known&.zero?
self.uprn = nil self.uprn = nil
if uprn_known_was == 1 if uprn_known_was == 1
self.address_line1 = nil reset_address_fields!
self.address_line2 = nil
self.town_or_city = nil
self.county = nil
self.pcodenk = nil
self.postcode_full = nil
self.la = nil
end end
end end
if uprn_known == 1 && uprn_confirmed&.zero? if uprn_known == 1 && uprn_confirmed&.zero?
self.uprn = nil reset_address_fields!
self.uprn_known = 0 self.uprn_known = 0
self.uprn_confirmed = nil self.uprn_confirmed = nil
self.address_line1 = nil end
self.address_line2 = nil
self.town_or_city = nil if form.start_year_2024_or_later?
self.county = nil if manual_address_entry_selected
self.pcodenk = nil self.uprn_known = 0
self.postcode_full = nil self.uprn_selection = nil
self.la = nil self.uprn_confirmed = nil
else
self.uprn_confirmed = 1 if uprn.present?
self.uprn_known = 1 if uprn.present?
reset_address_fields! if uprn.blank?
if uprn_changed?
self.uprn_selection = uprn
end
end
end end
if form.start_year_2025_or_later? && is_bedsit? if form.start_year_2025_or_later? && is_bedsit?
@ -248,4 +250,21 @@ private
def prevten_was_social_housing? def prevten_was_social_housing?
[1, 2].include?(prevten) || [1, 2].include?(prevtenbuy2) [1, 2].include?(prevten) || [1, 2].include?(prevtenbuy2)
end end
def reset_address_fields!
self.uprn = nil
self.uprn_known = nil
self.address_line1 = nil
self.address_line2 = nil
self.town_or_city = nil
self.county = nil
self.pcode1 = nil
self.pcode2 = nil
self.pcodenk = nil
self.address_line1_input = nil
self.postcode_full_input = nil
self.postcode_full = nil
self.is_la_inferred = nil
self.la = nil
end
end end

9
app/models/form/lettings/pages/address_fallback.rb

@ -3,14 +3,7 @@ class Form::Lettings::Pages::AddressFallback < ::Form::Page
super super
@id = "address" @id = "address"
@copy_key = "lettings.property_information.address" @copy_key = "lettings.property_information.address"
@depends_on = [ @depends_on = [{ "is_supported_housing?" => false, "manual_address_entry_selected" => true }]
{ "is_supported_housing?" => false, "uprn_known" => nil, "uprn_selection" => "uprn_not_listed" },
{ "is_supported_housing?" => false, "uprn_known" => 0, "uprn_selection" => "uprn_not_listed" },
{ "is_supported_housing?" => false, "uprn_confirmed" => 0, "uprn_selection" => "uprn_not_listed" },
{ "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 },
]
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

17
app/models/form/lettings/pages/address_search.rb

@ -0,0 +1,17 @@
class Form::Lettings::Pages::AddressSearch < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "address_search"
@copy_key = "sales.property_information.address_search"
@depends_on = [{ "is_supported_housing?" => false, "manual_address_entry_selected" => false }]
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end
def questions
@questions ||= [
Form::Lettings::Questions::AddressSearch.new(nil, nil, self),
]
end
QUESTION_NUMBER_FROM_YEAR = { 2024 => 12, 2025 => 16 }.freeze
end

44
app/models/form/lettings/questions/address_search.rb

@ -0,0 +1,44 @@
class Form::Lettings::Questions::AddressSearch < ::Form::Question
def initialize(id, hsh, page)
super
@id = "uprn"
@type = "address_search"
@copy_key = "lettings.property_information.address_search"
@plain_label = true
@bottom_guidance_partial = "address_search"
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
@hide_question_number_on_page = true
end
def answer_options(log = nil, _user = nil)
return {} unless ActiveRecord::Base.connected?
return {} unless log&.address_options&.any?
log.address_options.each_with_object({}) do |option, hash|
hash[option[:uprn]] = { "value" => "#{option[:address]} (#{option[:uprn]})" }
end
end
def get_extra_check_answer_value(log)
return unless log.uprn_known == 1
value = [
log.address_line1,
log.address_line2,
log.town_or_city,
log.county,
log.postcode_full,
(LocalAuthority.find_by(code: log.la)&.name if log.la.present?),
].select(&:present?)
return unless value.any?
"\n\n#{value.join("\n")}"
end
def displayed_answer_options(log, user = nil)
answer_options(log, user).transform_values { |value| value["value"] } || {}
end
QUESTION_NUMBER_FROM_YEAR = { 2024 => 12, 2025 => 16 }.freeze
end

1
app/models/form/lettings/questions/postcode_for_full_address.rb

@ -20,6 +20,7 @@ class Form::Lettings::Questions::PostcodeForFullAddress < ::Form::Question
@disable_clearing_if_not_routed_or_dynamic_answer_options = true @disable_clearing_if_not_routed_or_dynamic_answer_options = true
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
@hide_question_number_on_page = true @hide_question_number_on_page = true
@bottom_guidance_partial = "address_fallback"
end end
QUESTION_NUMBER_FROM_YEAR = { 2023 => 12, 2024 => 13, 2025 => 17 }.freeze QUESTION_NUMBER_FROM_YEAR = { 2023 => 12, 2024 => 13, 2025 => 17 }.freeze

6
app/models/form/lettings/subsections/property_information.rb

@ -30,11 +30,7 @@ class Form::Lettings::Subsections::PropertyInformation < ::Form::Subsection
def uprn_questions def uprn_questions
if form.start_year_2024_or_later? if form.start_year_2024_or_later?
[ [
Form::Lettings::Pages::Uprn.new(nil, nil, self), Form::Lettings::Pages::AddressSearch.new(nil, nil, self),
Form::Lettings::Pages::UprnConfirmation.new(nil, nil, self),
Form::Lettings::Pages::AddressMatcher.new(nil, nil, self),
Form::Lettings::Pages::NoAddressFound.new(nil, nil, self), # soft validation
Form::Lettings::Pages::UprnSelection.new(nil, nil, self),
Form::Lettings::Pages::AddressFallback.new(nil, nil, self), Form::Lettings::Pages::AddressFallback.new(nil, nil, self),
] ]
else else

11
app/models/form/sales/pages/address_fallback.rb

@ -3,14 +3,7 @@ class Form::Sales::Pages::AddressFallback < ::Form::Page
super super
@id = "address" @id = "address"
@copy_key = "sales.property_information.address" @copy_key = "sales.property_information.address"
@depends_on = [ @depends_on = [{ "manual_address_entry_selected" => true }]
{ "uprn_known" => nil, "uprn_selection" => "uprn_not_listed" },
{ "uprn_known" => 0, "uprn_selection" => "uprn_not_listed" },
{ "uprn_confirmed" => 0, "uprn_selection" => "uprn_not_listed" },
{ "uprn_known" => nil, "address_options_present?" => false },
{ "uprn_known" => 0, "address_options_present?" => false },
{ "uprn_confirmed" => 0, "address_options_present?" => false },
]
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end
@ -24,5 +17,5 @@ class Form::Sales::Pages::AddressFallback < ::Form::Page
] ]
end end
QUESTION_NUMBER_FROM_YEAR = { 2024 => 16, 2025 => 16 }.freeze QUESTION_NUMBER_FROM_YEAR = { 2024 => 16, 2025 => 14 }.freeze
end end

23
app/models/form/sales/pages/address_search.rb

@ -0,0 +1,23 @@
class Form::Sales::Pages::AddressSearch < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "address_search"
@copy_key = "sales.property_information.address_search"
@depends_on = [{ "manual_address_entry_selected" => false }]
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end
def questions
@questions ||= [
Form::Sales::Questions::AddressSearch.new(nil, nil, self),
]
end
def skip_href(log = nil)
return unless log
"/#{log.log_type.dasherize}s/#{log.id}/property-number-of-bedrooms"
end
QUESTION_NUMBER_FROM_YEAR = { 2024 => 15, 2025 => 13 }.freeze
end

1
app/models/form/sales/pages/no_address_found.rb

@ -16,7 +16,6 @@ class Form::Sales::Pages::NoAddressFound < ::Form::Page
{ "uprn_known" => nil, "address_options_present?" => false }, { "uprn_known" => nil, "address_options_present?" => false },
{ "uprn_known" => 0, "address_options_present?" => false }, { "uprn_known" => 0, "address_options_present?" => false },
{ "uprn_confirmed" => 0, "address_options_present?" => false }, { "uprn_confirmed" => 0, "address_options_present?" => false },
] ]
end end

44
app/models/form/sales/questions/address_search.rb

@ -0,0 +1,44 @@
class Form::Sales::Questions::AddressSearch < ::Form::Question
def initialize(id, hsh, page)
super
@id = "uprn"
@type = "address_search"
@copy_key = "sales.property_information.address_search"
@plain_label = true
@bottom_guidance_partial = "address_search"
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
@hide_question_number_on_page = true
end
def answer_options(log = nil, _user = nil)
return {} unless ActiveRecord::Base.connected?
return {} unless log&.address_options&.any?
log.address_options.each_with_object({}) do |option, hash|
hash[option[:uprn]] = { "value" => "#{option[:address]} (#{option[:uprn]})" }
end
end
def get_extra_check_answer_value(log)
return unless log.uprn_known == 1
value = [
log.address_line1,
log.address_line2,
log.town_or_city,
log.county,
log.postcode_full,
(LocalAuthority.find_by(code: log.la)&.name if log.la.present?),
].select(&:present?)
return unless value.any?
"\n\n#{value.join("\n")}"
end
def displayed_answer_options(log, user = nil)
answer_options(log, user).transform_values { |value| value["value"] } || {}
end
QUESTION_NUMBER_FROM_YEAR = { 2024 => 15, 2025 => 13 }.freeze
end

1
app/models/form/sales/questions/postcode_for_full_address.rb

@ -20,6 +20,7 @@ class Form::Sales::Questions::PostcodeForFullAddress < ::Form::Question
@disable_clearing_if_not_routed_or_dynamic_answer_options = true @disable_clearing_if_not_routed_or_dynamic_answer_options = true
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
@hide_question_number_on_page = true @hide_question_number_on_page = true
@bottom_guidance_partial = "address_fallback"
end end
QUESTION_NUMBER_FROM_YEAR = { 2023 => 15, 2024 => 16, 2025 => 14 }.freeze QUESTION_NUMBER_FROM_YEAR = { 2023 => 15, 2024 => 16, 2025 => 14 }.freeze

6
app/models/form/sales/subsections/property_information.rb

@ -24,11 +24,7 @@ class Form::Sales::Subsections::PropertyInformation < ::Form::Subsection
def uprn_questions def uprn_questions
if form.start_year_2024_or_later? if form.start_year_2024_or_later?
[ [
Form::Sales::Pages::Uprn.new(nil, nil, self), Form::Sales::Pages::AddressSearch.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::UprnSelection.new(nil, nil, self),
Form::Sales::Pages::AddressFallback.new(nil, nil, self), Form::Sales::Pages::AddressFallback.new(nil, nil, self),
Form::Sales::Pages::PropertyLocalAuthority.new(nil, nil, self), Form::Sales::Pages::PropertyLocalAuthority.new(nil, nil, self),
Form::Sales::Pages::Buyer1IncomeDiscountedMaxValueCheck.new("local_authority_buyer_1_income_max_value_check", nil, self, check_answers_card_number: nil), Form::Sales::Pages::Buyer1IncomeDiscountedMaxValueCheck.new("local_authority_buyer_1_income_max_value_check", nil, self, check_answers_card_number: nil),

20
app/models/log.rb

@ -75,8 +75,7 @@ class Log < ApplicationRecord
presenter = UprnDataPresenter.new(service.result) presenter = UprnDataPresenter.new(service.result)
self.uprn_known = 1 self.uprn_known = 1
self.uprn_confirmed = nil unless skip_update_uprn_confirmed self.uprn_selection = uprn
self.uprn_selection = nil
self.address_line1 = presenter.address_line1 self.address_line1 = presenter.address_line1
self.address_line2 = presenter.address_line2 self.address_line2 = presenter.address_line2
self.town_or_city = presenter.town_or_city self.town_or_city = presenter.town_or_city
@ -126,16 +125,27 @@ class Log < ApplicationRecord
end end
def address_options def address_options
return @address_options if @address_options && @last_searched_address_string == address_string if uprn.present?
service = UprnClient.new(uprn)
service.call
if service.result.blank? || service.error.present?
@address_options = []
return @address_options
end
presenter = UprnDataPresenter.new(service.result)
@address_options = [{ address: presenter.address, uprn: presenter.uprn }]
else
return @address_options if @address_options && @last_searched_address_string == address_string
return if address_string.blank?
if [address_line1_input, postcode_full_input].all?(&:present?)
@last_searched_address_string = address_string @last_searched_address_string = address_string
service = AddressClient.new(address_string) service = AddressClient.new(address_string)
service.call service.call
if service.result.blank? || service.error.present? if service.result.blank? || service.error.present?
@address_options = [] @address_options = []
return @answer_options return @address_options
end end
address_opts = [] address_opts = []

7
app/services/address_client.rb

@ -7,8 +7,9 @@ class AddressClient
ADDRESS = "api.os.uk".freeze ADDRESS = "api.os.uk".freeze
PATH = "/search/places/v1/find".freeze PATH = "/search/places/v1/find".freeze
def initialize(address) def initialize(address, options = {})
@address = address @address = address
@options = options
end end
def call def call
@ -43,8 +44,8 @@ private
params = { params = {
query: address, query: address,
key: ENV["OS_DATA_KEY"], key: ENV["OS_DATA_KEY"],
maxresults: 10, maxresults: @options[:maxresults] || 10,
minmatch: 0.4, minmatch: @options[:minmatch] || 0.4,
} }
uri.query = URI.encode_www_form(params) uri.query = URI.encode_www_form(params)
uri.to_s uri.to_s

1
app/services/bulk_upload/lettings/year2024/row_parser.rb

@ -1363,6 +1363,7 @@ private
attributes["address_line1_input"] = address_line1_input attributes["address_line1_input"] = address_line1_input
attributes["postcode_full_input"] = postcode_full attributes["postcode_full_input"] = postcode_full
attributes["select_best_address_match"] = true if field_16.blank? attributes["select_best_address_match"] = true if field_16.blank?
attributes["manual_address_entry_selected"] = field_16.blank?
end end
attributes attributes

1
app/services/bulk_upload/lettings/year2025/row_parser.rb

@ -1360,6 +1360,7 @@ private
attributes["address_line1_input"] = address_line1_input attributes["address_line1_input"] = address_line1_input
attributes["postcode_full_input"] = postcode_full attributes["postcode_full_input"] = postcode_full
attributes["select_best_address_match"] = true if field_18.blank? attributes["select_best_address_match"] = true if field_18.blank?
attributes["manual_address_entry_selected"] = field_18.blank?
end end
attributes attributes

1
app/services/bulk_upload/sales/year2024/row_parser.rb

@ -986,6 +986,7 @@ private
attributes["address_line1_input"] = address_line1_input attributes["address_line1_input"] = address_line1_input
attributes["postcode_full_input"] = postcode_full attributes["postcode_full_input"] = postcode_full
attributes["select_best_address_match"] = true if field_22.blank? attributes["select_best_address_match"] = true if field_22.blank?
attributes["manual_address_entry_selected"] = field_22.blank?
attributes["ethnic_group2"] = infer_buyer2_ethnic_group_from_ethnic attributes["ethnic_group2"] = infer_buyer2_ethnic_group_from_ethnic
attributes["ethnicbuy2"] = field_40 attributes["ethnicbuy2"] = field_40

1
app/services/bulk_upload/sales/year2025/row_parser.rb

@ -961,6 +961,7 @@ private
attributes["address_line1_input"] = address_line1_input attributes["address_line1_input"] = address_line1_input
attributes["postcode_full_input"] = postcode_full attributes["postcode_full_input"] = postcode_full
attributes["select_best_address_match"] = true if field_16.blank? attributes["select_best_address_match"] = true if field_16.blank?
attributes["manual_address_entry_selected"] = field_16.blank?
attributes["ethnic_group2"] = infer_buyer2_ethnic_group_from_ethnic attributes["ethnic_group2"] = infer_buyer2_ethnic_group_from_ethnic
attributes["ethnicbuy2"] = field_37 attributes["ethnicbuy2"] = field_37

22
app/services/csv/lettings_log_csv_service.rb

@ -208,6 +208,11 @@ module Csv
3 => "Intermediate Rent", 3 => "Intermediate Rent",
}.freeze }.freeze
UPRN_KNOWN_LABELS = {
0 => "No",
1 => "Yes",
}.freeze
LABELS = { LABELS = {
"lettype" => LETTYPE_LABELS, "lettype" => LETTYPE_LABELS,
"irproduct" => IRPRODUCT_LABELS, "irproduct" => IRPRODUCT_LABELS,
@ -215,6 +220,7 @@ module Csv
"newprop" => NEWPROP_LABELS, "newprop" => NEWPROP_LABELS,
"incref" => INCREF_LABELS, "incref" => INCREF_LABELS,
"renttype" => RENTTYPE_LABELS, "renttype" => RENTTYPE_LABELS,
"uprn_known" => UPRN_KNOWN_LABELS,
}.freeze }.freeze
CONVENTIONAL_YES_NO_ATTRIBUTES = %w[illness_type_1 illness_type_2 illness_type_3 illness_type_4 illness_type_5 illness_type_6 illness_type_7 illness_type_8 illness_type_9 illness_type_10 refused cbl cap chr accessible_register letting_allocation_none housingneeds_a housingneeds_b housingneeds_c housingneeds_d housingneeds_e housingneeds_f housingneeds_g housingneeds_h has_benefits nocharge postcode_known].freeze CONVENTIONAL_YES_NO_ATTRIBUTES = %w[illness_type_1 illness_type_2 illness_type_3 illness_type_4 illness_type_5 illness_type_6 illness_type_7 illness_type_8 illness_type_9 illness_type_10 refused cbl cap chr accessible_register letting_allocation_none housingneeds_a housingneeds_b housingneeds_c housingneeds_d housingneeds_e housingneeds_f housingneeds_g housingneeds_h has_benefits nocharge postcode_known].freeze
@ -249,6 +255,18 @@ module Csv
"letting_allocation_unknown" => %w[letting_allocation_none], "letting_allocation_unknown" => %w[letting_allocation_none],
}.freeze }.freeze
ATTRIBUTE_MAPPINGS_2024 = {
"uprn" => %w[uprn_known uprn],
}.freeze
def attribute_mappings
if @year >= 2024
ATTRIBUTE_MAPPINGS.merge(ATTRIBUTE_MAPPINGS_2024)
else
ATTRIBUTE_MAPPINGS
end
end
ORDERED_ADDRESS_FIELDS = %w[uprn address_line1 address_line2 town_or_city county postcode_full is_la_inferred la_label la uprn_known uprn_selection address_search_value_check address_line1_input postcode_full_input address_line1_as_entered address_line2_as_entered town_or_city_as_entered county_as_entered postcode_full_as_entered la_as_entered].freeze ORDERED_ADDRESS_FIELDS = %w[uprn address_line1 address_line2 town_or_city county postcode_full is_la_inferred la_label la uprn_known uprn_selection address_search_value_check address_line1_input postcode_full_input address_line1_as_entered address_line2_as_entered town_or_city_as_entered county_as_entered postcode_full_as_entered la_as_entered].freeze
SUPPORT_ONLY_ATTRIBUTES = %w[postcode_known is_la_inferred totchild totelder totadult net_income_known previous_la_known is_previous_la_inferred age1_known age2_known age3_known age4_known age5_known age6_known age7_known age8_known details_known_2 details_known_3 details_known_4 details_known_5 details_known_6 details_known_7 details_known_8 wrent wscharge wpschrge wsupchrg wtcharge wtshortfall old_form_id old_id tshortfall_known hhtype la prevloc updated_by_id uprn_confirmed address_line1_input postcode_full_input uprn_selection address_line1_as_entered address_line2_as_entered town_or_city_as_entered county_as_entered postcode_full_as_entered la_as_entered created_by].freeze SUPPORT_ONLY_ATTRIBUTES = %w[postcode_known is_la_inferred totchild totelder totadult net_income_known previous_la_known is_previous_la_inferred age1_known age2_known age3_known age4_known age5_known age6_known age7_known age8_known details_known_2 details_known_3 details_known_4 details_known_5 details_known_6 details_known_7 details_known_8 wrent wscharge wpschrge wsupchrg wtcharge wtshortfall old_form_id old_id tshortfall_known hhtype la prevloc updated_by_id uprn_confirmed address_line1_input postcode_full_input uprn_selection address_line1_as_entered address_line2_as_entered town_or_city_as_entered county_as_entered postcode_full_as_entered la_as_entered created_by].freeze
@ -279,10 +297,10 @@ module Csv
ordered_questions.flat_map do |question| ordered_questions.flat_map do |question|
if question.type == "checkbox" if question.type == "checkbox"
question.answer_options.keys.reject { |key| key == "divider" }.map { |key| question.answer_options.keys.reject { |key| key == "divider" }.map { |key|
ATTRIBUTE_MAPPINGS.fetch(key, key) attribute_mappings.fetch(key, key)
}.flatten }.flatten
else else
ATTRIBUTE_MAPPINGS.fetch(question.id, question.id) attribute_mappings.fetch(question.id, question.id)
end end
end end
end end

14
app/services/csv/sales_log_csv_service.rb

@ -152,6 +152,15 @@ module Csv
"uprn_confirmed" => "UPRNCONFIRMED", "uprn_confirmed" => "UPRNCONFIRMED",
}.freeze }.freeze
UPRN_CONFIRMED_LABELS = {
0 => "No",
1 => "Yes",
}.freeze
LABELS = {
"uprn_confirmed" => UPRN_CONFIRMED_LABELS,
}.freeze
def formatted_attribute_headers def formatted_attribute_headers
return @attributes unless @user.support? return @attributes unless @user.support?
@ -208,6 +217,9 @@ module Csv
unless @user.support? && @year >= 2024 unless @user.support? && @year >= 2024
mappings["postcode_full"] = %w[pcode1 pcode2] mappings["postcode_full"] = %w[pcode1 pcode2]
end end
if @year >= 2024
mappings["uprn"] = %w[uprn uprn_confirmed address_line1_input postcode_full_input uprn_selection]
end
mappings mappings
end end
@ -280,6 +292,8 @@ module Csv
end end
def get_label(value, attribute, log) def get_label(value, attribute, log)
return LABELS[attribute][value] if LABELS.key?(attribute)
log.form log.form
.get_question(attribute, log) .get_question(attribute, log)
&.label_from_value(value) &.label_from_value(value)

8
app/services/uprn_data_presenter.rb

@ -55,4 +55,12 @@ class UprnDataPresenter
def result_from_lpi? def result_from_lpi?
data["LPI_KEY"].present? data["LPI_KEY"].present?
end end
def uprn
data["UPRN"]
end
def address
data["ADDRESS"]
end
end end

24
app/views/form/_address_search_question.html.erb

@ -0,0 +1,24 @@
<% selected = @log.public_send(question.id) || "" %>
<% answers = question.displayed_answer_options(@log, current_user).map { |key, value| OpenStruct.new(id: key, name: select_option_name(value), resource: value) } %>
<%= render partial: "form/guidance/#{question.top_guidance_partial}" if question.top_guidance? %>
<%= f.govuk_select(question.id.to_sym,
label: legend(question, page_header, conditional),
"data-controller": "address-search",
"data-info": { search_url: address_search_url }.to_json,
caption: caption(caption_text, page_header, conditional),
hint: { text: question.hint_text&.html_safe }) do %>
<% if answers.any? %>
<% answers.each do |answer| %>
<option value="<%= answer.id %>"
data-synonyms="<%= answer_option_synonyms(answer.resource) %>"
data-append="<%= answer_option_append(answer.resource) %>"
data-hint="<%= answer_option_hint(answer.resource) %>"
<%= question.answer_selected?(@log, answer) ? "selected" : "" %>><%= answer.name || answer.resource %></option>
<% end %>
<% else %>
<option value="" disabled>Javascript is disabled. Please enter the address manually.</option>
<% end %>
<% end %>
<%= render partial: "form/guidance/#{question.bottom_guidance_partial}" if question.bottom_guidance? %>

21
app/views/form/_select_question.html.erb

@ -1,20 +1,23 @@
<% selected = @log.public_send(question.id) || "" %> <% selected = @log.public_send(question.id) || "" %>
<% answers = question.displayed_answer_options(@log, current_user).map { |key, value| OpenStruct.new(id: key, name: select_option_name(value), resource: value) } %> <% answers = question.displayed_answer_options(@log, current_user).map { |key, value| OpenStruct.new(id: key, name: select_option_name(value), resource: value) } %>
<%= render partial: "form/guidance/#{question.top_guidance_partial}" if question.top_guidance? %> <%= render partial: "form/guidance/#{question.top_guidance_partial}" if question.top_guidance? %>
<%= f.govuk_select(question.id.to_sym, <%= f.govuk_select(question.id.to_sym,
label: legend(question, page_header, conditional), label: legend(question, page_header, conditional),
"data-controller": "accessible-autocomplete", "data-controller": "accessible-autocomplete",
caption: caption(caption_text, page_header, conditional), caption: caption(caption_text, page_header, conditional),
hint: { text: question.hint_text&.html_safe }) do %> hint: { text: question.hint_text&.html_safe }) do %>
<% if answers.any? %>
<% answers.each do |answer| %> <% answers.each do |answer| %>
<option value="<%= answer.id %>" <option value="<%= answer.id %>"
data-synonyms="<%= answer_option_synonyms(answer.resource) %>" data-synonyms="<%= answer_option_synonyms(answer.resource) %>"
data-append="<%= answer_option_append(answer.resource) %>" data-append="<%= answer_option_append(answer.resource) %>"
data-hint="<%= answer_option_hint(answer.resource) %>" data-hint="<%= answer_option_hint(answer.resource) %>"
<%= question.answer_selected?(@log, answer) ? "selected" : "" %>><%= answer.name || answer.resource %></option> <%= question.answer_selected?(@log, answer) ? "selected" : "" %>><%= answer.name || answer.resource %></option>
<% end %> <% end %>
<% else %>
<option value="" disabled></option>
<% end %> <% end %>
<% end %>
<%= render partial: "form/guidance/#{question.bottom_guidance_partial}" if question.bottom_guidance? %> <%= render partial: "form/guidance/#{question.bottom_guidance_partial}" if question.bottom_guidance? %>

3
app/views/form/guidance/_address_fallback.html.erb

@ -0,0 +1,3 @@
<div class="govuk-button-group">
<%= govuk_link_to "Clear address and search instead", address_search_input_path(@log.log_type, @log.id), class: "govuk-button govuk-button--secondary" %>
</div>

7
app/views/form/guidance/_address_search.html.erb

@ -0,0 +1,7 @@
<%= govuk_details(summary_text: I18n.t("forms.#{@log.form.start_date.year}.#{@log.form.type}.guidance.address_search.title")) do %>
<%= I18n.t("forms.#{@log.form.start_date.year}.#{@log.form.type}.guidance.address_search.content").html_safe %>
<% end %>
<div class="govuk-button-group">
<%= govuk_link_to "Enter the address manually instead", address_manual_input_path(@log.log_type, @log.id), class: "govuk-button govuk-button--secondary" %>
</div>

7
config/locales/forms/2024/lettings/guidance.en.yml

@ -61,3 +61,10 @@ en:
<li>child benefit</li> <li>child benefit</li>
<li>council tax support</li> <li>council tax support</li>
</ul>" </ul>"
address_search:
title: "Can’t find the address you’re looking for?"
content: "<ul class=\"govuk-list govuk-list--bullet\">
<li>Some properties may not be available yet e.g. new builds; you might need to enter them manually instead</li>
<li>For UPRN (Unique Property Reference Number), please enter the full value exactly</li>
</ul>"

7
config/locales/forms/2024/lettings/property_information.en.yml

@ -50,6 +50,13 @@ en:
hint_text: "" hint_text: ""
question_text: "Select the correct address" question_text: "Select the correct address"
address_search:
page_header: "What is the property's address?"
check_answer_label: "Address"
check_answer_prompt: "Enter address or UPRN"
hint_text: "For example, '1 Victoria Road' or '10010457355'"
question_text: "Enter address or UPRN"
address: address:
page_header: "What is the property's address?" page_header: "What is the property's address?"
address_line1: address_line1:

7
config/locales/forms/2024/sales/guidance.en.yml

@ -44,3 +44,10 @@ en:
privacy_notice_buyer: privacy_notice_buyer:
content: "Make sure the buyer has seen or been given access to %{privacy_notice_link} before completing this log. This is a legal requirement under data protection legislation." content: "Make sure the buyer has seen or been given access to %{privacy_notice_link} before completing this log. This is a legal requirement under data protection legislation."
privacy_notice_link_text: "the Ministry of Housing, Communities and Local Government (MHCLG) privacy notice" privacy_notice_link_text: "the Ministry of Housing, Communities and Local Government (MHCLG) privacy notice"
address_search:
title: "Can’t find the address you’re looking for?"
content: "<ul class=\"govuk-list govuk-list--bullet\">
<li>Some properties may not be available yet e.g. new builds; you might need to enter them manually instead</li>
<li>For UPRN (Unique Property Reference Number), please enter the full value exactly</li>
</ul>"

7
config/locales/forms/2024/sales/property_information.en.yml

@ -43,6 +43,13 @@ en:
hint_text: "" hint_text: ""
question_text: "Select the correct address" question_text: "Select the correct address"
address_search:
page_header: "What is the property's address?"
check_answer_label: "Address"
check_answer_prompt: "Enter address or UPRN"
hint_text: "For example, '1 Victoria Road' or '10010457355'"
question_text: "Enter address or UPRN"
address: address:
page_header: "What is the property's address?" page_header: "What is the property's address?"
address_line1: address_line1:

7
config/locales/forms/2025/lettings/guidance.en.yml

@ -61,3 +61,10 @@ en:
<li>child benefit</li> <li>child benefit</li>
<li>council tax support</li> <li>council tax support</li>
</ul>" </ul>"
address_search:
title: "Can’t find the address you’re looking for?"
content: "<ul class=\"govuk-list govuk-list--bullet\">
<li>Some properties may not be available yet e.g. new builds; you might need to enter them manually instead</li>
<li>For UPRN (Unique Property Reference Number), please enter the full value exactly</li>
</ul>"

45
config/locales/forms/2025/lettings/property_information.en.yml

@ -10,45 +10,12 @@ en:
hint_text: "" hint_text: ""
question_text: "Is this the first time the property has been let as social housing?" question_text: "Is this the first time the property has been let as social housing?"
uprn: address_search:
page_header: "" page_header: "What is the property's address?"
uprn_known: check_answer_label: "Address"
check_answer_label: "UPRN known" check_answer_prompt: "Enter address or UPRN"
check_answer_prompt: "Enter UPRN if known" hint_text: "For example, '1 Victoria Road' or '10010457355'"
hint_text: "The Unique Property Reference Number (UPRN) is a unique number system created by Ordnance Survey and used by housing providers and various industries across the UK. An example UPRN is 10010457355.<br><br>The UPRN may not be the same as the property reference assigned by your organisation.<br><br>If you don’t know the UPRN you can enter the address of the property instead on the next screen." question_text: "Enter address or UPRN"
question_text: "Do you know the property's UPRN?"
uprn:
check_answer_label: "UPRN"
check_answer_prompt: ""
hint_text: ""
question_text: "What is the property's UPRN?"
uprn_confirmed:
page_header: "We found an address that might be this property"
check_answer_label: "Is this the right address?"
check_answer_prompt: "Tell us if this is the right address"
hint_text: ""
question_text: "Is this the property address?"
address_matcher:
page_header: "Find an address"
address_line1_input:
check_answer_label: "Find address"
check_answer_prompt: "Try find address"
hint_text: ""
question_text: "Address line 1"
postcode_full_input:
check_answer_label: ""
check_answer_prompt: ""
hint_text: ""
question_text: "Postcode"
uprn_selection:
page_header: "We found an address that might be this property"
check_answer_label: "Select correct address"
check_answer_prompt: "Select correct address"
hint_text: ""
question_text: "Select the correct address"
address: address:
page_header: "What is the property's address?" page_header: "What is the property's address?"

7
config/locales/forms/2025/sales/guidance.en.yml

@ -44,3 +44,10 @@ en:
privacy_notice_buyer: privacy_notice_buyer:
content: "Make sure the buyer has seen or been given access to %{privacy_notice_link} before completing this log. This is a legal requirement under data protection legislation." content: "Make sure the buyer has seen or been given access to %{privacy_notice_link} before completing this log. This is a legal requirement under data protection legislation."
privacy_notice_link_text: "the Ministry of Housing, Communities and Local Government (MHCLG) privacy notice" privacy_notice_link_text: "the Ministry of Housing, Communities and Local Government (MHCLG) privacy notice"
address_search:
title: "Can’t find the address you’re looking for?"
content: "<ul class=\"govuk-list govuk-list--bullet\">
<li>Some properties may not be available yet e.g. new builds; you might need to enter them manually instead</li>
<li>For UPRN (Unique Property Reference Number), please enter the full value exactly</li>
</ul>"

45
config/locales/forms/2025/sales/property_information.en.yml

@ -3,45 +3,12 @@ en:
2025: 2025:
sales: sales:
property_information: property_information:
uprn: address_search:
page_header: "" page_header: "What is the property's address?"
uprn_known: check_answer_label: "Address"
check_answer_label: "UPRN known" check_answer_prompt: "Enter address or UPRN"
check_answer_prompt: "Enter UPRN if known" hint_text: "For example, '1 Victoria Road' or '10010457355'"
hint_text: "The Unique Property Reference Number (UPRN) is a unique number system created by Ordnance Survey and used by housing providers and various industries across the UK. An example UPRN is 10010457355.<br><br>The UPRN may not be the same as the property reference assigned by your organisation.<br><br>If you don’t know the UPRN you can enter the address of the property instead on the next screen." question_text: "Enter address or UPRN"
question_text: "Do you know the property's UPRN?"
uprn:
check_answer_label: "UPRN"
check_answer_prompt: ""
hint_text: ""
question_text: "What is the property's UPRN?"
uprn_confirmed:
page_header: "We found an address that might be this property"
check_answer_label: "Is this the right address?"
check_answer_prompt: "Tell us if this is the right address"
hint_text: ""
question_text: "Is this the property address?"
address_matcher:
page_header: "Find an address"
address_line1_input:
check_answer_label: "Find address"
check_answer_prompt: "Try find address"
hint_text: ""
question_text: "Address line 1"
postcode_full_input:
check_answer_label: ""
check_answer_prompt: ""
hint_text: ""
question_text: "Postcode"
uprn_selection:
page_header: "We found an address that might be this property"
check_answer_label: "Select correct address"
check_answer_prompt: "Select correct address"
hint_text: ""
question_text: "Select the correct address"
address: address:
page_header: "What is the property's address?" page_header: "What is the property's address?"

14
config/locales/validations/sales/property_information.en.yml

@ -7,7 +7,7 @@ en:
joint_purchase: "Buyers’ last accommodation and discounted ownership postcodes must match." joint_purchase: "Buyers’ last accommodation and discounted ownership postcodes must match."
not_joint_purchase: "Buyer’s last accommodation and discounted ownership postcodes must match." not_joint_purchase: "Buyer’s last accommodation and discounted ownership postcodes must match."
invalid: "Enter a postcode in the correct format, for example AA1 1AA." invalid: "Enter a postcode in the correct format, for example AA1 1AA."
not_in_england: "It looks like you have entered a postcode outside of England - only submit Lettings forms for Lettings that occur in England" not_in_england: "It looks like you have entered a postcode outside of England. Only create logs for sales in England."
ppostcode_full: ppostcode_full:
postcode_must_match_previous: postcode_must_match_previous:
joint_purchase: "Buyers’ last accommodation and discounted ownership postcodes must match." joint_purchase: "Buyers’ last accommodation and discounted ownership postcodes must match."
@ -21,7 +21,7 @@ en:
joint_purchase: "Buyers’ last accommodation and discounted ownership postcodes must match." joint_purchase: "Buyers’ last accommodation and discounted ownership postcodes must match."
not_joint_purchase: "Buyer’s last accommodation and discounted ownership postcodes must match." not_joint_purchase: "Buyer’s last accommodation and discounted ownership postcodes must match."
invalid: "UPRN must be 12 digits or less." invalid: "UPRN must be 12 digits or less."
not_in_england: "It looks like you have an entered a postcode outside of England. Only create logs for lettings in England." not_in_england: "It looks like you have entered an address outside of England. Only create logs for sales in England."
beds: beds:
bedsits_have_max_one_bedroom: "Number of bedrooms must be 1 if the property is a bedsit." bedsits_have_max_one_bedroom: "Number of bedrooms must be 1 if the property is a bedsit."
proptype: proptype:
@ -29,11 +29,11 @@ en:
uprn_known: uprn_known:
invalid: "You must answer UPRN known?" invalid: "You must answer UPRN known?"
la: la:
not_in_england: "It looks like you have entered an address outside of England. Only create logs for lettings in England." not_in_england: "It looks like you have entered an address outside of England. Only create logs for sales in England."
uprn_confirmation: uprn_confirmation:
not_in_england: "It looks like you have entered an address outside of England. Only create logs for lettings in England." not_in_england: "It looks like you have entered an address outside of England. Only create logs for sales in England."
uprn_selection: uprn_selection:
not_in_england: "It looks like you have entered an address outside of England. Only create logs for lettings in England." not_in_england: "It looks like you have entered an address outside of England. Only create logs for sales in England."
saledate: saledate:
postcode_not_in_england: "It looks like you have an entered a postcode outside of England. Only create logs for lettings in England." postcode_not_in_england: "It looks like you have entered a postcode outside of England. Only create logs for sales in England."
address_not_in_england: "It looks like you have entered an address outside of England. Only create logs for lettings in England." address_not_in_england: "It looks like you have entered an address outside of England. Only create logs for sales in England."

4
config/routes.rb

@ -39,6 +39,10 @@ Rails.application.routes.draw do
get "/data-sharing-agreement", to: "content#data_sharing_agreement" get "/data-sharing-agreement", to: "content#data_sharing_agreement"
get "/service-moved", to: "maintenance#service_moved" get "/service-moved", to: "maintenance#service_moved"
get "/service-unavailable", to: "maintenance#service_unavailable" get "/service-unavailable", to: "maintenance#service_unavailable"
get "/address-search", to: "address_search#index"
get "/address-search/current", to: "address_search#current"
get "/address-search/manual-input/:log_type/:log_id", to: "address_search#manual_input", as: "address_manual_input"
get "/address-search/search-input/:log_type/:log_id", to: "address_search#search_input", as: "address_search_input"
get "collection-resources", to: "collection_resources#index" get "collection-resources", to: "collection_resources#index"
get "/collection-resources/:log_type/:year/:resource_type/download", to: "collection_resources#download_mandatory_collection_resource", as: :download_mandatory_collection_resource get "/collection-resources/:log_type/:year/:resource_type/download", to: "collection_resources#download_mandatory_collection_resource", as: :download_mandatory_collection_resource

6
db/migrate/20250219122817_add_manual_address_entry_selected_to_logs.rb

@ -0,0 +1,6 @@
class AddManualAddressEntrySelectedToLogs < ActiveRecord::Migration[7.2]
def change
add_column :sales_logs, :manual_address_entry_selected, :boolean, default: false
add_column :lettings_logs, :manual_address_entry_selected, :boolean, default: false
end
end

1
db/schema.rb

@ -771,6 +771,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_02_25_180643) do
t.decimal "mrentprestaircasing", precision: 10, scale: 2 t.decimal "mrentprestaircasing", precision: 10, scale: 2
t.datetime "lasttransaction" t.datetime "lasttransaction"
t.datetime "initialpurchase" t.datetime "initialpurchase"
t.boolean "manual_address_entry_selected", default: false
t.index ["assigned_to_id"], name: "index_sales_logs_on_assigned_to_id" t.index ["assigned_to_id"], name: "index_sales_logs_on_assigned_to_id"
t.index ["bulk_upload_id"], name: "index_sales_logs_on_bulk_upload_id" 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 ["created_by_id"], name: "index_sales_logs_on_created_by_id"

9
spec/factories/lettings_log.rb

@ -6,6 +6,7 @@ FactoryBot.define do
managing_organisation { assigned_to.organisation } managing_organisation { assigned_to.organisation }
created_at { Time.zone.today } created_at { Time.zone.today }
updated_at { Time.zone.today } updated_at { Time.zone.today }
manual_address_entry_selected { true }
before(:create) do |log, _evaluator| before(:create) do |log, _evaluator|
if log.period && !log.managing_organisation.organisation_rent_periods.exists?(rent_period: log.period) if log.period && !log.managing_organisation.organisation_rent_periods.exists?(rent_period: log.period)
@ -167,13 +168,11 @@ FactoryBot.define do
town_or_city { Faker::Address.city } town_or_city { Faker::Address.city }
ppcodenk { 1 } ppcodenk { 1 }
tshortfall_known { 1 } tshortfall_known { 1 }
after(:build) do |log, _evaluator| after(:build) do |log, evaluator|
if log.startdate >= Time.zone.local(2024, 4, 1) if log.startdate >= Time.zone.local(2024, 4, 1)
log.address_line1_input = log.address_line1
log.postcode_full_input = log.postcode_full
log.nationality_all_group = 826 log.nationality_all_group = 826
log.uprn = "10033558653" log.uprn = evaluator.uprn || "10033558653"
log.uprn_selection = 1 log.uprn_selection = evaluator.uprn_selection || "10033558653"
end end
end end
end end

10
spec/factories/sales_log.rb

@ -8,6 +8,8 @@ FactoryBot.define do
managing_organisation { assigned_to.organisation } managing_organisation { assigned_to.organisation }
created_at { Time.zone.now } created_at { Time.zone.now }
updated_at { Time.zone.now } updated_at { Time.zone.now }
manual_address_entry_selected { true }
trait :in_progress do trait :in_progress do
purchid { "PC123" } purchid { "PC123" }
ownershipsch { 2 } ownershipsch { 2 }
@ -166,14 +168,12 @@ FactoryBot.define do
nationalbuy2 { 13 } nationalbuy2 { 13 }
buy2living { 3 } buy2living { 3 }
proplen_asked { 1 } proplen_asked { 1 }
after(:build) do |log, _evaluator| after(:build) do |log, evaluator|
if log.saledate >= Time.zone.local(2024, 4, 1) if log.saledate >= Time.zone.local(2024, 4, 1)
log.address_line1_input = log.address_line1
log.postcode_full_input = log.postcode_full
log.nationality_all_group = 826 log.nationality_all_group = 826
log.nationality_all_buyer2_group = 826 log.nationality_all_buyer2_group = 826
log.uprn = "10033558653" log.uprn = evaluator.uprn || "10033558653"
log.uprn_selection = 1 log.uprn_selection = evaluator.uprn_selection || "10033558653"
end end
if log.saledate >= Time.zone.local(2025, 4, 1) if log.saledate >= Time.zone.local(2025, 4, 1)
log.relat2 = "X" if log.relat2 == "C" log.relat2 = "X" if log.relat2 == "C"

45
spec/features/form/address_search_spec.rb

@ -0,0 +1,45 @@
require "rails_helper"
require_relative "helpers"
RSpec.describe "Address Search" do
include Helpers
let(:user) { FactoryBot.create(:user) }
let(:sales_log) do
FactoryBot.create(
:sales_log,
:shared_ownership_setup_complete,
assigned_to: user,
manual_address_entry_selected: false,
)
end
before do
sign_in user
end
context "when using address search feature" do
before do
visit("/sales-logs/#{sales_log.id}/address-search")
end
it "allows searching by a UPRN", js: true do
find("#sales-log-uprn-field").click.native.send_keys("1", "0", "0", "3", "3", "5", "4", "4", "6", "1", "4", :down)
expect(find("#sales-log-uprn-field").value).to eq("10033544614")
end
it "allows searching by address", js: true do
find("#sales-log-uprn-field").click.native.send_keys("S", "W", "1", "5", :down, :enter)
expect(find("#sales-log-uprn-field").value).to eq("SW15")
end
it "displays the placeholder text", js: true do
expect(find("#sales-log-uprn-field")["placeholder"]).to eq("Start typing to search")
end
it "displays correct bottom guidance text" do
find("span.govuk-details__summary-text", text: "Can’t find the address you’re looking for?").click
expect(page).to have_content("Some properties may not be available yet e.g. new builds; you might need to enter them manually instead")
expect(page).to have_content("For UPRN (Unique Property Reference Number), please enter the full value exactly")
end
end
end

60
spec/features/form/form_navigation_spec.rb

@ -186,64 +186,4 @@ RSpec.describe "Form Navigation" do
expect(page).to have_current_path("/lettings-logs/#{id}/duplicate-logs?original_log_id=#{id}") expect(page).to have_current_path("/lettings-logs/#{id}/duplicate-logs?original_log_id=#{id}")
end end
end end
describe "searching for an address" do
let(:now) { Time.zone.local(2024, 5, 1) }
context "with a lettings log" do
let(:lettings_log) { create(:lettings_log, :setup_completed, startdate: Time.zone.local(2024, 5, 5), assigned_to: user) }
before do
stub_request(:get, /api\.os\.uk/)
.to_return(status: 200, body: { results: [{ DPA: { MATCH: 0.9, BUILDING_NAME: "result address line 1", POST_TOWN: "result town or city", POSTCODE: "AA1 1AA", UPRN: "12345" } }] }.to_json, headers: {})
end
it "allows searching for an address" do
visit("lettings-logs/#{id}/address-matcher")
fill_in("lettings-log-address-line1-input-field", with: "address")
fill_in("lettings-log-postcode-full-input-field", with: "A1 1AA")
click_button(text: "Search")
expect(page).to have_current_path("/lettings-logs/#{id}/uprn-selection")
end
it "allows searching for an address from check your answers page" do
visit("lettings-logs/#{id}/address-matcher?referrer=check_answers")
fill_in("lettings-log-address-line1-input-field", with: "address")
fill_in("lettings-log-postcode-full-input-field", with: "A1 1AA")
click_button(text: "Search")
expect(page).to have_current_path("/lettings-logs/#{id}/uprn-selection?referrer=check_answers&unanswered_pages=property_local_authority")
choose("lettings-log-uprn-selection-12345-field", allow_label_click: true)
click_button(text: "Save changes")
expect(page).to have_current_path("/lettings-logs/#{id}/property-information/check-answers")
end
end
context "with a sales log" do
let(:sales_log) { create(:sales_log, :outright_sale_setup_complete, saledate: Time.zone.local(2024, 5, 5), assigned_to: user) }
before do
stub_request(:get, /api\.os\.uk/)
.to_return(status: 200, body: { results: [{ DPA: { MATCH: 0.9, BUILDING_NAME: "result address line 1", POST_TOWN: "result town or city", POSTCODE: "AA1 1AA", UPRN: "12345" } }] }.to_json, headers: {})
end
it "allows searching for an address" do
visit("sales-logs/#{sales_log.id}/address-matcher")
fill_in("sales-log-address-line1-input-field", with: "address")
fill_in("sales-log-postcode-full-input-field", with: "A1 1AA")
click_button(text: "Search")
expect(page).to have_current_path("/sales-logs/#{sales_log.id}/uprn-selection")
end
it "allows searching for an address from check your answers page" do
visit("sales-logs/#{sales_log.id}/address-matcher?referrer=check_answers")
fill_in("sales-log-address-line1-input-field", with: "address")
fill_in("sales-log-postcode-full-input-field", with: "A1 1AA")
click_button(text: "Search")
expect(page).to have_current_path("/sales-logs/#{sales_log.id}/uprn-selection?referrer=check_answers&unanswered_pages=property_local_authority")
choose("sales-log-uprn-selection-12345-field", allow_label_click: true)
click_button(text: "Save changes")
expect(page).to have_current_path("/sales-logs/#{sales_log.id}/property-information/check-answers")
end
end
end
end end

375
spec/features/lettings_log_spec.rb

@ -729,380 +729,5 @@ RSpec.describe "Lettings Log Features" do
expect(duplicate_log.duplicate_set_id).to be_nil expect(duplicate_log.duplicate_set_id).to be_nil
end end
end end
context "when filling out address fields" do
let(:lettings_log) { create(:lettings_log, :setup_completed, assigned_to: user) }
before do
body = {
results: [
{
DPA: {
"POSTCODE": "AA1 1AA",
"POST_TOWN": "Bristol",
"ORGANISATION_NAME": "Some place",
},
},
],
}.to_json
WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key=OS_DATA_KEY&uprn=111")
.to_return(status: 200, body:, headers: {})
body = { results: [{ DPA: { UPRN: "111" } }] }.to_json
WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/find?query=Address+line+1%2C+AA1+1AA&key=OS_DATA_KEY&maxresults=10&minmatch=0.4")
.to_return(status: 200, body:, headers: {})
WebMock.stub_request(:get, "https://api.postcodes.io/postcodes/AA11AA")
.to_return(status: 200, body: "{\"status\":200,\"result\":{\"postcode\":\"AA1 1AA\",\"admin_district\":\"Westminster\",\"codes\":{\"admin_district\":\"E09000033\"}}}", headers: {})
WebMock.stub_request(:get, "https://api.postcodes.io/postcodes/AA12AA")
.to_return(status: 200, body: "{\"status\":200,\"result\":{\"postcode\":\"AA1 2AA\",\"admin_district\":\"Wigan\",\"codes\":{\"admin_district\":\"E08000010\"}}}", headers: {})
body = { results: [] }.to_json
WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/find?query=Address+line+1%2C+AA1+1AB&key=OS_DATA_KEY&maxresults=10&minmatch=0.4")
.to_return(status: 200, body:, headers: {})
visit("/lettings-logs/#{lettings_log.id}/uprn")
end
context "and uprn is known and answered" do
before do
choose "Yes"
fill_in("lettings_log[uprn]", with: "111")
click_button("Save and continue")
end
context "and uprn is confirmed" do
it "sets correct address fields" do
lettings_log.reload
expect(lettings_log.uprn_known).to eq(1) # yes
expect(lettings_log.uprn).to eq("111")
expect(lettings_log.uprn_confirmed).to eq(nil)
expect(lettings_log.uprn_selection).to eq(nil)
expect(lettings_log.postcode_known).to eq(1)
expect(lettings_log.postcode_full).to eq("AA1 1AA")
expect(lettings_log.address_line1).to eq("Some Place")
expect(lettings_log.address_line2).to eq(nil)
expect(lettings_log.town_or_city).to eq("Bristol")
expect(lettings_log.address_line1_input).to eq(nil)
expect(lettings_log.postcode_full_input).to eq(nil)
expect(lettings_log.address_search_value_check).to eq(nil)
expect(lettings_log.la).to eq("E09000033")
choose "Yes"
click_button("Save and continue")
lettings_log.reload
expect(lettings_log.uprn_known).to eq(1) # yes
expect(lettings_log.uprn).to eq("111")
expect(lettings_log.uprn_confirmed).to eq(1) # yes
expect(lettings_log.uprn_selection).to eq(nil)
expect(lettings_log.postcode_known).to eq(1)
expect(lettings_log.postcode_full).to eq("AA1 1AA")
expect(lettings_log.address_line1).to eq("Some Place")
expect(lettings_log.address_line2).to eq(nil)
expect(lettings_log.town_or_city).to eq("Bristol")
expect(lettings_log.address_line1_input).to eq(nil)
expect(lettings_log.postcode_full_input).to eq(nil)
expect(lettings_log.address_search_value_check).to eq(nil)
expect(lettings_log.la).to eq("E09000033")
end
context "and changes to uprn not known" do
it "sets correct address fields" do
visit("/lettings-logs/#{lettings_log.id}/uprn")
choose "No"
click_button("Save and continue")
lettings_log.reload
expect(lettings_log.uprn_known).to eq(0) # no
expect(lettings_log.uprn).to eq(nil)
expect(lettings_log.uprn_confirmed).to eq(nil)
expect(lettings_log.uprn_selection).to eq(nil)
expect(lettings_log.postcode_known).to eq(nil)
expect(lettings_log.postcode_full).to eq(nil)
expect(lettings_log.address_line1).to eq(nil)
expect(lettings_log.address_line2).to eq(nil)
expect(lettings_log.town_or_city).to eq(nil)
expect(lettings_log.address_line1_input).to eq(nil)
expect(lettings_log.postcode_full_input).to eq(nil)
expect(lettings_log.address_search_value_check).to eq(nil)
expect(lettings_log.la).to eq(nil)
end
end
end
context "and uprn is not confirmed" do
before do
choose "No, I want to search for the address instead"
click_button("Save and continue")
end
it "sets correct address fields" do
lettings_log.reload
expect(lettings_log.uprn_known).to eq(0) # no
expect(lettings_log.uprn).to eq(nil)
expect(lettings_log.uprn_confirmed).to eq(nil)
expect(lettings_log.uprn_selection).to eq(nil)
expect(lettings_log.postcode_known).to eq(nil)
expect(lettings_log.postcode_full).to eq(nil)
expect(lettings_log.address_line1).to eq(nil)
expect(lettings_log.address_line2).to eq(nil)
expect(lettings_log.town_or_city).to eq(nil)
expect(lettings_log.address_line1_input).to eq(nil)
expect(lettings_log.postcode_full_input).to eq(nil)
expect(lettings_log.address_search_value_check).to eq(nil)
expect(lettings_log.la).to eq(nil)
end
end
end
context "and uprn is not known" do
before do
choose "No"
click_button("Save and continue")
end
it "sets correct address fields" do
lettings_log.reload
expect(lettings_log.uprn_known).to eq(0) # no
expect(lettings_log.uprn).to eq(nil)
expect(lettings_log.uprn_confirmed).to eq(nil)
expect(lettings_log.uprn_selection).to eq(nil)
expect(lettings_log.postcode_known).to eq(nil)
expect(lettings_log.postcode_full).to eq(nil)
expect(lettings_log.address_line1).to eq(nil)
expect(lettings_log.address_line2).to eq(nil)
expect(lettings_log.town_or_city).to eq(nil)
expect(lettings_log.address_line1_input).to eq(nil)
expect(lettings_log.postcode_full_input).to eq(nil)
expect(lettings_log.address_search_value_check).to eq(nil)
expect(lettings_log.la).to eq(nil)
end
context "and the address is not found" do
it "sets correct address fields" do
fill_in("lettings_log[address_line1_input]", with: "Address line 1")
fill_in("lettings_log[postcode_full_input]", with: "AA1 1AB")
click_button("Search")
lettings_log.reload
expect(lettings_log.uprn_known).to eq(0) # no
expect(lettings_log.uprn).to eq(nil)
expect(lettings_log.uprn_confirmed).to eq(nil)
expect(lettings_log.uprn_selection).to eq(nil)
expect(lettings_log.postcode_known).to eq(nil)
expect(lettings_log.postcode_full).to eq(nil)
expect(lettings_log.address_line1).to eq(nil)
expect(lettings_log.address_line2).to eq(nil)
expect(lettings_log.town_or_city).to eq(nil)
expect(lettings_log.address_line1_input).to eq("Address line 1")
expect(lettings_log.postcode_full_input).to eq("AA1 1AB")
expect(lettings_log.address_search_value_check).to eq(nil)
expect(lettings_log.la).to eq(nil)
click_button("Confirm and continue")
lettings_log.reload
expect(lettings_log.uprn_known).to eq(0) # no
expect(lettings_log.uprn).to eq(nil)
expect(lettings_log.uprn_confirmed).to eq(nil)
expect(lettings_log.uprn_selection).to eq(nil)
expect(lettings_log.postcode_known).to eq(nil)
expect(lettings_log.postcode_full).to eq(nil)
expect(lettings_log.address_line1).to eq(nil)
expect(lettings_log.address_line2).to eq(nil)
expect(lettings_log.town_or_city).to eq(nil)
expect(lettings_log.address_line1_input).to eq("Address line 1")
expect(lettings_log.postcode_full_input).to eq("AA1 1AB")
expect(lettings_log.address_search_value_check).to eq(0)
expect(lettings_log.la).to eq(nil)
end
end
context "and address is found, re-searched and not found" do
before do
fill_in("lettings_log[address_line1_input]", with: "Address line 1")
fill_in("lettings_log[postcode_full_input]", with: "AA1 1AA")
click_button("Search")
visit("/lettings-logs/#{lettings_log.id}/address-matcher")
fill_in("lettings_log[address_line1_input]", with: "Address line 1")
fill_in("lettings_log[postcode_full_input]", with: "AA1 1AB")
click_button("Search")
end
it "routes to the correct page" do
expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/no-address-found")
end
end
context "and the user selects 'address_not_listed'" do
before do
fill_in("lettings_log[address_line1_input]", with: "Address line 1")
fill_in("lettings_log[postcode_full_input]", with: "AA1 1AA")
click_button("Search")
choose "The address is not listed, I want to enter the address manually"
click_button("Save and continue")
end
it "sets correct address fields" do
lettings_log.reload
expect(lettings_log.uprn_known).to eq(0) # no
expect(lettings_log.uprn).to eq(nil)
expect(lettings_log.uprn_confirmed).to eq(nil)
expect(lettings_log.uprn_selection).to eq("uprn_not_listed")
expect(lettings_log.postcode_known).to eq(1)
expect(lettings_log.postcode_full).to eq("AA1 1AA")
expect(lettings_log.address_line1).to eq("Address line 1")
expect(lettings_log.address_line2).to eq(nil)
expect(lettings_log.town_or_city).to eq(nil)
expect(lettings_log.address_line1_input).to eq("Address line 1")
expect(lettings_log.postcode_full_input).to eq("AA1 1AA")
expect(lettings_log.address_search_value_check).to eq(nil)
expect(lettings_log.la).to eq("E09000033")
end
context "and the user enters a new address manually" do
context "without changing a valid postcode" do
before do
fill_in("lettings_log[town_or_city]", with: "Town")
click_button("Save and continue")
end
it "sets correct address fields" do
lettings_log.reload
expect(lettings_log.uprn_known).to eq(0) # no
expect(lettings_log.uprn).to eq(nil)
expect(lettings_log.uprn_confirmed).to eq(nil)
expect(lettings_log.uprn_selection).to eq("uprn_not_listed")
expect(lettings_log.postcode_known).to eq(1)
expect(lettings_log.postcode_full).to eq("AA1 1AA")
expect(lettings_log.address_line1).to eq("Address line 1")
expect(lettings_log.address_line2).to eq("")
expect(lettings_log.town_or_city).to eq("Town")
expect(lettings_log.address_line1_input).to eq("Address line 1")
expect(lettings_log.postcode_full_input).to eq("AA1 1AA")
expect(lettings_log.address_search_value_check).to eq(nil)
expect(lettings_log.la).to eq("E09000033")
end
end
context "with changing the postcode" do
before do
fill_in("lettings_log[town_or_city]", with: "Town")
fill_in("lettings_log[postcode_full]", with: "AA12AA")
click_button("Save and continue")
end
it "sets correct address fields" do
lettings_log.reload
expect(lettings_log.uprn_known).to eq(0) # no
expect(lettings_log.uprn).to eq(nil)
expect(lettings_log.uprn_confirmed).to eq(nil)
expect(lettings_log.uprn_selection).to eq("uprn_not_listed")
expect(lettings_log.postcode_known).to eq(1)
expect(lettings_log.postcode_full).to eq("AA1 2AA")
expect(lettings_log.address_line1).to eq("Address line 1")
expect(lettings_log.address_line2).to eq("")
expect(lettings_log.town_or_city).to eq("Town")
expect(lettings_log.address_line1_input).to eq("Address line 1")
expect(lettings_log.postcode_full_input).to eq("AA1 1AA")
expect(lettings_log.address_search_value_check).to eq(nil)
expect(lettings_log.la).to eq("E08000010")
end
end
end
end
context "and the user selects 'address_not_listed' when partial postcode is entered" do
before do
fill_in("lettings_log[address_line1_input]", with: "Address line 1")
fill_in("lettings_log[postcode_full_input]", with: "AA1")
click_button("Search")
choose "The address is not listed, I want to enter the address manually"
click_button("Save and continue")
end
it "sets correct address fields" do
lettings_log.reload
expect(lettings_log.uprn_known).to eq(0) # no
expect(lettings_log.uprn).to eq(nil)
expect(lettings_log.uprn_confirmed).to eq(nil)
expect(lettings_log.uprn_selection).to eq("uprn_not_listed")
expect(lettings_log.postcode_known).to eq(nil)
expect(lettings_log.postcode_full).to eq(nil)
expect(lettings_log.address_line1).to eq("Address line 1")
expect(lettings_log.address_line2).to eq(nil)
expect(lettings_log.town_or_city).to eq(nil)
expect(lettings_log.address_line1_input).to eq("Address line 1")
expect(lettings_log.postcode_full_input).to eq("AA1")
expect(lettings_log.address_search_value_check).to eq(nil)
expect(lettings_log.la).to eq(nil)
end
end
context "and the user selects 'address_not_listed' and then changes their mind and selects an address" do
before do
fill_in("lettings_log[address_line1_input]", with: "Address line 1")
fill_in("lettings_log[postcode_full_input]", with: "AA1 1AA")
click_button("Search")
choose "The address is not listed, I want to enter the address manually"
click_button("Save and continue")
visit("/lettings-logs/#{lettings_log.id}/uprn-selection")
choose("lettings-log-uprn-selection-111-field", allow_label_click: true)
click_button("Save and continue")
end
it "sets correct address fields" do
lettings_log.reload
expect(lettings_log.uprn_known).to eq(1)
expect(lettings_log.uprn).to eq("111")
expect(lettings_log.uprn_confirmed).to eq(1)
expect(lettings_log.uprn_selection).to eq(nil)
expect(lettings_log.postcode_known).to eq(1)
expect(lettings_log.postcode_full).to eq("AA1 1AA")
expect(lettings_log.address_line1).to eq("Some Place")
expect(lettings_log.address_line2).to eq(nil)
expect(lettings_log.town_or_city).to eq("Bristol")
expect(lettings_log.address_line1_input).to eq("Address line 1")
expect(lettings_log.postcode_full_input).to eq("AA1 1AA")
expect(lettings_log.address_search_value_check).to eq(nil)
expect(lettings_log.la).to eq("E09000033")
end
end
context "and possible addresses found and selected" do
before do
fill_in("lettings_log[address_line1_input]", with: "Address line 1")
fill_in("lettings_log[postcode_full_input]", with: "AA1 1AA")
click_button("Search")
choose("lettings-log-uprn-selection-111-field", allow_label_click: true)
click_button("Save and continue")
end
it "sets correct address fields" do
lettings_log.reload
expect(lettings_log.uprn_known).to eq(1)
expect(lettings_log.uprn).to eq("111")
expect(lettings_log.uprn_confirmed).to eq(1)
expect(lettings_log.uprn_selection).to eq(nil)
expect(lettings_log.postcode_known).to eq(1)
expect(lettings_log.postcode_full).to eq("AA1 1AA")
expect(lettings_log.address_line1).to eq("Some Place")
expect(lettings_log.address_line2).to eq(nil)
expect(lettings_log.town_or_city).to eq("Bristol")
expect(lettings_log.address_line1_input).to eq("Address line 1")
expect(lettings_log.postcode_full_input).to eq("AA1 1AA")
expect(lettings_log.address_search_value_check).to eq(nil)
expect(lettings_log.la).to eq("E09000033")
end
end
end
end
end end
end end

375
spec/features/sales_log_spec.rb

@ -334,381 +334,6 @@ RSpec.describe "Sales Log Features" do
expect(page).to have_current_path("/sales-logs/bulk-uploads") expect(page).to have_current_path("/sales-logs/bulk-uploads")
end end
end end
context "when filling out address fields" do
let(:sales_log) { create(:sales_log, :shared_ownership_setup_complete, assigned_to: user) }
before do
body = {
results: [
{
DPA: {
"POSTCODE": "AA1 1AA",
"POST_TOWN": "Bristol",
"ORGANISATION_NAME": "Some place",
},
},
],
}.to_json
WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key=OS_DATA_KEY&uprn=111")
.to_return(status: 200, body:, headers: {})
body = { results: [{ DPA: { UPRN: "111" } }] }.to_json
WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/find?query=Address+line+1%2C+AA1+1AA&key=OS_DATA_KEY&maxresults=10&minmatch=0.4")
.to_return(status: 200, body:, headers: {})
WebMock.stub_request(:get, "https://api.postcodes.io/postcodes/AA11AA")
.to_return(status: 200, body: "{\"status\":200,\"result\":{\"postcode\":\"AA1 1AA\",\"admin_district\":\"Westminster\",\"codes\":{\"admin_district\":\"E09000033\"}}}", headers: {})
WebMock.stub_request(:get, "https://api.postcodes.io/postcodes/AA12AA")
.to_return(status: 200, body: "{\"status\":200,\"result\":{\"postcode\":\"AA1 2AA\",\"admin_district\":\"Wigan\",\"codes\":{\"admin_district\":\"E08000010\"}}}", headers: {})
body = { results: [] }.to_json
WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/find?query=Address+line+1%2C+AA1+1AB&key=OS_DATA_KEY&maxresults=10&minmatch=0.4")
.to_return(status: 200, body:, headers: {})
visit("/sales-logs/#{sales_log.id}/uprn")
end
context "and uprn is known and answered" do
before do
choose "Yes"
fill_in("sales_log[uprn]", with: "111")
click_button("Save and continue")
end
context "and uprn is confirmed" do
it "sets correct address fields" do
sales_log.reload
expect(sales_log.uprn_known).to eq(1) # yes
expect(sales_log.uprn).to eq("111")
expect(sales_log.uprn_confirmed).to eq(nil)
expect(sales_log.uprn_selection).to eq(nil)
expect(sales_log.pcodenk).to eq(0)
expect(sales_log.postcode_full).to eq("AA1 1AA")
expect(sales_log.address_line1).to eq("Some Place")
expect(sales_log.address_line2).to eq(nil)
expect(sales_log.town_or_city).to eq("Bristol")
expect(sales_log.address_line1_input).to eq(nil)
expect(sales_log.postcode_full_input).to eq(nil)
expect(sales_log.address_search_value_check).to eq(nil)
expect(sales_log.la).to eq("E09000033")
choose "Yes"
click_button("Save and continue")
sales_log.reload
expect(sales_log.uprn_known).to eq(1) # yes
expect(sales_log.uprn).to eq("111")
expect(sales_log.uprn_confirmed).to eq(1) # yes
expect(sales_log.uprn_selection).to eq(nil)
expect(sales_log.pcodenk).to eq(0)
expect(sales_log.postcode_full).to eq("AA1 1AA")
expect(sales_log.address_line1).to eq("Some Place")
expect(sales_log.address_line2).to eq(nil)
expect(sales_log.town_or_city).to eq("Bristol")
expect(sales_log.address_line1_input).to eq(nil)
expect(sales_log.postcode_full_input).to eq(nil)
expect(sales_log.address_search_value_check).to eq(nil)
expect(sales_log.la).to eq("E09000033")
end
context "and changes to uprn not known" do
it "sets correct address fields" do
visit("/sales-logs/#{sales_log.id}/uprn")
choose "No"
click_button("Save and continue")
sales_log.reload
expect(sales_log.uprn_known).to eq(0) # no
expect(sales_log.uprn).to eq(nil)
expect(sales_log.uprn_confirmed).to eq(nil)
expect(sales_log.uprn_selection).to eq(nil)
expect(sales_log.pcodenk).to eq(nil)
expect(sales_log.postcode_full).to eq(nil)
expect(sales_log.address_line1).to eq(nil)
expect(sales_log.address_line2).to eq(nil)
expect(sales_log.town_or_city).to eq(nil)
expect(sales_log.address_line1_input).to eq(nil)
expect(sales_log.postcode_full_input).to eq(nil)
expect(sales_log.address_search_value_check).to eq(nil)
expect(sales_log.la).to eq(nil)
end
end
end
context "and uprn is not confirmed" do
before do
choose "No, I want to search for the address instead"
click_button("Save and continue")
end
it "sets correct address fields" do
sales_log.reload
expect(sales_log.uprn_known).to eq(0) # no
expect(sales_log.uprn).to eq(nil)
expect(sales_log.uprn_confirmed).to eq(nil)
expect(sales_log.uprn_selection).to eq(nil)
expect(sales_log.pcodenk).to eq(nil)
expect(sales_log.postcode_full).to eq(nil)
expect(sales_log.address_line1).to eq(nil)
expect(sales_log.address_line2).to eq(nil)
expect(sales_log.town_or_city).to eq(nil)
expect(sales_log.address_line1_input).to eq(nil)
expect(sales_log.postcode_full_input).to eq(nil)
expect(sales_log.address_search_value_check).to eq(nil)
expect(sales_log.la).to eq(nil)
end
end
end
context "and uprn is not known" do
before do
choose "No"
click_button("Save and continue")
end
it "sets correct address fields" do
sales_log.reload
expect(sales_log.uprn_known).to eq(0) # no
expect(sales_log.uprn).to eq(nil)
expect(sales_log.uprn_confirmed).to eq(nil)
expect(sales_log.uprn_selection).to eq(nil)
expect(sales_log.pcodenk).to eq(nil)
expect(sales_log.postcode_full).to eq(nil)
expect(sales_log.address_line1).to eq(nil)
expect(sales_log.address_line2).to eq(nil)
expect(sales_log.town_or_city).to eq(nil)
expect(sales_log.address_line1_input).to eq(nil)
expect(sales_log.postcode_full_input).to eq(nil)
expect(sales_log.address_search_value_check).to eq(nil)
expect(sales_log.la).to eq(nil)
end
context "and the address is not found" do
it "sets correct address fields" do
fill_in("sales_log[address_line1_input]", with: "Address line 1")
fill_in("sales_log[postcode_full_input]", with: "AA1 1AB")
click_button("Search")
sales_log.reload
expect(sales_log.uprn_known).to eq(0) # no
expect(sales_log.uprn).to eq(nil)
expect(sales_log.uprn_confirmed).to eq(nil)
expect(sales_log.uprn_selection).to eq(nil)
expect(sales_log.pcodenk).to eq(nil)
expect(sales_log.postcode_full).to eq(nil)
expect(sales_log.address_line1).to eq(nil)
expect(sales_log.address_line2).to eq(nil)
expect(sales_log.town_or_city).to eq(nil)
expect(sales_log.address_line1_input).to eq("Address line 1")
expect(sales_log.postcode_full_input).to eq("AA1 1AB")
expect(sales_log.address_search_value_check).to eq(nil)
expect(sales_log.la).to eq(nil)
click_button("Confirm and continue")
sales_log.reload
expect(sales_log.uprn_known).to eq(0) # no
expect(sales_log.uprn).to eq(nil)
expect(sales_log.uprn_confirmed).to eq(nil)
expect(sales_log.uprn_selection).to eq(nil)
expect(sales_log.pcodenk).to eq(nil)
expect(sales_log.postcode_full).to eq(nil)
expect(sales_log.address_line1).to eq(nil)
expect(sales_log.address_line2).to eq(nil)
expect(sales_log.town_or_city).to eq(nil)
expect(sales_log.address_line1_input).to eq("Address line 1")
expect(sales_log.postcode_full_input).to eq("AA1 1AB")
expect(sales_log.address_search_value_check).to eq(0)
expect(sales_log.la).to eq(nil)
end
end
context "and address is found, re-searched and not found" do
before do
fill_in("sales_log[address_line1_input]", with: "Address line 1")
fill_in("sales_log[postcode_full_input]", with: "AA1 1AA")
click_button("Search")
visit("/sales-logs/#{sales_log.id}/address-matcher")
fill_in("sales_log[address_line1_input]", with: "Address line 1")
fill_in("sales_log[postcode_full_input]", with: "AA1 1AB")
click_button("Search")
end
it "routes to the correct page" do
expect(page).to have_current_path("/sales-logs/#{sales_log.id}/no-address-found")
end
end
context "and the user selects 'address_not_listed'" do
before do
fill_in("sales_log[address_line1_input]", with: "Address line 1")
fill_in("sales_log[postcode_full_input]", with: "AA1 1AA")
click_button("Search")
choose "The address is not listed, I want to enter the address manually"
click_button("Save and continue")
end
it "sets correct address fields" do
sales_log.reload
expect(sales_log.uprn_known).to eq(0) # no
expect(sales_log.uprn).to eq(nil)
expect(sales_log.uprn_confirmed).to eq(nil)
expect(sales_log.uprn_selection).to eq("uprn_not_listed")
expect(sales_log.pcodenk).to eq(0)
expect(sales_log.postcode_full).to eq("AA1 1AA")
expect(sales_log.address_line1).to eq("Address line 1")
expect(sales_log.address_line2).to eq(nil)
expect(sales_log.town_or_city).to eq(nil)
expect(sales_log.address_line1_input).to eq("Address line 1")
expect(sales_log.postcode_full_input).to eq("AA1 1AA")
expect(sales_log.address_search_value_check).to eq(nil)
expect(sales_log.la).to eq("E09000033")
end
context "and the user enters a new address manually" do
context "without changing a valid postcode" do
before do
fill_in("sales_log[town_or_city]", with: "Town")
click_button("Save and continue")
end
it "sets correct address fields" do
sales_log.reload
expect(sales_log.uprn_known).to eq(0) # no
expect(sales_log.uprn).to eq(nil)
expect(sales_log.uprn_confirmed).to eq(nil)
expect(sales_log.uprn_selection).to eq("uprn_not_listed")
expect(sales_log.pcodenk).to eq(0)
expect(sales_log.postcode_full).to eq("AA1 1AA")
expect(sales_log.address_line1).to eq("Address line 1")
expect(sales_log.address_line2).to eq("")
expect(sales_log.town_or_city).to eq("Town")
expect(sales_log.address_line1_input).to eq("Address line 1")
expect(sales_log.postcode_full_input).to eq("AA1 1AA")
expect(sales_log.address_search_value_check).to eq(nil)
expect(sales_log.la).to eq("E09000033")
end
end
context "with changing the postcode" do
before do
fill_in("sales_log[town_or_city]", with: "Town")
fill_in("sales_log[postcode_full]", with: "AA12AA")
click_button("Save and continue")
end
it "sets correct address fields" do
sales_log.reload
expect(sales_log.uprn_known).to eq(0) # no
expect(sales_log.uprn).to eq(nil)
expect(sales_log.uprn_confirmed).to eq(nil)
expect(sales_log.uprn_selection).to eq("uprn_not_listed")
expect(sales_log.pcodenk).to eq(0)
expect(sales_log.postcode_full).to eq("AA1 2AA")
expect(sales_log.address_line1).to eq("Address line 1")
expect(sales_log.address_line2).to eq("")
expect(sales_log.town_or_city).to eq("Town")
expect(sales_log.address_line1_input).to eq("Address line 1")
expect(sales_log.postcode_full_input).to eq("AA1 1AA")
expect(sales_log.address_search_value_check).to eq(nil)
expect(sales_log.la).to eq("E08000010")
end
end
end
end
context "and the user selects 'address_not_listed' when partial postcode is given" do
before do
fill_in("sales_log[address_line1_input]", with: "Address line 1")
fill_in("sales_log[postcode_full_input]", with: "1AA")
click_button("Search")
choose "The address is not listed, I want to enter the address manually"
click_button("Save and continue")
end
it "sets correct address fields" do
sales_log.reload
expect(sales_log.uprn_known).to eq(0) # no
expect(sales_log.uprn).to eq(nil)
expect(sales_log.uprn_confirmed).to eq(nil)
expect(sales_log.uprn_selection).to eq("uprn_not_listed")
expect(sales_log.pcodenk).to eq(nil)
expect(sales_log.postcode_full).to eq(nil)
expect(sales_log.address_line1).to eq("Address line 1")
expect(sales_log.address_line2).to eq(nil)
expect(sales_log.town_or_city).to eq(nil)
expect(sales_log.address_line1_input).to eq("Address line 1")
expect(sales_log.postcode_full_input).to eq("1AA")
expect(sales_log.address_search_value_check).to eq(nil)
expect(sales_log.la).to eq(nil)
end
end
context "and the user selects 'address_not_listed' and then changes their mind and selects an address" do
before do
fill_in("sales_log[address_line1_input]", with: "Address line 1")
fill_in("sales_log[postcode_full_input]", with: "AA1 1AA")
click_button("Search")
choose "The address is not listed, I want to enter the address manually"
click_button("Save and continue")
visit("/sales-logs/#{sales_log.id}/uprn-selection")
choose("sales-log-uprn-selection-111-field", allow_label_click: true)
click_button("Save and continue")
end
it "sets correct address fields" do
sales_log.reload
expect(sales_log.uprn_known).to eq(1)
expect(sales_log.uprn).to eq("111")
expect(sales_log.uprn_confirmed).to eq(1)
expect(sales_log.uprn_selection).to eq(nil)
expect(sales_log.pcodenk).to eq(0)
expect(sales_log.postcode_full).to eq("AA1 1AA")
expect(sales_log.address_line1).to eq("Some Place")
expect(sales_log.address_line2).to eq(nil)
expect(sales_log.town_or_city).to eq("Bristol")
expect(sales_log.address_line1_input).to eq("Address line 1")
expect(sales_log.postcode_full_input).to eq("AA1 1AA")
expect(sales_log.address_search_value_check).to eq(nil)
expect(sales_log.la).to eq("E09000033")
end
end
context "and possible addresses found and selected" do
before do
fill_in("sales_log[address_line1_input]", with: "Address line 1")
fill_in("sales_log[postcode_full_input]", with: "AA1 1AA")
click_button("Search")
choose("sales-log-uprn-selection-111-field", allow_label_click: true)
click_button("Save and continue")
end
it "sets correct address fields" do
sales_log.reload
expect(sales_log.uprn_known).to eq(1)
expect(sales_log.uprn).to eq("111")
expect(sales_log.uprn_confirmed).to eq(1)
expect(sales_log.uprn_selection).to eq(nil)
expect(sales_log.pcodenk).to eq(0)
expect(sales_log.postcode_full).to eq("AA1 1AA")
expect(sales_log.address_line1).to eq("Some Place")
expect(sales_log.address_line2).to eq(nil)
expect(sales_log.town_or_city).to eq("Bristol")
expect(sales_log.address_line1_input).to eq("Address line 1")
expect(sales_log.postcode_full_input).to eq("AA1 1AA")
expect(sales_log.address_search_value_check).to eq(nil)
expect(sales_log.la).to eq("E09000033")
end
end
end
end
end end
context "when a log becomes a duplicate" do context "when a log becomes a duplicate" do

2
spec/fixtures/files/sales_logs_csv_export_codes_24.csv vendored

File diff suppressed because one or more lines are too long

2
spec/fixtures/files/sales_logs_csv_export_labels_24.csv vendored

File diff suppressed because one or more lines are too long

2
spec/fixtures/files/sales_logs_csv_export_non_support_labels_24.csv vendored

File diff suppressed because one or more lines are too long

4
spec/models/bulk_upload_spec.rb

@ -24,7 +24,7 @@ RSpec.describe BulkUpload, type: :model do
let(:log) { build(:lettings_log, startdate: Time.zone.local(2025, 4, 2), bulk_upload:) } let(:log) { build(:lettings_log, startdate: Time.zone.local(2025, 4, 2), bulk_upload:) }
it "has the correct number of value checks to be set as confirmed" do it "has the correct number of value checks to be set as confirmed" do
expect(bulk_upload.fields_to_confirm(log)).to match_array %w[rent_value_check void_date_value_check major_repairs_date_value_check pregnancy_value_check retirement_value_check referral_value_check net_income_value_check scharge_value_check pscharge_value_check supcharg_value_check address_search_value_check multiple_partners_value_check partner_under_16_value_check reasonother_value_check] expect(bulk_upload.fields_to_confirm(log)).to match_array %w[rent_value_check void_date_value_check major_repairs_date_value_check pregnancy_value_check retirement_value_check referral_value_check net_income_value_check scharge_value_check pscharge_value_check supcharg_value_check multiple_partners_value_check partner_under_16_value_check reasonother_value_check]
end end
end end
@ -32,7 +32,7 @@ RSpec.describe BulkUpload, type: :model do
let(:log) { build(:sales_log, :saledate_today, bulk_upload:) } let(:log) { build(:sales_log, :saledate_today, bulk_upload:) }
it "has the correct number of value checks to be set as confirmed" do it "has the correct number of value checks to be set as confirmed" do
expect(bulk_upload.fields_to_confirm(log)).to match_array %w[value_value_check monthly_charges_value_check percentage_discount_value_check income1_value_check income2_value_check combined_income_value_check retirement_value_check old_persons_shared_ownership_value_check buyer_livein_value_check student_not_child_value_check wheel_value_check mortgage_value_check savings_value_check deposit_value_check staircase_bought_value_check stairowned_value_check hodate_check shared_ownership_deposit_value_check extrabor_value_check grant_value_check discounted_sale_value_check deposit_and_mortgage_value_check address_search_value_check multiple_partners_value_check partner_under_16_value_check] expect(bulk_upload.fields_to_confirm(log)).to match_array %w[value_value_check monthly_charges_value_check percentage_discount_value_check income1_value_check income2_value_check combined_income_value_check retirement_value_check old_persons_shared_ownership_value_check buyer_livein_value_check student_not_child_value_check wheel_value_check mortgage_value_check savings_value_check deposit_value_check staircase_bought_value_check stairowned_value_check hodate_check shared_ownership_deposit_value_check extrabor_value_check grant_value_check discounted_sale_value_check deposit_and_mortgage_value_check multiple_partners_value_check partner_under_16_value_check]
end end
end end
end end

9
spec/models/form/lettings/pages/address_fallback_spec.rb

@ -24,13 +24,6 @@ RSpec.describe Form::Lettings::Pages::AddressFallback, type: :model do
end end
it "has correct depends_on" do it "has correct depends_on" do
expect(page.depends_on).to eq([ expect(page.depends_on).to eq([{ "manual_address_entry_selected" => true, "is_supported_housing?" => false }])
{ "is_supported_housing?" => false, "uprn_known" => nil, "uprn_selection" => "uprn_not_listed" },
{ "is_supported_housing?" => false, "uprn_known" => 0, "uprn_selection" => "uprn_not_listed" },
{ "is_supported_housing?" => false, "uprn_confirmed" => 0, "uprn_selection" => "uprn_not_listed" },
{ "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 end
end end

42
spec/models/form/lettings/pages/address_search_spec.rb

@ -0,0 +1,42 @@
require "rails_helper"
RSpec.describe Form::Lettings::Pages::AddressSearch, type: :model do
subject(:page) { described_class.new(page_id, page_definition, subsection) }
let(:page_id) { nil }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date:)) }
let(:start_date) { Time.utc(2024, 4, 1) }
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[uprn])
end
it "has the correct id" do
expect(page.id).to eq("address_search")
end
it "has the correct description" do
expect(page.description).to be_nil
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "is_supported_housing?" => false, "manual_address_entry_selected" => false }])
end
it "has the correct question number" do
expect(page.question_number).to eq(12)
end
context "with 2025/26 form" do
let(:start_date) { Time.utc(2025, 4, 1) }
it "has the correct question number" do
expect(page.question_number).to eq(16)
end
end
end

68
spec/models/form/lettings/questions/address_search_spec.rb

@ -0,0 +1,68 @@
require "rails_helper"
RSpec.describe Form::Lettings::Questions::AddressSearch, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date:))) }
let(:start_date) { Time.utc(2024, 4, 1) }
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("uprn")
end
it "has the correct type" do
expect(question.type).to eq("address_search")
end
it "has the correct question number" do
expect(question.question_number).to eq(12)
end
context "with 2025/26 form" do
let(:start_date) { Time.utc(2025, 4, 1) }
it "has the correct question number" do
expect(question.question_number).to eq(16)
end
end
describe "get_extra_check_answer_value" do
context "when address is not present" do
let(:log) { build(:lettings_log, manual_address_entry_selected: false) }
it "returns nil" do
expect(question.get_extra_check_answer_value(log)).to be_nil
end
end
context "when address search is present" do
let(:log) do
build(
:lettings_log,
:completed,
address_line1: "19, Charlton Gardens",
town_or_city: "Bristol",
postcode_full: "BS10 6LU",
la: "E06000023",
uprn_known: 1,
uprn: 107,
uprn_confirmed: 1,
)
end
context "when uprn known" do
it "returns formatted value" do
expect(question.get_extra_check_answer_value(log)).to eq(
"\n\n19, Charlton Gardens\nBristol\nBS10 6LU\nBristol, City of",
)
end
end
end
end
end

8
spec/models/form/lettings/questions/uprn_selection_spec.rb

@ -90,9 +90,10 @@ RSpec.describe Form::Lettings::Questions::UprnSelection, type: :model do
context "when the log has address line 1 input only" do context "when the log has address line 1 input only" do
before do before do
allow(address_client_instance).to receive(:result).and_return(nil)
log.address_line1_input = "Address line 1" log.address_line1_input = "Address line 1"
log.postcode_full_input = nil log.postcode_full_input = nil
log.save!(valudate: false) log.save!(validate: false)
end end
it "has the correct input_playback" do it "has the correct input_playback" do
@ -102,9 +103,10 @@ RSpec.describe Form::Lettings::Questions::UprnSelection, type: :model do
context "when the log has postcode input only" do context "when the log has postcode input only" do
before do before do
allow(address_client_instance).to receive(:result).and_return(nil)
log.address_line1_input = nil log.address_line1_input = nil
log.postcode_full_input = "A1 1AA" log.postcode_full_input = "A1 1AA"
log.save!(valudate: false) log.save!(validate: false)
end end
it "has the correct input_playback" do it "has the correct input_playback" do
@ -116,7 +118,7 @@ RSpec.describe Form::Lettings::Questions::UprnSelection, type: :model do
before do before do
log.address_line1_input = "Address line 1" log.address_line1_input = "Address line 1"
log.postcode_full_input = "A1 1AA" log.postcode_full_input = "A1 1AA"
log.save!(valudate: false) log.save!(validate: false)
end end
it "has the correct input_playback" do it "has the correct input_playback" do

4
spec/models/form/lettings/questions/uprn_spec.rb

@ -52,12 +52,14 @@ RSpec.describe Form::Lettings::Questions::Uprn, type: :model do
la: "E09000003", la: "E09000003",
uprn_known:, uprn_known:,
uprn:, uprn:,
manual_address_entry_selected:,
) )
end end
context "when uprn known nil" do context "when uprn known nil" do
let(:uprn_known) { nil } let(:uprn_known) { nil }
let(:uprn) { nil } let(:uprn) { nil }
let(:manual_address_entry_selected) { true }
it "returns formatted value" do it "returns formatted value" do
expect(question.get_extra_check_answer_value(log)).to be_nil expect(question.get_extra_check_answer_value(log)).to be_nil
@ -67,6 +69,7 @@ RSpec.describe Form::Lettings::Questions::Uprn, type: :model do
context "when uprn known" do context "when uprn known" do
let(:uprn_known) { 1 } let(:uprn_known) { 1 }
let(:uprn) { 1 } let(:uprn) { 1 }
let(:manual_address_entry_selected) { false }
it "returns formatted value" do it "returns formatted value" do
expect(question.get_extra_check_answer_value(log)).to eq( expect(question.get_extra_check_answer_value(log)).to eq(
@ -78,6 +81,7 @@ RSpec.describe Form::Lettings::Questions::Uprn, type: :model do
context "when uprn not known" do context "when uprn not known" do
let(:uprn_known) { 0 } let(:uprn_known) { 0 }
let(:uprn) { nil } let(:uprn) { nil }
let(:manual_address_entry_selected) { true }
it "returns formatted value" do it "returns formatted value" do
expect(question.get_extra_check_answer_value(log)).to be_nil expect(question.get_extra_check_answer_value(log)).to be_nil

12
spec/models/form/lettings/subsections/property_information_spec.rb

@ -64,11 +64,7 @@ RSpec.describe Form::Lettings::Subsections::PropertyInformation, type: :model do
it "has correct pages" do it "has correct pages" do
expect(property_information.pages.map(&:id)).to eq( expect(property_information.pages.map(&:id)).to eq(
%w[ %w[
uprn address_search
uprn_confirmation
address_matcher
no_address_found
uprn_selection
address address
property_local_authority property_local_authority
local_authority_rent_value_check local_authority_rent_value_check
@ -105,11 +101,7 @@ RSpec.describe Form::Lettings::Subsections::PropertyInformation, type: :model do
property_let_type property_let_type
property_vacancy_reason_not_first_let property_vacancy_reason_not_first_let
property_vacancy_reason_first_let property_vacancy_reason_first_let
uprn address_search
uprn_confirmation
address_matcher
no_address_found
uprn_selection
address address
property_local_authority property_local_authority
local_authority_rent_value_check local_authority_rent_value_check

9
spec/models/form/sales/pages/address_fallback_spec.rb

@ -24,13 +24,6 @@ RSpec.describe Form::Sales::Pages::AddressFallback, type: :model do
end end
it "has correct depends_on" do it "has correct depends_on" do
expect(page.depends_on).to eq([ expect(page.depends_on).to eq([{ "manual_address_entry_selected" => true }])
{ "uprn_known" => nil, "uprn_selection" => "uprn_not_listed" },
{ "uprn_known" => 0, "uprn_selection" => "uprn_not_listed" },
{ "uprn_confirmed" => 0, "uprn_selection" => "uprn_not_listed" },
{ "uprn_known" => nil, "address_options_present?" => false },
{ "uprn_known" => 0, "address_options_present?" => false },
{ "uprn_confirmed" => 0, "address_options_present?" => false },
])
end end
end end

42
spec/models/form/sales/pages/address_search_spec.rb

@ -0,0 +1,42 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::AddressSearch, type: :model do
subject(:page) { described_class.new(page_id, page_definition, subsection) }
let(:page_id) { nil }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date:)) }
let(:start_date) { Time.utc(2024, 4, 1) }
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[uprn])
end
it "has the correct id" do
expect(page.id).to eq("address_search")
end
it "has the correct description" do
expect(page.description).to be_nil
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "manual_address_entry_selected" => false }])
end
it "has the correct question number" do
expect(page.question_number).to eq(15)
end
context "with 2025/26 form" do
let(:start_date) { Time.utc(2025, 4, 1) }
it "has the correct question number" do
expect(page.question_number).to eq(13)
end
end
end

4
spec/models/form/sales/pages/uprn_confirmation_spec.rb

@ -7,6 +7,10 @@ RSpec.describe Form::Sales::Pages::UprnConfirmation, type: :model do
let(:page_definition) { nil } let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection) } let(:subsection) { instance_double(Form::Subsection) }
before do
allow(subsection).to receive(:form).and_return(instance_double(Form, start_year_2024_or_later?: false))
end
it "has correct subsection" do it "has correct subsection" do
expect(page.subsection).to eq(subsection) expect(page.subsection).to eq(subsection)
end end

68
spec/models/form/sales/questions/address_search_spec.rb

@ -0,0 +1,68 @@
require "rails_helper"
RSpec.describe Form::Sales::Questions::AddressSearch, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date:))) }
let(:start_date) { Time.utc(2024, 4, 1) }
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("uprn")
end
it "has the correct type" do
expect(question.type).to eq("address_search")
end
it "has the correct question number" do
expect(question.question_number).to eq(15)
end
context "with 2025/26 form" do
let(:start_date) { Time.utc(2025, 4, 1) }
it "has the correct question number" do
expect(question.question_number).to eq(13)
end
end
describe "get_extra_check_answer_value" do
context "when address is not present" do
let(:log) { build(:sales_log, manual_address_entry_selected: false) }
it "returns nil" do
expect(question.get_extra_check_answer_value(log)).to be_nil
end
end
context "when address search is present" do
let(:log) do
build(
:sales_log,
:completed,
address_line1: "19, Charlton Gardens",
town_or_city: "Bristol",
postcode_full: "BS10 6LU",
la: "E06000023",
uprn_known: 1,
uprn: 107,
uprn_confirmed: 1,
)
end
context "when uprn known" do
it "returns formatted value" do
expect(question.get_extra_check_answer_value(log)).to eq(
"\n\n19, Charlton Gardens\nBristol\nBS10 6LU\nBristol, City of",
)
end
end
end
end
end

8
spec/models/form/sales/questions/uprn_selection_spec.rb

@ -90,9 +90,10 @@ RSpec.describe Form::Sales::Questions::UprnSelection, type: :model do
context "when the log has address line 1 input only" do context "when the log has address line 1 input only" do
before do before do
allow(address_client_instance).to receive(:result).and_return(nil)
log.address_line1_input = "Address line 1" log.address_line1_input = "Address line 1"
log.postcode_full_input = nil log.postcode_full_input = nil
log.save!(valudate: false) log.save!(validate: false)
end end
it "has the correct input_playback" do it "has the correct input_playback" do
@ -102,9 +103,10 @@ RSpec.describe Form::Sales::Questions::UprnSelection, type: :model do
context "when the log has postcode input only" do context "when the log has postcode input only" do
before do before do
allow(address_client_instance).to receive(:result).and_return(nil)
log.address_line1_input = nil log.address_line1_input = nil
log.postcode_full_input = "A1 1AA" log.postcode_full_input = "A1 1AA"
log.save!(valudate: false) log.save!(validate: false)
end end
it "has the correct input_playback" do it "has the correct input_playback" do
@ -116,7 +118,7 @@ RSpec.describe Form::Sales::Questions::UprnSelection, type: :model do
before do before do
log.address_line1_input = "Address line 1" log.address_line1_input = "Address line 1"
log.postcode_full_input = "A1 1AA" log.postcode_full_input = "A1 1AA"
log.save!(valudate: false) log.save!(validate: false)
end end
it "has the correct input_playback" do it "has the correct input_playback" do

12
spec/models/form/sales/subsections/property_information_spec.rb

@ -55,11 +55,7 @@ RSpec.describe Form::Sales::Subsections::PropertyInformation, type: :model do
it "has correct pages" do it "has correct pages" do
expect(property_information.pages.map(&:id)).to eq( expect(property_information.pages.map(&:id)).to eq(
%w[ %w[
uprn address_search
uprn_confirmation
address_matcher
no_address_found
uprn_selection
address address
property_local_authority property_local_authority
local_authority_buyer_1_income_max_value_check local_authority_buyer_1_income_max_value_check
@ -89,11 +85,7 @@ RSpec.describe Form::Sales::Subsections::PropertyInformation, type: :model do
it "has correct pages" do it "has correct pages" do
expect(property_information.pages.map(&:id)).to eq( expect(property_information.pages.map(&:id)).to eq(
%w[ %w[
uprn address_search
uprn_confirmation
address_matcher
no_address_found
uprn_selection
address address
property_local_authority property_local_authority
local_authority_buyer_1_income_max_value_check local_authority_buyer_1_income_max_value_check

7
spec/models/sales_log_spec.rb

@ -566,6 +566,7 @@ RSpec.describe SalesLog, type: :model do
ppostcode_full: nil, ppostcode_full: nil,
prevloc: nil, prevloc: nil,
saledate: Time.zone.local(2024, 5, 2), saledate: Time.zone.local(2024, 5, 2),
manual_address_entry_selected: true,
}) })
end end
@ -617,7 +618,7 @@ RSpec.describe SalesLog, type: :model do
end end
let(:address_sales_log_25_26) do let(:address_sales_log_25_26) do
create(:sales_log, :shared_ownership_setup_complete, postcode_full: "CA10 1AA", saledate: Time.zone.local(2025, 5, 2)) create(:sales_log, :shared_ownership_setup_complete, postcode_full: "CA10 1AA", saledate: Time.zone.local(2025, 5, 2), manual_address_entry_selected: true)
end end
before do before do
@ -672,11 +673,11 @@ RSpec.describe SalesLog, type: :model do
context "when saving address with LAs that have changed E-codes" do context "when saving address with LAs that have changed E-codes" do
context "when address inferred from uprn - we still get LA from postcode" do context "when address inferred from uprn - we still get LA from postcode" do
let(:address_sales_log_24_25) do let(:address_sales_log_24_25) do
create(:sales_log, :shared_ownership_setup_complete, uprn_known: 1, uprn: 1, saledate: Time.zone.local(2024, 5, 2)) create(:sales_log, :shared_ownership_setup_complete, manual_address_entry_selected: false, uprn_known: 1, uprn: 1, saledate: Time.zone.local(2024, 5, 2))
end end
let(:address_sales_log_25_26) do let(:address_sales_log_25_26) do
create(:sales_log, :shared_ownership_setup_complete, uprn_known: 1, uprn: 1, saledate: Time.zone.local(2025, 5, 2)) create(:sales_log, :shared_ownership_setup_complete, manual_address_entry_selected: false, uprn_known: 1, uprn: 1, saledate: Time.zone.local(2025, 5, 2))
end end
before do before do

4
spec/models/validations/sales/property_validations_spec.rb

@ -136,9 +136,9 @@ RSpec.describe Validations::Sales::PropertyValidations do
context "when within the limit and only numeric" do context "when within the limit and only numeric" do
let(:record) { build(:sales_log, uprn: "123456789012") } let(:record) { build(:sales_log, uprn: "123456789012") }
it "does not add an error" do it "does not add an invalid UPRN error" do
property_validator.validate_uprn(record) property_validator.validate_uprn(record)
expect(record.errors).not_to be_present expect(record.errors.added?(:uprn, I18n.t("validations.sales.property_information.uprn.invalid"))).to be false
end end
end end
end end

2
spec/request_helper.rb

@ -20,7 +20,7 @@ module RequestHelper
body = { results: [{ DPA: { UPRN: "10033558653" } }] }.to_json body = { results: [{ DPA: { UPRN: "10033558653" } }] }.to_json
WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/find?key&maxresults=10&minmatch=0.4&query=Address%20line%201,%20SW1A%201AA") WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/find?key&maxresults=10&minmatch=0.4&query=Address%20line%201,%20SW1A%201AA")
.to_return(status: 200, body:, headers: {}) .to_return(status: 200, body:, headers: {})
body = { results: [{ DPA: { "POSTCODE": "SW1A 1AA", "POST_TOWN": "London" } }] }.to_json body = { results: [{ DPA: { "POSTCODE": "SW1A 1AA", "POST_TOWN": "London", "PO_BOX_NUMBER": "The Mall, City Of Westminster" } }] }.to_json
WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key&uprn=1") WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key&uprn=1")
.to_return(status: 200, body:, headers: {}) .to_return(status: 200, body:, headers: {})
WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key&uprn=10033558653") WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key&uprn=10033558653")

148
spec/requests/address_search_controller_spec.rb

@ -0,0 +1,148 @@
require "rails_helper"
RSpec.describe AddressSearchController, type: :request do
let(:user) { create(:user) }
before do
sign_in user
end
describe "#manual input" do
context "when no address data is given and user chooses to enter address manually" do
let(:sales_log) { create(:sales_log, :shared_ownership_setup_complete, manual_address_entry_selected: false, assigned_to: user) }
it "correctly sets address fields" do
sales_log.reload
expect(sales_log.manual_address_entry_selected).to eq(false)
expect(sales_log.uprn_known).to eq(nil)
expect(sales_log.uprn).to eq(nil)
expect(sales_log.uprn_confirmed).to eq(nil)
expect(sales_log.uprn_selection).to eq(nil)
expect(sales_log.pcodenk).to eq(nil)
expect(sales_log.postcode_full).to eq(nil)
expect(sales_log.address_line1).to eq(nil)
expect(sales_log.address_line2).to eq(nil)
expect(sales_log.town_or_city).to eq(nil)
expect(sales_log.la).to eq(nil)
get "/address-search/manual-input/sales_log/#{sales_log.id}"
sales_log.reload
expect(sales_log.manual_address_entry_selected).to eq(true)
expect(sales_log.uprn_known).to eq(0)
expect(sales_log.uprn).to eq(nil)
expect(sales_log.uprn_confirmed).to eq(nil)
expect(sales_log.uprn_selection).to eq(nil)
expect(sales_log.pcodenk).to eq(nil)
expect(sales_log.postcode_full).to eq(nil)
expect(sales_log.address_line1).to eq(nil)
expect(sales_log.address_line2).to eq(nil)
expect(sales_log.town_or_city).to eq(nil)
expect(sales_log.la).to eq(nil)
end
end
context "when choosing to manually input an address for a log that has an address searched value" do
let(:lettings_log) { create(:lettings_log, :completed, manual_address_entry_selected: false, assigned_to: user) }
it "correctly sets address fields" do
lettings_log.reload
expect(lettings_log.uprn_known).to eq(1)
expect(lettings_log.uprn).to eq("10033558653")
expect(lettings_log.uprn_confirmed).to eq(1)
expect(lettings_log.uprn_selection).to eq("10033558653")
expect(lettings_log.postcode_known).to eq(1)
expect(lettings_log.postcode_full).to eq("SW1A 1AA")
expect(lettings_log.address_line1).to eq("The Mall, City Of Westminster")
expect(lettings_log.address_line2).to eq(nil)
expect(lettings_log.town_or_city).to eq("London")
expect(lettings_log.la).to eq("E09000033")
get "/address-search/manual-input/lettings_log/#{lettings_log.id}"
lettings_log.reload
expect(lettings_log.manual_address_entry_selected).to eq(true)
expect(lettings_log.uprn_known).to eq(0)
expect(lettings_log.uprn).to eq(nil)
expect(lettings_log.uprn_confirmed).to eq(nil)
expect(lettings_log.uprn_selection).to eq(nil)
expect(lettings_log.postcode_known).to eq(nil)
expect(lettings_log.postcode_full).to eq(nil)
expect(lettings_log.address_line1).to eq(nil)
expect(lettings_log.address_line2).to eq(nil)
expect(lettings_log.town_or_city).to eq(nil)
expect(lettings_log.la).to eq(nil)
end
end
end
describe "#search input" do
context "when no address is entered manually and choosing to search instead" do
let(:lettings_log) { create(:lettings_log, :setup_completed, manual_address_entry_selected: true, assigned_to: user) }
it "correctly sets address fields" do
lettings_log.reload
expect(lettings_log.manual_address_entry_selected).to eq(true)
expect(lettings_log.uprn_known).to eq(0)
expect(lettings_log.uprn).to eq(nil)
expect(lettings_log.uprn_confirmed).to eq(nil)
expect(lettings_log.uprn_selection).to eq(nil)
expect(lettings_log.postcode_known).to eq(nil)
expect(lettings_log.postcode_full).to eq(nil)
expect(lettings_log.address_line1).to eq(nil)
expect(lettings_log.address_line2).to eq(nil)
expect(lettings_log.town_or_city).to eq(nil)
expect(lettings_log.la).to eq(nil)
get "/address-search/search-input/lettings_log/#{lettings_log.id}"
lettings_log.reload
expect(lettings_log.manual_address_entry_selected).to eq(false)
expect(lettings_log.uprn_known).to eq(nil)
expect(lettings_log.uprn).to eq(nil)
expect(lettings_log.uprn_confirmed).to eq(nil)
expect(lettings_log.uprn_selection).to eq(nil)
expect(lettings_log.postcode_known).to eq(nil)
expect(lettings_log.postcode_full).to eq(nil)
expect(lettings_log.address_line1).to eq(nil)
expect(lettings_log.address_line2).to eq(nil)
expect(lettings_log.town_or_city).to eq(nil)
expect(lettings_log.la).to eq(nil)
end
end
context "when choosing to search for an address for a log that has an address searched value" do
let(:sales_log) { create(:sales_log, :completed, manual_address_entry_selected: true, town_or_city: "Test Town", assigned_to: user) }
it "correctly sets address fields" do
sales_log.reload
expect(sales_log.manual_address_entry_selected).to eq(true)
expect(sales_log.uprn_known).to eq(0)
expect(sales_log.uprn).to eq(nil)
expect(sales_log.uprn_confirmed).to eq(nil)
expect(sales_log.uprn_selection).to eq(nil)
expect(sales_log.pcodenk).to eq(0)
expect(sales_log.postcode_full).to eq("SW1A 1AA")
expect(sales_log.address_line1).to eq("Address line 1")
expect(sales_log.address_line2).to eq(nil)
expect(sales_log.town_or_city).to eq("Test Town")
expect(sales_log.la).to eq("E09000033")
get "/address-search/search-input/sales_log/#{sales_log.id}"
sales_log.reload
expect(sales_log.manual_address_entry_selected).to eq(false)
expect(sales_log.uprn_known).to eq(nil)
expect(sales_log.uprn).to eq(nil)
expect(sales_log.uprn_confirmed).to eq(nil)
expect(sales_log.uprn_selection).to eq(nil)
expect(sales_log.pcodenk).to eq(nil)
expect(sales_log.postcode_full).to eq(nil)
expect(sales_log.address_line1).to eq(nil)
expect(sales_log.address_line2).to eq(nil)
expect(sales_log.town_or_city).to eq(nil)
expect(sales_log.la).to eq(nil)
end
end
end
end

10
spec/requests/duplicate_logs_controller_spec.rb

@ -77,8 +77,8 @@ RSpec.describe DuplicateLogsController, type: :request do
end end
it "displays check your answers for each log with correct questions where UPRN is given" do it "displays check your answers for each log with correct questions where UPRN is given" do
lettings_log.update!(uprn: "123", uprn_known: 1, uprn_confirmed: 1) lettings_log.update!(uprn: "123", uprn_known: 1, uprn_confirmed: 1, manual_address_entry_selected: false)
duplicate_logs[0].update!(uprn: "123", uprn_known: 1, uprn_confirmed: 1) duplicate_logs[0].update!(uprn: "123", uprn_known: 1, uprn_confirmed: 1, manual_address_entry_selected: false)
get "/lettings-logs/#{lettings_log.id}/duplicate-logs?original_log_id=#{lettings_log.id}" get "/lettings-logs/#{lettings_log.id}/duplicate-logs?original_log_id=#{lettings_log.id}"
expect(page).to have_content("Q5 - Tenancy start date", count: 3) expect(page).to have_content("Q5 - Tenancy start date", count: 3)
@ -186,9 +186,9 @@ RSpec.describe DuplicateLogsController, type: :request do
end end
it "displays check your answers for each log with correct questions when UPRN is given" do it "displays check your answers for each log with correct questions when UPRN is given" do
sales_log.update!(uprn: "123", uprn_known: 1) sales_log.update!(uprn: "123", uprn_known: 1, manual_address_entry_selected: false)
duplicate_logs[0].update!(uprn: "123", uprn_known: 1) duplicate_logs[0].update!(uprn: "123", uprn_known: 1, manual_address_entry_selected: false)
duplicate_logs[1].update!(uprn: "123", uprn_known: 1) duplicate_logs[1].update!(uprn: "123", uprn_known: 1, manual_address_entry_selected: false)
get "/sales-logs/#{sales_log.id}/duplicate-logs?original_log_id=#{sales_log.id}" get "/sales-logs/#{sales_log.id}/duplicate-logs?original_log_id=#{sales_log.id}"
expect(page).to have_content("Q1 - Sale completion date", count: 3) expect(page).to have_content("Q1 - Sale completion date", count: 3)

8
spec/services/csv/sales_log_csv_service_spec.rb

@ -199,7 +199,7 @@ RSpec.describe Csv::SalesLogCsvService do
let(:fixed_time) { Time.zone.local(2024, 5, 1) } let(:fixed_time) { Time.zone.local(2024, 5, 1) }
before do before do
log.update!(nationality_all: 36) log.update!(nationality_all: 36, manual_address_entry_selected: false, uprn: "1", uprn_known: 1)
end end
it "exports the CSV with the 2024 ordering and all values correct" do it "exports the CSV with the 2024 ordering and all values correct" do
@ -286,6 +286,10 @@ RSpec.describe Csv::SalesLogCsvService do
let(:fixed_time) { Time.zone.local(2024, 5, 1) } let(:fixed_time) { Time.zone.local(2024, 5, 1) }
let(:year) { 2024 } let(:year) { 2024 }
before do
log.update!(manual_address_entry_selected: false, uprn: "1", uprn_known: 1)
end
it "exports the CSV with all values correct" do it "exports the CSV with all values correct" do
expected_content = CSV.read("spec/fixtures/files/sales_logs_csv_export_codes_24.csv") expected_content = CSV.read("spec/fixtures/files/sales_logs_csv_export_codes_24.csv")
values_to_delete = %w[ID] values_to_delete = %w[ID]
@ -338,7 +342,7 @@ RSpec.describe Csv::SalesLogCsvService do
let(:fixed_time) { Time.zone.local(2024, 5, 1) } let(:fixed_time) { Time.zone.local(2024, 5, 1) }
before do before do
log.update!(nationality_all: 36) log.update!(nationality_all: 36, manual_address_entry_selected: false, uprn: "1", uprn_known: 1)
end end
context "and exporting with labels" do context "and exporting with labels" do

2
spec/services/exports/lettings_log_export_service_spec.rb

@ -431,7 +431,7 @@ RSpec.describe Exports::LettingsLogExportService do
end end
context "and one lettings log is available for export" do context "and one lettings log is available for export" do
let!(:lettings_log) { FactoryBot.create(:lettings_log, :completed, assigned_to: user, age1: 35, sex1: "F", age2: 32, sex2: "M", ppostcode_full: "A1 1AA", nationality_all_group: 13, propcode: "123", postcode_full: "SE2 6RT", tenancycode: "BZ737", startdate: Time.zone.local(2024, 4, 2, 10, 36, 49), voiddate: Time.zone.local(2021, 11, 3), mrcdate: Time.zone.local(2022, 5, 5, 10, 36, 49), tenancylength: 5, underoccupation_benefitcap: 4, creation_method: 2, bulk_upload_id: 1, address_line1_as_entered: "address line 1 as entered", address_line2_as_entered: "address line 2 as entered", town_or_city_as_entered: "town or city as entered", county_as_entered: "county as entered", postcode_full_as_entered: "AB1 2CD", la_as_entered: "la as entered") } let!(:lettings_log) { FactoryBot.create(:lettings_log, :completed, assigned_to: user, age1: 35, sex1: "F", age2: 32, sex2: "M", ppostcode_full: "A1 1AA", nationality_all_group: 13, propcode: "123", postcode_full: "SE2 6RT", tenancycode: "BZ737", startdate: Time.zone.local(2024, 4, 2, 10, 36, 49), voiddate: Time.zone.local(2021, 11, 3), mrcdate: Time.zone.local(2022, 5, 5, 10, 36, 49), tenancylength: 5, underoccupation_benefitcap: 4, creation_method: 2, bulk_upload_id: 1, address_line1_as_entered: "address line 1 as entered", address_line2_as_entered: "address line 2 as entered", town_or_city_as_entered: "town or city as entered", county_as_entered: "county as entered", postcode_full_as_entered: "AB1 2CD", la_as_entered: "la as entered", manual_address_entry_selected: false, uprn: "1", uprn_known: 1) }
let(:expected_zip_filename) { "core_2024_2025_apr_mar_f0001_inc0001.zip" } let(:expected_zip_filename) { "core_2024_2025_apr_mar_f0001_inc0001.zip" }
let(:expected_data_filename) { "core_2024_2025_apr_mar_f0001_inc0001_pt001.xml" } let(:expected_data_filename) { "core_2024_2025_apr_mar_f0001_inc0001_pt001.xml" }
let(:xml_export_file) { File.open("spec/fixtures/exports/general_needs_log_24_25.xml", "r:UTF-8") } let(:xml_export_file) { File.open("spec/fixtures/exports/general_needs_log_24_25.xml", "r:UTF-8") }

13
spec/shared/shared_examples_for_derived_fields.rb

@ -25,11 +25,14 @@ RSpec.shared_examples "shared examples for derived fields" do |log_type|
end end
it "does not affect older logs with uprn_confirmed == 0" do it "does not affect older logs with uprn_confirmed == 0" do
log = FactoryBot.build(log_type, uprn_known: 0, uprn: nil, uprn_confirmed: 0) Timecop.freeze(Time.zone.local(2023, 4, 1)) do
log = FactoryBot.build(log_type, uprn_known: 0, uprn: nil, uprn_confirmed: 0)
expect { log.set_derived_fields! }.to not_change(log, :uprn_known) allow(log.form).to receive(:start_year_2024_or_later?).and_return(false)
.and not_change(log, :uprn) expect { log.set_derived_fields! }.to not_change(log, :uprn_known)
.and not_change(log, :uprn_confirmed) .and not_change(log, :uprn)
.and not_change(log, :uprn_confirmed)
end
Timecop.return
end end
end end
end end

2
spec/shared/shared_log_examples.rb

@ -81,7 +81,6 @@ RSpec.shared_examples "shared log examples" do |log_type|
expect { log.process_uprn_change! }.to change(log, :address_line1).from(nil).to("0, Building Name, Thoroughfare") expect { log.process_uprn_change! }.to change(log, :address_line1).from(nil).to("0, Building Name, Thoroughfare")
.and change(log, :town_or_city).from(nil).to("Posttown") .and change(log, :town_or_city).from(nil).to("Posttown")
.and change(log, :postcode_full).from(nil).to("POSTCODE") .and change(log, :postcode_full).from(nil).to("POSTCODE")
.and change(log, :uprn_confirmed).from(1).to(nil)
.and change(log, :county).from("county").to(nil) .and change(log, :county).from("county").to(nil)
end end
end end
@ -156,7 +155,6 @@ RSpec.shared_examples "shared log examples" do |log_type|
.and change(log, :uprn_confirmed).from(nil).to(1) .and change(log, :uprn_confirmed).from(nil).to(1)
.and change(log, :uprn).from(nil).to("UPRN") .and change(log, :uprn).from(nil).to("UPRN")
.and change(log, :uprn_known).from(nil).to(1) .and change(log, :uprn_known).from(nil).to(1)
.and change(log, :uprn_selection).from("UPRN").to(nil)
.and change(log, :county).from("county").to(nil) .and change(log, :county).from("county").to(nil)
end end
end end

Loading…
Cancel
Save