Browse Source

Merge branch 'main' into CLDC-1468-add-postcode-to-sales-property-info-journey

CLDC-1468-add-postcode-to-sales-property-info-journey
Mo Seedat 2 years ago
parent
commit
79d16d82c4
  1. 6
      Gemfile.lock
  2. 2
      app/components/search_component.html.erb
  3. 4
      app/components/search_component.rb
  4. 9
      app/controllers/locations_controller.rb
  5. 142
      app/controllers/organisation_relationships_controller.rb
  6. 10
      app/frontend/styles/_accessible-autocomplete.scss
  7. 3
      app/helpers/interruption_screen_helper.rb
  8. 5
      app/helpers/locations_helper.rb
  9. 28
      app/helpers/navigation_items_helper.rb
  10. 5
      app/models/bulk_upload.rb
  11. 2
      app/models/derived_variables/lettings_log_variables.rb
  12. 4
      app/models/form/lettings/questions/scheme_id.rb
  13. 6
      app/models/form/question.rb
  14. 16
      app/models/form/sales/pages/property_local_authority.rb
  15. 331
      app/models/form/sales/questions/property_local_authority.rb
  16. 24
      app/models/form/sales/questions/property_local_authority_known.rb
  17. 1
      app/models/form/sales/subsections/property_information.rb
  18. 1
      app/models/la_rent_range.rb
  19. 10
      app/models/lettings_log.rb
  20. 321
      app/models/location.rb
  21. 12
      app/models/organisation.rb
  22. 9
      app/models/organisation_relationship.rb
  23. 30
      app/models/validations/financial_validations.rb
  24. 4
      app/models/validations/property_validations.rb
  25. 10
      app/models/validations/soft_validations.rb
  26. 9
      app/services/exports/lettings_log_export_service.rb
  27. 2
      app/views/form/review.html.erb
  28. 6
      app/views/locations/edit_local_authority.html.erb
  29. 8
      app/views/logs/index.html.erb
  30. 22
      app/views/organisation_relationships/_housing_provider_list.erb
  31. 22
      app/views/organisation_relationships/_managing_agent_list.erb
  32. 3
      app/views/organisation_relationships/_related_organisation_select_question.html.erb
  33. 34
      app/views/organisation_relationships/add_housing_provider.html.erb
  34. 34
      app/views/organisation_relationships/add_managing_agent.html.erb
  35. 24
      app/views/organisation_relationships/housing_providers.html.erb
  36. 27
      app/views/organisation_relationships/managing_agents.html.erb
  37. 21
      app/views/organisation_relationships/remove_housing_provider.html.erb
  38. 21
      app/views/organisation_relationships/remove_managing_agent.html.erb
  39. 2
      app/views/organisations/index.html.erb
  40. 2
      app/views/organisations/logs.html.erb
  41. 16
      config/forms/2021_2022.json
  42. 16
      config/forms/2022_2023.json
  43. 6
      config/initializers/feature_toggle.rb
  44. 33
      config/locales/en.yml
  45. 7726
      config/rent_range_data/2022.csv
  46. 10
      config/routes.rb
  47. 8
      db/migrate/20221007133155_add_la_to_sales_log.rb
  48. 10
      db/migrate/20221017095918_add_relationship_type_to_org_relationships.rb
  49. 5
      db/migrate/20221019082625_rename_managing_agents_column.rb
  50. 5
      db/schema.rb
  51. 39
      db/seeds.rb
  52. 2
      docs/api/v1.json
  53. 2
      lib/tasks/rent_ranges.rake
  54. 4
      spec/factories/lettings_log.rb
  55. 5
      spec/factories/organisation.rb
  56. 14
      spec/factories/organisation_relationship.rb
  57. 2
      spec/factories/sales_log.rb
  58. 2
      spec/fixtures/exports/general_needs_log.csv
  59. 12
      spec/fixtures/exports/general_needs_log.xml
  60. 12
      spec/fixtures/exports/supported_housing_logs.xml
  61. 22
      spec/helpers/navigation_items_helper_spec.rb
  62. 1
      spec/models/form/sales/subsections/property_information_spec.rb
  63. 4
      spec/models/form_handler_spec.rb
  64. 4
      spec/models/lettings_log_spec.rb
  65. 96
      spec/models/organisation_spec.rb
  66. 93
      spec/models/validations/financial_validations_spec.rb
  67. 12
      spec/models/validations/household_validations_spec.rb
  68. 2
      spec/models/validations/property_validations_spec.rb
  69. 4
      spec/requests/form_controller_spec.rb
  70. 2
      spec/requests/lettings_logs_controller_spec.rb
  71. 8
      spec/requests/locations_controller_spec.rb
  72. 583
      spec/requests/organisation_relationships_controller_spec.rb
  73. 10
      spec/requests/organisations_controller_spec.rb
  74. 2
      spec/requests/sales_logs_controller_spec.rb
  75. 6
      spec/requests/users_controller_spec.rb

6
Gemfile.lock

@ -225,11 +225,11 @@ GEM
net-protocol
timeout
nio4r (2.5.8)
nokogiri (1.13.8-arm64-darwin)
nokogiri (1.13.9-arm64-darwin)
racc (~> 1.4)
nokogiri (1.13.8-x86_64-darwin)
nokogiri (1.13.9-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.13.8-x86_64-linux)
nokogiri (1.13.9-x86_64-linux)
racc (~> 1.4)
notifications-ruby-client (5.3.0)
jwt (>= 1.5, < 3)

2
app/components/search_component.html.erb

@ -10,6 +10,6 @@
autocomplete: "off",
class: "app-search__input" %>
<%= f.govuk_submit "Search", classes: "app-search__button" %>
<%= f.govuk_submit "Search", classes: "app-search__button govuk-button--secondary" %>
</div>
<% end %>

4
app/components/search_component.rb

@ -15,6 +15,10 @@ class SearchComponent < ViewComponent::Base
request.path
elsif request.path.include?("organisations") && request.path.include?("schemes")
request.path
elsif request.path.include?("organisations") && request.path.include?("housing-providers")
request.path
elsif request.path.include?("organisations") && request.path.include?("managing-agents")
request.path
elsif request.path.include?("users")
user_path(current_user)
elsif request.path.include?("organisations")

9
app/controllers/locations_controller.rb

@ -61,7 +61,11 @@ class LocationsController < ApplicationController
error_message = I18n.t("validations.location_admin_district")
@location.errors.add :location_admin_district, error_message
render :edit_local_authority, status: :unprocessable_entity
elsif @location.update(location_params)
else
if page == "edit-local-authority"
params[:location][:location_code] = Location.local_authorities.key(params[:location][:location_admin_district])
end
if @location.update(location_params)
case page
when "edit"
if @location.location_admin_district.nil?
@ -84,6 +88,7 @@ class LocationsController < ApplicationController
render :edit, status: :unprocessable_entity
end
end
end
private
@ -123,7 +128,7 @@ private
end
def location_params
required_params = params.require(:location).permit(:postcode, :name, :units, :type_of_unit, :add_another_location, :startdate, :mobility_type, :location_admin_district).merge(scheme_id: @scheme.id)
required_params = params.require(:location).permit(:postcode, :name, :units, :type_of_unit, :add_another_location, :startdate, :mobility_type, :location_admin_district, :location_code).merge(scheme_id: @scheme.id)
required_params[:postcode] = PostcodeService.clean(required_params[:postcode]) if required_params[:postcode]
required_params
end

142
app/controllers/organisation_relationships_controller.rb

@ -0,0 +1,142 @@
class OrganisationRelationshipsController < ApplicationController
include Pagy::Backend
include Modules::SearchFilter
before_action :authenticate_user!
before_action :authenticate_scope!
def housing_providers
housing_providers = organisation.housing_providers
unpaginated_filtered_housing_providers = filtered_collection(housing_providers, search_term)
organisations = Organisation.where.not(id: @organisation.id).pluck(:id, :name)
respond_to :html
@pagy, @housing_providers = pagy(unpaginated_filtered_housing_providers)
@organisations = organisations
@searched = search_term.presence
@total_count = housing_providers.size
end
def managing_agents
managing_agents = organisation.managing_agents
unpaginated_filtered_managing_agents = filtered_collection(managing_agents, search_term)
organisations = Organisation.where.not(id: @organisation.id).pluck(:id, :name)
respond_to :html
@pagy, @managing_agents = pagy(unpaginated_filtered_managing_agents)
@organisations = organisations
@searched = search_term.presence
@total_count = managing_agents.size
end
def add_housing_provider
@organisations = Organisation.where.not(id: @organisation.id).pluck(:id, :name)
respond_to :html
end
def add_managing_agent
@organisations = Organisation.where.not(id: @organisation.id).pluck(:id, :name)
respond_to :html
end
def create_housing_provider
child_organisation = @organisation
relationship_type = OrganisationRelationship::OWNING
if params[:organisation][:related_organisation_id].empty?
@organisation.errors.add :related_organisation_id, "You must choose a housing provider"
@organisations = Organisation.where.not(id: child_organisation.id).pluck(:id, :name)
render "organisation_relationships/add_housing_provider"
return
else
parent_organisation = related_organisation
if OrganisationRelationship.exists?(child_organisation:, parent_organisation:, relationship_type:)
@organisation.errors.add :related_organisation_id, "You have already added this housing provider"
@organisations = Organisation.where.not(id: child_organisation.id).pluck(:id, :name)
render "organisation_relationships/add_housing_provider"
return
end
end
create!(child_organisation:, parent_organisation:, relationship_type:)
flash[:notice] = "#{related_organisation.name} is now one of #{current_user.data_coordinator? ? 'your' : "this organisation's"} housing providers"
redirect_to housing_providers_organisation_path
end
def create_managing_agent
parent_organisation = @organisation
relationship_type = OrganisationRelationship::MANAGING
if params[:organisation][:related_organisation_id].empty?
@organisation.errors.add :related_organisation_id, "You must choose a managing agent"
@organisations = Organisation.where.not(id: parent_organisation.id).pluck(:id, :name)
render "organisation_relationships/add_managing_agent"
return
else
child_organisation = related_organisation
if OrganisationRelationship.exists?(child_organisation:, parent_organisation:, relationship_type:)
@organisation.errors.add :related_organisation_id, "You have already added this managing agent"
@organisations = Organisation.where.not(id: parent_organisation.id).pluck(:id, :name)
render "organisation_relationships/add_managing_agent"
return
end
end
create!(child_organisation:, parent_organisation:, relationship_type:)
flash[:notice] = "#{related_organisation.name} is now one of #{current_user.data_coordinator? ? 'your' : "this organisation's"} managing agents"
redirect_to managing_agents_organisation_path
end
def remove_housing_provider
@target_organisation_id = target_organisation.id
end
def delete_housing_provider
relationship = OrganisationRelationship.find_by!(
child_organisation: @organisation,
parent_organisation: target_organisation,
relationship_type: OrganisationRelationship::OWNING,
)
relationship.destroy!
flash[:notice] = "#{target_organisation.name} is no longer one of #{current_user.data_coordinator? ? 'your' : "this organisation's"} housing providers"
redirect_to housing_providers_organisation_path
end
def remove_managing_agent
@target_organisation_id = target_organisation.id
end
def delete_managing_agent
relationship = OrganisationRelationship.find_by!(
parent_organisation: @organisation,
child_organisation: target_organisation,
relationship_type: OrganisationRelationship::MANAGING,
)
relationship.destroy!
flash[:notice] = "#{target_organisation.name} is no longer one of #{current_user.data_coordinator? ? 'your' : "this organisation's"} managing agents"
redirect_to managing_agents_organisation_path
end
private
def create!(child_organisation:, parent_organisation:, relationship_type:)
@resource = OrganisationRelationship.new(child_organisation:, parent_organisation:, relationship_type:)
@resource.save!
end
def organisation
@organisation ||= Organisation.find(params[:id])
end
def related_organisation
@related_organisation ||= Organisation.find(params[:organisation][:related_organisation_id])
end
def target_organisation
@target_organisation ||= Organisation.find(params[:target_organisation_id])
end
def search_term
params["search"]
end
def authenticate_scope!
if current_user.organisation != organisation && !current_user.support?
render_not_found
end
end
end

10
app/frontend/styles/_accessible-autocomplete.scss

@ -5,12 +5,22 @@
.autocomplete__input {
font-family: inherit;
background-image: url("data:image/svg+xml,%3Csvg class='app-search__icon' width='20' height='20' viewBox='0 0 27 27' fill='none' xmlns='http://www.w3.org/2000/svg' aria-hidden='true' focusable='false'%3E%3Ccircle cx='12.0161' cy='11.0161' r='8.51613' stroke='currentColor' stroke-width='3'%3E%3C/circle%3E%3Cline x1='17.8668' y1='17.3587' x2='26.4475' y2='25.9393' stroke='currentColor' stroke-width='3'%3E%3C/line%3E%3C/svg%3E");
background-repeat: no-repeat;
background-size: 1em 1em;
background-position: 7px center;
text-indent: govuk-spacing(6);
}
.autocomplete__option__append {
font-weight: bold;
}
.autocomplete__hint {
font-family: inherit;
text-indent: govuk-spacing(6);
}
.autocomplete__option__hint {
display: block;
color: $govuk-secondary-text-colour;

3
app/helpers/interruption_screen_helper.rb

@ -5,7 +5,8 @@ module InterruptionScreenHelper
translation_params = {}
informative_text["arguments"].each do |argument|
value = if argument["label"]
lettings_log.form.get_question(argument["key"], lettings_log).answer_label(lettings_log).downcase
pre_casing_value = lettings_log.form.get_question(argument["key"], lettings_log).answer_label(lettings_log)
pre_casing_value.downcase
else
lettings_log.public_send(argument["key"])
end

5
app/helpers/locations_helper.rb

@ -12,6 +12,11 @@ module LocationsHelper
selection_options(Location.type_of_units)
end
def local_authorities_selection
null_option = [OpenStruct.new(id: "", name: "Select an option")]
null_option + Location.local_authorities.map { |code, name| OpenStruct.new(code:, name:) }
end
def selection_options(resource)
return [] if resource.blank?

28
app/helpers/navigation_items_helper.rb

@ -17,6 +17,8 @@ module NavigationItemsHelper
NavigationItem.new("Schemes", "/schemes", subnav_supported_housing_schemes_path?(path)),
NavigationItem.new("Users", users_organisation_path(current_user.organisation), subnav_users_path?(path)),
NavigationItem.new("About your organisation", "/organisations/#{current_user.organisation.id}", subnav_details_path?(path)),
(NavigationItem.new("Housing providers", housing_providers_organisation_path(current_user.organisation), housing_providers_path?(path)) if FeatureToggle.managing_owning_enabled?),
(NavigationItem.new("Managing agents", managing_agents_organisation_path(current_user.organisation), managing_agents_path?(path)) if FeatureToggle.managing_owning_enabled?),
].compact
else
[
@ -24,6 +26,8 @@ module NavigationItemsHelper
FeatureToggle.sales_log_enabled? ? NavigationItem.new("Sales logs", sales_logs_path, sales_logs_current?(path)) : nil,
NavigationItem.new("Users", users_organisation_path(current_user.organisation), subnav_users_path?(path)),
NavigationItem.new("About your organisation", "/organisations/#{current_user.organisation.id}", subnav_details_path?(path)),
(NavigationItem.new("Housing providers", housing_providers_organisation_path(current_user.organisation), housing_providers_path?(path)) if FeatureToggle.managing_owning_enabled?),
(NavigationItem.new("Managing agents", managing_agents_organisation_path(current_user.organisation), managing_agents_path?(path)) if FeatureToggle.managing_owning_enabled?),
].compact
end
end
@ -31,18 +35,22 @@ module NavigationItemsHelper
def secondary_items(path, current_organisation_id)
if current_user.organisation.holds_own_stock?
[
NavigationItem.new("Lettings logs", "/organisations/#{current_organisation_id}/lettings-logs", subnav_logs_path?(path)),
FeatureToggle.sales_log_enabled? ? NavigationItem.new("Sales logs", "/organisations/#{current_organisation_id}/sales-logs", sales_logs_current?(path)) : nil,
NavigationItem.new("Lettings logs", "/organisations/#{current_organisation_id}/lettings-logs", subnav_lettings_logs_path?(path)),
FeatureToggle.sales_log_enabled? ? NavigationItem.new("Sales logs", "/organisations/#{current_organisation_id}/sales-logs", subnav_sales_logs_path?(path)) : nil,
NavigationItem.new("Schemes", "/organisations/#{current_organisation_id}/schemes", subnav_supported_housing_schemes_path?(path)),
NavigationItem.new("Users", "/organisations/#{current_organisation_id}/users", subnav_users_path?(path)),
NavigationItem.new("About this organisation", "/organisations/#{current_organisation_id}", subnav_details_path?(path)),
(NavigationItem.new("Housing providers", housing_providers_organisation_path(current_organisation_id), housing_providers_path?(path)) if FeatureToggle.managing_owning_enabled?),
(NavigationItem.new("Managing agents", managing_agents_organisation_path(current_organisation_id), managing_agents_path?(path)) if FeatureToggle.managing_owning_enabled?),
].compact
else
[
NavigationItem.new("Lettings logs", "/organisations/#{current_organisation_id}/lettings-logs", subnav_logs_path?(path)),
NavigationItem.new("Lettings logs", "/organisations/#{current_organisation_id}/lettings-logs", subnav_lettings_logs_path?(path)),
FeatureToggle.sales_log_enabled? ? NavigationItem.new("Sales logs", "/organisations/#{current_organisation_id}/sales-logs", sales_logs_current?(path)) : nil,
NavigationItem.new("Users", "/organisations/#{current_organisation_id}/users", subnav_users_path?(path)),
NavigationItem.new("About this organisation", "/organisations/#{current_organisation_id}", subnav_details_path?(path)),
(NavigationItem.new("Housing providers", housing_providers_organisation_path(current_organisation_id), housing_providers_path?(path)) if FeatureToggle.managing_owning_enabled?),
(NavigationItem.new("Managing agents", managing_agents_organisation_path(current_organisation_id), managing_agents_path?(path)) if FeatureToggle.managing_owning_enabled?),
].compact
end
end
@ -84,11 +92,23 @@ private
(path.include?("/organisations") && path.include?("/users")) || path.include?("/users/")
end
def subnav_logs_path?(path)
def subnav_lettings_logs_path?(path)
path.include?("/organisations") && path.include?("/lettings-logs")
end
def subnav_sales_logs_path?(path)
path.include?("/organisations") && path.include?("/sales-logs")
end
def subnav_details_path?(path)
path.include?("/organisations") && path.include?("/details")
end
def housing_providers_path?(path)
path.include?("/housing-providers")
end
def managing_agents_path?(path)
path.include?("/managing-agents")
end
end

