Browse Source

Merge branch 'main' into CLDC-1675-rent-validations

# Conflicts:
#	app/models/validations/financial_validations.rb
#	config/locales/en.yml
pull/959/head
natdeanlewissoftwire 3 years ago
parent
commit
f55d6bb519
  1. 6
      Gemfile.lock
  2. 2
      app/components/search_component.html.erb
  3. 4
      app/components/search_component.rb
  4. 125
      app/controllers/organisation_relationships_controller.rb
  5. 10
      app/frontend/styles/_accessible-autocomplete.scss
  6. 28
      app/helpers/navigation_items_helper.rb
  7. 5
      app/models/bulk_upload.rb
  8. 4
      app/models/form/lettings/questions/scheme_id.rb
  9. 16
      app/models/form/sales/pages/buyer1_income.rb
  10. 16
      app/models/form/sales/pages/property_local_authority.rb
  11. 14
      app/models/form/sales/questions/buyer1_income.rb
  12. 21
      app/models/form/sales/questions/buyer1_income_known.rb
  13. 331
      app/models/form/sales/questions/property_local_authority.rb
  14. 24
      app/models/form/sales/questions/property_local_authority_known.rb
  15. 12
      app/models/form/sales/sections/finances.rb
  16. 15
      app/models/form/sales/subsections/income_benefits_and_outgoings.rb
  17. 1
      app/models/form/sales/subsections/property_information.rb
  18. 1
      app/models/form_handler.rb
  19. 12
      app/models/organisation.rb
  20. 9
      app/models/organisation_relationship.rb
  21. 17
      app/views/form/guidance/_what_counts_as_income_sales.html.erb
  22. 2
      app/views/form/review.html.erb
  23. 8
      app/views/logs/index.html.erb
  24. 22
      app/views/organisation_relationships/_housing_provider_list.erb
  25. 22
      app/views/organisation_relationships/_managing_agent_list.erb
  26. 3
      app/views/organisation_relationships/_related_organisation_select_question.html.erb
  27. 34
      app/views/organisation_relationships/add_housing_provider.html.erb
  28. 34
      app/views/organisation_relationships/add_managing_agent.html.erb
  29. 48
      app/views/organisation_relationships/housing_providers.html.erb
  30. 50
      app/views/organisation_relationships/managing_agents.html.erb
  31. 2
      app/views/organisations/index.html.erb
  32. 2
      app/views/organisations/logs.html.erb
  33. 6
      config/initializers/feature_toggle.rb
  34. 6
      config/routes.rb
  35. 8
      db/migrate/20221007133155_add_la_to_sales_log.rb
  36. 6
      db/migrate/20221007142742_add_income1_to_sales_log.rb
  37. 10
      db/migrate/20221017095918_add_relationship_type_to_org_relationships.rb
  38. 5
      db/migrate/20221019082625_rename_managing_agents_column.rb
  39. 9
      db/schema.rb
  40. 38
      db/seeds.rb
  41. 5
      spec/factories/organisation.rb
  42. 14
      spec/factories/organisation_relationship.rb
  43. 4
      spec/factories/sales_log.rb
  44. 22
      spec/helpers/navigation_items_helper_spec.rb
  45. 33
      spec/models/form/sales/pages/buyer1_income_spec.rb
  46. 33
      spec/models/form/sales/pages/household_wheelchair_spec.rb
  47. 55
      spec/models/form/sales/questions/buyer1_income_known_spec.rb
  48. 53
      spec/models/form/sales/questions/buyer1_income_spec.rb
  49. 49
      spec/models/form/sales/questions/household_wheelchair_spec.rb
  50. 33
      spec/models/form/sales/sections/finances_spec.rb
  51. 33
      spec/models/form/sales/subsections/income_benefits_and_outgoings_spec.rb
  52. 1
      spec/models/form/sales/subsections/property_information_spec.rb
  53. 4
      spec/models/form_handler_spec.rb
  54. 96
      spec/models/organisation_spec.rb
  55. 4
      spec/requests/form_controller_spec.rb
  56. 2
      spec/requests/lettings_logs_controller_spec.rb
  57. 8
      spec/requests/locations_controller_spec.rb
  58. 473
      spec/requests/organisation_relationships_controller_spec.rb
  59. 10
      spec/requests/organisations_controller_spec.rb
  60. 2
      spec/requests/sales_logs_controller_spec.rb
  61. 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")

