diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 21c76efd1..776259fab 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,6 +1,4 @@ class ApplicationController < ActionController::Base - default_form_builder GOVUKDesignSystemFormBuilder::FormBuilder - def render_not_found render "errors/not_found", status: :not_found end diff --git a/app/models/case_log.rb b/app/models/case_log.rb index 2426ecc0e..9bebe2bfc 100644 --- a/app/models/case_log.rb +++ b/app/models/case_log.rb @@ -130,6 +130,7 @@ class CaseLog < ApplicationRecord enum lettype: LET_TYPE, _suffix: true enum postcode_known: POLAR, _suffix: true enum la_known: POLAR, _suffix: true + enum net_income_known: NET_INCOME_KNOWN, _suffix: true AUTOGENERATED_FIELDS = %w[id status created_at updated_at discarded_at renttype lettype is_la_inferred totchild totelder totadult].freeze OPTIONAL_FIELDS = %w[postcode_known @@ -214,7 +215,7 @@ private self.month = startdate.month self.year = startdate.year end - self.incref = 1 if net_income_known == "Prefer not to say" + self.incref = 1 if net_income_known == "Tenant prefers not to say" self.hhmemb = other_hhmemb + 1 if other_hhmemb.present? self.renttype = RENT_TYPE_MAPPING[rent_type] self.lettype = "#{renttype} #{needstype} #{owning_organisation['Org type']}" if renttype.present? && needstype.present? && owning_organisation["Org type"].present? @@ -316,9 +317,11 @@ private dynamically_not_required << "tenancyother" end - unless net_income_known == "Yes" + if net_income_known == "Tenant prefers not to say" dynamically_not_required << "earnings" dynamically_not_required << "incfreq" + else + dynamically_not_required << "incref" end start_range = (other_hhmemb || 0) + 2 @@ -329,10 +332,6 @@ private dynamically_not_required << "ecstat#{n}" end - if net_income_known != "Prefer not to say" - dynamically_not_required << "incref" - end - required.delete_if { |key, _value| dynamically_not_required.include?(key) } end end diff --git a/app/models/constants/case_log.rb b/app/models/constants/case_log.rb index ea488d57c..204a94e6a 100644 --- a/app/models/constants/case_log.rb +++ b/app/models/constants/case_log.rb @@ -1070,4 +1070,11 @@ module Constants::CaseLog "Non-binary" => "X", "Prefer not to say" => "R", }.freeze + + NET_INCOME_KNOWN = { + "Yes – the household has a weekly income" => 0, + "Yes – the household has a monthly income" => 1, + "Yes – the household has a yearly income" => 2, + "Tenant prefers not to say" => 3, + }.freeze end diff --git a/app/models/form/question.rb b/app/models/form/question.rb index 63795f8cf..e4e843c0e 100644 --- a/app/models/form/question.rb +++ b/app/models/form/question.rb @@ -2,12 +2,14 @@ class Form::Question attr_accessor :id, :header, :hint_text, :description, :questions, :type, :min, :max, :step, :width, :fields_to_add, :result_field, :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, + :guidance_partial def initialize(id, hsh, page) @id = id @check_answer_label = hsh["check_answer_label"] @header = hsh["header"] + @guidance_partial = hsh["guidance_partial"] @hint_text = hsh["hint_text"] @type = hsh["type"] @min = hsh["min"] diff --git a/app/views/form/_checkbox_question.html.erb b/app/views/form/_checkbox_question.html.erb index 053d4499e..d351be1d1 100644 --- a/app/views/form/_checkbox_question.html.erb +++ b/app/views/form/_checkbox_question.html.erb @@ -1,3 +1,5 @@ +<%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> + <%= f.govuk_check_boxes_fieldset question.id.to_sym, caption: caption && !page_header.present? ? { text: caption.html_safe, size: "l" } : nil, legend: { text: question.header.html_safe, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" }, diff --git a/app/views/form/_date_question.html.erb b/app/views/form/_date_question.html.erb index b4f107cf6..3a1ddafa8 100644 --- a/app/views/form/_date_question.html.erb +++ b/app/views/form/_date_question.html.erb @@ -1,3 +1,5 @@ +<%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> + <%= f.govuk_date_field question.id.to_sym, caption: caption && !page_header.present? ? { text: caption.html_safe, size: "l" } : nil, legend: { text: question.header.html_safe, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" }, diff --git a/app/views/form/_numeric_question.html.erb b/app/views/form/_numeric_question.html.erb index 905477169..49872e8ee 100644 --- a/app/views/form/_numeric_question.html.erb +++ b/app/views/form/_numeric_question.html.erb @@ -1,3 +1,5 @@ +<%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> + <%= f.govuk_number_field question.id.to_sym, caption: caption && !page_header.present? ? { text: caption.html_safe, size: "l" } : nil, label: { text: question.header.html_safe, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" }, diff --git a/app/views/form/_radio_question.html.erb b/app/views/form/_radio_question.html.erb index aef21a90d..d7f94a8e9 100644 --- a/app/views/form/_radio_question.html.erb +++ b/app/views/form/_radio_question.html.erb @@ -1,3 +1,5 @@ +<%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> + <%= f.govuk_radio_buttons_fieldset question.id.to_sym, caption: caption && !page_header.present? ? { text: caption.html_safe, size: "l" } : nil, legend: { text: question.header.html_safe, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" }, diff --git a/app/views/form/_select_question.html.erb b/app/views/form/_select_question.html.erb index f3a07ae4c..6909d8470 100644 --- a/app/views/form/_select_question.html.erb +++ b/app/views/form/_select_question.html.erb @@ -1,3 +1,5 @@ +<%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> + <% selected = CaseLog::UK_LA[@case_log.public_send(question.id)] || "" %> <%= answers = question.answer_options.map { |key, value| OpenStruct.new(id: key, name: value) } f.govuk_collection_select question.id.to_sym, diff --git a/app/views/form/_text_question.html.erb b/app/views/form/_text_question.html.erb index 76c690787..dbf1cd92e 100644 --- a/app/views/form/_text_question.html.erb +++ b/app/views/form/_text_question.html.erb @@ -1,3 +1,5 @@ +<%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> + <%= f.govuk_text_field question.id.to_sym, caption: caption && !page_header.present? ? { text: caption.html_safe, size: "l" } : nil, label: { text: question.header.html_safe, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" }, diff --git a/app/views/form/_textarea_question.html.erb b/app/views/form/_textarea_question.html.erb index 1f062eb9f..3623d32c7 100644 --- a/app/views/form/_textarea_question.html.erb +++ b/app/views/form/_textarea_question.html.erb @@ -1,3 +1,5 @@ +<%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> + <%= f.govuk_text_area question.id.to_sym, caption: caption && !page_header.present? ? { text: caption.html_safe, size: "l" } : nil, label: { text: question.header.html_safe, size: !page_header.present? ? "l" : "m", tag: !page_header.present? ? "h1" : "h2" }, diff --git a/app/views/form/guidance/_what_counts_as_income.html.erb b/app/views/form/guidance/_what_counts_as_income.html.erb new file mode 100644 index 000000000..0ca777ea4 --- /dev/null +++ b/app/views/form/guidance/_what_counts_as_income.html.erb @@ -0,0 +1,16 @@ +<%= govuk_details(summary_text: 'What counts as income?') do %> +

You should include any income from:

+ + +

Don’t include:

+ +<% end %> diff --git a/config/forms/2021_2022.json b/config/forms/2021_2022.json index 68043367c..1a1423be5 100644 --- a/config/forms/2021_2022.json +++ b/config/forms/2021_2022.json @@ -1805,18 +1805,21 @@ "depends_on": { "about_this_log": "completed" }, "pages": { "net_income": { - "header": "", + "header": "Household’s combined income", "description": "", "questions": { "net_income_known": { "check_answer_label": "Income known", - "header": "Do you know the tenant and their partner’s net income?", + "header": "Do you know the household’s combined income after tax?", + "guidance_partial": "what_counts_as_income", "hint_text": "", "type": "radio", "answer_options": { - "0": "Yes", - "1": "No", - "2": "Tenant prefers not to say" + "0": "Yes – the household has a weekly income", + "1": "Yes – the household has a monthly income", + "2": "Yes – the household has a yearly income", + "divider_a": true, + "3": "Tenant prefers not to say" }, "conditional_for": { "earnings": ["Yes"], diff --git a/config/initializers/default_form_builder.rb b/config/initializers/default_form_builder.rb new file mode 100644 index 000000000..85458560a --- /dev/null +++ b/config/initializers/default_form_builder.rb @@ -0,0 +1 @@ +Rails.application.config.action_view.default_form_builder = GOVUKDesignSystemFormBuilder::FormBuilder diff --git a/db/migrate/20220107103143_convert_net_income_known_to_enum.rb b/db/migrate/20220107103143_convert_net_income_known_to_enum.rb new file mode 100644 index 000000000..b68ab1ccb --- /dev/null +++ b/db/migrate/20220107103143_convert_net_income_known_to_enum.rb @@ -0,0 +1,15 @@ +class ConvertNetIncomeKnownToEnum < ActiveRecord::Migration[7.0] + def up + change_table :case_logs, bulk: true do |t| + t.remove :net_income_known + t.column :net_income_known, :integer + end + end + + def down + change_table :case_logs, bulk: true do |t| + t.remove :net_income_known + t.column :net_income_known, :string + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 242c019fe..45a2d88f0 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2021_12_14_163230) do +ActiveRecord::Schema.define(version: 2022_01_07_103143) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -127,7 +127,6 @@ ActiveRecord::Schema.define(version: 2021_12_14_163230) do t.datetime "discarded_at" t.string "tenancyother" t.integer "override_net_income_validation" - t.string "net_income_known" t.string "gdpr_acceptance" t.string "gdpr_declined" t.string "property_owner_organisation" @@ -163,9 +162,9 @@ ActiveRecord::Schema.define(version: 2021_12_14_163230) do t.string "why_dont_you_know_la" t.integer "unitletas" t.integer "builtype" - t.datetime "property_void_date" t.bigint "owning_organisation_id" t.bigint "managing_organisation_id" + t.datetime "property_void_date" t.integer "renttype" t.integer "needstype" t.integer "lettype" @@ -178,6 +177,7 @@ ActiveRecord::Schema.define(version: 2021_12_14_163230) do t.integer "totchild" t.integer "totelder" t.integer "totadult" + t.integer "net_income_known" t.index ["discarded_at"], name: "index_case_logs_on_discarded_at" t.index ["managing_organisation_id"], name: "index_case_logs_on_managing_organisation_id" t.index ["owning_organisation_id"], name: "index_case_logs_on_owning_organisation_id" diff --git a/spec/factories/case_log.rb b/spec/factories/case_log.rb index 72763bdbb..2c7d4ef0a 100644 --- a/spec/factories/case_log.rb +++ b/spec/factories/case_log.rb @@ -113,7 +113,7 @@ FactoryBot.define do discarded_at { nil } tenancyother { nil } override_net_income_validation { nil } - net_income_known { "Yes" } + net_income_known { "Yes – the household has a weekly income" } gdpr_acceptance { "Yes" } gdpr_declined { "No" } property_owner_organisation { "Test" } diff --git a/spec/fixtures/complete_case_log.json b/spec/fixtures/complete_case_log.json index 4298aad27..b55aadba9 100644 --- a/spec/fixtures/complete_case_log.json +++ b/spec/fixtures/complete_case_log.json @@ -74,7 +74,7 @@ "mrcyear": 2020, "offered": 2, "wchair": "Yes", - "net_income_known": "Yes", + "net_income_known": "Yes – the household has a weekly income", "earnings": 0, "incfreq": null, "benefits": "Some", diff --git a/spec/fixtures/forms/2021_2022.json b/spec/fixtures/forms/2021_2022.json index befce0652..645acdd1a 100644 --- a/spec/fixtures/forms/2021_2022.json +++ b/spec/fixtures/forms/2021_2022.json @@ -368,6 +368,7 @@ "earnings": { "check_answer_label": "Income", "header": "What is the tenant’s /and partner’s combined income after tax?", + "guidance_partial": "what_counts_as_income", "type": "numeric", "min": 0, "step": 1, diff --git a/spec/models/case_log_spec.rb b/spec/models/case_log_spec.rb index 112c4f542..4bc012116 100644 --- a/spec/models/case_log_spec.rb +++ b/spec/models/case_log_spec.rb @@ -1030,11 +1030,9 @@ RSpec.describe Form, type: :model do owning_organisation: organisation, property_postcode: "M1 1AE", previous_postcode: "M2 2AE", - # rubocop:disable Style/DateTime - startdate: DateTime.new(2021, 10, 10), - mrcdate: DateTime.new(2021, 5, 4), - # rubocop:enable Style/DateTime - net_income_known: "Prefer not to say", + startdate: Time.gm(2021, 10, 10), + mrcdate: Time.gm(2021, 5, 4), + net_income_known: "Tenant prefers not to say", other_hhmemb: 6, rent_type: "London living rent", needstype: "General needs", diff --git a/spec/requests/form_controller_spec.rb b/spec/requests/form_controller_spec.rb index d103fa5fe..21c289bde 100644 --- a/spec/requests/form_controller_spec.rb +++ b/spec/requests/form_controller_spec.rb @@ -66,6 +66,13 @@ RSpec.describe FormController, type: :request do expect(response).to have_http_status(:not_found) end end + + context "a form page that has custom guidance" do + it "displays the correct partial" do + get "/logs/#{case_log.id}/net-income", headers: headers, params: {} + expect(response.body).to match("What counts as income?") + end + end end context "check answers pages" do diff --git a/spec/views/form/page_view_spec.rb b/spec/views/form/page_view_spec.rb new file mode 100644 index 000000000..2666421b0 --- /dev/null +++ b/spec/views/form/page_view_spec.rb @@ -0,0 +1,81 @@ +require "rails_helper" +require_relative "../../request_helper" + +RSpec.describe "form/page" do + before do + RequestHelper.stub_http_requests + end + + let(:case_log) { FactoryBot.create(:case_log, :in_progress) } + let(:form) { case_log.form } + let(:subsection) { form.get_subsection("income_and_benefits") } + let(:page) { form.get_page("net_income") } + let(:question) { page.questions.find { |q| q.id == "earnings" } } + let(:initial_attribs) { { type: "numeric", answer_options: nil } } + + def assign_attributes(object, attrs) + attrs.each_pair do |attr, value| + object.public_send("#{attr}=", value) + end + end + + context "given a question with extra guidance" do + let(:expected_guidance) { /What counts as income?/ } + + before do + assign(:case_log, case_log) + assign(:page, page) + assign(:subsection, subsection) + assign_attributes(question, attribs) + render + end + + after do + # Revert any changes we've made to avoid affecting other specs as the form, + # subsection, page, question objects being acted on are in memory + assign_attributes(question, initial_attribs) + end + + context "with radio type" do + let(:attribs) { { type: "radio", answer_options: { "1": "A", "2": "B" } } } + it "renders the guidance partial for radio questions" do + expect(rendered).to match(expected_guidance) + end + end + + context "with text type" do + let(:attribs) { { type: "text", answer_options: nil } } + it "renders the guidance partial for text questions" do + expect(rendered).to match(expected_guidance) + end + end + + context "with numeric type" do + let(:attribs) { { type: "numeric", answer_options: nil } } + it "renders the guidance partial for numeric questions" do + expect(rendered).to match(expected_guidance) + end + end + + context "with select type" do + let(:attribs) { { type: "select", answer_options: { "1": "A", "2": "B" } } } + it "renders the guidance partial for select questions" do + expect(rendered).to match(expected_guidance) + end + end + + context "with checkbox type" do + let(:attribs) { { type: "checkbox", answer_options: { "1": "A", "2": "B" } } } + it "renders the guidance partial for checkbox questions" do + expect(rendered).to match(expected_guidance) + end + end + + context "with date type" do + let(:attribs) { { type: "date", answer_options: nil } } + it "renders the guidance partial for date questions" do + expect(rendered).to match(expected_guidance) + end + end + end +end