5
app/models/bulk_upload.rb

@ -35,10 +35,7 @@ class BulkUpload
created_by: current_user,
)
map_row(row).each do |attr_key, attr_val|
update = lettings_log.update(attr_key => attr_val)
unless update
# TODO: determine what to do when a bulk upload contains field values that don't pass validations
end
_update = lettings_log.update(attr_key => attr_val)
rescue ArgumentError
# TODO: determine what we want to do when bulk upload contains totally invalid data for a field.
end

2
app/models/derived_variables/lettings_log_variables.rb

@ -45,7 +45,7 @@ module DerivedVariables::LettingsLogVariables
self.nocharge = household_charge&.zero? ? 1 : 0
if is_renewal?
self.underoccupation_benefitcap = 2 if collection_start_year == 2021
self.referral = 0
self.referral = 1
self.waityear = 2
if is_general_needs?
# fixed term

4
app/models/form/lettings/questions/scheme_id.rb

@ -33,10 +33,6 @@ class Form::Lettings::Questions::SchemeId < ::Form::Question
!supported_housing_selected?(lettings_log)
end
def answer_selected?(lettings_log, answer)
lettings_log[id] == answer.name || lettings_log[id] == answer.resource
end
private
def supported_housing_selected?(lettings_log)

6
app/models/form/question.rb

@ -313,7 +313,7 @@ private
sheltered: [0, 1],
armedforces: [1, 4, 5],
leftreg: [0],
reservist: [0],
reservist: [1],
preg_occ: [1],
illness: [1],
underoccupation_benefitcap: [4, 5, 6],
@ -337,7 +337,7 @@ private
sheltered: [2],
armedforces: [2],
leftreg: [1],
reservist: [1],
reservist: [2],
preg_occ: [2],
illness: [2],
underoccupation_benefitcap: [2],
@ -389,7 +389,7 @@ private
sheltered: [3],
armedforces: [3],
leftreg: [3],
reservist: [2],
reservist: [3],
preg_occ: [3],
hb: [6],
}.freeze

16
app/models/form/sales/pages/property_local_authority.rb

@ -0,0 +1,16 @@
class Form::Sales::Pages::PropertyLocalAuthority < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "property_local_authority"
@header = ""
@description = ""
@subsection = subsection
end
def questions
@questions ||= [
Form::Sales::Questions::PropertyLocalAuthorityKnown.new(nil, nil, self),
Form::Sales::Questions::PropertyLocalAuthority.new(nil, nil, self),
]
end
end

331
app/models/form/sales/questions/property_local_authority.rb

@ -0,0 +1,331 @@
class Form::Sales::Questions::PropertyLocalAuthority < ::Form::Question
def initialize(id, hsh, page)
super
@id = "la"
@check_answer_label = "Local authority"
@header = "What is the local authority of the property?"
@type = "select"
@answer_options = ANSWER_OPTIONS
@page = page
end
ANSWER_OPTIONS = {
"" => "Select an option",
"E07000223" => "Adur",
"E07000026" => "Allerdale",
"E07000032" => "Amber Valley",
"E07000224" => "Arun",
"E07000170" => "Ashfield",
"E07000105" => "Ashford",
"E07000200" => "Babergh",
"E09000002" => "Barking and Dagenham",
"E09000003" => "Barnet",
"E08000016" => "Barnsley",
"E07000027" => "Barrow-in-Furness",
"E07000066" => "Basildon",
"E07000084" => "Basingstoke and Deane",
"E07000171" => "Bassetlaw",
"E06000022" => "Bath and North East Somerset",
"E06000055" => "Bedford",
"E09000004" => "Bexley",
"E08000025" => "Birmingham",
"E07000129" => "Blaby",
"E06000008" => "Blackburn with Darwen",
"E06000009" => "Blackpool",
"E07000033" => "Bolsover",
"E08000001" => "Bolton",
"E07000136" => "Boston",
"E06000058" => "Bournemouth, Christchurch and Poole",
"E06000036" => "Bracknell Forest",
"E08000032" => "Bradford",
"E07000067" => "Braintree",
"E07000143" => "Breckland",
"E09000005" => "Brent",
"E07000068" => "Brentwood",
"E06000043" => "Brighton and Hove",
"E06000023" => "Bristol, City of",
"E07000144" => "Broadland",
"E09000006" => "Bromley",
"E07000234" => "Bromsgrove",
"E07000095" => "Broxbourne",
"E07000172" => "Broxtowe",
"E06000060" => "Buckinghamshire",
"E07000117" => "Burnley",
"E08000002" => "Bury",
"E08000033" => "Calderdale",
"E07000008" => "Cambridge",
"E09000007" => "Camden",
"E07000192" => "Cannock Chase",
"E07000106" => "Canterbury",
"E07000028" => "Carlisle",
"E07000069" => "Castle Point",
"E06000056" => "Central Bedfordshire",
"E07000130" => "Charnwood",
"E07000070" => "Chelmsford",
"E07000078" => "Cheltenham",
"E07000177" => "Cherwell",
"E06000049" => "Cheshire East",
"E06000050" => "Cheshire West and Chester",
"E07000034" => "Chesterfield",
"E07000225" => "Chichester",
"E07000118" => "Chorley",
"E09000001" => "City of London",
"E07000071" => "Colchester",
"E07000029" => "Copeland",
"E07000150" => "Corby",
"E06000052" => "Cornwall",
"E07000079" => "Cotswold",
"E06000047" => "County Durham",
"E08000026" => "Coventry",
"E07000163" => "Craven",
"E07000226" => "Crawley",
"E09000008" => "Croydon",
"E07000096" => "Dacorum",
"E06000005" => "Darlington",
"E07000107" => "Dartford",
"E07000151" => "Daventry",
"E06000015" => "Derby",
"E07000035" => "Derbyshire Dales",
"E08000017" => "Doncaster",
"E06000059" => "Dorset",
"E07000108" => "Dover",
"E08000027" => "Dudley",
"E09000009" => "Ealing",
"E07000009" => "East Cambridgeshire",
"E07000040" => "East Devon",
"E07000085" => "East Hampshire",
"E07000242" => "East Hertfordshire",
"E07000137" => "East Lindsey",
"E07000152" => "East Northamptonshire",
"E06000011" => "East Riding of Yorkshire",
"E07000193" => "East Staffordshire",
"E07000244" => "East Suffolk",
"E07000061" => "Eastbourne",
"E07000086" => "Eastleigh",
"E07000030" => "Eden",
"E07000207" => "Elmbridge",
"E09000010" => "Enfield",
"E07000072" => "Epping Forest",
"E07000208" => "Epsom and Ewell",
"E07000036" => "Erewash",
"E07000041" => "Exeter",
"E07000087" => "Fareham",
"E07000010" => "Fenland",
"E07000112" => "Folkestone and Hythe",
"E07000080" => "Forest of Dean",
"E07000119" => "Fylde",
"E08000037" => "Gateshead",
"E07000173" => "Gedling",
"E07000081" => "Gloucester",
"E07000088" => "Gosport",
"E07000109" => "Gravesham",
"E07000145" => "Great Yarmouth",
"E09000011" => "Greenwich",
"E07000209" => "Guildford",
"W06000002" => "Gwynedd",
"E09000012" => "Hackney",
"E06000006" => "Halton",
"E07000164" => "Hambleton",
"E09000013" => "Hammersmith and Fulham",
"E07000131" => "Harborough",
"E09000014" => "Haringey",
"E07000073" => "Harlow",
"E07000165" => "Harrogate",
"E09000015" => "Harrow",
"E07000089" => "Hart",
"E06000001" => "Hartlepool",
"E07000062" => "Hastings",
"E07000090" => "Havant",
"E09000016" => "Havering",
"E06000019" => "Herefordshire, County of",
"E07000098" => "Hertsmere",
"E07000037" => "High Peak",
"S12000017" => "Highland",
"E09000017" => "Hillingdon",
"E07000132" => "Hinckley and Bosworth",
"E07000227" => "Horsham",
"E09000018" => "Hounslow",
"E07000011" => "Huntingdonshire",
"E07000120" => "Hyndburn",
"E07000202" => "Ipswich",
"E06000046" => "Isle of Wight",
"E06000053" => "Isles of Scilly",
"E09000019" => "Islington",
"E09000020" => "Kensington and Chelsea",
"E07000153" => "Kettering",
"E07000146" => "King’s Lynn and West Norfolk",
"E06000010" => "Kingston upon Hull, City of",
"E09000021" => "Kingston upon Thames",
"E08000034" => "Kirklees",
"E08000011" => "Knowsley",
"E09000022" => "Lambeth",
"E07000121" => "Lancaster",
"E08000035" => "Leeds",
"E06000016" => "Leicester",
"E07000063" => "Lewes",
"E09000023" => "Lewisham",
"E07000194" => "Lichfield",
"E07000138" => "Lincoln",
"E08000012" => "Liverpool",
"E06000032" => "Luton",
"E07000110" => "Maidstone",
"E07000074" => "Maldon",
"E07000235" => "Malvern Hills",
"E08000003" => "Manchester",
"E07000174" => "Mansfield",
"E06000035" => "Medway",
"E07000133" => "Melton",
"E07000187" => "Mendip",
"E09000024" => "Merton",
"E07000042" => "Mid Devon",
"E07000203" => "Mid Suffolk",
"E07000228" => "Mid Sussex",
"E06000002" => "Middlesbrough",
"E06000042" => "Milton Keynes",
"E07000210" => "Mole Valley",
"E07000091" => "New Forest",
"E07000175" => "Newark and Sherwood",
"E08000021" => "Newcastle upon Tyne",
"E07000195" => "Newcastle-under-Lyme",
"E09000025" => "Newham",
"E07000043" => "North Devon",
"E07000038" => "North East Derbyshire",
"E06000012" => "North East Lincolnshire",
"E07000099" => "North Hertfordshire",
"E07000139" => "North Kesteven",
"E06000013" => "North Lincolnshire",
"E07000147" => "North Norfolk",
"E06000024" => "North Somerset",
"E08000022" => "North Tyneside",
"E07000218" => "North Warwickshire",
"E07000134" => "North West Leicestershire",
"E07000154" => "Northampton",
"E06000057" => "Northumberland",
"E07000148" => "Norwich",
"E06000018" => "Nottingham",
"E07000219" => "Nuneaton and Bedworth",
"E07000135" => "Oadby and Wigston",
"E08000004" => "Oldham",
"E07000178" => "Oxford",
"E07000122" => "Pendle",
"E06000031" => "Peterborough",
"E06000026" => "Plymouth",
"E06000044" => "Portsmouth",
"E07000123" => "Preston",
"E06000038" => "Reading",
"E09000026" => "Redbridge",
"E06000003" => "Redcar and Cleveland",
"E07000236" => "Redditch",
"E07000211" => "Reigate and Banstead",
"E07000124" => "Ribble Valley",
"E09000027" => "Richmond upon Thames",
"E07000166" => "Richmondshire",
"E08000005" => "Rochdale",
"E07000075" => "Rochford",
"E07000125" => "Rossendale",
"E07000064" => "Rother",
"E08000018" => "Rotherham",
"E07000220" => "Rugby",
"E07000212" => "Runnymede",
"E07000176" => "Rushcliffe",
"E07000092" => "Rushmoor",
"E06000017" => "Rutland",
"E07000167" => "Ryedale",
"E08000006" => "Salford",
"E08000028" => "Sandwell",
"E07000168" => "Scarborough",
"E07000188" => "Sedgemoor",
"E08000014" => "Sefton",
"E07000169" => "Selby",
"E07000111" => "Sevenoaks",
"E08000019" => "Sheffield",
"E06000051" => "Shropshire",
"E06000039" => "Slough",
"E08000029" => "Solihull",
"E07000246" => "Somerset West and Taunton",
"E07000012" => "South Cambridgeshire",
"E07000039" => "South Derbyshire",
"E06000025" => "South Gloucestershire",
"E07000044" => "South Hams",
"E07000140" => "South Holland",
"E07000141" => "South Kesteven",
"E07000031" => "South Lakeland",
"E07000149" => "South Norfolk",
"E07000155" => "South Northamptonshire",
"E07000179" => "South Oxfordshire",
"E07000126" => "South Ribble",
"E07000189" => "South Somerset",
"E07000196" => "South Staffordshire",
"E08000023" => "South Tyneside",
"E06000045" => "Southampton",
"E06000033" => "Southend-on-Sea",
"E09000028" => "Southwark",
"E07000213" => "Spelthorne",
"E07000240" => "St Albans",
"E08000013" => "St. Helens",
"E07000197" => "Stafford",
"E07000198" => "Staffordshire Moorlands",
"E07000243" => "Stevenage",
"E08000007" => "Stockport",
"E06000004" => "Stockton-on-Tees",
"E06000021" => "Stoke-on-Trent",
"E07000221" => "Stratford-on-Avon",
"E07000082" => "Stroud",
"E08000024" => "Sunderland",
"E07000214" => "Surrey Heath",
"E09000029" => "Sutton",
"E07000113" => "Swale",
"E06000030" => "Swindon",
"E08000008" => "Tameside",
"E07000199" => "Tamworth",
"E07000215" => "Tandridge",
"E07000045" => "Teignbridge",
"E06000020" => "Telford and Wrekin",
"E07000076" => "Tendring",
"E07000093" => "Test Valley",
"E07000083" => "Tewkesbury",
"E07000114" => "Thanet",
"E07000102" => "Three Rivers",
"E06000034" => "Thurrock",
"E07000115" => "Tonbridge and Malling",
"E06000027" => "Torbay",
"E07000046" => "Torridge",
"E09000030" => "Tower Hamlets",
"E08000009" => "Trafford",
"E07000116" => "Tunbridge Wells",
"E07000077" => "Uttlesford",
"E07000180" => "Vale of White Horse",
"E08000036" => "Wakefield",
"E08000030" => "Walsall",
"E09000031" => "Waltham Forest",
"E09000032" => "Wandsworth",
"E06000007" => "Warrington",
"E07000222" => "Warwick",
"E07000103" => "Watford",
"E07000216" => "Waverley",
"E07000065" => "Wealden",
"E07000156" => "Wellingborough",
"E07000241" => "Welwyn Hatfield",
"E06000037" => "West Berkshire",
"E07000047" => "West Devon",
"E07000127" => "West Lancashire",
"E07000142" => "West Lindsey",
"E07000181" => "West Oxfordshire",
"E07000245" => "West Suffolk",
"E09000033" => "Westminster",
"E08000010" => "Wigan",
"E06000054" => "Wiltshire",
"E07000094" => "Winchester",
"E06000040" => "Windsor and Maidenhead",
"E08000015" => "Wirral",
"E07000217" => "Woking",
"E06000041" => "Wokingham",
"E08000031" => "Wolverhampton",
"E07000237" => "Worcester",
"E07000229" => "Worthing",
"E07000238" => "Wychavon",
"E07000128" => "Wyre",
"E07000239" => "Wyre Forest",
"E06000014" => "York",
}.freeze
end

24
app/models/form/sales/questions/property_local_authority_known.rb

