From 9dc7e574bfc414cf41033e18a1c9d648ffb138e5 Mon Sep 17 00:00:00 2001 From: baarkerlounger <5101747+baarkerlounger@users.noreply.github.com> Date: Mon, 4 Apr 2022 12:01:50 +0100 Subject: [PATCH] CLDC-1116: Don't route to ecstat if age is under 16 (#446) * Ecstat dependent on age * Derive ecstat if age is under 16 * Test value label derivation * Ensure derived option is still shown in check answers --- app/models/case_log.rb | 7 ++++ app/models/form/page.rb | 14 +++++--- app/models/form/question.rb | 14 +++++++- app/views/form/_checkbox_question.html.erb | 2 +- app/views/form/_radio_question.html.erb | 2 +- app/views/form/_select_question.html.erb | 2 +- config/forms/2021_2022.json | 42 ++++++++++++++-------- spec/factories/case_log.rb | 1 + spec/fixtures/forms/2021_2022.json | 22 +++++++++--- spec/helpers/check_answers_helper_spec.rb | 3 +- spec/models/case_log_spec.rb | 21 ++++++++--- spec/models/form/page_spec.rb | 18 +++++++++- spec/models/form/question_spec.rb | 38 ++++++++++++++++++++ spec/models/form/subsection_spec.rb | 8 ++--- spec/models/form_handler_spec.rb | 2 +- spec/models/form_spec.rb | 7 ++-- 16 files changed, 163 insertions(+), 40 deletions(-) diff --git a/app/models/case_log.rb b/app/models/case_log.rb index ae44e1f40..824b0efff 100644 --- a/app/models/case_log.rb +++ b/app/models/case_log.rb @@ -409,6 +409,13 @@ private self.prevten = 30 if managing_organisation.provider_type == "LA" end end + (2..8).each do |idx| + if public_send("age#{idx}") && public_send("age#{idx}") < 16 + self["ecstat#{idx}"] = 9 + elsif public_send("ecstat#{idx}") == 9 && (public_send("age#{idx}").nil? || public_send("age#{idx}") >= 16) + self["ecstat#{idx}"] = nil + end + end end def process_postcode_changes! diff --git a/app/models/form/page.rb b/app/models/form/page.rb index 1a300f081..a2bac3bf2 100644 --- a/app/models/form/page.rb +++ b/app/models/form/page.rb @@ -46,10 +46,16 @@ private depends_on.any? do |conditions_set| conditions_set.all? do |question, value| - parts = question.split(".") - case_log_value = send_chain(parts, case_log) - - value.nil? ? case_log_value == value : !case_log_value.nil? && case_log_value == value + if value.is_a?(Hash) && value.key?("operator") + operator = value["operator"] + operand = value["operand"] + case_log[question]&.send(operator, operand) + else + parts = question.split(".") + case_log_value = send_chain(parts, case_log) + + value.nil? ? case_log_value == value : !case_log_value.nil? && case_log_value == value + end end end end diff --git a/app/models/form/question.rb b/app/models/form/question.rb index 03210ba0f..8d1d43a7c 100644 --- a/app/models/form/question.rb +++ b/app/models/form/question.rb @@ -60,7 +60,7 @@ class Form::Question def enabled?(case_log) return true if conditional_on.blank? - conditional_on.map { |condition| evaluate_condition(condition, case_log) }.all? + conditional_on.all? { |condition| evaluate_condition(condition, case_log) } end def hidden_in_check_answers? @@ -68,11 +68,18 @@ class Form::Question end def has_inferred_check_answers_value?(case_log) + return true if selected_answer_option_is_derived?(case_log) return inferred_check_answers_value["condition"].values[0] == case_log[inferred_check_answers_value["condition"].keys[0]] if inferred_check_answers_value.present? false end + def displayed_answer_options + answer_options.select do |_key, val| + !val.is_a?(Hash) || !val["derived"] + end + end + def update_answer_link_name(case_log) link_type = if has_inferred_check_answers_value?(case_log) "Change" @@ -150,6 +157,11 @@ class Form::Question private + def selected_answer_option_is_derived?(case_log) + selected_option = answer_options&.dig(case_log[id].to_s.presence) + selected_option.is_a?(Hash) && selected_option["derived"] + end + def has_inferred_display_value?(case_log) inferred_check_answers_value.present? && case_log[inferred_check_answers_value["condition"].keys.first] == inferred_check_answers_value["condition"].values.first end diff --git a/app/views/form/_checkbox_question.html.erb b/app/views/form/_checkbox_question.html.erb index f39200e4b..3c1bb0da9 100644 --- a/app/views/form/_checkbox_question.html.erb +++ b/app/views/form/_checkbox_question.html.erb @@ -6,7 +6,7 @@ hint: { text: question.hint_text&.html_safe } do %> <% after_divider = false %> - <% question.answer_options.map do |key, options| %> + <% question.displayed_answer_options.map do |key, options| %> <% if key.starts_with?("divider") %> <% after_divider = true %> <%= f.govuk_check_box_divider %> diff --git a/app/views/form/_radio_question.html.erb b/app/views/form/_radio_question.html.erb index b5e5526f2..12a8052b1 100644 --- a/app/views/form/_radio_question.html.erb +++ b/app/views/form/_radio_question.html.erb @@ -5,7 +5,7 @@ legend: legend(question, page_header, conditional), hint: { text: question.hint_text&.html_safe } do %> - <% question.answer_options.map do |key, options| %> + <% question.displayed_answer_options.map do |key, options| %> <% if key.starts_with?("divider") %> <%= f.govuk_radio_divider %> <% else %> diff --git a/app/views/form/_select_question.html.erb b/app/views/form/_select_question.html.erb index bcc588ba5..88a080bda 100644 --- a/app/views/form/_select_question.html.erb +++ b/app/views/form/_select_question.html.erb @@ -1,7 +1,7 @@ <%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> <% selected = @case_log.public_send(question.id) || "" %> -<%= answers = question.answer_options.map { |key, value| OpenStruct.new(id: key, name: value) } +<%= answers = question.displayed_answer_options.map { |key, value| OpenStruct.new(id: key, name: value) } f.govuk_collection_select question.id.to_sym, answers, :id, diff --git a/config/forms/2021_2022.json b/config/forms/2021_2022.json index a69b5916a..0090afa1e 100644 --- a/config/forms/2021_2022.json +++ b/config/forms/2021_2022.json @@ -1731,7 +1731,8 @@ "value": "Retired" }, "9": { - "value": "Child under 16" + "value": "Child under 16", + "derived": true }, "0": { "value": "Other" @@ -1747,7 +1748,8 @@ }, "depends_on": [ { - "details_known_2": 0 + "details_known_2": 0, + "age2": { "operator": ">", "operand": 15 } } ] }, @@ -1939,7 +1941,8 @@ "value": "Retired" }, "9": { - "value": "Child under 16" + "value": "Child under 16", + "derived": true }, "0": { "value": "Other" @@ -1955,7 +1958,8 @@ }, "depends_on": [ { - "details_known_3": 0 + "details_known_3": 0, + "age3": { "operator": ">", "operand": 15 } } ] }, @@ -2144,7 +2148,8 @@ "value": "Retired" }, "9": { - "value": "Child under 16" + "value": "Child under 16", + "derived": true }, "0": { "value": "Other" @@ -2160,7 +2165,8 @@ }, "depends_on": [ { - "details_known_4": 0 + "details_known_4": 0, + "age4": { "operator": ">", "operand": 15 } } ] }, @@ -2346,7 +2352,8 @@ "value": "Retired" }, "9": { - "value": "Child under 16" + "value": "Child under 16", + "derived": true }, "0": { "value": "Other" @@ -2362,7 +2369,8 @@ }, "depends_on": [ { - "details_known_5": 0 + "details_known_5": 0, + "age5": { "operator": ">", "operand": 15 } } ] }, @@ -2545,7 +2553,8 @@ "value": "Retired" }, "9": { - "value": "Child under 16" + "value": "Child under 16", + "derived": true }, "0": { "value": "Other" @@ -2561,7 +2570,8 @@ }, "depends_on": [ { - "details_known_6": 0 + "details_known_6": 0, + "age6": { "operator": ">", "operand": 15 } } ] }, @@ -2741,7 +2751,8 @@ "value": "Retired" }, "9": { - "value": "Child under 16" + "value": "Child under 16", + "derived": true }, "0": { "value": "Other" @@ -2757,7 +2768,8 @@ }, "depends_on": [ { - "details_known_7": 0 + "details_known_7": 0, + "age7": { "operator": ">", "operand": 15 } } ] }, @@ -2934,7 +2946,8 @@ "value": "Retired" }, "9": { - "value": "Child under 16" + "value": "Child under 16", + "derived": true }, "0": { "value": "Other" @@ -2950,7 +2963,8 @@ }, "depends_on": [ { - "details_known_8": 0 + "details_known_8": 0, + "age8": { "operator": ">", "operand": 15 } } ] } diff --git a/spec/factories/case_log.rb b/spec/factories/case_log.rb index 1ee6130ea..2bec19c48 100644 --- a/spec/factories/case_log.rb +++ b/spec/factories/case_log.rb @@ -15,6 +15,7 @@ FactoryBot.define do postcode_full { "PO5 3TE" } ppostcode_full { "SW2 6HI" } age1 { 17 } + age2 { 19 } end trait :soft_validations_triggered do status { 1 } diff --git a/spec/fixtures/forms/2021_2022.json b/spec/fixtures/forms/2021_2022.json index c61582c2f..e458d4215 100644 --- a/spec/fixtures/forms/2021_2022.json +++ b/spec/fixtures/forms/2021_2022.json @@ -128,8 +128,7 @@ "conditional_for": { "relat2": ">0", "age2": ">0", - "sex2": ">0", - "ecstat2": ">0" + "sex2": ">0" } }, "relat2": { @@ -172,7 +171,13 @@ "value": "Prefer not to say" } } - }, + } + } + }, + "person_2_working_situation": { + "header": "", + "description": "", + "questions": { "ecstat2": { "check_answer_label": "Person 2’s Work", "header": "Which of these best describes person 2’s working situation?", @@ -181,12 +186,21 @@ "0": { "value": "Other" }, + "9": { + "value": "Child under 16", + "derived": true + }, "1": { "value": "Prefer not to say" } } } - } + }, + "depends_on": [ + { + "age2": { "operator": ">", "operand": 15 } + } + ] }, "propcode": { "questions": { diff --git a/spec/helpers/check_answers_helper_spec.rb b/spec/helpers/check_answers_helper_spec.rb index ade2473c3..c0642db81 100644 --- a/spec/helpers/check_answers_helper_spec.rb +++ b/spec/helpers/check_answers_helper_spec.rb @@ -9,7 +9,7 @@ RSpec.describe CheckAnswersHelper do context "when a section hasn't been completed yet" do it "returns that you have unanswered questions" do expect(display_answered_questions_summary(subsection, case_log)) - .to match(/You have answered 2 of 6 questions./) + .to match(/You have answered 2 of 7 questions./) end end @@ -19,6 +19,7 @@ RSpec.describe CheckAnswersHelper do case_log.other_hhmemb = 0 case_log.propcode = "123" case_log.ecstat1 = 200 + case_log.ecstat2 = 9 expect(display_answered_questions_summary(subsection, case_log)) .to match(/You answered all the questions./) expect(display_answered_questions_summary(subsection, case_log)) diff --git a/spec/models/case_log_spec.rb b/spec/models/case_log_spec.rb index 0c751be4e..5777f4306 100644 --- a/spec/models/case_log_spec.rb +++ b/spec/models/case_log_spec.rb @@ -1339,21 +1339,21 @@ RSpec.describe CaseLog do context "when validating household members derived vars" do let!(:household_case_log) do - described_class.create({ + described_class.create!({ managing_organisation: organisation, owning_organisation: organisation, other_hhmemb: 4, - relat2: "C", + relat2: "X", relat3: "C", relat4: "X", relat5: "C", - relat7: "X", + relat7: "C", relat8: "X", age1: 22, - age2: 14, + age2: 16, age4: 60, age6: 88, - age7: 16, + age7: 14, age8: 42, }) end @@ -1372,6 +1372,17 @@ RSpec.describe CaseLog do record_from_db = ActiveRecord::Base.connection.execute("select totadult from case_logs where id=#{household_case_log.id}").to_a[0] expect(record_from_db["totadult"]).to eq(3) end + + it "correctly derives economic status for tenants under 16" do + record_from_db = ActiveRecord::Base.connection.execute("select ecstat7 from case_logs where id=#{household_case_log.id}").to_a[0] + expect(record_from_db["ecstat7"]).to eq(9) + end + + it "correctly resets economic status when age changes from under 16" do + household_case_log.update!(age7: 17) + record_from_db = ActiveRecord::Base.connection.execute("select ecstat7 from case_logs where id=#{household_case_log.id}").to_a[0] + expect(record_from_db["ecstat7"]).to eq(nil) + end end it "correctly derives and saves has_benefits" do diff --git a/spec/models/form/page_spec.rb b/spec/models/form/page_spec.rb index 1b8a5189b..a09fcace6 100644 --- a/spec/models/form/page_spec.rb +++ b/spec/models/form/page_spec.rb @@ -55,12 +55,28 @@ RSpec.describe Form::Page, type: :model do expect(page.routed_to?(case_log)).to be false end - it "evaluates not conditions correctly" do + it "evaluates met conditions correctly" do case_log.incfreq = "Weekly" expect(page.routed_to?(case_log)).to be true end end + context "with expression routing conditions" do + let(:section_id) { "household" } + let(:subsection_id) { "household_characteristics" } + let(:page_id) { "person_2_working_situation" } + + it "evaluates not met conditions correctly" do + case_log.age2 = 12 + expect(page.routed_to?(case_log)).to be false + end + + it "evaluates met conditions correctly" do + case_log.age2 = 17 + expect(page.routed_to?(case_log)).to be true + end + end + context "when the page's subsection has routing conditions" do let(:section_id) { "submission" } let(:subsection_id) { "declaration" } diff --git a/spec/models/form/question_spec.rb b/spec/models/form/question_spec.rb index 2f8535d14..94d65652a 100644 --- a/spec/models/form/question_spec.rb +++ b/spec/models/form/question_spec.rb @@ -116,6 +116,30 @@ RSpec.describe Form::Question, type: :model do end end + context "when answer options do not include derived options" do + it "displays all answer options" do + expect(question.displayed_answer_options).to match(question.answer_options) + end + end + + context "when answer options include derived options" do + let(:section_id) { "household" } + let(:subsection_id) { "household_characteristics" } + let(:page_id) { "person_2_working_situation" } + let(:question_id) { "ecstat2" } + let(:expected_answer_options) do + { "0" => { "value" => "Other" }, "1" => { "value" => "Prefer not to say" } } + end + + it "does not include those options in the displayed options" do + expect(question.displayed_answer_options).to match(expected_answer_options) + end + + it "can still map the value label" do + expect(question.label_from_value(9)).to eq("Child under 16") + end + end + context "when the saved answer is not in the value map" do it "displays the saved answer umapped" do expect(question.label_from_value(9999)).to eq("9999") @@ -219,6 +243,20 @@ RSpec.describe Form::Question, type: :model do end end + context "when the answer option is a derived answer option" do + let(:section_id) { "household" } + let(:subsection_id) { "household_characteristics" } + let(:page_id) { "person_2_working_situation" } + let(:question_id) { "ecstat2" } + let(:case_log) do + FactoryBot.create(:case_log, :in_progress, hhmemb: 2, details_known_2: 0, age2_known: 0, age2: 12) + end + + it "knows it has an inferred value for check answers" do + expect(question.has_inferred_check_answers_value?(case_log)).to be true + end + end + context "when type is date" do let(:section_id) { "local_authority" } let(:subsection_id) { "local_authority" } diff --git a/spec/models/form/subsection_spec.rb b/spec/models/form/subsection_spec.rb index 6492c1b21..1cd11eb7f 100644 --- a/spec/models/form/subsection_spec.rb +++ b/spec/models/form/subsection_spec.rb @@ -25,7 +25,7 @@ RSpec.describe Form::Subsection, type: :model do end it "has pages" do - expected_pages = %w[tenant_code person_1_age person_1_gender person_1_working_situation household_number_of_other_members propcode] + expected_pages = %w[tenant_code person_1_age person_1_gender person_1_working_situation household_number_of_other_members person_2_working_situation propcode] expect(subsection.pages.map(&:id)).to eq(expected_pages) end @@ -58,9 +58,9 @@ RSpec.describe Form::Subsection, type: :model do end it "has question helpers for the number of applicable questions" do - expected_questions = %w[tenant_code age1 sex1 ecstat1 other_hhmemb propcode] + expected_questions = %w[tenant_code age1 sex1 ecstat1 other_hhmemb ecstat2 propcode] expect(subsection.applicable_questions(case_log).map(&:id)).to eq(expected_questions) - expect(subsection.applicable_questions_count(case_log)).to eq(6) + expect(subsection.applicable_questions_count(case_log)).to eq(7) end it "has question helpers for the number of answered questions" do @@ -79,7 +79,7 @@ RSpec.describe Form::Subsection, type: :model do end it "has a question helpers for the unanswered questions" do - expected_questions = %w[sex1 ecstat1 other_hhmemb propcode] + expected_questions = %w[sex1 ecstat1 other_hhmemb ecstat2 propcode] expect(subsection.unanswered_questions(case_log).map(&:id)).to eq(expected_questions) end end diff --git a/spec/models/form_handler_spec.rb b/spec/models/form_handler_spec.rb index e9bdeb3fd..4accfa6c5 100644 --- a/spec/models/form_handler_spec.rb +++ b/spec/models/form_handler_spec.rb @@ -17,7 +17,7 @@ RSpec.describe FormHandler do form_handler = described_class.instance form = form_handler.get_form(test_form_name) expect(form).to be_a(Form) - expect(form.pages.count).to eq(33) + expect(form.pages.count).to eq(34) end end diff --git a/spec/models/form_spec.rb b/spec/models/form_spec.rb index 61387969b..d9c8b5ee9 100644 --- a/spec/models/form_spec.rb +++ b/spec/models/form_spec.rb @@ -128,9 +128,12 @@ RSpec.describe Form, type: :model do before do case_log.tenant_code = "123" case_log.age1 = 35 - case_log.sex1 = "Male" + case_log.sex1 = "M" case_log.ecstat1 = 0 - case_log.other_hhmemb = 0 + case_log.other_hhmemb = 1 + case_log.relat2 = "P" + case_log.sex2 = "F" + case_log.ecstat2 = 1 end it "returns the first page of the next incomplete subsection if the subsection is not in progress" do