Browse Source

Merge branch 'main' of https://github.com/communitiesuk/submit-social-housing-lettings-and-sales-data into CLDC-1381-Postcode-display-formatting

pull/781/head
Ted-U 3 years ago
parent
commit
892d270127
  1. 12
      Gemfile.lock
  2. 2
      app/controllers/content_controller.rb
  3. 16
      app/controllers/locations_controller.rb
  4. 38
      app/controllers/schemes_controller.rb
  5. 2
      app/frontend/controllers/accessible_autocomplete_controller.js
  6. 4
      app/frontend/styles/_errors.scss
  7. 1
      app/frontend/styles/application.scss
  8. 2
      app/helpers/details_table_helper.rb
  9. 3
      app/helpers/tab_nav_helper.rb
  10. 16
      app/models/case_log.rb
  11. 2
      app/models/form.rb
  12. 12
      app/models/form/question.rb
  13. 5
      app/models/form/setup/questions/created_by_id.rb
  14. 4
      app/models/form/setup/questions/location_id.rb
  15. 8
      app/models/form/setup/questions/scheme_id.rb
  16. 14
      app/models/location.rb
  17. 55
      app/models/scheme.rb
  18. 4
      app/models/validations/soft_validations.rb
  19. 7
      app/services/postcode_service.rb
  20. 61
      app/views/content/accessibility_statement.md
  21. 7
      app/views/form/_check_answers_summary_list.html.erb
  22. 3
      app/views/layouts/_footer.html.erb
  23. 14
      app/views/locations/index.html.erb
  24. 4
      app/views/organisations/_organisation_list.html.erb
  25. 8
      app/views/schemes/_scheme_list.html.erb
  26. 22
      app/views/schemes/_scheme_summary_list_row.html.erb
  27. 161
      app/views/schemes/check_answers.html.erb
  28. 3
      app/views/schemes/details.html.erb
  29. 3
      app/views/schemes/new.html.erb
  30. 18
      app/views/schemes/show.html.erb
  31. 6
      app/views/schemes/support.html.erb
  32. 4
      app/views/schemes/support_services_provider.html.erb
  33. 4
      config/initializers/feature_toggle.rb
  34. 3
      config/initializers/govuk_design_system_formbuilder.rb
  35. 2
      config/locales/en.yml
  36. 7
      db/migrate/20220719145718_add_confirmed_to_scheme.rb
  37. 5
      db/migrate/20220722083835_add_location_admin_district_to_locations.rb
  38. 4
      db/schema.rb
  39. 6
      db/seeds.rb
  40. 15
      docs/Gemfile.lock
  41. 11
      spec/components/primary_navigation_component_spec.rb
  42. 3
      spec/factories/location.rb
  43. 2
      spec/factories/scheme.rb
  44. 4
      spec/features/form/accessible_autocomplete_spec.rb
  45. 7
      spec/features/form/check_answers_page_spec.rb
  46. 7
      spec/features/schemes_helpers.rb
  47. 223
      spec/features/schemes_spec.rb
  48. 2
      spec/fixtures/files/case_logs_download.csv
  49. 51
      spec/models/case_log_spec.rb
  50. 2
      spec/models/form/setup/questions/created_by_id_spec.rb
  51. 2
      spec/models/form/setup/questions/location_id_spec.rb
  52. 2
      spec/models/form/setup/questions/scheme_id_spec.rb
  53. 4
      spec/models/form_spec.rb
  54. 28
      spec/models/location_spec.rb
  55. 6
      spec/models/validations/soft_validations_spec.rb
  56. 4
      spec/request_helper.rb
  57. 28
      spec/requests/form_controller_spec.rb
  58. 53
      spec/requests/locations_controller_spec.rb
  59. 39
      spec/requests/schemes_controller_spec.rb
  60. 7
      spec/services/exports/case_log_export_service_spec.rb
  61. 41
      spec/services/imports/case_logs_import_service_spec.rb
  62. 30
      yarn.lock

12
Gemfile.lock

@ -82,7 +82,7 @@ GEM
public_suffix (>= 2.0.2, < 5.0)
ast (2.4.2)
aws-eventstream (1.2.0)
aws-partitions (1.608.0)
aws-partitions (1.609.0)
aws-sdk-core (3.131.3)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.525.0)
@ -157,7 +157,7 @@ GEM
rubocop
smart_properties
erubi (1.10.0)
excon (0.92.3)
excon (0.92.4)
factory_bot (6.2.1)
activesupport (>= 5.0.0)
factory_bot_rails (6.2.0)
@ -175,7 +175,7 @@ GEM
pagy (~> 5.10.1)
railties (>= 6.1)
view_component (~> 2.56.2)
govuk_design_system_formbuilder (3.1.0)
govuk_design_system_formbuilder (3.1.1)
actionview (>= 6.1)
activemodel (>= 6.1)
activesupport (>= 6.1)
@ -225,11 +225,11 @@ GEM
net-protocol
timeout
nio4r (2.5.8)
nokogiri (1.13.7-arm64-darwin)
nokogiri (1.13.8-arm64-darwin)
racc (~> 1.4)
nokogiri (1.13.7-x86_64-darwin)
nokogiri (1.13.8-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.13.7-x86_64-linux)
nokogiri (1.13.8-x86_64-linux)
racc (~> 1.4)
notifications-ruby-client (5.3.0)
jwt (>= 1.5, < 3)

2
app/controllers/content_controller.rb

@ -1,6 +1,6 @@
class ContentController < ApplicationController
def accessibility_statement
render_content_page :accessibility_statement
render_content_page :accessibility_statement, page_title: "Accessibility statement for Submit social housing lettings and sales data (CORE)"
end
def privacy_notice

16
app/controllers/locations_controller.rb

@ -6,9 +6,12 @@ class LocationsController < ApplicationController
before_action :find_scheme
before_action :authenticate_action!
include Modules::SearchFilter
def index
@pagy, @locations = pagy(@scheme.locations)
@pagy, @locations = pagy(filtered_collection(@scheme.locations, search_term))
@total_count = @scheme.locations.size
@searched = search_term.presence
end
def new
@ -19,7 +22,12 @@ class LocationsController < ApplicationController
if date_params_missing?(location_params) || valid_date_params?(location_params)
@location = Location.new(location_params)
if @location.save
location_params[:add_another_location] == "Yes" ? redirect_to(new_location_path(id: @scheme.id)) : redirect_to(scheme_check_answers_path(scheme_id: @scheme.id))
if location_params[:add_another_location] == "Yes"
redirect_to new_location_path(@scheme)
else
check_answers_path = @scheme.confirmed? ? scheme_check_answers_path(@scheme, anchor: "locations") : scheme_check_answers_path(@scheme)
redirect_to check_answers_path
end
else
render :new, status: :unprocessable_entity
end
@ -92,4 +100,8 @@ private
required_params[:postcode] = PostcodeService.clean(required_params[:postcode]) if required_params[:postcode]
required_params
end
def search_term
params["search"]
end
end

38
app/controllers/schemes_controller.rb

@ -47,7 +47,6 @@ class SchemesController < ApplicationController
page = params[:scheme][:page]
validation_errors scheme_params
if @scheme.errors.empty? && @scheme.update(scheme_params)
if check_answers
if confirm_secondary_page? page
@ -104,6 +103,10 @@ private
@scheme.errors.add(key.to_sym)
end
end
if @scheme.arrangement_type_same? && arrangement_type_value(scheme_params[:arrangement_type]) != "D"
@scheme.errors.delete(:managing_organisation_id)
end
end
def confirm_secondary_page?(page)
@ -125,6 +128,8 @@ private
"schemes/details"
elsif page.include?("edit")
"schemes/edit_name"
elsif page.include?("check-answers")
"schemes/check_answers"
end
end
@ -150,6 +155,8 @@ private
end
when "edit-name"
scheme_path(@scheme)
when "check-answers"
schemes_path(scheme_id: @scheme.id)
end
end
@ -166,16 +173,35 @@ private
:secondary_client_group,
:support_type,
:arrangement_type,
:intended_stay)
:intended_stay,
:confirmed)
full_params = required_params[:arrangement_type] == "D" && required_params[:owning_organisation_id].present? ? required_params.merge(managing_organisation_id: required_params[:owning_organisation_id]) : required_params
if arrangement_type_changed_to_different_org?(required_params)
required_params[:managing_organisation_id] = nil
end
if arrangement_type_set_to_same_org?(required_params)
required_params[:managing_organisation_id] = required_params[:owning_organisation_id] || @scheme.owning_organisation_id
end
full_params[:sensitive] = full_params[:sensitive].to_i if full_params[:sensitive]
required_params[:sensitive] = required_params[:sensitive].to_i if required_params[:sensitive]
if current_user.data_coordinator?
full_params[:owning_organisation_id] = current_user.organisation_id
required_params[:owning_organisation_id] = current_user.organisation_id
end
full_params
required_params
end
def arrangement_type_set_to_same_org?(required_params)
arrangement_type_value(required_params[:arrangement_type]) == "D" || (required_params[:arrangement_type].blank? && @scheme.present? && @scheme.arrangement_type_same?)
end
def arrangement_type_changed_to_different_org?(required_params)
@scheme.present? && @scheme.arrangement_type_same? && arrangement_type_value(required_params[:arrangement_type]) != "D" && required_params[:managing_organisation_id].blank?
end
def arrangement_type_value(key)
key.present? ? Scheme::ARRANGEMENT_TYPE[key.to_sym] : nil
end
def search_term

2
app/frontend/controllers/accessible_autocomplete_controller.js

