Browse Source

CLDC-2072 changing startdate to another collection year clear invalid answers (#1469)

* write tests that invalid answers are cleared when the start date of a log is changed

* clear invalid answers when the start date of a log is changed

* correct error

* undo previous work to take new direction

* write tests to cover updated functionality of method in form to reset values when they are not routed to or when answers to radio questions are no longer valid

* update method in form to improve readability and to ensure that when the answers to radio questions are no longer valid, taht they are cleared

* add back an "end" deleted by mistake and fix a linting issue

* make some minor copy and variable name corrections

* fix a broken test after updates

* remove bulk upload tests for adding validations to unpermitted values to radio questions, these are now being cleared before validation

* fix some tests broken after rebasing

* add step to age questions in sales

* remove list of question ids from the form that should not be cleared, this information should be held on the questions themselves

* rename variables

* rename instance variable to be nice and long

* minor changes after rebase
pull/1563/head
Arthur Campbell 2 years ago committed by GitHub
parent
commit
6daa84b42e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 97
      app/models/form.rb
  2. 1
      app/models/form/lettings/questions/address_line1.rb
  3. 1
      app/models/form/lettings/questions/address_line2.rb
  4. 1
      app/models/form/lettings/questions/county.rb
  5. 1
      app/models/form/lettings/questions/la.rb
  6. 6
      app/models/form/lettings/questions/location_id.rb
  7. 1
      app/models/form/lettings/questions/postcode_for_full_address.rb
  8. 1
      app/models/form/lettings/questions/postcode_full.rb
  9. 1
      app/models/form/lettings/questions/postcode_known.rb
  10. 1
      app/models/form/lettings/questions/ppcodenk.rb
  11. 1
      app/models/form/lettings/questions/ppostcode_full.rb
  12. 1
      app/models/form/lettings/questions/previous_la_known.rb
  13. 1
      app/models/form/lettings/questions/prevloc.rb
  14. 1
      app/models/form/lettings/questions/town_or_city.rb
  15. 3
      app/models/form/question.rb
  16. 3
      app/models/form/sales/pages/extra_borrowing_value_check.rb
  17. 1
      app/models/form/sales/questions/address_line1.rb
  18. 1
      app/models/form/sales/questions/address_line2.rb
  19. 1
      app/models/form/sales/questions/age2.rb
  20. 1
      app/models/form/sales/questions/county.rb
  21. 1
      app/models/form/sales/questions/person_age.rb
  22. 1
      app/models/form/sales/questions/postcode.rb
  23. 1
      app/models/form/sales/questions/postcode_for_full_address.rb
  24. 1
      app/models/form/sales/questions/previous_la_known.rb
  25. 1
      app/models/form/sales/questions/previous_postcode.rb
  26. 1
      app/models/form/sales/questions/previous_postcode_known.rb
  27. 1
      app/models/form/sales/questions/prevloc.rb
  28. 1
      app/models/form/sales/questions/property_local_authority.rb
  29. 1
      app/models/form/sales/questions/town_or_city.rb
  30. 2
      app/models/log.rb
  31. 10
      app/views/form/_checkbox_question.html.erb
  32. 7
      config/forms/2021_2022.json
  33. 7
      config/forms/2022_2023.json
  34. 2
      config/locales/en.yml
  35. 6
      spec/factories/lettings_log.rb
  36. 24
      spec/factories/sales_log.rb
  37. 6
      spec/fixtures/forms/2021_2022.json
  38. 4
      spec/models/form/sales/questions/age2_spec.rb
  39. 4
      spec/models/form/sales/questions/person_age_spec.rb
  40. 2
      spec/models/form/sales/questions/uprn_confirmation_spec.rb
  41. 140
      spec/models/form_spec.rb
  42. 6
      spec/models/sales_log_spec.rb
  43. 18
      spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb
  44. 16
      spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb

97
app/models/form.rb

@ -178,48 +178,78 @@ class Form
pages.reject { |p| p.routed_to?(log, current_user) } pages.reject { |p| p.routed_to?(log, current_user) }
end end
def invalidated_questions(log) def reset_not_routed_questions_and_invalid_answers(log)
invalidated_page_questions(log) + invalidated_conditional_questions(log) reset_checkbox_questions_if_not_routed(log)
end
def invalidated_page_questions(log, current_user = nil)
# we're already treating these fields as a special case and reset their values upon saving a log
callback_questions = %w[postcode_known la ppcodenk previous_la_known prevloc postcode_full ppostcode_full location_id address_line1 address_line2 town_or_city county]
questions.reject { |q| q.page.routed_to?(log, current_user) || q.derived? || callback_questions.include?(q.id) } || []
end
def reset_not_routed_questions(log) reset_radio_questions_if_not_routed_or_invalid_answers(log)
enabled_questions = enabled_page_questions(log)
enabled_question_ids = enabled_questions.map(&:id)
invalidated_page_questions(log).each do |question| reset_free_user_input_questions_if_not_routed(log)
if %w[radio checkbox].include?(question.type) end
enabled_answer_options = enabled_question_ids.include?(question.id) ? enabled_questions.find { |q| q.id == question.id }.answer_options : {}
current_answer_option_valid = enabled_answer_options.present? ? enabled_answer_options.key?(log.public_send(question.id).to_s) : false
if !current_answer_option_valid && log.respond_to?(question.id.to_s) def reset_checkbox_questions_if_not_routed(log)
Rails.logger.debug("Cleared #{question.id} value") checkbox_questions = routed_and_not_routed_questions_by_type(log, type: "checkbox")
log.public_send("#{question.id}=", nil) checkbox_questions[:not_routed].each do |not_routed_question|
valid_options = checkbox_questions[:routed]
.select { |q| q.id == not_routed_question.id }
.flat_map { |q| q.answer_options.keys }
not_routed_question.answer_options.each_key do |invalid_option|
if !log.respond_to?(invalid_option) || valid_options.include?(invalid_option) || log.public_send(invalid_option).nil?
next
else else
clear_attribute(log, invalid_option)
(question.answer_options.keys - enabled_answer_options.keys).map do |invalid_answer_option|
Rails.logger.debug("Cleared #{invalid_answer_option} value")
log.public_send("#{invalid_answer_option}=", nil) if log.respond_to?(invalid_answer_option)
end
end end
end
end
end
def reset_radio_questions_if_not_routed_or_invalid_answers(log)
radio_questions = routed_and_not_routed_questions_by_type(log, type: "radio")
valid_radio_options = radio_questions[:routed]
.group_by(&:id)
.transform_values! { |q_array| q_array.flat_map { |q| q.answer_options.keys } }
radio_questions[:not_routed].each do |not_routed_question|
question_id = not_routed_question.id
if !log.respond_to?(question_id) || log.public_send(question_id).nil? || valid_radio_options.key?(question_id)
next
else
clear_attribute(log, question_id)
end
end
valid_radio_options.each do |question_id, valid_options|
if !log.respond_to?(question_id) || valid_options.include?(log.public_send(question_id).to_s)
next
else
clear_attribute(log, question_id)
end
end
end
def reset_free_user_input_questions_if_not_routed(log)
non_radio_checkbox_questions = routed_and_not_routed_questions_by_type(log)
enabled_question_ids = non_radio_checkbox_questions[:routed].map(&:id)
non_radio_checkbox_questions[:not_routed].each do |not_routed_question|
question_id = not_routed_question.id
if log.public_send(question_id).nil? || enabled_question_ids.include?(question_id)
next
else else
Rails.logger.debug("Cleared #{question.id} value") clear_attribute(log, question_id)
log.public_send("#{question.id}=", nil) unless enabled_question_ids.include?(question.id)
end end
end end
end end
def enabled_page_questions(log) def routed_and_not_routed_questions_by_type(log, type: nil, current_user: nil)
questions - invalidated_page_questions(log) questions_by_type = if type
questions.reject { |q| q.type != type || q.disable_clearing_if_not_routed_or_dynamic_answer_options }
else
questions.reject { |q| %w[radio checkbox].include?(q.type) || q.disable_clearing_if_not_routed_or_dynamic_answer_options }
end
routed, not_routed = questions_by_type.partition { |q| q.page.routed_to?(log, current_user) || q.derived? }
{ routed:, not_routed: }
end end
def invalidated_conditional_questions(log) def clear_attribute(log, attribute)
questions.reject { |q| q.enabled?(log) } || [] Rails.logger.debug("Cleared #{attribute} value")
log.public_send("#{attribute}=", nil)
end end
def readonly_questions def readonly_questions
@ -230,6 +260,13 @@ class Form
questions.select { |q| q.type == "numeric" } questions.select { |q| q.type == "numeric" }
end end
def previous_page(page_ids, page_index, log, current_user)
prev_page = get_page(page_ids[page_index - 1])
return prev_page.id if prev_page.routed_to?(log, current_user)
previous_page(page_ids, page_index - 1, log, current_user)
end
def send_chain(arr, log) def send_chain(arr, log)
Array(arr).inject(log) { |o, a| o.public_send(*a) } Array(arr).inject(log) { |o, a| o.public_send(*a) }
end end

1
app/models/form/lettings/questions/address_line1.rb

@ -7,6 +7,7 @@ class Form::Lettings::Questions::AddressLine1 < ::Form::Question
@type = "text" @type = "text"
@plain_label = true @plain_label = true
@check_answer_label = "Q12 - Address" @check_answer_label = "Q12 - Address"
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end end
def answer_label(log, _current_user = nil) def answer_label(log, _current_user = nil)

1
app/models/form/lettings/questions/address_line2.rb

@ -5,6 +5,7 @@ class Form::Lettings::Questions::AddressLine2 < ::Form::Question
@header = "Address line 2 (optional)" @header = "Address line 2 (optional)"
@type = "text" @type = "text"
@plain_label = true @plain_label = true
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end end
def hidden_in_check_answers?(_log = nil, _current_user = nil) def hidden_in_check_answers?(_log = nil, _current_user = nil)

1
app/models/form/lettings/questions/county.rb

@ -5,6 +5,7 @@ class Form::Lettings::Questions::County < ::Form::Question
@header = "County (optional)" @header = "County (optional)"
@type = "text" @type = "text"
@plain_label = true @plain_label = true
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end end
def hidden_in_check_answers?(_log = nil, _current_user = nil) def hidden_in_check_answers?(_log = nil, _current_user = nil)

1
app/models/form/lettings/questions/la.rb

@ -8,6 +8,7 @@ class Form::Lettings::Questions::La < ::Form::Question
@check_answers_card_number = 0 @check_answers_card_number = 0
@hint_text = "" @hint_text = ""
@question_number = 13 @question_number = 13
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end end
def answer_options def answer_options

6
app/models/form/lettings/questions/location_id.rb

@ -1,6 +1,7 @@
class Form::Lettings::Questions::LocationId < ::Form::Question class Form::Lettings::Questions::LocationId < ::Form::Question
def initialize(_id, hsh, page) def initialize(id, hsh, page)
super("location_id", hsh, page) super
@id = "location_id"
@check_answer_label = "Location" @check_answer_label = "Location"
@header = header_text @header = header_text
@type = "radio" @type = "radio"
@ -11,6 +12,7 @@ class Form::Lettings::Questions::LocationId < ::Form::Question
}, },
} }
@question_number = 10 @question_number = 10
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end end
def answer_options def answer_options

