Compare commits

...

4 Commits

Author SHA1 Message Date
Samuel Young ef7aa6af25
CLDC-4313: Add maximum bounds to value and mortgage questions (#3295) 3 days ago
Samuel Young 9a2daa5119
CLDC-4395: Fix staircasing sale wording (#3334) 3 days ago
Samuel Young cf551d652c
CLDC-4330: Stop login count resetting on deactivation (#3321) 3 days ago
Samuel Young 1dbcd70bb8
CLDC-4432: Skip invalid field errors if the question is not routed to (#3322) 4 days ago
  1. 6
      app/controllers/auth/confirmations_controller.rb
  2. 1
      app/controllers/auth/passwords_controller.rb
  3. 1
      app/models/form/sales/questions/management_fee.rb
  4. 1
      app/models/form/sales/questions/monthly_rent_before_staircasing.rb
  5. 1
      app/models/form/sales/questions/mortgage_amount.rb
  6. 1
      app/models/form/sales/questions/purchase_price.rb
  7. 1
      app/models/form/sales/questions/value.rb
  8. 2
      app/models/user.rb
  9. 8
      app/services/bulk_upload/lettings/year2025/row_parser.rb
  10. 8
      app/services/bulk_upload/lettings/year2026/row_parser.rb
  11. 8
      app/services/bulk_upload/sales/year2025/row_parser.rb
  12. 8
      app/services/bulk_upload/sales/year2026/row_parser.rb
  13. 2
      config/locales/forms/2024/sales/sale_information.en.yml
  14. 2
      config/locales/forms/2025/sales/sale_information.en.yml
  15. 2
      config/locales/forms/2026/sales/sale_information.en.yml
  16. 5
      db/migrate/20260420151627_add_force_reset_password_on_confirmation_to_users.rb
  17. 3
      db/schema.rb
  18. 16
      lib/tasks/correct_values_missing_max_for_2025_or_later_sales_logs.rake
  19. 4
      spec/models/form/sales/pages/monthly_rent_staircasing_owned_spec.rb
  20. 4
      spec/models/form/sales/pages/monthly_rent_staircasing_spec.rb
  21. 2
      spec/models/form/sales/pages/mortgage_amount_spec.rb
  22. 2
      spec/models/form/sales/pages/purchase_price_outright_ownership_spec.rb
  23. 2
      spec/models/form/sales/pages/purchase_price_spec.rb
  24. 2
      spec/models/form/sales/pages/value_shared_ownership_spec.rb
  25. 9
      spec/models/form/sales/questions/monthly_rent_before_staircasing_spec.rb
  26. 6
      spec/models/form/sales/questions/mortgage_amount_spec.rb
  27. 10
      spec/models/form/sales/questions/purchase_price_spec.rb
  28. 14
      spec/models/form/sales/questions/value_spec.rb
  29. 17
      spec/services/bulk_upload/lettings/year2025/row_parser_spec.rb
  30. 17
      spec/services/bulk_upload/lettings/year2026/row_parser_spec.rb
  31. 18
      spec/services/bulk_upload/sales/year2025/row_parser_spec.rb
  32. 18
      spec/services/bulk_upload/sales/year2026/row_parser_spec.rb

6
app/controllers/auth/confirmations_controller.rb

@ -5,7 +5,11 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController
yield resource if block_given? yield resource if block_given?
if resource.errors.empty? if resource.errors.empty?
if resource.sign_in_count.zero? # previously we reset sign_in_count on deactivation and had only the .zero? check here.
# this would force a password reset both if it was your very first log in, and on your first login after reactivation.
# now we have a specific flag for the latter case as resetting sign_in_count was difficult for auditing.
# note that some deactivated users will have a sign_in_count of 0 and not have this flag set if they were deactivated before we made this change.
if resource.force_reset_password_on_confirmation || resource.sign_in_count.zero?
token = resource.send(:set_reset_password_token) token = resource.send(:set_reset_password_token)
redirect_to "#{edit_user_password_url}?reset_password_token=#{token}&confirmation=true" redirect_to "#{edit_user_password_url}?reset_password_token=#{token}&confirmation=true"
else else

1
app/controllers/auth/passwords_controller.rb

@ -37,6 +37,7 @@ class Auth::PasswordsController < Devise::PasswordsController
if resource.errors.empty? if resource.errors.empty?
resource.unlock_access! if resource.respond_to?(:unlock_access!) resource.unlock_access! if resource.respond_to?(:unlock_access!)
resource.force_reset_password_on_confirmation = false
if Devise.sign_in_after_reset_password if Devise.sign_in_after_reset_password
set_flash_message!(:notice, password_update_flash_message) set_flash_message!(:notice, password_update_flash_message)
resource.after_database_authentication resource.after_database_authentication

1
app/models/form/sales/questions/management_fee.rb

@ -5,6 +5,7 @@ class Form::Sales::Questions::ManagementFee < ::Form::Question
@copy_key = "sales.sale_information.management_fee.management_fee" @copy_key = "sales.sale_information.management_fee.management_fee"
@type = "numeric" @type = "numeric"
@min = 1 @min = 1
@max = form.start_year_2025_or_later? ? 9_999 : nil
@step = 0.01 @step = 0.01
@width = 5 @width = 5
@prefix = "£" @prefix = "£"

1
app/models/form/sales/questions/monthly_rent_before_staircasing.rb

@ -5,6 +5,7 @@ class Form::Sales::Questions::MonthlyRentBeforeStaircasing < ::Form::Question
@copy_key = "sales.sale_information.mrent_staircasing.prestaircasing" @copy_key = "sales.sale_information.mrent_staircasing.prestaircasing"
@type = "numeric" @type = "numeric"
@min = 0 @min = 0
@max = form.start_year_2025_or_later? ? 9_999 : nil
@step = 0.01 @step = 0.01
@width = 5 @width = 5
@prefix = "£" @prefix = "£"

1
app/models/form/sales/questions/mortgage_amount.rb

@ -4,6 +4,7 @@ class Form::Sales::Questions::MortgageAmount < ::Form::Question
@id = "mortgage" @id = "mortgage"
@type = "numeric" @type = "numeric"
@min = 1 @min = 1
@max = form.start_year_2025_or_later? ? 999_999 : nil
@step = 1 @step = 1
@width = 5 @width = 5
@prefix = "£" @prefix = "£"

1
app/models/form/sales/questions/purchase_price.rb

@ -4,6 +4,7 @@ class Form::Sales::Questions::PurchasePrice < ::Form::Question
@id = "value" @id = "value"
@type = "numeric" @type = "numeric"
@min = form.start_year_2026_or_later? ? 15_000 : 0 @min = form.start_year_2026_or_later? ? 15_000 : 0
@max = form.start_year_2025_or_later? ? 999_999 : nil
@step = form.start_year_2026_or_later? ? 1 : 0.01 # 0.01 was a mistake that was fixed in 2026 @step = form.start_year_2026_or_later? ? 1 : 0.01 # 0.01 was a mistake that was fixed in 2026
@width = 5 @width = 5
@prefix = "£" @prefix = "£"

1
app/models/form/sales/questions/value.rb

@ -5,6 +5,7 @@ class Form::Sales::Questions::Value < ::Form::Question
@copy_key = form.start_year_2025_or_later? ? "sales.sale_information.value.#{page.id}" : "sales.sale_information.value" @copy_key = form.start_year_2025_or_later? ? "sales.sale_information.value.#{page.id}" : "sales.sale_information.value"
@type = "numeric" @type = "numeric"
@min = form.start_year_2026_or_later? ? 15_000 : 0 @min = form.start_year_2026_or_later? ? 15_000 : 0
@max = form.start_year_2025_or_later? ? 999_999 : nil
@step = 1 @step = 1
@width = 10 @width = 10
@prefix = "£" @prefix = "£"

2
app/models/user.rb

@ -179,7 +179,7 @@ class User < ApplicationRecord
update!( update!(
active: false, active: false,
confirmed_at: nil, confirmed_at: nil,
sign_in_count: 0, force_reset_password_on_confirmation: true,
initial_confirmation_sent: false, initial_confirmation_sent: false,
reactivate_with_organisation:, reactivate_with_organisation:,
unconfirmed_email: nil, unconfirmed_email: nil,

8
app/services/bulk_upload/lettings/year2025/row_parser.rb

@ -1035,6 +1035,14 @@ private
def add_errors_for_invalid_fields def add_errors_for_invalid_fields
invalid_fields.each do |field| invalid_fields.each do |field|
# ensure questions not routed to are not included in error report
error_questions_ids = field_mapping_for_errors
.select { |_k, fields| fields.map(&:to_s).include?(field.to_s) }
.keys
.map(&:to_s)
error_questions = questions.select { |question| error_questions_ids.include?(question.id) }
next if error_questions.none? { |question| question.page.routed_to?(log, nil) }
errors.delete(field) # take precedence over any other errors as this is a BU format issue errors.delete(field) # take precedence over any other errors as this is a BU format issue
errors.add(field, I18n.t("#{ERROR_BASE_KEY}.invalid_option", question: QUESTIONS[field.to_sym])) errors.add(field, I18n.t("#{ERROR_BASE_KEY}.invalid_option", question: QUESTIONS[field.to_sym]))
end end

8
app/services/bulk_upload/lettings/year2026/row_parser.rb

@ -1113,6 +1113,14 @@ private
def add_errors_for_invalid_fields def add_errors_for_invalid_fields
invalid_fields.each do |field| invalid_fields.each do |field|
# ensure questions not routed to are not included in error report
error_questions_ids = field_mapping_for_errors
.select { |_k, fields| fields.map(&:to_s).include?(field.to_s) }
.keys
.map(&:to_s)
error_questions = questions.select { |question| error_questions_ids.include?(question.id) }
next if error_questions.none? { |question| question.page.routed_to?(log, nil) }
errors.delete(field) # take precedence over any other errors as this is a BU format issue errors.delete(field) # take precedence over any other errors as this is a BU format issue
errors.add(field, I18n.t("#{ERROR_BASE_KEY}.invalid_option", question: QUESTIONS[field.to_sym])) errors.add(field, I18n.t("#{ERROR_BASE_KEY}.invalid_option", question: QUESTIONS[field.to_sym]))
end end

8
app/services/bulk_upload/sales/year2025/row_parser.rb

@ -689,6 +689,14 @@ private
def add_errors_for_invalid_fields def add_errors_for_invalid_fields
invalid_fields.each do |field| invalid_fields.each do |field|
# ensure questions not routed to are not included in error report
error_questions_ids = field_mapping_for_errors
.select { |_k, fields| fields.map(&:to_s).include?(field.to_s) }
.keys
.map(&:to_s)
error_questions = questions.select { |question| error_questions_ids.include?(question.id) }
next if error_questions.none? { |question| question.page.routed_to?(log, nil) }
errors.delete(field) # take precedence over any other errors as this is a BU format issue errors.delete(field) # take precedence over any other errors as this is a BU format issue
errors.add(field, I18n.t("#{ERROR_BASE_KEY}.invalid_option", question: QUESTIONS[field.to_sym])) errors.add(field, I18n.t("#{ERROR_BASE_KEY}.invalid_option", question: QUESTIONS[field.to_sym]))
end end

8
app/services/bulk_upload/sales/year2026/row_parser.rb

@ -750,6 +750,14 @@ private
def add_errors_for_invalid_fields def add_errors_for_invalid_fields
invalid_fields.each do |field| invalid_fields.each do |field|
# ensure questions not routed to are not included in error report
error_questions_ids = field_mapping_for_errors
.select { |_k, fields| fields.map(&:to_s).include?(field.to_s) }
.keys
.map(&:to_s)
error_questions = questions.select { |question| error_questions_ids.include?(question.id) }
next if error_questions.none? { |question| question.page.routed_to?(log, nil) }
errors.delete(field) # take precedence over any other errors as this is a BU format issue errors.delete(field) # take precedence over any other errors as this is a BU format issue
errors.add(field, I18n.t("#{ERROR_BASE_KEY}.invalid_option", question: QUESTIONS[field.to_sym])) errors.add(field, I18n.t("#{ERROR_BASE_KEY}.invalid_option", question: QUESTIONS[field.to_sym]))
end end

2
config/locales/forms/2024/sales/sale_information.en.yml

@ -57,7 +57,7 @@ en:
check_answer_label: "Part of a back-to-back staircasing transaction" check_answer_label: "Part of a back-to-back staircasing transaction"
check_answer_prompt: "Tell us if this is part of a back-to-back staircasing transaction" check_answer_prompt: "Tell us if this is part of a back-to-back staircasing transaction"
hint_text: "" hint_text: ""
question_text: "Is this transaction part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?" question_text: "Was this part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?"
resale: resale:
page_header: "" page_header: ""

2
config/locales/forms/2025/sales/sale_information.en.yml

@ -53,7 +53,7 @@ en:
check_answer_label: "Part of a back-to-back staircasing transaction" check_answer_label: "Part of a back-to-back staircasing transaction"
check_answer_prompt: "Tell us if this is part of a back-to-back staircasing transaction" check_answer_prompt: "Tell us if this is part of a back-to-back staircasing transaction"
hint_text: "Back-to-back staircasing transactions are used as a way for shared owners who own less than 100% of their property to sell on the open market. It involves the shared owner purchasing the remaining share from their landlord and immediately selling 100% of the property to a buyer on the open market. The landlord is then reimbursed for the staircasing transaction through the proceeds of sale to the buyer." hint_text: "Back-to-back staircasing transactions are used as a way for shared owners who own less than 100% of their property to sell on the open market. It involves the shared owner purchasing the remaining share from their landlord and immediately selling 100% of the property to a buyer on the open market. The landlord is then reimbursed for the staircasing transaction through the proceeds of sale to the buyer."
question_text: "Is this transaction part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?" question_text: "Was this part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?"
firststair: firststair:
page_header: "" page_header: ""

2
config/locales/forms/2026/sales/sale_information.en.yml

@ -53,7 +53,7 @@ en:
check_answer_label: "Part of a back-to-back staircasing transaction" check_answer_label: "Part of a back-to-back staircasing transaction"
check_answer_prompt: "Tell us if this is part of a back-to-back staircasing transaction" check_answer_prompt: "Tell us if this is part of a back-to-back staircasing transaction"
hint_text: "Back-to-back staircasing transactions are used as a way for shared owners who own less than 100% of their property to sell on the open market. It involves the shared owner purchasing the remaining share from their landlord and immediately selling 100% of the property to a buyer on the open market. The landlord is then reimbursed for the staircasing transaction through the proceeds of sale to the buyer." hint_text: "Back-to-back staircasing transactions are used as a way for shared owners who own less than 100% of their property to sell on the open market. It involves the shared owner purchasing the remaining share from their landlord and immediately selling 100% of the property to a buyer on the open market. The landlord is then reimbursed for the staircasing transaction through the proceeds of sale to the buyer."
question_text: "Is this transaction part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?" question_text: "Was this part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?"
firststair: firststair:
page_header: "" page_header: ""

5
db/migrate/20260420151627_add_force_reset_password_on_confirmation_to_users.rb

@ -0,0 +1,5 @@
class AddForceResetPasswordOnConfirmationToUsers < ActiveRecord::Migration[7.2]
def change
add_column :users, :force_reset_password_on_confirmation, :boolean, default: false
end
end

3
db/schema.rb

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.2].define(version: 2026_03_05_095832) do ActiveRecord::Schema[7.2].define(version: 2026_04_20_151627) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -924,6 +924,7 @@ ActiveRecord::Schema[7.2].define(version: 2026_03_05_095832) do
t.datetime "discarded_at" t.datetime "discarded_at"
t.string "phone_extension" t.string "phone_extension"
t.datetime "values_updated_at" t.datetime "values_updated_at"
t.boolean "force_reset_password_on_confirmation", default: false
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
t.index ["email"], name: "index_users_on_email", unique: true t.index ["email"], name: "index_users_on_email", unique: true
t.index ["encrypted_otp_secret_key"], name: "index_users_on_encrypted_otp_secret_key", unique: true t.index ["encrypted_otp_secret_key"], name: "index_users_on_encrypted_otp_secret_key", unique: true

16
lib/tasks/correct_values_missing_max_for_2025_or_later_sales_logs.rake

@ -0,0 +1,16 @@
desc "Clears mortgage, purchase price (the 'value' field), monthly rent before staircasing and management fee values for sales logs in the database if they are over their new max"
task correct_values_missing_max_for_2025_or_later_sales_logs: :environment do
mortgage_incorrect_logs = SalesLog.filter_by_year_or_later(2025).where("mortgage > 999999")
value_incorrect_logs = SalesLog.filter_by_year_or_later(2025).where("value > 999999")
mrentprestaircasing_incorrect_logs = SalesLog.filter_by_year_or_later(2025).where("mrentprestaircasing > 9999")
management_fee_incorrect_logs = SalesLog.filter_by_year_or_later(2025).where("management_fee > 9999")
all_incorrect_logs = (mortgage_incorrect_logs + value_incorrect_logs + mrentprestaircasing_incorrect_logs + management_fee_incorrect_logs).uniq
puts "Correcting #{all_incorrect_logs.count} sales logs, #{all_incorrect_logs.map(&:id)}"
mortgage_incorrect_logs.update!(mortgage: nil)
value_incorrect_logs.update!(value: nil)
mrentprestaircasing_incorrect_logs.update!(mrentprestaircasing: nil)
management_fee_incorrect_logs.update!(management_fee: nil)
puts "Done"
end

4
spec/models/form/sales/pages/monthly_rent_staircasing_owned_spec.rb

@ -1,11 +1,13 @@
require "rails_helper" require "rails_helper"
RSpec.describe Form::Sales::Pages::MonthlyRentStaircasingOwned, type: :model do RSpec.describe Form::Sales::Pages::MonthlyRentStaircasingOwned, type: :model do
include CollectionTimeHelper
subject(:page) { described_class.new(page_id, page_definition, subsection) } subject(:page) { described_class.new(page_id, page_definition, subsection) }
let(:page_id) { nil } let(:page_id) { nil }
let(:page_definition) { nil } let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2025, 4, 1))) } let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: current_collection_start_date, start_year_2025_or_later?: true)) }
it "has correct subsection" do it "has correct subsection" do
expect(page.subsection).to eq(subsection) expect(page.subsection).to eq(subsection)

