diff --git a/Gemfile b/Gemfile index 12d0bbdff..168ace7af 100644 --- a/Gemfile +++ b/Gemfile @@ -34,9 +34,8 @@ gem "json-schema" # Authentication # Point at branch until devise is compatible with Turbo, see https://github.com/heartcombo/devise/pull/5340 gem "devise", github: "baarkerlounger/devise", branch: "dluhc-fixes" -# Two-factor Authentication for devise models. Pointing at fork until this is merged for Rails 6 compatibility -# https://github.com/Houdini/two_factor_authentication/pull/204 -gem "two_factor_authentication", github: "baarkerlounger/two_factor_authentication" +# Two-factor Authentication for devise models. +gem "devise_two_factor_authentication" # UK postcode parsing and validation gem "uk_postcode" # Get rich data from postcode lookups. Wraps postcodes.io diff --git a/Gemfile.lock b/Gemfile.lock index ce5f8ffad..e2827bbed 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,17 +10,6 @@ GIT responders warden (~> 1.2.3) -GIT - remote: https://github.com/baarkerlounger/two_factor_authentication.git - revision: 5fa6ba40d90df9c1711d1b5eeff34686dda133a2 - specs: - two_factor_authentication (2.2.0) - devise - encryptor - rails (>= 3.1.1) - randexp - rotp (>= 4.0.0) - GEM remote: https://rubygems.org/ specs: @@ -145,6 +134,12 @@ GEM crack (0.4.5) rexml crass (1.0.6) + devise_two_factor_authentication (3.0.0) + devise + encryptor + rails (>= 3.1.1) + randexp + rotp (>= 4.0.0) diff-lcs (1.5.0) digest (3.1.0) docile (1.4.0) @@ -440,6 +435,7 @@ DEPENDENCIES capybara capybara-lockstep devise! + devise_two_factor_authentication dotenv-rails erb_lint factory_bot_rails @@ -475,7 +471,6 @@ DEPENDENCIES simplecov stimulus-rails timecop (~> 0.9.4) - two_factor_authentication! tzinfo-data uk_postcode view_component diff --git a/app/controllers/auth/passwords_controller.rb b/app/controllers/auth/passwords_controller.rb index e6b1f3a49..bd4b119cf 100644 --- a/app/controllers/auth/passwords_controller.rb +++ b/app/controllers/auth/passwords_controller.rb @@ -55,7 +55,7 @@ protected return unless resource.respond_to?(:need_two_factor_authentication?) && resource.need_two_factor_authentication?(request) - warden.session(resource_class.name.underscore)[TwoFactorAuthentication::NEED_AUTHENTICATION] = true + warden.session(resource_class.name.underscore)[DeviseTwoFactorAuthentication::NEED_AUTHENTICATION] = true end def password_update_flash_message diff --git a/app/controllers/auth/two_factor_authentication_controller.rb b/app/controllers/auth/two_factor_authentication_controller.rb index 81225496d..692c8baac 100644 --- a/app/controllers/auth/two_factor_authentication_controller.rb +++ b/app/controllers/auth/two_factor_authentication_controller.rb @@ -25,7 +25,7 @@ private def after_two_factor_success_for(resource) set_remember_two_factor_cookie(resource) - warden.session(resource_name)[TwoFactorAuthentication::NEED_AUTHENTICATION] = false + warden.session(resource_name)[DeviseTwoFactorAuthentication::NEED_AUTHENTICATION] = false bypass_sign_in(resource, scope: resource_name) resource.update!(second_factor_attempts_count: 0) diff --git a/app/controllers/case_logs_controller.rb b/app/controllers/case_logs_controller.rb index 876e337ff..740dd2003 100644 --- a/app/controllers/case_logs_controller.rb +++ b/app/controllers/case_logs_controller.rb @@ -14,12 +14,13 @@ class CaseLogsController < ApplicationController all_logs = current_user.case_logs unpaginated_filtered_logs = filtered_case_logs(filtered_collection(all_logs, search_term)) - @pagy, @case_logs = pagy(unpaginated_filtered_logs) - @searched = search_term.presence - @total_count = all_logs.size - respond_to do |format| - format.html + format.html do + @pagy, @case_logs = pagy(unpaginated_filtered_logs) + @searched = search_term.presence + @total_count = all_logs.size + end + format.csv do send_data unpaginated_filtered_logs.to_csv, filename: "logs-#{Time.zone.now}.csv" end diff --git a/app/controllers/locations_controller.rb b/app/controllers/locations_controller.rb new file mode 100644 index 000000000..568a2fa60 --- /dev/null +++ b/app/controllers/locations_controller.rb @@ -0,0 +1,67 @@ +class LocationsController < ApplicationController + include Pagy::Backend + before_action :authenticate_user! + before_action :authenticate_scope! + before_action :find_location, except: %i[new create index] + before_action :find_scheme + before_action :authenticate_action! + + def index + @pagy, @locations = pagy(@scheme.locations) + @total_count = @scheme.locations.size + end + + def new + @location = Location.new + end + + def create + @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)) + else + render :new, status: :unprocessable_entity + end + end + + def edit; end + + def update + if @location.update(location_params) + location_params[:add_another_location] == "Yes" ? redirect_to(new_location_path(@location.scheme)) : redirect_to(scheme_check_answers_path(@scheme, anchor: "locations")) + else + render :edit, status: :unprocessable_entity + end + end + +private + + def find_scheme + @scheme = if %w[new create index].include?(action_name) + Scheme.find(params[:id]) + else + @location.scheme + end + end + + def find_location + @location = Location.find(params[:id]) + end + + def authenticate_scope! + head :unauthorized and return unless current_user.data_coordinator? || current_user.support? + end + + def authenticate_action! + if %w[new edit update create index].include?(action_name) && !((current_user.organisation == @scheme.organisation) || current_user.support?) + render_not_found and return + end + end + + def location_params + required_params = params.require(:location).permit(:postcode, :name, :total_units, :type_of_unit, :wheelchair_adaptation, :add_another_location).merge(scheme_id: @scheme.id) + required_params[:postcode] = required_params[:postcode].delete(" ").upcase.encode("ASCII", "UTF-8", invalid: :replace, undef: :replace, replace: "") if required_params[:postcode] + required_params + end +end diff --git a/app/controllers/schemes_controller.rb b/app/controllers/schemes_controller.rb index 2e152b5eb..c32aabfcb 100644 --- a/app/controllers/schemes_controller.rb +++ b/app/controllers/schemes_controller.rb @@ -20,12 +20,6 @@ class SchemesController < ApplicationController @scheme = Scheme.find_by(id: params[:id]) end - def locations - @scheme = Scheme.find_by(id: params[:id]) - @pagy, @locations = pagy(@scheme.locations) - @total_count = @scheme.locations.size - end - def new @scheme = Scheme.new end @@ -100,7 +94,7 @@ private when "secondary-client-group" scheme_support_path(@scheme) when "support" - scheme_check_answers_path(@scheme) + new_location_path when "details" scheme_primary_client_group_path(@scheme) end @@ -113,7 +107,6 @@ private :managing_organisation_id, :scheme_type, :registered_under_care_act, - :total_units, :id, :has_other_client_group, :primary_client_group, diff --git a/app/helpers/tab_nav_helper.rb b/app/helpers/tab_nav_helper.rb index f316d5ab2..3ef211401 100644 --- a/app/helpers/tab_nav_helper.rb +++ b/app/helpers/tab_nav_helper.rb @@ -6,6 +6,11 @@ module TabNavHelper [govuk_link_to(link_text, user), "User #{user.email}"].join("\n") end + def location_cell(location) + link_text = location.postcode + [govuk_link_to(link_text, "/schemes/#{location.scheme.id}/locations/#{location.id}/edit", method: :patch), "Location #{location.name}"].join("\n") + end + def scheme_cell(scheme) link_text = scheme.service_name [govuk_link_to(link_text, scheme), "Scheme #{scheme.primary_client_group}"].join("\n") diff --git a/app/models/form/setup/questions/location_id.rb b/app/models/form/setup/questions/location_id.rb index 4105c98f1..fc6e421e1 100644 --- a/app/models/form/setup/questions/location_id.rb +++ b/app/models/form/setup/questions/location_id.rb @@ -22,8 +22,8 @@ class Form::Setup::Questions::LocationId < ::Form::Question def displayed_answer_options(case_log) return {} unless case_log.scheme - scheme_location_ids = Location.where(scheme_id: case_log.scheme.id).map(&:id).map(&:to_s) - answer_options.select { |k, _v| scheme_location_ids.include?(k) } + scheme_location_ids = case_log.scheme.locations.pluck(:id) + answer_options.select { |k, _v| scheme_location_ids.include?(k.to_i) } end def hidden_in_check_answers?(case_log, _current_user = nil) @@ -35,4 +35,8 @@ private def supported_housing_selected?(case_log) case_log.needstype == 2 end + + def selected_answer_option_is_derived?(_case_log) + false + end end diff --git a/app/models/form/setup/questions/scheme_id.rb b/app/models/form/setup/questions/scheme_id.rb index c4b5e618e..d17cbc806 100644 --- a/app/models/form/setup/questions/scheme_id.rb +++ b/app/models/form/setup/questions/scheme_id.rb @@ -37,4 +37,8 @@ private def supported_housing_selected?(case_log) case_log.needstype == 2 end + + def selected_answer_option_is_derived?(_case_log) + false + end end diff --git a/app/models/location.rb b/app/models/location.rb index d3cc23f82..c8be513eb 100644 --- a/app/models/location.rb +++ b/app/models/location.rb @@ -1,13 +1,28 @@ class Location < ApplicationRecord + include Validations::PropertyValidations + validate :validate_postcode belongs_to :scheme + attr_accessor :add_another_location + WHEELCHAIR_ADAPTATIONS = { - No: 0, Yes: 1, + No: 0, }.freeze enum wheelchair_adaptation: WHEELCHAIR_ADAPTATIONS + TYPE_OF_UNIT = { + "Self-contained flat or bedsit": 1, + "Self-contained flat or bedsit with common facilities": 2, + "Shared flat": 3, + "Shared house or hostel": 4, + "Bungalow": 5, + "Self-contained house": 6, + }.freeze + + enum type_of_unit: TYPE_OF_UNIT + def display_attributes [ { name: "Location code ", value: location_code, suffix: false }, @@ -17,4 +32,13 @@ class Location < ApplicationRecord { name: "Wheelchair adaptation", value: wheelchair_adaptation, suffix: false }, ] end + +private + + def validate_postcode + if postcode.nil? || !postcode&.match(Validations::PropertyValidations::POSTCODE_REGEXP) + error_message = I18n.t("validations.postcode") + errors.add :postcode, error_message + end + end end diff --git a/app/views/locations/edit.html.erb b/app/views/locations/edit.html.erb new file mode 100644 index 000000000..bbcccdef6 --- /dev/null +++ b/app/views/locations/edit.html.erb @@ -0,0 +1,63 @@ +<% content_for :title, "Add a location to this scheme" %> + +<% content_for :before_content do %> + <%= govuk_back_link( + text: "Back", + href: "/schemes/#{@scheme.id}/support", + ) %> +<% end %> + +<%= render partial: "organisations/headings", locals: { main: "Add a location to this scheme", sub: @scheme.service_name } %> + +<%= form_for(@location, method: :patch, url: location_path) do |f| %> +
+
+ <%= f.govuk_error_summary %> + + <%= f.govuk_text_field :postcode, + label: { size: "m" }, + hint: { text: "For example, SW1P 4DF." }, + width: 5 %> + + <%= f.govuk_text_field :name, + label: { text: "Name (optional)", size: "m" }, + hint: { text: "This is how you refer to this location within your organisation" } %> + + <%= f.govuk_number_field :total_units, + label: { text: "Total number of units at this location", size: "m" }, + width: 2, + hint: { text: "A unit can be a bedroom in a shared house or flat, or a house with 4 bedrooms. Do not include bedrooms used for wardens, managers, volunteers or sleep-in staff.s" }, + autofocus: true %> + + <% type_of_units_selection = Location.type_of_units.keys.map { |key, _| OpenStruct.new(id: key, name: key.to_s.humanize) } %> + + <%= f.govuk_collection_radio_buttons :type_of_unit, + type_of_units_selection, + :id, + :name, + legend: { text: "What is this type of scheme?", size: "m" } %> + + <% wheelchair_user_selection = Location.wheelchair_adaptations.keys.map { |key, _| OpenStruct.new(id: key, name: key.to_s.humanize) } %> + + <%= f.govuk_collection_radio_buttons :wheelchair_adaptation, + wheelchair_user_selection, + :id, + :name, + hint: { text: "This includes stairlifts, ramps, level-access showers or grab rails" }, + legend: { text: "Are the majority of units in this location built or adapted to wheelchair-user standards?", size: "m" } %> + + <%= govuk_section_break(visible: true, size: "m") %> + + <% another_location_selection = %w[Yes no].map { |key, _| OpenStruct.new(id: key, name: key.to_s.humanize) } %> + + <%= f.govuk_collection_radio_buttons :add_another_location, + another_location_selection, + :id, + :name, + inline: true, + legend: { text: "Do you want to add another location?", size: "m" } %> + + <%= f.govuk_submit "Save and continue" %> +
+
+<% end %> diff --git a/app/views/locations/index.html.erb b/app/views/locations/index.html.erb new file mode 100644 index 000000000..6d3cc3de3 --- /dev/null +++ b/app/views/locations/index.html.erb @@ -0,0 +1,49 @@ +<% title = @scheme.service_name %> +<% content_for :title, title %> +<% content_for :before_content do %> + <%= govuk_back_link( + text: "Back", + href: "/schemes/#{@scheme.id}", + ) %> +<% end %> +<%= render partial: "organisations/headings", locals: { main: @scheme.service_name, sub: nil } %> +<% location_caption = @scheme.locations.count.eql?(1) ? "1 location" : "#{@scheme.locations.count} locations" %> +<%= render SubNavigationComponent.new(items: scheme_items(request.path, @scheme.id, location_caption)) %> + +
+
+ <%= govuk_table do |table| %> + <%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %> + <%= @scheme.locations.count %> <%= @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", + }) %> + <% end %> + <% end %> + <% @locations.each do |location| %> + <%= table.body do |body| %> + <%= body.row do |row| %> + <% row.cell(text: location.id) %> + <% row.cell(text: location.postcode) %> + <% row.cell(text: location.total_units) %> + <% row.cell(text: simple_format("#{location.type_of_unit}#{location.wheelchair_adaptation == 'Yes' ? "\nWith wheelchair adaptations" : ''}")) %> + <% end %> + <% end %> + <% end %> + <% end %> +
+
+ +<%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "locations" } %> diff --git a/app/views/locations/new.html.erb b/app/views/locations/new.html.erb new file mode 100644 index 000000000..8c8ff23c5 --- /dev/null +++ b/app/views/locations/new.html.erb @@ -0,0 +1,63 @@ +<% content_for :title, "Add a location to this scheme" %> + +<% content_for :before_content do %> + <%= govuk_back_link( + text: "Back", + href: "/schemes/#{@scheme.id}/support", + ) %> +<% end %> + +<%= render partial: "organisations/headings", locals: { main: "Add a location to this scheme", sub: @scheme.service_name } %> + +<%= form_for(@location, method: :post, url: locations_path) do |f| %> +
+
+ <%= f.govuk_error_summary %> + + <%= f.govuk_text_field :postcode, + label: { size: "m" }, + hint: { text: "For example, SW1P 4DF." }, + width: 5 %> + + <%= f.govuk_text_field :name, + label: { text: "Name (optional)", size: "m" }, + hint: { text: "This is how you refer to this location within your organisation" } %> + + <%= f.govuk_number_field :total_units, + label: { text: "Total number of units at this location", size: "m" }, + width: 2, + hint: { text: "A unit can be a bedroom in a shared house or flat, or a house with 4 bedrooms. Do not include bedrooms used for wardens, managers, volunteers or sleep-in staff.s" }, + autofocus: true %> + + <% type_of_units_selection = Location.type_of_units.keys.map { |key, _| OpenStruct.new(id: key, name: key.to_s.humanize) } %> + + <%= f.govuk_collection_radio_buttons :type_of_unit, + type_of_units_selection, + :id, + :name, + legend: { text: "What is this type of scheme?", size: "m" } %> + + <% wheelchair_user_selection = Location.wheelchair_adaptations.keys.map { |key, _| OpenStruct.new(id: key, name: key.to_s.humanize) } %> + + <%= f.govuk_collection_radio_buttons :wheelchair_adaptation, + wheelchair_user_selection, + :id, + :name, + hint: { text: "This includes stairlifts, ramps, level-access showers or grab rails" }, + legend: { text: "Are the majority of units in this location built or adapted to wheelchair-user standards?", size: "m" } %> + + <%= govuk_section_break(visible: true, size: "m") %> + + <% another_location_selection = %w[Yes No].map { |key, _| OpenStruct.new(id: key, name: key.to_s.humanize) } %> + + <%= f.govuk_collection_radio_buttons :add_another_location, + another_location_selection, + :id, + :name, + inline: true, + legend: { text: "Do you want to add another location?", size: "m" } %> + + <%= f.govuk_submit "Save and continue" %> +
+
+<% end %> diff --git a/app/views/schemes/check_answers.html.erb b/app/views/schemes/check_answers.html.erb index 78fff8674..da0db6b15 100644 --- a/app/views/schemes/check_answers.html.erb +++ b/app/views/schemes/check_answers.html.erb @@ -1,65 +1,105 @@ <% content_for :title, "Check your answers before creating this scheme" %> -<%= render partial: "organisations/headings", locals: { main: "Check your changes before updating this scheme", sub: @scheme.service_name } %> +<%= render partial: "organisations/headings", locals: { main: "Check your changes before creating this scheme", sub: @scheme.service_name } %> -<%= 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] == ("Managed 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), - ) %> - <% 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), - ) %> - <% 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), - ) %> - <% 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), - ) %> +<% location_caption = @scheme.locations.count.eql?(1) ? "1 location" : "#{@scheme.locations.count} locations" %> + +
+
+ <%= 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] == ("Managed 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), + ) %> + <% 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), + ) %> + <% 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), + ) %> + <% 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), + ) %> + <% 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), + ) %> + <% end %> <% 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), - ) %> + <% component.tab(label: "Locations") do %> + <%= govuk_table do |table| %> + <%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %> + <%= @scheme.locations.count %> <%= @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", + }) %> + <% 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), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %> + <% row.cell(text: location.total_units) %> + <% row.cell(text: simple_format("#{location.type_of_unit}#{location.wheelchair_adaptation == 'Yes' ? "\nWith wheelchair adaptations" : ''}")) %> + <% end %> + <% end %> + <% end %> <% end %> + <%= govuk_button_link_to "Add a location", new_location_path(id: @scheme.id), secondary: true %> <% end %> <% end %> - <% end %> -<% end %> +
+
<%= govuk_button_link_to "Create scheme", schemes_path(scheme_id: @scheme.id), html: { method: :get } %> diff --git a/app/views/schemes/locations.html.erb b/app/views/schemes/locations.html.erb deleted file mode 100644 index 0b29024c7..000000000 --- a/app/views/schemes/locations.html.erb +++ /dev/null @@ -1,46 +0,0 @@ -<% title = @scheme.service_name %> -<% content_for :title, title %> - -<% content_for :before_content do %> - <%= govuk_back_link( - text: "Back", - href: "/schemes/#{@scheme.id}", - ) %> -<% end %> - -<%= render partial: "organisations/headings", locals: { main: @scheme.service_name, sub: nil } %> - -<%= render SubNavigationComponent.new(items: scheme_items(request.path, @scheme.id, @scheme.locations.count.eql?(1) ? "1 location" : "#{@scheme.locations.count} locations")) %> - -
-
- <% @locations.each do |location| %> -
-
-