1
app/models/form/lettings/questions/postcode_for_full_address.rb

@ -17,6 +17,7 @@ class Form::Lettings::Questions::PostcodeForFullAddress < ::Form::Question
}, },
} }
@plain_label = true @plain_label = true
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end end
def hidden_in_check_answers?(_log = nil, _current_user = nil) def hidden_in_check_answers?(_log = nil, _current_user = nil)

1
app/models/form/lettings/questions/postcode_full.rb

@ -10,5 +10,6 @@ class Form::Lettings::Questions::PostcodeFull < ::Form::Question
@check_answers_card_number = 0 @check_answers_card_number = 0
@hint_text = "" @hint_text = ""
@inferred_answers = { "la" => { "is_la_inferred" => true } } @inferred_answers = { "la" => { "is_la_inferred" => true } }
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end end
end end

1
app/models/form/lettings/questions/postcode_known.rb

@ -8,6 +8,7 @@ class Form::Lettings::Questions::PostcodeKnown < ::Form::Question
@check_answers_card_number = 0 @check_answers_card_number = 0
@hint_text = "" @hint_text = ""
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
@conditional_for = { "postcode_full" => [1] } @conditional_for = { "postcode_full" => [1] }
@hidden_in_check_answers = { "depends_on" => [{ "postcode_known" => 0 }, { "postcode_known" => 1 }] } @hidden_in_check_answers = { "depends_on" => [{ "postcode_known" => 0 }, { "postcode_known" => 1 }] }
end end