4
spec/models/form/sales/pages/monthly_rent_staircasing_spec.rb

@ -1,11 +1,13 @@
require "rails_helper" require "rails_helper"
RSpec.describe Form::Sales::Pages::MonthlyRentStaircasing, type: :model do RSpec.describe Form::Sales::Pages::MonthlyRentStaircasing, type: :model do
include CollectionTimeHelper
subject(:page) { described_class.new(page_id, page_definition, subsection) } subject(:page) { described_class.new(page_id, page_definition, subsection) }
let(:page_id) { nil } let(:page_id) { nil }
let(:page_definition) { nil } let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2025, 4, 1))) } let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: current_collection_start_year, start_year_2025_or_later?: true)) }
it "has correct subsection" do it "has correct subsection" do
expect(page.subsection).to eq(subsection) expect(page.subsection).to eq(subsection)

2
spec/models/form/sales/pages/mortgage_amount_spec.rb

@ -5,7 +5,7 @@ RSpec.describe Form::Sales::Pages::MortgageAmount, type: :model do
let(:page_id) { nil } let(:page_id) { nil }
let(:page_definition) { nil } let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection, id: "shared_ownership_initial_purchase", form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1), start_year_2026_or_later?: false)) } let(:subsection) { instance_double(Form::Subsection, id: "shared_ownership_initial_purchase", form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1), start_year_2025_or_later?: true, start_year_2026_or_later?: true)) }
it "has correct subsection" do it "has correct subsection" do
expect(page.subsection).to eq(subsection) expect(page.subsection).to eq(subsection)