125
app/controllers/organisation_relationships_controller.rb

@ -0,0 +1,125 @@
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 do |format|
format.html do
@pagy, @housing_providers = pagy(unpaginated_filtered_housing_providers)
@organisations = organisations
@searched = search_term.presence
@total_count = housing_providers.size
render "organisation_relationships/housing_providers", layout: "application"
end
end
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 do |format|
format.html do
@pagy, @managing_agents = pagy(unpaginated_filtered_managing_agents)
@organisations = organisations
@searched = search_term.presence
@total_count = managing_agents.size
render "organisation_relationships/managing_agents", layout: "application"
end
end
end
def add_housing_provider
@organisations = Organisation.where.not(id: @organisation.id).pluck(:id, :name)
respond_to do |format|
format.html do
render "organisation_relationships/add_housing_provider", layout: "application"
end
end
end
def add_managing_agent
@organisations = Organisation.where.not(id: @organisation.id).pluck(:id, :name)
respond_to do |format|
format.html do
render "organisation_relationships/add_managing_agent", layout: "application"
end
end
end
def create_housing_provider
child_organisation_id = @organisation.id
parent_organisation_id = related_organisation_id
relationship_type = OrganisationRelationship::OWNING
if 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
elsif OrganisationRelationship.exists?(child_organisation_id:, parent_organisation_id:, 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
create!(child_organisation_id:, parent_organisation_id:, relationship_type:)
redirect_to housing_providers_organisation_path(related_organisation_id:)
end
def create_managing_agent
parent_organisation_id = @organisation.id
child_organisation_id = related_organisation_id
relationship_type = OrganisationRelationship::MANAGING
if 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
elsif OrganisationRelationship.exists?(child_organisation_id:, parent_organisation_id:, 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
create!(child_organisation_id:, parent_organisation_id:, relationship_type:)
redirect_to managing_agents_organisation_path(related_organisation_id:)
end
private
def create!(child_organisation_id:, parent_organisation_id:, relationship_type:)
@resource = OrganisationRelationship.new(child_organisation_id:, parent_organisation_id:, relationship_type:)
@resource.save!
end
def create(child_organisation_id, parent_organisation_id, relationship_type)
@resource = OrganisationRelationship.new(child_organisation_id:, parent_organisation_id:, relationship_type:)
@resource.save!
end
def related_organisation_id
params["organisation"]["related_organisation_id"]
end
def organisation
@organisation ||= Organisation.find(params[: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;

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_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
[
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_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
@ -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

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)

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

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

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

14
app/models/form/sales/questions/buyer1_income.rb

@ -0,0 +1,14 @@
class Form::Sales::Questions::Buyer1Income < ::Form::Question
def initialize(id, hsh, page)
super
@id = "income1"
@check_answer_label = "Buyer 1’s gross annual income"
@header = "Buyer 1’s gross annual income"
@type = "numeric"
@page = page
@min = 0
@step = 1
@width = 5
@prefix = "£"
end
end

21
app/models/form/sales/questions/buyer1_income_known.rb

@ -0,0 +1,21 @@
class Form::Sales::Questions::Buyer1IncomeKnown < ::Form::Question
def initialize(id, hsh, page)
super
@id = "income1nk"
@check_answer_label = "Buyer 1’s gross annual income"
@header = "Do you know buyer 1’s annual income?"
@type = "radio"
@answer_options = ANSWER_OPTIONS
@page = page
@guidance_position = GuidancePosition::BOTTOM
@guidance_partial = "what_counts_as_income_sales"
@conditional_for = {
"income1" => [0],
}
end
ANSWER_OPTIONS = {
"0" => { "value" => "Yes" },
"1" => { "value" => "No" },
}.freeze
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

12
app/models/form/sales/sections/finances.rb

@ -0,0 +1,12 @@
class Form::Sales::Sections::Finances < ::Form::Section
def initialize(id, hsh, form)
super
@id = "finances"
@label = "Finances"
@description = ""
@form = form
@subsections = [
Form::Sales::Subsections::IncomeBenefitsAndOutgoings.new(nil, nil, self),
]
end
end

15
app/models/form/sales/subsections/income_benefits_and_outgoings.rb

@ -0,0 +1,15 @@
class Form::Sales::Subsections::IncomeBenefitsAndOutgoings < ::Form::Subsection
def initialize(id, hsh, section)
super
@id = "income_benefits_and_outgoings"
@label = "Income, benefits and outgoings"
@section = section
@depends_on = [{ "setup" => "completed" }]
end
def pages
@pages ||= [
Form::Sales::Pages::Buyer1Income.new(nil, nil, self),
]
end
end

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

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

1
app/models/form_handler.rb

@ -22,6 +22,7 @@ class FormHandler
sales_sections = [
Form::Sales::Sections::PropertyInformation,
Form::Sales::Sections::Household,
Form::Sales::Sections::Finances,
]
current_form = Form.new(nil, current_collection_start_year, sales_sections, "sales")
previous_form = Form.new(nil, current_collection_start_year - 1, sales_sections, "sales")

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

17
app/views/form/guidance/_what_counts_as_income_sales.html.erb

@ -0,0 +1,17 @@
<%= govuk_details(summary_text: "What counts as income?") do %>
<p class="govuk-body">You should include any income from:</p>
<ul class="govuk-list govuk-list--bullet">
<li>employment</li>
<li>pensions</li>
<li>investments</li>
<li>Universal Credit</li>
</ul>
<p class="govuk-body">Don’t include:</p>
<ul class="govuk-list govuk-list--bullet">
<li>National Insurance (NI) contributions and tax</li>
<li>housing benefit</li>
<li>child benefit</li>
<li>council tax support</li>
</ul>
<% end %>

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" => "",
}) %>

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/#{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/#{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 %>

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

@ -0,0 +1,48 @@
<% item_label = format_label(@pagy.count, "housing providers") %>
<% if current_user.data_coordinator? %>
<% if params["related_organisation_id"] %>
<%= govuk_notification_banner(
title_text: "Success",
success: true, title_heading_level: 3,
title_id: "swanky-notifications"
) do |notification_banner|
notification_banner.heading(text: "#{Organisation.find(params['related_organisation_id']).name} is now one of your housing providers")
end %>
<% end %>
<% end %>
<% 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>
<% if params["related_organisation_id"] %>
<%= govuk_notification_banner(
title_text: "Success",
success: true, title_heading_level: 3,
title_id: "swanky-notifications"
) do |notification_banner|
notification_banner.heading(text: "#{Organisation.find(params['related_organisation_id']).name} is now one of this organisation's housing providers")
end %>
<% end %>
<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? %>
<%= govuk_button_link_to "Add a housing provider", housing_providers_add_organisation_path(organisation_id: @organisation.id), html: { method: :get } %>
<% elsif 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 %>

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

@ -0,0 +1,50 @@
<% item_label = format_label(@pagy.count, "managing agents") %>
<% if current_user.data_coordinator? %>
<% if params["related_organisation_id"] %>
<%= govuk_notification_banner(
title_text: "Success",
success: true, title_heading_level: 3,
title_id: "swanky-notifications"
) do |notification_banner|
notification_banner.heading(text: "#{Organisation.find(params['related_organisation_id']).name} is now one of this organisation's managing agents")
end %>
<% end %>
<% end %>
<% 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>
<% if params["related_organisation_id"] %>
<%= govuk_notification_banner(
title_text: "Success",
success: true, title_heading_level: 3,
title_id: "swanky-notifications"
) do |notification_banner|
notification_banner.heading(text: "#{Organisation.find(params['related_organisation_id']).name} is now one of this organisation's managing agents")
end %>
<% end %>
<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? %>
<%= govuk_button_link_to "Add a managing agent", managing_agents_add_organisation_path(organisation_id: @organisation.id), html: { method: :get } %>
<% elsif 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 %>

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 %>

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

6
config/routes.rb

@ -79,6 +79,12 @@ 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"
post "housing-providers", to: "organisation_relationships#create_housing_provider"
get "managing-agents", to: "organisation_relationships#managing_agents"
get "managing-agents/add", to: "organisation_relationships#add_managing_agent"
post "managing-agents", to: "organisation_relationships#create_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

6
db/migrate/20221007142742_add_income1_to_sales_log.rb

@ -0,0 +1,6 @@
class AddIncome1ToSalesLog < ActiveRecord::Migration[7.0]
change_table :sales_logs, bulk: true do |t|
t.column :income1, :int
t.column :income1nk, :int
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

9
db/schema.rb

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2022_10_11_094347) do
ActiveRecord::Schema[7.0].define(version: 2022_10_19_082625) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -277,6 +277,7 @@ ActiveRecord::Schema[7.0].define(version: 2022_10_11_094347) 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|
@ -296,7 +297,7 @@ ActiveRecord::Schema[7.0].define(version: 2022_10_11_094347) 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"
@ -358,12 +359,16 @@ ActiveRecord::Schema[7.0].define(version: 2022_10_11_094347) do
t.integer "hholdcount"
t.integer "age3"
t.integer "age3_known"
t.integer "income1"
t.integer "income1nk"
t.integer "age4"
t.integer "age4_known"
t.integer "age5"
t.integer "age5_known"
t.integer "age6"
t.integer "age6_known"
t.string "la"
t.integer "la_known"
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"

38
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",
)

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