1
app/models/form/lettings/questions/ppcodenk.rb

@ -11,6 +11,7 @@ class Form::Lettings::Questions::Ppcodenk < ::Form::Question
@conditional_for = { "ppostcode_full" => [1] } @conditional_for = { "ppostcode_full" => [1] }
@hidden_in_check_answers = { "depends_on" => [{ "ppcodenk" => 0 }, { "ppcodenk" => 1 }] } @hidden_in_check_answers = { "depends_on" => [{ "ppcodenk" => 0 }, { "ppcodenk" => 1 }] }
@question_number = 80 @question_number = 80
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end end
ANSWER_OPTIONS = { "1" => { "value" => "Yes" }, "0" => { "value" => "No" } }.freeze ANSWER_OPTIONS = { "1" => { "value" => "Yes" }, "0" => { "value" => "No" } }.freeze

1
app/models/form/lettings/questions/ppostcode_full.rb

@ -11,5 +11,6 @@ class Form::Lettings::Questions::PpostcodeFull < ::Form::Question
@hint_text = "" @hint_text = ""
@inferred_answers = { "prevloc" => { "is_previous_la_inferred" => true } } @inferred_answers = { "prevloc" => { "is_previous_la_inferred" => true } }
@question_number = 80 @question_number = 80
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end end
end end

1
app/models/form/lettings/questions/previous_la_known.rb

@ -11,6 +11,7 @@ class Form::Lettings::Questions::PreviousLaKnown < ::Form::Question
@conditional_for = { "prevloc" => [1] } @conditional_for = { "prevloc" => [1] }
@hidden_in_check_answers = { "depends_on" => [{ "previous_la_known" => 0 }, { "previous_la_known" => 1 }] } @hidden_in_check_answers = { "depends_on" => [{ "previous_la_known" => 0 }, { "previous_la_known" => 1 }] }
@question_number = 81 @question_number = 81
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end end
ANSWER_OPTIONS = { "1" => { "value" => "Yes" }, "0" => { "value" => "No" } }.freeze ANSWER_OPTIONS = { "1" => { "value" => "Yes" }, "0" => { "value" => "No" } }.freeze

1
app/models/form/lettings/questions/prevloc.rb

@ -9,6 +9,7 @@ class Form::Lettings::Questions::Prevloc < ::Form::Question
@check_answers_card_number = 0 @check_answers_card_number = 0
@hint_text = "Select ‘Northern Ireland’, ‘Scotland’, ‘Wales’ or ‘Outside the UK’ if the household’s last settled home was outside England." @hint_text = "Select ‘Northern Ireland’, ‘Scotland’, ‘Wales’ or ‘Outside the UK’ if the household’s last settled home was outside England."
@question_number = 81 @question_number = 81
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end end
def answer_options def answer_options

1
app/models/form/lettings/questions/town_or_city.rb

@ -5,6 +5,7 @@ class Form::Lettings::Questions::TownOrCity < ::Form::Question
@header = "Town or city" @header = "Town or city"
@type = "text" @type = "text"
@plain_label = true @plain_label = true
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end end
def hidden_in_check_answers?(_log = nil, _current_user = nil) def hidden_in_check_answers?(_log = nil, _current_user = nil)

3
app/models/form/question.rb

@ -1,5 +1,5 @@
class Form::Question class Form::Question
attr_accessor :id, :header, :hint_text, :description, :questions, attr_accessor :id, :header, :hint_text, :description, :questions, :disable_clearing_if_not_routed_or_dynamic_answer_options,
:type, :min, :max, :step, :width, :fields_to_add, :result_field, :type, :min, :max, :step, :width, :fields_to_add, :result_field,
:conditional_for, :readonly, :answer_options, :page, :check_answer_label, :conditional_for, :readonly, :answer_options, :page, :check_answer_label,
:inferred_answers, :hidden_in_check_answers, :inferred_check_answers_value, :inferred_answers, :hidden_in_check_answers, :inferred_check_answers_value,
@ -42,6 +42,7 @@ class Form::Question
@unresolved_hint_text = hsh["unresolved_hint_text"] @unresolved_hint_text = hsh["unresolved_hint_text"]
@question_number = hsh["question_number"] @question_number = hsh["question_number"]
@plain_label = hsh["plain_label"] @plain_label = hsh["plain_label"]
@disable_clearing_if_not_routed_or_dynamic_answer_options = hsh["disable_clearing_if_not_routed_or_dynamic_answer_options"]
end end
end end

