diff --git a/app/controllers/form_controller.rb b/app/controllers/form_controller.rb index df924665a..aed35e7a1 100644 --- a/app/controllers/form_controller.rb +++ b/app/controllers/form_controller.rb @@ -7,11 +7,16 @@ class FormController < ApplicationController if @case_log @page = @case_log.form.get_page(params[:case_log][:page]) responses_for_page = responses_for_page(@page) - if @case_log.update(responses_for_page) - session[:errors] = nil + mandatory_questions_with_no_response = mandatory_questions_with_no_response(responses_for_page) + + if mandatory_questions_with_no_response.empty? && @case_log.update(responses_for_page) + session[:errors] = session[:fields] = nil redirect_to(successful_redirect_path) else redirect_path = "case_log_#{@page.id}_path" + mandatory_questions_with_no_response.map do |question| + @case_log.errors.add question.id.to_sym, question.unanswered_error_message + end session[:errors] = @case_log.errors.to_json Rails.logger.info "User triggered validation(s) on: #{@case_log.errors.map(&:attribute).join(', ')}" redirect_to(send(redirect_path, @case_log)) @@ -48,6 +53,7 @@ class FormController < ApplicationController messages.each { |message| @case_log.errors.add field.to_sym, message } end end + session["fields"].each { |field, value| @case_log[field] = value } if session["fields"] @subsection = @case_log.form.subsection_for_page(page) @page = @case_log.form.get_page(page.id) if @page.routed_to?(@case_log, current_user) @@ -120,4 +126,31 @@ private redirect_path = @case_log.form.next_page_redirect_path(@page, @case_log, current_user) send(redirect_path, @case_log) end + + def mandatory_questions_with_no_response(responses_for_page) + session["fields"] = {} + calc_questions = @page.questions.map(&:result_field) + @page.questions.select do |question| + next if calc_questions.include?(question.id) + + question_is_required?(question) && question_missing_response?(responses_for_page, question) + end + end + + def question_is_required?(question) + CaseLog::OPTIONAL_FIELDS.exclude?(question.id) && + @page.subsection.applicable_questions(@case_log).map(&:id).include?(question.id) + end + + def question_missing_response?(responses_for_page, question) + if %w[checkbox validation_override].include?(question.type) + question.answer_options.keys.reject { |x| x.match(/divider/) }.all? do |option| + session["fields"][option] = @case_log[option] = params["case_log"][question.id].include?(option) ? 1 : 0 + params["case_log"][question.id].exclude?(option) + end + else + session["fields"][question.id] = @case_log[question.id] = responses_for_page[question.id] + responses_for_page[question.id].nil? || responses_for_page[question.id].blank? + end + end end diff --git a/app/controllers/locations_controller.rb b/app/controllers/locations_controller.rb index b482b81f0..a2624a790 100644 --- a/app/controllers/locations_controller.rb +++ b/app/controllers/locations_controller.rb @@ -69,7 +69,7 @@ private end def location_params - required_params = params.require(:location).permit(:postcode, :name, :units, :type_of_unit, :wheelchair_adaptation, :add_another_location).merge(scheme_id: @scheme.id) + required_params = params.require(:location).permit(:postcode, :name, :units, :type_of_unit, :wheelchair_adaptation, :add_another_location, :startdate).merge(scheme_id: @scheme.id) required_params[:postcode] = PostcodeService.clean(required_params[:postcode]) if required_params[:postcode] required_params end diff --git a/app/frontend/controllers/accessible_autocomplete_controller.js b/app/frontend/controllers/accessible_autocomplete_controller.js index 0b17e8aa8..812cd37d3 100644 --- a/app/frontend/controllers/accessible_autocomplete_controller.js +++ b/app/frontend/controllers/accessible_autocomplete_controller.js @@ -15,7 +15,7 @@ export default class extends Controller { accessibleAutocomplete.enhanceSelectElement({ defaultValue: '', selectElement: selectEl, - minLength: 2, + minLength: 1, source: (query, populateResults) => { if (/\S/.test(query)) { populateResults(sort(query, options)) diff --git a/app/frontend/modules/search.js b/app/frontend/modules/search.js index d9d233ab1..5e22c0cf5 100644 --- a/app/frontend/modules/search.js +++ b/app/frontend/modules/search.js @@ -12,9 +12,13 @@ const clean = (text) => .toLowerCase() const cleanseOption = (option) => { + const synonyms = (option.synonyms || []).map(clean) + option.clean = { name: clean(option.name), nameWithoutStopWords: removeStopWords(option.name), + synonyms, + synonymsWithoutStopWords: synonyms.map(removeStopWords), boost: option.boost || 1 } @@ -41,19 +45,34 @@ const startsWith = (word, query) => word.search(startsWithRegExp(query)) === 0 const wordsStartsWithQuery = (word, regExps) => regExps.every((regExp) => word.search(regExp) >= 0) -const calculateWeight = ({ name, nameWithoutStopWords }, query) => { +const anyMatch = (words, query, evaluatorFunc) => words.some((word) => evaluatorFunc(word, query)) +const synonymsExactMatch = (synonyms, query) => anyMatch(synonyms, query, exactMatch) +const synonymsStartsWith = (synonyms, query) => anyMatch(synonyms, query, startsWith) + +const wordInSynonymStartsWithQuery = (synonyms, startsWithQueryWordsRegexes) => + anyMatch(synonyms, startsWithQueryWordsRegexes, wordsStartsWithQuery) + +const calculateWeight = ({ name, synonyms, nameWithoutStopWords, synonymsWithoutStopWords }, query) => { const queryWithoutStopWords = removeStopWords(query) if (exactMatch(name, query)) return 100 if (exactMatch(nameWithoutStopWords, queryWithoutStopWords)) return 95 + if (synonymsExactMatch(synonyms, query)) return 75 + if (synonymsExactMatch(synonymsWithoutStopWords, queryWithoutStopWords)) return 70 + if (startsWith(name, query)) return 60 if (startsWith(nameWithoutStopWords, queryWithoutStopWords)) return 55 + + if (synonymsStartsWith(synonyms, query)) return 50 + if (synonymsStartsWith(synonyms, queryWithoutStopWords)) return 40 + const startsWithRegExps = queryWithoutStopWords .split(/\s+/) .map(startsWithRegExp) if (wordsStartsWithQuery(nameWithoutStopWords, startsWithRegExps)) return 25 + if (wordInSynonymStartsWithQuery(synonymsWithoutStopWords, startsWithRegExps)) return 10 return 0 } @@ -91,8 +110,8 @@ export const sort = (query, options) => { export const suggestion = (value, options) => { const option = options.find((o) => o.name === value) if (option) { - const html = `${value}` - return html + const html = option.append ? `${value} ${option.append}` : `${value}` + return option.hint ? `${html}
${option.hint}
` : html } else { return 'No results found' } @@ -101,6 +120,9 @@ export const suggestion = (value, options) => { export const enhanceOption = (option) => { return { name: option.label, + synonyms: (option.getAttribute('data-synonyms') ? option.getAttribute('data-synonyms').split('|') : []), + append: option.getAttribute('data-append'), + hint: option.getAttribute('data-hint'), boost: parseFloat(option.getAttribute('data-boost')) || 1 } } diff --git a/app/models/case_log.rb b/app/models/case_log.rb index c2465105c..a8250056d 100644 --- a/app/models/case_log.rb +++ b/app/models/case_log.rb @@ -8,7 +8,6 @@ class CaseLogValidator < ActiveModel::Validator include Validations::TenancyValidations include Validations::DateValidations include Validations::LocalAuthorityValidations - include Validations::SubmissionValidations def validate(record) validation_methods = public_methods.select { |method| method.starts_with?("validate_") } @@ -103,6 +102,22 @@ class CaseLog < ApplicationRecord attribute_names - AUTOGENERATED_FIELDS end + def la + if location + location.location_code + else + super + end + end + + def postcode_full + if location + location.postcode + else + super + end + end + def completed? status == "completed" end @@ -466,7 +481,7 @@ class CaseLog < ApplicationRecord private - PIO = Postcodes::IO.new + PIO = PostcodeService.new def update_status! self.status = if all_fields_completed? && errors.empty? @@ -588,20 +603,7 @@ private end def get_inferred_la(postcode) - # Avoid network calls when postcode is invalid - return unless postcode.match(POSTCODE_REGEXP) - - postcode_lookup = nil - begin - # URI encoding only supports ASCII characters - ascii_postcode = PostcodeService.clean(postcode) - Timeout.timeout(5) { postcode_lookup = PIO.lookup(ascii_postcode) } - rescue Timeout::Error - Rails.logger.warn("Postcodes.io lookup timed out") - end - if postcode_lookup && postcode_lookup.info.present? - postcode_lookup.codes["admin_district"] - end + PIO.infer_la(postcode) end def get_has_benefits diff --git a/app/models/derived_variables/case_log_variables.rb b/app/models/derived_variables/case_log_variables.rb index 8a8d8429a..312c5a719 100644 --- a/app/models/derived_variables/case_log_variables.rb +++ b/app/models/derived_variables/case_log_variables.rb @@ -73,10 +73,8 @@ module DerivedVariables::CaseLogVariables self.location = scheme.locations.first end if location - self.la = location.county - self.postcode_full = location.postcode - wheelchair_adaptation_map = { 1 => 1, 0 => 2 } - self.wchair = wheelchair_adaptation_map[location.wheelchair_adaptation.to_i] + # TODO: Remove and replace with mobility type + self.wchair = location.wheelchair_adaptation_before_type_cast end if is_renewal? self.voiddate = startdate diff --git a/app/models/form/question.rb b/app/models/form/question.rb index 9dd7488cb..17d047cdb 100644 --- a/app/models/form/question.rb +++ b/app/models/form/question.rb @@ -136,7 +136,11 @@ class Form::Question labels = answer_options[value.to_s] labels["value"] if labels when "select" - answer_options[value.to_s] + if answer_options[value.to_s].respond_to?(:service_name) + answer_options[value.to_s].service_name + else + answer_options[value.to_s] + end else value.to_s end @@ -173,6 +177,16 @@ class Form::Question type == "radio" && RADIO_REFUSED_VALUE[id.to_sym]&.include?(value) end + def display_label + check_answer_label || header || id.humanize + end + + def unanswered_error_message + return I18n.t("validations.declaration.missing") if id == "declaration" + + I18n.t("validations.not_answered", question: display_label.downcase) + end + def suffix_label(case_log) return "" unless suffix return suffix if suffix.is_a?(String) @@ -191,6 +205,24 @@ class Form::Question label end + def answer_option_synonyms(resource) + return unless resource.respond_to?(:synonyms) + + resource.synonyms + end + + def answer_option_append(resource) + return unless resource.respond_to?(:appended_text) + + resource.appended_text + end + + def answer_option_hint(resource) + return unless resource.respond_to?(:hint) + + resource.hint + end + private def selected_answer_option_is_derived?(case_log) diff --git a/app/models/form/setup/questions/location_id.rb b/app/models/form/setup/questions/location_id.rb index fc6e421e1..9e6fb6038 100644 --- a/app/models/form/setup/questions/location_id.rb +++ b/app/models/form/setup/questions/location_id.rb @@ -13,7 +13,7 @@ class Form::Setup::Questions::LocationId < ::Form::Question answer_opts = {} return answer_opts unless ActiveRecord::Base.connected? - Location.select(:id, :postcode, :name).each_with_object(answer_opts) do |location, hsh| + Location.select(:id, :postcode, :name).where("startdate <= ? or startdate IS NULL", Time.zone.today).each_with_object(answer_opts) do |location, hsh| hsh[location.id.to_s] = { "value" => location.postcode, "hint" => location.name } hsh end diff --git a/app/models/form/setup/questions/scheme_id.rb b/app/models/form/setup/questions/scheme_id.rb index d17cbc806..e758b8444 100644 --- a/app/models/form/setup/questions/scheme_id.rb +++ b/app/models/form/setup/questions/scheme_id.rb @@ -13,8 +13,8 @@ class Form::Setup::Questions::SchemeId < ::Form::Question answer_opts = {} return answer_opts unless ActiveRecord::Base.connected? - Scheme.select(:id, :service_name).each_with_object(answer_opts) do |scheme, hsh| - hsh[scheme.id.to_s] = scheme.service_name + Scheme.select(:id, :service_name, :primary_client_group, :secondary_client_group).each_with_object(answer_opts) do |scheme, hsh| + hsh[scheme.id.to_s] = scheme hsh end end @@ -22,7 +22,7 @@ class Form::Setup::Questions::SchemeId < ::Form::Question def displayed_answer_options(case_log) return {} unless case_log.created_by - user_org_scheme_ids = Scheme.select(:id).where(owning_organisation_id: case_log.created_by.organisation_id).map(&:id) + user_org_scheme_ids = Scheme.select(:id).where(owning_organisation_id: case_log.created_by.organisation_id).joins(:locations).merge(Location.where("startdate <= ? or startdate IS NULL", Time.zone.today)).map(&:id) answer_options.select do |k, _v| user_org_scheme_ids.include?(k.to_i) end diff --git a/app/models/location.rb b/app/models/location.rb index bb2453625..a44640889 100644 --- a/app/models/location.rb +++ b/app/models/location.rb @@ -2,15 +2,27 @@ class Location < ApplicationRecord validate :validate_postcode belongs_to :scheme + before_save :infer_la!, if: :postcode_changed? + attr_accessor :add_another_location WHEELCHAIR_ADAPTATIONS = { Yes: 1, - No: 0, + No: 2, }.freeze enum wheelchair_adaptation: WHEELCHAIR_ADAPTATIONS + MOBILITY_TYPE = { + "Property fitted with equipment and adaptations (if not designed to above standards)": "A", + "Property designed to accessible general standard": "M", + "None": "N", + "Property designed to wheelchair user standard": "W", + "Missing": "X", + }.freeze + + enum mobility_type: MOBILITY_TYPE + TYPE_OF_UNIT = { "Self-contained flat or bedsit": 1, "Self-contained flat or bedsit with common facilities": 2, @@ -34,10 +46,16 @@ class Location < ApplicationRecord private + PIO = PostcodeService.new + def validate_postcode if postcode.nil? || !postcode&.match(POSTCODE_REGEXP) error_message = I18n.t("validations.postcode") errors.add :postcode, error_message end end + + def infer_la! + self.location_code = PIO.infer_la(postcode) + end end diff --git a/app/models/scheme.rb b/app/models/scheme.rb index bafa7d395..bdd9671ed 100644 --- a/app/models/scheme.rb +++ b/app/models/scheme.rb @@ -142,4 +142,16 @@ class Scheme < ApplicationRecord { name: "Intended length of stay", value: intended_stay }, ] end + + def synonyms + locations.map(&:postcode).join(",") + end + + def appended_text + "(#{locations.count { |location| location.startdate.blank? || location.startdate <= Time.zone.today }} locations)" + end + + def hint + [primary_client_group, secondary_client_group].filter(&:present?).join(", ") + end end diff --git a/app/models/validations/submission_validations.rb b/app/models/validations/submission_validations.rb deleted file mode 100644 index c44799892..000000000 --- a/app/models/validations/submission_validations.rb +++ /dev/null @@ -1,10 +0,0 @@ -module Validations::SubmissionValidations - # Validations methods need to be called 'validate_' to run on model save - # or 'validate_' to run on submit as well - - def validate_declaration(record) - if record.declaration&.zero? - record.errors.add :declaration, I18n.t("validations.declaration.missing") - end - end -end diff --git a/app/services/imports/case_logs_import_service.rb b/app/services/imports/case_logs_import_service.rb index 2a3f129df..942a88bb2 100644 --- a/app/services/imports/case_logs_import_service.rb +++ b/app/services/imports/case_logs_import_service.rb @@ -15,6 +15,12 @@ module Imports private + FORM_NAME_INDEX = { + start_year: 0, + rent_type: 2, + needs_type: 3, + }.freeze + GN_SH = { general_needs: 1, supported_housing: 2, @@ -175,15 +181,20 @@ module Imports attributes["first_time_property_let_as_social_housing"] = first_time_let(attributes["rsnvac"]) attributes["declaration"] = declaration(xml_doc) - # Set charges to 0 if others are partially populated - unless attributes["brent"].nil? && - attributes["scharge"].nil? && - attributes["pscharge"].nil? && - attributes["supcharg"].nil? - attributes["brent"] ||= BigDecimal("0.0") - attributes["scharge"] ||= BigDecimal("0.0") - attributes["pscharge"] ||= BigDecimal("0.0") - attributes["supcharg"] ||= BigDecimal("0.0") + set_partial_charges_to_zero(attributes) + + # Supported Housing fields + if attributes["needstype"] == GN_SH[:supported_housing] + old_visible_id = safe_string_as_integer(xml_doc, "_1cschemecode") + location = Location.find_by(old_visible_id:) + scheme = location.scheme + # Set the scheme via location, because the scheme old visible ID is not unique + attributes["location_id"] = location.id + attributes["scheme_id"] = scheme.id + attributes["sheltered"] = unsafe_string_as_integer(xml_doc, "Q1e") + attributes["chcharge"] = safe_string_as_decimal(xml_doc, "Q18b") + attributes["household_charge"] = household_charge(xml_doc) + attributes["is_carehome"] = is_carehome(scheme) end # Handles confidential schemes @@ -306,7 +317,7 @@ module Imports end def needs_type(xml_doc) - gn_sh = get_form_name_component(xml_doc, -1) + gn_sh = get_form_name_component(xml_doc, FORM_NAME_INDEX[:needs_type]) case gn_sh when "GN" GN_SH[:general_needs] @@ -319,7 +330,7 @@ module Imports # This does not match renttype (CDS) which is derived by case log logic def rent_type(xml_doc, lar, irproduct) - sr_ar_ir = get_form_name_component(xml_doc, -2) + sr_ar_ir = get_form_name_component(xml_doc, FORM_NAME_INDEX[:rent_type]) case sr_ar_ir when "SR" @@ -590,5 +601,40 @@ module Imports end end end + + def household_charge(xml_doc) + value = string_or_nil(xml_doc, "Q18c") + start_year = Integer(get_form_name_component(xml_doc, FORM_NAME_INDEX[:start_year])) + + if start_year <= 2021 + # Yes means that there are no charges (2021 or earlier) + value && value.include?("Yes") ? 1 : 0 + else + # Yes means that there are charges (2022 onwards) + value && value.include?("Yes") ? 0 : 1 + end + end + + def set_partial_charges_to_zero(attributes) + unless attributes["brent"].nil? && + attributes["scharge"].nil? && + attributes["pscharge"].nil? && + attributes["supcharg"].nil? + attributes["brent"] ||= BigDecimal("0.0") + attributes["scharge"] ||= BigDecimal("0.0") + attributes["pscharge"] ||= BigDecimal("0.0") + attributes["supcharg"] ||= BigDecimal("0.0") + end + end + + def is_carehome(scheme) + return nil unless scheme + + if [2, 3, 4].include?(scheme.registered_under_care_act_before_type_cast) + 1 + else + 0 + end + end end end diff --git a/app/services/imports/scheme_import_service.rb b/app/services/imports/scheme_import_service.rb index b68e7e7da..8be6b4603 100644 --- a/app/services/imports/scheme_import_service.rb +++ b/app/services/imports/scheme_import_service.rb @@ -5,20 +5,18 @@ module Imports end def create_scheme(xml_document) - old_id = string_or_nil(xml_document, "id") - status = string_or_nil(xml_document, "status") - - if status == "Approved" + attributes = scheme_attributes(xml_document) + if attributes["status"] == "Approved" Scheme.create!( - owning_organisation_id: find_owning_organisation_id(xml_document), - managing_organisation_id: find_managing_organisation_id(xml_document), - service_name: string_or_nil(xml_document, "name"), - arrangement_type: string_or_nil(xml_document, "arrangement_type"), - old_id:, - old_visible_id: safe_string_as_integer(xml_document, "visible-id"), + owning_organisation_id: attributes["owning_organisation_id"], + managing_organisation_id: attributes["managing_organisation_id"], + service_name: attributes["service_name"], + arrangement_type: attributes["arrangement_type"], + old_id: attributes["old_id"], + old_visible_id: attributes["old_visible_id"], ) else - @logger.warn("Scheme with legacy ID #{old_id} is not approved (#{status}), skipping") + @logger.warn("Scheme with legacy ID #{attributes['old_id']} is not approved (#{attributes['status']}), skipping") end end @@ -39,16 +37,33 @@ module Imports Integer(str, exception: false) end - def find_owning_organisation_id(xml_doc) - old_org_id = string_or_nil(xml_doc, "institution") + def scheme_attributes(xml_doc) + attributes = {} + attributes["old_id"] = string_or_nil(xml_doc, "id") + attributes["old_visible_id"] = string_or_nil(xml_doc, "visible-id") + attributes["status"] = string_or_nil(xml_doc, "status") + attributes["service_name"] = string_or_nil(xml_doc, "name") + attributes["arrangement_type"] = string_or_nil(xml_doc, "arrangement_type") + attributes["owning_org_old_id"] = string_or_nil(xml_doc, "institution") + attributes["owning_organisation_id"] = find_owning_organisation_id(attributes["owning_org_old_id"]) + attributes["management_org_old_visible_id"] = safe_string_as_integer(xml_doc, "agent") + attributes["managing_organisation_id"] = find_managing_organisation_id(attributes["management_org_old_visible_id"]) + + if attributes["arrangement_type"] == "D" && attributes["managing_organisation_id"].nil? + attributes["managing_organisation_id"] = attributes["owning_organisation_id"] + end + + attributes + end + + def find_owning_organisation_id(old_org_id) organisation = Organisation.find_by(old_org_id:) raise "Organisation not found with legacy ID #{old_org_id}" if organisation.nil? organisation.id end - def find_managing_organisation_id(xml_doc) - old_visible_id = safe_string_as_integer(xml_doc, "agent") + def find_managing_organisation_id(old_visible_id) return unless old_visible_id organisation = Organisation.find_by(old_visible_id:) diff --git a/app/services/imports/scheme_location_import_service.rb b/app/services/imports/scheme_location_import_service.rb index f05381c3b..9c29f67e2 100644 --- a/app/services/imports/scheme_location_import_service.rb +++ b/app/services/imports/scheme_location_import_service.rb @@ -67,11 +67,13 @@ module Imports attributes["registered_under_care_act"] = registered_under_care_act.zero? ? nil : registered_under_care_act attributes["support_type"] = safe_string_as_integer(xml_doc, "support-type") attributes["intended_stay"] = string_or_nil(xml_doc, "intended-stay") + attributes["mobility_type"] = string_or_nil(xml_doc, "mobility-type") attributes["primary_client_group"] = string_or_nil(xml_doc, "client-group-1") attributes["secondary_client_group"] = string_or_nil(xml_doc, "client-group-2") attributes["secondary_client_group"] = nil if attributes["primary_client_group"] == attributes["secondary_client_group"] attributes["sensitive"] = sensitive(xml_doc) - attributes["end_date"] = parse_end_date(xml_doc) + attributes["start_date"] = parse_date(xml_doc, "start-date") + attributes["end_date"] = parse_date(xml_doc, "end-date") attributes["location_name"] = string_or_nil(xml_doc, "name") attributes["postcode"] = string_or_nil(xml_doc, "postcode") attributes["units"] = safe_string_as_integer(xml_doc, "total-units") @@ -84,15 +86,16 @@ module Imports def add_location(scheme, attributes) if attributes["end_date"].nil? || attributes["end_date"] >= Time.zone.now - # wheelchair_adaptation: string_or_nil(xml_doc, "mobility-type"), begin Location.create!( name: attributes["location_name"], postcode: attributes["postcode"], + mobility_type: attributes["mobility_type"], units: attributes["units"], type_of_unit: attributes["type_of_unit"], old_visible_id: attributes["location_old_visible_id"], old_id: attributes["location_old_id"], + startdate: attributes["start_date"], scheme:, ) rescue ActiveRecord::RecordNotUnique @@ -185,9 +188,9 @@ module Imports end end - def parse_end_date(xml_doc) - end_date = string_or_nil(xml_doc, "end-date") - Time.zone.parse(end_date) if end_date + def parse_date(xml_doc, attribute) + date = string_or_nil(xml_doc, attribute) + Time.zone.parse(date) if date end end end diff --git a/app/services/postcode_service.rb b/app/services/postcode_service.rb index 737e3fd9c..a82dcc2aa 100644 --- a/app/services/postcode_service.rb +++ b/app/services/postcode_service.rb @@ -1,4 +1,25 @@ class PostcodeService + def initialize + @pio = Postcodes::IO.new + end + + def infer_la(postcode) + # Avoid network calls when postcode is invalid + return unless postcode.match(POSTCODE_REGEXP) + + postcode_lookup = nil + begin + # URI encoding only supports ASCII characters + ascii_postcode = self.class.clean(postcode) + Timeout.timeout(5) { postcode_lookup = @pio.lookup(ascii_postcode) } + rescue Timeout::Error + Rails.logger.warn("Postcodes.io lookup timed out") + end + if postcode_lookup && postcode_lookup.info.present? + postcode_lookup.codes["admin_district"] + end + end + def self.clean(postcode) postcode.encode("ASCII", "UTF-8", invalid: :replace, undef: :replace, replace: "").delete(" ").upcase end diff --git a/app/views/form/_select_question.html.erb b/app/views/form/_select_question.html.erb index 52c47d66d..adbddfcd1 100644 --- a/app/views/form/_select_question.html.erb +++ b/app/views/form/_select_question.html.erb @@ -1,13 +1,18 @@ <%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> <% selected = @case_log.public_send(question.id) || "" %> -<% answers = question.displayed_answer_options(@case_log).map { |key, value| OpenStruct.new(id: key, name: value) } %> - <%= f.govuk_collection_select question.id.to_sym, - answers, - :id, - :name, - caption: caption(caption_text, page_header, conditional), - label: legend(question, page_header, conditional), - hint: { text: question.hint_text&.html_safe }, - options: { disabled: [""], selected: }, - "data-controller": "accessible-autocomplete" %> +<% answers = question.displayed_answer_options(@case_log).map { |key, value| OpenStruct.new(id: key, name: value.respond_to?(:service_name) ? value.service_name : nil, resource: value) } %> +<%= f.govuk_select(question.id.to_sym, + label: legend(question, page_header, conditional), + "data-controller": "accessible-autocomplete", + caption: caption(caption_text, page_header, conditional), + hint: { text: question.hint_text&.html_safe }) do %> + <% answers.each do |answer| %> + + <% end %> + <% end %> diff --git a/app/views/form/page.html.erb b/app/views/form/page.html.erb index f4f5fe9ae..f0edd1afb 100644 --- a/app/views/form/page.html.erb +++ b/app/views/form/page.html.erb @@ -5,7 +5,6 @@ <% end %>
- <%= form_with model: @case_log, url: form_case_log_path(@case_log), method: "post", local: true do |f| %>
"> @@ -39,9 +38,17 @@ <% end %> <%= f.hidden_field :page, value: @page.id %> - <% if !@page.id.include?("value_check") %> - <%= f.govuk_submit "Save and continue" %> - <% end %> + +
+ <% if !@page.id.include?("value_check") && if request.query_parameters["referrer"] != "check_answers" %> + <%= f.govuk_submit "Save and continue" %> + <%= govuk_link_to "Skip for now", send(@case_log.form.next_page_redirect_path(@page, @case_log, current_user), @case_log) %> + <% else %> + <%= f.govuk_submit "Save changes" %> + <%= govuk_link_to "Cancel", "/logs/#{@case_log.id}/setup/check-answers" %> + <% end %> + <% end %> +
<% end %> diff --git a/app/views/locations/edit.html.erb b/app/views/locations/edit.html.erb index 9227b3efb..227605485 100644 --- a/app/views/locations/edit.html.erb +++ b/app/views/locations/edit.html.erb @@ -16,17 +16,17 @@ <%= f.govuk_text_field :postcode, label: { size: "m" }, - hint: { text: "For example, SW1P 4DF." }, + hint: { text: I18n.t("hints.location.postcode") }, width: 5 %> <%= f.govuk_text_field :name, - label: { text: "Location name (optional)", size: "m" }, - hint: { text: "This is how you refer to this location within your organisation" } %> + label: { text: I18n.t("questions.location.name"), size: "m" }, + hint: { text: I18n.t("hints.location.name") } %> <%= f.govuk_number_field :units, - label: { text: "Total number of units at this location", size: "m" }, + label: { text: I18n.t("questions.location.units"), 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" }, + hint: { text: I18n.t("hints.location.units") }, autofocus: true %> <% type_of_units_selection = Location.type_of_units.keys.map { |key, _| OpenStruct.new(id: key, name: key.to_s.humanize) } %> @@ -34,15 +34,19 @@ type_of_units_selection, :id, :name, - legend: { text: "What is this type of scheme?", size: "m" } %> + legend: { text: I18n.t("questions.location.type_of_unit"), 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" } %> + hint: { text: I18n.t("hints.location.wheelchair_adaptation") }, + legend: { text: I18n.t("questions.location.wheelchair_adaptation"), size: "m" } %> + + <%= f.govuk_date_field :startdate, + legend: { text: I18n.t("questions.location.startdate"), size: "m" }, + width: 20 %> <%= govuk_section_break(visible: true, size: "m") %> @@ -52,7 +56,7 @@ :id, :name, inline: true, - legend: { text: "Do you want to add another location?", size: "m" } %> + legend: { text: I18n.t("questions.location.add_another_location"), size: "m" } %> <%= f.hidden_field :page, value: "edit" %> diff --git a/app/views/locations/new.html.erb b/app/views/locations/new.html.erb index 8d964d07c..669a39594 100644 --- a/app/views/locations/new.html.erb +++ b/app/views/locations/new.html.erb @@ -16,17 +16,17 @@ <%= f.govuk_text_field :postcode, label: { size: "m" }, - hint: { text: "For example, SW1P 4DF." }, + hint: { text: I18n.t("hints.location.postcode") }, width: 5 %> <%= f.govuk_text_field :name, - label: { text: "Location name (optional)", size: "m" }, - hint: { text: "This is how you refer to this location within your organisation" } %> + label: { text: I18n.t("questions.location.name"), size: "m" }, + hint: { text: I18n.t("hints.location.name") } %> <%= f.govuk_number_field :units, - label: { text: "Total number of units at this location", size: "m" }, + label: { text: I18n.t("questions.location.units"), 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" }, + hint: { text: I18n.t("hints.location.units") }, autofocus: true %> <% type_of_units_selection = Location.type_of_units.keys.map { |key, _| OpenStruct.new(id: key, name: key.to_s.humanize) } %> @@ -35,7 +35,7 @@ type_of_units_selection, :id, :name, - legend: { text: "What is this type of scheme?", size: "m" } %> + legend: { text: I18n.t("questions.location.type_of_unit"), size: "m" } %> <% wheelchair_user_selection = Location.wheelchair_adaptations.keys.map { |key, _| OpenStruct.new(id: key, name: key.to_s.humanize) } %> @@ -43,8 +43,12 @@ 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" } %> + hint: { text: I18n.t("hints.location.wheelchair_adaptation") }, + legend: { text: I18n.t("questions.location.wheelchair_adaptation"), size: "m" } %> + + <%= f.govuk_date_field :startdate, + legend: { text: I18n.t("questions.location.startdate"), size: "m" }, + width: 20 %> <%= govuk_section_break(visible: true, size: "m") %> @@ -55,7 +59,7 @@ :id, :name, inline: true, - legend: { text: "Do you want to add another location?", size: "m" } %> + legend: { text: I18n.t("questions.location.add_another_location"), size: "m" } %> <%= f.govuk_submit "Save and continue" %> diff --git a/config/forms/2021_2022.json b/config/forms/2021_2022.json index d93f04e22..fac033857 100644 --- a/config/forms/2021_2022.json +++ b/config/forms/2021_2022.json @@ -1078,7 +1078,8 @@ "step": 1, "width": 2 } - } + }, + "depends_on": [{ "declaration": 1 }] }, "no_females_pregnant_household_lead_hhmemb_value_check": { "depends_on": [{ "no_females_in_a_pregnant_household?": true }], @@ -1120,11 +1121,7 @@ } }, "females_in_soft_age_range_in_pregnant_household_lead_hhmemb_value_check": { - "depends_on": [ - { - "female_in_pregnant_household_in_soft_validation_range?": true - } - ], + "depends_on": [{ "female_in_pregnant_household_in_soft_validation_range?": true }], "title_text": { "translation": "soft_validations.pregnancy.title", "arguments": [ @@ -1207,7 +1204,8 @@ "value": "Not known" } } - } + }, + "depends_on": [{ "declaration": 1 }] }, "no_females_pregnant_household_lead_age_value_check": { "depends_on": [{ "no_females_in_a_pregnant_household?": true }], @@ -1249,11 +1247,7 @@ } }, "females_in_soft_age_range_in_pregnant_household_lead_age_value_check": { - "depends_on": [ - { - "female_in_pregnant_household_in_soft_validation_range?": true - } - ], + "depends_on": [{ "female_in_pregnant_household_in_soft_validation_range?": true }], "title_text": { "translation": "soft_validations.pregnancy.title", "arguments": [ @@ -1318,7 +1312,8 @@ } } } - } + }, + "depends_on": [{ "declaration": 1 }] }, "no_females_pregnant_household_lead_value_check": { "depends_on": [{ "no_females_in_a_pregnant_household?": true }], @@ -1360,11 +1355,7 @@ } }, "females_in_soft_age_range_in_pregnant_household_lead_value_check": { - "depends_on": [ - { - "female_in_pregnant_household_in_soft_validation_range?": true - } - ], + "depends_on": [{ "female_in_pregnant_household_in_soft_validation_range?": true }], "title_text": { "translation": "soft_validations.pregnancy.title", "arguments": [ @@ -1435,7 +1426,8 @@ } } } - } + }, + "depends_on": [{ "declaration": 1 }] }, "lead_tenant_ethnic_background_arab": { "header": "", @@ -1456,11 +1448,7 @@ } } }, - "depends_on": [ - { - "ethnic_group": 4 - } - ] + "depends_on": [{ "ethnic_group": 4 }] }, "lead_tenant_ethnic_background_asian": { "header": "", @@ -1490,11 +1478,7 @@ } } }, - "depends_on": [ - { - "ethnic_group": 2 - } - ] + "depends_on": [{ "ethnic_group": 2 }] }, "lead_tenant_ethnic_background_black": { "header": "", @@ -1518,11 +1502,7 @@ } } }, - "depends_on": [ - { - "ethnic_group": 3 - } - ] + "depends_on": [{ "ethnic_group": 3 }] }, "lead_tenant_ethnic_background_mixed": { "header": "", @@ -1549,11 +1529,7 @@ } } }, - "depends_on": [ - { - "ethnic_group": 1 - } - ] + "depends_on": [{ "ethnic_group": 1 }] }, "lead_tenant_ethnic_background_white": { "header": "", @@ -1580,11 +1556,7 @@ } } }, - "depends_on": [ - { - "ethnic_group": 0 - } - ] + "depends_on": [{ "ethnic_group": 0 }] }, "lead_tenant_nationality": { "header": "", @@ -1650,7 +1622,8 @@ } } } - } + }, + "depends_on": [{ "declaration": 1 }] }, "lead_tenant_working_situation": { "header": "", @@ -1697,7 +1670,8 @@ } } } - } + }, + "depends_on": [{ "declaration": 1 }] }, "lead_tenant_under_retirement_value_check": { "depends_on": [{ "person_1_retired_under_soft_min_age?": true }], diff --git a/config/forms/2022_2023.json b/config/forms/2022_2023.json index 66cc072f3..db26c7d0f 100644 --- a/config/forms/2022_2023.json +++ b/config/forms/2022_2023.json @@ -1113,7 +1113,8 @@ "step": 1, "width": 2 } - } + }, + "depends_on": [{ "declaration": 1 }] }, "no_females_pregnant_household_lead_hhmemb_value_check": { "depends_on": [{ "no_females_in_a_pregnant_household?": true }], @@ -1155,11 +1156,7 @@ } }, "females_in_soft_age_range_in_pregnant_household_lead_hhmemb_value_check": { - "depends_on": [ - { - "female_in_pregnant_household_in_soft_validation_range?": true - } - ], + "depends_on": [{ "female_in_pregnant_household_in_soft_validation_range?": true }], "title_text": { "translation": "soft_validations.pregnancy.title", "arguments": [ @@ -1242,7 +1239,8 @@ "value": "Not known" } } - } + }, + "depends_on": [{ "declaration": 1 }] }, "no_females_pregnant_household_lead_age_value_check": { "depends_on": [{ "no_females_in_a_pregnant_household?": true }], @@ -1284,11 +1282,7 @@ } }, "females_in_soft_age_range_in_pregnant_household_lead_age_value_check": { - "depends_on": [ - { - "female_in_pregnant_household_in_soft_validation_range?": true - } - ], + "depends_on": [{ "female_in_pregnant_household_in_soft_validation_range?": true }], "title_text": { "translation": "soft_validations.pregnancy.title", "arguments": [ @@ -1353,7 +1347,8 @@ } } } - } + }, + "depends_on": [{ "declaration": 1 }] }, "no_females_pregnant_household_lead_value_check": { "depends_on": [{ "no_females_in_a_pregnant_household?": true }], @@ -1395,11 +1390,7 @@ } }, "females_in_soft_age_range_in_pregnant_household_lead_value_check": { - "depends_on": [ - { - "female_in_pregnant_household_in_soft_validation_range?": true - } - ], + "depends_on": [{ "female_in_pregnant_household_in_soft_validation_range?": true }], "title_text": { "translation": "soft_validations.pregnancy.title", "arguments": [ @@ -1470,7 +1461,8 @@ } } } - } + }, + "depends_on": [{ "declaration": 1 }] }, "lead_tenant_ethnic_background_arab": { "header": "", @@ -1491,11 +1483,7 @@ } } }, - "depends_on": [ - { - "ethnic_group": 4 - } - ] + "depends_on": [{ "ethnic_group": 4 }] }, "lead_tenant_ethnic_background_asian": { "header": "", @@ -1525,11 +1513,7 @@ } } }, - "depends_on": [ - { - "ethnic_group": 2 - } - ] + "depends_on": [{ "ethnic_group": 2 }] }, "lead_tenant_ethnic_background_black": { "header": "", @@ -1553,11 +1537,7 @@ } } }, - "depends_on": [ - { - "ethnic_group": 3 - } - ] + "depends_on": [{ "ethnic_group": 3 }] }, "lead_tenant_ethnic_background_mixed": { "header": "", @@ -1584,11 +1564,7 @@ } } }, - "depends_on": [ - { - "ethnic_group": 1 - } - ] + "depends_on": [{ "ethnic_group": 1 }] }, "lead_tenant_ethnic_background_white": { "header": "", @@ -1615,11 +1591,7 @@ } } }, - "depends_on": [ - { - "ethnic_group": 0 - } - ] + "depends_on": [{ "ethnic_group": 0 }] }, "lead_tenant_nationality": { "header": "", @@ -1649,7 +1621,8 @@ } } } - } + }, + "depends_on": [{ "declaration": 1 }] }, "lead_tenant_working_situation": { "header": "", @@ -1696,7 +1669,8 @@ } } } - } + }, + "depends_on": [{ "declaration": 1 }] }, "lead_tenant_under_retirement_value_check": { "depends_on": [{ "person_1_retired_under_soft_min_age?": true }], @@ -1743,9 +1717,7 @@ } }, "lead_tenant_over_retirement_value_check": { - "depends_on": [ - { "person_1_not_retired_over_soft_max_age?": true } - ], + "depends_on": [{ "person_1_not_retired_over_soft_max_age?": true }], "title_text": { "translation": "soft_validations.retirement.max.title", "arguments": [ diff --git a/config/locales/en.yml b/config/locales/en.yml index 4c3083b10..6b5ad6412 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -51,6 +51,7 @@ en: organisation: name_missing: "Enter the organisation’s name" provider_type_missing: "Select the organisation’s type" + not_answered: "You must answer %{question}" other_field_missing: "If %{main_field_label} is other then %{other_field_label} must be provided" other_field_not_required: "%{other_field_label} must not be provided if %{main_field_label} was not other" @@ -287,6 +288,22 @@ en: code_required: "Security code is required" code_incorrect: "Security code is incorrect" + questions: + location: + name: "Location name (optional)" + units: "Total number of units at this location" + type_of_unit: "What is this type of scheme?" + wheelchair_adaptation: "Are the majority of units in this location built or adapted to wheelchair-user standards?" + startdate: "When did the first property in this location become available under this scheme?" + add_another_location: "Do you want to add another location?" + + hints: + location: + postcode: "For example, SW1P 4DF." + name: "This is how you refer to this location within your organisation" + units: "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." + wheelchair_adaptation: "This includes stairlifts, ramps, level-access showers or grab rails" + test: one_argument: "This is based on the tenant’s work situation: %{ecstat1}" title_text: diff --git a/db/migrate/20220713095713_add_mobility_type_to_locations.rb b/db/migrate/20220713095713_add_mobility_type_to_locations.rb new file mode 100644 index 000000000..9f3284c0c --- /dev/null +++ b/db/migrate/20220713095713_add_mobility_type_to_locations.rb @@ -0,0 +1,7 @@ +class AddMobilityTypeToLocations < ActiveRecord::Migration[7.0] + def change + change_table :locations, bulk: true do |t| + t.column :mobility_type, :string + end + end +end diff --git a/db/migrate/20220714080044_add_location_startdate.rb b/db/migrate/20220714080044_add_location_startdate.rb new file mode 100644 index 000000000..9389960c5 --- /dev/null +++ b/db/migrate/20220714080044_add_location_startdate.rb @@ -0,0 +1,7 @@ +class AddLocationStartdate < ActiveRecord::Migration[7.0] + def change + change_table :locations, bulk: true do |t| + t.column :startdate, :datetime + end + end +end diff --git a/db/migrate/20220715133937_remove_county_from_location.rb b/db/migrate/20220715133937_remove_county_from_location.rb new file mode 100644 index 000000000..02fddb658 --- /dev/null +++ b/db/migrate/20220715133937_remove_county_from_location.rb @@ -0,0 +1,5 @@ +class RemoveCountyFromLocation < ActiveRecord::Migration[7.0] + def change + remove_column :locations, :county, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 2df8ecc58..675fed6e7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2022_07_12_143943) do +ActiveRecord::Schema[7.0].define(version: 2022_07_15_133937) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -245,13 +245,14 @@ ActiveRecord::Schema[7.0].define(version: 2022_07_12_143943) do t.integer "wheelchair_adaptation" t.bigint "scheme_id", null: false t.string "name" - t.string "county" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "units" t.integer "type_of_unit" t.string "old_id" t.integer "old_visible_id" + t.string "mobility_type" + t.datetime "startdate", precision: nil t.index ["old_id"], name: "index_locations_on_old_id", unique: true t.index ["scheme_id"], name: "index_locations_on_scheme_id" end diff --git a/spec/factories/location.rb b/spec/factories/location.rb index 7aebbbcde..daef3d2ce 100644 --- a/spec/factories/location.rb +++ b/spec/factories/location.rb @@ -1,12 +1,11 @@ FactoryBot.define do factory :location do - location_code { Faker::Name.initials(number: 10) } postcode { Faker::Address.postcode.delete(" ") } name { Faker::Address.street_name } type_of_unit { [1, 2, 3, 4, 6, 7].sample } type_of_building { "Purpose built" } - wheelchair_adaptation { 0 } - county { Faker::Address.state } + mobility_type { %w[A M N W X].sample } + wheelchair_adaptation { 2 } scheme end end diff --git a/spec/features/form/accessible_autocomplete_spec.rb b/spec/features/form/accessible_autocomplete_spec.rb index a8537199b..e948056b4 100644 --- a/spec/features/form/accessible_autocomplete_spec.rb +++ b/spec/features/form/accessible_autocomplete_spec.rb @@ -14,6 +14,7 @@ RSpec.describe "Accessible Automcomplete" do is_la_inferred: false, owning_organisation: user.organisation, managing_organisation: user.organisation, + created_by: user, ) end @@ -58,6 +59,32 @@ RSpec.describe "Accessible Automcomplete" do end end + context "when searching schemes" do + let(:scheme) { FactoryBot.create(:scheme, owning_organisation_id: case_log.created_by.organisation_id, primary_client_group: "Q", secondary_client_group: "P") } + + before do + FactoryBot.create(:location, scheme:, postcode: "W6 0ST") + FactoryBot.create(:location, scheme:, postcode: "SE6 1LB") + case_log.update!(needstype: 2) + visit("/logs/#{case_log.id}/scheme") + end + + it "can match on synonyms", js: true do + find("#case-log-scheme-id-field").click.native.send_keys("w", "6", :down, :enter) + expect(find("#case-log-scheme-id-field").value).to eq(scheme.service_name) + end + + it "displays appended text next to the options", js: true do + find("#case-log-scheme-id-field").click.native.send_keys("w", "6", :down, :enter) + expect(find(".autocomplete__option__append", visible: :hidden, text: /(2 locations)/)).to be_present + end + + it "displays hint text under the options", js: true do + find("#case-log-scheme-id-field").click.native.send_keys("w", "6", :down, :enter) + expect(find(".autocomplete__option__hint", visible: :hidden, text: /Young people at risk, Young people leaving care/)).to be_present + end + end + it "has the correct option selected if one has been saved" do case_log.update!(postcode_known: 0, previous_la_known: 1, prevloc: "E07000178") visit("/logs/#{case_log.id}/accessible-select") diff --git a/spec/features/form/check_answers_page_spec.rb b/spec/features/form/check_answers_page_spec.rb index aab201137..f3b9418a2 100644 --- a/spec/features/form/check_answers_page_spec.rb +++ b/spec/features/form/check_answers_page_spec.rb @@ -136,7 +136,7 @@ RSpec.describe "Form Check Answers Page" do first("a", text: /Change/).click uncheck("case-log-accessibility-requirements-housingneeds-c-field") check("case-log-accessibility-requirements-housingneeds-b-field") - click_button("Save and continue") + click_button("Save changes") expect(page).to have_current_path("/logs/#{empty_case_log.id}/household-needs/check-answers") end end diff --git a/spec/features/form/form_navigation_spec.rb b/spec/features/form/form_navigation_spec.rb index 1dbdaef25..d0ca551b4 100644 --- a/spec/features/form/form_navigation_spec.rb +++ b/spec/features/form/form_navigation_spec.rb @@ -13,6 +13,15 @@ RSpec.describe "Form Navigation" do created_by: user, ) end + let(:empty_case_log) do + FactoryBot.create( + :case_log, + owning_organisation: user.organisation, + managing_organisation: user.organisation, + created_by: user, + ) + end + let(:id) { case_log.id } let(:question_answers) do { @@ -47,11 +56,23 @@ RSpec.describe "Form Navigation" do pages = question_answers.map { |_key, val| val[:path] } pages[0..-2].each_with_index do |val, index| visit("/logs/#{id}/#{val}") - click_button("Save and continue") + click_link("Skip for now") expect(page).to have_current_path("/logs/#{id}/#{pages[index + 1]}") end end + it "a question page has a Skip for now link that lets you move on to the next question without inputting anything" do + visit("logs/#{empty_case_log.id}/tenant-code-test") + click_link(text: "Skip for now") + expect(page).to have_current_path("/logs/#{empty_case_log.id}/person-1-age") + end + + it "routes to check answers when skipping on the last page in the form" do + visit("logs/#{empty_case_log.id}/propcode") + click_link(text: "Skip for now") + expect(page).to have_current_path("/logs/#{empty_case_log.id}/household-characteristics/check-answers") + end + describe "Back link directs correctly", js: true do it "go back to tasklist page from tenant code" do visit("/logs/#{id}") @@ -89,4 +110,57 @@ RSpec.describe "Form Navigation" do end end end + + describe "Editing a log" do + it "a question page has a link allowing you to cancel your input and return to the check answers page" do + visit("logs/#{id}/tenant-code-test?referrer=check_answers") + click_link(text: "Cancel") + expect(page).to have_current_path("/logs/#{id}/setup/check-answers") + end + + context "when clicking save and continue on a mandatory question with no input" do + let(:id) { empty_case_log.id } + + it "shows a validation error on radio questions" do + visit("/logs/#{id}/renewal") + click_button("Save and continue") + expect(page).to have_selector("#error-summary-title") + expect(page).to have_selector("#case-log-renewal-error") + expect(page).to have_title("Error") + end + + it "shows a validation error on date questions" do + visit("/logs/#{id}/tenancy-start-date") + click_button("Save and continue") + expect(page).to have_selector("#error-summary-title") + expect(page).to have_selector("#case-log-startdate-error") + expect(page).to have_title("Error") + end + + context "when the page has a main and conditional question" do + context "when the conditional question is required but not answered" do + it "shows a validation error for the conditional question" do + visit("/logs/#{id}/armed-forces") + choose("case-log-armedforces-1-field", allow_label_click: true) + click_button("Save and continue") + expect(page).to have_selector("#error-summary-title") + expect(page).to have_selector("#case-log-leftreg-error") + expect(page).to have_title("Error") + end + end + end + end + + context "when clicking save and continue on an optional question with no input" do + let(:id) { empty_case_log.id } + + it "does not show a validation error" do + visit("/logs/#{id}/tenant-code") + click_button("Save and continue") + expect(page).not_to have_selector("#error-summary-title") + expect(page).not_to have_title("Error") + expect(page).to have_current_path("/logs/#{id}/property-reference") + end + end + end end diff --git a/spec/features/form/page_routing_spec.rb b/spec/features/form/page_routing_spec.rb index 83a1078ff..644bbb608 100644 --- a/spec/features/form/page_routing_spec.rb +++ b/spec/features/form/page_routing_spec.rb @@ -43,6 +43,7 @@ RSpec.describe "Form Page Routing" do choose("case-log-preg-occ-2-field", allow_label_click: true) click_button("Save and continue") expect(page).to have_current_path("/logs/#{id}/conditional-question-no-page") + choose("case-log-cbl-0-field", allow_label_click: true) click_button("Save and continue") expect(page).to have_current_path("/logs/#{id}/conditional-question/check-answers") end diff --git a/spec/features/form/progressive_total_field_spec.rb b/spec/features/form/progressive_total_field_spec.rb index 2e9b0cdf9..2ddd7dffd 100644 --- a/spec/features/form/progressive_total_field_spec.rb +++ b/spec/features/form/progressive_total_field_spec.rb @@ -33,6 +33,7 @@ RSpec.describe "Accessible Automcomplete" do it "total displays despite error message", js: true do visit("/logs/#{case_log.id}/rent") + choose("case-log-period-1-field", allow_label_click: true) fill_in("case-log-brent-field", with: 500) fill_in("case-log-scharge-field", with: 50) fill_in("case-log-pscharge-field", with: 50) @@ -40,6 +41,9 @@ RSpec.describe "Accessible Automcomplete" do expect(find("#case-log-tcharge-field").value).to eq("5600.00") click_button("Save and continue") expect(page).to have_selector(".govuk-error-summary") + fill_in("case-log-scharge-field", with: nil) + fill_in("case-log-pscharge-field", with: nil) + fill_in("case-log-supcharg-field-error", with: nil) fill_in("case-log-brent-field", with: 500) expect(find("#case-log-tcharge-field").value).to eq("500.00") fill_in("case-log-supcharg-field-error", with: 50) diff --git a/spec/features/form/saving_data_spec.rb b/spec/features/form/saving_data_spec.rb index 97aeb7521..425c0dca3 100644 --- a/spec/features/form/saving_data_spec.rb +++ b/spec/features/form/saving_data_spec.rb @@ -26,7 +26,6 @@ RSpec.describe "Form Saving Data" do tenancycode: { type: "text", answer: "BZ737", path: "tenant-code-test" }, age1: { type: "numeric", answer: 25, path: "person_1_age" }, sex1: { type: "radio", answer: { "F" => "Female" }, path: "person_1_gender" }, - hhmemb: { type: "numeric", answer: 3, path: "household_number_of_members" }, } end diff --git a/spec/features/schemes_spec.rb b/spec/features/schemes_spec.rb index cb5dd6770..9230ab143 100644 --- a/spec/features/schemes_spec.rb +++ b/spec/features/schemes_spec.rb @@ -772,4 +772,51 @@ RSpec.describe "Schemes scheme Features" do end end end + + context "when selecting a scheme" do + let!(:user) { FactoryBot.create(:user, :data_coordinator, last_sign_in_at: Time.zone.now) } + let!(:schemes) { FactoryBot.create_list(:scheme, 5, owning_organisation: user.organisation) } + let(:location) { FactoryBot.create(:location, scheme: schemes[2]) } + let!(:case_log) { FactoryBot.create(:case_log, created_by: user, needstype: 2) } + + before do + Timecop.freeze(Time.utc(2022, 6, 3)) + location.update!(startdate: nil) + FactoryBot.create(:location, scheme: schemes[0], startdate: nil) + FactoryBot.create(:location, scheme: schemes[1], startdate: nil) + FactoryBot.create(:location, scheme: schemes[1], startdate: nil) + FactoryBot.create(:location, scheme: schemes[1], startdate: Time.utc(2023, 6, 3)) + visit("/logs") + fill_in("user[email]", with: user.email) + fill_in("user[password]", with: user.password) + click_button("Sign in") + end + + after do + Timecop.unfreeze + end + + it "does not display the schemes without a location" do + visit("/logs/#{case_log.id}/scheme") + expect(find("#case-log-scheme-id-field").all("option").count).to eq(3) + end + + it "does not display the schemes with a location with a startdate in the future" do + location.update!(startdate: Time.utc(2022, 7, 4)) + visit("/logs/#{case_log.id}/scheme") + expect(find("#case-log-scheme-id-field").all("option").count).to eq(2) + end + + it "does display the schemes with a location with a startdate in the past" do + location.update!(startdate: Time.utc(2022, 5, 2)) + visit("/logs/#{case_log.id}/scheme") + expect(find("#case-log-scheme-id-field").all("option").count).to eq(3) + end + + it "does display the schemes with a location with a startdate being today" do + location.update!(startdate: Time.utc(2022, 6, 3)) + visit("/logs/#{case_log.id}/scheme") + expect(find("#case-log-scheme-id-field").all("option").count).to eq(3) + end + end end diff --git a/spec/fixtures/imports/case_logs/0b4a68df-30cc-474a-93c0-a56ce8fdad3b.xml b/spec/fixtures/imports/case_logs/0b4a68df-30cc-474a-93c0-a56ce8fdad3b.xml new file mode 100644 index 000000000..59c1eada7 --- /dev/null +++ b/spec/fixtures/imports/case_logs/0b4a68df-30cc-474a-93c0-a56ce8fdad3b.xml @@ -0,0 +1,524 @@ + + + 2021-CORE-SR-SH + 0b4a68df-30cc-474a-93c0-a56ce8fdad3b + c3061a2e6ea0b702e6f6210d5c52d2a92612d2aa + 7c5bd5fb549c09z2c55d9cb90d7ba84927e64618 + 7c5bd5fb549c09z2c55d9cb90d7ba84927e64618 + 2022-01-05T12:50:20.39153Z + 2022-01-05T12:50:20.39153Z + submitted-valid + 2021 + Manual Entry + + + + + Yes + 2021-11-05 +
123456
+ 2 Local Authority + + <_1cmangroupcode>123 + <_1cschemecode>10 + + 3 No + +
+ + <_2a>2 No + 1 Secure (inc flexible) + + <_2bTenCode>14044912001 + <_2cYears>2 + + + 72 + + Female + 5) Retired + 1 White: English/Scottish/Welsh/Northern Irish/British + 1 UK national resident in UK + 74 + + Male + Partner + 5) Retired + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2 No + + + 2 No + + + 9 Not in receipt of either UC or HB + + + 2 Some + Refused + + + + + 13 Property unsuitable because of ill health / disability + + + + <_9b override-field="">2 No + + + + + Yes + + 1 Yes + + + Yes + + + + + + + + 26 Owner occupation (private) + DLUHC + E08000035 + S80 4DJ + + 5 5 years or more + 9 3 years but under 4 years + + + 1 Not homeless + 2 No + + + + + + + + 1 Yes + 1 Yes + 1 Yes + + + 2 Tenant applied direct (no referral or nomination) + + + + 7 Weekly for 48 weeks + 125.00 + + + 7.00 + 132.00 + + + + + 2021-08-24 + + + 0 + 14044912 + + + 1 Yes + 15 First let of newbuild property + + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + <_100>0 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + <_70>1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 1 + 2 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 2 + 74 + 0 + 0 + 0 + 0 + 0 + 0 + 74 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 72 + 0 + 74 + 2022-01-05Z + 2022-01-20Z + + + + + + + + + + + + + 0 + 0 + 0 + 0 + 20 + 0 + 20 + A + 1 + 1 + 1 + 1 + 4 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + + 0.00 + 2 + 1 New Tenant + + 73 + 2 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 2 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 1 + 2 = 2 Adults at least one is an Elder + + + 15.00 + + + 2 Local Authority + + + E12000004 + 1 + DLUHC + DLUHC + N/A + N/A + + 1 + false + + + + + + + + + + + + + + + 15 + 15 + 000001005048 + D + 15 + 6 + 7 + 1 + 2 + A + P + M + + DLUHC + E08000035 + S80 4QE + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + + + + 115.38 + 121.85 + + + 6.46 + + 115.38 + 121.85 + + + 6.46 + + + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + + 115.38 + 0 + + 0 + 117.4 + 10 + + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 5 + 11 + 2021 + 24 + 8 + 2021 + + + + LS16 + 6FT + LS16 + 6FT + +
diff --git a/spec/models/case_log_spec.rb b/spec/models/case_log_spec.rb index d5cbed1cc..ae91b6b0c 100644 --- a/spec/models/case_log_spec.rb +++ b/spec/models/case_log_spec.rb @@ -1702,9 +1702,9 @@ RSpec.describe CaseLog do context "and not renewal" do let(:scheme) { FactoryBot.create(:scheme) } - let(:location) { FactoryBot.create(:location, scheme:, county: "E07000041", type_of_unit: 1, type_of_building: "Purpose built", wheelchair_adaptation: 0) } + let(:location) { FactoryBot.create(:location, scheme:, postcode: "M11AE", type_of_unit: 1, type_of_building: "Purpose built") } - let!(:supported_housing_case_log) do + let(:supported_housing_case_log) do described_class.create!({ managing_organisation: owning_organisation, owning_organisation:, @@ -1716,14 +1716,21 @@ RSpec.describe CaseLog do }) end + before do + stub_request(:get, /api.postcodes.io/) + .to_return(status: 200, body: "{\"status\":200,\"result\":{\"admin_district\":\"Manchester\",\"codes\":{\"admin_district\": \"E08000003\"}}}", headers: {}) + end + it "correctly infers and saves la" do record_from_db = ActiveRecord::Base.connection.execute("SELECT la from case_logs WHERE id=#{supported_housing_case_log.id}").to_a[0] - expect(record_from_db["la"]).to eq(location.county) + expect(record_from_db["la"]).to be_nil + expect(supported_housing_case_log.la).to eq("E08000003") end it "correctly infers and saves postcode" do record_from_db = ActiveRecord::Base.connection.execute("SELECT postcode_full from case_logs WHERE id=#{supported_housing_case_log.id}").to_a[0] - expect(record_from_db["postcode_full"]).to eq(location.postcode) + expect(record_from_db["postcode_full"]).to be_nil + expect(supported_housing_case_log.postcode_full).to eq("M11AE") end it "unittype_sh method returns the type_of_unit of the location" do diff --git a/spec/models/form/setup/questions/location_id_spec.rb b/spec/models/form/setup/questions/location_id_spec.rb index dd1bd9735..c8681810f 100644 --- a/spec/models/form/setup/questions/location_id_spec.rb +++ b/spec/models/form/setup/questions/location_id_spec.rb @@ -31,7 +31,74 @@ RSpec.describe Form::Setup::Questions::LocationId, type: :model do expect(question).not_to be_derived end - it "has the correct answer_options" do - expect(question.answer_options).to eq({}) + context "when there are no locations" do + it "the answer_options is an empty hash" do + expect(question.answer_options).to eq({}) + end + end + + context "when getting available locations" do + let(:scheme) { FactoryBot.create(:scheme) } + let(:case_log) { FactoryBot.create(:case_log, scheme:, needstype: 2) } + + context "when there are no locations" do + it "the displayed_answer_options is an empty hash" do + expect(question.displayed_answer_options(case_log)).to eq({}) + end + end + + context "when selected scheme has locations" do + before do + Timecop.freeze(Time.utc(2022, 5, 12)) + end + + after do + Timecop.unfreeze + end + + context "and all the locations have a future startdate" do + before do + FactoryBot.create(:location, scheme:, startdate: Time.utc(2022, 5, 13)) + FactoryBot.create(:location, scheme:, startdate: Time.utc(2023, 1, 1)) + end + + it "the displayed_answer_options is an empty hash" do + expect(question.displayed_answer_options(case_log)).to eq({}) + end + end + + context "and the locations have a no startdate" do + before do + FactoryBot.create(:location, scheme:, startdate: nil) + FactoryBot.create(:location, scheme:, startdate: nil) + end + + it "the displayed_answer_options shows the locations" do + expect(question.displayed_answer_options(case_log).count).to eq(2) + end + end + + context "and the locations have a past startdate" do + before do + FactoryBot.create(:location, scheme:, startdate: Time.utc(2022, 4, 10)) + FactoryBot.create(:location, scheme:, startdate: Time.utc(2022, 5, 12)) + end + + it "the displayed_answer_options shows the locations" do + expect(question.displayed_answer_options(case_log).count).to eq(2) + end + end + + context "and some locations have a past startdate" do + before do + FactoryBot.create(:location, scheme:, startdate: Time.utc(2022, 10, 10)) + FactoryBot.create(:location, scheme:, startdate: Time.utc(2022, 5, 12)) + end + + it "the displayed_answer_options shows the active location" do + expect(question.displayed_answer_options(case_log).count).to eq(1) + end + end + end end end diff --git a/spec/models/form/setup/questions/scheme_id_spec.rb b/spec/models/form/setup/questions/scheme_id_spec.rb index c40d5302c..2b016f463 100644 --- a/spec/models/form/setup/questions/scheme_id_spec.rb +++ b/spec/models/form/setup/questions/scheme_id_spec.rb @@ -50,9 +50,21 @@ RSpec.describe Form::Setup::Questions::SchemeId, type: :model do FactoryBot.create(:scheme, owning_organisation: organisation_2) end - it "has the correct answer_options based on the schemes the user's organisation owns or manages" do - expected_answer = { scheme.id.to_s => scheme.service_name } - expect(question.displayed_answer_options(case_log)).to eq(expected_answer) + context "when a scheme with at least 1 location exists" do + before do + FactoryBot.create(:location, scheme:) + end + + it "has the correct answer_options based on the schemes the user's organisation owns or manages" do + expected_answer = { scheme.id.to_s => scheme } + expect(question.displayed_answer_options(case_log)).to eq(expected_answer) + end + end + + context "when there are no schemes with locations" do + it "returns an empty hash" do + expect(question.displayed_answer_options(case_log)).to eq({}) + end end end end diff --git a/spec/models/location_spec.rb b/spec/models/location_spec.rb index a886c6034..ae78df6c5 100644 --- a/spec/models/location_spec.rb +++ b/spec/models/location_spec.rb @@ -4,9 +4,20 @@ RSpec.describe Location, type: :model do describe "#new" do let(:location) { FactoryBot.build(:location) } + before do + stub_request(:get, /api.postcodes.io/) + .to_return(status: 200, body: "{\"status\":200,\"result\":{\"admin_district\":\"Manchester\",\"codes\":{\"admin_district\": \"E08000003\"}}}", headers: {}) + end + it "belongs to an organisation" do expect(location.scheme).to be_a(Scheme) end + + it "infers the local authority" do + location.postcode = "M1 1AE" + location.save! + expect(location.location_code).to eq("E08000003") + end end describe "#validate_postcode" do diff --git a/spec/models/scheme_spec.rb b/spec/models/scheme_spec.rb index 96c9b86a7..d81d93bd6 100644 --- a/spec/models/scheme_spec.rb +++ b/spec/models/scheme_spec.rb @@ -46,6 +46,10 @@ RSpec.describe Scheme, type: :model do end context "when searching by all searchable fields" do + before do + location_2.update!(postcode: location_2.postcode.gsub(scheme_1.id.to_s, "0")) + end + it "returns case insensitive matching records" do expect(described_class.search_by(scheme_1.id.to_s).count).to eq(1) expect(described_class.search_by(scheme_1.id.to_s).first.id).to eq(scheme_1.id) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 5787ffe66..78867bad9 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -80,6 +80,7 @@ RSpec.configure do |config| Capybara.server = :puma, { Silent: true } config.include Devise::Test::ControllerHelpers, type: :controller + config.include Devise::Test::ControllerHelpers, type: :view config.include Devise::Test::IntegrationHelpers, type: :request config.include ViewComponent::TestHelpers, type: :component config.include Capybara::RSpecMatchers, type: :component diff --git a/spec/requests/case_logs_controller_spec.rb b/spec/requests/case_logs_controller_spec.rb index f34f7bb66..eef4744d4 100644 --- a/spec/requests/case_logs_controller_spec.rb +++ b/spec/requests/case_logs_controller_spec.rb @@ -778,7 +778,7 @@ RSpec.describe CaseLogsController, type: :request do it "dowloads searched logs" do get "/logs?search=#{case_log.id}", headers:, params: {} csv = CSV.parse(response.body) - expect(csv.count).to eq(2) + expect(csv.count).to eq(CaseLog.search_by(case_log.id.to_s).count + 1) end context "when both filter and search applied" do diff --git a/spec/requests/locations_controller_spec.rb b/spec/requests/locations_controller_spec.rb index c67b4358a..0f770ddb3 100644 --- a/spec/requests/locations_controller_spec.rb +++ b/spec/requests/locations_controller_spec.rb @@ -90,7 +90,8 @@ RSpec.describe LocationsController, type: :request do context "when signed in as a data coordinator" do let(:user) { FactoryBot.create(:user, :data_coordinator) } let!(:scheme) { FactoryBot.create(:scheme, owning_organisation: user.organisation) } - let(:params) { { location: { name: "Test", units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "No", postcode: "ZZ1 1ZZ" } } } + let(:startdate) { Time.utc(2022, 2, 2) } + let(:params) { { location: { name: "Test", units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "No", postcode: "ZZ1 1ZZ", startdate: } } } before do sign_in user @@ -111,6 +112,7 @@ RSpec.describe LocationsController, type: :request do expect(Location.last.units).to eq(5) expect(Location.last.type_of_unit).to eq("Bungalow") expect(Location.last.wheelchair_adaptation).to eq("No") + expect(Location.last.startdate).to eq(startdate) end context "when postcode is submitted with lower case" do @@ -389,8 +391,9 @@ RSpec.describe LocationsController, type: :request do context "when signed in as a data coordinator" do let(:user) { FactoryBot.create(:user, :data_coordinator) } let!(:scheme) { FactoryBot.create(:scheme, owning_organisation: user.organisation) } - let!(:location) { FactoryBot.create(:location, scheme:) } - let(:params) { { location: { name: "Test", units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "No", postcode: "ZZ1 1ZZ", page: "edit" } } } + let!(:location) { FactoryBot.create(:location, scheme:) } + let(:startdate) { Time.utc(2021, 1, 2) } + let(:params) { { location: { name: "Test", units: "5", type_of_unit: "Bungalow", wheelchair_adaptation: "No", add_another_location: "No", postcode: "ZZ1 1ZZ", startdate:, page: "edit" } } } before do sign_in user @@ -410,6 +413,7 @@ RSpec.describe LocationsController, type: :request do expect(Location.last.units).to eq(5) expect(Location.last.type_of_unit).to eq("Bungalow") expect(Location.last.wheelchair_adaptation).to eq("No") + expect(Location.last.startdate).to eq(startdate) end context "when updating from edit-name page" do diff --git a/spec/services/imports/case_logs_import_service_spec.rb b/spec/services/imports/case_logs_import_service_spec.rb index e44d15f31..27b6b975c 100644 --- a/spec/services/imports/case_logs_import_service_spec.rb +++ b/spec/services/imports/case_logs_import_service_spec.rb @@ -16,6 +16,9 @@ RSpec.describe Imports::CaseLogsImportService do end before do + WebMock.stub_request(:get, /api.postcodes.io\/postcodes\/LS166FT/) + .to_return(status: 200, body: '{"status":200,"result":{"codes":{"admin_district":"E08000035"}}}', headers: {}) + allow(Organisation).to receive(:find_by).and_return(nil) allow(Organisation).to receive(:find_by).with(old_visible_id: organisation.old_visible_id.to_i).and_return(organisation) @@ -23,12 +26,17 @@ RSpec.describe Imports::CaseLogsImportService do FactoryBot.create(:user, old_user_id: "c3061a2e6ea0b702e6f6210d5c52d2a92612d2aa", organisation:) FactoryBot.create(:user, old_user_id: "e29c492473446dca4d50224f2bb7cf965a261d6f", organisation:) + # Scheme and Location + scheme = FactoryBot.create(:scheme, old_visible_id: 123) + FactoryBot.create(:location, + old_visible_id: 10, + scheme_id: scheme.id, + wheelchair_adaptation: 1, + postcode: "LS166FT") + # Stub the form handler to use the real form allow(FormHandler.instance).to receive(:get_form).with("2021_2022").and_return(real_2021_2022_form) allow(FormHandler.instance).to receive(:get_form).with("2022_2023").and_return(real_2022_2023_form) - - WebMock.stub_request(:get, /api.postcodes.io\/postcodes\/LS166FT/) - .to_return(status: 200, body: '{"status":200,"result":{"codes":{"admin_district":"E08000035"}}}', headers: {}) end context "when importing case logs" do @@ -36,11 +44,12 @@ RSpec.describe Imports::CaseLogsImportService do let(:case_log_id) { "0ead17cb-1668-442d-898c-0d52879ff592" } let(:case_log_id2) { "166fc004-392e-47a8-acb8-1c018734882b" } let(:case_log_id3) { "00d2343e-d5fa-4c89-8400-ec3854b0f2b4" } + let(:case_log_id4) { "0b4a68df-30cc-474a-93c0-a56ce8fdad3b" } before do # Stub the S3 file listing and download allow(storage_service).to receive(:list_files) - .and_return(%W[#{remote_folder}/#{case_log_id}.xml #{remote_folder}/#{case_log_id2}.xml #{remote_folder}/#{case_log_id3}.xml]) + .and_return(%W[#{remote_folder}/#{case_log_id}.xml #{remote_folder}/#{case_log_id2}.xml #{remote_folder}/#{case_log_id3}.xml #{remote_folder}/#{case_log_id4}.xml]) allow(storage_service).to receive(:get_file_io) .with("#{remote_folder}/#{case_log_id}.xml") .and_return(open_file(fixture_directory, case_log_id), open_file(fixture_directory, case_log_id)) @@ -50,6 +59,9 @@ RSpec.describe Imports::CaseLogsImportService do allow(storage_service).to receive(:get_file_io) .with("#{remote_folder}/#{case_log_id3}.xml") .and_return(open_file(fixture_directory, case_log_id3), open_file(fixture_directory, case_log_id3)) + allow(storage_service).to receive(:get_file_io) + .with("#{remote_folder}/#{case_log_id4}.xml") + .and_return(open_file(fixture_directory, case_log_id4), open_file(fixture_directory, case_log_id4)) end it "successfully create all case logs" do @@ -57,45 +69,45 @@ RSpec.describe Imports::CaseLogsImportService do expect(logger).not_to receive(:warn) expect(logger).not_to receive(:info) expect { case_log_service.create_logs(remote_folder) } - .to change(CaseLog, :count).by(3) + .to change(CaseLog, :count).by(4) end it "only updates existing case logs" do expect(logger).not_to receive(:error) expect(logger).not_to receive(:warn) - expect(logger).to receive(:info).with(/Updating case log/).exactly(3).times + expect(logger).to receive(:info).with(/Updating case log/).exactly(4).times expect { 2.times { case_log_service.create_logs(remote_folder) } } - .to change(CaseLog, :count).by(3) + .to change(CaseLog, :count).by(4) end context "when there are status discrepancies" do - let(:case_log_id4) { "893ufj2s-lq77-42m4-rty6-ej09gh585uy1" } - let(:case_log_id5) { "5ybz29dj-l33t-k1l0-hj86-n4k4ma77xkcd" } - let(:case_log_file) { open_file(fixture_directory, case_log_id4) } + let(:case_log_id5) { "893ufj2s-lq77-42m4-rty6-ej09gh585uy1" } + let(:case_log_id6) { "5ybz29dj-l33t-k1l0-hj86-n4k4ma77xkcd" } + let(:case_log_file) { open_file(fixture_directory, case_log_id5) } let(:case_log_xml) { Nokogiri::XML(case_log_file) } before do - allow(storage_service).to receive(:get_file_io) - .with("#{remote_folder}/#{case_log_id4}.xml") - .and_return(open_file(fixture_directory, case_log_id4), open_file(fixture_directory, case_log_id4)) allow(storage_service).to receive(:get_file_io) .with("#{remote_folder}/#{case_log_id5}.xml") .and_return(open_file(fixture_directory, case_log_id5), open_file(fixture_directory, case_log_id5)) + allow(storage_service).to receive(:get_file_io) + .with("#{remote_folder}/#{case_log_id6}.xml") + .and_return(open_file(fixture_directory, case_log_id6), open_file(fixture_directory, case_log_id6)) end it "the logger logs a warning with the case log's old id/filename" do expect(logger).to receive(:warn).with(/is not completed/).once - expect(logger).to receive(:warn).with(/Case log with old id:#{case_log_id4} is incomplete but status should be complete/).once + expect(logger).to receive(:warn).with(/Case log with old id:#{case_log_id5} is incomplete but status should be complete/).once case_log_service.send(:create_log, case_log_xml) end it "on completion the ids of all logs with status discrepancies are logged in a warning" do allow(storage_service).to receive(:list_files) - .and_return(%W[#{remote_folder}/#{case_log_id4}.xml #{remote_folder}/#{case_log_id5}.xml]) - allow(logger).to receive(:warn).with(/is not completed/) - allow(logger).to receive(:warn).with(/is incomplete but status should be complete/) - expect(logger).to receive(:warn).with(/The following case logs had status discrepancies: \[893ufj2s-lq77-42m4-rty6-ej09gh585uy1, 5ybz29dj-l33t-k1l0-hj86-n4k4ma77xkcd\]/).once + .and_return(%W[#{remote_folder}/#{case_log_id5}.xml #{remote_folder}/#{case_log_id6}.xml]) + expect(logger).to receive(:warn).with(/is not completed/).twice + expect(logger).to receive(:warn).with(/is incomplete but status should be complete/).twice + expect(logger).to receive(:warn).with(/The following case logs had status discrepancies: \[893ufj2s-lq77-42m4-rty6-ej09gh585uy1, 5ybz29dj-l33t-k1l0-hj86-n4k4ma77xkcd\]/) case_log_service.create_logs(remote_folder) end @@ -111,8 +123,8 @@ RSpec.describe Imports::CaseLogsImportService do before { case_log_xml.at_xpath("//xmlns:VYEAR").content = 2023 } it "does not import the voiddate" do - allow(logger).to receive(:warn).with(/is not completed/) - allow(logger).to receive(:warn).with(/Case log with old id:#{case_log_id} is incomplete but status should be complete/) + expect(logger).to receive(:warn).with(/is not completed/) + expect(logger).to receive(:warn).with(/Case log with old id:#{case_log_id} is incomplete but status should be complete/) case_log_service.send(:create_log, case_log_xml) @@ -138,7 +150,7 @@ RSpec.describe Imports::CaseLogsImportService do it "sets the economic status to child under 16" do # The update is done when calculating derived variables - allow(logger).to receive(:warn).with(/Differences found when saving log/) + expect(logger).to receive(:warn).with(/Differences found when saving log/) case_log_service.send(:create_log, case_log_xml) case_log = CaseLog.where(old_id: case_log_id).first @@ -203,5 +215,19 @@ RSpec.describe Imports::CaseLogsImportService do end end end + + context "and this is a supported housing log" do + let(:case_log_id) { "0b4a68df-30cc-474a-93c0-a56ce8fdad3b" } + + it "sets the scheme and location values" do + expect(logger).not_to receive(:warn) + case_log_service.send(:create_log, case_log_xml) + case_log = CaseLog.find_by(old_id: case_log_id) + + expect(case_log.scheme_id).not_to be_nil + expect(case_log.location_id).not_to be_nil + expect(case_log.status).to eq("completed") + end + end end end diff --git a/spec/services/imports/scheme_import_service_spec.rb b/spec/services/imports/scheme_import_service_spec.rb index 3cfb76c96..9071007cc 100644 --- a/spec/services/imports/scheme_import_service_spec.rb +++ b/spec/services/imports/scheme_import_service_spec.rb @@ -60,5 +60,18 @@ RSpec.describe Imports::SchemeImportService do .not_to change(Scheme, :count) end end + + context "and the scheme arrange type is direct" do + before do + scheme_xml.at_xpath("//mgmtgroup:arrangement_type").content = "D" + scheme_xml.at_xpath("//mgmtgroup:agent").content = "" + end + + it "assigns both owning and managing organisation to the same one" do + scheme = scheme_service.create_scheme(scheme_xml) + expect(scheme.owning_organisation).to eq(owning_org) + expect(scheme.managing_organisation).to eq(owning_org) + end + end end end diff --git a/spec/services/imports/scheme_location_import_service_spec.rb b/spec/services/imports/scheme_location_import_service_spec.rb index f87fc3724..199424f54 100644 --- a/spec/services/imports/scheme_location_import_service_spec.rb +++ b/spec/services/imports/scheme_location_import_service_spec.rb @@ -134,9 +134,11 @@ RSpec.describe Imports::SchemeLocationImportService do expect(location.name).to eq("Location 1") expect(location.postcode).to eq("S44 6EJ") expect(location.units).to eq(5) + expect(location.mobility_type).to eq("Property fitted with equipment and adaptations (if not designed to above standards)") expect(location.type_of_unit).to eq("Bungalow") expect(location.old_id).to eq(first_location_id) expect(location.old_visible_id).to eq(10) + expect(location.startdate).to eq("1900-01-01") expect(location.scheme).to eq(scheme) end