From fbb67f5fabac25241e5a76517e127427fbac0474 Mon Sep 17 00:00:00 2001 From: Arthur Campbell <51094020+arfacamble@users.noreply.github.com> Date: Thu, 2 Feb 2023 17:05:42 +0000 Subject: [PATCH 1/9] CLDC-1923 amend validation message for single ended ranges (#1253) * replace range validation with multiple to account for single-ended ranges * add test for question with min but no max * add min and max to household count question and remove bespoke validation. Remove code allowing max and no min given this situation does not seem to exist int eh form * amend minor typo, add back a soft validation that had mysteriously gone missing --- .../shared_ownership_deposit_value_check.rb | 2 +- .../questions/number_of_others_in_property.rb | 2 + .../sales/household_validations.rb | 8 ---- app/models/validations/shared_validations.rb | 20 ++++++--- config/locales/en.yml | 6 ++- ...ared_ownership_deposit_value_check_spec.rb | 2 +- .../validations/household_validations_spec.rb | 4 +- .../sales/household_validations_spec.rb | 42 ------------------- .../validations/shared_validations_spec.rb | 24 +++++++---- .../requests/lettings_logs_controller_spec.rb | 2 +- 10 files changed, 40 insertions(+), 72 deletions(-) diff --git a/app/models/form/sales/pages/shared_ownership_deposit_value_check.rb b/app/models/form/sales/pages/shared_ownership_deposit_value_check.rb index 907968028..be2126482 100644 --- a/app/models/form/sales/pages/shared_ownership_deposit_value_check.rb +++ b/app/models/form/sales/pages/shared_ownership_deposit_value_check.rb @@ -8,7 +8,7 @@ class Form::Sales::Pages::SharedOwnershipDepositValueCheck < ::Form::Page ] @informative_text = {} @title_text = { - "translation" => "soft_validations.shared_owhership_deposit.title_text", + "translation" => "soft_validations.shared_ownership_deposit.title_text", "arguments" => [ { "key" => "expected_shared_ownership_deposit_value", diff --git a/app/models/form/sales/questions/number_of_others_in_property.rb b/app/models/form/sales/questions/number_of_others_in_property.rb index 3eb1f34e6..cf590291b 100644 --- a/app/models/form/sales/questions/number_of_others_in_property.rb +++ b/app/models/form/sales/questions/number_of_others_in_property.rb @@ -7,5 +7,7 @@ class Form::Sales::Questions::NumberOfOthersInProperty < ::Form::Question @type = "numeric" @hint_text = "You can provide details for a maximum of 4 other people." @width = 2 + @min = 0 + @max = 4 end end diff --git a/app/models/validations/sales/household_validations.rb b/app/models/validations/sales/household_validations.rb index 4f2290268..f9318ab0f 100644 --- a/app/models/validations/sales/household_validations.rb +++ b/app/models/validations/sales/household_validations.rb @@ -1,14 +1,6 @@ module Validations::Sales::HouseholdValidations include Validations::SharedValidations - def validate_number_of_other_people_living_in_the_property(record) - return if record.hholdcount.blank? - - unless record.hholdcount >= 0 && record.hholdcount <= 4 - record.errors.add :hholdcount, I18n.t("validations.numeric.valid", field: "Number of other people living in the property", min: 0, max: 4) - end - end - def validate_household_number_of_other_members(record) (2..6).each do |n| validate_person_age_matches_relationship(record, n) diff --git a/app/models/validations/shared_validations.rb b/app/models/validations/shared_validations.rb index 2d7ac0c9e..8452b9a52 100644 --- a/app/models/validations/shared_validations.rb +++ b/app/models/validations/shared_validations.rb @@ -20,20 +20,16 @@ module Validations::SharedValidations next unless question.min || question.max next unless record[question.id] - field = question.check_answer_label || question.id - min = [question.prefix, number_with_delimiter(question.min, delimiter: ","), question.suffix].join("") - max = [question.prefix, number_with_delimiter(question.max, delimiter: ","), question.suffix].join("") - begin answer = Float(record.public_send("#{question.id}_before_type_cast")) rescue ArgumentError - record.errors.add question.id.to_sym, I18n.t("validations.numeric.valid", field:, min:, max:) + add_range_error(record, question) end next unless answer if (question.min && question.min > answer) || (question.max && question.max < answer) - record.errors.add question.id.to_sym, I18n.t("validations.numeric.valid", field:, min:, max:) + add_range_error(record, question) end end end @@ -108,4 +104,16 @@ private def person_is_partner?(relationship) relationship == "P" end + + def add_range_error(record, question) + field = question.check_answer_label || question.id + min = [question.prefix, number_with_delimiter(question.min, delimiter: ","), question.suffix].join("") if question.min + max = [question.prefix, number_with_delimiter(question.max, delimiter: ","), question.suffix].join("") if question.max + + if min && max + record.errors.add question.id.to_sym, I18n.t("validations.numeric.within_range", field:, min:, max:) + elsif min + record.errors.add question.id.to_sym, I18n.t("validations.numeric.above_min", field:, min:) + end + end end diff --git a/config/locales/en.yml b/config/locales/en.yml index a469aa737..22aa05214 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -131,7 +131,8 @@ en: other_field_missing: "If %{main_field_label} is other then %{other_field_label} must be provided" other_field_not_required: "%{other_field_label} must not be provided if %{main_field_label} was not other" numeric: - valid: "%{field} must be between %{min} and %{max}" + within_range: "%{field} must be between %{min} and %{max}" + above_min: "%{field} must be at least %{min}" date: invalid_date: "Enter a date in the correct format, for example 31 1 2022" outside_collection_window: "Enter a date within the current collection windows" @@ -470,9 +471,10 @@ en: title_text: "You told us the time between the start of the tenancy and the major repairs completion date is more than 2 years" void_date: title_text: "You told us the time between the start of the tenancy and the void date is more than 2 years" - shared_owhership_deposit: + shared_ownership_deposit: title_text: "Mortgage, deposit and cash discount total should equal £%{expected_shared_ownership_deposit_value}" old_persons_shared_ownership: "At least one buyer should be aged over 64 for Older persons’ shared ownership scheme" + staircase_bought_seems_high: "You said %{percentage}% was bought in this staircasing transaction, which seems high. Are you sure?" monthly_charges_over_soft_max: title_text: "The amount of monthly charges is high for this type of property and sale type" diff --git a/spec/models/form/sales/pages/shared_ownership_deposit_value_check_spec.rb b/spec/models/form/sales/pages/shared_ownership_deposit_value_check_spec.rb index afe25f4d3..893a5cb5b 100644 --- a/spec/models/form/sales/pages/shared_ownership_deposit_value_check_spec.rb +++ b/spec/models/form/sales/pages/shared_ownership_deposit_value_check_spec.rb @@ -33,7 +33,7 @@ RSpec.describe Form::Sales::Pages::SharedOwnershipDepositValueCheck, type: :mode it "has the correct title_text" do expect(page.title_text).to eq({ - "translation" => "soft_validations.shared_owhership_deposit.title_text", + "translation" => "soft_validations.shared_ownership_deposit.title_text", "arguments" => [ { "key" => "expected_shared_ownership_deposit_value", diff --git a/spec/models/validations/household_validations_spec.rb b/spec/models/validations/household_validations_spec.rb index 0d1be05ae..daa3feef1 100644 --- a/spec/models/validations/household_validations_spec.rb +++ b/spec/models/validations/household_validations_spec.rb @@ -463,14 +463,14 @@ RSpec.describe Validations::HouseholdValidations do record.hhmemb = -1 household_validator.validate_numeric_min_max(record) expect(record.errors["hhmemb"]) - .to include(match I18n.t("validations.numeric.valid", field: "Number of Household Members", min: 0, max: 8)) + .to include(match I18n.t("validations.numeric.within_range", field: "Number of Household Members", min: 0, max: 8)) end it "validates that the number of household members cannot be more than 8" do record.hhmemb = 9 household_validator.validate_numeric_min_max(record) expect(record.errors["hhmemb"]) - .to include(match I18n.t("validations.numeric.valid", field: "Number of Household Members", min: 0, max: 8)) + .to include(match I18n.t("validations.numeric.within_range", field: "Number of Household Members", min: 0, max: 8)) end it "expects that the number of other household members is between the min and max" do diff --git a/spec/models/validations/sales/household_validations_spec.rb b/spec/models/validations/sales/household_validations_spec.rb index 26d369a35..10367390e 100644 --- a/spec/models/validations/sales/household_validations_spec.rb +++ b/spec/models/validations/sales/household_validations_spec.rb @@ -5,48 +5,6 @@ RSpec.describe Validations::Sales::HouseholdValidations do let(:validator_class) { Class.new { include Validations::Sales::HouseholdValidations } } - describe "#validate_number_of_other_people_living_in_the_property" do - context "when within permitted bounds" do - let(:record) { build(:sales_log, hholdcount: 2) } - - it "does not add an error" do - household_validator.validate_number_of_other_people_living_in_the_property(record) - - expect(record.errors[:hholdcount]).not_to be_present - end - end - - context "when blank" do - let(:record) { build(:sales_log, hholdcount: nil) } - - it "does not add an error" do - household_validator.validate_number_of_other_people_living_in_the_property(record) - - expect(record.errors[:hholdcount]).not_to be_present - end - end - - context "when below lower bound" do - let(:record) { build(:sales_log, hholdcount: -1) } - - it "adds an error" do - household_validator.validate_number_of_other_people_living_in_the_property(record) - - expect(record.errors[:hholdcount]).to be_present - end - end - - context "when higher than upper bound" do - let(:record) { build(:sales_log, hholdcount: 5) } - - it "adds an error" do - household_validator.validate_number_of_other_people_living_in_the_property(record) - - expect(record.errors[:hholdcount]).to be_present - end - end - end - describe "household member validations" do let(:record) { build(:sales_log) } diff --git a/spec/models/validations/shared_validations_spec.rb b/spec/models/validations/shared_validations_spec.rb index c1d8cb3af..151050d8b 100644 --- a/spec/models/validations/shared_validations_spec.rb +++ b/spec/models/validations/shared_validations_spec.rb @@ -18,42 +18,42 @@ RSpec.describe Validations::SharedValidations do record.age1 = "random" shared_validator.validate_numeric_min_max(record) expect(record.errors["age1"]) - .to include(match I18n.t("validations.numeric.valid", field: "Lead tenant’s age", min: 16, max: 120)) + .to include(match I18n.t("validations.numeric.within_range", field: "Lead tenant’s age", min: 16, max: 120)) end it "validates that other household member ages are a number" do record.age2 = "random" shared_validator.validate_numeric_min_max(record) expect(record.errors["age2"]) - .to include(match I18n.t("validations.numeric.valid", field: "Person 2’s age", min: 1, max: 120)) + .to include(match I18n.t("validations.numeric.within_range", field: "Person 2’s age", min: 1, max: 120)) end it "validates that person 1's age is greater than 16" do record.age1 = 15 shared_validator.validate_numeric_min_max(record) expect(record.errors["age1"]) - .to include(match I18n.t("validations.numeric.valid", field: "Lead tenant’s age", min: 16, max: 120)) + .to include(match I18n.t("validations.numeric.within_range", field: "Lead tenant’s age", min: 16, max: 120)) end it "validates that other household member ages are greater than 1" do record.age2 = 0 shared_validator.validate_numeric_min_max(record) expect(record.errors["age2"]) - .to include(match I18n.t("validations.numeric.valid", field: "Person 2’s age", min: 1, max: 120)) + .to include(match I18n.t("validations.numeric.within_range", field: "Person 2’s age", min: 1, max: 120)) end it "validates that person 1's age is less than 121" do record.age1 = 121 shared_validator.validate_numeric_min_max(record) expect(record.errors["age1"]) - .to include(match I18n.t("validations.numeric.valid", field: "Lead tenant’s age", min: 16, max: 120)) + .to include(match I18n.t("validations.numeric.within_range", field: "Lead tenant’s age", min: 16, max: 120)) end it "validates that other household member ages are greater than 121" do record.age2 = 123 shared_validator.validate_numeric_min_max(record) expect(record.errors["age2"]) - .to include(match I18n.t("validations.numeric.valid", field: "Person 2’s age", min: 1, max: 120)) + .to include(match I18n.t("validations.numeric.within_range", field: "Person 2’s age", min: 1, max: 120)) end it "validates that person 1's age is between 16 and 120" do @@ -69,21 +69,27 @@ RSpec.describe Validations::SharedValidations do end end + it "adds the correct validation text when a question has a min but not a max" do + sales_record.savings = -10 + shared_validator.validate_numeric_min_max(sales_record) + expect(sales_record.errors["savings"]).to include(match I18n.t("validations.numeric.above_min", field: "Buyer’s total savings (to nearest £10) before any deposit paid", min: "£0")) + end + context "when validating percent" do it "validates that suffixes are added in the error message" do sales_record.stairbought = 150 shared_validator.validate_numeric_min_max(sales_record) expect(sales_record.errors["stairbought"]) - .to include(match I18n.t("validations.numeric.valid", field: "Percentage bought in this staircasing transaction", min: "0%", max: "100%")) + .to include(match I18n.t("validations.numeric.within_range", field: "Percentage bought in this staircasing transaction", min: "0%", max: "100%")) end end context "when validating price" do it "validates that £ prefix and , is added in the error message" do - sales_record.income1 = "random" + sales_record.income1 = -5 shared_validator.validate_numeric_min_max(sales_record) expect(sales_record.errors["income1"]) - .to include(match I18n.t("validations.numeric.valid", field: "Buyer 1’s gross annual income", min: "£0", max: "£999,999")) + .to include(match I18n.t("validations.numeric.within_range", field: "Buyer 1’s gross annual income", min: "£0", max: "£999,999")) end end end diff --git a/spec/requests/lettings_logs_controller_spec.rb b/spec/requests/lettings_logs_controller_spec.rb index 1bb0afbf9..57311b70d 100644 --- a/spec/requests/lettings_logs_controller_spec.rb +++ b/spec/requests/lettings_logs_controller_spec.rb @@ -82,7 +82,7 @@ RSpec.describe LettingsLogsController, type: :request do it "validates lettings log parameters" do json_response = JSON.parse(response.body) expect(response).to have_http_status(:unprocessable_entity) - expect(json_response["errors"]).to match_array([["offered", [I18n.t("validations.property.offered.relet_number")]], ["age1", [I18n.t("validations.numeric.valid", field: "Lead tenant’s age", min: 16, max: 120)]]]) + expect(json_response["errors"]).to match_array([["offered", [I18n.t("validations.property.offered.relet_number")]], ["age1", [I18n.t("validations.numeric.within_range", field: "Lead tenant’s age", min: 16, max: 120)]]]) end end From 4597d5b50ac66508ec63a35e3eecc7bee93aae9c Mon Sep 17 00:00:00 2001 From: Arthur Campbell <51094020+arfacamble@users.noreply.github.com> Date: Thu, 2 Feb 2023 17:05:51 +0000 Subject: [PATCH 2/9] CLDC-1828 amend copy on soft validation screen for rent value (#1251) * amend copy on soft validation screen for rent value * amend copy after PO review --- config/forms/2021_2022.json | 8 ++++---- config/forms/2022_2023.json | 32 ++++++++------------------------ config/locales/en.yml | 9 +++------ 3 files changed, 15 insertions(+), 34 deletions(-) diff --git a/config/forms/2021_2022.json b/config/forms/2021_2022.json index e47191f6e..49931ee05 100644 --- a/config/forms/2021_2022.json +++ b/config/forms/2021_2022.json @@ -8411,7 +8411,7 @@ } ], "title_text": { - "translation": "soft_validations.rent.min.title_text", + "translation": "soft_validations.rent.outside_range_title", "arguments": [ { "key": "brent", @@ -8421,7 +8421,7 @@ ] }, "informative_text": { - "translation": "soft_validations.rent.min.hint_text", + "translation": "soft_validations.rent.min_hint_text", "arguments": [ { "key": "soft_min_for_period", @@ -8463,7 +8463,7 @@ } ], "title_text": { - "translation": "soft_validations.rent.max.title_text", + "translation": "soft_validations.rent.outside_range_title", "arguments": [ { "key": "brent", @@ -8473,7 +8473,7 @@ ] }, "informative_text": { - "translation": "soft_validations.rent.max.hint_text", + "translation": "soft_validations.rent.max_hint_text", "arguments": [ { "key": "soft_max_for_period", diff --git a/config/forms/2022_2023.json b/config/forms/2022_2023.json index 7c54b67d4..ebd1bd594 100644 --- a/config/forms/2022_2023.json +++ b/config/forms/2022_2023.json @@ -8376,7 +8376,7 @@ } ], "title_text": { - "translation": "soft_validations.rent.min.title_text", + "translation": "soft_validations.rent.outside_range_title", "arguments": [ { "key": "brent", @@ -8385,16 +8385,7 @@ } ] }, - "informative_text": { - "translation": "soft_validations.rent.min.hint_text", - "arguments": [ - { - "key": "soft_min_for_period", - "label": false, - "i18n_template": "soft_min_for_period" - } - ] - }, + "informative_text": {}, "questions": { "rent_value_check": { "check_answer_label": "Total rent confirmation", @@ -8408,7 +8399,8 @@ } ] }, - "header": "Are you sure this is correct?", + "header": "This rent is lower than expected for this property type, in this area. Check:", + "hint_text": "
Are you sure this is correct?
", "type": "interruption_screen", "answer_options": { "0": { @@ -8428,7 +8420,7 @@ } ], "title_text": { - "translation": "soft_validations.rent.max.title_text", + "translation": "soft_validations.rent.outside_range_title", "arguments": [ { "key": "brent", @@ -8437,16 +8429,7 @@ } ] }, - "informative_text": { - "translation": "soft_validations.rent.max.hint_text", - "arguments": [ - { - "key": "soft_max_for_period", - "label": false, - "i18n_template": "soft_max_for_period" - } - ] - }, + "informative_text": {}, "questions": { "rent_value_check": { "check_answer_label": "Total rent confirmation", @@ -8460,7 +8443,8 @@ } ] }, - "header": "Are you sure this is correct?", + "header": "This rent is higher than expected for this property type, in this area. Check:", + "hint_text": "Are you sure this is correct?
", "type": "interruption_screen", "answer_options": { "0": { diff --git a/config/locales/en.yml b/config/locales/en.yml index 22aa05214..63b71301c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -448,12 +448,9 @@ en: in_soft_max_range: message: "Net income is higher than expected based on the lead tenant’s working situation. Are you sure this is correct?" rent: - min: - title_text: "You told us the rent is %{brent}" - hint_text: "The minimum rent expected for this type of property in this local authority is £%{soft_min_for_period}." - max: - title_text: "You told us the rent is %{brent}" - hint_text: "The maximum rent expected for this type of property in this local authority is £%{soft_max_for_period}." + outside_range_title: "You told us the rent is %{brent}" + min_hint_text: "The minimum rent expected for this type of property in this local authority is £%{soft_min_for_period}." + max_hint_text: "The maximum rent expected for this type of property in this local authority is £%{soft_max_for_period}." retirement: min: title: "You told us this person is under %{age} and retired" From 07997ac0eb968cc2085176a5de731432d47aa575 Mon Sep 17 00:00:00 2001 From: Jack <113976590+bibblobcode@users.noreply.github.com> Date: Fri, 3 Feb 2023 09:05:56 +0000 Subject: [PATCH 3/9] CLDC-1931 Update financial year validation message (#1266) --- config/locales/en.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config/locales/en.yml b/config/locales/en.yml index 63b71301c..2321446bd 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -149,7 +149,7 @@ en: intermediate_rent_product_name: blank: "Enter name of other intermediate rent product" saledate: - financial_year: "Date must be from 22/23 financial year" + financial_year: "Date must be from 22/23 financial year, which is between 1st April 2022 and 31st March 2023" startdate: later_than_14_days_after: "The tenancy start date must not be later than 14 days from today’s date" before_scheme_end_date: "The tenancy start date must be before the end date for this supported housing scheme" @@ -377,7 +377,7 @@ en: no_choices: "You cannot answer this question as you told us nobody in the household has a physical or mental health condition (or other illness) expected to last 12 months or more" postcode: discounted_ownership: "Last settled accommodation and discounted ownership property postcodes must match" - + tenancy: length: fixed_term_not_required: "You must only answer the length of the tenancy if it's fixed-term" @@ -390,7 +390,7 @@ en: declaration: missing: "You must show the DLUHC privacy notice to the tenant before you can submit this log." - + privacynotice: missing: "You must show the DLUHC privacy notice to the buyer before you can submit this log." @@ -472,7 +472,7 @@ en: title_text: "Mortgage, deposit and cash discount total should equal £%{expected_shared_ownership_deposit_value}" old_persons_shared_ownership: "At least one buyer should be aged over 64 for Older persons’ shared ownership scheme" staircase_bought_seems_high: "You said %{percentage}% was bought in this staircasing transaction, which seems high. Are you sure?" - monthly_charges_over_soft_max: + monthly_charges_over_soft_max: title_text: "The amount of monthly charges is high for this type of property and sale type" devise: From d6a5f6718e0396cc22f941eb5350973a4fff5433 Mon Sep 17 00:00:00 2001 From: Jack <113976590+bibblobcode@users.noreply.github.com> Date: Fri, 3 Feb 2023 09:08:19 +0000 Subject: [PATCH 4/9] CLDC 1865 Refactor questions to avoid race conditions (#1255) * Extract time helper methods * Add missing specs for BulkUploadController#start * refactor ManagingOrganisation question * refactor StockOwner question --- app/helpers/collection_time_helper.rb | 16 +++++++ .../questions/managing_organisation.rb | 44 ++++++++----------- .../form/lettings/questions/stock_owner.rb | 42 +++++++----------- app/models/form_handler.rb | 16 +------ spec/helpers/collection_time_helper_spec.rb | 34 ++++++++++++++ .../questions/managing_organisation_spec.rb | 32 ++++++++++++++ spec/models/form_handler_spec.rb | 12 ----- spec/requests/bulk_upload_controller_spec.rb | 42 ++++++++++++++++++ 8 files changed, 158 insertions(+), 80 deletions(-) create mode 100644 app/helpers/collection_time_helper.rb create mode 100644 spec/helpers/collection_time_helper_spec.rb diff --git a/app/helpers/collection_time_helper.rb b/app/helpers/collection_time_helper.rb new file mode 100644 index 000000000..8478d06e7 --- /dev/null +++ b/app/helpers/collection_time_helper.rb @@ -0,0 +1,16 @@ +module CollectionTimeHelper + def current_collection_start_year + today = Time.zone.now + window_end_date = Time.zone.local(today.year, 4, 1) + today < window_end_date ? today.year - 1 : today.year + end + + def collection_start_date(date) + window_end_date = Time.zone.local(date.year, 4, 1) + date < window_end_date ? Time.zone.local(date.year - 1, 4, 1) : Time.zone.local(date.year, 4, 1) + end + + def current_collection_start_date + Time.zone.local(current_collection_start_year, 4, 1) + end +end diff --git a/app/models/form/lettings/questions/managing_organisation.rb b/app/models/form/lettings/questions/managing_organisation.rb index e2e3b3e3d..bd9cbb8b9 100644 --- a/app/models/form/lettings/questions/managing_organisation.rb +++ b/app/models/form/lettings/questions/managing_organisation.rb @@ -1,48 +1,45 @@ class Form::Lettings::Questions::ManagingOrganisation < ::Form::Question - attr_accessor :current_user, :log - def initialize(id, hsh, page) super @id = "managing_organisation_id" @check_answer_label = "Managing agent" @header = "Which organisation manages this letting?" @type = "select" - @answer_options = answer_options end - def answer_options + def answer_options(log = nil, user = nil) opts = { "" => "Select an option" } return opts unless ActiveRecord::Base.connected? - return opts unless current_user + return opts unless user return opts unless log if log.managing_organisation.present? opts = opts.merge({ log.managing_organisation.id => log.managing_organisation.name }) end - if current_user.support? + if user.support? if log.owning_organisation.holds_own_stock? opts[log.owning_organisation.id] = "#{log.owning_organisation.name} (Owning organisation)" end else - opts[current_user.organisation.id] = "#{current_user.organisation.name} (Your organisation)" + opts[user.organisation.id] = "#{user.organisation.name} (Your organisation)" end - opts.merge(managing_organisations_answer_options) + orgs = if user.support? + log.owning_organisation + else + user.organisation + end.managing_agents.pluck(:id, :name).to_h + + opts.merge(orgs) end def displayed_answer_options(log, user) - @current_user = user - @log = log - - answer_options + answer_options(log, user) end - def label_from_value(value, log = nil, user = nil) - @log = log - @current_user = user - + def label_from_value(value, _log = nil, _user = nil) return unless value answer_options[value] @@ -53,25 +50,20 @@ class Form::Lettings::Questions::ManagingOrganisation < ::Form::Question end def hidden_in_check_answers?(log, user = nil) - @current_user = user - @current_user.nil? || !@page.routed_to?(log, user) + user.nil? || !@page.routed_to?(log, user) end def enabled true end + def answer_label(log, _current_user = nil) + Organisation.find_by(id: log.managing_organisation_id)&.name + end + private def selected_answer_option_is_derived?(_log) true end - - def managing_organisations_answer_options - if current_user.support? - log.owning_organisation - else - current_user.organisation - end.managing_agents.pluck(:id, :name).to_h - end end diff --git a/app/models/form/lettings/questions/stock_owner.rb b/app/models/form/lettings/questions/stock_owner.rb index e2d2a633d..531341739 100644 --- a/app/models/form/lettings/questions/stock_owner.rb +++ b/app/models/form/lettings/questions/stock_owner.rb @@ -1,6 +1,4 @@ class Form::Lettings::Questions::StockOwner < ::Form::Question - attr_accessor :current_user, :log - def initialize(id, hsh, page) super @id = "owning_organisation_id" @@ -9,38 +7,38 @@ class Form::Lettings::Questions::StockOwner < ::Form::Question @type = "select" end - def answer_options + def answer_options(log = nil, user = nil) answer_opts = { "" => "Select an option" } return answer_opts unless ActiveRecord::Base.connected? - return answer_opts unless current_user + return answer_opts unless user return answer_opts unless log if log.owning_organisation_id.present? answer_opts = answer_opts.merge({ log.owning_organisation.id => log.owning_organisation.name }) end - if !current_user.support? && current_user.organisation.holds_own_stock? - answer_opts[current_user.organisation.id] = "#{current_user.organisation.name} (Your organisation)" + if !user.support? && user.organisation.holds_own_stock? + answer_opts[user.organisation.id] = "#{user.organisation.name} (Your organisation)" end + stock_owners_answer_options = if user.support? + Organisation + else + user.organisation.stock_owners + end.pluck(:id, :name).to_h + answer_opts.merge(stock_owners_answer_options) end def displayed_answer_options(log, user = nil) - @current_user = user - @log = log - - answer_options + answer_options(log, user) end def label_from_value(value, log = nil, user = nil) - @log = log - @current_user = user - return unless value - answer_options[value] + answer_options(log, user)[value] end def derived? @@ -48,13 +46,11 @@ class Form::Lettings::Questions::StockOwner < ::Form::Question end def hidden_in_check_answers?(_log, user = nil) - @current_user = user + return false if user.support? - return false if current_user.support? + stock_owners = user.organisation.stock_owners - stock_owners = current_user.organisation.stock_owners - - if current_user.organisation.holds_own_stock? + if user.organisation.holds_own_stock? stock_owners.count.zero? else stock_owners.count <= 1 @@ -70,12 +66,4 @@ private def selected_answer_option_is_derived?(_log) true end - - def stock_owners_answer_options - if current_user.support? - Organisation - else - current_user.organisation.stock_owners - end.pluck(:id, :name).to_h - end end diff --git a/app/models/form_handler.rb b/app/models/form_handler.rb index 51a0b6177..098a7b429 100644 --- a/app/models/form_handler.rb +++ b/app/models/form_handler.rb @@ -1,5 +1,6 @@ class FormHandler include Singleton + include CollectionTimeHelper attr_reader :forms def initialize @@ -44,21 +45,6 @@ class FormHandler forms end - def current_collection_start_year - today = Time.zone.now - window_end_date = Time.zone.local(today.year, 4, 1) - today < window_end_date ? today.year - 1 : today.year - end - - def collection_start_date(date) - window_end_date = Time.zone.local(date.year, 4, 1) - date < window_end_date ? Time.zone.local(date.year - 1, 4, 1) : Time.zone.local(date.year, 4, 1) - end - - def current_collection_start_date - Time.zone.local(current_collection_start_year, 4, 1) - end - def form_name_from_start_year(year, type) form_mappings = { 0 => "current_#{type}", 1 => "previous_#{type}", -1 => "next_#{type}" } form_mappings[current_collection_start_year - year] diff --git a/spec/helpers/collection_time_helper_spec.rb b/spec/helpers/collection_time_helper_spec.rb new file mode 100644 index 000000000..3b02802f2 --- /dev/null +++ b/spec/helpers/collection_time_helper_spec.rb @@ -0,0 +1,34 @@ +require "rails_helper" + +RSpec.describe CollectionTimeHelper do + let(:current_user) { create(:user, :data_coordinator) } + let(:user) { create(:user, :data_coordinator) } + + around do |example| + Timecop.freeze(now) do + example.run + end + end + + describe "Current collection start year" do + context "when the date is after 1st of April" do + let(:now) { Time.utc(2022, 8, 3) } + + it "returns the same year as the current start year" do + expect(current_collection_start_year).to eq(2022) + end + + it "returns the correct current start date" do + expect(current_collection_start_date).to eq(Time.zone.local(2022, 4, 1)) + end + end + + context "with the date before 1st of April" do + let(:now) { Time.utc(2022, 2, 3) } + + it "returns the previous year as the current start year" do + expect(current_collection_start_year).to eq(2021) + end + end + end +end diff --git a/spec/models/form/lettings/questions/managing_organisation_spec.rb b/spec/models/form/lettings/questions/managing_organisation_spec.rb index 47cd30458..1a64ee973 100644 --- a/spec/models/form/lettings/questions/managing_organisation_spec.rb +++ b/spec/models/form/lettings/questions/managing_organisation_spec.rb @@ -199,4 +199,36 @@ RSpec.describe Form::Lettings::Questions::ManagingOrganisation, type: :model do end end end + + describe "#answer_label" do + context "when answered" do + let(:managing_organisation) { create(:organisation) } + let(:log) { create(:lettings_log, managing_organisation:) } + + it "returns org name" do + expect(question.answer_label(log)).to eq(managing_organisation.name) + end + end + + context "when unanswered" do + let(:log) { create(:lettings_log, managing_organisation: nil) } + + it "returns nil" do + expect(question.answer_label(log)).to be_nil + end + end + + context "when org does not exist" do + let(:managing_organisation) { create(:organisation) } + let(:log) { create(:lettings_log, managing_organisation:) } + + before do + allow(Organisation).to receive(:find_by).and_return(nil) + end + + it "returns nil" do + expect(question.answer_label(log)).to be_nil + end + end + end end diff --git a/spec/models/form_handler_spec.rb b/spec/models/form_handler_spec.rb index c09dbb998..ce9c66e85 100644 --- a/spec/models/form_handler_spec.rb +++ b/spec/models/form_handler_spec.rb @@ -76,10 +76,6 @@ RSpec.describe FormHandler do context "when the date is after 1st of April" do let(:now) { Time.utc(2022, 8, 3) } - it "returns the same year as the current start year" do - expect(form_handler.current_collection_start_year).to eq(2022) - end - it "returns the correct current lettings form name" do expect(form_handler.form_name_from_start_year(2022, "lettings")).to eq("current_lettings") end @@ -103,19 +99,11 @@ RSpec.describe FormHandler do it "returns the correct next sales form name" do expect(form_handler.form_name_from_start_year(2023, "sales")).to eq("next_sales") end - - it "returns the correct current start date" do - expect(form_handler.current_collection_start_date).to eq(Time.zone.local(2022, 4, 1)) - end end context "with the date before 1st of April" do let(:now) { Time.utc(2022, 2, 3) } - it "returns the previous year as the current start year" do - expect(form_handler.current_collection_start_year).to eq(2021) - end - it "returns the correct current lettings form name" do expect(form_handler.form_name_from_start_year(2021, "lettings")).to eq("current_lettings") end diff --git a/spec/requests/bulk_upload_controller_spec.rb b/spec/requests/bulk_upload_controller_spec.rb index fc3afd21c..7aa0d16e0 100644 --- a/spec/requests/bulk_upload_controller_spec.rb +++ b/spec/requests/bulk_upload_controller_spec.rb @@ -13,6 +13,14 @@ RSpec.describe BulkUploadController, type: :request do end context "when a user is not signed in" do + describe "GET #start" do + before { get start_bulk_upload_lettings_logs_path, headers:, params: {} } + + it "does not let you see the bulk upload page" do + expect(response).to redirect_to("/account/sign-in") + end + end + describe "GET #show" do before { get url, headers:, params: {} } @@ -50,6 +58,40 @@ RSpec.describe BulkUploadController, type: :request do end end + describe "GET #start" do + before do + Timecop.freeze(time) + get start_bulk_upload_lettings_logs_path + end + + after do + Timecop.unfreeze + end + + context "when not crossover period" do + let(:time) { Time.utc(2022, 2, 8) } + + it "redirects to bulk upload path" do + expect(request).to redirect_to( + bulk_upload_lettings_log_path( + id: "prepare-your-file", + form: { year: 2021 }, + ), + ) + end + end + + context "when crossover period" do + let(:time) { Time.utc(2022, 6, 8) } + + it "redirects to bulk upload path" do + expect(request).to redirect_to( + bulk_upload_lettings_log_path(id: "year"), + ) + end + end + end + describe "POST #bulk upload" do context "with a valid file based on the upload template" do let(:request) { post url, params: { bulk_upload: { lettings_log_bulk_upload: valid_file } } } From 45ed2e6ea6c613b94ec1c1d89dbd79a9d7bce986 Mon Sep 17 00:00:00 2001 From: Jack <113976590+bibblobcode@users.noreply.github.com> Date: Fri, 3 Feb 2023 09:09:46 +0000 Subject: [PATCH 5/9] CLDC-1849 Add hint to discounted ownership purchase price question (#1250) * CLDC-1849 Add hint to discounted ownership purchase price * Use PurchasePriceDiscountedOwnership in rtb page * show question with updated hint to certain types of answers * rubocop * Only display shared ownership hint in some occations --- .../form/sales/pages/about_price_not_rtb.rb | 2 +- .../form/sales/pages/about_price_rtb.rb | 2 +- app/models/form/sales/pages/purchase_price.rb | 3 +- .../purchase_price_outright_ownership.rb | 14 ++++++++ .../form/sales/questions/purchase_price.rb | 1 + .../purchase_price_outright_ownership.rb | 12 +++++++ .../discounted_ownership_scheme.rb | 1 + .../form/sales/subsections/outright_sale.rb | 2 +- app/models/sales_log.rb | 4 +++ app/views/form/page.html.erb | 21 +++++++++-- .../purchase_price_outright_ownership_spec.rb | 35 +++++++++++++++++++ .../form/sales/pages/purchase_price_spec.rb | 3 +- .../sales/questions/purchase_price_spec.rb | 4 ++- .../discounted_ownership_scheme_spec.rb | 2 ++ spec/models/form_handler_spec.rb | 4 +-- 15 files changed, 98 insertions(+), 12 deletions(-) create mode 100644 app/models/form/sales/pages/purchase_price_outright_ownership.rb create mode 100644 app/models/form/sales/questions/purchase_price_outright_ownership.rb create mode 100644 spec/models/form/sales/pages/purchase_price_outright_ownership_spec.rb diff --git a/app/models/form/sales/pages/about_price_not_rtb.rb b/app/models/form/sales/pages/about_price_not_rtb.rb index ce55d6cd8..c69b9a890 100644 --- a/app/models/form/sales/pages/about_price_not_rtb.rb +++ b/app/models/form/sales/pages/about_price_not_rtb.rb @@ -11,7 +11,7 @@ class Form::Sales::Pages::AboutPriceNotRtb < ::Form::Page def questions @questions ||= [ - Form::Sales::Questions::Value.new(nil, nil, self), + Form::Sales::Questions::PurchasePrice.new(nil, nil, self), Form::Sales::Questions::Grant.new(nil, nil, self), ] end diff --git a/app/models/form/sales/pages/about_price_rtb.rb b/app/models/form/sales/pages/about_price_rtb.rb index ff338f6f6..35696e966 100644 --- a/app/models/form/sales/pages/about_price_rtb.rb +++ b/app/models/form/sales/pages/about_price_rtb.rb @@ -10,7 +10,7 @@ class Form::Sales::Pages::AboutPriceRtb < ::Form::Page def questions @questions ||= [ - Form::Sales::Questions::Value.new(nil, nil, self), + Form::Sales::Questions::PurchasePrice.new(nil, nil, self), Form::Sales::Questions::Discount.new(nil, nil, self), ] end diff --git a/app/models/form/sales/pages/purchase_price.rb b/app/models/form/sales/pages/purchase_price.rb index dc9f7576e..af13fc682 100644 --- a/app/models/form/sales/pages/purchase_price.rb +++ b/app/models/form/sales/pages/purchase_price.rb @@ -2,8 +2,7 @@ class Form::Sales::Pages::PurchasePrice < ::Form::Page def initialize(id, hsh, subsection) super @depends_on = [ - { "ownershipsch" => 3 }, - { "rent_to_buy_full_ownership?" => true }, + { "ownershipsch" => 2, "rent_to_buy_full_ownership?" => false }, ] end diff --git a/app/models/form/sales/pages/purchase_price_outright_ownership.rb b/app/models/form/sales/pages/purchase_price_outright_ownership.rb new file mode 100644 index 000000000..6bf044d01 --- /dev/null +++ b/app/models/form/sales/pages/purchase_price_outright_ownership.rb @@ -0,0 +1,14 @@ +class Form::Sales::Pages::PurchasePriceOutrightOwnership < ::Form::Page + def initialize(id, hsh, subsection) + super + @depends_on = [ + { "outright_sale_or_discounted_with_full_ownership?" => true }, + ] + end + + def questions + @questions ||= [ + Form::Sales::Questions::PurchasePriceOutrightOwnership.new(nil, nil, self), + ] + end +end diff --git a/app/models/form/sales/questions/purchase_price.rb b/app/models/form/sales/questions/purchase_price.rb index af7e8afb9..6242e45e8 100644 --- a/app/models/form/sales/questions/purchase_price.rb +++ b/app/models/form/sales/questions/purchase_price.rb @@ -8,5 +8,6 @@ class Form::Sales::Questions::PurchasePrice < ::Form::Question @min = 0 @width = 5 @prefix = "£" + @hint_text = "For all schemes, including Right to Acquire (RTA), Right to Buy (RTB), Voluntary Right to Buy (VRTB) or Preserved Right to Buy (PRTB) sales, enter the full price of the property without any discount" end end diff --git a/app/models/form/sales/questions/purchase_price_outright_ownership.rb b/app/models/form/sales/questions/purchase_price_outright_ownership.rb new file mode 100644 index 000000000..9824a4629 --- /dev/null +++ b/app/models/form/sales/questions/purchase_price_outright_ownership.rb @@ -0,0 +1,12 @@ +class Form::Sales::Questions::PurchasePriceOutrightOwnership < ::Form::Question + def initialize(id, hsh, page) + super + @id = "value" + @check_answer_label = "Purchase price" + @header = "What is the full purchase price?" + @type = "numeric" + @min = 0 + @width = 5 + @prefix = "£" + end +end diff --git a/app/models/form/sales/subsections/discounted_ownership_scheme.rb b/app/models/form/sales/subsections/discounted_ownership_scheme.rb index 6070afda6..499411ffc 100644 --- a/app/models/form/sales/subsections/discounted_ownership_scheme.rb +++ b/app/models/form/sales/subsections/discounted_ownership_scheme.rb @@ -14,6 +14,7 @@ class Form::Sales::Subsections::DiscountedOwnershipScheme < ::Form::Subsection Form::Sales::Pages::AboutPriceNotRtb.new(nil, nil, self), Form::Sales::Pages::GrantValueCheck.new(nil, nil, self), Form::Sales::Pages::PurchasePrice.new("purchase_price_discounted_ownership", nil, self), + Form::Sales::Pages::PurchasePriceOutrightOwnership.new("purchase_price_outright_ownership", nil, self), Form::Sales::Pages::DepositAndMortgageValueCheck.new("discounted_ownership_deposit_and_mortgage_value_check_after_value_and_discount", nil, self), Form::Sales::Pages::Mortgageused.new("mortgage_used_discounted_ownership", nil, self), Form::Sales::Pages::MortgageValueCheck.new("discounted_ownership_mortgage_used_mortgage_value_check", nil, self), diff --git a/app/models/form/sales/subsections/outright_sale.rb b/app/models/form/sales/subsections/outright_sale.rb index 5d7d05d10..69f77c4b2 100644 --- a/app/models/form/sales/subsections/outright_sale.rb +++ b/app/models/form/sales/subsections/outright_sale.rb @@ -8,7 +8,7 @@ class Form::Sales::Subsections::OutrightSale < ::Form::Subsection def pages @pages ||= [ - Form::Sales::Pages::PurchasePrice.new("purchase_price_outright_sale", nil, self), + Form::Sales::Pages::PurchasePriceOutrightOwnership.new("purchase_price_outright_sale", nil, self), Form::Sales::Pages::Mortgageused.new("mortgage_used_outright_sale", nil, self), Form::Sales::Pages::MortgageValueCheck.new("outright_sale_mortgage_used_mortgage_value_check", nil, self), Form::Sales::Pages::MortgageAmount.new("mortgage_amount_outright_sale", nil, self), diff --git a/app/models/sales_log.rb b/app/models/sales_log.rb index 1ca6ee8a1..b217ff69c 100644 --- a/app/models/sales_log.rb +++ b/app/models/sales_log.rb @@ -132,6 +132,10 @@ class SalesLog < Log type == 29 end + def outright_sale_or_discounted_with_full_ownership? + ownershipsch == 3 || (ownershipsch == 2 && rent_to_buy_full_ownership?) + end + def is_type_discount? type == 18 end diff --git a/app/views/form/page.html.erb b/app/views/form/page.html.erb index c7b18cdda..6d9d44baa 100644 --- a/app/views/form/page.html.erb +++ b/app/views/form/page.html.erb @@ -34,9 +34,26 @@ <%= govuk_section_break(visible: true, size: "m") %> <% end %> <% if question.type == "interruption_screen" %> - <%= render partial: "form/#{question.type}_question", locals: { question:, caption_text: @subsection.label, page_header: @page.header, lettings_log: @log, title_text: @page.title_text, informative_text: @page.informative_text, form: @form, f:, conditional: false } %> + <%= render partial: "form/#{question.type}_question", locals: { + question:, + caption_text: @subsection.label, + page_header: @page.header, + lettings_log: @log, + title_text: @page.title_text, + informative_text: @page.informative_text, + form: @form, + f:, + conditional: false, + } %> <% else %> - <%= render partial: "form/#{question.type}_question", locals: { question:, caption_text: @page.header_partial.present? ? nil : @subsection.label, page_header: @page.header, lettings_log: @log, f:, conditional: false } %> + <%= render partial: "form/#{question.type}_question", locals: { + question:, + caption_text: @page.header_partial.present? ? nil : @subsection.label, + page_header: @page.header, + lettings_log: @log, + f:, + conditional: false, + } %> <% end %> <% end %> diff --git a/spec/models/form/sales/pages/purchase_price_outright_ownership_spec.rb b/spec/models/form/sales/pages/purchase_price_outright_ownership_spec.rb new file mode 100644 index 000000000..88c357034 --- /dev/null +++ b/spec/models/form/sales/pages/purchase_price_outright_ownership_spec.rb @@ -0,0 +1,35 @@ +require "rails_helper" + +RSpec.describe Form::Sales::Pages::PurchasePriceOutrightOwnership, type: :model do + subject(:page) { described_class.new(page_id, page_definition, subsection) } + + let(:page_id) { "purchase_price" } + let(:page_definition) { nil } + let(:subsection) { instance_double(Form::Subsection) } + + it "has correct subsection" do + expect(page.subsection).to eq(subsection) + end + + it "has correct questions" do + expect(page.questions.map(&:id)).to eq(%w[value]) + end + + it "has the correct id" do + expect(page.id).to eq("purchase_price") + end + + it "has the correct header" do + expect(page.header).to be_nil + end + + it "has the correct description" do + expect(page.description).to be_nil + end + + it "has correct depends_on" do + expect(page.depends_on).to eq([ + { "outright_sale_or_discounted_with_full_ownership?" => true }, + ]) + end +end diff --git a/spec/models/form/sales/pages/purchase_price_spec.rb b/spec/models/form/sales/pages/purchase_price_spec.rb index 4d0be701f..6ec2b7ac0 100644 --- a/spec/models/form/sales/pages/purchase_price_spec.rb +++ b/spec/models/form/sales/pages/purchase_price_spec.rb @@ -29,8 +29,7 @@ RSpec.describe Form::Sales::Pages::PurchasePrice, type: :model do it "has correct depends_on" do expect(page.depends_on).to eq([ - { "ownershipsch" => 3 }, - { "rent_to_buy_full_ownership?" => true }, + { "ownershipsch" => 2, "rent_to_buy_full_ownership?" => false }, ]) end end diff --git a/spec/models/form/sales/questions/purchase_price_spec.rb b/spec/models/form/sales/questions/purchase_price_spec.rb index 8894a592f..ce0aaae9c 100644 --- a/spec/models/form/sales/questions/purchase_price_spec.rb +++ b/spec/models/form/sales/questions/purchase_price_spec.rb @@ -32,7 +32,9 @@ RSpec.describe Form::Sales::Questions::PurchasePrice, type: :model do end it "has the correct hint" do - expect(question.hint_text).to be_nil + expect(question.hint_text).to eq( + "For all schemes, including Right to Acquire (RTA), Right to Buy (RTB), Voluntary Right to Buy (VRTB) or Preserved Right to Buy (PRTB) sales, enter the full price of the property without any discount", + ) end it "has correct width" do diff --git a/spec/models/form/sales/subsections/discounted_ownership_scheme_spec.rb b/spec/models/form/sales/subsections/discounted_ownership_scheme_spec.rb index 5702f7783..525ad06b4 100644 --- a/spec/models/form/sales/subsections/discounted_ownership_scheme_spec.rb +++ b/spec/models/form/sales/subsections/discounted_ownership_scheme_spec.rb @@ -12,6 +12,7 @@ RSpec.describe Form::Sales::Subsections::DiscountedOwnershipScheme, type: :model end it "has correct pages" do + puts discounted_ownership_scheme.pages.map(&:id) expect(discounted_ownership_scheme.pages.map(&:id)).to eq( %w[ living_before_purchase_discounted_ownership @@ -20,6 +21,7 @@ RSpec.describe Form::Sales::Subsections::DiscountedOwnershipScheme, type: :model about_price_not_rtb grant_value_check purchase_price_discounted_ownership + purchase_price_outright_ownership discounted_ownership_deposit_and_mortgage_value_check_after_value_and_discount mortgage_used_discounted_ownership discounted_ownership_mortgage_used_mortgage_value_check diff --git a/spec/models/form_handler_spec.rb b/spec/models/form_handler_spec.rb index ce9c66e85..d332d2b21 100644 --- a/spec/models/form_handler_spec.rb +++ b/spec/models/form_handler_spec.rb @@ -52,14 +52,14 @@ RSpec.describe FormHandler do it "is able to load a current sales form" do form = form_handler.get_form("current_sales") expect(form).to be_a(Form) - expect(form.pages.count).to eq(209) + expect(form.pages.count).to eq(210) expect(form.name).to eq("2022_2023_sales") end it "is able to load a previous sales form" do form = form_handler.get_form("previous_sales") expect(form).to be_a(Form) - expect(form.pages.count).to eq(209) + expect(form.pages.count).to eq(210) expect(form.name).to eq("2021_2022_sales") end end From 5077dc035bfd723d9875c99279ca84f58245ce7d Mon Sep 17 00:00:00 2001 From: Phil Lee