@ -0,0 +1,24 @@
class Form::Sales::Questions::PropertyLocalAuthorityKnown < ::Form::Question
def initialize(id, hsh, page)
super
@id = "la_known"
@check_answer_label = "Local authority known"
@header = "Do you know the local authority of the property?"
@type = "radio"
@answer_options = ANSWER_OPTIONS
@conditional_for = { "la" => [1] }
@hidden_in_check_answers = {
"depends_on" => [
{
"la_known" => 1,
},
],
}
@page = page
end
ANSWER_OPTIONS = {
"1" => { "value" => "Yes" },
"0" => { "value" => "No" },
}.freeze
end

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

@ -13,6 +13,7 @@ class Form::Sales::Subsections::PropertyInformation < ::Form::Subsection
Form::Sales::Pages::PropertyBuildingType.new(nil, nil, self),
Form::Sales::Pages::PropertyUnitType.new(nil, nil, self),
Form::Sales::Pages::PropertyPostcode.new(nil, nil, self),
Form::Sales::Pages::PropertyLocalAuthority.new(nil, nil, self),
]
end
end

1
app/models/la_rent_range.rb

@ -1,2 +1,3 @@
class LaRentRange < ApplicationRecord
MAX_BEDS = 4
end

10
app/models/lettings_log.rb

@ -435,13 +435,19 @@ class LettingsLog < Log
Csv::LettingsLogCsvService.new(user).to_csv
end
def beds_for_la_rent_range
return 0 if is_supported_housing?
beds.nil? ? nil : [beds, LaRentRange::MAX_BEDS].min
end
def soft_min_for_period
soft_min = LaRentRange.find_by(start_year: collection_start_year, la:, beds:, lettype:).soft_min
soft_min = LaRentRange.find_by(start_year: collection_start_year, la:, beds: beds_for_la_rent_range, lettype:).soft_min
"#{soft_value_for_period(soft_min)} #{SUFFIX_FROM_PERIOD[period].presence || 'every week'}"
end
def soft_max_for_period
soft_max = LaRentRange.find_by(start_year: collection_start_year, la:, beds:, lettype:).soft_max
soft_max = LaRentRange.find_by(start_year: collection_start_year, la:, beds: beds_for_la_rent_range, lettype:).soft_max
"#{soft_value_for_period(soft_max)} #{SUFFIX_FROM_PERIOD[period].presence || 'every week'}"
end

321
app/models/location.rb

@ -17,6 +17,327 @@ class Location < ApplicationRecord
scope :started, -> { where("startdate <= ?", Time.zone.today).or(where(startdate: nil)) }
scope :active, -> { where(confirmed: true).and(started) }
LOCAL_AUTHORITIES = {
"E07000223": "Adur",
"E07000026": "Allerdale",
"E07000032": "Amber Valley",
"E07000224": "Arun",
"E07000170": "Ashfield",
"E07000105": "Ashford",
"E07000200": "Babergh",
"E09000002": "Barking and Dagenham",
"E09000003": "Barnet",
"E08000016": "Barnsley",
"E07000027": "Barrow-in-Furness",
"E07000066": "Basildon",
"E07000084": "Basingstoke and Deane",
"E07000171": "Bassetlaw",
"E06000022": "Bath and North East Somerset",
"E06000055": "Bedford",
"E09000004": "Bexley",
"E08000025": "Birmingham",
"E07000129": "Blaby",
"E06000008": "Blackburn with Darwen",
"E06000009": "Blackpool",
"E07000033": "Bolsover",
"E08000001": "Bolton",
"E07000136": "Boston",
"E06000058": "Bournemouth, Christchurch and Poole",
"E06000036": "Bracknell Forest",
"E08000032": "Bradford",
"E07000067": "Braintree",
"E07000143": "Breckland",
"E09000005": "Brent",
"E07000068": "Brentwood",
"E06000043": "Brighton and Hove",
"E06000023": "Bristol, City of",
"E07000144": "Broadland",
"E09000006": "Bromley",
"E07000234": "Bromsgrove",
"E07000095": "Broxbourne",
"E07000172": "Broxtowe",
"E06000060": "Buckinghamshire",
"E07000117": "Burnley",
"E08000002": "Bury",
"E08000033": "Calderdale",
"E07000008": "Cambridge",
"E09000007": "Camden",
"E07000192": "Cannock Chase",
"E07000106": "Canterbury",
"E07000028": "Carlisle",
"E07000069": "Castle Point",
"E06000056": "Central Bedfordshire",
"E07000130": "Charnwood",
"E07000070": "Chelmsford",
"E07000078": "Cheltenham",
"E07000177": "Cherwell",
"E06000049": "Cheshire East",
"E06000050": "Cheshire West and Chester",
"E07000034": "Chesterfield",
"E07000225": "Chichester",
"E07000118": "Chorley",
"E09000001": "City of London",
"E07000071": "Colchester",
"E07000029": "Copeland",
"E07000150": "Corby",
"E06000052": "Cornwall",
"E07000079": "Cotswold",
"E06000047": "County Durham",
"E08000026": "Coventry",
"E07000163": "Craven",
"E07000226": "Crawley",
"E09000008": "Croydon",
"E07000096": "Dacorum",
"E06000005": "Darlington",
"E07000107": "Dartford",
"E07000151": "Daventry",
"E06000015": "Derby",
"E07000035": "Derbyshire Dales",
"E08000017": "Doncaster",
"E06000059": "Dorset",
"E07000108": "Dover",
"E08000027": "Dudley",
"E09000009": "Ealing",
"E07000009": "East Cambridgeshire",
"E07000040": "East Devon",
"E07000085": "East Hampshire",
"E07000242": "East Hertfordshire",
"E07000137": "East Lindsey",
"E07000152": "East Northamptonshire",
"E06000011": "East Riding of Yorkshire",
"E07000193": "East Staffordshire",
"E07000244": "East Suffolk",
"E07000061": "Eastbourne",
"E07000086": "Eastleigh",
"E07000030": "Eden",
"E07000207": "Elmbridge",
"E09000010": "Enfield",
"E07000072": "Epping Forest",
"E07000208": "Epsom and Ewell",
"E07000036": "Erewash",
"E07000041": "Exeter",
"E07000087": "Fareham",
"E07000010": "Fenland",
"E07000112": "Folkestone and Hythe",
"E07000080": "Forest of Dean",
"E07000119": "Fylde",
"E08000037": "Gateshead",
"E07000173": "Gedling",
"E07000081": "Gloucester",
"E07000088": "Gosport",
"E07000109": "Gravesham",
"E07000145": "Great Yarmouth",
"E09000011": "Greenwich",
"E07000209": "Guildford",
"W06000002": "Gwynedd",
"E09000012": "Hackney",
"E06000006": "Halton",
"E07000164": "Hambleton",
"E09000013": "Hammersmith and Fulham",
"E07000131": "Harborough",
"E09000014": "Haringey",
"E07000073": "Harlow",
"E07000165": "Harrogate",
"E09000015": "Harrow",
"E07000089": "Hart",
"E06000001": "Hartlepool",
"E07000062": "Hastings",
"E07000090": "Havant",
"E09000016": "Havering",
"E06000019": "Herefordshire, County of",
"E07000098": "Hertsmere",
"E07000037": "High Peak",
"S12000017": "Highland",
"E09000017": "Hillingdon",
"E07000132": "Hinckley and Bosworth",
"E07000227": "Horsham",
"E09000018": "Hounslow",
"E07000011": "Huntingdonshire",
"E07000120": "Hyndburn",
"E07000202": "Ipswich",
"E06000046": "Isle of Wight",
"E06000053": "Isles of Scilly",
"E09000019": "Islington",
"E09000020": "Kensington and Chelsea",
"E07000153": "Kettering",
"E07000146": "King’s Lynn and West Norfolk",
"E06000010": "Kingston upon Hull, City of",
"E09000021": "Kingston upon Thames",
"E08000034": "Kirklees",
"E08000011": "Knowsley",
"E09000022": "Lambeth",
"E07000121": "Lancaster",
"E08000035": "Leeds",
"E06000016": "Leicester",
"E07000063": "Lewes",
"E09000023": "Lewisham",
"E07000194": "Lichfield",
"E07000138": "Lincoln",
"E08000012": "Liverpool",
"E06000032": "Luton",
"E07000110": "Maidstone",
"E07000074": "Maldon",
"E07000235": "Malvern Hills",
"E08000003": "Manchester",
"E07000174": "Mansfield",
"E06000035": "Medway",
"E07000133": "Melton",
"E07000187": "Mendip",
"E09000024": "Merton",
"E07000042": "Mid Devon",
"E07000203": "Mid Suffolk",
"E07000228": "Mid Sussex",
"E06000002": "Middlesbrough",
"E06000042": "Milton Keynes",
"E07000210": "Mole Valley",
"E07000091": "New Forest",
"E07000175": "Newark and Sherwood",
"E08000021": "Newcastle upon Tyne",
"E07000195": "Newcastle-under-Lyme",
"E09000025": "Newham",
"E07000043": "North Devon",
"E07000038": "North East Derbyshire",
"E06000012": "North East Lincolnshire",
"E07000099": "North Hertfordshire",
"E07000139": "North Kesteven",
"E06000013": "North Lincolnshire",
"E07000147": "North Norfolk",
"E06000024": "North Somerset",
"E08000022": "North Tyneside",
"E07000218": "North Warwickshire",
"E07000134": "North West Leicestershire",
"E07000154": "Northampton",
"E06000057": "Northumberland",
"E07000148": "Norwich",
"E06000018": "Nottingham",
"E07000219": "Nuneaton and Bedworth",
"E07000135": "Oadby and Wigston",
"E08000004": "Oldham",
"E07000178": "Oxford",
"E07000122": "Pendle",
"E06000031": "Peterborough",
"E06000026": "Plymouth",
"E06000044": "Portsmouth",
"E07000123": "Preston",
"E06000038": "Reading",
"E09000026": "Redbridge",
"E06000003": "Redcar and Cleveland",
"E07000236": "Redditch",
"E07000211": "Reigate and Banstead",
"E07000124": "Ribble Valley",
"E09000027": "Richmond upon Thames",
"E07000166": "Richmondshire",
"E08000005": "Rochdale",
"E07000075": "Rochford",
"E07000125": "Rossendale",
"E07000064": "Rother",
"E08000018": "Rotherham",
"E07000220": "Rugby",
"E07000212": "Runnymede",
"E07000176": "Rushcliffe",
"E07000092": "Rushmoor",
"E06000017": "Rutland",
"E07000167": "Ryedale",
"E08000006": "Salford",
"E08000028": "Sandwell",
"E07000168": "Scarborough",
"E07000188": "Sedgemoor",
"E08000014": "Sefton",
"E07000169": "Selby",
"E07000111": "Sevenoaks",
"E08000019": "Sheffield",
"E06000051": "Shropshire",
"E06000039": "Slough",
"E08000029": "Solihull",
"E07000246": "Somerset West and Taunton",
"E07000012": "South Cambridgeshire",
"E07000039": "South Derbyshire",
"E06000025": "South Gloucestershire",
"E07000044": "South Hams",
"E07000140": "South Holland",
"E07000141": "South Kesteven",
"E07000031": "South Lakeland",
"E07000149": "South Norfolk",
"E07000155": "South Northamptonshire",
"E07000179": "South Oxfordshire",
"E07000126": "South Ribble",
"E07000189": "South Somerset",
"E07000196": "South Staffordshire",
"E08000023": "South Tyneside",
"E06000045": "Southampton",
"E06000033": "Southend-on-Sea",
"E09000028": "Southwark",
"E07000213": "Spelthorne",
"E07000240": "St Albans",
"E08000013": "St. Helens",
"E07000197": "Stafford",
"E07000198": "Staffordshire Moorlands",
"E07000243": "Stevenage",
"E08000007": "Stockport",
"E06000004": "Stockton-on-Tees",
"E06000021": "Stoke-on-Trent",
"E07000221": "Stratford-on-Avon",
"E07000082": "Stroud",
"E08000024": "Sunderland",
"E07000214": "Surrey Heath",
"E09000029": "Sutton",
"E07000113": "Swale",
"E06000030": "Swindon",
"E08000008": "Tameside",
"E07000199": "Tamworth",
"E07000215": "Tandridge",
"E07000045": "Teignbridge",
"E06000020": "Telford and Wrekin",
"E07000076": "Tendring",
"E07000093": "Test Valley",
"E07000083": "Tewkesbury",
"E07000114": "Thanet",
"E07000102": "Three Rivers",
"E06000034": "Thurrock",
"E07000115": "Tonbridge and Malling",
"E06000027": "Torbay",
"E07000046": "Torridge",
"E09000030": "Tower Hamlets",
"E08000009": "Trafford",
"E07000116": "Tunbridge Wells",
"E07000077": "Uttlesford",
"E07000180": "Vale of White Horse",
"E08000036": "Wakefield",
"E08000030": "Walsall",
"E09000031": "Waltham Forest",
"E09000032": "Wandsworth",
"E06000007": "Warrington",
"E07000222": "Warwick",
"E07000103": "Watford",
"E07000216": "Waverley",
"E07000065": "Wealden",
"E07000156": "Wellingborough",
"E07000241": "Welwyn Hatfield",
"E06000037": "West Berkshire",
"E07000047": "West Devon",
"E07000127": "West Lancashire",
"E07000142": "West Lindsey",
"E07000181": "West Oxfordshire",
"E07000245": "West Suffolk",
"E09000033": "Westminster",
"E08000010": "Wigan",
"E06000054": "Wiltshire",
"E07000094": "Winchester",
"E06000040": "Windsor and Maidenhead",
"E08000015": "Wirral",
"E07000217": "Woking",
"E06000041": "Wokingham",
"E08000031": "Wolverhampton",
"E07000237": "Worcester",
"E07000229": "Worthing",
"E07000238": "Wychavon",
"E07000128": "Wyre",
"E07000239": "Wyre Forest",
"E06000014": "York",
}.freeze
enum local_authorities: LOCAL_AUTHORITIES
MOBILITY_TYPE = {
"Wheelchair-user standard": "W",
"Fitted with equipment and adaptations": "A",

12
app/models/organisation.rb

@ -13,9 +13,13 @@ class Organisation < ApplicationRecord
has_many :child_organisation_relationships, foreign_key: :parent_organisation_id, class_name: "OrganisationRelationship"
has_many :child_organisations, through: :child_organisation_relationships
has_many :housing_provider_relationships, -> { where(relationship_type: OrganisationRelationship::OWNING) }, foreign_key: :child_organisation_id, class_name: "OrganisationRelationship"
has_many :housing_providers, through: :housing_provider_relationships, source: :parent_organisation
has_many :managing_agent_relationships, -> { where(relationship_type: OrganisationRelationship::MANAGING) }, foreign_key: :parent_organisation_id, class_name: "OrganisationRelationship"
has_many :managing_agents, through: :managing_agent_relationships, source: :child_organisation
scope :search_by_name, ->(name) { where("name ILIKE ?", "%#{name}%") }
scope :search_by, ->(param) { search_by_name(param) }
has_paper_trail
auto_strip_attributes :name
@ -82,9 +86,9 @@ class Organisation < ApplicationRecord
{ name: "Registration number", value: housing_registration_no || "", editable: false },
{ name: "Rent_periods", value: rent_period_labels, editable: false, format: :bullet },
{ name: "Owns housing stock", value: holds_own_stock ? "Yes" : "No", editable: false },
{ name: "Other stock owners", value: other_stock_owners, editable: false },
{ name: "Managing agents", value: managing_agents, editable: false },
({ name: "Other stock owners", value: other_stock_owners, editable: false } unless FeatureToggle.managing_owning_enabled?),
({ name: "Managing agents", value: managing_agents_label, editable: false } unless FeatureToggle.managing_owning_enabled?),
{ name: "Data protection agreement", value: data_protection_agreement_string, editable: false },
]
].compact
end
end

9
app/models/organisation_relationship.rb

@ -1,4 +1,13 @@
class OrganisationRelationship < ApplicationRecord
belongs_to :child_organisation, class_name: "Organisation"
belongs_to :parent_organisation, class_name: "Organisation"
OWNING = "owning".freeze
MANAGING = "managing".freeze
RELATIONSHIP_TYPE = {
OWNING => 0,
MANAGING => 1,
}.freeze
enum relationship_type: RELATIONSHIP_TYPE
end

30
app/models/validations/financial_validations.rb

