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