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:
+
+ - employment
+ - pensions
+ - Universal Credit
+
+
+ Don’t include:
+
+ - National Insurance (NI) contributions and tax
+ - housing benefit
+ - child benefit
+ - council tax support
+
+<% 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