- <%= location.name %> -

-
-
-
- <% location.display_attributes.each do |attribute| %> -
-
- <%= attribute[:name] %> -
-
- <%= attribute[:value] %> - <% if attribute[:suffix] %> - <%= attribute[:suffix] %> - <% end %> -
-
- <% end %> -
-
-
- <% end %> -
-
- -<%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "locations" } %> diff --git a/config/routes.rb b/config/routes.rb index 0cbaa82c2..a643f2ab4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -44,7 +44,7 @@ Rails.application.routes.draw do get "check-answers", to: "schemes#check_answers" member do - get "locations", to: "schemes#locations" + resources :locations end end diff --git a/db/migrate/20220630114748_add_name_to_locations.rb b/db/migrate/20220630114748_add_name_to_locations.rb new file mode 100644 index 000000000..8da11bb69 --- /dev/null +++ b/db/migrate/20220630114748_add_name_to_locations.rb @@ -0,0 +1,5 @@ +class AddNameToLocations < ActiveRecord::Migration[7.0] + change_table :locations, bulk: true do |t| + t.integer :total_units + end +end diff --git a/db/migrate/20220630114917_remove_total_units_schemes.rb b/db/migrate/20220630114917_remove_total_units_schemes.rb new file mode 100644 index 000000000..77c022757 --- /dev/null +++ b/db/migrate/20220630114917_remove_total_units_schemes.rb @@ -0,0 +1,5 @@ +class RemoveTotalUnitsSchemes < ActiveRecord::Migration[7.0] + def change + remove_column :schemes, :total_units, :integer + end +end diff --git a/db/migrate/20220630120251_change_location_type_of_unit.rb b/db/migrate/20220630120251_change_location_type_of_unit.rb new file mode 100644 index 000000000..47d1e17b6 --- /dev/null +++ b/db/migrate/20220630120251_change_location_type_of_unit.rb @@ -0,0 +1,6 @@ +class ChangeLocationTypeOfUnit < ActiveRecord::Migration[7.0] + change_table :locations, bulk: true do |t| + t.remove :type_of_unit + t.integer :type_of_unit + end +end diff --git a/db/schema.rb b/db/schema.rb index 31d6d4071..429738353 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -191,9 +191,9 @@ ActiveRecord::Schema[7.0].define(version: 2022_07_05_130923) do t.integer "joint" t.bigint "created_by_id" t.integer "illness_type_0" + t.integer "retirement_value_check" t.integer "tshortfall_known" t.integer "sheltered" - t.integer "retirement_value_check" t.integer "pregnancy_value_check" t.integer "hhtype" t.integer "new_old" @@ -239,7 +239,6 @@ ActiveRecord::Schema[7.0].define(version: 2022_07_05_130923) do create_table "locations", force: :cascade do |t| t.string "location_code" t.string "postcode" - t.string "type_of_unit" t.string "type_of_building" t.integer "wheelchair_adaptation" t.bigint "scheme_id", null: false @@ -247,12 +246,14 @@ ActiveRecord::Schema[7.0].define(version: 2022_07_05_130923) do t.string "county" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.integer "total_units" + t.integer "type_of_unit" t.index ["scheme_id"], name: "index_locations_on_scheme_id" end create_table "logs_exports", force: :cascade do |t| t.datetime "created_at", default: -> { "CURRENT_TIMESTAMP" } - t.datetime "started_at", precision: nil, null: false + t.datetime "started_at", null: false t.integer "base_number", default: 1, null: false t.integer "increment_number", default: 1, null: false t.boolean "empty_export", default: false, null: false @@ -305,7 +306,6 @@ ActiveRecord::Schema[7.0].define(version: 2022_07_05_130923) do t.string "primary_client_group" t.string "secondary_client_group" t.integer "sensitive" - t.integer "total_units" t.integer "scheme_type" t.integer "registered_under_care_act" t.integer "support_type" diff --git a/db/seeds.rb b/db/seeds.rb index 0da986c48..175922921 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -76,7 +76,6 @@ unless Rails.env.test? registered_under_care_act: 0, support_type: 1, scheme_type: 4, - total_units: 5, intended_stay: "M", primary_client_group: "O", secondary_client_group: "H", @@ -90,7 +89,6 @@ unless Rails.env.test? registered_under_care_act: 1, support_type: 1, scheme_type: 5, - total_units: 2, intended_stay: "S", primary_client_group: "D", secondary_client_group: "E", @@ -104,7 +102,6 @@ unless Rails.env.test? registered_under_care_act: 1, support_type: 4, scheme_type: 7, - total_units: 7, intended_stay: "X", primary_client_group: "G", secondary_client_group: "R", @@ -117,7 +114,7 @@ unless Rails.env.test? location_code: "S254-CU193AA", postcode: "CU19 3AA", name: "Rectory Road", - type_of_unit: "Self-contained flat or bedsit", + type_of_unit: 4, type_of_building: "Purpose-built", county: "Mid Sussex", wheelchair_adaptation: 0, @@ -128,7 +125,7 @@ unless Rails.env.test? location_code: "S254-DM250DC", postcode: "DM25 0DC", name: "Smithy Lane", - type_of_unit: "Self-contained flat or bedsit with common facilities", + type_of_unit: 1, type_of_building: "Converted from previous residential or non-residential property", county: "Fife", wheelchair_adaptation: 1, @@ -139,7 +136,7 @@ unless Rails.env.test? location_code: "S254-YX130WP", postcode: "YX13 0WP", name: "Smithy Lane", - type_of_unit: "Shared house or hostel", + type_of_unit: 2, type_of_building: "Converted from previous residential or non-residential property", county: "Rochford", wheelchair_adaptation: 1, diff --git a/docs/service_overview.md b/docs/service_overview.md index 355a89112..c921cd82a 100644 --- a/docs/service_overview.md +++ b/docs/service_overview.md @@ -1,5 +1,5 @@ # Service overview -All lettings and and sales of social housing in England need to be logged with the Department for levelling up, housing and communities (DLUHC). This is done by Local Authorities and Housing Associations, who are the primary users of this service. Data is collected via a form that runs on an annual data collection window basis. Form changes are made annually to add new questions, remove any that are no longer needed, or adjust wording or answer options etc. Each data collection window runs from 1st April to 1st April + an extra 3 months to allow for any late submissions, meaning that between April and July, two collection windows are open simultaneously and logs can be submitted for either. +All lettings and and sales of social housing in England need to be logged with the Department for levelling up, housing and communities (DLUHC). This is done by Local Authorities and Housing Associations, who are the primary users of this service. Data is collected via a form that runs on an annual data collection window basis. Form changes are made annually to add new questions, remove any that are no longer needed, or adjust wording or answer options etc. Each data collection window runs from 1st April to 1st April + an extra 3 months to allow for any late submissions, meaning that between April and June, two collection windows are open simultaneously and logs can be submitted for either. ADD (Analytics & Data Directorate) statisticians are the other primary users of the service. The data collected is transferred to DLUHCs data warehouse (CDS - consolidated data store), via nightly exports to XML which are transferred to S3 and ingested from there. CDS ingests and transforms the data, ultimately storing it in a MS SQL database and exposing it to analysts and statisticians via Amazon Workspaces. diff --git a/spec/factories/location.rb b/spec/factories/location.rb index e547e0bd2..53f8d2cd0 100644 --- a/spec/factories/location.rb +++ b/spec/factories/location.rb @@ -3,7 +3,7 @@ FactoryBot.define do location_code { Faker::Name.initials(number: 10) } postcode { Faker::Address.postcode.delete(" ") } name { Faker::Address.street_name } - type_of_unit { Faker::Lorem.word } + type_of_unit { Faker::Number.within(range: 1..6) } type_of_building { Faker::Lorem.word } wheelchair_adaptation { 0 } county { Faker::Address.state } diff --git a/spec/factories/scheme.rb b/spec/factories/scheme.rb index df314894c..5317d9e1d 100644 --- a/spec/factories/scheme.rb +++ b/spec/factories/scheme.rb @@ -5,7 +5,6 @@ FactoryBot.define do registered_under_care_act { Faker::Number.within(range: 0..1) } support_type { Faker::Number.within(range: 0..6) } scheme_type { 0 } - total_units { Faker::Number.number(digits: 2) } intended_stay { %w[M P S V X].sample } 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 } diff --git a/spec/features/schemes_spec.rb b/spec/features/schemes_spec.rb index da644df4f..4ef8c9cb3 100644 --- a/spec/features/schemes_spec.rb +++ b/spec/features/schemes_spec.rb @@ -208,7 +208,7 @@ RSpec.describe "Schemes scheme Features" do it "shows details of those locations" do locations.each do |location| - expect(page).to have_content(location.location_code) + expect(page).to have_content(location.id) end end end @@ -375,173 +375,286 @@ RSpec.describe "Schemes scheme Features" do click_button "Save and continue" end - it "lets me check my answers" do - expect(page).to have_content "Check your changes before updating this scheme" + it "lets me add location" do + expect(page).to have_content "Add a location to this scheme" end - context "when changing answers" do - it "displays change links" do - assert_selector "a", text: "Change", count: 12 + context "when I press the back button" do + before do + click_link "Back" end - context "when changing details" do - before do - click_link("Change", href: "/schemes/#{scheme.id}/details?check_answers=true", match: :first) - end - - it "allows changing details questions" do - expect(page).to have_current_path("/schemes/#{scheme.id}/details?check_answers=true") + it "lets me select the secondary group" do + expect(page).to have_current_path("/schemes/#{scheme.id}/support") + expect(page).to have_content "What support does this scheme provide?" + end - fill_in "Scheme name", with: "Example" - choose "Direct access hostel" - choose "Yes – registered care home providing nursing care" + context "when I amend support" do + it "returns to the add location page" do click_button "Save and continue" - - expect(page).to have_current_path("/schemes/#{scheme.id}/check-answers") - expect(page).to have_content "Example" - expect(page).to have_content "Yes – registered care home providing nursing care" + expect(page).to have_current_path("/schemes/#{scheme.id}/locations/new") end + end + end - context "when I press the back button" do - before do - click_link "Back" - end + context "when I add location to the scheme" do + before do + fill_in "Postcode", with: "SW1P 4DF" + fill_in "Name (optional)", with: "Some name" + fill_in "Total number of units at this location", with: 1 + choose "Self-contained house" + choose "location-wheelchair-adaptation-no-field" + choose "location-add-another-location-no-field" + click_button "Save and continue" + end - it "lets me select the support answers" do - expect(page).to have_current_path("/schemes/#{scheme.id}/check-answers") - expect(page).to have_content "Check your changes before updating this scheme" - end - end + it "lets me check my answers" do + expect(page).to have_content "Check your changes before creating this scheme" end - context "when changing primary client group" do + context "when I select to view locations" do before do - click_link("Change", href: "/schemes/#{scheme.id}/primary-client-group?check_answers=true") + click_link "Locations" end - it "allows changing primary-client-group question" do - expect(page).to have_current_path("/schemes/#{scheme.id}/primary-client-group?check_answers=true") + it "displays information about locations" do + expect(page).to have_content "Locations" + expect(page).to have_content "#{scheme.locations.count} location" + end - choose "Older people with support needs" + it "displays information about newly created location" do + expect(page).to have_content "SW1P4DF" + expect(page).to have_content "Some name" + expect(page).to have_content "Self-contained house" + end + end + + context "and I select to add another location a scheme" do + before do + click_link "Add a location" + fill_in "Postcode", with: "XX1 1XX" + fill_in "Name (optional)", with: "Other name" + fill_in "Total number of units at this location", with: 2 + choose "Self-contained house" + choose "location-wheelchair-adaptation-no-field" + choose "location-add-another-location-no-field" click_button "Save and continue" + end - expect(page).to have_current_path("/schemes/#{scheme.id}/check-answers") - expect(page).to have_content "Older people with support needs" + it "lets me check my answers" do + expect(page).to have_content "Check your changes before creating this scheme" end - context "when I press the back button" do + context "when I select to view locations" do before do - click_link "Back" + click_link "Locations" end - it "lets me select the support answers" do - expect(page).to have_current_path("/schemes/#{scheme.id}/check-answers") - expect(page).to have_content "Check your changes before updating this scheme" + it "displays information about another location" do + expect(page).to have_content "Locations" + expect(page).to have_content "#{scheme.locations.count} location" + end + + it "displays information about newly created location" do + expect(page).to have_content "XX11XX" + expect(page).to have_content "Other name" + expect(page).to have_content "Self-contained house" + end + + context "when changing location details" do + before do + click_link "XX11XX" + fill_in "Postcode", with: "ZZ1 1ZZ" + click_button "Save and continue" + end + + it "displays changed location" do + expect(page).to have_content "Locations" + expect(page).to have_content "#{scheme.locations.count} location" + expect(page).to have_content "ZZ11ZZ" + end end end end - context "when changing confirm secondary group answer" do - before do - click_link("Change", href: "/schemes/#{scheme.id}/confirm-secondary-client-group?check_answers=true") + context "when changing answers" do + it "displays change links" do + assert_selector "a", text: "Change", count: 12 end - it "allows changing confirm-secondary-client-group question to yes" do - expect(page).to have_current_path("/schemes/#{scheme.id}/confirm-secondary-client-group?check_answers=true") + context "when changing details" do + before do + click_link("Change", href: "/schemes/#{scheme.id}/details?check_answers=true", match: :first) + end - choose "Yes" - click_button "Save and continue" + it "allows changing details questions" do + expect(page).to have_current_path("/schemes/#{scheme.id}/details?check_answers=true") - expect(page).to have_current_path("/schemes/#{scheme.id}/secondary-client-group?check_answers=true") + fill_in "Scheme name", with: "Example" + choose "Direct access hostel" + choose "Yes – registered care home providing nursing care" + click_button "Save and continue" - choose "People at risk of domestic violence" - click_button "Save and continue" + expect(page).to have_current_path("/schemes/#{scheme.id}/check-answers") + expect(page).to have_content "Example" + expect(page).to have_content "Yes – registered care home providing nursing care" + end - expect(page).to have_current_path("/schemes/#{scheme.id}/check-answers") - expect(page).to have_content "People at risk of domestic violence" + context "when I press the back button" do + before do + click_link "Back" + end + + it "lets me select the support answers" 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 + end end - context "when I press the back button" do + context "when changing primary client group" do before do - click_link "Back" + click_link("Change", href: "/schemes/#{scheme.id}/primary-client-group?check_answers=true") end - it "lets me select the support answers" do + it "allows changing primary-client-group question" do + expect(page).to have_current_path("/schemes/#{scheme.id}/primary-client-group?check_answers=true") + + choose "Older people with support needs" + click_button "Save and continue" + expect(page).to have_current_path("/schemes/#{scheme.id}/check-answers") - expect(page).to have_content "Check your changes before updating this scheme" + expect(page).to have_content "Older people with support needs" end - end - end - context "when allows changing confirm-secondary-client-group question to no" do - before do - click_link("Change", href: "/schemes/#{scheme.id}/confirm-secondary-client-group?check_answers=true") + context "when I press the back button" do + before do + click_link "Back" + end + + it "lets me select the support answers" 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 + end end - it "allows changing confirm-secondary-client-group question to no" do - expect(page).to have_current_path("/schemes/#{scheme.id}/confirm-secondary-client-group?check_answers=true") + context "when changing confirm secondary group answer" do + before do + click_link("Change", href: "/schemes/#{scheme.id}/confirm-secondary-client-group?check_answers=true") + end - choose "No" - click_button "Save and continue" + it "allows changing confirm-secondary-client-group question to yes" do + expect(page).to have_current_path("/schemes/#{scheme.id}/confirm-secondary-client-group?check_answers=true") - expect(page).to have_current_path("/schemes/#{scheme.id}/check-answers") - expect(page).not_to have_content "Secondary client group" - end - end + choose "Yes" + click_button "Save and continue" - context "when changing secondary-client-group question" do - before do - click_link("Change", href: "/schemes/#{scheme.id}/secondary-client-group?check_answers=true") - end + expect(page).to have_current_path("/schemes/#{scheme.id}/secondary-client-group?check_answers=true") - it "allows changing secondary-client-group question" do - expect(page).to have_current_path("/schemes/#{scheme.id}/secondary-client-group?check_answers=true") + choose "People at risk of domestic violence" + click_button "Save and continue" - choose "People at risk of domestic violence" - click_button "Save and continue" + expect(page).to have_current_path("/schemes/#{scheme.id}/check-answers") + expect(page).to have_content "People at risk of domestic violence" + end + + context "when I press the back button" do + before do + click_link "Back" + end - expect(page).to have_current_path("/schemes/#{scheme.id}/check-answers") - expect(page).to have_content "People at risk of domestic violence" + it "lets me select the support answers" 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 + end end - context "when I press the back button" do + context "when allows changing confirm-secondary-client-group question to no" do before do - click_link "Back" + click_link("Change", href: "/schemes/#{scheme.id}/confirm-secondary-client-group?check_answers=true") end - it "lets me select the support answers" do + it "allows changing confirm-secondary-client-group question to no" do + expect(page).to have_current_path("/schemes/#{scheme.id}/confirm-secondary-client-group?check_answers=true") + + choose "No" + click_button "Save and continue" + expect(page).to have_current_path("/schemes/#{scheme.id}/check-answers") - expect(page).to have_content "Check your changes before updating this scheme" + expect(page).not_to have_content "Secondary client group" end end - end - context "when changing support questions" do - before do - click_link("Change", href: "/schemes/#{scheme.id}/support?check_answers=true", match: :first) - end + context "when changing secondary-client-group question" do + before do + click_link("Change", href: "/schemes/#{scheme.id}/secondary-client-group?check_answers=true") + end - it "allows changing support questions" do - expect(page).to have_current_path("/schemes/#{scheme.id}/support?check_answers=true") + it "allows changing secondary-client-group question" do + expect(page).to have_current_path("/schemes/#{scheme.id}/secondary-client-group?check_answers=true") - choose "Resettlement support" - choose "Medium stay" - click_button "Save and continue" + choose "People at risk of domestic violence" + click_button "Save and continue" + + expect(page).to have_current_path("/schemes/#{scheme.id}/check-answers") + expect(page).to have_content "People at risk of domestic violence" + end - expect(page).to have_current_path("/schemes/#{scheme.id}/check-answers") - expect(page).to have_content "Resettlement support" - expect(page).to have_content "Medium stay" + context "when I press the back button" do + before do + click_link "Back" + end + + it "lets me select the support answers" 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 + end end - context "when I press the back button" do + context "when changing support questions" do before do - click_link "Back" + click_link("Change", href: "/schemes/#{scheme.id}/support?check_answers=true", match: :first) end - it "lets me select the support answers" do - expect(page).to have_current_path("/schemes/#{scheme.id}/check-answers") - expect(page).to have_content "Check your changes before updating this scheme" + it "allows changing support questions" do + expect(page).to have_current_path("/schemes/#{scheme.id}/support?check_answers=true") + + choose "Resettlement support" + choose "Medium stay" + click_button "Save and continue" + + expect(page).to have_current_path("/schemes/#{scheme.id}/locations/new") + expect(page).to have_content "Add a location to this scheme" end + + context "when I press the back button" do + before do + click_link "Back" + end + + it "lets me select the support answers" 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 + end + end + end + + context "and I select to create a scheme" do + before do + click_link "Create scheme" + end + + it "adds scheme to the list of schemes" do + expect(page).to have_content "Supported housing schemes" + expect(page).to have_content scheme.id_to_display + expect(page).to have_content scheme.service_name + expect(page).to have_content scheme.organisation.name + expect(page).to have_content scheme.stock_owning_organisation.name + expect(page).to have_content "#{scheme.organisation.name} has been created." end end end diff --git a/spec/fixtures/complete_case_log.json b/spec/fixtures/complete_case_log.json index 9cb1fc62f..227da864b 100644 --- a/spec/fixtures/complete_case_log.json +++ b/spec/fixtures/complete_case_log.json @@ -1,138 +1,186 @@ { "case_log": { - "tenancycode": "T657", - "age1": 35, - "sex1": "F", - "ethnic": 0, - "national": 0, - "prevten": 6, - "armedforces": 1, - "armed_forces_partner": "", - "ecstat1": 1, - "hhmemb": 8, - "relat2": "P", - "age2": 32, - "sex2": "M", - "ecstat2": 6, - "relat3": "C", - "age3": 12, - "sex3": "M", - "ecstat3": 9, - "relat4": "C", - "age4": 12, - "sex4": "F", - "ecstat4": 9, - "relat5": "C", - "age5": 10, - "sex5": "X", - "ecstat5": 9, - "relat6": "C", - "age6": 5, - "sex6": "R", - "ecstat6": 9, - "age7": 5, - "sex7": "R", - "ecstat7": 9, - "relat8": "C", - "age8": 2, - "sex8": "R", - "ecstat8": 9, - "homeless": 2, - "reason": 1, - "underoccupation_benefitcap": 0, - "leftreg": 1, - "reservist": 0, - "illness": 1, - "preg_occ": 1, - "startdate": "12/12/2021", - "startertenancy": 0, - "tenancylength": 5, - "tenancy": 1, - "landlord": 1, - "previous_la_known": 1, - "la": "Barnet", - "postcode_full": "NW1 5TY", - "property_relet": 0, - "rsnvac": 14, - "property_reference": "P9876", - "unittype_gn": 7, - "property_building_type": "dummy", - "beds": 3, - "voiddate": "10/10/2020", - "majorrepairs": 1, - "mrcdate": "11/11/2020", - "offered": 2, - "wchair": 1, - "net_income_known": 1, - "earnings": 150, - "incfreq": 1, - "benefits": 1, - "hb": 1, - "period": 2, - "brent": 200, - "scharge": 50, - "pscharge": 40, - "supcharg": 35, - "tcharge": 325, - "outstanding_amount": 1, - "layear": 2, - "lawaitlist": 1, - "prevloc": "E07000105", - "ppostcode_full": "SE2 6RT", - "reasonpref": 1, - "cbl": 0, - "chr": 1, - "cap": 0, - "hbrentshortfall": 1, - "tshortfall": 12, - "reasonother": null, - "housingneeds_a": 1, - "housingneeds_b": 0, - "housingneeds_c": 0, - "housingneeds_f": 0, - "housingneeds_g": 0, - "housingneeds_h": 0, - "accessibility_requirements_prefer_not_to_say": 0, - "illness_type_1": 0, - "illness_type_2": 1, - "illness_type_3": 0, - "illness_type_4": 0, - "illness_type_8": 0, - "illness_type_5": 0, - "illness_type_6": 0, - "illness_type_7": 0, - "illness_type_9": 0, - "illness_type_10": 0, - "condition_effects_prefer_not_to_say": 1, - "rp_homeless": 0, - "rp_insan_unsat": 0, - "rp_medwel": 0, - "rp_hardship": 0, - "rp_dontknow": 0, - "discarded_at": "05/05/2020", - "net_income_value_check": 0, - "property_owner_organisation": "", - "property_manager_organisation": "", - "rent_type": 0, - "intermediate_rent_product_name": "", - "needstype": 1, - "sale_completion_date": "01/01/2020", - "purchaser_code": "", - "propcode": "123", - "postcode": "a1", - "postcod2": "w3", - "first_time_property_let_as_social_housing": 0, - "unitletas": 1, - "builtype": 0, - "property_wheelchair_accessible": 1, - "void_or_renewal_date": "05/05/2020", - "renewal": 0, - "new_build_handover_date": "01/01/2019", - "has_benefits": 1, - "household_charge": 0, - "is_carehome": 0, - "sheltered": 0, - "declaration": 1, - "referral": 1 + "tenancycode":"T1245", + "age1":34, + "sex1":"M", + "ethnic":1, + "national":1, + "prevten":3, + "ecstat1":1, + "hhmemb":3, + "age2":29, + "sex2":"F", + "ecstat2":2, + "age3":11, + "sex3":"R", + "ecstat3":9, + "age4":null, + "sex4":null, + "ecstat4":null, + "age5":null, + "sex5":null, + "ecstat5":null, + "age6":null, + "sex6":null, + "ecstat6":null, + "age7":null, + "sex7":null, + "ecstat7":null, + "age8":null, + "sex8":null, + "ecstat8":null, + "homeless":1, + "underoccupation_benefitcap":2, + "leftreg":null, + "reservist":null, + "illness":2, + "preg_occ":2, + "startertenancy":2, + "tenancylength":null, + "tenancy":2, + "ppostcode_full":"NW18TR", + "rsnvac":5, + "unittype_gn":7, + "beds":2, + "offered":0, + "wchair":1, + "earnings":190, + "incfreq":1, + "benefits":3, + "period":3, + "layear":7, + "waityear":2, + "postcode_full":"NW18EE", + "reasonpref":2, + "cbl":1, + "chr":0, + "cap":0, + "reasonother":"", + "housingneeds_a":0, + "housingneeds_b":0, + "housingneeds_c":1, + "housingneeds_f":0, + "housingneeds_g":0, + "housingneeds_h":0, + "illness_type_1":null, + "illness_type_2":null, + "illness_type_3":null, + "illness_type_4":null, + "illness_type_8":null, + "illness_type_5":null, + "illness_type_6":null, + "illness_type_7":null, + "illness_type_9":null, + "illness_type_10":null, + "rp_homeless":null, + "rp_insan_unsat":null, + "rp_medwel":null, + "rp_hardship":null, + "rp_dontknow":null, + "tenancyother":"", + "net_income_value_check":null, + "property_owner_organisation":null, + "property_manager_organisation":null, + "sale_or_letting":null, + "irproduct_other":"", + "purchaser_code":null, + "reason":42, + "propcode":"PT562", + "majorrepairs":1, + "la":"E09000007", + "prevloc":"E09000007", + "hb":9, + "hbrentshortfall":null, + "property_relet":null, + "mrcdate":"2021-05-07T00:00:00.000+01:00", + "incref":null, + "sale_completion_date":null, + "startdate":"2021-06-06T00:00:00.000+01:00", + "armedforces":2, + "first_time_property_let_as_social_housing":0, + "unitletas":1, + "builtype":1, + "voiddate":"2021-05-05T00:00:00.000+01:00", + "owning_organisation_id":1, + "managing_organisation_id":1, + "renttype":2, + "needstype":1, + "lettype":7, + "postcode_known":1, + "is_la_inferred":true, + "totchild":1, + "totelder":0, + "totadult":2, + "net_income_known":0, + "nocharge":0, + "is_carehome":null, + "household_charge":null, + "referral":2, + "brent":"350.0", + "scharge":"11.0", + "pscharge":"11.0", + "supcharg":"0.0", + "tcharge":"372.0", + "tshortfall":null, + "chcharge":null, + "declaration":1, + "ppcodenk":1, + "previous_la_known":null, + "is_previous_la_inferred":true, + "age1_known":0, + "age2_known":0, + "age3_known":0, + "age4_known":null, + "age5_known":null, + "age6_known":null, + "age7_known":null, + "age8_known":null, + "ethnic_group":0, + "ethnic_other":null, + "letting_allocation_unknown":0, + "details_known_2":0, + "details_known_3":0, + "details_known_4":null, + "details_known_5":null, + "details_known_6":null, + "details_known_7":null, + "details_known_8":null, + "rent_type":1, + "has_benefits":0, + "renewal":0, + "wrent":"87.5", + "wscharge":"2.75", + "wpschrge":"2.75", + "wsupchrg":"0.0", + "wtcharge":"93.0", + "wtshortfall":null, + "refused":1, + "housingneeds":2, + "wchchrg":null, + "newprop":2, + "relat2":"P", + "relat3":"C", + "relat4":null, + "relat5":null, + "relat6":null, + "relat7":null, + "relat8":null, + "rent_value_check":null, + "old_form_id":null, + "lar":null, + "irproduct":null, + "old_id":null, + "joint":null, + "created_by_id":2, + "illness_type_0":null, + "retirement_value_check":null, + "tshortfall_known":null, + "sheltered":null, + "pregnancy_value_check":null, + "hhtype":6, + "new_old":1, + "vacdays":30, + "scheme_id":null, + "location_id":null } } diff --git a/spec/helpers/tab_nav_helper_spec.rb b/spec/helpers/tab_nav_helper_spec.rb index f1e483fd1..3403ce490 100644 --- a/spec/helpers/tab_nav_helper_spec.rb +++ b/spec/helpers/tab_nav_helper_spec.rb @@ -3,7 +3,8 @@ require "rails_helper" RSpec.describe TabNavHelper do let(:organisation) { FactoryBot.create(:organisation) } let(:user) { FactoryBot.build(:user, organisation:) } - let(:scheme) { FactoryBot.build(:scheme, service_name: "Some name") } + let(:scheme) { FactoryBot.create(:scheme, service_name: "Some name") } + let(:location) { FactoryBot.create(:location, scheme:) } describe "#user_cell" do it "returns user link and email separated by a newline character" do @@ -19,9 +20,16 @@ RSpec.describe TabNavHelper do end end + describe "#location_cell" do + it "returns the location link to the postcode with optional name" do + expected_html = "#{location.postcode}\nLocation #{location.name}" + expect(location_cell(location)).to match(expected_html) + end + end + describe "#scheme_cell" do it "returns the scheme link service name and primary user group separated by a newline character" do - expected_html = "#{scheme.service_name}\nScheme #{scheme.primary_client_group}" + expected_html = "#{scheme.service_name}\nScheme #{scheme.primary_client_group}" expect(scheme_cell(scheme)).to match(expected_html) end end diff --git a/spec/models/location_spec.rb b/spec/models/location_spec.rb new file mode 100644 index 000000000..a886c6034 --- /dev/null +++ b/spec/models/location_spec.rb @@ -0,0 +1,27 @@ +require "rails_helper" + +RSpec.describe Location, type: :model do + describe "#new" do + let(:location) { FactoryBot.build(:location) } + + it "belongs to an organisation" do + expect(location.scheme).to be_a(Scheme) + end + end + + describe "#validate_postcode" do + let(:location) { FactoryBot.build(:location) } + + it "does not add an error if postcode is valid" do + location.postcode = "M1 1AE" + location.save! + expect(location.errors).to be_empty + end + + it "does add an error when the postcode is invalid" do + location.postcode = "invalid" + expect { location.save! } + .to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Postcode Enter a postcode in the correct format, for example AA1 1AA") + end + end +end diff --git a/spec/requests/locations_controller_spec.rb b/spec/requests/locations_controller_spec.rb new file mode 100644 index 000000000..36025a306 --- /dev/null +++ b/spec/requests/locations_controller_spec.rb @@ -0,0 +1,784 @@ +require "rails_helper" + +RSpec.describe LocationsController, type: :request do + let(:page) { Capybara::Node::Simple.new(response.body) } + let(:user) { FactoryBot.create(:user, :support) } + let!(:scheme) { FactoryBot.create(:scheme, organisation: user.organisation) } + + describe "#new" do + context "when not signed in" do + it "redirects to the sign in page" do + get "/schemes/1/locations/new" + expect(response).to redirect_to("/account/sign-in") + end + end + + context "when signed in as a data provider" do + let(:user) { FactoryBot.create(:user) } + + before do + sign_in user + get "/schemes/1/locations/new" + end + + it "returns 401 unauthorized" do + request + expect(response).to have_http_status(:unauthorized) + end + end + + context "when signed in as a data coordinator" do + let(:user) { FactoryBot.create(:user, :data_coordinator) } + let!(:scheme) { FactoryBot.create(:scheme, organisation: user.organisation) } + + before do + sign_in user + get "/schemes/#{scheme.id}/locations/new" + end + + it "returns a template for a new location" do + expect(response).to have_http_status(:ok) + expect(page).to have_content("Add a location to this scheme") + end + + context "when trying to new location to a scheme that belongs to another organisation" do + let(:another_scheme) { FactoryBot.create(:scheme) } + + it "displays the new page with an error message" do + get "/schemes/#{another_scheme.id}/locations/new" + expect(response).to have_http_status(:not_found) + end + end + end + + context "when signed in as a support user" do + before do + allow(user).to receive(:need_two_factor_authentication?).and_return(false) + sign_in user + get "/schemes/#{scheme.id}/locations/new" + end + + it "returns a template for a new location" do + expect(response).to have_http_status(:ok) + expect(page).to have_content("Add a location to this scheme") + end + end + end + + describe "#create" do + context "when not signed in" do + it "redirects to the sign in page" do + post "/schemes/1/locations" + expect(response).to redirect_to("/account/sign-in") + end + end + + context "when signed in as a data provider" do + let(:user) { FactoryBot.create(:user) } + + before do + sign_in user + post "/schemes/1/locations" + end + + it "returns 401 unauthorized" do + request + expect(response).to have_http_status(:unauthorized) + end + end + + context "when signed in as a data coordinator" do + let(:user) { FactoryBot.create(:user, :data_coordinator) } + let!(:scheme) { FactoryBot.create(:scheme, organisation: user.organisation) } + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "No", postcode: "ZZ1 1ZZ" } } } + + before do + sign_in user + post "/schemes/#{scheme.id}/locations", params: params + end + + it "creates a new location for scheme with valid params and redirects to correct page" do + expect { post "/schemes/#{scheme.id}/locations", params: }.to change(Location, :count).by(1) + follow_redirect! + expect(response).to have_http_status(:ok) + expect(page).to have_content("Check your answers before creating this scheme") + end + + it "creates a new location for scheme with valid params" do + expect(Location.last.scheme.organisation_id).to eq(user.organisation_id) + expect(Location.last.name).to eq("Test") + expect(Location.last.postcode).to eq("ZZ11ZZ") + expect(Location.last.total_units).to eq(5) + expect(Location.last.type_of_unit).to eq("Bungalow") + expect(Location.last.wheelchair_adaptation).to eq("No") + end + + context "when postcode is submitted with lower case" do + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "No", postcode: "zz1 1zz" } } } + + it "creates a new location for scheme with postcode " do + expect(Location.last.postcode).to eq("ZZ11ZZ") + end + end + + context "when trying to add location to a scheme that belongs to another organisation" do + let(:another_scheme) { FactoryBot.create(:scheme) } + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "No", postcode: "ZZ1 1ZZ" } } } + + it "displays the new page with an error message" do + post "/schemes/#{another_scheme.id}/locations", params: params + expect(response).to have_http_status(:not_found) + end + end + + context "when required postcode param is missing" do + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "No" } } } + + it "displays the new page with an error message" do + expect(response).to have_http_status(:unprocessable_entity) + expect(page).to have_content(I18n.t("validations.postcode")) + end + end + + context "when do you want to add another location is selected as yes" do + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "Yes", postcode: "ZZ1 1ZZ" } } } + + it "creates a new location for scheme with valid params and redirects to correct page" do + expect { post "/schemes/#{scheme.id}/locations", params: }.to change(Location, :count).by(1) + follow_redirect! + expect(response).to have_http_status(:ok) + expect(page).to have_content("Add a location to this scheme") + end + + it "creates a new location for scheme with valid params" do + expect(Location.last.scheme.organisation_id).to eq(user.organisation_id) + expect(Location.last.name).to eq("Test") + expect(Location.last.total_units).to eq(5) + expect(Location.last.type_of_unit).to eq("Bungalow") + expect(Location.last.wheelchair_adaptation).to eq("No") + end + end + + context "when do you want to add another location is selected as no" do + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "No", postcode: "ZZ1 1ZZ" } } } + + it "creates a new location for scheme with valid params and redirects to correct page" do + expect { post "/schemes/#{scheme.id}/locations", params: }.to change(Location, :count).by(1) + follow_redirect! + expect(response).to have_http_status(:ok) + expect(page).to have_content("Check your changes before creating this scheme") + end + + it "creates a new location for scheme with valid params" do + expect(Location.last.scheme.organisation_id).to eq(user.organisation_id) + expect(Location.last.name).to eq("Test") + expect(Location.last.total_units).to eq(5) + expect(Location.last.type_of_unit).to eq("Bungalow") + expect(Location.last.wheelchair_adaptation).to eq("No") + end + end + + context "when do you want to add another location is not selected" do + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", postcode: "ZZ1 1ZZ" } } } + + it "creates a new location for scheme with valid params and redirects to correct page" do + expect { post "/schemes/#{scheme.id}/locations", params: }.to change(Location, :count).by(1) + follow_redirect! + expect(response).to have_http_status(:ok) + expect(page).to have_content("Check your changes before creating this scheme") + end + + it "creates a new location for scheme with valid params" do + expect(Location.last.scheme.organisation_id).to eq(user.organisation_id) + expect(Location.last.name).to eq("Test") + expect(Location.last.total_units).to eq(5) + expect(Location.last.type_of_unit).to eq("Bungalow") + expect(Location.last.wheelchair_adaptation).to eq("No") + end + end + end + + context "when signed in as a support user" do + let(:user) { FactoryBot.create(:user, :support) } + let!(:scheme) { FactoryBot.create(:scheme) } + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "No", postcode: "ZZ1 1ZZ" } } } + + before do + allow(user).to receive(:need_two_factor_authentication?).and_return(false) + sign_in user + post "/schemes/#{scheme.id}/locations", params: params + end + + it "creates a new location for scheme with valid params and redirects to correct page" do + expect { post "/schemes/#{scheme.id}/locations", params: }.to change(Location, :count).by(1) + follow_redirect! + expect(response).to have_http_status(:ok) + expect(page).to have_content("Check your answers before creating this scheme") + end + + it "creates a new location for scheme with valid params" do + expect(Location.last.name).to eq("Test") + expect(Location.last.postcode).to eq("ZZ11ZZ") + expect(Location.last.total_units).to eq(5) + expect(Location.last.type_of_unit).to eq("Bungalow") + expect(Location.last.wheelchair_adaptation).to eq("No") + end + + context "when postcode is submitted with lower case" do + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "No", postcode: "zz1 1zz" } } } + + it "creates a new location for scheme with postcode " do + expect(Location.last.postcode).to eq("ZZ11ZZ") + end + end + + context "when required postcode param is missing" do + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "No" } } } + + it "displays the new page with an error message" do + post "/schemes/#{scheme.id}/locations", params: params + expect(response).to have_http_status(:unprocessable_entity) + expect(page).to have_content(I18n.t("validations.postcode")) + end + end + + context "when do you want to add another location is selected as yes" do + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "Yes", postcode: "ZZ1 1ZZ" } } } + + it "creates a new location for scheme with valid params and redirects to correct page" do + expect { post "/schemes/#{scheme.id}/locations", params: }.to change(Location, :count).by(1) + follow_redirect! + expect(response).to have_http_status(:ok) + expect(page).to have_content("Add a location to this scheme") + end + + it "creates a new location for scheme with valid params" do + expect(Location.last.name).to eq("Test") + expect(Location.last.total_units).to eq(5) + expect(Location.last.type_of_unit).to eq("Bungalow") + expect(Location.last.wheelchair_adaptation).to eq("No") + end + end + + context "when do you want to add another location is selected as no" do + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "No", postcode: "ZZ1 1ZZ" } } } + + it "creates a new location for scheme with valid params and redirects to correct page" do + expect { post "/schemes/#{scheme.id}/locations", params: }.to change(Location, :count).by(1) + follow_redirect! + expect(response).to have_http_status(:ok) + expect(page).to have_content("Check your changes before creating this scheme") + end + + it "creates a new location for scheme with valid params" do + expect(Location.last.name).to eq("Test") + expect(Location.last.total_units).to eq(5) + expect(Location.last.type_of_unit).to eq("Bungalow") + expect(Location.last.wheelchair_adaptation).to eq("No") + end + end + + context "when do you want to add another location is not selected" do + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", postcode: "ZZ1 1ZZ" } } } + + it "creates a new location for scheme with valid params and redirects to correct page" do + expect { post "/schemes/#{scheme.id}/locations", params: }.to change(Location, :count).by(1) + follow_redirect! + expect(response).to have_http_status(:ok) + expect(page).to have_content("Check your changes before creating this scheme") + end + + it "creates a new location for scheme with valid params" do + expect(Location.last.name).to eq("Test") + expect(Location.last.total_units).to eq(5) + expect(Location.last.type_of_unit).to eq("Bungalow") + expect(Location.last.wheelchair_adaptation).to eq("No") + end + end + end + end + + describe "#edit" do + context "when not signed in" do + it "redirects to the sign in page" do + get "/schemes/1/locations/1/edit" + expect(response).to redirect_to("/account/sign-in") + end + end + + context "when signed in as a data provider" do + let(:user) { FactoryBot.create(:user) } + + before do + sign_in user + get "/schemes/1/locations/1/edit" + end + + it "returns 401 unauthorized" do + request + expect(response).to have_http_status(:unauthorized) + end + end + + context "when signed in as a data coordinator" do + let(:user) { FactoryBot.create(:user, :data_coordinator) } + let!(:scheme) { FactoryBot.create(:scheme, organisation: user.organisation) } + let!(:location) { FactoryBot.create(:location, scheme:) } + + before do + sign_in user + get "/schemes/#{scheme.id}/locations/#{location.id}/edit" + end + + it "returns a template for a new location" do + expect(response).to have_http_status(:ok) + expect(page).to have_content("Add a location to this scheme") + end + + context "when trying to new location to a scheme that belongs to another organisation" do + let(:another_scheme) { FactoryBot.create(:scheme) } + let(:another_location) { FactoryBot.create(:location, scheme: another_scheme) } + + it "displays the new page with an error message" do + get "/schemes/#{another_scheme.id}/locations/#{another_location.id}/edit" + expect(response).to have_http_status(:not_found) + end + end + end + + context "when signed in as a support user" do + let(:user) { FactoryBot.create(:user, :support) } + let!(:scheme) { FactoryBot.create(:scheme, organisation: user.organisation) } + let!(:location) { FactoryBot.create(:location, scheme:) } + + before do + allow(user).to receive(:need_two_factor_authentication?).and_return(false) + sign_in user + get "/schemes/#{scheme.id}/locations/#{location.id}/edit" + end + + it "returns a template for a new location" do + expect(response).to have_http_status(:ok) + expect(page).to have_content("Add a location to this scheme") + end + end + end + + describe "#update" do + context "when not signed in" do + it "redirects to the sign in page" do + patch "/schemes/1/locations/1" + expect(response).to redirect_to("/account/sign-in") + end + end + + context "when signed in as a data provider" do + let(:user) { FactoryBot.create(:user) } + + before do + sign_in user + patch "/schemes/1/locations/1" + end + + it "returns 401 unauthorized" do + request + expect(response).to have_http_status(:unauthorized) + end + end + + context "when signed in as a data coordinator" do + let(:user) { FactoryBot.create(:user, :data_coordinator) } + let!(:scheme) { FactoryBot.create(:scheme, organisation: user.organisation) } + let!(:location) { FactoryBot.create(:location, scheme:) } + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "No", postcode: "ZZ1 1ZZ" } } } + + before do + sign_in user + patch "/schemes/#{scheme.id}/locations/#{location.id}", params: params + end + + it "updates existing location for scheme with valid params and redirects to correct page" do + follow_redirect! + expect(response).to have_http_status(:ok) + expect(page).to have_content("Check your answers before creating this scheme") + end + + it "updates existing location for scheme with valid params" do + expect(Location.last.scheme.organisation_id).to eq(user.organisation_id) + expect(Location.last.name).to eq("Test") + expect(Location.last.postcode).to eq("ZZ11ZZ") + expect(Location.last.total_units).to eq(5) + expect(Location.last.type_of_unit).to eq("Bungalow") + expect(Location.last.wheelchair_adaptation).to eq("No") + end + + context "when postcode is submitted with lower case" do + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "No", postcode: "zz1 1zz" } } } + + it "updates existing location for scheme with postcode " do + expect(Location.last.postcode).to eq("ZZ11ZZ") + end + end + + context "when trying to update location for a scheme that belongs to another organisation" do + let(:another_scheme) { FactoryBot.create(:scheme) } + let(:another_location) { FactoryBot.create(:location) } + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "No", postcode: "ZZ1 1ZZ" } } } + + it "displays the new page with an error message" do + patch "/schemes/#{another_scheme.id}/locations/#{another_location.id}", params: params + expect(response).to have_http_status(:not_found) + end + end + + context "when required postcode param is invalid" do + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "No", postcode: "invalid" } } } + + it "displays the new page with an error message" do + expect(response).to have_http_status(:unprocessable_entity) + expect(page).to have_content(I18n.t("validations.postcode")) + end + end + + context "when do you want to add another location is selected as yes" do + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "Yes", postcode: "ZZ1 1ZZ" } } } + + it "updates existing location for scheme with valid params and redirects to correct page" do + follow_redirect! + expect(response).to have_http_status(:ok) + expect(page).to have_content("Add a location to this scheme") + end + + it "updates existing location for scheme with valid params" do + expect(Location.last.scheme.organisation_id).to eq(user.organisation_id) + expect(Location.last.name).to eq("Test") + expect(Location.last.total_units).to eq(5) + expect(Location.last.type_of_unit).to eq("Bungalow") + expect(Location.last.wheelchair_adaptation).to eq("No") + end + end + + context "when do you want to add another location is selected as no" do + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "No", postcode: "ZZ1 1ZZ" } } } + + it "updates existing location for scheme with valid params and redirects to correct page" do + follow_redirect! + expect(response).to have_http_status(:ok) + expect(page).to have_content("Check your changes before creating this scheme") + end + + it "updates existing location for scheme with valid params" do + expect(Location.last.scheme.organisation_id).to eq(user.organisation_id) + expect(Location.last.name).to eq("Test") + expect(Location.last.total_units).to eq(5) + expect(Location.last.type_of_unit).to eq("Bungalow") + expect(Location.last.wheelchair_adaptation).to eq("No") + end + end + + context "when do you want to add another location is not selected" do + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", postcode: "ZZ1 1ZZ" } } } + + it "updates existing location for scheme with valid params and redirects to correct page" do + follow_redirect! + expect(response).to have_http_status(:ok) + expect(page).to have_content("Check your changes before creating this scheme") + end + + it "updates existing location for scheme with valid params" do + expect(Location.last.scheme.organisation_id).to eq(user.organisation_id) + expect(Location.last.name).to eq("Test") + expect(Location.last.total_units).to eq(5) + expect(Location.last.type_of_unit).to eq("Bungalow") + expect(Location.last.wheelchair_adaptation).to eq("No") + end + end + end + + context "when signed in as a support user" do + let(:user) { FactoryBot.create(:user, :data_coordinator) } + let!(:scheme) { FactoryBot.create(:scheme, organisation: user.organisation) } + let!(:location) { FactoryBot.create(:location, scheme:) } + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "No", postcode: "ZZ1 1ZZ" } } } + + 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 + end + + it "updates a location for scheme with valid params and redirects to correct page" do + follow_redirect! + expect(response).to have_http_status(:ok) + expect(page).to have_content("Check your answers before creating this scheme") + end + + it "updates existing location for scheme with valid params" do + expect(Location.last.name).to eq("Test") + expect(Location.last.postcode).to eq("ZZ11ZZ") + expect(Location.last.total_units).to eq(5) + expect(Location.last.type_of_unit).to eq("Bungalow") + expect(Location.last.wheelchair_adaptation).to eq("No") + end + + context "when postcode is submitted with lower case" do + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "No", postcode: "zz1 1zz" } } } + + it "updates a location for scheme with postcode " do + expect(Location.last.postcode).to eq("ZZ11ZZ") + end + end + + context "when required postcode param is missing" do + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "No", postcode: "invalid" } } } + + it "displays the new page with an error message" do + expect(response).to have_http_status(:unprocessable_entity) + expect(page).to have_content(I18n.t("validations.postcode")) + end + end + + context "when do you want to add another location is selected as yes" do + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "Yes", postcode: "ZZ1 1ZZ" } } } + + it "updates location for scheme with valid params and redirects to correct page" do + follow_redirect! + expect(response).to have_http_status(:ok) + expect(page).to have_content("Add a location to this scheme") + end + + it "updates existing location for scheme with valid params" do + expect(Location.last.name).to eq("Test") + expect(Location.last.total_units).to eq(5) + expect(Location.last.type_of_unit).to eq("Bungalow") + expect(Location.last.wheelchair_adaptation).to eq("No") + end + end + + context "when do you want to add another location is selected as no" do + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "No", postcode: "ZZ1 1ZZ" } } } + + it "updates a location for scheme with valid params and redirects to correct page" do + follow_redirect! + expect(response).to have_http_status(:ok) + expect(page).to have_content("Check your changes before creating this scheme") + end + + it "updates existing location for scheme with valid params" do + expect(Location.last.name).to eq("Test") + expect(Location.last.total_units).to eq(5) + expect(Location.last.type_of_unit).to eq("Bungalow") + expect(Location.last.wheelchair_adaptation).to eq("No") + end + end + + context "when do you want to add another location is not selected" do + let(:params) { { location: { name: "Test", total_units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", postcode: "ZZ1 1ZZ" } } } + + it "updates a location for scheme with valid params and redirects to correct page" do + follow_redirect! + expect(response).to have_http_status(:ok) + expect(page).to have_content("Check your changes before creating this scheme") + end + + it "updates a location for scheme with valid params" do + expect(Location.last.name).to eq("Test") + expect(Location.last.total_units).to eq(5) + expect(Location.last.type_of_unit).to eq("Bungalow") + expect(Location.last.wheelchair_adaptation).to eq("No") + end + end + end + end + + describe "#index" do + context "when not signed in" do + it "redirects to the sign in page" do + get "/schemes/#{scheme.id}/locations" + expect(response).to redirect_to("/account/sign-in") + end + end + + context "when signed in as a data provider user" do + let(:user) { FactoryBot.create(:user) } + + before do + sign_in user + get "/schemes/#{scheme.id}/locations" + end + + it "returns 401 unauthorized" do + request + expect(response).to have_http_status(:unauthorized) + end + end + + context "when signed in as a data coordinator user" do + let(:user) { FactoryBot.create(:user, :data_coordinator) } + let!(:scheme) { FactoryBot.create(:scheme, organisation: user.organisation) } + let!(:locations) { FactoryBot.create_list(:location, 3, scheme:) } + + before do + sign_in user + get "/schemes/#{scheme.id}/locations" + end + + context "when coordinator attempts to see scheme belonging to a different organisation" do + let!(:another_scheme) { FactoryBot.create(:scheme) } + + before do + FactoryBot.create(:location, scheme:) + end + + it "returns 404 not found" do + get "/schemes/#{another_scheme.id}/locations" + expect(response).to have_http_status(:not_found) + end + end + + it "shows scheme" do + locations.each do |location| + 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.wheelchair_adaptation) + end + end + + it "has page heading" do + expect(page).to have_content(scheme.service_name) + end + + it "has correct title" do + expected_title = CGI.escapeHTML("#{scheme.service_name} - Submit social housing lettings and sales data (CORE) - GOV.UK") + expect(page).to have_title(expected_title) + end + + context "when paginating over 20 results" do + let!(:locations) { FactoryBot.create_list(:location, 25, scheme:) } + + context "when on the first page" do + before do + get "/schemes/#{scheme.id}/locations" + end + + it "shows which schemes are being shown on the current page" do + expect(CGI.unescape_html(response.body)).to match("Showing 1 to 20 of #{locations.count} locations") + end + + it "has correct page 1 of 2 title" do + expected_title = CGI.escapeHTML("#{scheme.service_name} (page 1 of 2) - Submit social housing lettings and sales data (CORE) - GOV.UK") + expect(page).to have_title(expected_title) + end + + it "has pagination links" do + expect(page).not_to have_content("Previous") + expect(page).not_to have_link("Previous") + expect(page).to have_content("Next") + expect(page).to have_link("Next") + end + end + + context "when on the second page" do + before do + get "/schemes/#{scheme.id}/locations?page=2" + end + + it "shows which schemes are being shown on the current page" do + expect(CGI.unescape_html(response.body)).to match("Showing 21 to 25 of #{locations.count} locations") + end + + it "has correct page 2 of 2 title" do + expected_title = CGI.escapeHTML("#{scheme.service_name} (page 2 of 2) - Submit social housing lettings and sales data (CORE) - GOV.UK") + expect(page).to have_title(expected_title) + end + + it "has pagination links" do + expect(page).to have_content("Previous") + expect(page).to have_link("Previous") + expect(page).not_to have_content("Next") + expect(page).not_to have_link("Next") + end + end + end + end + + context "when signed in as a support user" do + let(:user) { FactoryBot.create(:user, :support) } + let!(:scheme) { FactoryBot.create(:scheme) } + let!(:locations) { FactoryBot.create_list(:location, 3, scheme:) } + + before do + allow(user).to receive(:need_two_factor_authentication?).and_return(false) + sign_in user + get "/schemes/#{scheme.id}/locations" + end + + it "shows scheme" do + locations.each do |location| + 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.wheelchair_adaptation) + end + end + + it "has page heading" do + expect(page).to have_content(scheme.service_name) + end + + it "has correct title" do + expected_title = CGI.escapeHTML("#{scheme.service_name} - Submit social housing lettings and sales data (CORE) - GOV.UK") + expect(page).to have_title(expected_title) + end + + context "when paginating over 20 results" do + let!(:locations) { FactoryBot.create_list(:location, 25, scheme:) } + + context "when on the first page" do + before do + get "/schemes/#{scheme.id}/locations" + end + + it "shows which schemes are being shown on the current page" do + expect(CGI.unescape_html(response.body)).to match("Showing 1 to 20 of #{locations.count} locations") + end + + it "has correct page 1 of 2 title" do + expected_title = CGI.escapeHTML("#{scheme.service_name} (page 1 of 2) - Submit social housing lettings and sales data (CORE) - GOV.UK") + expect(page).to have_title(expected_title) + end + + it "has pagination links" do + expect(page).not_to have_content("Previous") + expect(page).not_to have_link("Previous") + expect(page).to have_content("Next") + expect(page).to have_link("Next") + end + end + + context "when on the second page" do + before do + get "/schemes/#{scheme.id}/locations?page=2" + end + + it "shows which schemes are being shown on the current page" do + expect(CGI.unescape_html(response.body)).to match("Showing 21 to 25 of #{locations.count} locations") + end + + it "has correct page 1 of 2 title" do + expected_title = CGI.escapeHTML("#{scheme.service_name} (page 2 of 2) - Submit social housing lettings and sales data (CORE) - GOV.UK") + expect(page).to have_title(expected_title) + end + + it "has pagination links" do + expect(page).to have_content("Previous") + expect(page).to have_link("Previous") + expect(page).not_to have_content("Next") + expect(page).not_to have_link("Next") + end + end + end + end + end +end diff --git a/spec/requests/schemes_controller_spec.rb b/spec/requests/schemes_controller_spec.rb index 782123144..a49749964 100644 --- a/spec/requests/schemes_controller_spec.rb +++ b/spec/requests/schemes_controller_spec.rb @@ -536,7 +536,7 @@ RSpec.describe SchemesController, type: :request do context "when signed in as a data coordinator" do let(:user) { FactoryBot.create(:user, :data_coordinator) } - let(:params) { { scheme: { service_name: "testy", sensitive: "1", scheme_type: "Foyer", registered_under_care_act: "No", total_units: "1" } } } + let(:params) { { scheme: { service_name: "testy", sensitive: "1", scheme_type: "Foyer", registered_under_care_act: "No" } } } before do sign_in user @@ -569,7 +569,7 @@ RSpec.describe SchemesController, type: :request do context "when signed in as a support user" do let(:organisation) { FactoryBot.create(:organisation) } let(:user) { FactoryBot.create(:user, :support) } - let(:params) { { scheme: { service_name: "testy", sensitive: "1", scheme_type: "Foyer", registered_under_care_act: "No", total_units: "1", owning_organisation_id: organisation.id } } } + let(:params) { { scheme: { service_name: "testy", sensitive: "1", scheme_type: "Foyer", registered_under_care_act: "No", owning_organisation_id: organisation.id } } } before do allow(user).to receive(:need_two_factor_authentication?).and_return(false) @@ -600,7 +600,7 @@ RSpec.describe SchemesController, type: :request do end context "when required organisation id param is missing" do - let(:params) { { "scheme" => { "service_name" => "qweqwer", "sensitive" => "Yes", "owning_organisation_id" => "", "scheme_type" => "Foyer", "registered_under_care_act" => "Yes – part registered as a care home", "total_units" => "1" } } } + let(:params) { { "scheme" => { "service_name" => "qweqwer", "sensitive" => "Yes", "owning_organisation_id" => "", "scheme_type" => "Foyer", "registered_under_care_act" => "Yes – part registered as a care home" } } } it "displays the new page with an error message" do post "/schemes", params: params @@ -662,7 +662,7 @@ RSpec.describe SchemesController, type: :request do it "renders check answers page after successful update" do follow_redirect! expect(response).to have_http_status(:ok) - expect(page).to have_content("Check your changes before updating this scheme") + expect(page).to have_content("Check your changes before creating this scheme") end it "updates a scheme with valid params" do @@ -707,7 +707,7 @@ RSpec.describe SchemesController, type: :request do it "renders check answers page after successful update" do follow_redirect! expect(response).to have_http_status(:ok) - expect(page).to have_content("Check your changes before updating this scheme") + expect(page).to have_content("Check your changes before creating this scheme") end it "updates a scheme with valid params" do @@ -737,7 +737,7 @@ RSpec.describe SchemesController, type: :request do it "renders check answers page after successful update" do follow_redirect! expect(response).to have_http_status(:ok) - expect(page).to have_content("Check your changes before updating this scheme") + expect(page).to have_content("Check your changes before creating this scheme") end it "updates a scheme with valid params" do @@ -750,10 +750,10 @@ RSpec.describe SchemesController, type: :request do context "when updating support" do let(:params) { { scheme: { intended_stay: "Medium stay", support_type: "Resettlement support", page: "support" } } } - it "renders confirm secondary group after successful update" do + it "renders add location to this scheme successful update" do follow_redirect! expect(response).to have_http_status(:ok) - expect(page).to have_content("Check your changes before updating this scheme") + expect(page).to have_content("Add a location to this scheme") end it "updates a scheme with valid params" do @@ -768,7 +768,7 @@ RSpec.describe SchemesController, type: :request do it "renders check answers page after successful update" do follow_redirect! expect(response).to have_http_status(:ok) - expect(page).to have_content("Check your changes before updating this scheme") + expect(page).to have_content("Check your changes before creating this scheme") end it "updates a scheme with valid params" do @@ -780,7 +780,7 @@ RSpec.describe SchemesController, type: :request do end context "when updating details" do - let(:params) { { scheme: { service_name: "testy", sensitive: "1", scheme_type: "Foyer", registered_under_care_act: "No", total_units: "1", page: "details" } } } + let(:params) { { scheme: { service_name: "testy", sensitive: "1", scheme_type: "Foyer", registered_under_care_act: "No", page: "details" } } } it "renders confirm secondary group after successful update" do follow_redirect! @@ -794,16 +794,15 @@ 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.total_units).to eq(1) end context "when updating from check answers page" do - let(:params) { { scheme: { service_name: "testy", sensitive: "1", scheme_type: "Foyer", registered_under_care_act: "No", total_units: "1", page: "details", check_answers: "true" } } } + let(:params) { { scheme: { service_name: "testy", sensitive: "1", scheme_type: "Foyer", registered_under_care_act: "No", page: "details", check_answers: "true" } } } it "renders check answers page after successful update" do follow_redirect! expect(response).to have_http_status(:ok) - expect(page).to have_content("Check your changes before updating this scheme") + expect(page).to have_content("Check your changes before creating this scheme") end it "updates a scheme with valid params" do @@ -812,7 +811,6 @@ 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.total_units).to eq(1) end end end @@ -848,7 +846,7 @@ RSpec.describe SchemesController, type: :request do it "renders check answers page after successful update" do follow_redirect! expect(response).to have_http_status(:ok) - expect(page).to have_content("Check your changes before updating this scheme") + expect(page).to have_content("Check your changes before creating this scheme") end it "updates a scheme with valid params" do @@ -893,7 +891,7 @@ RSpec.describe SchemesController, type: :request do it "renders check answers page after successful update" do follow_redirect! expect(response).to have_http_status(:ok) - expect(page).to have_content("Check your changes before updating this scheme") + expect(page).to have_content("Check your changes before creating this scheme") end it "updates a scheme with valid params" do @@ -923,7 +921,7 @@ RSpec.describe SchemesController, type: :request do it "renders check answers page after successful update" do follow_redirect! expect(response).to have_http_status(:ok) - expect(page).to have_content("Check your changes before updating this scheme") + expect(page).to have_content("Check your changes before creating this scheme") end it "updates a scheme with valid params" do @@ -939,7 +937,7 @@ RSpec.describe SchemesController, type: :request do it "renders confirm secondary group after successful update" do follow_redirect! expect(response).to have_http_status(:ok) - expect(page).to have_content("Check your changes before updating this scheme") + expect(page).to have_content("Add a location to this scheme") end it "updates a scheme with valid params" do @@ -954,7 +952,7 @@ RSpec.describe SchemesController, type: :request do it "renders check answers page after successful update" do follow_redirect! expect(response).to have_http_status(:ok) - expect(page).to have_content("Check your changes before updating this scheme") + expect(page).to have_content("Check your changes before creating this scheme") end it "updates a scheme with valid params" do @@ -972,7 +970,6 @@ RSpec.describe SchemesController, type: :request do sensitive: "1", scheme_type: "Foyer", registered_under_care_act: "No", - total_units: "1", page: "details", owning_organisation_id: another_organisation.id, managing_organisation_id: another_organisation.id } } @@ -990,18 +987,17 @@ 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.total_units).to eq(1) expect(scheme_to_update.reload.owning_organisation_id).to eq(another_organisation.id) expect(scheme_to_update.reload.managing_organisation_id).to eq(another_organisation.id) end context "when updating from check answers page" do - let(:params) { { scheme: { service_name: "testy", sensitive: "1", scheme_type: "Foyer", registered_under_care_act: "No", total_units: "1", page: "details", check_answers: "true" } } } + let(:params) { { scheme: { service_name: "testy", sensitive: "1", scheme_type: "Foyer", registered_under_care_act: "No", page: "details", check_answers: "true" } } } it "renders check answers page after successful update" do follow_redirect! expect(response).to have_http_status(:ok) - expect(page).to have_content("Check your changes before updating this scheme") + expect(page).to have_content("Check your changes before creating this scheme") end it "updates a scheme with valid params" do @@ -1010,7 +1006,6 @@ 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.total_units).to eq(1) end end end @@ -1315,7 +1310,7 @@ RSpec.describe SchemesController, type: :request do it "returns a template for a support" do expect(response).to have_http_status(:ok) - expect(page).to have_content("Check your changes before updating this scheme") + expect(page).to have_content("Check your changes before creating this scheme") end context "when attempting to access check-answers scheme page for another organisation" do @@ -1342,7 +1337,7 @@ RSpec.describe SchemesController, type: :request do it "returns a template for a support" do expect(response).to have_http_status(:ok) - expect(page).to have_content("Check your changes before updating this scheme") + expect(page).to have_content("Check your changes before creating this scheme") end end end