@ -181,15 +181,31 @@ private
return if record.startdate.blank?
collection_year = record.collection_start_year
rent_range = LaRentRange.find_by(start_year: collection_year, la: record.la, beds: record.beds, lettype: record.lettype)
rent_range = LaRentRange.find_by(start_year: collection_year, la: record.la, beds: record.beds_for_la_rent_range, lettype: record.lettype)
if rent_range.present? && !weekly_value_in_range(record, "brent", rent_range.hard_min, rent_range.hard_max) && record.brent.present? && record.period.present?
record.errors.add :brent, I18n.t("validations.financial.brent.not_in_range")
record.errors.add :beds, I18n.t("validations.financial.brent.beds.not_in_range")
record.errors.add :la, I18n.t("validations.financial.brent.la.not_in_range")
record.errors.add :rent_type, I18n.t("validations.financial.brent.rent_type.not_in_range")
record.errors.add :needstype, I18n.t("validations.financial.brent.needstype.not_in_range")
record.errors.add :period, I18n.t("validations.financial.brent.period.not_in_range")
if record.weekly_value(record["brent"]) < rent_range.hard_min
record.errors.add :brent, I18n.t("validations.financial.brent.below_hard_min")
record.errors.add :beds, I18n.t("validations.financial.brent.beds.below_hard_min")
record.errors.add :la, I18n.t("validations.financial.brent.la.below_hard_min")
record.errors.add :postcode_known, I18n.t("validations.financial.brent.postcode_known.below_hard_min")
record.errors.add :scheme_id, I18n.t("validations.financial.brent.scheme_id.below_hard_min")
record.errors.add :location_id, I18n.t("validations.financial.brent.location_id.below_hard_min")
record.errors.add :rent_type, I18n.t("validations.financial.brent.rent_type.below_hard_min")
record.errors.add :needstype, I18n.t("validations.financial.brent.needstype.below_hard_min")
record.errors.add :period, I18n.t("validations.financial.brent.period.below_hard_min")
elsif record.beds.blank? || record.beds < LaRentRange::MAX_BEDS
record.errors.add :brent, I18n.t("validations.financial.brent.above_hard_max")
record.errors.add :beds, I18n.t("validations.financial.brent.beds.above_hard_max")
record.errors.add :la, I18n.t("validations.financial.brent.la.above_hard_max")
record.errors.add :postcode_known, I18n.t("validations.financial.brent.postcode_known.above_hard_max")
record.errors.add :scheme_id, I18n.t("validations.financial.brent.scheme_id.above_hard_max")
record.errors.add :location_id, I18n.t("validations.financial.brent.location_id.above_hard_max")
record.errors.add :rent_type, I18n.t("validations.financial.brent.rent_type.above_hard_max")
record.errors.add :needstype, I18n.t("validations.financial.brent.needstype.above_hard_max")
record.errors.add :period, I18n.t("validations.financial.brent.period.above_hard_max")
end
end
end
end

4
app/models/validations/property_validations.rb

@ -55,8 +55,8 @@ module Validations::PropertyValidations
end
def validate_shared_housing_rooms(record)
if record.beds.present? && record.beds.negative?
record.errors.add :beds, I18n.t("validations.property.beds.negative")
if record.beds.present? && record.beds <= 0
record.errors.add :beds, I18n.t("validations.property.beds.non_positive")
end
unless record.unittype_gn.nil?

10
app/models/validations/soft_validations.rb

@ -28,15 +28,19 @@ module Validations::SoftValidations
def rent_in_soft_min_range?
return unless brent && weekly_value(brent) && startdate
rent_range = LaRentRange.find_by(start_year: collection_start_year, la:, beds:, lettype: get_lettype)
rent_range = LaRentRange.find_by(start_year: collection_start_year, la:, beds: beds_for_la_rent_range, lettype: get_lettype)
rent_range.present? && weekly_value(brent).between?(rent_range.hard_min, rent_range.soft_min)
end
def rent_in_soft_max_range?
return unless brent && weekly_value(brent) && startdate
rent_range = LaRentRange.find_by(start_year: collection_start_year, la:, beds:, lettype: get_lettype)
rent_range.present? && weekly_value(brent).between?(rent_range.soft_max, rent_range.hard_max)
rent_range = LaRentRange.find_by(start_year: collection_start_year, la:, beds: beds_for_la_rent_range, lettype: get_lettype)
if beds.present? && rent_range.present? && beds > LaRentRange::MAX_BEDS
weekly_value(brent) > rent_range.soft_max
elsif rent_range.present?
weekly_value(brent).between?(rent_range.soft_max, rent_range.hard_max)
end
end
(1..8).each do |person_num|

9
app/services/exports/lettings_log_export_service.rb

@ -176,9 +176,12 @@ module Exports
attribute_hash["manhcnum"] = lettings_log.managing_organisation.housing_registration_no
end
# Mapping which would require a change in our data model
attribute_hash["createddate"] = attribute_hash["created_at"]
attribute_hash["uploaddate"] = attribute_hash["updated_at"]
# Covert date times to ISO 8601
attribute_hash["createddate"] = lettings_log.created_at&.iso8601
attribute_hash["uploaddate"] = lettings_log.updated_at&.iso8601
attribute_hash["mrcdate"] = lettings_log.mrcdate&.iso8601
attribute_hash["startdate"] = lettings_log.startdate&.iso8601
attribute_hash["voiddate"] = lettings_log.voiddate&.iso8601
attribute_hash["cbl"] = 2 if attribute_hash["cbl"]&.zero?
attribute_hash["cap"] = 2 if attribute_hash["cap"]&.zero?

2
app/views/form/review.html.erb

@ -1,7 +1,7 @@
<% content_for :title, "Review lettings log" %>
<% content_for :breadcrumbs, govuk_breadcrumbs(breadcrumbs: {
"Logs" => "/logs",
"Log #{@log.id}" => "/logs/#{@log.id}",
"Log #{@log.id}" => "/lettings-logs/#{@log.id}",
"Review lettings log" => "",
}) %>

6
app/views/locations/edit_local_authority.html.erb

@ -12,12 +12,8 @@
<%= render partial: "organisations/headings", locals: { main: "What is the local authority of #{@location.postcode}?", sub: @scheme.service_name } %>
<% la_list = FormHandler.instance.current_lettings_form.get_question("la", nil).answer_options.values %>
<% las = la_list.map { |la| OpenStruct.new(name: la) } %>
<%= f.govuk_collection_select :location_admin_district,
las,
local_authorities_selection,
:name,
:name,
label: { hidden: true },

8
app/views/logs/index.html.erb

@ -1,9 +1,13 @@
<% item_label = format_label(@pagy.count, "log") %>
<% item_label = format_label(@pagy.count, "logs") %>
<% title = format_title(@searched, "Logs", current_user, item_label, @pagy.count, nil) %>
<% content_for :title, title %>
<%= render partial: "organisations/headings", locals: current_user.support? ? { main: "Logs", sub: nil } : { main: "Logs", sub: current_user.organisation.name } %>
<% if current_page?(controller: 'lettings_logs', action: 'index') %>
<%= render partial: "organisations/headings", locals: current_user.support? ? { main: "Lettings logs", sub: nil } : { main: "Lettings logs", sub: current_user.organisation.name } %>
<% elsif current_page?(controller: 'sales_logs', action: 'index') %>
<%= render partial: "organisations/headings", locals: current_user.support? ? { main: "Sales logs", sub: nil } : { main: "Sales logs", sub: current_user.organisation.name } %>
<% end %>
<div class="app-filter-layout" data-controller="filter-layout">
<div class="govuk-button-group app-filter-toggle">

22
app/views/organisation_relationships/_housing_provider_list.erb

@ -0,0 +1,22 @@
<section class="app-table-group" tabindex="0" aria-labelledby="<%= title.dasherize %>">
<%= govuk_table do |table| %>
<%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %>
<%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "housing providers", path: request.path)) %>
<% end %>
<% @housing_providers.each do |housing_provider| %>
<%= table.body do |body| %>
<%= body.row do |row| %>
<% row.cell(text: housing_provider.name) %>
<% if current_user.data_coordinator? || current_user.support? %>
<% row.cell(html_attributes: {
scope: "row",
class: "govuk-!-text-align-right",
}) do %>
<%= govuk_link_to("Remove", housing_providers_remove_organisation_path(target_organisation_id: housing_provider.id)) %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
</section>

22
app/views/organisation_relationships/_managing_agent_list.erb

@ -0,0 +1,22 @@
<section class="app-table-group" tabindex="0" aria-labelledby="<%= title.dasherize %>">
<%= govuk_table do |table| %>
<%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %>
<%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "agents", path: request.path)) %>
<% end %>
<% @managing_agents.each do |managing_agent| %>
<%= table.body do |body| %>
<%= body.row do |row| %>
<% row.cell(text: managing_agent.name) %>
<% if current_user.data_coordinator? || current_user.support? %>
<% row.cell(html_attributes: {
scope: "row",
class: "govuk-!-text-align-right",
}) do %>
<%= govuk_link_to("Remove", managing_agents_remove_organisation_path(target_organisation_id: managing_agent.id)) %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
</section>

3
app/views/organisation_relationships/_related_organisation_select_question.html.erb

@ -0,0 +1,3 @@
<% answers = question.answer_options.map { |key, value| OpenStruct.new(id: key, name: value) } %>
<%= f.govuk_collection_select :related_organisation_id, answers, :id, :name, label: { hidden: true }, "data-controller": "accessible-autocomplete" do %>
<% end %>

34
app/views/organisation_relationships/add_housing_provider.html.erb

@ -0,0 +1,34 @@
<%= form_with model: @organisation, url: housing_providers_organisation_path, method: "post", local: true do |f| %>
<% if current_user.support? %>
<%= render partial: "organisations/headings", locals: { main: @organisation.name, sub: nil } %>
<%= render SubNavigationComponent.new(items: secondary_items(request.path, @organisation.id)) %>
<h2 class="govuk-visually-hidden">Add Housing Provider</h2>
<%= govuk_back_link(href: :back) %>
<%= render partial: "organisations/headings", locals: { main: "What is the name of this organisation's housing provider?", sub: nil } %>
<p class="govuk-body">Start typing to search for a housing provider</p>
<% else %>
<% content_for :before_content do %>
<%= govuk_back_link(href: :back) %>
<% end %>
<%= render partial: "organisations/headings", locals: { main: "What is the name of your housing provider?", sub: nil } %>
<p class="govuk-body">Start typing to search for your housing provider</p>
<% end %>
<% answer_options = { "" => "Select an option" } %>
<% @organisations.each do |organisation| %>
<% answer_options[organisation[0]] = organisation[1] %>
<% end %>
<%= render partial: "organisation_relationships/related_organisation_select_question", locals: {
question: Form::Question.new("", { "answer_options" => answer_options }, nil),
f:,
} %>
<%= f.govuk_submit "Add" %>
<%= govuk_details(summary_text: "Can't find the housing provider you're looking for?") do %>
<ul class="govuk-list govuk-list--bullet">
<li>Double check the spelling and try again</li>
<li>Type the first few letters to see the suggestions</li>
<li>If you still can't find it,
<%= govuk_link_to("contact the DLUHC service desk", "https://digital.dclg.gov.uk/jira/servicedesk/customer/portal/4/group/21", rel: "noreferrer noopener", target: "_blank") %>
</li>
</ul>
<% end %>
<% end %>

34
app/views/organisation_relationships/add_managing_agent.html.erb

@ -0,0 +1,34 @@
<%= form_with model: @organisation, url: managing_agents_organisation_path, method: "post", local: true do |f| %>
<% if current_user.support? %>
<%= render partial: "organisations/headings", locals: { main: @organisation.name, sub: nil } %>
<%= render SubNavigationComponent.new(items: secondary_items(request.path, @organisation.id)) %>
<h2 class="govuk-visually-hidden">Add Managing Agent</h2>
<%= govuk_back_link(href: :back) %>
<%= render partial: "organisations/headings", locals: { main: "What is the name of this organisation's managing agent?", sub: nil } %>
<p class="govuk-body">Start typing to search for a managing agent</p>
<% else %>
<% content_for :before_content do %>
<%= govuk_back_link(href: :back) %>
<% end %>
<%= render partial: "organisations/headings", locals: { main: "What is the name of your managing agent?", sub: nil } %>
<p class="govuk-body">Start typing to search for your managing agent</p>
<% end %>
<% answer_options = { "" => "Select an option" } %>
<% @organisations.each do |organisation| %>
<% answer_options[organisation[0]] = organisation[1] %>
<% end %>
<%= render partial: "organisation_relationships/related_organisation_select_question", locals: {
question: Form::Question.new("", { "answer_options" => answer_options }, nil),
f:,
} %>
<%= f.govuk_submit "Add" %>
<%= govuk_details(summary_text: "Can't find the managing agent you're looking for?") do %>
<ul class="govuk-list govuk-list--bullet">
<li>Double check the spelling and try again</li>
<li>Type the first few letters to see the suggestions</li>
<li>If you still can't find it,
<%= govuk_link_to("contact the DLUHC service desk", "https://digital.dclg.gov.uk/jira/servicedesk/customer/portal/4/group/21", rel: "noreferrer noopener", target: "_blank") %>
</li>
</ul>
<% end %>
<% end %>

24
app/views/organisation_relationships/housing_providers.html.erb

@ -0,0 +1,24 @@
<% item_label = format_label(@pagy.count, "housing providers") %>
<% if current_user.support? %>
<%= render partial: "organisations/headings", locals: { main: @organisation.name, sub: nil } %>
<%= render SubNavigationComponent.new(items: secondary_items(request.path, @organisation.id)) %>
<h2 class="govuk-visually-hidden">Housing Providers</h2>
<p class="govuk-body">This organisation can submit logs for its housing providers.</p>
<% if @total_count == 0 %>
<p class="govuk-body">This organisation does not currently have any housing providers.</p>
<% end %>
<% else %>
<%= render partial: "organisations/headings", locals: { main: "Your housing providers", sub: current_user.organisation.name } %>
<p class="govuk-body">Your organisation can submit logs for its housing providers.</p>
<% if @total_count == 0 %>
<p class="govuk-body">You do not currently have any housing providers.</p>
<% end %>
<% end %>
<% if current_user.support? || current_user.data_coordinator? %>
<%= govuk_button_link_to "Add a housing provider", housing_providers_add_organisation_path, html: { method: :get } %>
<% end %>
<% if @total_count != 0 %>
<%= render SearchComponent.new(current_user:, search_label: "Search for a housing provider", value: @searched) %>
<%= render partial: "organisation_relationships/housing_provider_list", locals: { index: @housing_providers, title: "Housing providers", pagy: @pagy, searched: @searched, item_label:, total_count: @total_count } %>
<%= render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "housing providers" } %>
<% end %>

27
app/views/organisation_relationships/managing_agents.html.erb

@ -0,0 +1,27 @@
<% item_label = format_label(@pagy.count, "managing agents") %>
<% if current_user.support? %>
<%= render partial: "organisations/headings", locals: { main: @organisation.name, sub: nil } %>
<%= render SubNavigationComponent.new(
items: secondary_items(request.path, @organisation.id),
) %>
<h2 class="govuk-visually-hidden">Managing Agents</h2>
<p class="govuk-body">A managing agent can submit logs for this organisation.</p>
<% if @total_count == 0 %>
<p class="govuk-body">This organisation does not currently have any managing agents.</p>
<% end %>
<% else %>
<%= render partial: "organisations/headings", locals: { main: "This organisation managing agents", sub: current_user.organisation.name } %>
<p class="govuk-body">A managing agent can submit logs for this organisation.</p>
<% if @total_count == 0 %>
<p class="govuk-body">This organisation does not currently have any managing agents.</p>
<% end %>
<% end %>
<% if current_user.support? || current_user.data_coordinator? %>
<%= govuk_button_link_to "Add a managing agent", managing_agents_add_organisation_path, html: { method: :get } %>
<% end %>
<% if @total_count != 0 %>
<%= render SearchComponent.new(current_user:, search_label: "Search for a managing agent", value: @searched) %>
<%= render partial: "organisation_relationships/managing_agent_list", locals: { index: @managing_agents, title: "Managing agents", pagy: @pagy, searched: @searched, item_label:, total_count: @total_count } %>
<%= render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "Managing agents" } %>
<% end %>

21
app/views/organisation_relationships/remove_housing_provider.html.erb

@ -0,0 +1,21 @@
<%= form_with url: housing_providers_organisation_path(target_organisation_id: @target_organisation.id), method: "delete", local: true do |f| %>
<% if current_user.support? %>
<%= render partial: "organisations/headings", locals: { main: @organisation.name, sub: nil } %>
<%= render SubNavigationComponent.new(items: secondary_items(request.path, @organisation.id)) %>
<h2 class="govuk-visually-hidden">Remove Housing Provider</h2>
<% end %>
<% if current_user.support? %>
<%= govuk_back_link(href: :back) %>
<%= render partial: "organisations/headings", locals: { main: "You are removing ‘#{@target_organisation.name}’ from this organisation's housing providers", sub: nil } %>
<% else %>
<% content_for :before_content do %>
<%= govuk_back_link(href: :back) %>
<% end %>
<%= render partial: "organisations/headings", locals: { main: "You are removing ‘#{@target_organisation.name}’ from your organisation's housing providers", sub: nil } %>
<% end %>
<%= govuk_warning_text text: "You will no longer be able to submit logs for #{@target_organisation.name}" %>
<div class="govuk-button-group">
<%= f.govuk_submit "Confirm" %>
<%= govuk_button_link_to "Cancel", housing_providers_organisation_path(current_user.organisation), html: { method: :get }, secondary: true %>
</div>
<% end %>

