Browse Source

CLDC-883: Clear dependent answers when the dependency value changes (#206)

* Failing spec

* Reset invalidated fields

* Page routing can also depend on subsection
pull/217/head
baarkerlounger 3 years ago committed by GitHub
parent
commit
37fbf16c1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      app/models/bulk_upload.rb
  2. 74
      app/models/case_log.rb
  3. 20
      app/models/form.rb
  4. 8
      app/models/form/page.rb
  5. 2
      app/models/form/question.rb
  6. 4
      spec/factories/case_log.rb
  7. 18
      spec/features/form/validations_spec.rb
  8. 4
      spec/fixtures/complete_case_log.json
  9. BIN
      spec/fixtures/files/2021_22_lettings_bulk_upload.xlsx
  10. 2
      spec/fixtures/forms/2021_2022.json
  11. 22
      spec/models/case_log_spec.rb
  12. 12
      spec/models/form/page_spec.rb

8
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

74
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

20
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

8
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|

2
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

4
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" }

18
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

4
spec/fixtures/complete_case_log.json vendored

@ -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)",

BIN
spec/fixtures/files/2021_22_lettings_bulk_upload.xlsx vendored

Binary file not shown.

2
spec/fixtures/forms/2021_2022.json vendored

@ -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": "",

22
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,

12
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

Loading…
Cancel
Save