4
spec/factories/sales_log.rb

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

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

33
spec/models/form/sales/pages/buyer1_income_spec.rb

@ -0,0 +1,33 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::Buyer1Income, type: :model do
subject(:page) { described_class.new(page_id, page_definition, subsection) }
let(:page_id) { nil }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection) }
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[income1nk income1])
end
it "has the correct id" do
expect(page.id).to eq("buyer_1_income")
end
it "has the correct header" do
expect(page.header).to eq("")
end
it "has the correct description" do
expect(page.description).to eq("")
end
it "has correct depends_on" do
expect(page.depends_on).to be_nil
end
end

33
spec/models/form/sales/pages/household_wheelchair_spec.rb

@ -0,0 +1,33 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::HouseholdWheelchair, type: :model do
subject(:page) { described_class.new(page_id, page_definition, subsection) }
let(:page_id) { nil }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection) }
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[wheel])
end
it "has the correct id" do
expect(page.id).to eq("household_wheelchair")
end
it "has the correct header" do
expect(page.header).to eq("")
end
it "has the correct description" do
expect(page.description).to eq("")
end
it "has correct depends_on" do
expect(page.depends_on).to be_nil
end
end

55
spec/models/form/sales/questions/buyer1_income_known_spec.rb

@ -0,0 +1,55 @@
require "rails_helper"
RSpec.describe Form::Sales::Questions::Buyer1IncomeKnown, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("income1nk")
end
it "has the correct header" do
expect(question.header).to eq("Do you know buyer 1’s annual income?")
end
it "has the correct check_answer_label" do
expect(question.check_answer_label).to eq("Buyer 1’s gross annual income")
end
it "has the correct type" do
expect(question.type).to eq("radio")
end
it "is not marked as derived" do
expect(question.derived?).to be false
end
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"0" => { "value" => "Yes" },
"1" => { "value" => "No" },
})
end
it "has correct conditional for" do
expect(question.conditional_for).to eq({
"income1" => [0],
})
end
it "has the correct guidance_partial" do
expect(question.guidance_partial).to eq("what_counts_as_income_sales")
end
it "has the correct guidance position", :aggregate_failures do
expect(question.bottom_guidance?).to eq(true)
expect(question.top_guidance?).to eq(false)
end
end