3
app/models/form/sales/pages/extra_borrowing_value_check.rb

@ -9,8 +9,7 @@ class Form::Sales::Pages::ExtraBorrowingValueCheck < Form::Page
@title_text = { @title_text = {
"translation" => "soft_validations.extra_borrowing.title", "translation" => "soft_validations.extra_borrowing.title",
} }
@informative_text = { @informative_text = {}
}
end end
def questions def questions

1
app/models/form/sales/questions/address_line1.rb

@ -7,6 +7,7 @@ class Form::Sales::Questions::AddressLine1 < ::Form::Question
@type = "text" @type = "text"
@plain_label = true @plain_label = true
@check_answer_label = "Q15 - Address" @check_answer_label = "Q15 - Address"
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end end
def answer_label(log, _current_user = nil) def answer_label(log, _current_user = nil)

1
app/models/form/sales/questions/address_line2.rb

@ -5,6 +5,7 @@ class Form::Sales::Questions::AddressLine2 < ::Form::Question
@header = "Address line 2 (optional)" @header = "Address line 2 (optional)"
@type = "text" @type = "text"
@plain_label = true @plain_label = true
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end end
def hidden_in_check_answers?(_log = nil, _current_user = nil) def hidden_in_check_answers?(_log = nil, _current_user = nil)

1
app/models/form/sales/questions/age2.rb

@ -13,6 +13,7 @@ class Form::Sales::Questions::Age2 < ::Form::Question
@check_answers_card_number = 2 @check_answers_card_number = 2
@max = 110 @max = 110
@min = 0 @min = 0
@step = 1
@question_number = 28 @question_number = 28
end end
end end

1
app/models/form/sales/questions/county.rb

@ -5,6 +5,7 @@ class Form::Sales::Questions::County < ::Form::Question
@header = "County (optional)" @header = "County (optional)"
@type = "text" @type = "text"
@plain_label = true @plain_label = true
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end end
def hidden_in_check_answers?(_log = nil, _current_user = nil) def hidden_in_check_answers?(_log = nil, _current_user = nil)

1
app/models/form/sales/questions/person_age.rb

@ -12,6 +12,7 @@ class Form::Sales::Questions::PersonAge < ::Form::Question
@check_answers_card_number = person_index @check_answers_card_number = person_index
@min = 0 @min = 0
@max = 110 @max = 110
@step = 1
@question_number = 29 + (4 * person_index) @question_number = 29 + (4 * person_index)
end end
end end

1
app/models/form/sales/questions/postcode.rb

@ -17,5 +17,6 @@ class Form::Sales::Questions::Postcode < ::Form::Question
"is_la_inferred" => true, "is_la_inferred" => true,
}, },
} }
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end end
end end

1
app/models/form/sales/questions/postcode_for_full_address.rb

@ -17,6 +17,7 @@ class Form::Sales::Questions::PostcodeForFullAddress < ::Form::Question
}, },
} }
@plain_label = true @plain_label = true
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end end
def hidden_in_check_answers?(_log = nil, _current_user = nil) def hidden_in_check_answers?(_log = nil, _current_user = nil)

1
app/models/form/sales/questions/previous_la_known.rb

@ -21,6 +21,7 @@ class Form::Sales::Questions::PreviousLaKnown < ::Form::Question
"prevloc" => [1], "prevloc" => [1],
} }
@question_number = 58 @question_number = 58
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end end
ANSWER_OPTIONS = { ANSWER_OPTIONS = {

1
app/models/form/sales/questions/previous_postcode.rb

@ -18,5 +18,6 @@ class Form::Sales::Questions::PreviousPostcode < ::Form::Question
}, },
} }
@question_number = 57 @question_number = 57
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end end
end end

1
app/models/form/sales/questions/previous_postcode_known.rb

@ -21,6 +21,7 @@ class Form::Sales::Questions::PreviousPostcodeKnown < ::Form::Question
], ],
} }
@question_number = 57 @question_number = 57
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end end
ANSWER_OPTIONS = { ANSWER_OPTIONS = {

1
app/models/form/sales/questions/prevloc.rb

@ -12,6 +12,7 @@ class Form::Sales::Questions::Prevloc < ::Form::Question
"value" => "Not known", "value" => "Not known",
}] }]
@question_number = 58 @question_number = 58
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end end
def answer_options def answer_options

1
app/models/form/sales/questions/property_local_authority.rb

@ -6,6 +6,7 @@ class Form::Sales::Questions::PropertyLocalAuthority < ::Form::Question
@header = "What is the property’s local authority?" @header = "What is the property’s local authority?"
@type = "select" @type = "select"
@question_number = 16 @question_number = 16
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end end
def answer_options def answer_options

1
app/models/form/sales/questions/town_or_city.rb

@ -5,6 +5,7 @@ class Form::Sales::Questions::TownOrCity < ::Form::Question
@header = "Town or city" @header = "Town or city"
@type = "text" @type = "text"
@plain_label = true @plain_label = true
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end end
def hidden_in_check_answers?(_log = nil, _current_user = nil) def hidden_in_check_answers?(_log = nil, _current_user = nil)

2
app/models/log.rb

@ -174,7 +174,7 @@ private
def reset_invalidated_dependent_fields! def reset_invalidated_dependent_fields!
return unless form return unless form
form.reset_not_routed_questions(self) form.reset_not_routed_questions_and_invalid_answers(self)
reset_created_by! reset_created_by!
end end