2
spec/models/form/sales/pages/purchase_price_outright_ownership_spec.rb

@ -5,7 +5,7 @@ RSpec.describe Form::Sales::Pages::PurchasePriceOutrightOwnership, type: :model
let(:page_id) { "purchase_price" } let(:page_id) { "purchase_price" }
let(:page_definition) { nil } let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection, id: "discounted_ownership_scheme", form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1), start_year_2026_or_later?: false)) } let(:subsection) { instance_double(Form::Subsection, id: "discounted_ownership_scheme", form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1), start_year_2025_or_later?: true, start_year_2026_or_later?: false)) }
it "has correct subsection" do it "has correct subsection" do
expect(page.subsection).to eq(subsection) expect(page.subsection).to eq(subsection)

2
spec/models/form/sales/pages/purchase_price_spec.rb

@ -8,7 +8,7 @@ RSpec.describe Form::Sales::Pages::PurchasePrice, type: :model do
let(:subsection) { instance_double(Form::Subsection) } let(:subsection) { instance_double(Form::Subsection) }
before do before do
allow(subsection).to receive_messages(form: instance_double(Form, start_year_2024_or_later?: false, start_date: Time.zone.local(2023, 4, 1), start_year_2026_or_later?: false), id: "discounted_ownership_scheme") allow(subsection).to receive_messages(form: instance_double(Form, start_year_2024_or_later?: true, start_date: Time.zone.local(2023, 4, 1), start_year_2025_or_later?: true, start_year_2026_or_later?: false), id: "discounted_ownership_scheme")
end end
it "has correct subsection" do it "has correct subsection" do

