Submit social housing lettings and sales data (CORE)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

408 lines
12 KiB

class Form::Question
attr_accessor :id, :header, :hint_text, :description, :questions, :disable_clearing_if_not_routed_or_dynamic_answer_options,
: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,
:guidance_partial, :prefix, :suffix, :requires_js, :fields_added, :derived,
:check_answers_card_number, :unresolved_hint_text, :question_number, :hide_question_number_on_page, :plain_label, :error_label
module GuidancePosition
TOP = 1
BOTTOM = 2
end
def initialize(id, hsh, page)
@id = id
@page = page
if hsh
@check_answer_label = hsh["check_answer_label"]
@header = hsh["header"]
@guidance_partial = hsh["guidance_partial"]
@guidance_position = hsh["guidance_position"] || GuidancePosition::TOP
@hint_text = hsh["hint_text"]
@type = hsh["type"]
@min = hsh["min"]
@max = hsh["max"]
@step = hsh["step"]
@width = hsh["width"]
@fields_to_add = hsh["fields-to-add"]
@result_field = hsh["result-field"]
@readonly = hsh["readonly"]
@answer_options = hsh["answer_options"]
@conditional_for = hsh["conditional_for"]
@inferred_answers = hsh["inferred_answers"]
@inferred_check_answers_value = hsh["inferred_check_answers_value"]
@hidden_in_check_answers = hsh["hidden_in_check_answers"]
@derived = hsh["derived"]
@prefix = hsh["prefix"]
@suffix = hsh["suffix"]
@requires_js = hsh["requires_js"]
@fields_added = hsh["fields_added"]
@check_answers_card_number = hsh["check_answers_card_number"] || 0
@unresolved_hint_text = hsh["unresolved_hint_text"]
@question_number = hsh["question_number"]
@hide_question_number_on_page = hsh["hide_question_number_on_page"] || false
@plain_label = hsh["plain_label"]
@error_label = hsh["error_label"]
@disable_clearing_if_not_routed_or_dynamic_answer_options = hsh["disable_clearing_if_not_routed_or_dynamic_answer_options"]
end
end
delegate :subsection, to: :page
delegate :form, to: :subsection
CLDC-1723 Overhaul letting log owning & managing org questions & tests (#1140) * test: check managing org not gone from answer opts when relationship deleted * feat: add current managing org to answer opts * feat: check if managing org exists before trying to show it * wip * test: improve managing orgs opts test when not support * test: improve managing orgs opts tests when support * test: make relationship deletion test consistent with other tests * test: add "(with hint)" to managing org opts test descriptions * test: refactor managing orgs opts tests for support user case * fix: don't call user in get_answer_label in CYA component * style: reorder instance vars and remove old comments in managing_organisation.rb * refactor: ensure label_from_value always accepts log & nil as args * lint * test: pass in log and user in housing provider opts test for support user * test: update housing provider opts tests for non-support user * feat: update housing provider answer opts to include current HP in db * style: add space after user definition * test: make context definition more human-readable * test: refactor housing providers opts tests (not support user) * test: check housing prov. still selectable after deleting relationship * fix: define log and current_user instance vars in label_from_value (housing prov.) * lint * test: update lettings log feature tests to differentiate between different numbers of stock owners when acting as a data coordinator * test: check owning & managing orgs set correctly when a log is created * test: add line breaks and start context descriptions with and (not if) * test: artificially reference org_rel2 to avoid lint offense * feat: don't set log owning org as user's org if that org doesn't hold stock * test: improve test context descriptions in lettings_log_spec * test: finish overhauling owning and managing org tests in lettings_log_spec * test: change let! to let where possible in spec/features/lettings_log_spec.rb * test: change let! to let where possible in spec/models/form/lettings/questions/managing_organisation_spec.rb * test: change let! to let where possible in spec/models/form/lettings/questions/stock_owner_spec.rb * test: remove if statement from "coordinator user's org doesn't hold stock" managing org test * test: remove if statement from "coordinator user's org does hold stock" no managing orgs managing org test * test: remove if statement from "coordinator user's org does hold stock" >=1 managing orgs managing org test plus refactor previous test * test: explicitly reference org rels in "coordinator user's org doesn't hold stock" managing org test * test: don't create vars inside other vars (for tests edited/created in this branch) * chore: save schema changes after migration Co-authored-by: Phil Lee <asmega@users.noreply.github.com>
2 years ago
def answer_label(log, user = nil)
return checkbox_answer_label(log) if type == "checkbox"
return log[id]&.to_formatted_s(:govuk_date).to_s if type == "date"
CLDC-1723 Overhaul letting log owning & managing org questions & tests (#1140) * test: check managing org not gone from answer opts when relationship deleted * feat: add current managing org to answer opts * feat: check if managing org exists before trying to show it * wip * test: improve managing orgs opts test when not support * test: improve managing orgs opts tests when support * test: make relationship deletion test consistent with other tests * test: add "(with hint)" to managing org opts test descriptions * test: refactor managing orgs opts tests for support user case * fix: don't call user in get_answer_label in CYA component * style: reorder instance vars and remove old comments in managing_organisation.rb * refactor: ensure label_from_value always accepts log & nil as args * lint * test: pass in log and user in housing provider opts test for support user * test: update housing provider opts tests for non-support user * feat: update housing provider answer opts to include current HP in db * style: add space after user definition * test: make context definition more human-readable * test: refactor housing providers opts tests (not support user) * test: check housing prov. still selectable after deleting relationship * fix: define log and current_user instance vars in label_from_value (housing prov.) * lint * test: update lettings log feature tests to differentiate between different numbers of stock owners when acting as a data coordinator * test: check owning & managing orgs set correctly when a log is created * test: add line breaks and start context descriptions with and (not if) * test: artificially reference org_rel2 to avoid lint offense * feat: don't set log owning org as user's org if that org doesn't hold stock * test: improve test context descriptions in lettings_log_spec * test: finish overhauling owning and managing org tests in lettings_log_spec * test: change let! to let where possible in spec/features/lettings_log_spec.rb * test: change let! to let where possible in spec/models/form/lettings/questions/managing_organisation_spec.rb * test: change let! to let where possible in spec/models/form/lettings/questions/stock_owner_spec.rb * test: remove if statement from "coordinator user's org doesn't hold stock" managing org test * test: remove if statement from "coordinator user's org does hold stock" no managing orgs managing org test * test: remove if statement from "coordinator user's org does hold stock" >=1 managing orgs managing org test plus refactor previous test * test: explicitly reference org rels in "coordinator user's org doesn't hold stock" managing org test * test: don't create vars inside other vars (for tests edited/created in this branch) * chore: save schema changes after migration Co-authored-by: Phil Lee <asmega@users.noreply.github.com>
2 years ago
answer = label_from_value(log[id], log, user) if log[id].present?
answer_label = [prefix, format_value(answer), suffix_label(log)].join("") if answer
inferred_answer_value(log) || answer_label
end
def notification_banner(_log = nil); end
def get_inferred_answers(log)
return [] unless inferred_answers
enabled_inferred_answers(inferred_answers, log).keys.map do |question_id|
question = form.get_question(question_id, log)
if question.present?
question.label_from_value(log[question_id])
else
Array(question_id.to_s.split(".")).inject(log) { |l, method| l.present? ? l.public_send(*method) : "" }
end
end
end
def get_extra_check_answer_value(_log)
nil
end
def read_only?
!!readonly
end
def enabled?(log)
return true if conditional_on.blank?
conditional_on.all? { |condition| evaluate_condition(condition, log) }
end
def hidden_in_check_answers?(log, current_user = nil)
if hidden_in_check_answers.is_a?(Hash)
form.depends_on_met(hidden_in_check_answers["depends_on"], log)
else
hidden_in_check_answers || !page.routed_to?(log, current_user)
end
end
def displayed_to_user?(log)
page.routed_to?(log, nil) && enabled?(log)
end
def derived?
!!derived
end
def displayed_answer_options(log, _current_user = nil)
answer_options.select do |_key, val|
!val.is_a?(Hash) || !val["depends_on"] || form.depends_on_met(val["depends_on"], log)
end
end
def action_text(log)
if is_derived_or_has_inferred_check_answers_value?(log)
"Change"
elsif type == "checkbox"
answer_options.keys.any? { |key| value_is_yes?(log[key]) } ? "Change" : "Answer"
else
log[id].blank? ? "Answer" : "Change"
end
end
Cldc 1440 household situation section (#1132) * feat: add question page and subsection (#1120) * feat: add question page and subsection * refactor: linting * feat: remove schema rows from other branch * feat: slight refactor, fix tag behaviour and add section tests * test: update and add tests * feat: update status behaviour * feat: update subsection status tag * refactor: linting * [CLDC-857] Add household wheelchair check (#1122) * [CLDC-857] Add household wheelchair check * Hide only if answered * Cldc 1497 ever served armed forces (#1124) * feat: add question and page * test: update tests * refactor: linting and slight test updates * test: fix tests * Cldc 1498 still serving armed forces (#1123) * feat: new question and tests * test: update subsection spec * test: update tests * feat: (future) conflict resolving * feat: more conflict resolution * feat: update db field * test: update id * test: updates * Cldc 1488 last accomodation (#1125) * Add postcode fields * Add previous postcode page and questions * Add last accommodation page to household situation subsection * add previous la known to the db * infer correct location fields * styling * Reorder disability questions (#1127) * [CLDC-1487] Add buyer1 previous tenure question (#1133) * use collection_start_year instead of the startdate (#1128) * [CLDC-1487] Add buyer 1 previous tenure Co-authored-by: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> * feat: fix routing (#1141) * Add last accommodation la question (#1142) * move hint text (#1146) Co-authored-by: natdeanlewissoftwire <94526761+natdeanlewissoftwire@users.noreply.github.com> Co-authored-by: Jack S <113976590+bibblobcode@users.noreply.github.com>
2 years ago
def unanswered?(log)
return answer_options.keys.none? { |key| value_is_yes?(log[key]) } if type == "checkbox"
log[id].blank?
end
def completed?(log)
return answer_options.keys.any? { |key| value_is_yes?(log[key]) } if type == "checkbox"
log[id].present? || !log.respond_to?(id.to_sym) || has_inferred_display_value?(log)
end
def value_from_label(label)
return unless label
case type
when "radio"
answer_options.find { |opt| opt.second["value"] == label.to_s }.first
when "select"
answer_options.find { |opt| opt.second == label.to_s }.first
else
label
end
end
CLDC-1723 Overhaul letting log owning & managing org questions & tests (#1140) * test: check managing org not gone from answer opts when relationship deleted * feat: add current managing org to answer opts * feat: check if managing org exists before trying to show it * wip * test: improve managing orgs opts test when not support * test: improve managing orgs opts tests when support * test: make relationship deletion test consistent with other tests * test: add "(with hint)" to managing org opts test descriptions * test: refactor managing orgs opts tests for support user case * fix: don't call user in get_answer_label in CYA component * style: reorder instance vars and remove old comments in managing_organisation.rb * refactor: ensure label_from_value always accepts log & nil as args * lint * test: pass in log and user in housing provider opts test for support user * test: update housing provider opts tests for non-support user * feat: update housing provider answer opts to include current HP in db * style: add space after user definition * test: make context definition more human-readable * test: refactor housing providers opts tests (not support user) * test: check housing prov. still selectable after deleting relationship * fix: define log and current_user instance vars in label_from_value (housing prov.) * lint * test: update lettings log feature tests to differentiate between different numbers of stock owners when acting as a data coordinator * test: check owning & managing orgs set correctly when a log is created * test: add line breaks and start context descriptions with and (not if) * test: artificially reference org_rel2 to avoid lint offense * feat: don't set log owning org as user's org if that org doesn't hold stock * test: improve test context descriptions in lettings_log_spec * test: finish overhauling owning and managing org tests in lettings_log_spec * test: change let! to let where possible in spec/features/lettings_log_spec.rb * test: change let! to let where possible in spec/models/form/lettings/questions/managing_organisation_spec.rb * test: change let! to let where possible in spec/models/form/lettings/questions/stock_owner_spec.rb * test: remove if statement from "coordinator user's org doesn't hold stock" managing org test * test: remove if statement from "coordinator user's org does hold stock" no managing orgs managing org test * test: remove if statement from "coordinator user's org does hold stock" >=1 managing orgs managing org test plus refactor previous test * test: explicitly reference org rels in "coordinator user's org doesn't hold stock" managing org test * test: don't create vars inside other vars (for tests edited/created in this branch) * chore: save schema changes after migration Co-authored-by: Phil Lee <asmega@users.noreply.github.com>
2 years ago
def label_from_value(value, _log = nil, _user = nil)
return unless value
label = case type
when "radio"
labels = answer_options[value.to_s]
labels["value"] if labels
when "select"
if answer_options[value.to_s].respond_to?(:service_name)
answer_options[value.to_s].service_name
else
answer_options[value.to_s]
end
else
value.to_s
end
label || value.to_s
end
def value_is_yes?(value)
case type
when "checkbox"
value == 1
when "radio"
RADIO_YES_VALUE[id.to_sym]&.include?(value)
else
%w[yes].include?(value.downcase)
end
end
def value_is_no?(value)
case type
when "checkbox"
value && value.zero?
when "radio"
RADIO_NO_VALUE[id.to_sym]&.include?(value)
else
%w[no].include?(value.downcase)
end
end
def value_is_dont_know?(value)
type == "radio" && RADIO_DONT_KNOW_VALUE[id.to_sym]&.include?(value)
end
def value_is_refused?(value)
type == "radio" && RADIO_REFUSED_VALUE[id.to_sym]&.include?(value)
end
def error_display_label
error_label || check_answer_label || header || id.humanize
end
def unanswered_error_message
return I18n.t("validations.declaration.missing") if id == "declaration"
return I18n.t("validations.privacynotice.missing") if id == "privacynotice"
I18n.t("validations.not_answered", question: error_display_label.downcase)
end
def suffix_label(log)
return "" unless suffix
return suffix if suffix.is_a?(String)
label = ""
suffix.each do |s|
condition = s["depends_on"]
next unless condition
answer = log.send(condition.keys.first)
if answer == condition.values.first
label = s["label"]
end
end
label
end
def answer_selected?(log, answer)
return false unless type == "select"
log[id].to_s == answer.id.to_s
end
def top_guidance?
@guidance_partial && @guidance_position == GuidancePosition::TOP
end
def bottom_guidance?
@guidance_partial && @guidance_position == GuidancePosition::BOTTOM
end
def is_derived_or_has_inferred_check_answers_value?(log)
selected_answer_option_is_derived?(log) || has_inferred_check_answers_value?(log)
end
def question_number_string(hidden: false)
if @question_number && !hidden && form.start_date.year >= 2023
"Q#{@question_number}"
end
end
private
def selected_answer_option_is_derived?(log)
selected_option = answer_options&.dig(log[id].to_s.presence)
selected_option.is_a?(Hash) && selected_option["depends_on"] && form.depends_on_met(selected_option["depends_on"], log)
end
def has_inferred_check_answers_value?(log)
return false unless inferred_check_answers_value
inferred_check_answers_value&.any? { |inferred_value| log[inferred_value["condition"].keys.first] == inferred_value["condition"].values.first }
end
def has_inferred_display_value?(log)
inferred_check_answers_value.present? && inferred_check_answers_value.any? { |inferred_value| log[inferred_value["condition"].keys.first] == inferred_value["condition"].values.first }
end
def checkbox_answer_label(log)
answer = []
return "Yes" if id == "declaration" && value_is_yes?(log["declaration"])
answer_options.each { |key, options| value_is_yes?(log[key]) ? answer << options["value"] : nil }
answer.join(", ")
end
def format_value(answer_label)
prefix == "£" ? ActionController::Base.helpers.number_to_currency(answer_label, delimiter: ",", format: "%n") : answer_label
end
def conditional_on
@conditional_on ||= form.conditional_question_conditions.select do |condition|
condition[:to] == id
end
end
def evaluate_condition(condition, log)
case page.questions.find { |q| q.id == condition[:from] }.type
when "numeric"
operator = condition[:cond][/[<>=]+/].to_sym
operand = condition[:cond][/\d+/].to_i
log[condition[:from]].present? && log[condition[:from]].send(operator, operand)
when "text", "radio", "select"
log[condition[:from]].present? && condition[:cond].include?(log[condition[:from]])
else
raise "Not implemented yet"
end
end
def enabled_inferred_answers(inferred_answers, log)
inferred_answers.filter do |_attribute, condition|
condition.all? do |condition_key, condition_value|
log.public_send(condition_key) == condition_value
end
end
end
def inferred_answer_value(log)
return unless inferred_check_answers_value
inferred_answer = inferred_check_answers_value.find { |inferred_value| log[inferred_value["condition"].keys.first] == inferred_value["condition"].values.first }
inferred_answer["value"] if inferred_answer.present?
end
RADIO_YES_VALUE = {
renewal: [1],
postcode_known: [1],
ppcodenk: [1],
previous_la_known: [1],
first_time_property_let_as_social_housing: [1],
wchair: [1],
majorrepairs: [1],
startertenancy: [0],
sheltered: [0, 1],
armedforces: [1, 4, 5],
leftreg: [6],
reservist: [1],
preg_occ: [1],
illness: [1],
underoccupation_benefitcap: [4, 5, 6],
reasonpref: [1],
net_income_known: [0],
household_charge: [0],
is_carehome: [1],
hbrentshortfall: [1],
net_income_value_check: [0],
}.freeze
RADIO_NO_VALUE = {
renewal: [0],
postcode_known: [0],
ppcodenk: [0],
previous_la_known: [0],
first_time_property_let_as_social_housing: [0],
wchair: [0],
majorrepairs: [0],
startertenancy: [1],
sheltered: [2],
armedforces: [2],
leftreg: [4],
reservist: [2],
preg_occ: [2],
illness: [2],
underoccupation_benefitcap: [2],
reasonpref: [2],
net_income_known: [1],
household_charge: [1],
is_carehome: [0],
hbrentshortfall: [2],
net_income_value_check: [1],
}.freeze
RADIO_DONT_KNOW_VALUE = {
sheltered: [3],
underoccupation_benefitcap: [3],
reasonpref: [3],
hbrentshortfall: [3],
layear: [7],
reason_for_leaving_last_settled_home: [32],
hb: [5],
benefits: [3],
unitletas: [3],
illness: [3],
}.freeze
RADIO_REFUSED_VALUE = {
sex1: %w[R],
sex2: %w[R],
sex3: %w[R],
sex4: %w[R],
sex5: %w[R],
sex6: %w[R],
sex7: %w[R],
sex8: %w[R],
relat2: [3],
relat3: [3],
relat4: [3],
relat5: [3],
relat6: [3],
relat7: [3],
relat8: [3],
ecstat1: [10],
ecstat2: [10],
ecstat3: [10],
ecstat4: [10],
ecstat5: [10],
ecstat6: [10],
ecstat7: [10],
ecstat8: [10],
sheltered: [3],
armedforces: [3],
leftreg: [3],
reservist: [3],
preg_occ: [3],
hb: [6],
}.freeze
end