21
app/views/organisation_relationships/remove_managing_agent.html.erb

@ -0,0 +1,21 @@
<%= form_with url: managing_agents_organisation_path(target_organisation_id: @target_organisation.id), method: "delete", local: true do |f| %>
<% if current_user.support? %>
<%= render partial: "organisations/headings", locals: { main: @organisation.name, sub: nil } %>
<%= render SubNavigationComponent.new(items: secondary_items(request.path, @organisation.id)) %>
<h2 class="govuk-visually-hidden">Remove Managing Agent</h2>
<% end %>
<% if current_user.support? %>
<%= govuk_back_link(href: :back) %>
<%= render partial: "organisations/headings", locals: { main: "You are removing ‘#{@target_organisation.name}’ from this organisation's managing agents", sub: nil } %>
<% else %>
<% content_for :before_content do %>
<%= govuk_back_link(href: :back) %>
<% end %>
<%= render partial: "organisations/headings", locals: { main: "You are removing ‘#{@target_organisation.name}’ from your organisation's managing agents", sub: nil } %>
<% end %>
<%= govuk_warning_text text: "#{@target_organisation.name} will no longer be able to submit logs for you" %>
<div class="govuk-button-group">
<%= f.govuk_submit "Confirm" %>
<%= govuk_button_link_to "Cancel", managing_agents_organisation_path(current_user.organisation), html: { method: :get }, secondary: true %>
</div>
<% end %>

2
app/views/organisations/index.html.erb

@ -1,4 +1,4 @@
<% item_label = format_label(@pagy.count, "organisation") %>
<% item_label = format_label(@pagy.count, "organisations") %>
<% title = format_title(@searched, "Organisations", current_user, item_label, @pagy.count, nil) %>
<% content_for :title, title %>

2
app/views/organisations/logs.html.erb

@ -1,4 +1,4 @@
<% item_label = format_label(@pagy.count, "log") %>
<% item_label = format_label(@pagy.count, "logs") %>
<% title = format_title(@searched, "Logs", current_user, item_label, @pagy.count, @organisation.name) %>
<% content_for :title, title %>

16
config/forms/2021_2022.json

@ -5759,16 +5759,16 @@
"type": "radio",
"check_answer_label": "Person seriously injured or ill as result of serving in UK armed forces",
"answer_options": {
"0": {
"1": {
"value": "Yes"
},
"1": {
"2": {
"value": "No"
},
"divider": {
"value": true
},
"2": {
"3": {
"value": "Person prefers not to say"
}
}
@ -8430,11 +8430,6 @@
"informative_text": {
"translation": "soft_validations.rent.min.hint_text",
"arguments": [
{
"key": "la",
"label": true,
"i18n_template": "la"
},
{
"key": "soft_min_for_period",
"label": false,
@ -8487,11 +8482,6 @@
"informative_text": {
"translation": "soft_validations.rent.max.hint_text",
"arguments": [
{
"key": "la",
"label": true,
"i18n_template": "la"
},
{
"key": "soft_max_for_period",
"label": false,

16
config/forms/2022_2023.json

@ -5761,16 +5761,16 @@
"type": "radio",
"check_answer_label": "Person seriously injured or ill as result of serving in UK armed forces",
"answer_options": {
"0": {
"1": {
"value": "Yes"
},
"1": {
"2": {
"value": "No"
},
"divider": {
"value": true
},
"2": {
"3": {
"value": "Person prefers not to say"
}
}
@ -8386,11 +8386,6 @@
"informative_text": {
"translation": "soft_validations.rent.min.hint_text",
"arguments": [
{
"key": "la",
"label": true,
"i18n_template": "la"
},
{
"key": "soft_min_for_period",
"label": false,
@ -8443,11 +8438,6 @@
"informative_text": {
"translation": "soft_validations.rent.max.hint_text",
"arguments": [
{
"key": "la",
"label": true,
"i18n_template": "la"
},
{
"key": "soft_max_for_period",
"label": false,

6
config/initializers/feature_toggle.rb

@ -8,4 +8,10 @@ class FeatureToggle
false
end
def self.managing_owning_enabled?
return true unless Rails.env.production?
false
end
end

33
config/locales/en.yml

@ -146,7 +146,7 @@ en:
one_seven_bedroom_shared: "A shared house must have 1 to 7 bedrooms"
one_three_bedroom_single_tenant_shared: "A shared house with fewer than two tenants must have 1 to 3 bedrooms"
beds:
negative: "Number of bedrooms has to be greater than 0"
non_positive: "Number of bedrooms has to be greater than 0"
over_max: "Number of bedrooms cannot be more than 12"
financial:
@ -188,17 +188,32 @@ en:
general_needs: "Enter a value for the support charge between £0 and £60 per week if the landlord is a local authority and it is a general needs letting"
supported_housing: "Enter a value for the support charge between £0 and £120 per week if the landlord is a local authority and it is a supported housing letting"
brent:
not_in_range: "Basic rent is outside of the expected range based on the lettings type, local authority and number of bedrooms"
below_hard_min: "Rent is below the absolute minimum expected for a property of this type. Please check the rent, rent period, local authority and (if general needs) number of bedrooms"
above_hard_max: "Rent is higher than the absolute maximum expected for a property of this type. Please check the rent, rent period, local authority and (if general needs) number of bedrooms"
scheme_id:
below_hard_min: "Rent is below the absolute minimum expected for a property of this type. Please check the rent, rent period and local authority"
above_hard_max: "Rent is higher than the absolute maximum expected for a property of this type. Please check the rent, rent period and local authority"
location_id:
below_hard_min: "Rent is below the absolute minimum expected for a property of this type. Please check the rent, rent period and local authority"
above_hard_max: "Rent is higher than the absolute maximum expected for a property of this type. Please check the rent, rent period and local authority"
postcode_known:
below_hard_min: "Rent is below the absolute minimum expected for a property of this type. Please check the rent, rent period, local authority and number of bedrooms"
above_hard_max: "Rent is higher than the absolute maximum expected for a property of this type. Please check the rent, rent period, local authority and number of bedrooms"
la:
not_in_range: "Basic rent is outside of the expected range based on this local authority"
below_hard_min: "Rent is below the absolute minimum expected for a property of this type based on this local authority"
above_hard_max: "Rent is higher than the absolute maximum expected for a property of this type based on this local authority"
beds:
not_in_range: "Basic rent is outside of the expected range based on this number of bedrooms"
below_hard_min: "Rent is below the absolute minimum expected for a property of this type based on this number of bedrooms"
above_hard_max: "Rent is higher than the absolute maximum expected for a property of this type based on this number of bedrooms"
needstype:
not_in_range: "Basic rent is outside of the expected range based on this lettings type"
below_hard_min: "Rent is below the absolute minimum expected for a property of this type based on this lettings type"
above_hard_max: "Rent is higher than the absolute maximum expected for a property of this type based on this lettings type"
rent_type:
not_in_range: "Basic rent is outside of the expected range based on this lettings type"
below_hard_min: "Rent is below the absolute minimum expected for a property of this type based on this lettings type"
above_hard_max: "Rent is higher than the absolute maximum expected for a property of this type based on this lettings type"
period:
not_in_range: "Basic rent is outside of the expected range based on this period"
below_hard_min: "Rent is below the absolute minimum expected for a property of this type based on this period"
above_hard_max: "Rent is higher than the absolute maximum expected for a property of this type based on this period"
charges:
complete_1_of_3: "Answer either the ‘household rent and charges’ question or ‘is this accommodation a care home‘, or select ‘no’ for ‘does the household pay rent or charges for the accommodation?’"
tcharge:
@ -307,10 +322,10 @@ en:
rent:
min:
title_text: "You told us the rent is %{brent}"
hint_text: "The minimum rent for this type of property in %{la} is £%{soft_min_for_period}."
hint_text: "The minimum rent expected for this type of property in this local authority is £%{soft_min_for_period}."
max:
title_text: "You told us the rent is %{brent}"
hint_text: "The maximum rent for this type of property in %{la} is £%{soft_max_for_period}."
hint_text: "The maximum rent expected for this type of property in this local authority is £%{soft_max_for_period}."
retirement:
min:
title: "You told us this person is under %{age} and retired"

7726
config/rent_range_data/2022.csv

File diff suppressed because it is too large Load Diff

10
config/routes.rb

@ -79,6 +79,16 @@ Rails.application.routes.draw do
post "logs/email-csv", to: "organisations#email_csv"
get "logs/csv-confirmation", to: "lettings_logs#csv_confirmation"
get "schemes", to: "organisations#schemes"
get "housing-providers", to: "organisation_relationships#housing_providers"
get "housing-providers/add", to: "organisation_relationships#add_housing_provider"
get "housing-providers/remove", to: "organisation_relationships#remove_housing_provider"
post "housing-providers", to: "organisation_relationships#create_housing_provider"
delete "housing-providers", to: "organisation_relationships#delete_housing_provider"
get "managing-agents", to: "organisation_relationships#managing_agents"
get "managing-agents/add", to: "organisation_relationships#add_managing_agent"
get "managing-agents/remove", to: "organisation_relationships#remove_managing_agent"
post "managing-agents", to: "organisation_relationships#create_managing_agent"
delete "managing-agents", to: "organisation_relationships#delete_managing_agent"
end
end

8
db/migrate/20221007133155_add_la_to_sales_log.rb

@ -0,0 +1,8 @@
class AddLaToSalesLog < ActiveRecord::Migration[7.0]
def change
change_table :sales_logs, bulk: true do |t|
t.column :la, :string
t.column :la_known, :integer
end
end
end

10
db/migrate/20221017095918_add_relationship_type_to_org_relationships.rb

@ -0,0 +1,10 @@
class AddRelationshipTypeToOrgRelationships < ActiveRecord::Migration[7.0]
def change
add_column(
:organisation_relationships,
:relationship_type,
:integer,
null: false, # rubocop:disable Rails/NotNullColumn
)
end
end

5
db/migrate/20221019082625_rename_managing_agents_column.rb

@ -0,0 +1,5 @@
class RenameManagingAgentsColumn < ActiveRecord::Migration[7.0]
def change
rename_column :organisations, :managing_agents, :managing_agents_label
end
end

5
db/schema.rb

@ -296,6 +296,7 @@ ActiveRecord::Schema[7.0].define(version: 2022_10_18_221143) do
t.integer "parent_organisation_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "relationship_type", null: false
end
create_table "organisation_rent_periods", force: :cascade do |t|
@ -315,7 +316,7 @@ ActiveRecord::Schema[7.0].define(version: 2022_10_18_221143) do
t.string "postcode"
t.boolean "holds_own_stock"
t.string "other_stock_owners"
t.string "managing_agents"
t.string "managing_agents_label"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "active"
@ -393,6 +394,8 @@ ActiveRecord::Schema[7.0].define(version: 2022_10_18_221143) do
t.boolean "pcodenk", default: true
t.integer "postcode_known"
t.integer "la_known"
t.integer "income1"
t.integer "income1nk"
t.index ["created_by_id"], name: "index_sales_logs_on_created_by_id"
t.index ["managing_organisation_id"], name: "index_sales_logs_on_managing_organisation_id"
t.index ["owning_organisation_id"], name: "index_sales_logs_on_owning_organisation_id"

39
db/seeds.rb

@ -8,14 +8,35 @@
# rubocop:disable Rails/Output
unless Rails.env.test?
housing_provider = Organisation.find_or_create_by!(
name: "Housing Provider",
address_line1: "2 Marsham Street",
address_line2: "London",
postcode: "SW1P 4DF",
holds_own_stock: true,
other_stock_owners: "None",
managing_agents_label: "None",
provider_type: "LA",
)
managing_agent = Organisation.find_or_create_by!(
name: "Managing Agent",
address_line1: "2 Marsham Street",
address_line2: "London",
postcode: "SW1P 4DF",
holds_own_stock: true,
other_stock_owners: "None",
managing_agents_label: "None",
provider_type: "LA",
)
org = Organisation.find_or_create_by!(
name: "DLUHC",
address_line1: "2 Marsham Street",
address_line2: "London",
postcode: "SW1P 4DF",
holds_own_stock: false,
holds_own_stock: true,
other_stock_owners: "None",
managing_agents: "None",
managing_agents_label: "None",
provider_type: "LA",
) do
info = "Seeded DLUHC Organisation"
@ -26,6 +47,17 @@ unless Rails.env.test?
end
end
OrganisationRelationship.create!(
child_organisation: org,
parent_organisation: housing_provider,
relationship_type: OrganisationRelationship::OWNING,
)
OrganisationRelationship.create!(
child_organisation: managing_agent,
parent_organisation: org,
relationship_type: OrganisationRelationship::MANAGING,
)
if Rails.env.development? && User.count.zero?
User.create!(
name: "Provider",
@ -65,7 +97,7 @@ unless Rails.env.test?
postcode: "BA21 4AT",
holds_own_stock: false,
other_stock_owners: "None",
managing_agents: "None",
managing_agents_label: "None",
provider_type: "LA",
)
@ -157,6 +189,7 @@ unless Rails.env.test?
Dir.glob("config/rent_range_data/*.csv").each do |path|
start_year = File.basename(path, ".csv")
Rake::Task["data_import:rent_ranges"].invoke(start_year, path)
Rake::Task["data_import:rent_ranges"].reenable
end
end
end

2
docs/api/v1.json

@ -311,7 +311,7 @@
"reason": 1,
"underoccupation_benefitcap": 0,
"leftreg": 1,
"reservist": 0,
"reservist": 1,
"illness": 1,
"preg_occ": 0,
"tenancy_code": "BZ757",

2
lib/tasks/rent_ranges.rake

@ -24,6 +24,6 @@ namespace :data_import do
)
count += 1
end
pp "Created/updated #{count} LA Rent Range records" unless Rails.env.test?
pp "Created/updated #{count} LA Rent Range records for #{start_year}" unless Rails.env.test?
end
end

4
spec/factories/lettings_log.rb

@ -56,7 +56,7 @@ FactoryBot.define do
homeless { 1 }
underoccupation_benefitcap { 0 }
leftreg { 1 }
reservist { 0 }
reservist { 1 }
illness { 1 }
preg_occ { 2 }
startertenancy { 1 }
@ -132,7 +132,7 @@ FactoryBot.define do
hbrentshortfall { 1 }
tshortfall { 12 }
property_relet { 0 }
mrcdate { Time.utc(2020, 5, 0o5, 10, 36, 49) }
mrcdate { Time.zone.local(2020, 5, 5, 10, 36, 49) }
incref { 0 }
startdate { Time.utc(2022, 2, 2, 10, 36, 49) }
armedforces { 1 }

5
spec/factories/organisation.rb

@ -17,9 +17,4 @@ FactoryBot.define do
created_at { Time.zone.now }
updated_at { Time.zone.now }
end
factory :organisation_relationship do
child_organisation { FactoryBot.create(:organisation) }
parent_organisation { FactoryBot.create(:organisation) }
end
end

14
spec/factories/organisation_relationship.rb

@ -0,0 +1,14 @@
FactoryBot.define do
factory :organisation_relationship do
child_organisation { FactoryBot.create(:organisation) }
parent_organisation { FactoryBot.create(:organisation) }
trait :owning do
relationship_type { OrganisationRelationship::OWNING }
end
trait :managing do
relationship_type { OrganisationRelationship::MANAGING }
end
end
end

2
spec/factories/sales_log.rb

@ -49,6 +49,8 @@ FactoryBot.define do
age6 { 40 }
income1nk { 0 }
income1 { 10_000 }
la_known { "1" }
la { "E09000003" }
end
end
end

2
spec/fixtures/exports/general_needs_log.csv vendored

@ -1,2 +1,2 @@
status,tenancycode,age1,sex1,ethnic,national,prevten,ecstat1,hhmemb,age2,sex2,ecstat2,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,leftreg,reservist,illness,preg_occ,startertenancy,tenancylength,tenancy,ppostcode_full,rsnvac,unittype_gn,beds,offered,wchair,earnings,incfreq,benefits,period,layear,waityear,postcode_full,reasonpref,cbl,chr,cap,reasonother,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,illness_type_1,illness_type_2,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,irproduct_other,reason,propcode,la,prevloc,hb,hbrentshortfall,mrcdate,incref,startdate,armedforces,unitletas,builtype,voiddate,renttype,needstype,lettype,totchild,totelder,totadult,nocharge,referral,brent,scharge,pscharge,supcharg,tcharge,tshortfall,chcharge,ppcodenk,has_benefits,renewal,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat2,relat3,relat4,relat5,relat6,relat7,relat8,lar,irproduct,joint,sheltered,hhtype,new_old,vacdays,form,owningorgid,owningorgname,hcnum,maningorgid,maningorgname,manhcnum,createddate,uploaddate
2,BZ737,35,F,2,4,6,0,2,32,M,6,,,,,,,,,,,,,,,,,,,1,0,1,0,1,2,1,5,1,SE2 6RT,6,7,3,2,1,68,1,1,2,2,1,NW1 5TY,1,1,1,2,,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,,,4,123,E09000003,E07000105,6,1,2020-05-05 10:36:49 UTC,0,2022-02-02 10:36:49 UTC,1,2,1,2019-11-03 00:00:00 UTC,2,1,7,0,0,2,0,2,200.0,50.0,40.0,35.0,325.0,12.0,,1,1,0,100.0,25.0,20.0,17.5,162.5,6.0,0,1,,2,P,,,,,,,,,,,4,2,638,{id},{owning_org_id},DLUHC,1234,{managing_org_id},DLUHC,1234,2022-02-08 16:52:15 UTC,2022-02-08 16:52:15 UTC
2,BZ737,35,F,2,4,6,0,2,32,M,6,,,,,,,,,,,,,,,,,,,1,0,1,1,1,2,1,5,1,SE2 6RT,6,7,3,2,1,68,1,1,2,2,1,NW1 5TY,1,1,1,2,,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,,,4,123,E09000003,E07000105,6,1,2020-05-05T10:36:49+01:00,0,2022-02-02T10:36:49+00:00,1,2,1,2019-11-03T00:00:00+00:00,2,1,7,0,0,2,0,2,200.0,50.0,40.0,35.0,325.0,12.0,,1,1,0,100.0,25.0,20.0,17.5,162.5,6.0,0,1,,2,P,,,,,,,,,,,4,2,638,{id},{owning_org_id},DLUHC,1234,{managing_org_id},DLUHC,1234,2022-02-08T16:52:15+00:00,2022-02-08T16:52:15+00:00

1 status tenancycode age1 sex1 ethnic national prevten ecstat1 hhmemb age2 sex2 ecstat2 age3 sex3 ecstat3 age4 sex4 ecstat4 age5 sex5 ecstat5 age6 sex6 ecstat6 age7 sex7 ecstat7 age8 sex8 ecstat8 homeless underoccupation_benefitcap leftreg reservist illness preg_occ startertenancy tenancylength tenancy ppostcode_full rsnvac unittype_gn beds offered wchair earnings incfreq benefits period layear waityear postcode_full reasonpref cbl chr cap reasonother housingneeds_a housingneeds_b housingneeds_c housingneeds_f housingneeds_g housingneeds_h illness_type_1 illness_type_2 illness_type_3 illness_type_4 illness_type_8 illness_type_5 illness_type_6 illness_type_7 illness_type_9 illness_type_10 rp_homeless rp_insan_unsat rp_medwel rp_hardship rp_dontknow tenancyother irproduct_other reason propcode la prevloc hb hbrentshortfall mrcdate incref startdate armedforces unitletas builtype voiddate renttype needstype lettype totchild totelder totadult nocharge referral brent scharge pscharge supcharg tcharge tshortfall chcharge ppcodenk has_benefits renewal wrent wscharge wpschrge wsupchrg wtcharge wtshortfall refused housingneeds wchchrg newprop relat2 relat3 relat4 relat5 relat6 relat7 relat8 lar irproduct joint sheltered hhtype new_old vacdays form owningorgid owningorgname hcnum maningorgid maningorgname manhcnum createddate uploaddate
2 2 BZ737 35 F 2 4 6 0 2 32 M 6 1 0 1 0 1 1 2 1 5 1 SE2 6RT 6 7 3 2 1 68 1 1 2 2 1 NW1 5TY 1 1 1 2 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 4 123 E09000003 E07000105 6 1 2020-05-05 10:36:49 UTC 2020-05-05T10:36:49+01:00 0 2022-02-02 10:36:49 UTC 2022-02-02T10:36:49+00:00 1 2 1 2019-11-03 00:00:00 UTC 2019-11-03T00:00:00+00:00 2 1 7 0 0 2 0 2 200.0 50.0 40.0 35.0 325.0 12.0 1 1 0 100.0 25.0 20.0 17.5 162.5 6.0 0 1 2 P 4 2 638 {id} {owning_org_id} DLUHC 1234 {managing_org_id} DLUHC 1234 2022-02-08 16:52:15 UTC 2022-02-08T16:52:15+00:00 2022-02-08 16:52:15 UTC 2022-02-08T16:52:15+00:00

12
spec/fixtures/exports/general_needs_log.xml vendored

@ -34,7 +34,7 @@
<homeless>1</homeless>
<underoccupation_benefitcap>0</underoccupation_benefitcap>
<leftreg>1</leftreg>
<reservist>0</reservist>
<reservist>1</reservist>
<illness>1</illness>
<preg_occ>2</preg_occ>
<startertenancy>1</startertenancy>
@ -87,13 +87,13 @@
<prevloc>E07000105</prevloc>
<hb>6</hb>
<hbrentshortfall>1</hbrentshortfall>
<mrcdate>2020-05-05 10:36:49 UTC</mrcdate>
<mrcdate>2020-05-05T10:36:49+01:00</mrcdate>
<incref>0</incref>
<startdate>2022-02-02 10:36:49 UTC</startdate>
<startdate>2022-02-02T10:36:49+00:00</startdate>
<armedforces>1</armedforces>
<unitletas>2</unitletas>
<builtype>1</builtype>
<voiddate>2019-11-03 00:00:00 UTC</voiddate>
<voiddate>2019-11-03T00:00:00+00:00</voiddate>
<renttype>2</renttype>
<needstype>1</needstype>
<lettype>7</lettype>
@ -142,8 +142,8 @@
<maningorgid>{managing_org_id}</maningorgid>
<maningorgname>DLUHC</maningorgname>
<manhcnum>1234</manhcnum>
<createddate>2022-02-08 16:52:15 UTC</createddate>
<uploaddate>2022-02-08 16:52:15 UTC</uploaddate>
<createddate>2022-02-08T16:52:15+00:00</createddate>
<uploaddate>2022-02-08T16:52:15+00:00</uploaddate>
<providertype>1</providertype>
</form>
</forms>

12
spec/fixtures/exports/supported_housing_logs.xml vendored

@ -34,7 +34,7 @@
<homeless>1</homeless>
<underoccupation_benefitcap>0</underoccupation_benefitcap>
<leftreg>1</leftreg>
<reservist>0</reservist>
<reservist>1</reservist>
<illness>1</illness>
<preg_occ>2</preg_occ>
<startertenancy>1</startertenancy>
@ -86,13 +86,13 @@
<prevloc>E07000105</prevloc>
<hb>6</hb>
<hbrentshortfall>1</hbrentshortfall>
<mrcdate>2020-05-05 10:36:49 UTC</mrcdate>
<mrcdate>2020-05-05T10:36:49+01:00</mrcdate>
<incref>0</incref>
<startdate>2022-02-02 10:36:49 UTC</startdate>
<startdate>2022-02-02T10:36:49+00:00</startdate>
<armedforces>1</armedforces>
<unitletas/>
<builtype/>
<voiddate>2019-11-03 00:00:00 UTC</voiddate>
<voiddate>2019-11-03T00:00:00+00:00</voiddate>
<renttype>2</renttype>
<needstype>2</needstype>
<lettype>8</lettype>
@ -141,8 +141,8 @@
<maningorgid>{managing_org_id}</maningorgid>
<maningorgname>DLUHC</maningorgname>
<manhcnum>1234</manhcnum>
<createddate>2022-02-08 16:52:15 UTC</createddate>
<uploaddate>2022-02-08 16:52:15 UTC</uploaddate>
<createddate>2022-02-08T16:52:15+00:00</createddate>
<uploaddate>2022-02-08T16:52:15+00:00</uploaddate>
<unittype_sh>7</unittype_sh>
<confidential>1</confidential>
<cligrp1>G</cligrp1>

22
spec/helpers/navigation_items_helper_spec.rb

@ -346,6 +346,8 @@ RSpec.describe NavigationItemsHelper do
NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false),
NavigationItemsHelper::NavigationItem.new("Users", users_path, false),
NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false),
NavigationItemsHelper::NavigationItem.new("Housing providers", "/organisations/#{current_user.organisation.id}/housing-providers", false),
NavigationItemsHelper::NavigationItem.new("Managing agents", "/organisations/#{current_user.organisation.id}/managing-agents", false),
]
end
@ -362,6 +364,8 @@ RSpec.describe NavigationItemsHelper do
NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false),
NavigationItemsHelper::NavigationItem.new("Users", users_path, false),
NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false),
NavigationItemsHelper::NavigationItem.new("Housing providers", "/organisations/#{current_user.organisation.id}/housing-providers", false),
NavigationItemsHelper::NavigationItem.new("Managing agents", "/organisations/#{current_user.organisation.id}/managing-agents", false),
]
end
@ -378,6 +382,8 @@ RSpec.describe NavigationItemsHelper do
NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false),
NavigationItemsHelper::NavigationItem.new("Users", users_path, true),
NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false),
NavigationItemsHelper::NavigationItem.new("Housing providers", "/organisations/#{current_user.organisation.id}/housing-providers", false),
NavigationItemsHelper::NavigationItem.new("Managing agents", "/organisations/#{current_user.organisation.id}/managing-agents", false),
]
end
@ -394,6 +400,8 @@ RSpec.describe NavigationItemsHelper do
NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false),
NavigationItemsHelper::NavigationItem.new("Users", users_path, false),
NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, true),
NavigationItemsHelper::NavigationItem.new("Housing providers", "/organisations/#{current_user.organisation.id}/housing-providers", false),
NavigationItemsHelper::NavigationItem.new("Managing agents", "/organisations/#{current_user.organisation.id}/managing-agents", false),
]
end
@ -410,6 +418,8 @@ RSpec.describe NavigationItemsHelper do
NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false),
NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false),
NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false),
NavigationItemsHelper::NavigationItem.new("Housing providers", "/organisations/#{current_user.organisation.id}/housing-providers", false),
NavigationItemsHelper::NavigationItem.new("Managing agents", "/organisations/#{current_user.organisation.id}/managing-agents", false),
]
end
@ -426,6 +436,8 @@ RSpec.describe NavigationItemsHelper do
NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false),
NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", true),
NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false),
NavigationItemsHelper::NavigationItem.new("Housing providers", "/organisations/#{current_user.organisation.id}/housing-providers", false),
NavigationItemsHelper::NavigationItem.new("Managing agents", "/organisations/#{current_user.organisation.id}/managing-agents", false),
]
end
@ -442,6 +454,8 @@ RSpec.describe NavigationItemsHelper do
NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", true),
NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false),
NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false),
NavigationItemsHelper::NavigationItem.new("Housing providers", "/organisations/#{current_user.organisation.id}/housing-providers", false),
NavigationItemsHelper::NavigationItem.new("Managing agents", "/organisations/#{current_user.organisation.id}/managing-agents", false),
]
end
@ -618,6 +632,8 @@ RSpec.describe NavigationItemsHelper do
NavigationItemsHelper::NavigationItem.new("Schemes", "/organisations/#{current_user.organisation.id}/schemes", false),
NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false),
NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", false),
NavigationItemsHelper::NavigationItem.new("Housing providers", "/organisations/#{current_user.organisation.id}/housing-providers", false),
NavigationItemsHelper::NavigationItem.new("Managing agents", "/organisations/#{current_user.organisation.id}/managing-agents", false),
]
end
@ -646,6 +662,8 @@ RSpec.describe NavigationItemsHelper do
NavigationItemsHelper::NavigationItem.new("Schemes", "/organisations/#{current_user.organisation.id}/schemes", false),
NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", true),
NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", false),
NavigationItemsHelper::NavigationItem.new("Housing providers", "/organisations/#{current_user.organisation.id}/housing-providers", false),
NavigationItemsHelper::NavigationItem.new("Managing agents", "/organisations/#{current_user.organisation.id}/managing-agents", false),
]
end
@ -674,6 +692,8 @@ RSpec.describe NavigationItemsHelper do
NavigationItemsHelper::NavigationItem.new("Schemes", "/organisations/#{current_user.organisation.id}/schemes", true),
NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false),
NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", false),
NavigationItemsHelper::NavigationItem.new("Housing providers", "/organisations/#{current_user.organisation.id}/housing-providers", false),
NavigationItemsHelper::NavigationItem.new("Managing agents", "/organisations/#{current_user.organisation.id}/managing-agents", false),
]
end
@ -702,6 +722,8 @@ RSpec.describe NavigationItemsHelper do
NavigationItemsHelper::NavigationItem.new("Schemes", "/organisations/#{current_user.organisation.id}/schemes", false),
NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false),
NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", true),
NavigationItemsHelper::NavigationItem.new("Housing providers", "/organisations/#{current_user.organisation.id}/housing-providers", false),
NavigationItemsHelper::NavigationItem.new("Managing agents", "/organisations/#{current_user.organisation.id}/managing-agents", false),
]
end

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