2
spec/models/form/sales/pages/value_shared_ownership_spec.rb

@ -5,7 +5,7 @@ RSpec.describe Form::Sales::Pages::ValueSharedOwnership, type: :model do
let(:page_id) { "value_shared_ownership" } let(:page_id) { "value_shared_ownership" }
let(:page_definition) { nil } let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1), start_year_2026_or_later?: false), id: "shared_ownership") } let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1), start_year_2025_or_later?: true, start_year_2026_or_later?: false), id: "shared_ownership") }
before do before do
allow(page.subsection.form).to receive(:start_year_2025_or_later?).and_return(false) allow(page.subsection.form).to receive(:start_year_2025_or_later?).and_return(false)

9
spec/models/form/sales/questions/monthly_rent_before_staircasing_spec.rb

@ -1,11 +1,14 @@
require "rails_helper" require "rails_helper"
RSpec.describe Form::Sales::Questions::MonthlyRentBeforeStaircasing, type: :model do RSpec.describe Form::Sales::Questions::MonthlyRentBeforeStaircasing, type: :model do
include CollectionTimeHelper
subject(:question) { described_class.new(question_id, question_definition, page) } subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil } let(:question_id) { nil }
let(:question_definition) { nil } let(:question_definition) { nil }
let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1)))) } let(:start_year_2025_or_later?) { true }
let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date: current_collection_start_date, start_year_2025_or_later?: start_year_2025_or_later?))) }
it "has correct page" do it "has correct page" do
expect(question.page).to eq(page) expect(question.page).to eq(page)
@ -34,4 +37,8 @@ RSpec.describe Form::Sales::Questions::MonthlyRentBeforeStaircasing, type: :mode
it "has correct min" do it "has correct min" do
expect(question.min).to eq(0) expect(question.min).to eq(0)
end end
it "has correct max" do
expect(question.max).to eq(9_999)
end
end end

