diff --git a/app/models/case_log.rb b/app/models/case_log.rb index 68cbef3ac..5e78990f3 100644 --- a/app/models/case_log.rb +++ b/app/models/case_log.rb @@ -186,6 +186,10 @@ class CaseLog < ApplicationRecord previous_la_known == 1 end + def tshortfall_unknown? + tshortfall_known == 1 + end + def is_secure_tenancy? # 1: Secure (including flexible) tenancy == 1 @@ -367,6 +371,10 @@ class CaseLog < ApplicationRecord "#{soft_value_for_period(soft_max)} #{SUFFIX_FROM_PERIOD[period].presence || 'every week'}" end + def optional_fields + OPTIONAL_FIELDS + dynamically_not_required + end + private PIO = Postcodes::IO.new @@ -425,7 +433,8 @@ private def dynamically_not_required previous_la_known_field = postcode_known? ? %w[previous_la_known] : [] - ((form.invalidated_questions(self) + form.readonly_questions).map(&:id) + previous_la_known_field).uniq + tshortfall_field = tshortfall_unknown? ? %w[tshortfall] : [] + previous_la_known_field + tshortfall_field end def set_derived_fields! @@ -458,6 +467,7 @@ private end end self.has_benefits = get_has_benefits + self.tshortfall_known = 0 if tshortfall self.wtshortfall = if tshortfall && receives_housing_related_benefits? weekly_value(tshortfall) end @@ -622,13 +632,9 @@ private end def all_fields_nil? - init_fields = %w[owning_organisation_id managing_organisation_id] - fields = mandatory_fields.difference(init_fields) - fields.none? { |field| public_send(field).present? if respond_to?(field) } - end - - def mandatory_fields - form.questions.map(&:id).difference(OPTIONAL_FIELDS, dynamically_not_required) + not_started_statuses = %i[not_started cannot_start_yet] + subsection_statuses = form.subsections.map { |subsection| subsection.status(self) }.uniq + subsection_statuses.all? { |status| not_started_statuses.include?(status) } end def age_refused? diff --git a/app/models/form.rb b/app/models/form.rb index e339f6fdc..7f48d7d4c 100644 --- a/app/models/form.rb +++ b/app/models/form.rb @@ -124,8 +124,8 @@ class Form end def enabled_page_questions(case_log) - pages_that_are_routed_to = pages.select { |p| p.routed_to?(case_log) } - pages_that_are_routed_to.flat_map(&:questions) || [] + pages_that_are_routed_to_or_derived = pages.select { |p| p.routed_to?(case_log) || p.derived } + pages_that_are_routed_to_or_derived.flat_map(&:questions) || [] end def invalidated_conditional_questions(case_log) @@ -155,6 +155,8 @@ class Form return true unless depends_on depends_on.any? do |conditions_set| + return false unless conditions_set + conditions_set.all? do |question, value| if value.is_a?(Hash) && value.key?("operator") operator = value["operator"] diff --git a/app/models/form/page.rb b/app/models/form/page.rb index 155f85d20..7822c2b68 100644 --- a/app/models/form/page.rb +++ b/app/models/form/page.rb @@ -1,5 +1,5 @@ class Form::Page - attr_accessor :id, :header, :description, :questions, + attr_accessor :id, :header, :description, :questions, :derived, :depends_on, :title_text, :informative_text, :subsection, :hide_subsection_label def initialize(id, hsh, subsection) @@ -8,6 +8,7 @@ class Form::Page @description = hsh["description"] @questions = hsh["questions"].map { |q_id, q| Form::Question.new(q_id, q, self) } @depends_on = hsh["depends_on"] + @derived = hsh["derived"] @title_text = hsh["title_text"] @informative_text = hsh["informative_text"] @hide_subsection_label = hsh["hide_subsection_label"] diff --git a/app/models/form/subsection.rb b/app/models/form/subsection.rb index 95216c9d4..20b65ad9f 100644 --- a/app/models/form/subsection.rb +++ b/app/models/form/subsection.rb @@ -31,7 +31,7 @@ class Form::Subsection end qs = applicable_questions(case_log) - qs_optional_removed = qs.reject { |q| CaseLog::OPTIONAL_FIELDS.include?(q.id) } + qs_optional_removed = qs.reject { |q| case_log.optional_fields.include?(q.id) } return :not_started if qs.all? { |question| case_log[question.id].blank? || question.read_only? } return :completed if qs_optional_removed.all? { |question| question.completed?(case_log) } diff --git a/app/services/imports/case_logs_import_service.rb b/app/services/imports/case_logs_import_service.rb index cf7cd4cee..56ccbd8d8 100644 --- a/app/services/imports/case_logs_import_service.rb +++ b/app/services/imports/case_logs_import_service.rb @@ -125,8 +125,9 @@ module Imports attributes["supcharg"] = safe_string_as_decimal(xml_doc, "Q18aiv") attributes["tcharge"] = safe_string_as_decimal(xml_doc, "Q18av") + attributes["hbrentshortfall"] = unsafe_string_as_integer(xml_doc, "Q18d") attributes["tshortfall"] = safe_string_as_decimal(xml_doc, "Q18dyes") - attributes["hbrentshortfall"] = hbshortfall(xml_doc, attributes) + attributes["tshortfall_known"] = tshortfall_known?(xml_doc, attributes) attributes["voiddate"] = compose_date(xml_doc, "VDAY", "VMONTH", "VYEAR") attributes["mrcdate"] = compose_date(xml_doc, "MRCDAY", "MRCMONTH", "MRCYEAR") @@ -214,7 +215,7 @@ module Imports end def fields_not_present_in_softwire_data - %w[majorrepairs illness_type_0] + %w[majorrepairs illness_type_0 tshortfall_known] end def check_status_completed(case_log, previous_status) @@ -520,14 +521,11 @@ module Imports ((2..8).map { |x| string_or_nil(xml_doc, "P#{x}Rel") } + [string_or_nil(xml_doc, "P1Sex")]).compact end - def hbshortfall(xml_doc, attributes) - shortfall = unsafe_string_as_integer(xml_doc, "Q18d") - if attributes["tshortfall"].blank? && shortfall == 1 && overridden?(xml_doc, "xmlns", "Q18dyes") - # If they have said there is a shortfall but then not entered one, and that has been - # manually overridden we instead infer that they actually didn't know whether there is a shortfall. - 3 + def tshortfall_known?(xml_doc, attributes) + if attributes["tshortfall"].blank? && attributes["hbrentshortfall"] == 1 && overridden?(xml_doc, "xmlns", "Q18dyes") + 1 else - shortfall + 0 end end end diff --git a/config/forms/2021_2022.json b/config/forms/2021_2022.json index 8715e1a46..1fe6bfb0d 100644 --- a/config/forms/2021_2022.json +++ b/config/forms/2021_2022.json @@ -5717,6 +5717,29 @@ } ] }, + "outstanding_amount_known": { + "header": "", + "description": "", + "questions": { + "tshortfall_known": { + "check_answer_label": "", + "header": "", + "hint_text": "", + "hidden_in_check_answers": true, + "type": "radio", + "answer_options": { + "0": { + "value": "Yes" + }, + "1": { + "value": "No" + } + } + } + }, + "derived": true, + "depends_on": [false] + }, "outstanding_amount": { "header": "", "description": "", diff --git a/config/forms/2022_2023.json b/config/forms/2022_2023.json index 513a67e15..f445ebe7c 100644 --- a/config/forms/2022_2023.json +++ b/config/forms/2022_2023.json @@ -5740,6 +5740,29 @@ } ] }, + "outstanding_amount_known": { + "header": "", + "description": "", + "questions": { + "tshortfall_known": { + "check_answer_label": "", + "header": "", + "hint_text": "", + "hidden_in_check_answers": true, + "type": "radio", + "answer_options": { + "0": { + "value": "Yes" + }, + "1": { + "value": "No" + } + } + } + }, + "derived": true, + "depends_on": [false] + }, "outstanding_amount": { "header": "", "description": "", diff --git a/db/migrate/20220510091620_add_tshortfall_known_case_logs.rb b/db/migrate/20220510091620_add_tshortfall_known_case_logs.rb new file mode 100644 index 000000000..1059324ba --- /dev/null +++ b/db/migrate/20220510091620_add_tshortfall_known_case_logs.rb @@ -0,0 +1,5 @@ +class AddTshortfallKnownCaseLogs < ActiveRecord::Migration[7.0] + def change + add_column :case_logs, :tshortfall_known, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index ddab93cfe..ba9001e74 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[7.0].define(version: 2022_05_06_092350) do +ActiveRecord::Schema[7.0].define(version: 2022_05_10_091620) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -222,6 +222,7 @@ ActiveRecord::Schema[7.0].define(version: 2022_05_06_092350) do t.integer "joint" t.bigint "created_by_id" t.integer "illness_type_0" + t.integer "tshortfall_known" t.index ["created_by_id"], name: "index_case_logs_on_created_by_id" t.index ["managing_organisation_id"], name: "index_case_logs_on_managing_organisation_id" t.index ["old_id"], name: "index_case_logs_on_old_id", unique: true diff --git a/spec/fixtures/exports/case_logs.xml b/spec/fixtures/exports/case_logs.xml index 00dfd5243..66795680e 100644 --- a/spec/fixtures/exports/case_logs.xml +++ b/spec/fixtures/exports/case_logs.xml @@ -164,6 +164,7 @@ {created_by_id} + 0 1 diff --git a/spec/fixtures/forms/2022_2023.json b/spec/fixtures/forms/2022_2023.json index 8d856aa1c..e9fcb6990 100644 --- a/spec/fixtures/forms/2022_2023.json +++ b/spec/fixtures/forms/2022_2023.json @@ -94,6 +94,29 @@ "width": 10 } } + }, + "outstanding_amount_known": { + "header": "", + "description": "", + "questions": { + "tshortfall_known": { + "check_answer_label": "", + "header": "", + "hint_text": "", + "hidden_in_check_answers": true, + "type": "radio", + "answer_options": { + "0": { + "value": "Yes" + }, + "1": { + "value": "No" + } + } + } + }, + "derived": true, + "depends_on": [false] } } } diff --git a/spec/models/case_log_spec.rb b/spec/models/case_log_spec.rb index 7fdbd6381..f420ade1c 100644 --- a/spec/models/case_log_spec.rb +++ b/spec/models/case_log_spec.rb @@ -1660,6 +1660,26 @@ RSpec.describe CaseLog do expect(relet_case_log["newprop"]).to eq(2) end end + + context "when a total shortfall is provided" do + it "derives that tshortfall is known" do + case_log.update!({ tshortfall: 10 }) + record_from_db = ActiveRecord::Base.connection.execute("select tshortfall_known from case_logs where id=#{case_log.id}").to_a[0] + expect(record_from_db["tshortfall_known"]).to eq(0) + expect(case_log["tshortfall_known"]).to eq(0) + end + end + end + + describe "optional fields" do + let(:case_log) { FactoryBot.create(:case_log) } + + context "when tshortfall is marked as not known" do + it "makes tshortfall optional" do + case_log.update!({ tshortfall: nil, tshortfall_known: 1 }) + expect(case_log.optional_fields).to include("tshortfall") + end + end end describe "resetting invalidated fields" do @@ -1751,6 +1771,32 @@ RSpec.describe CaseLog do end end + describe "tshortfall_unknown?" do + context "when tshortfall is nil" do + let(:case_log) { FactoryBot.create(:case_log, :in_progress, tshortfall_known: nil) } + + it "returns false" do + expect(case_log.tshortfall_unknown?).to be false + end + end + + context "when tshortfall is No" do + let(:case_log) { FactoryBot.create(:case_log, :in_progress, tshortfall_known: 1) } + + it "returns false" do + expect(case_log.tshortfall_unknown?).to be true + end + end + + context "when tshortfall is Yes" do + let(:case_log) { FactoryBot.create(:case_log, :in_progress, tshortfall_known: 0) } + + it "returns false" do + expect(case_log.tshortfall_unknown?).to be false + end + end + end + describe "paper trail" do let(:case_log) { FactoryBot.create(:case_log, :in_progress) } diff --git a/spec/models/form_spec.rb b/spec/models/form_spec.rb index 43628ab78..08cd1718e 100644 --- a/spec/models/form_spec.rb +++ b/spec/models/form_spec.rb @@ -190,5 +190,17 @@ RSpec.describe Form, type: :model do expect(form.invalidated_page_questions(case_log).map(&:id).uniq).to eq(expected_invalid) end end + + context "when a page is marked as `derived` and `depends_on: false`" do + let(:case_log) { FactoryBot.build(:case_log, :in_progress, startdate: Time.utc(2023, 2, 2, 10, 36, 49)) } + + it "does not count it's questions as invalidated" do + expect(form.enabled_page_questions(case_log).map(&:id).uniq).to include("tshortfall_known") + end + + it "does not route to the page" do + expect(form.invalidated_pages(case_log).map(&:id)).to include("outstanding_amount_known") + end + end end end