@ -17,6 +17,7 @@ RSpec.describe Form::Sales::Subsections::PropertyInformation, type: :model do
property_number_of_bedrooms
property_building_type
property_unit_type
property_local_authority
],
)
end

4
spec/models/form_handler_spec.rb

@ -61,14 +61,14 @@ RSpec.describe FormHandler do
it "is able to load a current sales form" do
form = form_handler.get_form("current_sales")
expect(form).to be_a(Form)
expect(form.pages.count).to eq(40)
expect(form.pages.count).to eq(41)
expect(form.name).to eq("2022_2023_sales")
end
it "is able to load a previous sales form" do
form = form_handler.get_form("previous_sales")
expect(form).to be_a(Form)
expect(form.pages.count).to eq(40)
expect(form.pages.count).to eq(41)
expect(form.name).to eq("2021_2022_sales")
end
end

4
spec/models/lettings_log_spec.rb

@ -1431,8 +1431,8 @@ RSpec.describe LettingsLog do
it "correctly derives and saves referral" do
record_from_db = ActiveRecord::Base.connection.execute("select referral from lettings_logs where id=#{lettings_log.id}").to_a[0]
expect(record_from_db["referral"]).to eq(0)
expect(lettings_log["referral"]).to eq(0)
expect(record_from_db["referral"]).to eq(1)
expect(lettings_log["referral"]).to eq(1)
end
it "correctly derives and saves vacdays" do

96
spec/models/organisation_spec.rb

@ -27,19 +27,101 @@ RSpec.describe Organisation, type: :model do
.to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Provider type #{I18n.t('validations.organisation.provider_type_missing')}")
end
context "with parent/child association" do
let(:child_organisation) { FactoryBot.create(:organisation, name: "DLUHC Child") }
context "with parent/child associations", :aggregate_failures do
let!(:child_organisation) { FactoryBot.create(:organisation, name: "DLUHC Child") }
let!(:grandchild_organisation) { FactoryBot.create(:organisation, name: "DLUHC Grandchild") }
before do
FactoryBot.create(:organisation_relationship, child_organisation:, parent_organisation: organisation)
FactoryBot.create(
:organisation_relationship,
:owning,
child_organisation:,
parent_organisation: organisation,
)
FactoryBot.create(
:organisation_relationship,
:owning,
child_organisation: grandchild_organisation,
parent_organisation: child_organisation,
)
end
it "has correct child_organisations" do
expect(organisation.child_organisations).to eq([child_organisation])
expect(child_organisation.child_organisations).to eq([grandchild_organisation])
end
it "has correct parent_organisations" do
expect(child_organisation.parent_organisations).to eq([organisation])
expect(grandchild_organisation.parent_organisations).to eq([child_organisation])
end
end
context "with owning association", :aggregate_failures do
let!(:child_organisation) { FactoryBot.create(:organisation, name: "DLUHC Child") }
let!(:grandchild_organisation) { FactoryBot.create(:organisation, name: "DLUHC Grandchild") }
before do
FactoryBot.create(
:organisation_relationship,
:managing,
child_organisation:,
parent_organisation: organisation,
)
FactoryBot.create(
:organisation_relationship,
:owning,
child_organisation:,
parent_organisation: organisation,
)
it "has correct child" do
expect(organisation.child_organisations.first).to eq(child_organisation)
FactoryBot.create(
:organisation_relationship,
:owning,
child_organisation: grandchild_organisation,
parent_organisation: child_organisation,
)
end
it "has correct housing_providers" do
expect(child_organisation.housing_providers).to eq([organisation])
expect(grandchild_organisation.housing_providers).to eq([child_organisation])
end
end
context "with managing association", :aggregate_failures do
let!(:child_organisation) { FactoryBot.create(:organisation, name: "DLUHC Child") }
let!(:grandchild_organisation) { FactoryBot.create(:organisation, name: "DLUHC Grandchild") }
before do
FactoryBot.create(
:organisation_relationship,
:managing,
child_organisation:,
parent_organisation: organisation,
)
FactoryBot.create(
:organisation_relationship,
:owning,
child_organisation:,
parent_organisation: organisation,
)
FactoryBot.create(
:organisation_relationship,
:managing,
child_organisation: grandchild_organisation,
parent_organisation: child_organisation,
)
end
it "has correct parent" do
expect(child_organisation.parent_organisations.first).to eq(organisation)
it "has correct managing_agents" do
expect(organisation.managing_agents).to eq([child_organisation])
expect(child_organisation.managing_agents).to eq([grandchild_organisation])
expect(grandchild_organisation.managing_agents).to eq([])
end
end