6
spec/models/form/sales/questions/mortgage_amount_spec.rb

@ -5,7 +5,7 @@ RSpec.describe Form::Sales::Questions::MortgageAmount, type: :model do
let(:question_id) { nil } let(:question_id) { nil }
let(:question_definition) { nil } let(:question_definition) { nil }
let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, id: "shared_ownership_initial_purchase", form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1), start_year_2026_or_later?: false))) } let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, id: "shared_ownership_initial_purchase", form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1), start_year_2025_or_later?: true, start_year_2026_or_later?: true))) }
it "has correct page" do it "has correct page" do
expect(question.page).to be(page) expect(question.page).to be(page)
@ -35,6 +35,10 @@ RSpec.describe Form::Sales::Questions::MortgageAmount, type: :model do
expect(question.min).to be(1) expect(question.min).to be(1)
end end
it "has correct max" do
expect(question.max).to eq(999_999)
end
context "when the mortgage is not used" do context "when the mortgage is not used" do
let(:log) { build(:sales_log, :completed, mortgageused: 2, deposit: nil) } let(:log) { build(:sales_log, :completed, mortgageused: 2, deposit: nil) }

10
spec/models/form/sales/questions/purchase_price_spec.rb

@ -9,7 +9,7 @@ RSpec.describe Form::Sales::Questions::PurchasePrice, type: :model do
let(:question_definition) { nil } let(:question_definition) { nil }
let(:start_year) { current_collection_start_year } let(:start_year) { current_collection_start_year }
let(:start_year_2026_or_later?) { false } let(:start_year_2026_or_later?) { false }
let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, id: "shared_ownership_initial_purchase", form: instance_double(Form, start_date: collection_start_date_for_year(start_year), start_year_2026_or_later?: start_year_2026_or_later?))) } let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, id: "shared_ownership_initial_purchase", form: instance_double(Form, start_date: collection_start_date_for_year(start_year), start_year_2026_or_later?: start_year_2026_or_later?, start_year_2025_or_later?: true))) }
it "has correct page" do it "has correct page" do
expect(question.page).to eq(page) expect(question.page).to eq(page)
@ -65,6 +65,10 @@ RSpec.describe Form::Sales::Questions::PurchasePrice, type: :model do
it "has correct min" do it "has correct min" do
expect(question.min).to eq(0) expect(question.min).to eq(0)
end end
it "has correct max" do
expect(question.max).to eq(999_999)
end
end end
context "with year 2026", metadata: { year: 26 } do context "with year 2026", metadata: { year: 26 } do
@ -74,5 +78,9 @@ RSpec.describe Form::Sales::Questions::PurchasePrice, type: :model do
it "has correct min" do it "has correct min" do
expect(question.min).to eq(15_000) expect(question.min).to eq(15_000)
end end
it "has correct max" do
expect(question.max).to eq(999_999)
end
end end
end end