53
spec/models/form/sales/questions/buyer1_income_spec.rb

@ -0,0 +1,53 @@
require "rails_helper"
RSpec.describe Form::Sales::Questions::Buyer1Income, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("income1")
end
it "has the correct header" do
expect(question.header).to eq("Buyer 1’s gross annual income")
end
it "has the correct check_answer_label" do
expect(question.check_answer_label).to eq("Buyer 1’s gross annual income")
end
it "has the correct type" do
expect(question.type).to eq("numeric")
end
it "is not marked as derived" do
expect(question.derived?).to be false
end
it "has the correct hint" do
expect(question.hint_text).to be_nil
end
it "has correct width" do
expect(question.width).to eq(5)
end
it "has correct step" do
expect(question.step).to eq(1)
end
it "has correct prefix" do
expect(question.prefix).to eq("£")
end
it "has correct min" do
expect(question.min).to eq(0)
end
end

49
spec/models/form/sales/questions/household_wheelchair_spec.rb

@ -0,0 +1,49 @@
require "rails_helper"
RSpec.describe Form::Sales::Questions::HouseholdWheelchair, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("wheel")
end
it "has the correct header" do
expect(question.header).to eq("Does anyone in the household use a wheelchair?")
end
it "has the correct check_answer_label" do
expect(question.check_answer_label).to be_nil
end
it "has the correct type" do
expect(question.type).to eq("radio")
end
it "is not marked as derived" do
expect(question.derived?).to be false
end
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"1" => { "value" => "Yes" },
"2" => { "value" => "No" },
"3" => { "value" => "Don't know" },
})
end
it "has correct conditional for" do
expect(question.conditional_for).to be_nil
end
it "has the correct hint" do
expect(question.hint_text).to eq("This can be inside or outside the home")
end
end

