diff --git a/app/models/bulk_upload.rb b/app/models/bulk_upload.rb index c7f211064..66d24bf8d 100644 --- a/app/models/bulk_upload.rb +++ b/app/models/bulk_upload.rb @@ -157,7 +157,7 @@ class BulkUpload mrcmonth: row[93], mrcyear: row[94], # supported_scheme: row[95], - startdate: row[96].to_s + row[97].to_s + row[98].to_s, + startdate: date_time(row[98], row[97], row[96]), # startdate_day: row[96], # startdate_month: row[97], # startdate_year: row[98], @@ -201,6 +201,12 @@ class BulkUpload } end + def date_time(year, month, day) + return unless year && month && day + + Time.zone.local("20#{year}", month.to_s, day.to_s) + end + def other_hhmemb(row) [13, 14, 15, 16, 17, 18, 19].count { |idx| row[idx].present? } end diff --git a/app/models/case_log.rb b/app/models/case_log.rb index 042759733..4b9d196b9 100644 --- a/app/models/case_log.rb +++ b/app/models/case_log.rb @@ -35,10 +35,11 @@ class CaseLog < ApplicationRecord default_scope -> { kept } validates_with CaseLogValidator - before_save :update_status! before_validation :process_postcode_changes!, if: :property_postcode_changed? + before_validation :reset_invalidated_dependent_fields! before_validation :reset_location_fields!, unless: :postcode_known? before_validation :set_derived_fields! + before_save :update_status! belongs_to :owning_organisation, class_name: "Organisation" belongs_to :managing_organisation, class_name: "Organisation" @@ -46,7 +47,6 @@ class CaseLog < ApplicationRecord scope :for_organisation, ->(org) { where(owning_organisation: org).or(where(managing_organisation: org)) } enum status: { "not_started" => 0, "in_progress" => 1, "completed" => 2 } - enum ethnic: ETHNIC enum national: NATIONAL, _suffix: true enum ecstat1: ECSTAT, _suffix: true @@ -135,10 +135,8 @@ class CaseLog < ApplicationRecord 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 incfreq tcharge].freeze - OPTIONAL_FIELDS = %w[postcode_known - la_known - first_time_property_let_as_social_housing].freeze + AUTOGENERATED_FIELDS = %w[id status created_at updated_at discarded_at].freeze + OPTIONAL_FIELDS = %w[postcode_known la_known first_time_property_let_as_social_housing].freeze def form FormHandler.instance.get_form(form_name) @@ -212,6 +210,16 @@ private end end + def reset_invalidated_dependent_fields! + form.invalidated_page_questions(self).each do |question| + public_send("#{question.id}=", nil) if respond_to?(question.id.to_s) + end + end + + def dynamically_not_required + (form.invalidated_questions(self) + form.readonly_questions).map(&:id).uniq + end + def set_derived_fields! if previous_postcode.present? self.ppostc1 = UKPostcode.parse(previous_postcode).outcode @@ -295,62 +303,16 @@ private end def all_fields_completed? - mandatory_fields.none? { |_key, val| val.nil? } + mandatory_fields.none? { |field| public_send(field).nil? if respond_to?(field) } end def all_fields_nil? init_fields = %w[owning_organisation_id managing_organisation_id] - fields = mandatory_fields.except(*init_fields) - fields.all? { |_key, val| val.nil? } + fields = mandatory_fields.difference(init_fields) + fields.none? { |field| public_send(field).present? if respond_to?(field) } end def mandatory_fields - required = attributes.except(*(AUTOGENERATED_FIELDS + OPTIONAL_FIELDS)) - - dynamically_not_required = [] - - if reason != "Other" - dynamically_not_required << "other_reason_for_leaving_last_settled_home" - end - - if earnings.to_i.zero? - dynamically_not_required << "incfreq" - end - - if sale_or_letting == "Letting" - dynamically_not_required << "sale_completion_date" - end - - if la.present? - dynamically_not_required << "why_dont_you_know_la" - end - - if tenancy == "Secure (including flexible)" - dynamically_not_required << "tenancylength" - end - - unless net_income_in_soft_max_range? || net_income_in_soft_min_range? - dynamically_not_required << "override_net_income_validation" - end - - unless tenancy == "Other" - dynamically_not_required << "tenancyother" - end - - dynamically_not_required << if net_income_known == "Tenant prefers not to say" - "earnings" - else - "incref" - end - - start_range = (other_hhmemb || 0) + 2 - (start_range..8).each do |n| - dynamically_not_required << "age#{n}" - dynamically_not_required << "sex#{n}" - dynamically_not_required << "relat#{n}" - dynamically_not_required << "ecstat#{n}" - end - - required.delete_if { |key, _value| dynamically_not_required.include?(key) } + form.questions.map(&:id).difference(OPTIONAL_FIELDS, dynamically_not_required) end end diff --git a/app/models/form.rb b/app/models/form.rb index 370a3cd41..4474eae8e 100644 --- a/app/models/form.rb +++ b/app/models/form.rb @@ -53,4 +53,24 @@ class Form c.map { |k, v| v.keys.map { |key| Hash(from: k, to: key, cond: v[key]) } } }.flatten end + + def invalidated_pages(case_log) + pages.reject { |p| p.routed_to?(case_log) } + end + + def invalidated_questions(case_log) + (invalidated_page_questions(case_log) + invalidated_conditional_questions(case_log)).uniq + end + + def invalidated_page_questions(case_log) + invalidated_pages(case_log).flat_map(&:questions) || [] + end + + def invalidated_conditional_questions(case_log) + questions.reject { |q| q.enabled?(case_log) } || [] + end + + def readonly_questions + questions.select(&:read_only?) + end end diff --git a/app/models/form/page.rb b/app/models/form/page.rb index 12b1065d6..c550114a2 100644 --- a/app/models/form/page.rb +++ b/app/models/form/page.rb @@ -22,6 +22,14 @@ class Form::Page end def routed_to?(case_log) + return true unless depends_on || subsection.depends_on + + subsection.enabled?(case_log) && depends_on_met(case_log) + end + +private + + def depends_on_met(case_log) return true unless depends_on depends_on.all? do |question, value| diff --git a/app/models/form/question.rb b/app/models/form/question.rb index 2bf2d4345..edbf96178 100644 --- a/app/models/form/question.rb +++ b/app/models/form/question.rb @@ -80,7 +80,7 @@ class Form::Question # Special case as No is a valid answer but doesn't let you progress and use the service return false if id == "gdpr_acceptance" && case_log[id] == "No" - case_log[id].present? + case_log[id].present? || !case_log.respond_to?(id.to_sym) end private diff --git a/spec/factories/case_log.rb b/spec/factories/case_log.rb index 8a133549b..c472d6f40 100644 --- a/spec/factories/case_log.rb +++ b/spec/factories/case_log.rb @@ -38,7 +38,7 @@ FactoryBot.define do tenant_code { "BZ737" } postcode { "NW1 7TY" } age1 { 35 } - sex1 { "F" } + sex1 { "Female" } ethnic { 2 } national { 4 } prevten { "Private sector tenancy" } @@ -54,7 +54,7 @@ FactoryBot.define do leftreg { "No - they left up to 5 years ago" } reservist { "No" } illness { "Yes" } - preg_occ { "No" } + preg_occ { "Yes" } accessibility_requirements { "No" } condition_effects { "dummy" } tenancy_code { "BZ757" } diff --git a/spec/features/form/validations_spec.rb b/spec/features/form/validations_spec.rb index 4aa72e261..0ba1eff37 100644 --- a/spec/features/form/validations_spec.rb +++ b/spec/features/form/validations_spec.rb @@ -102,6 +102,24 @@ RSpec.describe "validations" do end end + describe "Compound validations" do + context "when you select two compatible answers, that become incompatible if the first answer changes", js: true do + it "clears the second answer on change of the first" do + case_log.update!(other_hhmemb: 1, relat2: "Partner", age2: 32, sex2: "Female", ecstat2: "Not seeking work") + visit("/logs/#{id}/conditional-question") + choose("case-log-preg-occ-yes-field", allow_label_click: true) + click_button("Save and continue") + choose("case-log-cbl-yes-field", allow_label_click: true) + click_button("Save and continue") + page.go_back + click_link("Back") + choose("case-log-preg-occ-no-field", allow_label_click: true) + click_button("Save and continue") + expect(case_log.reload.cbl).to be_nil + end + end + end + describe "Soft Validation" do context "given a weekly net income that is above the expected amount for the given economic status but below the hard max" do let(:case_log) do diff --git a/spec/fixtures/complete_case_log.json b/spec/fixtures/complete_case_log.json index 3683832c9..1c8b61097 100644 --- a/spec/fixtures/complete_case_log.json +++ b/spec/fixtures/complete_case_log.json @@ -50,10 +50,10 @@ "accessibility_requirements": "No", "condition_effects": "dummy", "tenancy_code": "BZ757", - "startdate": "12/12/2020", + "startdate": "12/12/2021", "day": 12, "month": 12, - "year": 2020, + "year": 2021, "startertenancy": "No", "tenancylength": "5", "tenancy": "Secure (including flexible)", diff --git a/spec/fixtures/files/2021_22_lettings_bulk_upload.xlsx b/spec/fixtures/files/2021_22_lettings_bulk_upload.xlsx index c8e86cdbe..ee6644dd4 100644 Binary files a/spec/fixtures/files/2021_22_lettings_bulk_upload.xlsx and b/spec/fixtures/files/2021_22_lettings_bulk_upload.xlsx differ diff --git a/spec/fixtures/forms/2021_2022.json b/spec/fixtures/forms/2021_2022.json index 7bcc5df4f..6edb3608b 100644 --- a/spec/fixtures/forms/2021_2022.json +++ b/spec/fixtures/forms/2021_2022.json @@ -596,7 +596,7 @@ "header": "", "description": "", "questions": { - "why_dont_you_know_la": { + "reason": { "check_answer_label": "Reason for not knowing local authority", "header": "Give a reason why you don’t know the postcode or local authority", "hint_text": "", diff --git a/spec/models/case_log_spec.rb b/spec/models/case_log_spec.rb index 5c2e8f5be..b0fd2dd6b 100644 --- a/spec/models/case_log_spec.rb +++ b/spec/models/case_log_spec.rb @@ -722,8 +722,8 @@ RSpec.describe Form, type: :model do it "cannot be later than the tenancy start date" do expect { CaseLog.create!( - mrcdate: Date.new(2020, 10, 10), - startdate: Date.new(2020, 10, 9), + mrcdate: Date.new(2021, 10, 10), + startdate: Date.new(2021, 10, 9), owning_organisation: owning_organisation, managing_organisation: managing_organisation, ) @@ -731,8 +731,8 @@ RSpec.describe Form, type: :model do expect { CaseLog.create!( - mrcdate: Date.new(2020, 10, 9), - startdate: Date.new(2020, 10, 10), + mrcdate: Date.new(2021, 10, 9), + startdate: Date.new(2021, 10, 10), owning_organisation: owning_organisation, managing_organisation: managing_organisation, ) @@ -771,7 +771,7 @@ RSpec.describe Form, type: :model do it "must have less than two years between the tenancy start date and major repairs date" do expect { CaseLog.create!( - startdate: Date.new(2020, 10, 10), + startdate: Date.new(2021, 10, 10), mrcdate: Date.new(2017, 10, 10), owning_organisation: owning_organisation, managing_organisation: managing_organisation, @@ -784,7 +784,7 @@ RSpec.describe Form, type: :model do it "must have less than 10 years between the tenancy start date and void" do expect { CaseLog.create!( - startdate: Date.new(2020, 10, 10), + startdate: Date.new(2021, 10, 10), property_void_date: Date.new(2009, 10, 10), owning_organisation: owning_organisation, managing_organisation: managing_organisation, @@ -793,7 +793,7 @@ RSpec.describe Form, type: :model do expect { CaseLog.create!( - startdate: Date.new(2020, 10, 10), + startdate: Date.new(2021, 10, 10), property_void_date: Date.new(2015, 10, 10), owning_organisation: owning_organisation, managing_organisation: managing_organisation, @@ -804,8 +804,8 @@ RSpec.describe Form, type: :model do it "must be before the tenancy start date" do expect { CaseLog.create!( - startdate: Date.new(2020, 10, 10), - property_void_date: Date.new(2021, 10, 10), + startdate: Date.new(2021, 10, 10), + property_void_date: Date.new(2021, 10, 11), owning_organisation: owning_organisation, managing_organisation: managing_organisation, ) @@ -813,7 +813,7 @@ RSpec.describe Form, type: :model do expect { CaseLog.create!( - startdate: Date.new(2020, 10, 10), + startdate: Date.new(2021, 10, 10), property_void_date: Date.new(2019, 10, 10), owning_organisation: owning_organisation, managing_organisation: managing_organisation, @@ -824,7 +824,7 @@ RSpec.describe Form, type: :model do it "must be before major repairs date if major repairs date provided" do expect { CaseLog.create!( - startdate: Date.new(2020, 10, 10), + startdate: Date.new(2021, 10, 10), mrcdate: Date.new(2019, 10, 10), property_void_date: Date.new(2019, 11, 11), owning_organisation: owning_organisation, diff --git a/spec/models/form/page_spec.rb b/spec/models/form/page_spec.rb index a0eac90e9..403227e83 100644 --- a/spec/models/form/page_spec.rb +++ b/spec/models/form/page_spec.rb @@ -63,5 +63,17 @@ RSpec.describe Form::Page, type: :model do expect(subject.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" } + let(:page_id) { "declaration" } + let(:completed_case_log) { FactoryBot.build(:case_log, :completed, incfreq: "Weekly") } + + it "evaluates the sections dependencies" do + expect(subject.routed_to?(case_log)).to be false + expect(subject.routed_to?(completed_case_log)).to be true + end + end end end