14
spec/models/form/sales/questions/value_spec.rb

@ -9,11 +9,7 @@ RSpec.describe Form::Sales::Questions::Value, type: :model do
let(:question_definition) { nil } let(:question_definition) { nil }
let(:start_year) { current_collection_start_year } let(:start_year) { current_collection_start_year }
let(:start_year_2026_or_later?) { false } let(:start_year_2026_or_later?) { false }
let(:page) { instance_double(Form::Page, id: "value_shared_ownership", subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date: collection_start_date_for_year(start_year), start_year_2026_or_later?: start_year_2026_or_later?), id: "shared_ownership")) } let(:page) { instance_double(Form::Page, id: "value_shared_ownership", subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date: collection_start_date_for_year(start_year), start_year_2026_or_later?: start_year_2026_or_later?, start_year_2025_or_later?: true), id: "shared_ownership")) }
before do
allow(page.subsection.form).to receive(:start_year_2025_or_later?).and_return(false)
end
it "has correct page" do it "has correct page" do
expect(question.page).to eq(page) expect(question.page).to eq(page)
@ -45,6 +41,10 @@ RSpec.describe Form::Sales::Questions::Value, type: :model do
it "has correct min" do it "has correct min" do
expect(question.min).to eq(0) expect(question.min).to eq(0)
end end
it "has correct max" do
expect(question.max).to eq(999_999)
end
end end
context "with year 2026", metadata: { year: 26 } do context "with year 2026", metadata: { year: 26 } do
@ -54,5 +54,9 @@ RSpec.describe Form::Sales::Questions::Value, type: :model do
it "has correct min" do it "has correct min" do
expect(question.min).to eq(15_000) expect(question.min).to eq(15_000)
end end
it "has correct max" do
expect(question.max).to eq(999_999)
end
end end
end end