33
spec/models/form/sales/sections/finances_spec.rb

@ -0,0 +1,33 @@
require "rails_helper"
RSpec.describe Form::Sales::Sections::Finances, type: :model do
subject(:section) { described_class.new(section_id, section_definition, form) }
let(:section_id) { nil }
let(:section_definition) { nil }
let(:form) { instance_double(Form) }
it "has correct form" do
expect(section.form).to eq(form)
end
it "has correct subsections" do
expect(section.subsections.map(&:id)).to eq(
%w[
income_benefits_and_outgoings
],
)
end
it "has the correct id" do
expect(section.id).to eq("finances")
end
it "has the correct label" do
expect(section.label).to eq("Finances")
end
it "has the correct description" do
expect(section.description).to eq("")
end
end

33
spec/models/form/sales/subsections/income_benefits_and_outgoings_spec.rb

@ -0,0 +1,33 @@
require "rails_helper"
RSpec.describe Form::Sales::Subsections::IncomeBenefitsAndOutgoings, type: :model do
subject(:subsection) { described_class.new(subsection_id, subsection_definition, section) }
let(:subsection_id) { nil }
let(:subsection_definition) { nil }
let(:section) { instance_double(Form::Sales::Sections::Household) }
it "has correct section" do
expect(subsection.section).to eq(section)
end
it "has correct pages" do
expect(subsection.pages.map(&:id)).to eq(
%w[
buyer_1_income
],
)
end
it "has the correct id" do
expect(subsection.id).to eq("income_benefits_and_outgoings")
end
it "has the correct label" do
expect(subsection.label).to eq("Income, benefits and outgoings")
end
it "has correct depends on" do
expect(subsection.depends_on).to eq([{ "setup" => "completed" }])
end
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(39)
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(39)
expect(form.pages.count).to eq(41)
expect(form.name).to eq("2021_2022_sales")
end
end

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

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

473
spec/requests/organisation_relationships_controller_spec.rb

@ -0,0 +1,473 @@
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?related_organisation_id=#{housing_provider.id}")
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?related_organisation_id=#{managing_agent.id}")
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?related_organisation_id=#{housing_provider.id}")
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?related_organisation_id=#{managing_agent.id}")
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 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
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