@ -6,7 +6,7 @@ import { enhanceOption, suggestion, sort } from '../modules/search'
export default class extends Controller {
connect () {
const selectEl = this.element
const selectOptions = Array.from(selectEl.options)
const selectOptions = Array.from(selectEl.options).filter(function (option, index, arr) { return option.value !== '' })
const options = selectOptions.map((o) => enhanceOption(o))
const matches = /^(\w+)\[(\w+)\]$/.exec(selectEl.name)

4
app/frontend/styles/_errors.scss

@ -0,0 +1,4 @@
.app-summary-list__value--error {
border-left: $govuk-border-width solid $govuk-error-colour;
padding-left: govuk-spacing(3);
}

1
app/frontend/styles/application.scss

@ -40,6 +40,7 @@ $govuk-breakpoints: (
@import "primary-navigation";
@import "search";
@import "sub-navigation";
@import "errors";
// App utilities
.app-\!-colour-muted {

2
app/helpers/details_table_helper.rb

@ -4,7 +4,7 @@ module DetailsTableHelper
list = attribute[:value].map { |value| "<li>#{value}</li>" }.join
simple_format(list, { class: "govuk-list govuk-list--bullet" }, wrapper_tag: "ul")
else
value = attribute[:value].is_a?(Array) ? attribute[:value].first : attribute[:value] || "None"
value = attribute[:value].is_a?(Array) ? attribute[:value].first : attribute[:value] || "<span class=\"app-!-colour-muted\">You didn’t answer this question</span>".html_safe
simple_format(value.to_s, { class: "govuk-body" }, wrapper_tag: "p")
end

3
app/helpers/tab_nav_helper.rb

@ -14,7 +14,8 @@ module TabNavHelper
def scheme_cell(scheme)
link_text = scheme.service_name
[govuk_link_to(link_text, scheme), "<span class=\"govuk-visually-hidden\">Scheme </span><span class=\"govuk-!-font-weight-regular app-!-colour-muted\">#{scheme.primary_client_group}</span>"].join("\n")
link = scheme.confirmed? ? scheme : scheme_check_answers_path(scheme)
[govuk_link_to(link_text, link), "<span class=\"govuk-visually-hidden\">Scheme </span><span class=\"govuk-!-font-weight-regular app-!-colour-muted\">#{scheme.primary_client_group}</span>"].join("\n")
end
def org_cell(user)

16
app/models/case_log.rb

@ -413,7 +413,7 @@ class CaseLog < ApplicationRecord
csv << attribute_names + %w[unittype_sh]
all.find_each do |record|
csv << record.attributes.merge({ "unittype_sh" => record.unittype_sh }).map do |att, val|
csv << record.attributes.merge({ "unittype_sh" => record.unittype_sh, "la" => record.la }).map do |att, val|
record.form.get_question(att, record)&.label_from_value(val) || val
end
end
@ -538,10 +538,17 @@ private
self.created_by = nil if created_by.organisation != owning_organisation
end
def reset_scheme
return unless scheme && owning_organisation
self.scheme = nil if scheme.owning_organisation != owning_organisation
end
def reset_invalidated_dependent_fields!
return unless form
reset_created_by
reset_scheme
reset_not_routed_questions
reset_derived_questions
end
@ -602,7 +609,8 @@ private
end
def get_inferred_la(postcode)
PIO.infer_la(postcode)
result = PIO.lookup(postcode)
result[:location_code] if result
end
def get_has_benefits
@ -610,9 +618,9 @@ private
end
def get_lettype
return unless renttype.present? && needstype.present? && owning_organisation.present? && owning_organisation[:provider_type].present?
return unless rent_type.present? && needstype.present? && owning_organisation.present? && owning_organisation[:provider_type].present?
case RENT_TYPE_MAPPING_LABELS[renttype]
case RENT_TYPE_MAPPING_LABELS[RENT_TYPE_MAPPING[rent_type]]
when "Social Rent"
if is_supported_housing?
owning_organisation[:provider_type] == "PRP" ? 2 : 4

2
app/models/form.rb

@ -79,7 +79,7 @@ class Form
when :in_progress
"#{next_subsection.id}/check_answers".dasherize
when :not_started
first_question_in_subsection = next_subsection.pages.first.id
first_question_in_subsection = next_subsection.pages.find { |page| page.routed_to?(case_log, nil) }.id
first_question_in_subsection.to_s.dasherize
else
"error"

12
app/models/form/question.rb

@ -51,16 +51,20 @@ class Form::Question
def get_inferred_answers(case_log)
return [] unless inferred_answers
enabled_inferred_answers(inferred_answers, case_log).keys.map do |x|
question = form.get_question(x, case_log)
enabled_inferred_answers(inferred_answers, case_log).keys.map do |question_id|
question = form.get_question(question_id, case_log)
if question.present?
question.label_from_value(case_log[x])
question.label_from_value(case_log[question_id])
else
Array(x.to_s.split(".")).inject(case_log) { |o, a| o.present? ? o.public_send(*a) : "" }
Array(question_id.to_s.split(".")).inject(case_log) { |log, method| log.present? ? log.public_send(*method) : "" }
end
end
end
def get_extra_check_answer_value(_case_log)
nil
end
def read_only?
!!readonly
end

5
app/models/form/setup/questions/created_by_id.rb

@ -13,8 +13,9 @@ class Form::Setup::Questions::CreatedById < ::Form::Question
answer_opts = { "" => "Select an option" }
return answer_opts unless ActiveRecord::Base.connected?
User.select(:id, :name).each_with_object(answer_opts) do |user, hsh|
hsh[user.id] = user.name
User.select(:id, :name, :email).each_with_object(answer_opts) do |user, hsh|
hsh[user.id] = user.name if user.name.present?
hsh[user.id] = user.email if user.name.blank?
hsh
end
end

4
app/models/form/setup/questions/location_id.rb

@ -34,6 +34,10 @@ class Form::Setup::Questions::LocationId < ::Form::Question
!supported_housing_selected?(case_log)
end
def get_extra_check_answer_value(case_log)
case_log.form.get_question("la", nil).label_from_value(case_log.la)
end
private
def supported_housing_selected?(case_log)

8
app/models/form/setup/questions/scheme_id.rb

@ -20,11 +20,11 @@ class Form::Setup::Questions::SchemeId < ::Form::Question
end
def displayed_answer_options(case_log)
return {} unless case_log.created_by
user_org_scheme_ids = Scheme.select(:id).where(owning_organisation_id: case_log.created_by.organisation_id).joins(:locations).merge(Location.where("startdate <= ? or startdate IS NULL", Time.zone.today)).map(&:id)
organisation = case_log.owning_organisation || case_log.created_by&.organisation
schemes = organisation ? Scheme.select(:id).where(owning_organisation_id: organisation.id, confirmed: true) : Scheme.select(:id).where(confirmed: true)
filtered_scheme_ids = schemes.joins(:locations).merge(Location.where("startdate <= ? or startdate IS NULL", Time.zone.today)).map(&:id)
answer_options.select do |k, _v|
user_org_scheme_ids.include?(k.to_i) || k.blank?
filtered_scheme_ids.include?(k.to_i) || k.blank?
end
end

14
app/models/location.rb

@ -3,10 +3,14 @@ class Location < ApplicationRecord
validates :units, :type_of_unit, :mobility_type, presence: true
belongs_to :scheme
before_save :infer_la!, if: :postcode_changed?
before_save :lookup_postcode!, if: :postcode_changed?
attr_accessor :add_another_location
scope :search_by_postcode, ->(postcode) { where("postcode ILIKE ?", "%#{postcode.gsub(/\s+/, '')}%") }
scope :search_by_name, ->(name) { where("name ILIKE ?", "%#{name}%") }
scope :search_by, ->(param) { search_by_name(param).or(search_by_postcode(param)) }
MOBILITY_TYPE = {
"Wheelchair-user standard": "W",
"Fitted with equipment and adaptations": "A",
@ -48,7 +52,11 @@ private
end
end
def infer_la!
self.location_code = PIO.infer_la(postcode)
def lookup_postcode!
result = PIO.lookup(postcode)
if result
self.location_code = result[:location_code]
self.location_admin_district = result[:location_admin_district]
end
end
end

55
app/models/scheme.rb

@ -6,9 +6,11 @@ class Scheme < ApplicationRecord
scope :filter_by_id, ->(id) { where(id: (id.start_with?("S") ? id[1..] : id)) }
scope :search_by_service_name, ->(name) { where("service_name ILIKE ?", "%#{name}%") }
scope :search_by_postcode, ->(postcode) { joins(:locations).where("locations.postcode ILIKE ?", "%#{postcode.delete(' ')}%") }
scope :search_by_postcode, ->(postcode) { joins("LEFT JOIN locations ON locations.scheme_id = schemes.id").where("locations.postcode ILIKE ?", "%#{postcode.delete(' ')}%") }
scope :search_by, ->(param) { search_by_postcode(param).or(search_by_service_name(param)).or(filter_by_id(param)).distinct }
validate :validate_confirmed
SENSITIVE = {
No: 0,
Yes: 1,
@ -100,49 +102,49 @@ class Scheme < ApplicationRecord
def check_details_attributes
[
{ name: "Service code", value: id_to_display },
{ name: "Name", value: service_name },
{ name: "Confidential information", value: sensitive },
{ name: "Type of scheme", value: scheme_type },
{ name: "Registered under Care Standards Act 2000", value: registered_under_care_act },
{ name: "Housing stock owned by", value: owning_organisation.name },
{ name: "Support services provided by", value: arrangement_type },
{ name: "Service code", value: id_to_display, id: "id" },
{ name: "Name", value: service_name, id: "service_name" },
{ name: "Confidential information", value: sensitive, id: "sensitive" },
{ name: "Type of scheme", value: scheme_type, id: "scheme_type" },
{ name: "Registered under Care Standards Act 2000", value: registered_under_care_act, id: "registered_under_care_act" },
{ name: "Housing stock owned by", value: owning_organisation.name, id: "owning_organisation_id" },
{ name: "Support services provided by", value: arrangement_type, id: "arrangement_type" },
]
end
def check_support_services_provider_attributes
[
{ name: "Organisation providing support", value: managing_organisation&.name },
{ name: "Organisation providing support", value: managing_organisation&.name, id: "managing_organisation_id" },
]
end
def check_primary_client_attributes
[
{ name: "Primary client group", value: primary_client_group },
{ name: "Primary client group", value: primary_client_group, id: "primary_client_group" },
]
end
def check_secondary_client_confirmation_attributes
[
{ name: "Has another client group", value: has_other_client_group },
{ name: "Has another client group", value: has_other_client_group, id: "has_other_client_group" },
]
end
def check_secondary_client_attributes
[
{ name: "Secondary client group", value: secondary_client_group },
{ name: "Secondary client group", value: secondary_client_group, id: "secondary_client_group" },
]
end
def check_support_attributes
[
{ name: "Level of support given", value: support_type },
{ name: "Intended length of stay", value: intended_stay },
{ name: "Level of support given", value: support_type, id: "support_type" },
{ name: "Intended length of stay", value: intended_stay, id: "intended_stay" },
]
end
def display_attributes
[
base_attributes = [
{ name: "Scheme code", value: id_to_display },
{ name: "Name", value: service_name, edit: true },
{ name: "Confidential information", value: sensitive, edit: true },
@ -157,6 +159,11 @@ class Scheme < ApplicationRecord
{ name: "Level of support given", value: support_type },
{ name: "Intended length of stay", value: intended_stay },
]
if arrangement_type_same?
base_attributes.delete({ name: "Organisation providing support", value: managing_organisation&.name })
end
base_attributes
end
def synonyms
@ -196,4 +203,22 @@ class Scheme < ApplicationRecord
}
Scheme.intended_stays.keys.excluding("Missing").map { |key, _| OpenStruct.new(id: key, name: key.to_s.humanize, description: hints[key.to_sym]) }
end
def arrangement_type_same?
arrangement_type.present? && ARRANGEMENT_TYPE[arrangement_type.to_sym] == "D"
end
def validate_confirmed
required_attributes = attribute_names - %w[id created_at updated_at old_id old_visible_id confirmed end_date sensitive secondary_client_group total_units has_other_client_group]
if confirmed == true
required_attributes.any? do |attribute|
if self[attribute].blank?
errors.add :base
errors.add attribute.to_sym
self.confirmed = false
end
end
end
end
end

4
app/models/validations/soft_validations.rb

@ -28,14 +28,14 @@ module Validations::SoftValidations
def rent_in_soft_min_range?
return unless brent && weekly_value(brent) && startdate
rent_range = LaRentRange.find_by(start_year: collection_start_year, la:, beds:, lettype:)
rent_range = LaRentRange.find_by(start_year: collection_start_year, la:, beds:, lettype: get_lettype)
rent_range.present? && weekly_value(brent).between?(rent_range.hard_min, rent_range.soft_min)
end
def rent_in_soft_max_range?
return unless brent && weekly_value(brent) && startdate
rent_range = LaRentRange.find_by(start_year: collection_start_year, la:, beds:, lettype:)
rent_range = LaRentRange.find_by(start_year: collection_start_year, la:, beds:, lettype: get_lettype)
rent_range.present? && weekly_value(brent).between?(rent_range.soft_max, rent_range.hard_max)
end

7
app/services/postcode_service.rb

@ -3,7 +3,7 @@ class PostcodeService
@pio = Postcodes::IO.new
end
def infer_la(postcode)
def lookup(postcode)
# Avoid network calls when postcode is invalid
return unless postcode.match(POSTCODE_REGEXP)
@ -16,7 +16,10 @@ class PostcodeService
Rails.logger.warn("Postcodes.io lookup timed out")
end
if postcode_lookup && postcode_lookup.info.present?
postcode_lookup.codes["admin_district"]
{
location_code: postcode_lookup.codes["admin_district"],
location_admin_district: postcode_lookup&.admin_district,
}
end
end

61
app/views/content/accessibility_statement.md

@ -0,0 +1,61 @@
This accessibility statement applies to the Submit social housing lettings and sales data (CORE) online service.
This website is run by the Department for Levelling Up, Housing and Communities (DLUHC). We want as many people as possible to be able to use this website. For example, that means you should be able to:
- change colours, contrast levels and fonts
- zoom in up to 300% without the text spilling off the screen
- navigate most of the website using just a keyboard
- navigate most of the website using speech recognition software
- listen to most of the website using a screen reader (including the most recent versions of JAWS, NVDA and VoiceOver)
We’ve also made the website text as simple as possible to understand.
AbilityNet has advice on making your device easier to use if you have a disability.
### How accessible this website is
We know some parts of this website are not fully accessible:
- an empty link on the ‘Users’ page within ‘Your Organisation’ will cause problems for some users of assistive technology (A)
- an optional field is not clearly labelled on the ‘Invite user to submit CORE data’ page (A)
- a skip link for an error message on the ‘Household’s combined income after tax’ page doesn’t work (A)
- radio buttons on multiple pages had different functionality depending on the browser used (AAA)
- the required currency symbol on the ‘Household’s combined income after tax’ page is hidden from the label for users of screen readers (AAA)
- the input type ‘email’ interferes with Dragon voice activation software on the ‘Sign in to your account to submit CORE data page’ (AAA)
### Feedback and contact information
If you need information on this website in a different format like accessible PDF, large print, easy read, audio recording or braille, you can contact us by:
- email: [dluhc.digital-services@levellingup.gov.uk](mailto: dluhc.digital-services@levellingup.gov.uk)
- phone: 0333 202 5084
We’ll consider your request and get back to you in 2 working days.
You can also contact us through the helpdesk.
### Reporting accessibility problems with this website
We’re always looking to improve the accessibility of this website. If you find any problems not listed on this page or think we’re not meeting accessibility requirements, contact the helpdesk.
### Enforcement procedure
The Equality and Human Rights Commission (EHRC) is responsible for enforcing the Public Sector Bodies (Websites and Mobile Applications) (No. 2) Accessibility Regulations 2018 (the ‘accessibility regulations’). If you’re not happy with how we respond to your complaint, contact the Equality Advisory and Support Service (EASS).
## Technical information about this website’s accessibility
DLUHC is committed to making its website accessible, in accordance with the Public Sector Bodies (Websites and Mobile Applications) (No. 2) Accessibility Regulations 2018.
### Compliance status
This website is partially compliant with the Web Content Accessibility Guidelines version 2.1 AA standard due to the non-compliances listed below.
## Non-accessible content
The content listed below is non-accessible for the following reasons.
### Non-compliance with the accessibility regulations
An empty link on the ‘Users’ page within ‘Your Organisation’ will cause problems for some users of assistive technology. This fails WCAG 2.1 success criteria 2.4.4 Link Purpose – in context (Level A) and 3.3.2 Labels or Instructions (Level A).
We plan to fix this by December 2022.

7
app/views/form/_check_answers_summary_list.html.erb

@ -3,7 +3,12 @@
<% summary_list.row do |row| %>
<% row.key { question.check_answer_label.to_s.presence || question.header.to_s } %>
<% row.value do %>
<%= get_answer_label(question, @case_log) %><br>
<span class="govuk-!-margin-right-4"><%= get_answer_label(question, @case_log) %></span>
<% extra_value = question.get_extra_check_answer_value(@case_log) %>
<% if extra_value %>
<span class="govuk-!-font-weight-regular app-!-colour-muted"><%= extra_value %></span>
<% end %>
<br>
<% question.get_inferred_answers(@case_log).each do |inferred_answer| %>
<span class="govuk-!-font-weight-regular app-!-colour-muted"><%= inferred_answer %></span>
<% end %>

3
app/views/layouts/_footer.html.erb

@ -31,6 +31,9 @@
<h2 class="govuk-visually-hidden">Helpful links</h2>
<ul class="govuk-footer__inline-list govuk-!-margin-top-6 govuk-!-margin-bottom-0">
<li class="govuk-footer__inline-list-item">
<%= govuk_link_to("Accessibility statement", accessibility_statement_path, class: "govuk-footer__link") %>
</li>
<li class="govuk-footer__inline-list-item">
<%= govuk_link_to("Privacy notice", privacy_notice_path, class: "govuk-footer__link") %>
</li>

14
app/views/locations/index.html.erb

@ -1,4 +1,5 @@
<% title = @scheme.service_name %>
<% item_label = format_label(@pagy.count, "location") %>
<% title = format_title(@searched, @scheme.service_name, current_user, item_label, @pagy.count, nil) %>
<% content_for :title, title %>
<% content_for :before_content do %>
@ -12,9 +13,13 @@
<%= render SubNavigationComponent.new(items: scheme_items(request.path, @scheme.id, "Locations")) %>
<%= render SearchComponent.new(current_user:, search_label: "Search by location name or postcode", value: @searched) %>
<%= govuk_section_break(visible: true, size: "m") %>
<%= govuk_table do |table| %>
<%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %>
<strong><%= @scheme.locations.count %></strong> <%= @scheme.locations.count.eql?(1) ? "location" : "locations" %>
<%= render(SearchResultCaptionComponent.new(searched: @searched, count: @pagy.count, item_label:, total_count: @total_count, item: "locations", path: request.path)) %>
<% end %>
<%= table.head do |head| %>
<%= head.row do |row| %>
@ -30,7 +35,7 @@
<% row.cell(header: true, text: "Common unit type", html_attributes: {
scope: "col",
}) %>
<% row.cell(header: true, text: "Mobility type", html_attributes: {
<% row.cell(header: true, text: "Available from", html_attributes: {
scope: "col",
}) %>
<% end %>
@ -42,10 +47,11 @@
<% row.cell(text: simple_format(location_cell(location, "/schemes/#{@scheme.id}/locations/#{location.id}/edit-name"), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %>
<% row.cell(text: location.units) %>
<% row.cell(text: simple_format("<span>#{location.type_of_unit}</span>")) %>
<% row.cell(text: location.mobility_type) %>
<% row.cell(text: location.startdate&.to_formatted_s(:govuk_date)) %>
<% end %>
<% end %>
<% end %>
<% end %>
<%= govuk_button_link_to "Add a location", new_location_path(id: @scheme.id), secondary: true %>
<%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "locations" } %>

4
app/views/organisations/_organisation_list.html.erb

@ -1,8 +1,8 @@
<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: "organisations", path: request.path)) %>
<% end %>
<%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "organisations", path: request.path)) %>
<% end %>
<%= table.head do |head| %>
<%= head.row do |row| %>
<% row.cell(header: true, text: "Name", html_attributes: {

8
app/views/schemes/_scheme_list.html.erb

@ -11,7 +11,10 @@
<% row.cell(header: true, text: "Scheme", html_attributes: {
scope: "col",
}) %>
<% row.cell(header: true, text: "Managed by", html_attributes: {
<% row.cell(header: true, text: "Locations", html_attributes: {
scope: "col",
}) %>
<% row.cell(header: true, text: "Support provided by", html_attributes: {
scope: "col",
}) %>
<% row.cell(header: true, text: "Created", html_attributes: {
@ -24,8 +27,9 @@
<%= body.row do |row| %>
<% row.cell(text: scheme.id_to_display) %>
<% row.cell(text: simple_format(scheme_cell(scheme), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %>
<% row.cell(text: scheme.locations&.count) %>
<% row.cell(text: scheme.managing_organisation&.name) %>
<% row.cell(text: scheme.created_at.to_formatted_s(:govuk_date)) %>
<% row.cell(text: scheme.confirmed? ? scheme.created_at.to_formatted_s(:govuk_date) : govuk_tag(colour: "grey", text: "Incomplete")) %>
<% end %>
<% end %>
<% end %>

22
app/views/schemes/_scheme_summary_list_row.html.erb

@ -0,0 +1,22 @@
<div class="<%= "govuk-summary-list__row #{scheme.confirmed? && attribute[:name] != 'Name' ? 'govuk-summary-list__row--no-actions' : ''}" %>">
<dt class="govuk-summary-list__key">
<%= attribute[:name].to_s %>
</dt>
<% if scheme.errors[attribute[:id].to_sym].count > 0 %>
<dd class="govuk-summary-list__value app-summary-list__value--error">
<p class="govuk-error-message">
<span class="govuk-error-message govuk-visually-hidden">Error:</span> <%= scheme.errors[attribute[:id].to_sym][0] %>
</p>
<%= details_html(attribute) %>
</dd>
<% else %>
<dd class="govuk-summary-list__value">
<%= details_html(attribute) %>
</dd>
<% end %>
<% if !scheme.confirmed? || attribute[:name] == "Name" %>
<dd class="govuk-summary-list__actions">
<a class="govuk-link" href="<%= change_link %>">Change</a>
</dd>
<% end %>
</div>

161
app/views/schemes/check_answers.html.erb

@ -1,116 +1,79 @@
<% content_for :title, "Check your answers before creating this scheme" %>
<%= render partial: "organisations/headings", locals: { main: "Check your changes before creating this scheme", sub: @scheme.service_name } %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-three-quarters-from-desktop">
<%= govuk_tabs(title: "Check your answers before creating this scheme") do |component| %>
<% component.tab(label: "Scheme") do %>
<%= govuk_summary_list do |summary_list| %>
<% @scheme.check_details_attributes.each do |attr| %>
<% next if current_user.data_coordinator? && attr[:name] == ("owned by") %>
<%= summary_list.row do |row| %>
<% row.key { attr[:name].to_s } %>
<% row.value { details_html(attr) } %>
<% row.action(
text: "Change",
href: scheme_details_path(scheme_id: @scheme.id, check_answers: true),
) %>
<%= form_for(@scheme, as: :scheme, method: :patch) do |f| %>
<div class="govuk-grid-row">
<%= f.govuk_error_summary %>
<%= govuk_tabs(title: "Check your answers before creating this scheme") do |component| %>
<% component.tab(label: "Scheme") do %>
<dl class="govuk-summary-list">
<% @scheme.check_details_attributes.each do |attr| %>
<% next if current_user.data_coordinator? && attr[:name] == ("owned by") %>
<%= render partial: "scheme_summary_list_row", locals: { scheme: @scheme, attribute: attr, change_link: scheme_details_path(@scheme, check_answers: true) } %>
<% end %>
<% end %>
<% @scheme.check_support_services_provider_attributes.each do |attr| %>
<%= summary_list.row do |row| %>
<% row.key { attr[:name].to_s } %>
<% row.value { details_html(attr) } %>
<% row.action(
text: "Change",
href: scheme_support_services_provider_path(scheme_id: @scheme.id, check_answers: true),
) %>
<% if !@scheme.arrangement_type_same? %>
<% @scheme.check_support_services_provider_attributes.each do |attr| %>
<%= render partial: "scheme_summary_list_row", locals: { scheme: @scheme, attribute: attr, change_link: scheme_support_services_provider_path(@scheme, check_answers: true) } %>
<% end %>
<% end %>
<% end %>
<% @scheme.check_primary_client_attributes.each do |attr| %>
<%= summary_list.row do |row| %>
<% row.key { attr[:name].to_s } %>
<% row.value { details_html(attr) } %>
<% row.action(
text: "Change",
href: scheme_primary_client_group_path(scheme_id: @scheme.id, check_answers: true),
) %>
<% @scheme.check_primary_client_attributes.each do |attr| %>
<%= render partial: "scheme_summary_list_row", locals: { scheme: @scheme, attribute: attr, change_link: scheme_primary_client_group_path(@scheme, check_answers: true) } %>
<% end %>
<% end %>
<% @scheme.check_secondary_client_confirmation_attributes.each do |attr| %>
<%= summary_list.row do |row| %>
<% row.key { attr[:name].to_s } %>
<% row.value { details_html(attr) } %>
<% row.action(
text: "Change",
href: scheme_confirm_secondary_client_group_path(scheme_id: @scheme.id, check_answers: true),
) %>
<% @scheme.check_secondary_client_confirmation_attributes.each do |attr| %>
<%= render partial: "scheme_summary_list_row", locals: { scheme: @scheme, attribute: attr, change_link: scheme_confirm_secondary_client_group_path(@scheme, check_answers: true) } %>
<% end %>
<% end %>
<% if @scheme.has_other_client_group == "Yes" %>
<% @scheme.check_secondary_client_attributes.each do |attr| %>
<%= summary_list.row do |row| %>
<% row.key { attr[:name].to_s } %>
<% row.value { details_html(attr) } %>
<% row.action(
text: "Change",
href: scheme_secondary_client_group_path(scheme_id: @scheme.id, check_answers: true),
) %>
<% if @scheme.has_other_client_group == "Yes" %>
<% @scheme.check_secondary_client_attributes.each do |attr| %>
<%= render partial: "scheme_summary_list_row", locals: { scheme: @scheme, attribute: attr, change_link: scheme_secondary_client_group_path(@scheme, check_answers: true) } %>
<% end %>
<% end %>
<% end %>
<% @scheme.check_support_attributes.each do |attr| %>
<%= summary_list.row do |row| %>
<% row.key { attr[:name].to_s } %>
<% row.value { details_html(attr) } %>
<% row.action(
text: "Change",
href: scheme_support_path(scheme_id: @scheme.id, check_answers: true),
) %>
<% @scheme.check_support_attributes.each do |attr| %>
<%= render partial: "scheme_summary_list_row", locals: { scheme: @scheme, attribute: attr, change_link: scheme_support_path(@scheme, check_answers: true) } %>
<% end %>
<% end %>
</dl>
<% end %>
<% end %>
<% component.tab(label: "Locations") do %>
<%= govuk_table do |table| %>
<%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %>
<strong><%= @scheme.locations.count %></strong> <%= @scheme.locations.count.eql?(1) ? "location" : "locations" %>
<% end %>
<%= table.head do |head| %>
<%= head.row do |row| %>
<% row.cell(header: true, text: "Code", html_attributes: {
scope: "col",
}) %>
<% row.cell(header: true, text: "Postcode", html_attributes: {
scope: "col",
}) %>
<% row.cell(header: true, text: "Units", html_attributes: {
scope: "col",
}) %>
<% row.cell(header: true, text: "Common unit type", html_attributes: {
scope: "col",
}) %>
<% row.cell(header: true, text: "Mobility type", html_attributes: {
scope: "col",
}) %>
<% component.tab(label: "Locations") do %>
<%= govuk_table do |table| %>
<%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %>
<strong><%= @scheme.locations.count %></strong> <%= @scheme.locations.count.eql?(1) ? "location" : "locations" %>
<% end %>
<% end %>
<% @scheme.locations.each do |location| %>
<%= table.body do |body| %>
<%= body.row do |row| %>
<% row.cell(text: location.id) %>
<% row.cell(text: simple_format(location_cell(location, "/schemes/#{@scheme.id}/locations/#{location.id}/edit"), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %>
<% row.cell(text: location.units) %>
<% row.cell(text: simple_format("<span>#{location.type_of_unit}</span>")) %>
<% row.cell(text: location.mobility_type) %>
<% end %>
<%= table.head do |head| %>
<%= head.row do |row| %>
<% row.cell(header: true, text: "Code", html_attributes: {
scope: "col",
}) %>
<% row.cell(header: true, text: "Postcode", html_attributes: {
scope: "col",
}) %>
<% row.cell(header: true, text: "Units", html_attributes: {
scope: "col",
}) %>
<% row.cell(header: true, text: "Common unit type", html_attributes: {
scope: "col",
}) %>
<% row.cell(header: true, text: "Available from", html_attributes: {
scope: "col",
}) %>
<% end %>
<% end %>
<% @scheme.locations.each do |location| %>
<%= table.body do |body| %>
<%= body.row do |row| %>
<% row.cell(text: location.id) %>
<% row.cell(text: simple_format(location_cell(location, "/schemes/#{@scheme.id}/locations/#{location.id}/edit"), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %>
<% row.cell(text: location.units) %>
<% row.cell(text: simple_format("<span>#{location.type_of_unit}</span>")) %>
<% row.cell(text: location.startdate&.to_formatted_s(:govuk_date)) %>
<% end %>
<% end %>
<% end %>
<% end %>
<%= govuk_button_link_to "Add a location", new_location_path(id: @scheme.id), secondary: true %>
<% end %>
<%= govuk_button_link_to "Add a location", new_location_path(id: @scheme.id), secondary: true %>
<% end %>
<% end %>
</div>
</div>
<%= govuk_button_link_to "Create scheme", schemes_path(scheme_id: @scheme.id), html: { method: :get } %>
<%= f.hidden_field :page, value: "check-answers" %>
<%= f.hidden_field :confirmed, value: "true" %>
<% button_label = @scheme.confirmed? ? "Save" : "Create scheme" %>
<%= f.govuk_submit button_label %>
<% end %>

3
app/views/schemes/details.html.erb

@ -49,8 +49,7 @@
:id,
:name,
:description,
legend: { text: "Is this scheme registered under the Care Standards Act 2000?", size: "m" },
bold_labels: false %>
legend: { text: "Is this scheme registered under the Care Standards Act 2000?", size: "m" } %>
<% organisations = Organisation.all.map { |org| OpenStruct.new(id: org.id, name: org.name) } %>

3
app/views/schemes/new.html.erb

@ -53,8 +53,7 @@
:id,
:name,
:description,
legend: { text: "Is this scheme registered under the Care Standards Act 2000?", size: "m" },
bold_labels: false %>
legend: { text: "Is this scheme registered under the Care Standards Act 2000?", size: "m" } %>
<% if current_user.support? %>
<%= f.govuk_collection_select :owning_organisation_id,

18
app/views/schemes/show.html.erb

@ -13,16 +13,14 @@
<%= render SubNavigationComponent.new(items: scheme_items(request.path, @scheme.id, "Locations")) %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-three-quarters-from-desktop">
<%= govuk_summary_list do |summary_list| %>
<% @scheme.display_attributes.each do |attr| %>
<% next if current_user.data_coordinator? && attr[:name] == ("Housing stock owned by") %>
<%= summary_list.row do |row| %>
<% row.key { attr[:name].eql?("Registered under Care Standards Act 2000") ? "Registered under Care Standards Act 2000" : attr[:name].to_s.humanize } %>
<% row.value { details_html(attr) } %>
<% row.action(text: "Change", href: scheme_edit_name_path(scheme_id: @scheme.id)) if attr[:edit] %>
<% end %>
<%= govuk_summary_list do |summary_list| %>
<% @scheme.display_attributes.each do |attr| %>
<% next if current_user.data_coordinator? && attr[:name] == ("Housing stock owned by") %>
<%= summary_list.row do |row| %>
<% row.key { attr[:name].eql?("Registered under Care Standards Act 2000") ? "Registered under Care Standards Act 2000" : attr[:name].to_s.humanize } %>
<% row.value { details_html(attr) } %>
<% row.action(text: "Change", href: scheme_edit_name_path(scheme_id: @scheme.id)) if attr[:edit] %>
<% end %>
<% end %>
</div>
<% end %>
</div>

6
app/views/schemes/support.html.erb

@ -23,8 +23,7 @@
:id,
:name,
:description,
legend: { text: "Level of support given", size: "m" },
bold_labels: false %>
legend: { text: "Level of support given", size: "m" } %>
<% intended_length_of_stay_options_hints = { "Very short stay": "Up to one month.", "Short stay": "Up to one year.", "Medium stay": "More than one year but with an expectation to move on.", "Permanent": "Provides a home for life with no requirement for the tenant to move." } %>
@ -35,8 +34,7 @@
:id,
:name,
:description,
legend: { text: "Intended length of stay", size: "m" },
bold_labels: false %>
legend: { text: "Intended length of stay", size: "m" } %>
<%= f.hidden_field :page, value: "support" %>

4
app/views/schemes/support_services_provider.html.erb

@ -7,8 +7,6 @@
) %>
<% end %>
<%= render partial: "organisations/headings", locals: { main: "Which organisation provides the support services used by this scheme?", sub: nil } %>
<%= form_for(@scheme, method: :patch) do |f| %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
@ -22,7 +20,7 @@
managing_org_answer_options,
:id,
:name,
label: { text: "Which organisation manages this scheme?", size: "m" },
label: { text: "Which organisation provides the support services used by this scheme?", size: "m" },
options: { required: true },
"data-controller": %w[accessible-autocomplete conditional-filter] %>

4
config/initializers/feature_toggle.rb

@ -1,8 +1,6 @@
class FeatureToggle
def self.supported_housing_schemes_enabled?
return true unless Rails.env.production?
false
true
end
def self.startdate_two_week_validation_enabled?

3
config/initializers/govuk_design_system_formbuilder.rb

@ -0,0 +1,3 @@
GOVUKDesignSystemFormBuilder.configure do |conf|
conf.default_collection_radio_buttons_auto_bold_labels = false
end

2
config/locales/en.yml

@ -65,6 +65,8 @@ en:
invalid: "Select if this scheme provides for another client group"
arrangement_type:
invalid: "Select who provides the support services used by this scheme"
base:
invalid: "You must answer all the required questions for this scheme"
location:
attributes:
startdate:

7
db/migrate/20220719145718_add_confirmed_to_scheme.rb

@ -0,0 +1,7 @@
class AddConfirmedToScheme < ActiveRecord::Migration[7.0]
def change
change_table :schemes, bulk: true do |t|
t.column :confirmed, :boolean
end
end
end

5
db/migrate/20220722083835_add_location_admin_district_to_locations.rb

@ -0,0 +1,5 @@
class AddLocationAdminDistrictToLocations < ActiveRecord::Migration[7.0]
def change
add_column :locations, :location_admin_district, :string
end
end

4
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_07_20_111635) do
ActiveRecord::Schema[7.0].define(version: 2022_07_22_083835) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -250,6 +250,7 @@ ActiveRecord::Schema[7.0].define(version: 2022_07_20_111635) do
t.integer "old_visible_id"
t.string "mobility_type"
t.datetime "startdate", precision: nil
t.string "location_admin_district"
t.index ["old_id"], name: "index_locations_on_old_id", unique: true
t.index ["scheme_id"], name: "index_locations_on_scheme_id"
end
@ -320,6 +321,7 @@ ActiveRecord::Schema[7.0].define(version: 2022_07_20_111635) do
t.string "old_id"
t.integer "old_visible_id"
t.integer "total_units"
t.boolean "confirmed"
t.index ["managing_organisation_id"], name: "index_schemes_on_managing_organisation_id"
t.index ["owning_organisation_id"], name: "index_schemes_on_owning_organisation_id"
end

6
db/seeds.rb

@ -28,6 +28,7 @@ unless Rails.env.test?
if Rails.env.development? && User.count.zero?
User.create!(
name: "Provider",
email: "provider@example.com",
password: "password",
organisation: org,
@ -36,6 +37,7 @@ unless Rails.env.test?
)
User.create!(
name: "Coordinator",
email: "coordinator@example.com",
password: "password",
organisation: org,
@ -44,6 +46,7 @@ unless Rails.env.test?
)
User.create!(
name: "Support",
email: "support@example.com",
password: "password",
organisation: org,
@ -80,6 +83,7 @@ unless Rails.env.test?
primary_client_group: "O",
secondary_client_group: "H",
owning_organisation: org,
confirmed: true,
created_at: Time.zone.now,
)
@ -93,6 +97,7 @@ unless Rails.env.test?
primary_client_group: "D",
secondary_client_group: "E",
owning_organisation: org,
confirmed: true,
created_at: Time.zone.now,
)
@ -106,6 +111,7 @@ unless Rails.env.test?
primary_client_group: "G",
secondary_client_group: "R",
owning_organisation: dummy_org,
confirmed: true,
created_at: Time.zone.now,
)

15
docs/Gemfile.lock

@ -1,7 +1,7 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (6.0.5)
activesupport (6.0.5.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
@ -32,7 +32,7 @@ GEM
ffi (1.15.5)
forwardable-extended (2.6.0)
gemoji (3.0.1)
github-pages (226)
github-pages (227)
github-pages-health-check (= 1.17.9)
jekyll (= 3.9.2)
jekyll-avatar (= 0.7.0)
@ -74,7 +74,7 @@ GEM
liquid (= 4.0.3)
mercenary (~> 0.3)
minima (= 2.5.1)
nokogiri (>= 1.13.4, < 2.0)
nokogiri (>= 1.13.6, < 2.0)
rouge (= 3.26.0)
terminal-table (~> 1.4)
github-pages-health-check (1.17.9)
@ -211,7 +211,9 @@ GEM
jekyll-feed (~> 0.9)
jekyll-seo-tag (~> 2.1)
minitest (5.16.2)
nokogiri (1.13.6-arm64-darwin)
nokogiri (1.13.7-arm64-darwin)
racc (~> 1.4)
nokogiri (1.13.7-x86_64-linux)
racc (~> 1.4)
octokit (4.25.1)
faraday (>= 1, < 3)
@ -243,7 +245,7 @@ GEM
thread_safe (0.3.6)
typhoeus (1.4.0)
ethon (>= 0.9.0)
tzinfo (1.2.9)
tzinfo (1.2.10)
thread_safe (~> 0.1)
unf (0.1.4)
unf_ext
@ -254,10 +256,11 @@ GEM
PLATFORMS
arm64-darwin-21
x86_64-linux
DEPENDENCIES
github-pages
webrick
BUNDLED WITH
2.3.4
2.3.14

11
spec/components/primary_navigation_component_spec.rb

@ -39,15 +39,4 @@ RSpec.describe PrimaryNavigationComponent, type: :component do
expect(result.text).to include("Schemes")
end
end
context "when production environment" do
before do
allow(Rails.env).to receive(:production?).and_return(true)
end
it "doesn't render schemes" do
result = render_inline(described_class.new(items:))
expect(result.text).not_to include("Schemes")
end
end
end

3
spec/factories/location.rb

@ -6,6 +6,9 @@ FactoryBot.define do
units { [1, 2, 3, 4, 6, 7].sample }
type_of_building { "Purpose built" }
mobility_type { %w[A M N W X].sample }
location_code { "E09000033" }
location_admin_district { "Westminster" }
startdate { Faker::Date.between(from: 6.months.ago, to: Time.zone.today) }
scheme
trait :export do
postcode { "SW1A 2AA" }

2
spec/factories/scheme.rb

@ -10,6 +10,8 @@ FactoryBot.define do
primary_client_group { %w[O H M L A G F B D E I S N R Q P X].sample }
secondary_client_group { %w[O H M L A G F B D E I S N R Q P X].sample }
owning_organisation { FactoryBot.create(:organisation) }
managing_organisation { FactoryBot.create(:organisation) }
confirmed { true }
created_at { Time.zone.now }
trait :export do
sensitive { 1 }

4
spec/features/form/accessible_autocomplete_spec.rb

@ -60,7 +60,7 @@ RSpec.describe "Accessible Automcomplete" do
end
context "when searching schemes" do
let(:scheme) { FactoryBot.create(:scheme, owning_organisation_id: case_log.created_by.organisation_id, primary_client_group: "Q", secondary_client_group: "P") }
let(:scheme) { FactoryBot.create(:scheme, owning_organisation_id: case_log.created_by.organisation_id, managing_organisation_id: case_log.created_by.organisation_id, primary_client_group: "Q", secondary_client_group: "P") }
before do
FactoryBot.create(:location, scheme:, postcode: "W6 0ST")
@ -76,7 +76,7 @@ RSpec.describe "Accessible Automcomplete" do
it "displays appended text next to the options", js: true do
find("#case-log-scheme-id-field").click.native.send_keys("w", "6", :down, :enter)
expect(find(".autocomplete__option__append", visible: :hidden, text: scheme.service_name)).to be_present
expect(find(".autocomplete__option", visible: :hidden, text: scheme.service_name)).to be_present
expect(find("span", visible: :hidden, text: "2 locations")).to be_present
end

7
spec/features/form/check_answers_page_spec.rb

@ -144,6 +144,13 @@ RSpec.describe "Form Check Answers Page" do
expect(page).to have_content("Location")
expect(page).to have_content(location.name)
end
it "displays inferred postcode with the location_admin_district" do
case_log.update!(location:)
visit("/logs/#{id}/setup/check-answers")
expect(page).to have_content("Location")
expect(page).to have_content(location.location_admin_district)
end
end
context "when the user changes their answer from check answer page" do

7
spec/features/schemes_helpers.rb

@ -55,9 +55,12 @@ module SchemesHelpers
end
def fill_in_and_save_location
fill_in "Postcode", with: "SW1P 4DF"
fill_in "Postcode", with: "AA1 1AA"
fill_in "Location name (optional)", with: "Some name"
fill_in "Total number of units at this location", with: 1
fill_in "Total number of units at this location", with: 5
fill_in "Day", with: 2
fill_in "Month", with: 2
fill_in "Year", with: 2022
choose "Self-contained house"
choose "location-add-another-location-no-field"
choose "location-mobility-type-none-field"

223
spec/features/schemes_spec.rb

@ -119,6 +119,14 @@ RSpec.describe "Schemes scheme Features" do
expect(page).to have_content(scheme_to_search.id_to_display)
end
it "returns results with no location" do
scheme_without_location = FactoryBot.create(:scheme)
visit "/schemes"
fill_in("search", with: scheme_without_location.id_to_display)
click_button("Search")
expect(page).to have_content(scheme_without_location.id_to_display)
end
it "allows clearing the search results" do
fill_in("search", with: scheme_to_search.id_to_display)
click_button("Search")
@ -175,7 +183,7 @@ RSpec.describe "Schemes scheme Features" do
context "when there are locations that belong to the selected scheme" do
let!(:schemes) { FactoryBot.create_list(:scheme, 5) }
let(:scheme) { schemes.first }
let!(:locations) { FactoryBot.create_list(:location, 3, scheme:) }
let!(:locations) { FactoryBot.create_list(:location, 3, scheme:, postcode: "AA11AA", startdate: Time.utc(2022, 1, 1)) }
before do
visit("schemes")
@ -198,7 +206,92 @@ RSpec.describe "Schemes scheme Features" do
expect(page).to have_content(location.postcode)
expect(page).to have_content(location.units)
expect(page).to have_content(location.type_of_unit)
expect(page).to have_content(location.mobility_type)
expect(page).to have_content(location.startdate&.to_formatted_s(:govuk_date))
end
end
end
context "when I search for a specific location" do
before do
click_link("Locations")
end
it "there is a search bar with a message and search button for locations" do
expect(page).to have_field("search")
expect(page).to have_content("Search by location name or postcode")
expect(page).to have_button("Search")
end
context "when I fill in search information and press the search button" do
let(:postcode_to_search) { "NW38RR" }
let(:location_name_to_search) { "search name location" }
let(:location_to_search) { FactoryBot.create(:location, postcode: postcode_to_search, name: location_name_to_search, scheme:) }
before do
fill_in("search", with: location_to_search.name)
click_button("Search")
end
it "displays scheme matching the location name" do
expect(page).to have_content(location_name_to_search)
end
context "when I want to clear results" do
it "there is link to clear the search results" do
expect(page).to have_link("Clear search")
end
it "displays all schemes after I clear the search results" do
click_link("Clear search")
Location.all.each do |location|
expect(page).to have_content(location.name)
end
end
end
end
end
context "when the user clicks add location" do
before do
click_link("Locations")
click_link("Add a location")
end
it "shows the new location form" do
expect(page).to have_content("Add a location to this scheme")
end
context "when the user completes the new location form" do
let(:location_name) { "Area 42" }
before do
fill_in "Postcode", with: "NW1L 5DP"
fill_in "Location name (optional)", with: location_name
fill_in "Total number of units at this location", with: 1
choose "Bungalow"
choose "location-mobility-type-none-field"
choose "location-add-another-location-no-field"
click_button "Save and continue"
end
it "shows the check answers page location tab" do
expect(page.current_url.split("/").last).to eq("check-answers#locations")
expect(page).to have_content(location_name)
end
it "has the correct action button text" do
expect(page).to have_button("Save")
expect(page).not_to have_button("Create scheme")
end
context "when you click to view the scheme details" do
before do
click_link("Scheme")
end
it "does not let you change details other than the name" do
assert_selector "a", text: "Change", count: 1
end
end
end
end
@ -383,10 +476,11 @@ RSpec.describe "Schemes scheme Features" do
end
it "displays information about the first created location" do
expect(page).to have_content "SW1P4DF"
expect(page).to have_content "AA11AA"
expect(page).to have_content "Some name"
expect(page).to have_content "5"
expect(page).to have_content "Self-contained house"
expect(page).to have_content "None"
expect(page).to have_content "2 February 2022"
end
it "displays information about another location" do
@ -420,7 +514,6 @@ RSpec.describe "Schemes scheme Features" do
expect(page).to have_content "Locations"
expect(page).to have_content "#{scheme.locations.count} location"
expect(page).to have_content "ZZ11ZZ"
expect(page).to have_content("Wheelchair-user standard")
end
end
@ -431,7 +524,7 @@ RSpec.describe "Schemes scheme Features" do
end
it "displays change links" do
assert_selector "a", text: "Change", count: 13
assert_selector "a", text: "Change", count: 12
end
it "allows changing details questions" do
@ -451,13 +544,20 @@ RSpec.describe "Schemes scheme Features" do
expect(page).to have_current_path("/schemes/#{scheme.id}/check-answers")
expect(page).to have_content "Check your changes before creating this scheme"
end
it "indicates if the scheme is not complete" do
click_link("Change", href: "/schemes/#{scheme.id}/details?check_answers=true", match: :first)
choose "Another registered housing provider"
click_button "Save and continue"
expect(page).to have_content("You didn’t answer this question")
end
end
context "when selecting 'create a scheme'" do
before do
create_and_save_a_scheme
fill_in_and_save_location
click_link "Create scheme"
click_button "Create scheme"
end
it "adds scheme to the list of schemes" do
@ -465,7 +565,7 @@ RSpec.describe "Schemes scheme Features" do
expect(page).to have_content scheme.id_to_display
expect(page).to have_content scheme.service_name
expect(page).to have_content scheme.owning_organisation.name
expect(page).to have_content "#{scheme.owning_organisation.name} has been created."
expect(page).to have_content "#{scheme.service_name} has been created."
end
end
@ -535,6 +635,20 @@ RSpec.describe "Schemes scheme Features" do
expect(page).to have_current_path("/schemes/#{scheme.id}/check-answers")
expect(page).to have_content "Check your changes before creating this scheme"
end
it "keeps the provider answer when swithing between other provider options" do
click_link("Change", href: "/schemes/#{scheme.id}/details?check_answers=true", match: :first)
choose "Another organisation"
click_button "Save and continue"
expect(page).to have_content(another_organisation.name)
end
it "does not display the answer if it's changed to the same support provider" do
click_link("Change", href: "/schemes/#{scheme.id}/details?check_answers=true", match: :first)
choose "The same organisation that owns the housing stock"
click_button "Save and continue"
expect(page).not_to have_content("Organisation providing support")
end
end
end
end
@ -641,6 +755,91 @@ RSpec.describe "Schemes scheme Features" do
end
end
end
context "when I search for a specific location" do
before do
click_link("Locations")
end
it "there is a search bar with a message and search button for locations" do
expect(page).to have_field("search")
expect(page).to have_content("Search by location name or postcode")
expect(page).to have_button("Search")
end
context "when I fill in search information and press the search button" do
let(:postcode_to_search) { "NW38RR" }
let(:location_name_to_search) { "search name location" }
let(:location_to_search) { FactoryBot.create(:location, postcode: postcode_to_search, name: location_name_to_search, scheme:) }
before do
fill_in("search", with: location_to_search.name)
click_button("Search")
end
it "displays scheme matching the location name" do
expect(page).to have_content(location_name_to_search)
end
context "when I want to clear results" do
it "there is link to clear the search results" do
expect(page).to have_link("Clear search")
end
it "displays all schemes after I clear the search results" do
click_link("Clear search")
Location.all.each do |location|
expect(page).to have_content(location.name)
end
end
end
end
end
context "when the user clicks add location" do
before do
click_link("Locations")
click_link("Add a location")
end
it "shows the new location form" do
expect(page).to have_content("Add a location to this scheme")
end
context "when the user completes the new location form" do
let(:location_name) { "Area 42" }
before do
fill_in "Postcode", with: "NW1L 5DP"
fill_in "Location name (optional)", with: location_name
fill_in "Total number of units at this location", with: 1
choose "Bungalow"
choose "location-mobility-type-none-field"
choose "location-add-another-location-no-field"
click_button "Save and continue"
end
it "shows the check answers page location tab" do
expect(page.current_url.split("/").last).to eq("check-answers#locations")
expect(page).to have_content(location_name)
end
it "has the correct action button text" do
expect(page).to have_button("Save")
expect(page).not_to have_button("Create scheme")
end
context "when you click to view the scheme details" do
before do
click_link("Scheme")
end
it "does not let you change details other than the name" do
assert_selector "a", text: "Change", count: 1
end
end
end
end
end
end
end
@ -649,7 +848,7 @@ RSpec.describe "Schemes scheme Features" do
context "when selecting a scheme" do
let!(:user) { FactoryBot.create(:user, :data_coordinator, last_sign_in_at: Time.zone.now) }
let!(:schemes) { FactoryBot.create_list(:scheme, 5, owning_organisation: user.organisation) }
let!(:schemes) { FactoryBot.create_list(:scheme, 5, owning_organisation: user.organisation, managing_organisation: user.organisation, arrangement_type: "The same organisation that owns the housing stock") }
let(:location) { FactoryBot.create(:location, scheme: schemes[2]) }
let!(:case_log) { FactoryBot.create(:case_log, created_by: user, needstype: 2) }
@ -692,5 +891,11 @@ RSpec.describe "Schemes scheme Features" do
visit("/logs/#{case_log.id}/scheme")
expect(find("#case-log-scheme-id-field").all("option").count).to eq(4)
end
it "does display the schemes that are not completed" do
schemes[2].update!(confirmed: false)
visit("/logs/#{case_log.id}/scheme")
expect(find("#case-log-scheme-id-field").all("option").count).to eq(3)
end
end
end

2
spec/fixtures/files/case_logs_download.csv vendored

@ -1,2 +1,2 @@
id,status,created_at,updated_at,tenancycode,age1,sex1,ethnic,national,prevten,ecstat1,hhmemb,age2,sex2,ecstat2,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,leftreg,reservist,illness,preg_occ,startertenancy,tenancylength,tenancy,ppostcode_full,rsnvac,unittype_gn,beds,offered,wchair,earnings,incfreq,benefits,period,layear,waityear,postcode_full,reasonpref,cbl,chr,cap,reasonother,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,illness_type_1,illness_type_2,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,net_income_value_check,property_owner_organisation,property_manager_organisation,sale_or_letting,irproduct_other,purchaser_code,reason,propcode,majorrepairs,la,prevloc,hb,hbrentshortfall,property_relet,mrcdate,incref,sale_completion_date,startdate,armedforces,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,owning_organisation_id,managing_organisation_id,renttype,needstype,lettype,postcode_known,is_la_inferred,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,brent,scharge,pscharge,supcharg,tcharge,tshortfall,chcharge,declaration,ppcodenk,previous_la_known,is_previous_la_inferred,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,ethnic_other,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,rent_type,has_benefits,renewal,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat2,relat3,relat4,relat5,relat6,relat7,relat8,rent_value_check,old_form_id,lar,irproduct,old_id,joint,created_by_id,illness_type_0,retirement_value_check,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,scheme_id,location_id,unittype_sh
{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,DLUHC,{owning_org_id},,Supported housing,,,false,0,0,0,,0,,,,,,,,,,,,,,false,,,,,,,,,,,,,,,,,,,,0,,,,,,,,0,,,,,,,,,,,,,,,,,Danny Rojas,,,,,,9,,,{scheme_id},SE11TE,6
{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Westminster,,,,,,,,,,,,,,DLUHC,{owning_org_id},,Supported housing,,,false,0,0,0,,0,,,,,,,,,,,,,,false,,,,,,,,,,,,,,,,,,,,0,,,,,,,,0,,,,,,,,,,,,,,,,,Danny Rojas,,,,,,9,,,{scheme_id},SE11TE,6

1 id status created_at updated_at tenancycode age1 sex1 ethnic national prevten ecstat1 hhmemb age2 sex2 ecstat2 age3 sex3 ecstat3 age4 sex4 ecstat4 age5 sex5 ecstat5 age6 sex6 ecstat6 age7 sex7 ecstat7 age8 sex8 ecstat8 homeless underoccupation_benefitcap leftreg reservist illness preg_occ startertenancy tenancylength tenancy ppostcode_full rsnvac unittype_gn beds offered wchair earnings incfreq benefits period layear waityear postcode_full reasonpref cbl chr cap reasonother housingneeds_a housingneeds_b housingneeds_c housingneeds_f housingneeds_g housingneeds_h illness_type_1 illness_type_2 illness_type_3 illness_type_4 illness_type_8 illness_type_5 illness_type_6 illness_type_7 illness_type_9 illness_type_10 rp_homeless rp_insan_unsat rp_medwel rp_hardship rp_dontknow tenancyother net_income_value_check property_owner_organisation property_manager_organisation sale_or_letting irproduct_other purchaser_code reason propcode majorrepairs la prevloc hb hbrentshortfall property_relet mrcdate incref sale_completion_date startdate armedforces first_time_property_let_as_social_housing unitletas builtype voiddate owning_organisation_id managing_organisation_id renttype needstype lettype postcode_known is_la_inferred totchild totelder totadult net_income_known nocharge is_carehome household_charge referral brent scharge pscharge supcharg tcharge tshortfall chcharge declaration ppcodenk previous_la_known is_previous_la_inferred age1_known age2_known age3_known age4_known age5_known age6_known age7_known age8_known ethnic_group ethnic_other letting_allocation_unknown details_known_2 details_known_3 details_known_4 details_known_5 details_known_6 details_known_7 details_known_8 rent_type has_benefits renewal wrent wscharge wpschrge wsupchrg wtcharge wtshortfall refused housingneeds wchchrg newprop relat2 relat3 relat4 relat5 relat6 relat7 relat8 rent_value_check old_form_id lar irproduct old_id joint created_by_id illness_type_0 retirement_value_check tshortfall_known sheltered pregnancy_value_check hhtype new_old vacdays scheme_id location_id unittype_sh
2 {id} in_progress 2022-02-08 16:52:15 +0000 2022-02-08 16:52:15 +0000 2 Westminster DLUHC {owning_org_id} Supported housing false 0 0 0 0 false 0 0 Danny Rojas 9 {scheme_id} SE11TE 6

51
spec/models/case_log_spec.rb

@ -211,19 +211,6 @@ RSpec.describe CaseLog do
})
end
context "when a case log is created in production" do
before do
allow(Rails.env).to receive(:production?).and_return(true)
end
it "derives that all forms are general needs" do
case_log = FactoryBot.create(:case_log)
record_from_db = ActiveRecord::Base.connection.execute("select needstype from case_logs where id=#{case_log.id}").to_a[0]
expect(record_from_db["needstype"]).to eq(1)
expect(case_log["needstype"]).to eq(1)
end
end
it "correctly derives and saves partial and full major repairs date" do
record_from_db = ActiveRecord::Base.connection.execute("select mrcdate from case_logs where id=#{case_log.id}").to_a[0]
expect(record_from_db["mrcdate"].day).to eq(4)
@ -1875,6 +1862,28 @@ RSpec.describe CaseLog do
expect { case_log.update!(owning_organisation: organisation_2) }
.to change { case_log.reload.created_by }.from(created_by_user).to(nil)
end
context "when the organisation selected doesn't match the scheme set" do
let(:scheme) { FactoryBot.create(:scheme, owning_organisation: created_by_user.organisation) }
let(:location) { FactoryBot.create(:location, scheme:) }
let(:case_log) { FactoryBot.create(:case_log, owning_organisation: nil, needstype: 2, scheme_id: scheme.id) }
it "clears the scheme value" do
case_log.update!(owning_organisation: organisation_2)
expect(case_log.reload.scheme).to be nil
end
end
context "when the organisation selected still matches the scheme set" do
let(:scheme) { FactoryBot.create(:scheme, owning_organisation: organisation_2) }
let(:location) { FactoryBot.create(:location, scheme:) }
let(:case_log) { FactoryBot.create(:case_log, owning_organisation: nil, needstype: 2, scheme_id: scheme.id) }
it "does not clear the scheme value" do
case_log.update!(owning_organisation: organisation_2)
expect(case_log.reload.scheme_id).to eq(scheme.id)
end
end
end
end
@ -2223,30 +2232,20 @@ RSpec.describe CaseLog do
case_log = FactoryBot.create(:case_log)
expect(case_log.supported_housing_schemes_enabled?).to eq(true)
end
context "when in the production environment" do
before do
allow(Rails.env).to receive(:production?).and_return(true)
end
it "returns false for a case log" do
case_log = FactoryBot.create(:case_log)
expect(case_log.supported_housing_schemes_enabled?).to eq(false)
end
end
end
describe "csv download" do
let(:csv_export_file) { File.open("spec/fixtures/files/case_logs_download.csv", "r:UTF-8") }
let(:scheme) { FactoryBot.create(:scheme) }
let(:location) { FactoryBot.create(:location, scheme:, type_of_unit: 6, postcode: "SE11TE") }
let(:location) { FactoryBot.create(:location, :export, scheme:, type_of_unit: 6, postcode: "SE11TE") }
let(:user) { FactoryBot.create(:user, organisation: location.scheme.owning_organisation) }
before do
Timecop.freeze(Time.utc(2022, 6, 5))
end
it "generates a correct csv from a case log" do
case_log = FactoryBot.create(:case_log, needstype: 2, scheme:, location:)
case_log = FactoryBot.create(:case_log, needstype: 2, scheme:, location:, owning_organisation: scheme.owning_organisation, created_by: user)
expected_content = csv_export_file.read
expected_content.sub!(/\{id\}/, case_log["id"].to_s)
expected_content.sub!(/\{owning_org_id\}/, case_log["owning_organisation_id"].to_s)

2
spec/models/form/setup/questions/created_by_id_spec.rb

@ -10,11 +10,13 @@ RSpec.describe Form::Setup::Questions::CreatedById, type: :model do
let(:form) { instance_double(Form) }
let!(:user_1) { FactoryBot.create(:user, name: "first user") }
let!(:user_2) { FactoryBot.create(:user, name: "second user") }
let!(:user_3) { FactoryBot.create(:user, name: nil, email: "madeupmail@example.com") }
let(:expected_answer_options) do
{
"" => "Select an option",
user_1.id => user_1.name,
user_2.id => user_2.name,
user_3.id => user_3.email,
}
end

2
spec/models/form/setup/questions/location_id_spec.rb

@ -39,7 +39,7 @@ RSpec.describe Form::Setup::Questions::LocationId, type: :model do
context "when getting available locations" do
let(:scheme) { FactoryBot.create(:scheme) }
let(:case_log) { FactoryBot.create(:case_log, scheme:, needstype: 2) }
let(:case_log) { FactoryBot.create(:case_log, owning_organisation: scheme.owning_organisation, scheme:, needstype: 2) }
context "when there are no locations" do
it "the displayed_answer_options is an empty hash" do

2
spec/models/form/setup/questions/scheme_id_spec.rb

@ -43,7 +43,7 @@ RSpec.describe Form::Setup::Questions::SchemeId, type: :model do
let(:organisation) { FactoryBot.create(:organisation) }
let(:organisation_2) { FactoryBot.create(:organisation) }
let(:user) { FactoryBot.create(:user, organisation:) }
let(:scheme) { FactoryBot.create(:scheme, owning_organisation: organisation) }
let(:scheme) { FactoryBot.create(:scheme, owning_organisation: organisation, managing_organisation: organisation) }
let(:case_log) { FactoryBot.create(:case_log, created_by: user, needstype: 2) }
before do

4
spec/models/form_spec.rb

@ -149,9 +149,9 @@ RSpec.describe Form, type: :model do
expect(form.next_incomplete_section_redirect_path(subsection, case_log)).to eq("household-needs/check-answers")
end
it "returns the first page of the next incomplete subsection (skipping completed subsections)" do
it "returns the first page of the next incomplete subsection (skipping completed subsections, and pages that are not routed to)" do
answer_household_needs(case_log)
expect(form.next_incomplete_section_redirect_path(subsection, case_log)).to eq("accessible-select-too")
expect(form.next_incomplete_section_redirect_path(subsection, case_log)).to eq("property-postcode")
end
it "returns the declaration section for a completed case log" do

28
spec/models/location_spec.rb

@ -55,4 +55,32 @@ RSpec.describe Location, type: :model do
.to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Type of unit Select the most common type of unit at this location")
end
end
describe "scopes" do
before do
FactoryBot.create(:location, name: "ABC", postcode: "NW18RR")
FactoryBot.create(:location, name: "XYZ", postcode: "SE16HJ")
end
context "when searching by name" do
it "returns case insensitive matching records" do
expect(described_class.search_by_name("abc").count).to eq(1)
expect(described_class.search_by_name("AbC").count).to eq(1)
end
end
context "when searching by postcode" do
it "returns case insensitive matching records" do
expect(described_class.search_by_postcode("se1 6hj").count).to eq(1)
expect(described_class.search_by_postcode("SE1 6HJ").count).to eq(1)
end
end
context "when searching by all searchable field" do
it "returns case insensitive matching records" do
expect(described_class.search_by("aBc").count).to eq(1)
expect(described_class.search_by("nw18rr").count).to eq(1)
end
end
end
end

6
spec/models/validations/soft_validations_spec.rb

@ -1,7 +1,8 @@
require "rails_helper"
RSpec.describe Validations::SoftValidations do
let(:record) { FactoryBot.create(:case_log) }
let(:organisation) { FactoryBot.create(:organisation, provider_type: "PRP") }
let(:record) { FactoryBot.create(:case_log, owning_organisation: organisation) }
describe "rent min max validations" do
before do
@ -18,7 +19,8 @@ RSpec.describe Validations::SoftValidations do
)
record.la = "E07000223"
record.lettype = 1
record.needstype = 1
record.rent_type = 0
record.beds = 1
record.period = 1
record.startdate = Time.zone.local(2021, 10, 10)

4
spec/request_helper.rb

@ -5,6 +5,10 @@ module RequestHelper
WebMock.disable_net_connect!(allow_localhost: true)
WebMock.stub_request(:get, /api.postcodes.io/)
.to_return(status: 200, body: "{\"status\":404,\"error\":\"Postcode not found\"}", headers: {})
WebMock.stub_request(:get, "https://api.postcodes.io/postcodes/AA11AA")
.to_return(status: 200, body: "{\"status\":200,\"result\":{\"postcode\":\"AA1 1AA\",\"admin_district\":\"Westminster\",\"codes\":{\"admin_district\":\"E09000033\"}}}", headers: {})
WebMock.stub_request(:post, /api.notifications.service.gov.uk\/v2\/notifications\/email/)
.to_return(status: 200, body: "", headers: {})
WebMock.stub_request(:post, /api.notifications.service.gov.uk\/v2\/notifications\/sms/)

28
spec/requests/form_controller_spec.rb

@ -93,6 +93,34 @@ RSpec.describe FormController, type: :request do
expect(response.body).to match("What counts as income?")
end
end
context "when viewing the setup section schemes page" do
context "when the user is support" do
let(:user) { FactoryBot.create(:user, :support) }
context "when organisation and user have not been selected yet" do
let(:case_log) do
FactoryBot.create(
:case_log,
owning_organisation: nil,
managing_organisation: nil,
created_by: nil,
needstype: 2,
)
end
before do
locations = FactoryBot.create_list(:location, 5)
locations.each { |location| location.scheme.update!(arrangement_type: "The same organisation that owns the housing stock", managing_organisation_id: location.scheme.owning_organisation_id) }
end
it "returns an unfiltered list of schemes" do
get "/logs/#{case_log.id}/scheme", headers: headers, params: {}
expect(response.body.scan("<option value=").count).to eq(6)
end
end
end
end
end
context "when displaying check answers pages" do

53
spec/requests/locations_controller_spec.rb

@ -804,7 +804,7 @@ RSpec.describe LocationsController, type: :request do
expect(page).to have_content(location.id)
expect(page).to have_content(location.postcode)
expect(page).to have_content(location.type_of_unit)
expect(page).to have_content(location.mobility_type)
expect(page).to have_content(location.startdate&.to_formatted_s(:govuk_date))
end
end
@ -864,6 +864,30 @@ RSpec.describe LocationsController, type: :request do
end
end
end
context "when searching" do
let(:searched_location) { locations.first }
let(:search_param) { searched_location.name }
before do
get "/schemes/#{scheme.id}/locations?search=#{search_param}"
end
it "returns matching results" do
expect(page).to have_content(searched_location.name)
locations[1..].each do |location|
expect(page).not_to have_content(location.name)
end
end
it "updates the table caption" do
expect(page).to have_content("1 location found matching ‘#{search_param}")
end
it "has search in the title" do
expect(page).to have_title("#{scheme.service_name} (1 location matching ‘#{search_param}’) - Submit social housing lettings and sales data (CORE) - GOV.UK")
end
end
end
context "when signed in as a support user" do
@ -882,7 +906,7 @@ RSpec.describe LocationsController, type: :request do
expect(page).to have_content(location.id)
expect(page).to have_content(location.postcode)
expect(page).to have_content(location.type_of_unit)
expect(page).to have_content(location.mobility_type)
expect(page).to have_content(location.startdate&.to_formatted_s(:govuk_date))
end
end
@ -942,6 +966,31 @@ RSpec.describe LocationsController, type: :request do
end
end
end
context "when searching" do
let(:searched_location) { locations.first }
let(:search_param) { searched_location.name }
before do
get "/schemes/#{scheme.id}/locations?search=#{search_param}"
end
it "returns matching results" do
expect(page).to have_content(searched_location.name)
locations[1..].each do |location|
expect(page).not_to have_content(location.name)
end
end
it "updates the table caption" do
expect(page).to have_content("1 location found matching ‘#{search_param}")
end
it "has search in the title" do
expected_title = CGI.escapeHTML("#{scheme.service_name} (1 location matching ‘#{search_param}’) - Submit social housing lettings and sales data (CORE) - GOV.UK")
expect(page).to have_title(expected_title)
end
end
end
end

39
spec/requests/schemes_controller_spec.rb

@ -68,6 +68,19 @@ RSpec.describe SchemesController, type: :request do
end
end
it "shows incomplete tag if the scheme is not confirmed" do
schemes[0].update!(confirmed: nil)
get "/schemes"
assert_select ".govuk-tag", text: /Incomplete/, count: 1
end
it "displays a link to check answers page if the scheme is incomplete" do
scheme = schemes[0]
scheme.update!(confirmed: nil)
get "/schemes"
expect(page).to have_link(nil, href: /schemes\/#{scheme.id}\/check-answers/)
end
it "shows a search bar" do
expect(page).to have_field("search", type: "search")
end
@ -166,6 +179,15 @@ RSpec.describe SchemesController, type: :request do
end
end
it "returns results with no location" do
scheme_without_location = FactoryBot.create(:scheme)
get "/schemes?search=#{scheme_without_location.id}"
expect(page).to have_content(scheme_without_location.id_to_display)
schemes.each do |scheme|
expect(page).not_to have_content(scheme.id_to_display)
end
end
it "updates the table caption" do
expect(page).to have_content("1 scheme found matching ‘#{search_param}")
end
@ -557,13 +579,22 @@ RSpec.describe SchemesController, type: :request do
context "when signed in as a data coordinator" do
let(:user) { FactoryBot.create(:user, :data_coordinator) }
let(:scheme_to_update) { FactoryBot.create(:scheme, owning_organisation: user.organisation) }
let(:scheme_to_update) { FactoryBot.create(:scheme, owning_organisation: user.organisation, confirmed: nil) }
before do
sign_in user
patch "/schemes/#{scheme_to_update.id}", params:
end
context "when confirming unfinished scheme" do
let(:params) { { scheme: { owning_organisation_id: user.organisation.id, arrangement_type: "V", confirmed: true, page: "check-answers" } } }
it "does not allow the scheme to be confirmed" do
expect(response).to have_http_status(:unprocessable_entity)
expect(page).to have_content(I18n.t("activerecord.errors.models.scheme.attributes.base.invalid"))
end
end
context "when params are missing" do
let(:params) do
{ scheme: {
@ -612,7 +643,7 @@ RSpec.describe SchemesController, type: :request do
end
context "when updating support services provider" do
let(:params) { { scheme: { managing_organisation_id: organisation.id, page: "support-services-provider" } } }
let(:params) { { scheme: { arrangement_type: "Another organisation", managing_organisation_id: organisation.id, page: "support-services-provider" } } }
it "renders primary client group after successful update" do
follow_redirect!
@ -786,6 +817,7 @@ RSpec.describe SchemesController, type: :request do
registered_under_care_act: "No",
page: "details",
owning_organisation_id: organisation.id,
managing_organisation_id: organisation.id,
arrangement_type: "D" } }
end
@ -1043,7 +1075,7 @@ RSpec.describe SchemesController, type: :request do
scheme_type: "Foyer",
registered_under_care_act: "No",
page: "details",
arrangement_type: "D",
arrangement_type: "The same organisation that owns the housing stock",
owning_organisation_id: another_organisation.id } }
end
@ -1078,6 +1110,7 @@ RSpec.describe SchemesController, type: :request do
expect(scheme_to_update.reload.scheme_type).to eq("Foyer")
expect(scheme_to_update.reload.sensitive).to eq("Yes")
expect(scheme_to_update.reload.registered_under_care_act).to eq("No")
expect(scheme_to_update.reload.managing_organisation_id).to eq(scheme_to_update.owning_organisation_id)
end
end
end

7
spec/services/exports/case_log_export_service_spec.rb

@ -247,9 +247,12 @@ RSpec.describe Exports::CaseLogExportService do
context "when exporting a supporting housing case logs in XML" do
let(:export_file) { File.open("spec/fixtures/exports/supported_housing_logs.xml", "r:UTF-8") }
let(:location) { FactoryBot.create(:location, :export) }
let(:organisation) { FactoryBot.create(:organisation, provider_type: "LA") }
let(:user) { FactoryBot.create(:user, organisation:) }
let(:scheme) { FactoryBot.create(:scheme, :export, owning_organisation: organisation) }
let(:location) { FactoryBot.create(:location, :export, scheme:) }
let(:case_log) { FactoryBot.create(:case_log, :completed, :export, :sh, scheme: location.scheme, location:) }
let(:case_log) { FactoryBot.create(:case_log, :completed, :export, :sh, scheme:, location:, created_by: user, owning_organisation: organisation) }
it "generates an XML export file with the expected content" do
expected_content = replace_entity_ids(case_log, export_file.read)

41
spec/services/imports/case_logs_import_service_spec.rb

@ -20,7 +20,7 @@ RSpec.describe Imports::CaseLogsImportService do
before do
WebMock.stub_request(:get, /api.postcodes.io\/postcodes\/LS166FT/)
.to_return(status: 200, body: '{"status":200,"result":{"codes":{"admin_district":"E08000035"}}}', headers: {})
.to_return(status: 200, body: '{"status":200,"result":{"admin_district":"Westminster","codes":{"admin_district":"E08000035"}}}', headers: {})
allow(Organisation).to receive(:find_by).and_return(nil)
allow(Organisation).to receive(:find_by).with(old_visible_id: organisation.old_visible_id.to_i).and_return(organisation)
@ -216,7 +216,7 @@ RSpec.describe Imports::CaseLogsImportService do
end
end
context "and the net income soft validation is triggered" do
context "and the net income soft validation is triggered (net_income_value_check)" do
before do
case_log_xml.at_xpath("//xmlns:Q8a").content = "1 Weekly"
case_log_xml.at_xpath("//xmlns:Q8Money").content = 890.00
@ -229,6 +229,43 @@ RSpec.describe Imports::CaseLogsImportService do
end
end
context "and the rent soft validation is triggered (rent_value_check)" do
before do
case_log_xml.at_xpath("//xmlns:Q18ai").content = 200.00
case_log_xml.at_xpath("//xmlns:Q18av").content = 232.02
case_log_xml.at_xpath("//xmlns:Q17").content = "1 Weekly for 52 weeks"
LaRentRange.create!(
start_year: 2021,
la: "E08000035",
beds: 2,
lettype: 1,
soft_max: 900,
hard_max: 1500,
soft_min: 500,
hard_min: 100,
)
end
it "completes the log" do
case_log_service.send(:create_log, case_log_xml)
case_log = CaseLog.find_by(old_id: case_log_id)
expect(case_log.status).to eq("completed")
end
end
context "and the retirement soft validation is triggered (retirement_value_check)" do
before do
case_log_xml.at_xpath("//xmlns:P1Age").content = 68
case_log_xml.at_xpath("//xmlns:P1Eco").content = "6) Not Seeking Work"
end
it "completes the log" do
case_log_service.send(:create_log, case_log_xml)
case_log = CaseLog.find_by(old_id: case_log_id)
expect(case_log.status).to eq("completed")
end
end
context "and this is a supported housing log with multiple locations under a scheme" do
let(:case_log_id) { "0b4a68df-30cc-474a-93c0-a56ce8fdad3b" }

30
yarn.lock

@ -1448,9 +1448,9 @@ acorn@^7.1.1:
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1:
version "8.7.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30"
integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==
version "8.8.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8"
integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==
ajv-formats@^2.1.1:
version "2.1.1"
@ -1915,9 +1915,9 @@ camelcase@^5.3.1:
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
caniuse-lite@^1.0.30001366:
version "1.0.30001367"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001367.tgz#2b97fe472e8fa29c78c5970615d7cd2ee414108a"
integrity sha512-XDgbeOHfifWV3GEES2B8rtsrADx4Jf+juKX2SICJcaUhjYBO3bR96kvEIHa15VU6ohtOhBZuPGGYGbXMRn0NCw==
version "1.0.30001368"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001368.tgz#c5c06381c6051cd863c45021475434e81936f713"
integrity sha512-wgfRYa9DenEomLG/SdWgQxpIyvdtH3NW8Vq+tB6AwR9e56iOIcu1im5F/wNdDf04XlKHXqIx4N8Jo0PemeBenQ==
chalk@^1.1.3:
version "1.1.3"
@ -2384,9 +2384,9 @@ ejs@^3.1.6:
jake "^10.8.5"
electron-to-chromium@^1.4.188:
version "1.4.195"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.195.tgz#139b2d95a42a3f17df217589723a1deac71d1473"
integrity sha512-vefjEh0sk871xNmR5whJf9TEngX+KTKS3hOHpjoMpauKkwlGwtMz1H8IaIjAT/GNnX0TbGwAdmVoXCAzXf+PPg==
version "1.4.198"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.198.tgz#36a8e7871046f7f94c01dc0133912fd5cf226c6a"
integrity sha512-jwqQPdKGeAslcq8L+1SZZgL6uDiIDmTe9Gq4brsdWAH27y7MJ2g9Ue6MyST3ogmSM49EAQP7bype1V5hsuNrmQ==
element-closest@^2.0.2:
version "2.0.2"
@ -3090,9 +3090,9 @@ globals@^11.1.0:
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
globals@^13.15.0:
version "13.16.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-13.16.0.tgz#9be4aca28f311aaeb974ea54978ebbb5e35ce46a"
integrity sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q==
version "13.17.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-13.17.0.tgz#902eb1e680a41da93945adbdcb5a9f361ba69bd4"
integrity sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==
dependencies:
type-fest "^0.20.2"
@ -3822,9 +3822,9 @@ linkify-it@^3.0.1:
uc.micro "^1.0.1"
liquidjs@^9.36.1:
version "9.39.1"
resolved "https://registry.yarnpkg.com/liquidjs/-/liquidjs-9.39.1.tgz#850039c7c95ad5fa5ff66aa3dddfd6d2fad93128"
integrity sha512-TFu19Cml0K2T34UY50ZKUApHgiiEaayqNW9J3o3ubLQCuhMFhY7jqwNpY1oVvvAJxCCxVqL6bJnGc1Fktth0Fw==
version "9.39.2"
resolved "https://registry.yarnpkg.com/liquidjs/-/liquidjs-9.39.2.tgz#f2409f1108b91fc4f1b06e1f51f2be70f434cb92"
integrity sha512-7fdd8hn7U/FUANGWaBbF41LmsRS40uXj/jFldyddYqgLDGMyZSYJsajOvZIZGkAxJdUYbZksrHRU4Q8kg8sTPg==
list-to-array@^1.1.0:
version "1.1.0"

Loading…
Cancel
Save