17
spec/services/bulk_upload/lettings/year2025/row_parser_spec.rb

@ -646,9 +646,9 @@ RSpec.describe BulkUpload::Lettings::Year2025::RowParser do
end end
describe "invalid fields" do describe "invalid fields" do
let(:attributes) { setup_section_params.merge({ field_45: 0 }) }
context "when a field has been marked as invalid" do context "when a field has been marked as invalid" do
let(:attributes) { setup_section_params.merge({ field_45: 0 }) }
before do before do
parser.add_invalid_field("field_45") parser.add_invalid_field("field_45")
end end
@ -659,6 +659,19 @@ RSpec.describe BulkUpload::Lettings::Year2025::RowParser do
expect(parser.errors[:field_45]).to include(I18n.t("validations.lettings.2025.bulk_upload.invalid_option", question: "What is the lead tenant’s nationality?")) expect(parser.errors[:field_45]).to include(I18n.t("validations.lettings.2025.bulk_upload.invalid_option", question: "What is the lead tenant’s nationality?"))
end end
end end
context "when a field has been marked as invalid but it is not routed to" do
let(:attributes) { setup_section_params.merge({ field_117: 2 }) }
before do
parser.add_invalid_field("field_118")
end
it "does not set an error on that field" do
parser.valid?
expect(parser.errors[:field_118].size).to eq(0)
end
end
end end
end end
end end