93
spec/models/validations/financial_validations_spec.rb

@ -247,6 +247,8 @@ RSpec.describe Validations::FinancialValidations do
end
describe "rent and charges validations" do
let!(:location) { FactoryBot.create(:location, location_code: "E07000223") }
context "when the owning organisation is a private registered provider" do
before { record.owning_organisation.provider_type = 2 }
@ -777,7 +779,7 @@ RSpec.describe Validations::FinancialValidations do
context "when validating ranges based on LA and needstype" do
before do
LaRentRange.create(
LaRentRange.create!(
ranges_rent_id: "1",
la: "E07000223",
beds: 1,
@ -788,9 +790,21 @@ RSpec.describe Validations::FinancialValidations do
hard_max: 100.99,
start_year: 2021,
)
LaRentRange.create!(
ranges_rent_id: "2",
la: "E07000223",
beds: 0,
lettype: 2,
soft_min: 12.41,
soft_max: 89.54,
hard_min: 9.87,
hard_max: 100.99,
start_year: 2021,
)
end
it "validates hard minimum" do
it "validates hard minimum for general needs" do
record.needstype = 1
record.lettype = 1
record.period = 1
record.la = "E07000223"
@ -800,10 +814,24 @@ RSpec.describe Validations::FinancialValidations do
financial_validator.validate_rent_amount(record)
expect(record.errors["brent"])
.to include(match I18n.t("validations.financial.brent.not_in_range"))
.to include(match I18n.t("validations.financial.brent.below_hard_min"))
end
it "validates hard minimum for supported housing" do
record.needstype = 2
record.lettype = 2
record.period = 1
record.location = location
record.startdate = Time.zone.local(2021, 9, 17)
record.brent = 9.17
financial_validator.validate_rent_amount(record)
expect(record.errors["brent"])
.to include(match I18n.t("validations.financial.brent.below_hard_min"))
end
it "validates hard max" do
it "validates hard max for general needs" do
record.needstype = 1
record.lettype = 1
record.period = 1
record.la = "E07000223"
@ -813,15 +841,52 @@ RSpec.describe Validations::FinancialValidations do
financial_validator.validate_rent_amount(record)
expect(record.errors["brent"])
.to include(match I18n.t("validations.financial.brent.not_in_range"))
.to include(match I18n.t("validations.financial.brent.above_hard_max"))
expect(record.errors["beds"])
.to include(match I18n.t("validations.financial.brent.beds.not_in_range"))
.to include(match I18n.t("validations.financial.brent.beds.above_hard_max"))
expect(record.errors["la"])
.to include(match I18n.t("validations.financial.brent.la.not_in_range"))
.to include(match I18n.t("validations.financial.brent.la.above_hard_max"))
expect(record.errors["postcode_known"])
.to include(match I18n.t("validations.financial.brent.postcode_known.above_hard_max"))
expect(record.errors["scheme_id"])
.to include(match I18n.t("validations.financial.brent.scheme_id.above_hard_max"))
expect(record.errors["location_id"])
.to include(match I18n.t("validations.financial.brent.location_id.above_hard_max"))
expect(record.errors["rent_type"])
.to include(match I18n.t("validations.financial.brent.rent_type.not_in_range"))
.to include(match I18n.t("validations.financial.brent.rent_type.above_hard_max"))
expect(record.errors["needstype"])
.to include(match I18n.t("validations.financial.brent.needstype.not_in_range"))
.to include(match I18n.t("validations.financial.brent.needstype.above_hard_max"))
expect(record.errors["period"])
.to include(match I18n.t("validations.financial.brent.period.above_hard_max"))
end
it "validates hard max for supported housing" do
record.needstype = 2
record.lettype = 2
record.period = 1
record.location = location
record.startdate = Time.zone.local(2021, 9, 17)
record.brent = 200
financial_validator.validate_rent_amount(record)
expect(record.errors["brent"])
.to include(match I18n.t("validations.financial.brent.above_hard_max"))
expect(record.errors["beds"])
.to include(match I18n.t("validations.financial.brent.beds.above_hard_max"))
expect(record.errors["la"])
.to include(match I18n.t("validations.financial.brent.la.above_hard_max"))
expect(record.errors["postcode_known"])
.to include(match I18n.t("validations.financial.brent.postcode_known.above_hard_max"))
expect(record.errors["scheme_id"])
.to include(match I18n.t("validations.financial.brent.scheme_id.above_hard_max"))
expect(record.errors["location_id"])
.to include(match I18n.t("validations.financial.brent.location_id.above_hard_max"))
expect(record.errors["rent_type"])
.to include(match I18n.t("validations.financial.brent.rent_type.above_hard_max"))
expect(record.errors["needstype"])
.to include(match I18n.t("validations.financial.brent.needstype.above_hard_max"))
expect(record.errors["period"])
.to include(match I18n.t("validations.financial.brent.period.above_hard_max"))
end
it "validates hard max for correct collection year" do
@ -834,15 +899,15 @@ RSpec.describe Validations::FinancialValidations do
financial_validator.validate_rent_amount(record)
expect(record.errors["brent"])
.to include(match I18n.t("validations.financial.brent.not_in_range"))
.to include(match I18n.t("validations.financial.brent.above_hard_max"))
expect(record.errors["beds"])
.to include(match I18n.t("validations.financial.brent.beds.not_in_range"))
.to include(match I18n.t("validations.financial.brent.beds.above_hard_max"))
expect(record.errors["la"])
.to include(match I18n.t("validations.financial.brent.la.not_in_range"))
.to include(match I18n.t("validations.financial.brent.la.above_hard_max"))
expect(record.errors["rent_type"])
.to include(match I18n.t("validations.financial.brent.rent_type.not_in_range"))
.to include(match I18n.t("validations.financial.brent.rent_type.above_hard_max"))
expect(record.errors["needstype"])
.to include(match I18n.t("validations.financial.brent.needstype.not_in_range"))
.to include(match I18n.t("validations.financial.brent.needstype.above_hard_max"))
end
it "does not error if some of the fields are missing" do

12
spec/models/validations/household_validations_spec.rb

@ -256,7 +256,7 @@ RSpec.describe Validations::HouseholdValidations do
context "when the tenant or partner was and is not a member of the armed forces" do
it "validates that injured in the armed forces is not yes" do
record.armedforces = 2
record.reservist = 0
record.reservist = 1
household_validator.validate_armed_forces(record)
expect(record.errors["reservist"])
.to include(match I18n.t("validations.household.reservist.injury_not_required"))
@ -266,7 +266,7 @@ RSpec.describe Validations::HouseholdValidations do
context "when the tenant prefers not to say if they were or are in the armed forces" do
it "validates that injured in the armed forces is not yes" do
record.armedforces = 3
record.reservist = 0
record.reservist = 1
household_validator.validate_armed_forces(record)
expect(record.errors["reservist"])
.to include(match I18n.t("validations.household.reservist.injury_not_required"))
@ -276,7 +276,7 @@ RSpec.describe Validations::HouseholdValidations do
context "when the tenant was or is a regular member of the armed forces" do
it "expects that injured in the armed forces can be yes" do
record.armedforces = 0
record.reservist = 0
record.reservist = 1
household_validator.validate_armed_forces(record)
expect(record.errors["reservist"]).to be_empty
end
@ -285,7 +285,7 @@ RSpec.describe Validations::HouseholdValidations do
context "when the tenant was or is a reserve member of the armed forces" do
it "expects that injured in the armed forces can be yes" do
record.armedforces = 1
record.reservist = 0
record.reservist = 1
household_validator.validate_armed_forces(record)
expect(record.errors["reservist"]).to be_empty
end
@ -294,7 +294,7 @@ RSpec.describe Validations::HouseholdValidations do
context "when the tenant’s partner was or is a member of the armed forces" do
it "expects that injured in the armed forces can be yes" do
record.armedforces = 5
record.reservist = 0
record.reservist = 1
household_validator.validate_armed_forces(record)
expect(record.errors["reservist"]).to be_empty
end
@ -319,7 +319,7 @@ RSpec.describe Validations::HouseholdValidations do
it "expects that they served in the armed forces and may have been injured" do
record.armedforces = 1
record.leftreg = 0
record.reservist = 0
record.reservist = 1
household_validator.validate_armed_forces(record)
expect(record.errors["leftreg"]).to be_empty
expect(record.errors["reservist"]).to be_empty

2
spec/models/validations/property_validations_spec.rb

@ -134,7 +134,7 @@ RSpec.describe Validations::PropertyValidations do
it "adds an error" do
record.beds = -4
property_validator.validate_shared_housing_rooms(record)
expect(record.errors["beds"]).to include(I18n.t("validations.property.beds.negative"))
expect(record.errors["beds"]).to include(I18n.t("validations.property.beds.non_positive"))
end
end

4
spec/requests/form_controller_spec.rb

@ -158,7 +158,7 @@ RSpec.describe FormController, type: :request do
before do
Timecop.freeze(Time.zone.local(2022, 12, 1))
get "/lettings-logs/#{lettings_log_2022.id}/setup/check-answers", headers: headers, params: {}
get "/lettings-logs/#{lettings_log_2022.id}/setup/check-answers", headers:, params: {}
end
after do
@ -267,7 +267,7 @@ RSpec.describe FormController, type: :request do
it "logs that validation was triggered" do
expect(Rails.logger).to receive(:info).with("User triggered validation(s) on: age1").once
post "/lettings-logs/#{lettings_log.id}/form", params: params
post "/lettings-logs/#{lettings_log.id}/form", params:
end
context "when the number of days is too high for the month" do

2
spec/requests/lettings_logs_controller_spec.rb

@ -365,7 +365,7 @@ RSpec.describe LettingsLogsController, type: :request do
it "has search results in the title" do
get "/lettings-logs?search=#{log_to_search.id}", headers: headers, params: {}
expect(page).to have_title("Logs (1 log matching ‘#{log_to_search.id}’) - Submit social housing lettings and sales data (CORE) - GOV.UK")
expect(page).to have_title("Logs (1 logs matching ‘#{log_to_search.id}’) - Submit social housing lettings and sales data (CORE) - GOV.UK")
end
it "shows lettings logs matching the id" do

8
spec/requests/locations_controller_spec.rb

@ -100,7 +100,7 @@ RSpec.describe LocationsController, type: :request do
before do
sign_in user
post "/schemes/#{scheme.id}/locations", params: params
post "/schemes/#{scheme.id}/locations", params:
end
it "creates a new location for scheme with valid params and redirects to correct page" do
@ -302,7 +302,7 @@ RSpec.describe LocationsController, type: :request do
before do
allow(user).to receive(:need_two_factor_authentication?).and_return(false)
sign_in user
post "/schemes/#{scheme.id}/locations", params: params
post "/schemes/#{scheme.id}/locations", params:
end
it "creates a new location for scheme with valid params and redirects to correct page" do
@ -573,7 +573,7 @@ RSpec.describe LocationsController, type: :request do
before do
sign_in user
patch "/schemes/#{scheme.id}/locations/#{location.id}", params: params
patch "/schemes/#{scheme.id}/locations/#{location.id}", params:
end
it "updates existing location for scheme with valid params and redirects to correct page" do
@ -714,7 +714,7 @@ RSpec.describe LocationsController, type: :request do
before do
allow(user).to receive(:need_two_factor_authentication?).and_return(false)
sign_in user
patch "/schemes/#{scheme.id}/locations/#{location.id}", params: params
patch "/schemes/#{scheme.id}/locations/#{location.id}", params:
end
it "updates a location for scheme with valid params and redirects to correct page" do

583
spec/requests/organisation_relationships_controller_spec.rb

@ -0,0 +1,583 @@
require "rails_helper"
RSpec.describe OrganisationRelationshipsController, type: :request do
let(:organisation) { user.organisation }
let!(:unauthorised_organisation) { FactoryBot.create(:organisation) }
let(:headers) { { "Accept" => "text/html" } }
let(:page) { Capybara::Node::Simple.new(response.body) }
context "when user is signed in" do
let(:user) { FactoryBot.create(:user, :data_coordinator) }
context "with a data coordinator user" do
before do
sign_in user
end
context "when accessing the housing providers tab" do
context "with an organisation that the user belongs to" do
let!(:housing_provider) { FactoryBot.create(:organisation) }
let!(:other_org_housing_provider) { FactoryBot.create(:organisation, name: "Foobar LTD") }
let!(:other_organisation) { FactoryBot.create(:organisation, name: "Foobar LTD 2") }
before do
FactoryBot.create(:organisation_relationship, child_organisation: organisation, parent_organisation: housing_provider, relationship_type: OrganisationRelationship.relationship_types[:owning])
FactoryBot.create(:organisation_relationship, child_organisation: other_organisation, parent_organisation: other_org_housing_provider, relationship_type: OrganisationRelationship.relationship_types[:owning])
get "/organisations/#{organisation.id}/housing-providers", headers:, params: {}
end
it "shows the tab navigation" do
expected_html = "<nav class=\"app-primary-navigation\""
expect(response.body).to include(expected_html)
end
it "shows an add housing provider button" do
expect(page).to have_link("Add a housing provider")
end
it "shows a table of housing providers" do
expected_html = "<table class=\"govuk-table\""
expect(response.body).to include(expected_html)
expect(response.body).to include(housing_provider.name)
end
it "shows only housing providers for the current user's organisation" do
expect(page).to have_content(housing_provider.name)
expect(page).not_to have_content(other_org_housing_provider.name)
end
it "shows the pagination count" do
expect(page).to have_content("1 total housing providers")
end
context "when adding a housing provider" do
before do
get "/organisations/#{organisation.id}/housing-providers/add", headers:, params: {}
end
it "has the correct header" do
expect(response.body).to include("What is the name of your housing provider?")
end
it "shows an add button" do
expect(page).to have_button("Add")
end
end
end
context "with an organisation that are not in scope for the user, i.e. that they do not belong to" do
before do
get "/organisations/#{unauthorised_organisation.id}/housing-providers", headers:, params: {}
end
it "returns not found 404 from users page" do
expect(response).to have_http_status(:not_found)
end
end
end
context "when accessing the managing agents tab" do
context "with an organisation that the user belongs to" do
let!(:managing_agent) { FactoryBot.create(:organisation) }
let!(:other_org_managing_agent) { FactoryBot.create(:organisation, name: "Foobar LTD") }
let!(:other_organisation) { FactoryBot.create(:organisation, name: "Foobar LTD") }
before do
FactoryBot.create(:organisation_relationship, parent_organisation: organisation, child_organisation: managing_agent, relationship_type: OrganisationRelationship.relationship_types[:managing])
FactoryBot.create(:organisation_relationship, parent_organisation: other_organisation, child_organisation: other_org_managing_agent, relationship_type: OrganisationRelationship.relationship_types[:managing])
get "/organisations/#{organisation.id}/managing-agents", headers:, params: {}
end
it "shows the tab navigation" do
expected_html = "<nav class=\"app-primary-navigation\""
expect(response.body).to include(expected_html)
end
it "shows an add managing-agent button" do
expect(page).to have_link("Add a managing agent")
end
it "shows a table of managing-agents" do
expected_html = "<table class=\"govuk-table\""
expect(response.body).to include(expected_html)
expect(response.body).to include(managing_agent.name)
end
it "shows only managing-agents for the current user's organisation" do
expect(page).to have_content(managing_agent.name)
expect(page).not_to have_content(other_org_managing_agent.name)
end
it "shows the pagination count" do
expect(page).to have_content("1 total agents")
end
end
context "when adding a managing agent" do
before do
get "/organisations/#{organisation.id}/managing-agents/add", headers:, params: {}
end
it "has the correct header" do
expect(response.body).to include("What is the name of your managing agent?")
end
end
context "with an organisation that are not in scope for the user, i.e. that they do not belong to" do
before do
get "/organisations/#{unauthorised_organisation.id}/managing-agents", headers:, params: {}
end
it "returns not found 404 from users page" do
expect(response).to have_http_status(:not_found)
end
end
end
describe "organisation_relationships#create_housing_provider" do
let!(:housing_provider) { FactoryBot.create(:organisation) }
let(:params) do
{
"organisation": {
"related_organisation_id": housing_provider.id,
},
}
end
let(:request) { post "/organisations/#{organisation.id}/housing-providers", headers:, params: }
it "creates a new organisation relationship" do
expect { request }.to change(OrganisationRelationship, :count).by(1)
end
it "sets the organisation relationship attributes correctly" do
request
expect(OrganisationRelationship).to exist(child_organisation_id: organisation.id, parent_organisation_id: housing_provider.id, relationship_type: OrganisationRelationship::OWNING)
end
it "redirects to the organisation list" do
request
expect(response).to redirect_to("/organisations/#{organisation.id}/housing-providers")
end
end
describe "organisation_relationships#create_managing_agent" do
let!(:managing_agent) { FactoryBot.create(:organisation) }
let(:params) do
{
"organisation": {
"related_organisation_id": managing_agent.id,
},
}
end
let(:request) { post "/organisations/#{organisation.id}/managing-agents", headers:, params: }
it "creates a new organisation relationship" do
expect { request }.to change(OrganisationRelationship, :count).by(1)
end
it "sets the organisation relationship attributes correctly" do
request
expect(OrganisationRelationship).to exist(parent_organisation_id: organisation.id, child_organisation_id: managing_agent.id, relationship_type: OrganisationRelationship::MANAGING)
end
it "redirects to the organisation list" do
request
expect(response).to redirect_to("/organisations/#{organisation.id}/managing-agents")
end
end
describe "organisation_relationships#delete_housing_provider" do
let!(:housing_provider) { FactoryBot.create(:organisation) }
let(:params) do
{
"target_organisation_id": housing_provider.id,
}
end
let(:request) { delete "/organisations/#{organisation.id}/housing-providers", headers:, params: }
before do
FactoryBot.create(:organisation_relationship, :owning, child_organisation: organisation, parent_organisation: housing_provider)
end
it "deletes the new organisation relationship" do
expect { request }.to change(OrganisationRelationship, :count).by(-1)
end
it "redirects to the organisation list" do
request
expect(response).to redirect_to("/organisations/#{organisation.id}/housing-providers")
end
end
describe "organisation_relationships#delete_managing_agent" do
let!(:managing_agent) { FactoryBot.create(:organisation) }
let(:params) do
{
"target_organisation_id": managing_agent.id,
}
end
let(:request) { delete "/organisations/#{organisation.id}/managing-agents", headers:, params: }
before do
FactoryBot.create(
:organisation_relationship,
:managing,
parent_organisation: organisation,
child_organisation: managing_agent,
)
end
it "deletes the new organisation relationship" do
expect { request }.to change(OrganisationRelationship, :count).by(-1)
end
it "redirects to the organisation list" do
request
expect(response).to redirect_to("/organisations/#{organisation.id}/managing-agents")
end
end
end
context "with a data provider user" do
let(:user) { FactoryBot.create(:user) }
before do
sign_in user
end
context "when accessing the housing providers tab" do
context "with an organisation that the user belongs to" do
let!(:housing_provider) { FactoryBot.create(:organisation) }
let!(:other_org_housing_provider) { FactoryBot.create(:organisation, name: "Foobar LTD") }
let!(:other_organisation) { FactoryBot.create(:organisation, name: "Foobar LTD") }
before do
FactoryBot.create(:organisation_relationship, child_organisation: organisation, parent_organisation: housing_provider, relationship_type: OrganisationRelationship.relationship_types[:owning])
FactoryBot.create(:organisation_relationship, child_organisation: other_organisation, parent_organisation: other_org_housing_provider, relationship_type: OrganisationRelationship.relationship_types[:owning])
get "/organisations/#{organisation.id}/housing-providers", headers:, params: {}
end
it "shows the tab navigation" do
expected_html = "<nav class=\"app-primary-navigation\""
expect(response.body).to include(expected_html)
end
it "doesn't show an add housing provider button" do
expect(page).not_to have_link("Add a housing provider")
end
it "shows a table of housing providers" do
expected_html = "<table class=\"govuk-table\""
expect(response.body).to include(expected_html)
expect(response.body).to include(housing_provider.name)
end
it "shows only housing providers for the current user's organisation" do
expect(page).to have_content(housing_provider.name)
expect(page).not_to have_content(other_org_housing_provider.name)
end
it "shows the pagination count" do
expect(page).to have_content("1 total housing providers")
end
end
context "with an organisation that are not in scope for the user, i.e. that they do not belong to" do
before do
get "/organisations/#{unauthorised_organisation.id}/housing-providers", headers:, params: {}
end
it "returns not found 404 from users page" do
expect(response).to have_http_status(:not_found)
end
end
end
context "when accessing the managing agents tab" do
context "with an organisation that the user belongs to" do
let!(:managing_agent) { FactoryBot.create(:organisation) }
let!(:other_org_managing_agent) { FactoryBot.create(:organisation, name: "Foobar LTD") }
let!(:other_organisation) { FactoryBot.create(:organisation, name: "Foobar LTD") }
before do
FactoryBot.create(:organisation_relationship, parent_organisation: organisation, child_organisation: managing_agent, relationship_type: OrganisationRelationship.relationship_types[:managing])
FactoryBot.create(:organisation_relationship, parent_organisation: other_organisation, child_organisation: other_org_managing_agent, relationship_type: OrganisationRelationship.relationship_types[:managing])
get "/organisations/#{organisation.id}/managing-agents", headers:, params: {}
end
it "shows the tab navigation" do
expected_html = "<nav class=\"app-primary-navigation\""
expect(response.body).to include(expected_html)
end
it "doesn't show an add managing agent button" do
expect(page).not_to have_link("Add a managing agent")
end
it "shows a table of managing agents" do
expected_html = "<table class=\"govuk-table\""
expect(response.body).to include(expected_html)
expect(response.body).to include(managing_agent.name)
end
it "shows only managing agents for the current user's organisation" do
expect(page).to have_content(managing_agent.name)
expect(page).not_to have_content(other_org_managing_agent.name)
end
it "shows the pagination count" do
expect(page).to have_content("1 total agents")
end
end
context "when adding a managing agent" do
before do
get "/organisations/#{organisation.id}/managing-agents/add", headers:, params: {}
end
it "has the correct header" do
expect(response.body).to include("What is the name of your managing agent?")
end
end
context "with an organisation that are not in scope for the user, i.e. that they do not belong to" do
before do
get "/organisations/#{unauthorised_organisation.id}/managing-agents", headers:, params: {}
end
it "returns not found 404 from users page" do
expect(response).to have_http_status(:not_found)
end
end
end
end
context "with a support user" do
let(:user) { FactoryBot.create(:user, :support) }
before do
allow(user).to receive(:need_two_factor_authentication?).and_return(false)
sign_in user
end
describe "organisation_relationships#create_housing_provider" do
let!(:housing_provider) { FactoryBot.create(:organisation) }
let(:params) do
{
"organisation": {
"related_organisation_id": housing_provider.id,
},
}
end
let(:request) { post "/organisations/#{organisation.id}/housing-providers", headers:, params: }
it "creates a new organisation relationship" do
expect { request }.to change(OrganisationRelationship, :count).by(1)
end
it "sets the organisation relationship attributes correctly" do
request
expect(OrganisationRelationship).to exist(child_organisation_id: organisation.id, parent_organisation_id: housing_provider.id, relationship_type: OrganisationRelationship::OWNING)
end
it "redirects to the organisation list" do
request
expect(response).to redirect_to("/organisations/#{organisation.id}/housing-providers")
end
end
describe "organisation_relationships#create_managing_agent" do
let!(:managing_agent) { FactoryBot.create(:organisation) }
let(:params) do
{
"organisation": {
"related_organisation_id": managing_agent.id,
},
}
end
let(:request) { post "/organisations/#{organisation.id}/managing-agents", headers:, params: }
it "creates a new organisation relationship" do
expect { request }.to change(OrganisationRelationship, :count).by(1)
end
it "sets the organisation relationship attributes correctly" do
request
expect(OrganisationRelationship).to exist(parent_organisation_id: organisation.id, child_organisation_id: managing_agent.id, relationship_type: OrganisationRelationship::MANAGING)
end
it "redirects to the organisation list" do
request
expect(response).to redirect_to("/organisations/#{organisation.id}/managing-agents")
end
end
describe "organisation_relationships#delete_housing_provider" do
let!(:housing_provider) { FactoryBot.create(:organisation) }
let(:params) do
{
"target_organisation_id": housing_provider.id,
}
end
let(:request) { delete "/organisations/#{organisation.id}/housing-providers", headers:, params: }
before do
FactoryBot.create(:organisation_relationship, :owning, child_organisation: organisation, parent_organisation: housing_provider)
end
it "deletes the new organisation relationship" do
expect { request }.to change(OrganisationRelationship, :count).by(-1)
end
it "redirects to the organisation list" do
request
expect(response).to redirect_to("/organisations/#{organisation.id}/housing-providers")
end
end
describe "organisation_relationships#delete_managing_agent" do
let!(:managing_agent) { FactoryBot.create(:organisation) }
let(:params) do
{
"target_organisation_id": managing_agent.id,
}
end
let(:request) { delete "/organisations/#{organisation.id}/managing-agents", headers:, params: }
before do
FactoryBot.create(
:organisation_relationship,
:managing,
parent_organisation: organisation,
child_organisation: managing_agent,
)
end
it "deletes the new organisation relationship" do
expect { request }.to change(OrganisationRelationship, :count).by(-1)
end
it "redirects to the organisation list" do
request
expect(response).to redirect_to("/organisations/#{organisation.id}/managing-agents")
end
end
context "when viewing a specific organisation's housing providers" do
let!(:housing_provider) { FactoryBot.create(:organisation) }
let!(:other_org_housing_provider) { FactoryBot.create(:organisation, name: "Foobar LTD") }
let!(:other_organisation) { FactoryBot.create(:organisation, name: "Foobar LTD 2") }
before do
FactoryBot.create(:organisation_relationship, child_organisation: organisation, parent_organisation: housing_provider, relationship_type: OrganisationRelationship.relationship_types[:owning])
FactoryBot.create(:organisation_relationship, child_organisation: other_organisation, parent_organisation: other_org_housing_provider, relationship_type: OrganisationRelationship.relationship_types[:owning])
get "/organisations/#{organisation.id}/housing-providers", headers:, params: {}
end
it "displays the name of the organisation" do
expect(page).to have_content(organisation.name)
end
it "has a sub-navigation with correct tabs" do
expect(page).to have_css(".app-sub-navigation")
expect(page).to have_content("Users")
end
it "shows a table of housing providers" do
expected_html = "<table class=\"govuk-table\""
expect(response.body).to include(expected_html)
expect(response.body).to include(housing_provider.name)
end
it "shows only housing providers for this organisation" do
expect(page).to have_content(housing_provider.name)
expect(page).not_to have_content(other_org_housing_provider.name)
end
it "shows remove link(s)" do
expect(response.body).to include("Remove")
end
it "shows the pagination count" do
expect(page).to have_content("1 total housing providers")
end
context "when adding a housing provider" do
before do
get "/organisations/#{organisation.id}/housing-providers/add", headers:, params: {}
end
it "has the correct header" do
expect(response.body).to include("What is the name of this organisation&#39;s housing provider?")
end
it "shows an add button" do
expect(page).to have_button("Add")
end
end
end
context "when viewing a specific organisation's managing agents" do
let!(:managing_agent) { FactoryBot.create(:organisation) }
let!(:other_org_managing_agent) { FactoryBot.create(:organisation, name: "Foobar LTD") }
let!(:other_organisation) { FactoryBot.create(:organisation, name: "Foobar LTD 2") }
before do
FactoryBot.create(:organisation_relationship, parent_organisation: organisation, child_organisation: managing_agent, relationship_type: OrganisationRelationship.relationship_types[:managing])
FactoryBot.create(:organisation_relationship, parent_organisation: other_organisation, child_organisation: other_org_managing_agent, relationship_type: OrganisationRelationship.relationship_types[:managing])
get "/organisations/#{organisation.id}/managing-agents", headers:, params: {}
end
it "displays the name of the organisation" do
expect(page).to have_content(organisation.name)
end
it "has a sub-navigation with correct tabs" do
expect(page).to have_css(".app-sub-navigation")
expect(page).to have_content("Users")
end
it "shows a table of managing agents" do
expected_html = "<table class=\"govuk-table\""
expect(response.body).to include(expected_html)
expect(response.body).to include(managing_agent.name)
end
it "shows only managing agents for this organisation" do
expect(page).to have_content(managing_agent.name)
expect(page).not_to have_content(other_org_managing_agent.name)
end
it "shows the pagination count" do
expect(page).to have_content("1 total agents")
end
it "shows remove link(s)" do
expect(response.body).to include("Remove")
end
context "when adding a housing provider" do
before do
get "/organisations/#{organisation.id}/managing-agents/add", headers:, params: {}
end
it "has the correct header" do
expect(response.body).to include("What is the name of this organisation&#39;s managing agent?")
end
it "shows an add button" do
expect(page).to have_button("Add")
end
end
end
end
end
end

10
spec/requests/organisations_controller_spec.rb

@ -254,7 +254,7 @@ RSpec.describe OrganisationsController, type: :request do
expect(response.body).to include(user.email)
end
it "shows hidden accesibility fields only for active users in the current user's organisation" do
it "shows hidden accessibility fields only for active users in the current user's organisation" do
expected_case_row_log = "<span class=\"govuk-visually-hidden\">User </span><span class=\"govuk-!-font-weight-regular app-!-colour-muted\">#{user.email}</span>"
unauthorized_case_row_log = "<span class=\"govuk-visually-hidden\">User </span><span class=\"govuk-!-font-weight-regular app-!-colour-muted\">#{other_org_user.email}</span>"
expect(CGI.unescape_html(response.body)).to include(expected_case_row_log)
@ -617,7 +617,7 @@ RSpec.describe OrganisationsController, type: :request do
it "has search results in the title" do
get "/organisations/#{organisation.id}/lettings-logs?search=#{log_to_search.id}", headers: headers, params: {}
expect(page).to have_title("#{organisation.name} (1 log matching ‘#{log_to_search.id}’) - Submit social housing lettings and sales data (CORE) - GOV.UK")
expect(page).to have_title("#{organisation.name} (1 logs matching ‘#{log_to_search.id}’) - Submit social housing lettings and sales data (CORE) - GOV.UK")
end
it "shows lettings logs matching the id" do
@ -761,7 +761,7 @@ RSpec.describe OrganisationsController, type: :request do
it "has search results in the title" do
get "/organisations/#{organisation.id}/sales-logs?search=#{log_to_search.id}", headers: headers, params: {}
expect(page).to have_title("#{organisation.name} (1 log matching ‘#{log_to_search.id}’) - Submit social housing lettings and sales data (CORE) - GOV.UK")
expect(page).to have_title("#{organisation.name} (1 logs matching ‘#{log_to_search.id}’) - Submit social housing lettings and sales data (CORE) - GOV.UK")
end
it "shows sales logs matching the id" do
@ -1025,11 +1025,11 @@ RSpec.describe OrganisationsController, type: :request do
end
it "updates the table caption" do
expect(page).to have_content("1 organisation found matching ‘#{search_param}")
expect(page).to have_content("1 organisations found matching ‘#{search_param}")
end
it "has search in the title" do
expect(page).to have_title("Organisations (1 organisation matching ‘#{search_param}’) - Submit social housing lettings and sales data (CORE) - GOV.UK")
expect(page).to have_title("Organisations (1 organisations matching ‘#{search_param}’) - Submit social housing lettings and sales data (CORE) - GOV.UK")
end
context "when the search term matches more than 1 result" do

2
spec/requests/sales_logs_controller_spec.rb

@ -265,7 +265,7 @@ RSpec.describe SalesLogsController, type: :request do
it "has search results in the title" do
get "/sales-logs?search=#{log_to_search.id}", headers: headers, params: {}
expect(page).to have_title("Logs (1 log matching ‘#{log_to_search.id}’) - Submit social housing lettings and sales data (CORE) - GOV.UK")
expect(page).to have_title("Logs (1 logs matching ‘#{log_to_search.id}’) - Submit social housing lettings and sales data (CORE) - GOV.UK")
end
it "shows sales logs matching the id" do

6
spec/requests/users_controller_spec.rb

@ -952,7 +952,7 @@ RSpec.describe UsersController, type: :request do
context "when the current user matches the user ID" do
before do
get "/users/#{user.id}/deactivate", headers: headers, params: {}
get "/users/#{user.id}/deactivate", headers:, params: {}
end
it "redirects user to user page" do
@ -962,7 +962,7 @@ RSpec.describe UsersController, type: :request do
context "when the current user does not match the user ID" do
before do
get "/users/#{other_user.id}/deactivate", headers: headers, params: {}
get "/users/#{other_user.id}/deactivate", headers:, params: {}
end
it "shows deactivation page with deactivate and cancel buttons for the user" do
@ -983,7 +983,7 @@ RSpec.describe UsersController, type: :request do
context "when the current user does not match the user ID" do
before do
other_user.update!(active: false)
get "/users/#{other_user.id}/reactivate", headers: headers, params: {}
get "/users/#{other_user.id}/reactivate", headers:, params: {}
end
it "shows reactivation page with reactivate and cancel buttons for the user" do

Loading…
Cancel
Save