10
app/views/form/_checkbox_question.html.erb

@ -6,14 +6,14 @@
hint: { text: question.hint_text&.html_safe } do %> hint: { text: question.hint_text&.html_safe } do %>
<% after_divider = false %> <% after_divider = false %>
<% question.displayed_answer_options(@log).map do |key, options| %> <% question.displayed_answer_options(@log).map do |key, option| %>
<% if key.starts_with?("divider") %> <% if key.starts_with?("divider") %>
<% after_divider = true %> <% after_divider = true %>
<%= f.govuk_check_box_divider %> <%= f.govuk_check_box_divider %>
<% else %> <% else %>
<%= f.govuk_check_box question.id, key, <%= f.govuk_check_box question.id, key,
label: { text: options["value"] }, label: { text: option["value"] },
hint: { text: options["hint"] }, hint: { text: option["hint"] },
checked: @log[key] == 1, checked: @log[key] == 1,
exclusive: after_divider, exclusive: after_divider,
**stimulus_html_attributes(question) %> **stimulus_html_attributes(question) %>

7
config/forms/2021_2022.json

@ -24,6 +24,7 @@
"header": "Do you know the property’s postcode?", "header": "Do you know the property’s postcode?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"answer_options": { "answer_options": {
"1": { "1": {
"value": "Yes" "value": "Yes"
@ -54,6 +55,7 @@
"hint_text": "", "hint_text": "",
"type": "text", "type": "text",
"width": 5, "width": 5,
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"inferred_answers": { "inferred_answers": {
"la": { "la": {
"is_la_inferred": true "is_la_inferred": true
@ -82,6 +84,7 @@
"header": "What is the local authority of the property?", "header": "What is the local authority of the property?",
"hint_text": "", "hint_text": "",
"type": "select", "type": "select",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"answer_options": { "answer_options": {
"": "Select an option", "": "Select an option",
"E07000223": "Adur", "E07000223": "Adur",
@ -6482,6 +6485,7 @@
"header": "Do you know the postcode of the household’s last settled accommodation?", "header": "Do you know the postcode of the household’s last settled accommodation?",
"hint_text": "This is also known as the household’s ‘last settled home’.", "hint_text": "This is also known as the household’s ‘last settled home’.",
"type": "radio", "type": "radio",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"answer_options": { "answer_options": {
"1": { "1": {
"value": "Yes" "value": "Yes"
@ -6512,6 +6516,7 @@
"hint_text": "", "hint_text": "",
"type": "text", "type": "text",
"width": 5, "width": 5,
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"inferred_answers": { "inferred_answers": {
"prevloc": { "prevloc": {
"is_previous_la_inferred": true "is_previous_la_inferred": true
@ -6535,6 +6540,7 @@
"header": "Do you know the local authority of the household’s last settled accommodation?", "header": "Do you know the local authority of the household’s last settled accommodation?",
"hint_text": "This is also known as the household’s ‘last settled home’.", "hint_text": "This is also known as the household’s ‘last settled home’.",
"type": "radio", "type": "radio",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"hidden_in_check_answers": { "hidden_in_check_answers": {
"depends_on": [ "depends_on": [
{ {
@ -6564,6 +6570,7 @@
"header": "Select a local authority", "header": "Select a local authority",
"hint_text": "Select ‘Northern Ireland’, ‘Scotland’, ‘Wales’ or ‘Outside the UK’ if the household’s last settled home was outside England.", "hint_text": "Select ‘Northern Ireland’, ‘Scotland’, ‘Wales’ or ‘Outside the UK’ if the household’s last settled home was outside England.",
"type": "select", "type": "select",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"answer_options": { "answer_options": {
"": "Select an option", "": "Select an option",
"S12000033": "Aberdeen City", "S12000033": "Aberdeen City",

7
config/forms/2022_2023.json

@ -24,6 +24,7 @@
"header": "Do you know the property’s postcode?", "header": "Do you know the property’s postcode?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"answer_options": { "answer_options": {
"1": { "1": {
"value": "Yes" "value": "Yes"
@ -54,6 +55,7 @@
"hint_text": "", "hint_text": "",
"type": "text", "type": "text",
"width": 5, "width": 5,
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"inferred_answers": { "inferred_answers": {
"la": { "la": {
"is_la_inferred": true "is_la_inferred": true
@ -82,6 +84,7 @@
"header": "What is the local authority of the property?", "header": "What is the local authority of the property?",
"hint_text": "", "hint_text": "",
"type": "select", "type": "select",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"answer_options": { "answer_options": {
"": "Select an option", "": "Select an option",
"E07000223": "Adur", "E07000223": "Adur",
@ -6435,6 +6438,7 @@
"header": "Do you know the postcode of the household’s last settled accommodation?", "header": "Do you know the postcode of the household’s last settled accommodation?",
"hint_text": "This is also known as the household’s ‘last settled home’.", "hint_text": "This is also known as the household’s ‘last settled home’.",
"type": "radio", "type": "radio",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"answer_options": { "answer_options": {
"1": { "1": {
"value": "Yes" "value": "Yes"
@ -6465,6 +6469,7 @@
"hint_text": "", "hint_text": "",
"type": "text", "type": "text",
"width": 5, "width": 5,
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"inferred_answers": { "inferred_answers": {
"prevloc": { "prevloc": {
"is_previous_la_inferred": true "is_previous_la_inferred": true
@ -6488,6 +6493,7 @@
"header": "Do you know the local authority of the household’s last settled accommodation?", "header": "Do you know the local authority of the household’s last settled accommodation?",
"hint_text": "This is also known as the household’s ‘last settled home’.", "hint_text": "This is also known as the household’s ‘last settled home’.",
"type": "radio", "type": "radio",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"hidden_in_check_answers": { "hidden_in_check_answers": {
"depends_on": [ "depends_on": [
{ {
@ -6517,6 +6523,7 @@
"header": "Select a local authority", "header": "Select a local authority",
"hint_text": "Select ‘Northern Ireland’, ‘Scotland’, ‘Wales’ or ‘Outside the UK’ if the household’s last settled home was outside England.", "hint_text": "Select ‘Northern Ireland’, ‘Scotland’, ‘Wales’ or ‘Outside the UK’ if the household’s last settled home was outside England.",
"type": "select", "type": "select",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"answer_options": { "answer_options": {
"": "Select an option", "": "Select an option",
"S12000033": "Aberdeen City", "S12000033": "Aberdeen City",

2
config/locales/en.yml

@ -365,7 +365,7 @@ en:
over_20: "The lead tenant must be under 20 as you told us their housing situation immediately before this letting was a children’s home or foster care" over_20: "The lead tenant must be under 20 as you told us their housing situation immediately before this letting was a children’s home or foster care"
ecstat: ecstat:
retired_over_70: "Person %{person_num} must be retired if over 70" retired_over_70: "Person %{person_num} must be retired if over 70"
child_under_16: "Person’s %{person_num} working situation must be ’child under 16‘ as you told us they’re under 16" child_under_16: "Person %{person_num}’s working situation must be ‘child under 16’ as you told us they’re under 16"
child_over_16: "Answer cannot be ‘child under 16’ as you told us the person %{person_num} is older than 16" child_over_16: "Answer cannot be ‘child under 16’ as you told us the person %{person_num} is older than 16"
not_student_16_19: "Person’s %{person_num} working situation must be full-time student or prefers not to say as you told us they’re between 16 and 19." not_student_16_19: "Person’s %{person_num} working situation must be full-time student or prefers not to say as you told us they’re between 16 and 19."
student_16_19: student_16_19:

6
spec/factories/lettings_log.rb

@ -158,6 +158,12 @@ FactoryBot.define do
sheltered { 0 } sheltered { 0 }
household_charge { 0 } household_charge { 0 }
end end
trait :sheltered_housing do
needstype { 2 }
end
trait :startdate_today do
startdate { Time.zone.today }
end
created_at { Time.zone.today } created_at { Time.zone.today }
updated_at { Time.zone.today } updated_at { Time.zone.today }
end end

24
spec/factories/sales_log.rb

@ -10,6 +10,30 @@ FactoryBot.define do
type { 8 } type { 8 }
saledate { Time.utc(2023, 2, 2, 10, 36, 49) } saledate { Time.utc(2023, 2, 2, 10, 36, 49) }
end end
trait :shared_ownership do
ownershipsch { 1 }
type { 30 }
end
trait :privacy_notice_seen do
privacynotice { 1 }
end
trait :saledate_today do
saledate { Time.zone.today }
end
trait :shared_ownership_setup_complete do
saledate_today
ownershipsch { 1 }
type { 30 }
jointpur { 2 }
end
trait :outright_sale_setup_complete do
saledate_today
ownershipsch { 3 }
type { 10 }
companybuy { 2 }
buylivein { 1 }
jointpur { 2 }
end
trait :completed do trait :completed do
ownershipsch { 2 } ownershipsch { 2 }
type { 8 } type { 8 }

6
spec/fixtures/forms/2021_2022.json vendored

@ -411,6 +411,7 @@
"hint_text": "Type ahead to filter the options", "hint_text": "Type ahead to filter the options",
"type": "select", "type": "select",
"check_answer_label": "Accessible Select", "check_answer_label": "Accessible Select",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"answer_options": { "answer_options": {
"": "Select an option", "": "Select an option",
"E07000223": "Adur", "E07000223": "Adur",
@ -473,6 +474,7 @@
"hint_text": "Type ahead to filter the options", "hint_text": "Type ahead to filter the options",
"type": "select", "type": "select",
"check_answer_label": "Accessible Select", "check_answer_label": "Accessible Select",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"answer_options": { "answer_options": {
"": "Select an option", "": "Select an option",
"E07000223": "Adur", "E07000223": "Adur",
@ -500,6 +502,7 @@
"header": "Do you know the property’s postcode?", "header": "Do you know the property’s postcode?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"answer_options": { "answer_options": {
"1": { "1": {
"value": "Yes" "value": "Yes"
@ -521,6 +524,7 @@
"hint_text": "", "hint_text": "",
"type": "text", "type": "text",
"width": 5, "width": 5,
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"inferred_answers": { "inferred_answers": {
"la": { "la": {
"is_la_inferred": true "is_la_inferred": true
@ -544,6 +548,7 @@
"header": "Do you know what local authority the property is located in?", "header": "Do you know what local authority the property is located in?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"answer_options": { "answer_options": {
"0": { "0": {
"value": "No" "value": "No"
@ -1151,6 +1156,7 @@
"header": "Postcode for the previous accommodation", "header": "Postcode for the previous accommodation",
"hint_text": "If the household has moved from settled accommodation immediately prior to being re-housed", "hint_text": "If the household has moved from settled accommodation immediately prior to being re-housed",
"type": "text", "type": "text",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"width": 5 "width": 5
} }
} }

4
spec/models/form/sales/questions/age2_spec.rb

@ -59,4 +59,8 @@ RSpec.describe Form::Sales::Questions::Age2, type: :model do
it "has the correct max" do it "has the correct max" do
expect(question.max).to eq(110) expect(question.max).to eq(110)
end end
it "has the correct step" do
expect(question.step).to be 1
end
end end

4
spec/models/form/sales/questions/person_age_spec.rb

@ -40,6 +40,10 @@ RSpec.describe Form::Sales::Questions::PersonAge, type: :model do
expect(question.width).to eq(3) expect(question.width).to eq(3)
end end
it "has the correct step" do
expect(question.step).to be 1
end
context "with person 2" do context "with person 2" do
let(:person_index) { 2 } let(:person_index) { 2 }
let(:question_id) { "age2" } let(:question_id) { "age2" }

2
spec/models/form/sales/questions/uprn_confirmation_spec.rb

@ -50,7 +50,7 @@ RSpec.describe Form::Sales::Questions::UprnConfirmation, type: :model do
context "when address is present" do context "when address is present" do
it "returns formatted value" do it "returns formatted value" do
log = create(:sales_log, address_line1: "1, Test Street", town_or_city: "Test Town", county: "Test County", postcode_full: "AA1 1AA", uprn: "1234", uprn_known: 1) log = build(:sales_log, :outright_sale_setup_complete, address_line1: "1, Test Street", town_or_city: "Test Town", county: "Test County", postcode_full: "AA1 1AA", uprn: "1234", uprn_known: 1)
expect(question.notification_banner(log)).to eq( expect(question.notification_banner(log)).to eq(
{ {

140
spec/models/form_spec.rb

@ -204,32 +204,140 @@ RSpec.describe Form, type: :model do
end end
end end
describe "invalidated_page_questions" do describe "#reset_not_routed_questions_and_invalid_answers" do
let(:lettings_log) { FactoryBot.create(:lettings_log, :in_progress, needstype: 1) } around do |example|
let(:expected_invalid) { %w[scheme_id retirement_value_check condition_effects cbl conditional_question_no_second_question net_income_value_check dependent_question offered layear declaration] } Singleton.__init__(FormHandler)
Timecop.freeze(now) do
FormHandler.instance.use_real_forms!
example.run
end
FormHandler.instance.use_fake_forms!
end
let(:now) { Time.zone.local(2023, 5, 5) }
context "when there are multiple radio questions for attribute X" do
context "and attribute Y is changed such that a different question for X is routed to" do
let(:log) { FactoryBot.create(:lettings_log, :setup_completed, :sheltered_housing, startdate: now, renewal: 0, prevten:) }
context "and the value of X remains valid" do
let(:prevten) { 36 }
context "when dependencies are not met" do it "the value of this attribute is not cleared" do
it "returns an array of question keys whose pages conditions are not met" do log.renewal = 1
expect(form.invalidated_page_questions(lettings_log).map(&:id).uniq).to eq(expected_invalid) log.form.reset_not_routed_questions_and_invalid_answers(log)
expect(log.prevten).to be 36
end
end
context "and the value of X is now invalid" do
let(:prevten) { 30 }
it "the value of this attribute is cleared" do
log.renewal = 1
log.form.reset_not_routed_questions_and_invalid_answers(log)
expect(log.prevten).to be nil
end
end
end end
end end
context "with two pages having the same question and only one has dependencies met" do context "when there is one radio question for attribute X" do
it "returns an array of question keys whose pages conditions are not met" do context "and the start date or sale date is changed such that the collection year changes and there are different options" do
lettings_log["preg_occ"] = "No" let(:log) { FactoryBot.create(:lettings_log, :setup_completed, :sheltered_housing, startdate: now, sheltered:) }
expect(form.invalidated_page_questions(lettings_log).map(&:id).uniq).to eq(expected_invalid)
context "and the value of X remains valid" do
let(:sheltered) { 2 }
it "the value of this attribute is not cleared" do
log.update!(startdate: Time.zone.local(2023, 1, 1))
expect(log.sheltered).to be 2
end
end
context "and the value of X is now invalid" do
let(:sheltered) { 5 }
it "the value of this attribute is cleared" do
log.update!(startdate: Time.zone.local(2023, 1, 1))
expect(log.sheltered).to be nil
end
end
end end
end end
context "when a question is marked as `derived` and `depends_on: false`" do context "when there is one free user input question for an attribute X" do
let(:lettings_log) { FactoryBot.build(:lettings_log, :in_progress, startdate: Time.utc(2022, 4, 2, 10, 36, 49)) } let(:log) { FactoryBot.create(:sales_log, :shared_ownership_setup_complete, staircase: 1, stairbought: 25) }
it "does not count it's questions as invalidated" do context "and attribute Y is changed such that it is no longer routed to" do
expect(form.enabled_page_questions(lettings_log).map(&:id).uniq).to include("tshortfall_known") it "the value of this attribute is cleared" do
expect(log.stairbought).to be 25
log.staircase = 2
log.form.reset_not_routed_questions_and_invalid_answers(log)
expect(log.stairbought).to be nil
end
end end
end
context "when there are multiple free user input questions for attribute X" do
context "and attribute Y is changed such that a different question for X is routed to" do
let(:log) { FactoryBot.create(:sales_log, :saledate_today, :shared_ownership, :privacy_notice_seen, jointpur: 1, jointmore: 2, hholdcount: expected_hholdcount) }
let(:expected_hholdcount) { 2 }
it "the value of this attribute is not cleared" do
log.jointpur = 2
log.form.reset_not_routed_questions_and_invalid_answers(log)
expect(log.hholdcount).to eq expected_hholdcount
end
end
context "and attribute Y is changed such that no questions for X are routed to" do
let(:log) { FactoryBot.create(:sales_log, :shared_ownership_setup_complete, value: initial_value) }
let(:initial_value) { 200_000.to_d }
it "the value of this attribute is cleared" do
expect(log.value).to eq initial_value
log.ownershipsch = 2
log.form.reset_not_routed_questions_and_invalid_answers(log)
expect(log.value).to be nil
end
end
end
context "when a value is changed such that a checkbox question is no longer routed to" do
let(:log) { FactoryBot.create(:lettings_log, :setup_completed, startdate: now, reasonpref: 1, rp_homeless: 1, rp_medwel: 1, rp_hardship: 1) }
it "all attributes relating to that checkbox question are cleared" do
expect(log.rp_homeless).to be 1
log.reasonpref = 2
log.form.reset_not_routed_questions_and_invalid_answers(log)
expect(log.rp_homeless).to be nil
expect(log.rp_medwel).to be nil
expect(log.rp_hardship).to be nil
end
end
context "when an attribute is derived, but no questions for that attribute are routed to" do
let(:log) { FactoryBot.create(:sales_log, :outright_sale_setup_complete, value: 200_000) }
it "the value of this attribute is not cleared" do
expect(log.deposit).to be nil
log.update!(mortgageused: 2)
expect(log.form.questions.any? { |q| q.id == "deposit" && q.page.routed_to?(log, nil) }).to be false
expect(log.deposit).not_to be nil
end
end
context "when an attribute is related to a callback question with no set answer options, and no questions for that attribute are routed to" do
let(:location) { FactoryBot.create(:location) }
let(:log) { FactoryBot.create(:lettings_log, :startdate_today) }
it "does not route to the page" do # Pages::PropertyPostcode and questions inside have been removed from form. do we not want to delete and migration delete_column?
expect(form.invalidated_pages(lettings_log).map(&:id)).to include("outstanding_amount_known") it "the value of this attribute is not cleared" do
expect(log.form.questions.find { |q| q.id == "location_id" }.answer_options.keys).to be_empty
log.location_id = location.id
log.form.reset_not_routed_questions_and_invalid_answers(log)
expect(log.location_id).not_to be nil
end end
end end
end end

6
spec/models/sales_log_spec.rb

@ -181,7 +181,7 @@ RSpec.describe SalesLog, type: :model do
let(:sales_log) { create(:sales_log, :completed) } let(:sales_log) { create(:sales_log, :completed) }
it "correctly derives and saves exday, exmonth and exyear" do it "correctly derives and saves exday, exmonth and exyear" do
sales_log.update!(exdate: Time.gm(2022, 5, 4), saledate: Time.gm(2022, 7, 4), ownershipsch: 1, staircase: 2, resale: 2) sales_log.update!(exdate: Time.gm(2022, 5, 4), saledate: Time.gm(2022, 7, 4), ownershipsch: 1, type: 18, staircase: 2, resale: 2, proplen: 0)
record_from_db = ActiveRecord::Base.connection.execute("select exday, exmonth, exyear from sales_logs where id=#{sales_log.id}").to_a[0] record_from_db = ActiveRecord::Base.connection.execute("select exday, exmonth, exyear from sales_logs where id=#{sales_log.id}").to_a[0]
expect(record_from_db["exday"]).to eq(4) expect(record_from_db["exday"]).to eq(4)
expect(record_from_db["exmonth"]).to eq(5) expect(record_from_db["exmonth"]).to eq(5)
@ -571,8 +571,8 @@ RSpec.describe SalesLog, type: :model do
end end
end end
context "when service errors" do context "when the API returns an error" do
let(:sales_log) { create(:sales_log, uprn_known: 1, uprn: "123456789", uprn_confirmed: 1) } let(:sales_log) { build(:sales_log, :outright_sale_setup_complete, uprn_known: 1, uprn: "123456789", uprn_confirmed: 1) }
let(:error_message) { "error" } let(:error_message) { "error" }
it "adds error to sales log" do it "adds error to sales log" do

18
spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb

@ -852,16 +852,6 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do
end end
end end
describe "#field_134" do
context "when an unpermitted value" do
let(:attributes) { { bulk_upload:, field_134: "3" } }
it "has errors on the field" do
expect(parser.errors[:field_134]).to be_present
end
end
end
describe "#field_103" do describe "#field_103" do
context "when null" do context "when null" do
let(:attributes) { setup_section_params.merge({ field_103: nil }) } let(:attributes) { setup_section_params.merge({ field_103: nil }) }
@ -874,14 +864,6 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do
expect(parser.errors[:field_103]).to eql(["You must answer type of building"]) expect(parser.errors[:field_103]).to eql(["You must answer type of building"])
end end
end end
context "when unpermitted values" do
let(:attributes) { setup_section_params.merge({ field_103: "4" }) }
it "returns an error" do
expect(parser.errors[:field_103]).to be_present
end
end
end end
end end

16
spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb

@ -793,14 +793,6 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do
end end
describe "#field_6" do # renewal describe "#field_6" do # renewal
context "when an unpermitted value" do
let(:attributes) { { bulk_upload:, field_6: "3" } }
it "has errors on the field" do
expect(parser.errors[:field_6]).to be_present
end
end
context "when blank" do context "when blank" do
let(:attributes) { { bulk_upload:, field_1: owning_org.old_visible_id, field_6: "" } } let(:attributes) { { bulk_upload:, field_1: owning_org.old_visible_id, field_6: "" } }
@ -842,14 +834,6 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do
expect(parser.errors[:field_30]).to eql(["You must answer type of building"]) expect(parser.errors[:field_30]).to eql(["You must answer type of building"])
end end
end end
context "when unpermitted values" do
let(:attributes) { setup_section_params.merge({ field_30: "4" }) }
it "returns an error" do
expect(parser.errors[:field_30]).to be_present
end
end
end end
describe "#field_52" do # age2 describe "#field_52" do # age2

Loading…
Cancel
Save