17
spec/services/bulk_upload/lettings/year2026/row_parser_spec.rb

@ -538,9 +538,9 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do
end end
describe "invalid fields" do describe "invalid fields" do
let(:attributes) { setup_section_params.merge({ field_46: 0 }) }
context "when a field has been marked as invalid" do context "when a field has been marked as invalid" do
let(:attributes) { setup_section_params.merge({ field_46: 0 }) }
before do before do
parser.add_invalid_field("field_46") parser.add_invalid_field("field_46")
end end
@ -551,6 +551,19 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do
expect(parser.errors[:field_46]).to include(match(I18n.t("validations.lettings.2026.bulk_upload.invalid_option", question: "What is the lead tenant’s nationality?"))) expect(parser.errors[:field_46]).to include(match(I18n.t("validations.lettings.2026.bulk_upload.invalid_option", question: "What is the lead tenant’s nationality?")))
end end
end end
context "when a field has been marked as invalid but it is not routed to" do
let(:attributes) { setup_section_params.merge({ field_135: 2 }) }
before do
parser.add_invalid_field("field_136")
end
it "does not set an error on that field" do
parser.valid?
expect(parser.errors[:field_136].size).to eq(0)
end
end
end end
end end

18
spec/services/bulk_upload/sales/year2025/row_parser_spec.rb

@ -338,9 +338,10 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
end end
describe "invalid fields" do describe "invalid fields" do
let(:attributes) { setup_section_params.merge({ field_31: 0 }) }
context "when a field has been marked as invalid" do context "when a field has been marked as invalid" do
# field_34 nationality is only shown if field_10 staircasing is no
let(:attributes) { setup_section_params.merge({ field_10: 2, field_31: 0 }) }
before do before do
parser.add_invalid_field("field_31") parser.add_invalid_field("field_31")
end end
@ -351,6 +352,19 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
expect(parser.errors[:field_31]).to include(match(I18n.t("validations.sales.2025.bulk_upload.invalid_option", question: "What is buyer 1’s nationality?"))) expect(parser.errors[:field_31]).to include(match(I18n.t("validations.sales.2025.bulk_upload.invalid_option", question: "What is buyer 1’s nationality?")))
end end
end end
context "when a field has been marked as invalid but it is not routed to" do
let(:attributes) { setup_section_params.merge({ field_10: 1, field_31: 0 }) }
before do
parser.add_invalid_field("field_31")
end
it "does not set an error on that field" do
parser.valid?
expect(parser.errors[:field_31].size).to eq(0)
end
end
end end
end end
end end

18
spec/services/bulk_upload/sales/year2026/row_parser_spec.rb

@ -344,9 +344,10 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do
end end
describe "invalid fields" do describe "invalid fields" do
let(:attributes) { setup_section_params.merge({ field_34: 0 }) }
context "when a field has been marked as invalid" do context "when a field has been marked as invalid" do
# field_34 nationality is only shown if field_10 staircasing is no
let(:attributes) { setup_section_params.merge({ field_10: 2, field_34: 0 }) }
before do before do
parser.add_invalid_field("field_34") parser.add_invalid_field("field_34")
end end
@ -357,6 +358,19 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do
expect(parser.errors[:field_34]).to include(match(I18n.t("validations.sales.2026.bulk_upload.invalid_option", question: "What is buyer 1's nationality?"))) expect(parser.errors[:field_34]).to include(match(I18n.t("validations.sales.2026.bulk_upload.invalid_option", question: "What is buyer 1's nationality?")))
end end
end end
context "when a field has been marked as invalid but it is not routed to" do
let(:attributes) { setup_section_params.merge({ field_10: 1, field_34: 0 }) }
before do
parser.add_invalid_field("field_34")
end
it "does not set an error on that field" do
parser.valid?
expect(parser.errors[:field_34].size).to eq(0)
end
end
end end
end end
end end

Loading…
Cancel
Save