Browse Source

Merge branch 'main' into CLDC-3723-Add-validation-on-location-number-of-units

pull/2798/head
Manny Dinssa 1 year ago committed by GitHub
parent
commit
24261437b4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      Gemfile
  2. 5
      app/components/create_log_actions_component.html.erb
  3. 8
      app/components/create_log_actions_component.rb
  4. 27
      app/controllers/csv_downloads_controller.rb
  5. 14
      app/controllers/lettings_logs_controller.rb
  6. 1
      app/controllers/merge_requests_controller.rb
  7. 14
      app/controllers/sales_logs_controller.rb
  8. 2
      app/controllers/schemes_controller.rb
  9. 4
      app/helpers/merge_requests_helper.rb
  10. 13
      app/jobs/email_csv_job.rb
  11. 13
      app/jobs/scheme_email_csv_job.rb
  12. 10
      app/models/csv_download.rb
  13. 5
      app/models/derived_variables/lettings_log_variables.rb
  14. 5
      app/models/derived_variables/sales_log_variables.rb
  15. 5
      app/models/form/lettings/pages/no_address_found.rb
  16. 1
      app/models/form/lettings/subsections/household_needs.rb
  17. 2
      app/models/form/page.rb
  18. 2
      app/models/form/question.rb
  19. 2
      app/models/form/sales/pages/buyer_interview.rb
  20. 1
      app/models/form/sales/pages/deposit.rb
  21. 1
      app/models/form/sales/pages/deposit_discount.rb
  22. 1
      app/models/form/sales/pages/discount.rb
  23. 1
      app/models/form/sales/pages/equity.rb
  24. 13
      app/models/form/sales/pages/estate_management_fee.rb
  25. 1
      app/models/form/sales/pages/extra_borrowing.rb
  26. 1
      app/models/form/sales/pages/grant.rb
  27. 12
      app/models/form/sales/pages/handover_date_check.rb
  28. 12
      app/models/form/sales/pages/living_before_purchase.rb
  29. 1
      app/models/form/sales/pages/monthly_rent.rb
  30. 1
      app/models/form/sales/pages/mortgage_amount.rb
  31. 1
      app/models/form/sales/pages/mortgage_lender.rb
  32. 1
      app/models/form/sales/pages/mortgage_lender_other.rb
  33. 1
      app/models/form/sales/pages/mortgage_length.rb
  34. 1
      app/models/form/sales/pages/mortgageused.rb
  35. 1
      app/models/form/sales/pages/previous_bedrooms.rb
  36. 1
      app/models/form/sales/pages/previous_property_type.rb
  37. 1
      app/models/form/sales/pages/previous_tenure.rb
  38. 2
      app/models/form/sales/pages/privacy_notice.rb
  39. 1
      app/models/form/sales/pages/resale.rb
  40. 2
      app/models/form/sales/pages/staircase.rb
  41. 1
      app/models/form/sales/pages/value_shared_ownership.rb
  42. 2
      app/models/form/sales/questions/buyer_interview.rb
  43. 1
      app/models/form/sales/questions/deposit_amount.rb
  44. 1
      app/models/form/sales/questions/deposit_discount.rb
  45. 1
      app/models/form/sales/questions/discount.rb
  46. 1
      app/models/form/sales/questions/equity.rb
  47. 1
      app/models/form/sales/questions/extra_borrowing.rb
  48. 1
      app/models/form/sales/questions/fromprop.rb
  49. 1
      app/models/form/sales/questions/grant.rb
  50. 24
      app/models/form/sales/questions/has_management_fee.rb
  51. 12
      app/models/form/sales/questions/management_fee.rb
  52. 1
      app/models/form/sales/questions/monthly_rent.rb
  53. 1
      app/models/form/sales/questions/mortgage_amount.rb
  54. 1
      app/models/form/sales/questions/mortgage_lender.rb
  55. 1
      app/models/form/sales/questions/mortgage_lender_other.rb
  56. 1
      app/models/form/sales/questions/mortgage_length.rb
  57. 1
      app/models/form/sales/questions/mortgageused.rb
  58. 1
      app/models/form/sales/questions/previous_bedrooms.rb
  59. 1
      app/models/form/sales/questions/previous_tenure.rb
  60. 2
      app/models/form/sales/questions/privacy_notice.rb
  61. 1
      app/models/form/sales/questions/resale.rb
  62. 1
      app/models/form/sales/questions/value.rb
  63. 10
      app/models/form/sales/sections/sale_information.rb
  64. 1
      app/models/form/sales/subsections/discounted_ownership_scheme.rb
  65. 15
      app/models/form/sales/subsections/household_situation.rb
  66. 15
      app/models/form/sales/subsections/income_benefits_and_savings.rb
  67. 15
      app/models/form/sales/subsections/other_household_information.rb
  68. 1
      app/models/form/sales/subsections/outright_sale.rb
  69. 48
      app/models/form/sales/subsections/shared_ownership_initial_purchase.rb
  70. 2
      app/models/form/sales/subsections/shared_ownership_scheme.rb
  71. 4
      app/models/form/section.rb
  72. 1
      app/models/log.rb
  73. 7
      app/models/merge_request_organisation.rb
  74. 27
      app/models/validations/property_validations.rb
  75. 16
      app/models/validations/sales/property_validations.rb
  76. 5
      app/models/validations/sales/sale_information_validations.rb
  77. 6
      app/models/validations/sales/soft_validations.rb
  78. 16
      app/policies/csv_download_policy.rb
  79. 2
      app/services/bulk_upload/lettings/year2024/csv_parser.rb
  80. 2
      app/services/bulk_upload/sales/year2024/csv_parser.rb
  81. 50
      app/services/csv/downloader.rb
  82. 4
      app/services/feature_toggle.rb
  83. 2
      app/services/mandatory_collection_resources_service.rb
  84. 10
      app/views/csv_downloads/show.html.erb
  85. 8
      app/views/errors/download_link_expired.html.erb
  86. 6
      app/views/form/guidance/_financial_calculations_shared_ownership.html.erb
  87. 5
      app/views/logs/_tasklist.html.erb
  88. 8
      app/views/merge_requests/_notification_banners.html.erb
  89. 2
      app/views/merge_requests/show.html.erb
  90. 4
      config/locales/en.yml
  91. 2
      config/locales/forms/2024/lettings/setup.en.yml
  92. 8
      config/locales/forms/2024/lettings/soft_validations.en.yml
  93. 21
      config/locales/forms/2025/sales/sale_information.en.yml
  94. 17
      config/locales/validations/lettings/property_information.en.yml
  95. 11
      config/locales/validations/sales/property_information.en.yml
  96. 2
      config/locales/validations/sales/sale_information.en.yml
  97. 12
      config/routes.rb
  98. 8
      db/migrate/20241114154215_add_management_fee_fields.rb
  99. 12
      db/migrate/20241118104046_add_csv_download_table.rb
  100. 16
      db/schema.rb
  101. Some files were not shown because too many files have changed in this diff Show More

4
Gemfile

@ -62,6 +62,8 @@ gem "possessive"
# Strip whitespace from active record attributes # Strip whitespace from active record attributes
gem "auto_strip_attributes" gem "auto_strip_attributes"
# Use sidekiq for background processing # Use sidekiq for background processing
gem "factory_bot_rails"
gem "faker"
gem "method_source", "~> 1.1" gem "method_source", "~> 1.1"
gem "rails_admin", "~> 3.1" gem "rails_admin", "~> 3.1"
gem "ruby-openai" gem "ruby-openai"
@ -75,8 +77,6 @@ group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console # Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem "byebug", platforms: %i[mri mingw x64_mingw] gem "byebug", platforms: %i[mri mingw x64_mingw]
gem "dotenv-rails" gem "dotenv-rails"
gem "factory_bot_rails"
gem "faker"
gem "pry-byebug" gem "pry-byebug"
gem "parallel_tests" gem "parallel_tests"

5
app/components/create_log_actions_component.html.erb

@ -7,5 +7,10 @@
<% if user.support? %> <% if user.support? %>
<%= govuk_button_link_to view_uploads_button_copy, view_uploads_button_href, secondary: true %> <%= govuk_button_link_to view_uploads_button_copy, view_uploads_button_href, secondary: true %>
<% end %> <% end %>
<% if FeatureToggle.create_test_logs_enabled? %>
<%= govuk_button_link_to "Create test log", create_test_log_href, secondary: true %>
<%= govuk_button_link_to "Create test log (setup only)", create_setup_test_log_href, secondary: true %>
<% end %>
<% end %> <% end %>
</div> </div>

8
app/components/create_log_actions_component.rb

@ -34,6 +34,14 @@ class CreateLogActionsComponent < ViewComponent::Base
send("bulk_upload_#{log_type}_log_path", id: "start") send("bulk_upload_#{log_type}_log_path", id: "start")
end end
def create_test_log_href
send("create_test_#{log_type}_log_path")
end
def create_setup_test_log_href
send("create_setup_test_#{log_type}_log_path")
end
def view_uploads_button_copy def view_uploads_button_copy
"View #{log_type} bulk uploads" "View #{log_type} bulk uploads"
end end

27
app/controllers/csv_downloads_controller.rb

@ -0,0 +1,27 @@
class CsvDownloadsController < ApplicationController
before_action :authenticate_user!
def show
@csv_download = CsvDownload.find(params[:id])
authorize @csv_download
return render "errors/download_link_expired" if @csv_download.expired?
end
def download
csv_download = CsvDownload.find(params[:id])
authorize csv_download
return render "errors/download_link_expired" if csv_download.expired?
downloader = Csv::Downloader.new(csv_download:)
if Rails.env.development?
downloader.call
send_file downloader.path, filename: csv_download.filename, type: "text/csv"
else
presigned_url = downloader.presigned_url
redirect_to presigned_url, allow_other_host: true
end
end
end

14
app/controllers/lettings_logs_controller.rb

@ -149,6 +149,20 @@ class LettingsLogsController < LogsController
end end
end end
def create_test_log
return render_not_found unless FeatureToggle.create_test_logs_enabled?
log = FactoryBot.create(:lettings_log, :completed, assigned_to: current_user, ppostcode_full: "SW1A 1AA")
redirect_to lettings_log_path(log)
end
def create_setup_test_log
return render_not_found unless FeatureToggle.create_test_logs_enabled?
log = FactoryBot.create(:lettings_log, :setup_completed, assigned_to: current_user)
redirect_to lettings_log_path(log)
end
private private
def session_filters def session_filters

1
app/controllers/merge_requests_controller.rb

@ -143,6 +143,7 @@ private
if [day, month, year].none?(&:blank?) && Date.valid_date?(year.to_i, month.to_i, day.to_i) if [day, month, year].none?(&:blank?) && Date.valid_date?(year.to_i, month.to_i, day.to_i)
merge_request_params["merge_date"] = Time.zone.local(year.to_i, month.to_i, day.to_i) merge_request_params["merge_date"] = Time.zone.local(year.to_i, month.to_i, day.to_i)
@merge_request.errors.add(:merge_date, :more_than_year_from_today) if Time.zone.local(year.to_i, month.to_i, day.to_i) - 1.year > Time.zone.today
else else
@merge_request.errors.add(:merge_date, :invalid) @merge_request.errors.add(:merge_date, :invalid)
end end

14
app/controllers/sales_logs_controller.rb

@ -119,6 +119,20 @@ class SalesLogsController < LogsController
end end
end end
def create_test_log
return render_not_found unless FeatureToggle.create_test_logs_enabled?
log = FactoryBot.create(:sales_log, :completed, assigned_to: current_user)
redirect_to sales_log_path(log)
end
def create_setup_test_log
return render_not_found unless FeatureToggle.create_test_logs_enabled?
log = FactoryBot.create(:sales_log, :shared_ownership_setup_complete, assigned_to: current_user)
redirect_to sales_log_path(log)
end
private private
def session_filters def session_filters

2
app/controllers/schemes_controller.rb

@ -152,7 +152,7 @@ class SchemesController < ApplicationController
flash[:notice] = if scheme_previously_confirmed flash[:notice] = if scheme_previously_confirmed
"#{@scheme.service_name} has been updated." "#{@scheme.service_name} has been updated."
else else
"#{@scheme.service_name} has been created. It does not require helpdesk approval." "#{@scheme.service_name} has been created."
end end
redirect_to scheme_path(@scheme) redirect_to scheme_path(@scheme)
end end

4
app/helpers/merge_requests_helper.rb

@ -276,4 +276,8 @@ module MergeRequestsHelper
def any_organisations_share_logs?(organisations, type) def any_organisations_share_logs?(organisations, type)
organisations.any? { |organisation| organisation.send("#{type}_logs").filter_by_managing_organisation(organisations.where.not(id: organisation.id)).exists? } organisations.any? { |organisation| organisation.send("#{type}_logs").filter_by_managing_organisation(organisations.where.not(id: organisation.id)).exists? }
end end
def begin_merge_disabled?(merge_request)
merge_request.status != "ready_to_merge" || merge_request.merge_date.future?
end
end end

13
app/jobs/email_csv_job.rb

@ -1,9 +1,10 @@
class EmailCsvJob < ApplicationJob class EmailCsvJob < ApplicationJob
include Rails.application.routes.url_helpers
queue_as :default queue_as :default
BYTE_ORDER_MARK = "\uFEFF".freeze # Required to ensure Excel always reads CSV as UTF-8 BYTE_ORDER_MARK = "\uFEFF".freeze # Required to ensure Excel always reads CSV as UTF-8
EXPIRATION_TIME = 24.hours.to_i EXPIRATION_TIME = 48.hours.to_i
def perform(user, search_term = nil, filters = {}, all_orgs = false, organisation = nil, codes_only_export = false, log_type = "lettings", year = nil) # rubocop:disable Style/OptionalBooleanParameter - sidekiq can't serialise named params def perform(user, search_term = nil, filters = {}, all_orgs = false, organisation = nil, codes_only_export = false, log_type = "lettings", year = nil) # rubocop:disable Style/OptionalBooleanParameter - sidekiq can't serialise named params
export_type = codes_only_export ? "codes" : "labels" export_type = codes_only_export ? "codes" : "labels"
@ -20,10 +21,16 @@ class EmailCsvJob < ApplicationJob
filename = "#{[log_type, 'logs', organisation&.name, Time.zone.now].compact.join('-')}.csv" filename = "#{[log_type, 'logs', organisation&.name, Time.zone.now].compact.join('-')}.csv"
storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["BULK_UPLOAD_BUCKET"]) storage_service = if FeatureToggle.upload_enabled?
Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["BULK_UPLOAD_BUCKET"])
else
Storage::LocalDiskService.new
end
storage_service.write_file(filename, BYTE_ORDER_MARK + csv_string) storage_service.write_file(filename, BYTE_ORDER_MARK + csv_string)
csv_download = CsvDownload.create!(user:, organisation: user.organisation, filename:, download_type: log_type, expiration_time: EXPIRATION_TIME)
url = storage_service.get_presigned_url(filename, EXPIRATION_TIME) url = csv_download_url(csv_download.id, host: ENV["APP_HOST"])
CsvDownloadMailer.new.send_csv_download_mail(user, url, EXPIRATION_TIME) CsvDownloadMailer.new.send_csv_download_mail(user, url, EXPIRATION_TIME)
end end

13
app/jobs/scheme_email_csv_job.rb

@ -1,9 +1,10 @@
class SchemeEmailCsvJob < ApplicationJob class SchemeEmailCsvJob < ApplicationJob
include Rails.application.routes.url_helpers
queue_as :default queue_as :default
BYTE_ORDER_MARK = "\uFEFF".freeze # Required to ensure Excel always reads CSV as UTF-8 BYTE_ORDER_MARK = "\uFEFF".freeze # Required to ensure Excel always reads CSV as UTF-8
EXPIRATION_TIME = 24.hours.to_i EXPIRATION_TIME = 48.hours.to_i
def perform(user, search_term = nil, filters = {}, all_orgs = false, organisation = nil, download_type = "combined") # rubocop:disable Style/OptionalBooleanParameter - sidekiq can't serialise named params def perform(user, search_term = nil, filters = {}, all_orgs = false, organisation = nil, download_type = "combined") # rubocop:disable Style/OptionalBooleanParameter - sidekiq can't serialise named params
unfiltered_schemes = if organisation.present? && user.support? unfiltered_schemes = if organisation.present? && user.support?
@ -23,10 +24,16 @@ class SchemeEmailCsvJob < ApplicationJob
filename = "#{['schemes-and-locations', organisation&.name, Time.zone.now].compact.join('-')}.csv" filename = "#{['schemes-and-locations', organisation&.name, Time.zone.now].compact.join('-')}.csv"
end end
storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["BULK_UPLOAD_BUCKET"]) storage_service = if FeatureToggle.upload_enabled?
Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["BULK_UPLOAD_BUCKET"])
else
Storage::LocalDiskService.new
end
storage_service.write_file(filename, BYTE_ORDER_MARK + csv_string) storage_service.write_file(filename, BYTE_ORDER_MARK + csv_string)
csv_download = CsvDownload.create!(user:, organisation: user.organisation, filename:, download_type:, expiration_time: EXPIRATION_TIME)
url = storage_service.get_presigned_url(filename, EXPIRATION_TIME) url = csv_download_url(csv_download.id, host: ENV["APP_HOST"])
CsvDownloadMailer.new.send_csv_download_mail(user, url, EXPIRATION_TIME) CsvDownloadMailer.new.send_csv_download_mail(user, url, EXPIRATION_TIME)
end end

10
app/models/csv_download.rb

@ -0,0 +1,10 @@
class CsvDownload < ApplicationRecord
enum download_type: { lettings: "lettings", sales: "sales", schemes: "schemes", locations: "locations", combined: "combined" }
belongs_to :user
belongs_to :organisation
def expired?
created_at < expiration_time.seconds.ago
end
end

5
app/models/derived_variables/lettings_log_variables.rb

@ -124,6 +124,11 @@ module DerivedVariables::LettingsLogVariables
self.nationality_all = nationality_all_group if nationality_uk_or_prefers_not_to_say? self.nationality_all = nationality_all_group if nationality_uk_or_prefers_not_to_say?
if startdate_changed? && !LocalAuthority.active(startdate).where(code: la).exists?
self.la = nil
self.is_la_inferred = false
end
reset_address_fields! if is_supported_housing? reset_address_fields! if is_supported_housing?
end end

5
app/models/derived_variables/sales_log_variables.rb

@ -75,6 +75,11 @@ module DerivedVariables::SalesLogVariables
self.nationality_all = nationality_all_group if nationality_uk_or_prefers_not_to_say? self.nationality_all = nationality_all_group if nationality_uk_or_prefers_not_to_say?
self.nationality_all_buyer2 = nationality_all_buyer2_group if nationality2_uk_or_prefers_not_to_say? self.nationality_all_buyer2 = nationality_all_buyer2_group if nationality2_uk_or_prefers_not_to_say?
if saledate_changed? && !LocalAuthority.active(saledate).where(code: la).exists?
self.la = nil
self.is_la_inferred = false
end
set_encoded_derived_values!(DEPENDENCIES) set_encoded_derived_values!(DEPENDENCIES)
end end

5
app/models/form/lettings/pages/no_address_found.rb

@ -3,12 +3,13 @@ class Form::Lettings::Pages::NoAddressFound < ::Form::Page
super super
@id = "no_address_found" @id = "no_address_found"
@type = "interruption_screen" @type = "interruption_screen"
@copy_key = "lettings.soft_validations.no_address_found"
@title_text = { @title_text = {
"translation" => "soft_validations.no_address_found.title_text", "translation" => "forms.#{form.start_date.year}.#{@copy_key}.title_text",
"arguments" => [], "arguments" => [],
} }
@informative_text = { @informative_text = {
"translation" => "soft_validations.no_address_found.informative_text", "translation" => "forms.#{form.start_date.year}.#{@copy_key}.informative_text",
"arguments" => [], "arguments" => [],
} }
@depends_on = [ @depends_on = [

1
app/models/form/lettings/subsections/household_needs.rb

@ -2,7 +2,6 @@ class Form::Lettings::Subsections::HouseholdNeeds < ::Form::Subsection
def initialize(id, hsh, section) def initialize(id, hsh, section)
super super
@id = "household_needs" @id = "household_needs"
@copy_key = "lettings.household_needs.housingneeds_type"
@label = "Household needs" @label = "Household needs"
@depends_on = [{ "non_location_setup_questions_completed?" => true }] @depends_on = [{ "non_location_setup_questions_completed?" => true }]
end end

2
app/models/form/page.rb

@ -25,7 +25,7 @@ class Form::Page
delegate :form, to: :subsection delegate :form, to: :subsection
def copy_key def copy_key
@copy_key ||= "#{form.type}.#{subsection.id}.#{questions[0].id}" @copy_key ||= "#{form.type}.#{subsection.copy_key}.#{questions[0].id}"
end end
def header def header

2
app/models/form/question.rb

@ -51,7 +51,7 @@ class Form::Question
delegate :form, to: :subsection delegate :form, to: :subsection
def copy_key def copy_key
@copy_key ||= "#{form.type}.#{subsection.id}.#{id}" @copy_key ||= "#{form.type}.#{subsection.copy_key}.#{id}"
end end
def check_answer_label def check_answer_label

2
app/models/form/sales/pages/buyer_interview.rb

@ -2,7 +2,7 @@ class Form::Sales::Pages::BuyerInterview < ::Form::Page
def initialize(id, hsh, subsection, joint_purchase:) def initialize(id, hsh, subsection, joint_purchase:)
super(id, hsh, subsection) super(id, hsh, subsection)
@joint_purchase = joint_purchase @joint_purchase = joint_purchase
@copy_key = "sales.#{subsection.id}.noint.#{joint_purchase ? 'joint_purchase' : 'not_joint_purchase'}" @copy_key = "sales.#{subsection.copy_key}.noint.#{joint_purchase ? 'joint_purchase' : 'not_joint_purchase'}"
end end
def questions def questions

1
app/models/form/sales/pages/deposit.rb

@ -3,7 +3,6 @@ class Form::Sales::Pages::Deposit < ::Form::Page
super(id, hsh, subsection) super(id, hsh, subsection)
@ownershipsch = ownershipsch @ownershipsch = ownershipsch
@optional = optional @optional = optional
@copy_key = "sales.sale_information.deposit"
end end
def questions def questions

1
app/models/form/sales/pages/deposit_discount.rb

@ -2,7 +2,6 @@ class Form::Sales::Pages::DepositDiscount < ::Form::Page
def initialize(id, hsh, subsection, optional:) def initialize(id, hsh, subsection, optional:)
super(id, hsh, subsection) super(id, hsh, subsection)
@optional = optional @optional = optional
@copy_key = "sales.sale_information.cashdis"
end end
def questions def questions

1
app/models/form/sales/pages/discount.rb

@ -2,7 +2,6 @@ class Form::Sales::Pages::Discount < ::Form::Page
def initialize(id, hsh, subsection) def initialize(id, hsh, subsection)
super super
@id = "discount" @id = "discount"
@copy_key = "sales.sale_information.discount"
@depends_on = [{ @depends_on = [{
"right_to_buy?" => true, "right_to_buy?" => true,
}] }]

1
app/models/form/sales/pages/equity.rb

@ -2,7 +2,6 @@ class Form::Sales::Pages::Equity < ::Form::Page
def initialize(id, hsh, subsection) def initialize(id, hsh, subsection)
super super
@id = "equity" @id = "equity"
@copy_key = "sales.sale_information.equity"
end end
def questions def questions

13
app/models/form/sales/pages/estate_management_fee.rb

@ -0,0 +1,13 @@
class Form::Sales::Pages::EstateManagementFee < ::Form::Page
def initialize(id, hsh, subsection)
super
@copy_key = "sales.sale_information.management_fee"
end
def questions
@questions ||= [
Form::Sales::Questions::HasManagementFee.new(nil, nil, self),
Form::Sales::Questions::ManagementFee.new(nil, nil, self),
]
end
end

1
app/models/form/sales/pages/extra_borrowing.rb

@ -2,7 +2,6 @@ class Form::Sales::Pages::ExtraBorrowing < ::Form::Page
def initialize(id, hsh, subsection, ownershipsch:) def initialize(id, hsh, subsection, ownershipsch:)
super(id, hsh, subsection) super(id, hsh, subsection)
@ownershipsch = ownershipsch @ownershipsch = ownershipsch
@copy_key = "sales.sale_information.extrabor"
@description = "" @description = ""
@subsection = subsection @subsection = subsection
@depends_on = [{ @depends_on = [{

1
app/models/form/sales/pages/grant.rb

@ -2,7 +2,6 @@ class Form::Sales::Pages::Grant < ::Form::Page
def initialize(id, hsh, subsection) def initialize(id, hsh, subsection)
super super
@id = "grant" @id = "grant"
@copy_key = "sales.sale_information.grant"
@depends_on = [{ @depends_on = [{
"right_to_buy?" => false, "right_to_buy?" => false,
"rent_to_buy_full_ownership?" => false, "rent_to_buy_full_ownership?" => false,

12
app/models/form/sales/pages/handover_date_check.rb

@ -3,8 +3,6 @@ class Form::Sales::Pages::HandoverDateCheck < ::Form::Page
super super
@id = "handover_date_check" @id = "handover_date_check"
@copy_key = "sales.soft_validations.hodate_check" @copy_key = "sales.soft_validations.hodate_check"
@depends_on = [{ "saledate_check" => nil, "hodate_3_years_or_more_saledate?" => true },
{ "saledate_check" => 1, "hodate_3_years_or_more_saledate?" => true }]
@informative_text = {} @informative_text = {}
@title_text = { @title_text = {
"translation" => "forms.#{form.start_date.year}.#{@copy_key}.title_text", "translation" => "forms.#{form.start_date.year}.#{@copy_key}.title_text",
@ -12,6 +10,16 @@ class Form::Sales::Pages::HandoverDateCheck < ::Form::Page
} }
end end
def depends_on
if form.start_year_2025_or_later?
[{ "saledate_check" => nil, "hodate_5_years_or_more_saledate?" => true },
{ "saledate_check" => 1, "hodate_5_years_or_more_saledate?" => true }]
else
[{ "saledate_check" => nil, "hodate_3_years_or_more_saledate?" => true },
{ "saledate_check" => 1, "hodate_3_years_or_more_saledate?" => true }]
end
end
def questions def questions
@questions ||= [ @questions ||= [
Form::Sales::Questions::HandoverDateCheck.new(nil, nil, self), Form::Sales::Questions::HandoverDateCheck.new(nil, nil, self),

12
app/models/form/sales/pages/living_before_purchase.rb

@ -19,11 +19,17 @@ class Form::Sales::Pages::LivingBeforePurchase < ::Form::Page
end end
end end
def depends_on def routed_to?(log, _user)
super && page_routed_to?(log)
end
def page_routed_to?(log)
return false if form.start_year_2025_or_later? && log.resale != 2
if @joint_purchase if @joint_purchase
[{ "joint_purchase?" => true }] log.joint_purchase?
else else
[{ "not_joint_purchase?" => true }, { "jointpur" => nil }] log.not_joint_purchase? || log.jointpur.nil?
end end
end end
end end

1
app/models/form/sales/pages/monthly_rent.rb

@ -2,7 +2,6 @@ class Form::Sales::Pages::MonthlyRent < ::Form::Page
def initialize(id, hsh, subsection) def initialize(id, hsh, subsection)
super super
@id = "monthly_rent" @id = "monthly_rent"
@copy_key = "sales.sale_information.mrent"
end end
def questions def questions

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

@ -2,7 +2,6 @@ class Form::Sales::Pages::MortgageAmount < ::Form::Page
def initialize(id, hsh, subsection, ownershipsch:) def initialize(id, hsh, subsection, ownershipsch:)
super(id, hsh, subsection) super(id, hsh, subsection)
@ownershipsch = ownershipsch @ownershipsch = ownershipsch
@copy_key = "sales.sale_information.mortgage"
@depends_on = [{ "mortgage_used?" => true }] @depends_on = [{ "mortgage_used?" => true }]
end end

1
app/models/form/sales/pages/mortgage_lender.rb

@ -2,7 +2,6 @@ class Form::Sales::Pages::MortgageLender < ::Form::Page
def initialize(id, hsh, subsection, ownershipsch:) def initialize(id, hsh, subsection, ownershipsch:)
super(id, hsh, subsection) super(id, hsh, subsection)
@ownershipsch = ownershipsch @ownershipsch = ownershipsch
@copy_key = "sales.sale_information.mortgagelender"
@description = "" @description = ""
@subsection = subsection @subsection = subsection
@depends_on = [{ @depends_on = [{

1
app/models/form/sales/pages/mortgage_lender_other.rb

@ -2,7 +2,6 @@ class Form::Sales::Pages::MortgageLenderOther < ::Form::Page
def initialize(id, hsh, subsection, ownershipsch:) def initialize(id, hsh, subsection, ownershipsch:)
super(id, hsh, subsection) super(id, hsh, subsection)
@ownershipsch = ownershipsch @ownershipsch = ownershipsch
@copy_key = "sales.sale_information.mortgagelenderother"
@description = "" @description = ""
@subsection = subsection @subsection = subsection
@depends_on = [{ @depends_on = [{

1
app/models/form/sales/pages/mortgage_length.rb

@ -2,7 +2,6 @@ class Form::Sales::Pages::MortgageLength < ::Form::Page
def initialize(id, hsh, subsection, ownershipsch:) def initialize(id, hsh, subsection, ownershipsch:)
super(id, hsh, subsection) super(id, hsh, subsection)
@ownershipsch = ownershipsch @ownershipsch = ownershipsch
@copy_key = "sales.sale_information.mortlen"
@depends_on = [{ @depends_on = [{
"mortgageused" => 1, "mortgageused" => 1,
}] }]

1
app/models/form/sales/pages/mortgageused.rb

@ -1,7 +1,6 @@
class Form::Sales::Pages::Mortgageused < ::Form::Page class Form::Sales::Pages::Mortgageused < ::Form::Page
def initialize(id, hsh, subsection, ownershipsch:) def initialize(id, hsh, subsection, ownershipsch:)
super(id, hsh, subsection) super(id, hsh, subsection)
@copy_key = "sales.sale_information.mortgageused"
@ownershipsch = ownershipsch @ownershipsch = ownershipsch
end end

1
app/models/form/sales/pages/previous_bedrooms.rb

@ -2,7 +2,6 @@ class Form::Sales::Pages::PreviousBedrooms < ::Form::Page
def initialize(id, hsh, subsection) def initialize(id, hsh, subsection)
super super
@id = "previous_bedrooms" @id = "previous_bedrooms"
@copy_key = "sales.sale_information.frombeds"
@depends_on = [ @depends_on = [
{ {
"soctenant" => 1, "soctenant" => 1,

1
app/models/form/sales/pages/previous_property_type.rb

@ -2,7 +2,6 @@ class Form::Sales::Pages::PreviousPropertyType < ::Form::Page
def initialize(id, hsh, subsection) def initialize(id, hsh, subsection)
super super
@id = "previous_property_type" @id = "previous_property_type"
@copy_key = "sales.sale_information.fromprop"
@description = "" @description = ""
@subsection = subsection @subsection = subsection
@depends_on = [ @depends_on = [

1
app/models/form/sales/pages/previous_tenure.rb

@ -2,7 +2,6 @@ class Form::Sales::Pages::PreviousTenure < ::Form::Page
def initialize(id, hsh, subsection) def initialize(id, hsh, subsection)
super super
@id = "shared_ownership_previous_tenure" @id = "shared_ownership_previous_tenure"
@copy_key = "sales.sale_information.socprevten"
@header = "" @header = ""
@description = "" @description = ""
@subsection = subsection @subsection = subsection

2
app/models/form/sales/pages/privacy_notice.rb

@ -1,7 +1,7 @@
class Form::Sales::Pages::PrivacyNotice < ::Form::Page class Form::Sales::Pages::PrivacyNotice < ::Form::Page
def initialize(id, hsh, subsection, joint_purchase:) def initialize(id, hsh, subsection, joint_purchase:)
super(id, hsh, subsection) super(id, hsh, subsection)
@copy_key = "sales.#{subsection.id}.privacynotice.#{joint_purchase ? 'joint_purchase' : 'not_joint_purchase'}" @copy_key = "sales.#{subsection.copy_key}.privacynotice.#{joint_purchase ? 'joint_purchase' : 'not_joint_purchase'}"
@joint_purchase = joint_purchase @joint_purchase = joint_purchase
end end

1
app/models/form/sales/pages/resale.rb

@ -2,7 +2,6 @@ class Form::Sales::Pages::Resale < ::Form::Page
def initialize(id, hsh, subsection) def initialize(id, hsh, subsection)
super super
@id = "resale" @id = "resale"
@copy_key = "sales.sale_information.resale"
@depends_on = [ @depends_on = [
{ {
"staircase" => 2, "staircase" => 2,

2
app/models/form/sales/pages/staircase.rb

@ -3,7 +3,7 @@ class Form::Sales::Pages::Staircase < ::Form::Page
super super
@id = "staircasing" @id = "staircasing"
@depends_on = [{ "ownershipsch" => 1 }] @depends_on = [{ "ownershipsch" => 1 }]
@copy_key = "sales.#{subsection.id}.staircasing" @copy_key = "sales.#{subsection.copy_key}.staircasing"
end end
def questions def questions

1
app/models/form/sales/pages/value_shared_ownership.rb

@ -2,7 +2,6 @@ class Form::Sales::Pages::ValueSharedOwnership < ::Form::Page
def initialize(id, hsh, subsection) def initialize(id, hsh, subsection)
super super
@id = "value_shared_ownership" @id = "value_shared_ownership"
@copy_key = "sales.sale_information.value"
end end
def questions def questions

2
app/models/form/sales/questions/buyer_interview.rb

@ -2,7 +2,7 @@ class Form::Sales::Questions::BuyerInterview < ::Form::Question
def initialize(id, hsh, page, joint_purchase:) def initialize(id, hsh, page, joint_purchase:)
super(id, hsh, page) super(id, hsh, page)
@id = "noint" @id = "noint"
@copy_key = "sales.#{subsection.id}.noint.#{joint_purchase ? 'joint_purchase' : 'not_joint_purchase'}" @copy_key = "sales.#{subsection.copy_key}.noint.#{joint_purchase ? 'joint_purchase' : 'not_joint_purchase'}"
@type = "radio" @type = "radio"
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]

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

@ -2,7 +2,6 @@ class Form::Sales::Questions::DepositAmount < ::Form::Question
def initialize(id, hsh, subsection, ownershipsch:, optional:) def initialize(id, hsh, subsection, ownershipsch:, optional:)
super(id, hsh, subsection) super(id, hsh, subsection)
@id = "deposit" @id = "deposit"
@copy_key = "sales.sale_information.deposit"
@type = "numeric" @type = "numeric"
@min = 0 @min = 0
@max = 999_999 @max = 999_999

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

@ -2,7 +2,6 @@ class Form::Sales::Questions::DepositDiscount < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "cashdis" @id = "cashdis"
@copy_key = "sales.sale_information.cashdis"
@type = "numeric" @type = "numeric"
@min = 0 @min = 0
@max = 999_999 @max = 999_999

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

@ -3,7 +3,6 @@ class Form::Sales::Questions::Discount < ::Form::Question
super super
@id = "discount" @id = "discount"
@type = "numeric" @type = "numeric"
@copy_key = "sales.sale_information.discount"
@min = 0 @min = 0
@max = form.start_year_2024_or_later? ? 70 : 100 @max = form.start_year_2024_or_later? ? 70 : 100
@step = 0.1 @step = 0.1

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

@ -2,7 +2,6 @@ class Form::Sales::Questions::Equity < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "equity" @id = "equity"
@copy_key = "sales.sale_information.equity"
@type = "numeric" @type = "numeric"
@min = 0 @min = 0
@max = 100 @max = 100

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

@ -2,7 +2,6 @@ class Form::Sales::Questions::ExtraBorrowing < ::Form::Question
def initialize(id, hsh, subsection, ownershipsch:) def initialize(id, hsh, subsection, ownershipsch:)
super(id, hsh, subsection) super(id, hsh, subsection)
@id = "extrabor" @id = "extrabor"
@copy_key = "sales.sale_information.extrabor"
@type = "radio" @type = "radio"
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@page = page @page = page

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

@ -2,7 +2,6 @@ class Form::Sales::Questions::Fromprop < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "fromprop" @id = "fromprop"
@copy_key = "sales.sale_information.fromprop"
@type = "radio" @type = "radio"
@page = page @page = page
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS

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

@ -2,7 +2,6 @@ class Form::Sales::Questions::Grant < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "grant" @id = "grant"
@copy_key = "sales.sale_information.grant"
@type = "numeric" @type = "numeric"
@min = 0 @min = 0
@max = 999_999 @max = 999_999

24
app/models/form/sales/questions/has_management_fee.rb

@ -0,0 +1,24 @@
class Form::Sales::Questions::HasManagementFee < ::Form::Question
def initialize(id, hsh, subsection)
super
@id = "has_management_fee"
@copy_key = "sales.sale_information.management_fee.has_management_fee"
@type = "radio"
@answer_options = ANSWER_OPTIONS
@conditional_for = {
"management_fee" => [1],
}
@hidden_in_check_answers = {
"depends_on" => [
{
"has_management_fee" => 1,
},
],
}
end
ANSWER_OPTIONS = {
"1" => { "value" => "Yes" },
"0" => { "value" => "No" },
}.freeze
end

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

@ -0,0 +1,12 @@
class Form::Sales::Questions::ManagementFee < ::Form::Question
def initialize(id, hsh, subsection)
super
@id = "management_fee"
@copy_key = "sales.sale_information.management_fee.management_fee"
@type = "numeric"
@min = 1
@step = 0.01
@width = 5
@prefix = "£"
end
end

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

@ -2,7 +2,6 @@ class Form::Sales::Questions::MonthlyRent < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "mrent" @id = "mrent"
@copy_key = "sales.sale_information.mrent"
@type = "numeric" @type = "numeric"
@min = 0 @min = 0
@step = 0.01 @step = 0.01

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

@ -2,7 +2,6 @@ class Form::Sales::Questions::MortgageAmount < ::Form::Question
def initialize(id, hsh, subsection, ownershipsch:) def initialize(id, hsh, subsection, ownershipsch:)
super(id, hsh, subsection) super(id, hsh, subsection)
@id = "mortgage" @id = "mortgage"
@copy_key = "sales.sale_information.mortgage"
@type = "numeric" @type = "numeric"
@min = 1 @min = 1
@step = 1 @step = 1

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

@ -2,7 +2,6 @@ class Form::Sales::Questions::MortgageLender < ::Form::Question
def initialize(id, hsh, subsection, ownershipsch:) def initialize(id, hsh, subsection, ownershipsch:)
super(id, hsh, subsection) super(id, hsh, subsection)
@id = "mortgagelender" @id = "mortgagelender"
@copy_key = "sales.sale_information.mortgagelender"
@type = "select" @type = "select"
@page = page @page = page
@bottom_guidance_partial = "mortgage_lender" @bottom_guidance_partial = "mortgage_lender"

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

@ -2,7 +2,6 @@ class Form::Sales::Questions::MortgageLenderOther < ::Form::Question
def initialize(id, hsh, subsection, ownershipsch:) def initialize(id, hsh, subsection, ownershipsch:)
super(id, hsh, subsection) super(id, hsh, subsection)
@id = "mortgagelenderother" @id = "mortgagelenderother"
@copy_key = "sales.sale_information.mortgagelenderother"
@type = "text" @type = "text"
@page = page @page = page
@ownershipsch = ownershipsch @ownershipsch = ownershipsch

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

@ -2,7 +2,6 @@ class Form::Sales::Questions::MortgageLength < ::Form::Question
def initialize(id, hsh, subsection, ownershipsch:) def initialize(id, hsh, subsection, ownershipsch:)
super(id, hsh, subsection) super(id, hsh, subsection)
@id = "mortlen" @id = "mortlen"
@copy_key = "sales.sale_information.mortlen"
@type = "numeric" @type = "numeric"
@min = 0 @min = 0
@max = 60 @max = 60

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

@ -2,7 +2,6 @@ class Form::Sales::Questions::Mortgageused < ::Form::Question
def initialize(id, hsh, subsection, ownershipsch:) def initialize(id, hsh, subsection, ownershipsch:)
super(id, hsh, subsection) super(id, hsh, subsection)
@id = "mortgageused" @id = "mortgageused"
@copy_key = "sales.sale_information.mortgageused"
@type = "radio" @type = "radio"
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@ownershipsch = ownershipsch @ownershipsch = ownershipsch

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

@ -2,7 +2,6 @@ class Form::Sales::Questions::PreviousBedrooms < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "frombeds" @id = "frombeds"
@copy_key = "sales.sale_information.frombeds"
@type = "numeric" @type = "numeric"
@width = 5 @width = 5
@min = 1 @min = 1

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

@ -2,7 +2,6 @@ class Form::Sales::Questions::PreviousTenure < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "socprevten" @id = "socprevten"
@copy_key = "sales.sale_information.socprevten"
@type = "radio" @type = "radio"
@page = page @page = page
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS

2
app/models/form/sales/questions/privacy_notice.rb

@ -2,7 +2,7 @@ class Form::Sales::Questions::PrivacyNotice < ::Form::Question
def initialize(id, hsh, page, joint_purchase:) def initialize(id, hsh, page, joint_purchase:)
super(id, hsh, page) super(id, hsh, page)
@id = "privacynotice" @id = "privacynotice"
@copy_key = "sales.#{subsection.id}.privacynotice.#{joint_purchase ? 'joint_purchase' : 'not_joint_purchase'}" @copy_key = "sales.#{subsection.copy_key}.privacynotice.#{joint_purchase ? 'joint_purchase' : 'not_joint_purchase'}"
@type = "checkbox" @type = "checkbox"
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
@joint_purchase = joint_purchase @joint_purchase = joint_purchase

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

@ -2,7 +2,6 @@ class Form::Sales::Questions::Resale < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "resale" @id = "resale"
@copy_key = "sales.sale_information.resale"
@type = "radio" @type = "radio"
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]

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

@ -2,7 +2,6 @@ class Form::Sales::Questions::Value < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "value" @id = "value"
@copy_key = "sales.sale_information.value"
@type = "numeric" @type = "numeric"
@min = 0 @min = 0
@step = 1 @step = 1

10
app/models/form/sales/sections/sale_information.rb

@ -5,9 +5,17 @@ class Form::Sales::Sections::SaleInformation < ::Form::Section
@label = "Sale information" @label = "Sale information"
@description = "" @description = ""
@subsections = [ @subsections = [
Form::Sales::Subsections::SharedOwnershipScheme.new(nil, nil, self), shared_ownership_scheme_subsection,
Form::Sales::Subsections::DiscountedOwnershipScheme.new(nil, nil, self), Form::Sales::Subsections::DiscountedOwnershipScheme.new(nil, nil, self),
Form::Sales::Subsections::OutrightSale.new(nil, nil, self), Form::Sales::Subsections::OutrightSale.new(nil, nil, self),
] || [] ] || []
end end
def shared_ownership_scheme_subsection
if form.start_year_2025_or_later?
Form::Sales::Subsections::SharedOwnershipInitialPurchase.new(nil, nil, self)
else
Form::Sales::Subsections::SharedOwnershipScheme.new(nil, nil, self)
end
end
end end

1
app/models/form/sales/subsections/discounted_ownership_scheme.rb

@ -4,6 +4,7 @@ class Form::Sales::Subsections::DiscountedOwnershipScheme < ::Form::Subsection
@id = "discounted_ownership_scheme" @id = "discounted_ownership_scheme"
@label = "Discounted ownership scheme" @label = "Discounted ownership scheme"
@depends_on = [{ "ownershipsch" => 2, "setup_completed?" => true }] @depends_on = [{ "ownershipsch" => 2, "setup_completed?" => true }]
@copy_key = "sale_information"
end end
def pages def pages

15
app/models/form/sales/subsections/household_situation.rb

@ -3,7 +3,14 @@ class Form::Sales::Subsections::HouseholdSituation < ::Form::Subsection
super super
@id = "household_situation" @id = "household_situation"
@label = "Household situation" @label = "Household situation"
@depends_on = [{ "setup_completed?" => true }] end
def depends_on
if form.start_year_2025_or_later?
[{ "setup_completed?" => true, "is_staircase?" => false }]
else
[{ "setup_completed?" => true }]
end
end end
def pages def pages
@ -16,4 +23,10 @@ class Form::Sales::Subsections::HouseholdSituation < ::Form::Subsection
Form::Sales::Pages::Buyer2PreviousHousingSituation.new(nil, nil, self), Form::Sales::Pages::Buyer2PreviousHousingSituation.new(nil, nil, self),
].flatten.compact ].flatten.compact
end end
def displayed_in_tasklist?(log)
return true unless form.start_year_2025_or_later?
log.staircase != 1
end
end end

15
app/models/form/sales/subsections/income_benefits_and_savings.rb

@ -3,7 +3,14 @@ class Form::Sales::Subsections::IncomeBenefitsAndSavings < ::Form::Subsection
super super
@id = "income_benefits_and_savings" @id = "income_benefits_and_savings"
@label = "Income, benefits and savings" @label = "Income, benefits and savings"
@depends_on = [{ "setup_completed?" => true }] end
def depends_on
if form.start_year_2025_or_later?
[{ "setup_completed?" => true, "is_staircase?" => false }]
else
[{ "setup_completed?" => true }]
end
end end
def pages def pages
@ -36,6 +43,12 @@ class Form::Sales::Subsections::IncomeBenefitsAndSavings < ::Form::Subsection
].compact ].compact
end end
def displayed_in_tasklist?(log)
return true unless form.start_year_2025_or_later?
log.staircase != 1
end
private private
def previous_shared_page def previous_shared_page

15
app/models/form/sales/subsections/other_household_information.rb

@ -3,7 +3,14 @@ class Form::Sales::Subsections::OtherHouseholdInformation < ::Form::Subsection
super super
@id = "other_household_information" @id = "other_household_information"
@label = "Other household information" @label = "Other household information"
@depends_on = [{ "setup_completed?" => true }] end
def depends_on
if form.start_year_2025_or_later?
[{ "setup_completed?" => true, "is_staircase?" => false }]
else
[{ "setup_completed?" => true }]
end
end end
def pages def pages
@ -17,4 +24,10 @@ class Form::Sales::Subsections::OtherHouseholdInformation < ::Form::Subsection
Form::Sales::Pages::HouseholdWheelchairCheck.new("wheelchair_check", nil, self), Form::Sales::Pages::HouseholdWheelchairCheck.new("wheelchair_check", nil, self),
] ]
end end
def displayed_in_tasklist?(log)
return true unless form.start_year_2025_or_later?
log.staircase != 1
end
end end

1
app/models/form/sales/subsections/outright_sale.rb

@ -4,6 +4,7 @@ class Form::Sales::Subsections::OutrightSale < ::Form::Subsection
@id = "outright_sale" @id = "outright_sale"
@label = "Outright sale" @label = "Outright sale"
@depends_on = [{ "ownershipsch" => 3, "setup_completed?" => true }] @depends_on = [{ "ownershipsch" => 3, "setup_completed?" => true }]
@copy_key = "sale_information"
end end
def pages def pages

48
app/models/form/sales/subsections/shared_ownership_initial_purchase.rb

@ -0,0 +1,48 @@
class Form::Sales::Subsections::SharedOwnershipInitialPurchase < ::Form::Subsection
def initialize(id, hsh, section)
super
@id = "shared_ownership_initial_purchase"
@label = "Shared ownership - initial purchase"
@depends_on = [{ "ownershipsch" => 1, "setup_completed?" => true, "staircase" => 2 }]
end
def pages
@pages ||= [
Form::Sales::Pages::Resale.new(nil, nil, self),
Form::Sales::Pages::LivingBeforePurchase.new("living_before_purchase_shared_ownership_joint_purchase", nil, self, ownershipsch: 1, joint_purchase: true),
Form::Sales::Pages::LivingBeforePurchase.new("living_before_purchase_shared_ownership", nil, self, ownershipsch: 1, joint_purchase: false),
Form::Sales::Pages::HandoverDate.new(nil, nil, self),
Form::Sales::Pages::HandoverDateCheck.new(nil, nil, self),
Form::Sales::Pages::BuyerPrevious.new("buyer_previous_joint_purchase", nil, self, joint_purchase: true),
Form::Sales::Pages::BuyerPrevious.new("buyer_previous_not_joint_purchase", nil, self, joint_purchase: false),
Form::Sales::Pages::PreviousBedrooms.new(nil, nil, self),
Form::Sales::Pages::PreviousPropertyType.new(nil, nil, self),
Form::Sales::Pages::PreviousTenure.new(nil, nil, self),
Form::Sales::Pages::ValueSharedOwnership.new(nil, nil, self),
Form::Sales::Pages::AboutPriceValueCheck.new("about_price_shared_ownership_value_check", nil, self),
Form::Sales::Pages::Equity.new(nil, nil, self),
Form::Sales::Pages::SharedOwnershipDepositValueCheck.new("shared_ownership_equity_value_check", nil, self),
Form::Sales::Pages::Mortgageused.new("mortgage_used_shared_ownership", nil, self, ownershipsch: 1),
Form::Sales::Pages::MortgageValueCheck.new("mortgage_used_mortgage_value_check", nil, self),
Form::Sales::Pages::MortgageAmount.new("mortgage_amount_shared_ownership", nil, self, ownershipsch: 1),
Form::Sales::Pages::SharedOwnershipDepositValueCheck.new("shared_ownership_mortgage_amount_value_check", nil, self),
Form::Sales::Pages::MortgageValueCheck.new("mortgage_amount_mortgage_value_check", nil, self),
Form::Sales::Pages::MortgageLength.new("mortgage_length_shared_ownership", nil, self, ownershipsch: 1),
Form::Sales::Pages::Deposit.new("deposit_shared_ownership", nil, self, ownershipsch: 1, optional: false),
Form::Sales::Pages::Deposit.new("deposit_shared_ownership_optional", nil, self, ownershipsch: 1, optional: true),
Form::Sales::Pages::DepositValueCheck.new("deposit_joint_purchase_value_check", nil, self, joint_purchase: true),
Form::Sales::Pages::DepositValueCheck.new("deposit_value_check", nil, self, joint_purchase: false),
Form::Sales::Pages::DepositDiscount.new("deposit_discount", nil, self, optional: false),
Form::Sales::Pages::DepositDiscount.new("deposit_discount_optional", nil, self, optional: true),
Form::Sales::Pages::SharedOwnershipDepositValueCheck.new("shared_ownership_deposit_value_check", nil, self),
Form::Sales::Pages::MonthlyRent.new(nil, nil, self),
Form::Sales::Pages::LeaseholdCharges.new("leasehold_charges_shared_ownership", nil, self, ownershipsch: 1),
Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_shared_ownership_value_check", nil, self),
Form::Sales::Pages::EstateManagementFee.new("estate_management_fee", nil, self),
].compact
end
def displayed_in_tasklist?(log)
log.staircase == 2 && (log.ownershipsch.nil? || log.ownershipsch == 1)
end
end

2
app/models/form/sales/subsections/shared_ownership_scheme.rb

@ -11,7 +11,7 @@ class Form::Sales::Subsections::SharedOwnershipScheme < ::Form::Subsection
@pages ||= [ @pages ||= [
Form::Sales::Pages::LivingBeforePurchase.new("living_before_purchase_shared_ownership_joint_purchase", nil, self, ownershipsch: 1, joint_purchase: true), Form::Sales::Pages::LivingBeforePurchase.new("living_before_purchase_shared_ownership_joint_purchase", nil, self, ownershipsch: 1, joint_purchase: true),
Form::Sales::Pages::LivingBeforePurchase.new("living_before_purchase_shared_ownership", nil, self, ownershipsch: 1, joint_purchase: false), Form::Sales::Pages::LivingBeforePurchase.new("living_before_purchase_shared_ownership", nil, self, ownershipsch: 1, joint_purchase: false),
(Form::Sales::Pages::Staircase.new(nil, nil, self) unless form.start_year_2025_or_later?), Form::Sales::Pages::Staircase.new(nil, nil, self),
Form::Sales::Pages::AboutStaircase.new("about_staircasing_joint_purchase", nil, self, joint_purchase: true), Form::Sales::Pages::AboutStaircase.new("about_staircasing_joint_purchase", nil, self, joint_purchase: true),
Form::Sales::Pages::AboutStaircase.new("about_staircasing_not_joint_purchase", nil, self, joint_purchase: false), Form::Sales::Pages::AboutStaircase.new("about_staircasing_not_joint_purchase", nil, self, joint_purchase: false),
Form::Sales::Pages::StaircaseBoughtValueCheck.new(nil, nil, self), Form::Sales::Pages::StaircaseBoughtValueCheck.new(nil, nil, self),

4
app/models/form/section.rb

@ -10,4 +10,8 @@ class Form::Section
@subsections = hsh["subsections"].map { |s_id, s| Form::Subsection.new(s_id, s, self) } @subsections = hsh["subsections"].map { |s_id, s| Form::Subsection.new(s_id, s, self) }
end end
end end
def displayed_in_tasklist?(log)
subsections.any? { |subsection| subsection.displayed_in_tasklist?(log) }
end
end end

1
app/models/log.rb

@ -111,6 +111,7 @@ class Log < ApplicationRecord
self.town_or_city = nil self.town_or_city = nil
self.county = nil self.county = nil
self.postcode_full = postcode_full_input self.postcode_full = postcode_full_input
process_postcode_changes!
else else
self.uprn = uprn_selection self.uprn = uprn_selection
self.uprn_confirmed = 1 self.uprn_confirmed = 1

7
app/models/merge_request_organisation.rb

@ -29,5 +29,12 @@ private
if merging_organisation_id.blank? || !Organisation.where(id: merging_organisation_id).exists? if merging_organisation_id.blank? || !Organisation.where(id: merging_organisation_id).exists?
merge_request.errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_not_selected")) merge_request.errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_not_selected"))
end end
existing_merges = MergeRequestOrganisation.with_merging_organisation(merging_organisation)
if existing_merges.count.positive?
existing_merge_request = existing_merges.first.merge_request
errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_part_of_another_merge"))
merge_request.errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_part_of_another_incomplete_merge", organisation: merging_organisation.name, absorbing_organisation: existing_merge_request.absorbing_organisation&.name, merge_date: existing_merge_request.merge_date&.to_fs(:govuk_date)))
end
end end
end end

27
app/models/validations/property_validations.rb

@ -46,4 +46,31 @@ module Validations::PropertyValidations
record.errors.add :postcode_full, :wrong_format, message: error_message record.errors.add :postcode_full, :wrong_format, message: error_message
end end
end end
def validate_la_in_england(record)
return unless record.form.start_year_2025_or_later?
if record.is_general_needs?
return unless record.la
return if record.la.in?(LocalAuthority.england.pluck(:code))
record.errors.add :la, I18n.t("validations.lettings.property.la.not_in_england")
record.errors.add :postcode_full, I18n.t("validations.lettings.property.postcode_full.not_in_england")
record.errors.add :uprn, I18n.t("validations.lettings.property.uprn.not_in_england")
record.errors.add :uprn_confirmation, I18n.t("validations.lettings.property.uprn_confirmation.not_in_england")
record.errors.add :uprn_selection, I18n.t("validations.lettings.property.uprn_selection.not_in_england")
if record.uprn.present?
record.errors.add :startdate, I18n.t("validations.lettings.property.startdate.address_not_in_england")
else
record.errors.add :startdate, I18n.t("validations.lettings.property.startdate.postcode_not_in_england")
end
elsif record.is_supported_housing?
return unless record.location
return if record.location.location_code.in?(LocalAuthority.england.pluck(:code))
record.errors.add :location_id, I18n.t("validations.lettings.property.location_id.not_in_england")
record.errors.add :scheme_id, I18n.t("validations.lettings.property.scheme_id.not_in_england")
record.errors.add :startdate, I18n.t("validations.lettings.property.startdate.location_not_in_england")
end
end
end end

16
app/models/validations/sales/property_validations.rb

@ -36,4 +36,20 @@ module Validations::Sales::PropertyValidations
record.errors.add :postcode_full, :wrong_format, message: error_message record.errors.add :postcode_full, :wrong_format, message: error_message
end end
end end
def validate_la_in_england(record)
return unless record.form.start_year_2025_or_later? && record.la.present?
return if record.la.in?(LocalAuthority.england.pluck(:code))
record.errors.add :la, I18n.t("validations.sales.property_information.la.not_in_england")
record.errors.add :postcode_full, I18n.t("validations.sales.property_information.postcode_full.not_in_england")
record.errors.add :uprn, I18n.t("validations.sales.property_information.uprn.not_in_england")
record.errors.add :uprn_confirmation, I18n.t("validations.sales.property_information.uprn_confirmation.not_in_england")
record.errors.add :uprn_selection, I18n.t("validations.sales.property_information.uprn_selection.not_in_england")
if record.uprn.present?
record.errors.add :saledate, I18n.t("validations.sales.property_information.saledate.address_not_in_england")
else
record.errors.add :saledate, I18n.t("validations.sales.property_information.saledate.postcode_not_in_england")
end
end
end end

5
app/models/validations/sales/sale_information_validations.rb

@ -12,7 +12,10 @@ module Validations::Sales::SaleInformationValidations
record.errors.add :saledate, I18n.t("validations.sales.sale_information.saledate.must_be_after_hodate") record.errors.add :saledate, I18n.t("validations.sales.sale_information.saledate.must_be_after_hodate")
end end
if record.saledate - record.hodate >= 3.years && record.form.start_year_2024_or_later? if (record.saledate - 5.years) >= record.hodate && record.form.start_year_2025_or_later?
record.errors.add :hodate, I18n.t("validations.sales.sale_information.hodate.must_be_less_than_5_years_from_saledate")
record.errors.add :saledate, I18n.t("validations.sales.sale_information.saledate.must_be_less_than_5_years_from_hodate")
elsif (record.saledate - 3.years) >= record.hodate && record.startdate.year <= 2024
record.errors.add :hodate, I18n.t("validations.sales.sale_information.hodate.must_be_less_than_3_years_from_saledate") record.errors.add :hodate, I18n.t("validations.sales.sale_information.hodate.must_be_less_than_3_years_from_saledate")
record.errors.add :saledate, I18n.t("validations.sales.sale_information.saledate.must_be_less_than_3_years_from_hodate") record.errors.add :saledate, I18n.t("validations.sales.sale_information.saledate.must_be_less_than_3_years_from_hodate")
end end

6
app/models/validations/sales/soft_validations.rb

@ -110,6 +110,12 @@ module Validations::Sales::SoftValidations
saledate - hodate >= 3.years saledate - hodate >= 3.years
end end
def hodate_5_years_or_more_saledate?
return unless hodate && saledate
saledate - hodate >= 5.years
end
def purchase_price_higher_or_lower_text def purchase_price_higher_or_lower_text
value < sale_range.soft_min ? "lower" : "higher" value < sale_range.soft_min ? "lower" : "higher"
end end

16
app/policies/csv_download_policy.rb

@ -0,0 +1,16 @@
class CsvDownloadPolicy
attr_reader :current_user, :csv_download
def initialize(current_user, csv_download)
@current_user = current_user
@csv_download = csv_download
end
def show?
@current_user == @csv_download.user || @current_user.support? || @current_user.organisation == @csv_download.organisation
end
def download?
@current_user == @csv_download.user || @current_user.support? || @current_user.organisation == @csv_download.organisation
end
end

2
app/services/bulk_upload/lettings/year2024/csv_parser.rb

@ -15,7 +15,7 @@ class BulkUpload::Lettings::Year2024::CsvParser
def row_offset def row_offset
if with_headers? if with_headers?
rows.find_index { |row| row[0].match(/field number/i) } + 1 rows.find_index { |row| row[0].present? && row[0].match(/field number/i) } + 1
else else
0 0
end end

2
app/services/bulk_upload/sales/year2024/csv_parser.rb

@ -15,7 +15,7 @@ class BulkUpload::Sales::Year2024::CsvParser
def row_offset def row_offset
if with_headers? if with_headers?
rows.find_index { |row| row[0].match(/field number/i) } + 1 rows.find_index { |row| row[0].present? && row[0].match(/field number/i) } + 1
else else
0 0
end end

50
app/services/csv/downloader.rb

@ -0,0 +1,50 @@
class Csv::Downloader
attr_reader :csv_download
delegate :path, to: :file
def initialize(csv_download:)
@csv_download = csv_download
end
def call
download
end
def delete_local_file!
file.unlink
end
def presigned_url
s3_storage_service.get_presigned_url(csv_download.filename, 60, response_content_disposition: "attachment; filename=#{csv_download.filename}")
end
private
def download
io = storage_service.get_file_io(csv_download.filename)
file.write(io.read)
io.close
file.close
end
def file
@file ||= Tempfile.new
end
def storage_service
@storage_service ||= if FeatureToggle.upload_enabled?
s3_storage_service
else
local_disk_storage_service
end
end
def s3_storage_service
Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["BULK_UPLOAD_BUCKET"])
end
def local_disk_storage_service
Storage::LocalDiskService.new
end
end

4
app/services/feature_toggle.rb

@ -46,4 +46,8 @@ class FeatureToggle
def self.managing_resources_enabled? def self.managing_resources_enabled?
!Rails.env.production? !Rails.env.production?
end end
def self.create_test_logs_enabled?
Rails.env.development? || Rails.env.review?
end
end end

2
app/services/mandatory_collection_resources_service.rb

@ -46,7 +46,7 @@ class MandatoryCollectionResourcesService
year_range = "#{year} to #{year + 1}" year_range = "#{year} to #{year + 1}"
case resource case resource
when "paper_form" when "paper_form"
"#{log_type} log for tenants (#{year_range})" "#{log_type} paper form (#{year_range})"
when "bulk_upload_template" when "bulk_upload_template"
"#{log_type} bulk upload template (#{year_range})" "#{log_type} bulk upload template (#{year_range})"
when "bulk_upload_specification" when "bulk_upload_specification"

10
app/views/csv_downloads/show.html.erb

@ -0,0 +1,10 @@
<% title = "Downlaod CSV file" %>
<% content_for :title, title %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds-from-desktop">
<h1 class="govuk-heading-l govuk-!-margin-bottom-6">You are about to download a CSV file</h1>
<p class="govuk-body-m">Filename: <%= @csv_download.filename %></p>
<%= govuk_button_link_to "Download CSV", download_csv_download_path(@csv_download) %>
</div>
</div>

8
app/views/errors/download_link_expired.html.erb

@ -0,0 +1,8 @@
<% content_for :title, "This link has expired" %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<h1 class="govuk-heading-xl govuk-!-margin-bottom-6">This link has expired.</h1>
<p class="govuk-body-m">Download the logs again to get a new link.</p>
</div>
</div>

6
app/views/form/guidance/_financial_calculations_shared_ownership.html.erb

@ -20,11 +20,11 @@
<% end %> <% end %>
must equal must equal
the purchase price <%= question_link("value", log, current_user) %> the purchase price <%= question_link("value", log, current_user) %>
<% stairbought_page = log.form.get_question("stairbought", log).page %> <% stairbought_page = log.form.get_question("stairbought", log)&.page %>
<% if stairbought_page.routed_to?(log, current_user) %> <% if stairbought_page&.routed_to?(log, current_user) %>
multiplied by the percentage bought <%= question_link("stairbought", log, current_user) %> multiplied by the percentage bought <%= question_link("stairbought", log, current_user) %>
<% else %> <% else %>
multiplied by the percentage equity stake <%= question_link("equity", log, current_user) %> multiplied by the percentage equity share <%= question_link("equity", log, current_user) %>
<% end %> <% end %>
</p> </p>
<% end %> <% end %>

5
app/views/logs/_tasklist.html.erb

@ -1,5 +1,6 @@
<ol class="app-task-list govuk-!-margin-top-8"> <ol class="app-task-list govuk-!-margin-top-8">
<% @log.form.sections.map do |section| %> <% @log.form.sections.each do |section| %>
<% next unless section.displayed_in_tasklist?(@log) %>
<li> <li>
<h2 class="app-task-list__section-heading"> <h2 class="app-task-list__section-heading">
<%= section.label %> <%= section.label %>
@ -8,7 +9,7 @@
<p class="govuk-body"><%= section.description.html_safe %></p> <p class="govuk-body"><%= section.description.html_safe %></p>
<% end %> <% end %>
<ul class="app-task-list__items"> <ul class="app-task-list__items">
<% section.subsections.map do |subsection| %> <% section.subsections.each do |subsection| %>
<% if subsection.displayed_in_tasklist?(@log) && (subsection.applicable_questions(@log).count > 0 || !subsection.enabled?(@log)) %> <% if subsection.displayed_in_tasklist?(@log) && (subsection.applicable_questions(@log).count > 0 || !subsection.enabled?(@log)) %>
<% subsection_status = subsection.status(@log) %> <% subsection_status = subsection.status(@log) %>
<li class="app-task-list__item"> <li class="app-task-list__item">

8
app/views/merge_requests/_notification_banners.html.erb

@ -19,3 +19,11 @@
No changes have been made. Try beginning the merge again. No changes have been made. Try beginning the merge again.
<% end %> <% end %>
<% end %> <% end %>
<% if @merge_request.merge_date&.future? %>
<%= govuk_notification_banner(title_text: "Important") do %>
<p class="govuk-notification-banner__heading govuk-!-width-full" style="max-width: fit-content">
This merge is happening in the future. Wait until the merge date to begin this merge.
</p>
<% end %>
<% end %>

2
app/views/merge_requests/show.html.erb

@ -12,7 +12,7 @@
</h1> </h1>
<% unless @merge_request.status == "request_merged" || @merge_request.status == "processing" %> <% unless @merge_request.status == "request_merged" || @merge_request.status == "processing" %>
<div class="govuk-button-group"> <div class="govuk-button-group">
<%= govuk_button_link_to "Begin merge", merge_start_confirmation_merge_request_path(@merge_request), disabled: @merge_request.status != "ready_to_merge" %> <%= govuk_button_link_to "Begin merge", merge_start_confirmation_merge_request_path(@merge_request), disabled: begin_merge_disabled?(@merge_request) %>
<%= govuk_button_link_to "Delete merge request", delete_confirmation_merge_request_path(@merge_request), warning: true %> <%= govuk_button_link_to "Delete merge request", delete_confirmation_merge_request_path(@merge_request), warning: true %>
</div> </div>
<% end %> <% end %>

4
config/locales/en.yml

@ -183,8 +183,11 @@ en:
merge_date: merge_date:
blank: "Enter a merge date." blank: "Enter a merge date."
invalid: "Enter a valid merge date." invalid: "Enter a valid merge date."
more_than_year_from_today: "The merge date must not be later than a year from today’s date."
existing_absorbing_organisation: existing_absorbing_organisation:
blank: "You must answer absorbing organisation already active?" blank: "You must answer absorbing organisation already active?"
merging_organisation_id:
part_of_another_merge: "Another merge request records %{organisation} as merging into %{absorbing_organisation} on %{merge_date}. Select another organisation or remove this organisation from the other merge request."
notification: notification:
attributes: attributes:
title: title:
@ -372,6 +375,7 @@ en:
during_deactivated_period: "The location is already deactivated during this date, please enter a different date." during_deactivated_period: "The location is already deactivated during this date, please enter a different date."
merge_request: merge_request:
organisation_part_of_another_merge: "This organisation is part of another merge - select a different one." organisation_part_of_another_merge: "This organisation is part of another merge - select a different one."
organisation_part_of_another_incomplete_merge: "Another merge request records %{organisation} as merging into %{absorbing_organisation} on %{merge_date}. Select another organisation or remove this organisation from the other merge request."
organisation_not_selected: "Select an organisation from the search list." organisation_not_selected: "Select an organisation from the search list."
soft_validations: soft_validations:

2
config/locales/forms/2024/lettings/setup.en.yml

@ -30,7 +30,7 @@ en:
scheme_id: scheme_id:
page_header: "Scheme" page_header: "Scheme"
check_answer_label: "Scheme name" check_answer_label: "Scheme name"
hint_text: "Enter postcode or scheme name.<br><br>A supported housing scheme provides shared or self-contained housing for a particular client group, for example younger or vulnerable people." hint_text: "Enter postcode, scheme name, or scheme code (for example, S123).<br><br>A supported housing scheme provides shared or self-contained housing for a particular client group, for example younger or vulnerable people."
question_text: "What scheme is this log for?" question_text: "What scheme is this log for?"
location_id: location_id:

8
config/locales/forms/2024/lettings/soft_validations.en.yml

@ -130,3 +130,11 @@ en:
question_text: "Are you sure the property has been vacant for this long?" question_text: "Are you sure the property has been vacant for this long?"
title_text: "You told us the property has been vacant for 2 years." title_text: "You told us the property has been vacant for 2 years."
informative_text: "This is longer than we would expect." informative_text: "This is longer than we would expect."
no_address_found:
page_header: ""
check_answer_label: "No address found"
hint_text: ""
question_text: "We could not find an address that matches your search. You can search again or continue to enter the address manually."
title_text: "No address found"
informative_text: "We could not find an address that matches your search. You can search again or continue to enter the address manually."

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

@ -107,9 +107,9 @@ en:
equity: equity:
page_header: "About the price of the property" page_header: "About the price of the property"
check_answer_label: "Initial percentage equity stake" check_answer_label: "Initial percentage equity share"
hint_text: "Enter the amount of initial equity held by the purchaser (for example, 25% or 50%)" hint_text: "Enter the amount of initial equity share held by the purchaser (for example, 25% or 50%)"
question_text: "What was the initial percentage equity stake purchased?" question_text: "What was the initial percentage share purchased?"
mortgageused: mortgageused:
page_header: "Mortgage Amount" page_header: "Mortgage Amount"
@ -168,9 +168,9 @@ en:
leaseholdcharges: leaseholdcharges:
page_header: "" page_header: ""
has_mscharge: has_mscharge:
check_answer_label: "Does the property have any monthly leasehold charges?" check_answer_label: "Does the property have any service charges?"
hint_text: "For example, service and management charges" hint_text: "For example, service and management charges"
question_text: "Does the property have any monthly leasehold charges?" question_text: "Does the property have any service charges?"
mscharge: mscharge:
check_answer_label: "Monthly leasehold charges" check_answer_label: "Monthly leasehold charges"
hint_text: "" hint_text: ""
@ -199,3 +199,14 @@ en:
check_answer_label: "Amount of any loan, grant or subsidy" check_answer_label: "Amount of any loan, grant or subsidy"
hint_text: "For all schemes except Right to Buy (RTB), Preserved Right to Buy (PRTB), Voluntary Right to Buy (VRTB) and Rent to Buy" hint_text: "For all schemes except Right to Buy (RTB), Preserved Right to Buy (PRTB), Voluntary Right to Buy (VRTB) and Rent to Buy"
question_text: "What was the amount of any loan, grant, discount or subsidy given?" question_text: "What was the amount of any loan, grant, discount or subsidy given?"
management_fee:
page_header: ""
has_management_fee:
check_answer_label: "Does the property have an estate management fee?"
hint_text: "Estate management fees are typically used for the maintenance of communal gardens, payments, private roads, car parks and/or play areas within new build estates."
question_text: "Does the property have an estate management fee?"
management_fee:
check_answer_label: "Monthly estate management fee"
hint_text: ""
question_text: "Enter the total monthly management fee"

17
config/locales/validations/lettings/property_information.en.yml

@ -4,6 +4,7 @@ en:
property: property:
postcode_full: postcode_full:
invalid: "Enter a postcode in the correct format, for example AA1 1AA." invalid: "Enter a postcode in the correct format, for example AA1 1AA."
not_in_england: "It looks like you have an entered a postcode outside of England. Only create logs for lettings in England."
rsnvac: rsnvac:
non_temp_accommodation: "Answer cannot be re-let to tenant who occupied the same property as temporary accommodation as this accommodation is not temporary." non_temp_accommodation: "Answer cannot be re-let to tenant who occupied the same property as temporary accommodation as this accommodation is not temporary."
referral_invalid: "Answer cannot be re-let to tenant who occupied the same property as temporary accommodation as a different source of referral for this letting." referral_invalid: "Answer cannot be re-let to tenant who occupied the same property as temporary accommodation as a different source of referral for this letting."
@ -18,4 +19,18 @@ en:
one_seven_bedroom_shared: "A shared house must have 1 to 7 bedrooms." one_seven_bedroom_shared: "A shared house must have 1 to 7 bedrooms."
uprn: uprn:
invalid: "UPRN must be 12 digits or less." invalid: "UPRN must be 12 digits or less."
not_in_england: "It looks like you have entered an address outside of England. Only create logs for lettings in England."
uprn_confirmation:
not_in_england: "It looks like you have entered an address outside of England. Only create logs for lettings in England."
uprn_selection:
not_in_england: "It looks like you have entered an address outside of England. Only create logs for lettings in England."
la:
not_in_england: "It looks like you have entered an address outside of England. Only create logs for lettings in England."
scheme_id:
not_in_england: "This scheme’s only location is outside of England. Only create logs for lettings in England."
location_id:
not_in_england: "It looks like you have selected a location outside of England. Only create logs for lettings in England."
startdate:
postcode_not_in_england: "It looks like you have an entered a postcode outside of England. Only create logs for lettings in England."
address_not_in_england: "It looks like you have entered an address outside of England. Only create logs for lettings in England."
location_not_in_england: "It looks like you have selected a location outside of England. Only create logs for lettings in England."

11
config/locales/validations/sales/property_information.en.yml

@ -7,6 +7,7 @@ en:
joint_purchase: "Buyers’ last accommodation and discounted ownership postcodes must match." joint_purchase: "Buyers’ last accommodation and discounted ownership postcodes must match."
not_joint_purchase: "Buyer’s last accommodation and discounted ownership postcodes must match." not_joint_purchase: "Buyer’s last accommodation and discounted ownership postcodes must match."
invalid: "Enter a postcode in the correct format, for example AA1 1AA." invalid: "Enter a postcode in the correct format, for example AA1 1AA."
not_in_england: "It looks like you have entered a postcode outside of England - only submit Lettings forms for Lettings that occur in England"
ppostcode_full: ppostcode_full:
postcode_must_match_previous: postcode_must_match_previous:
joint_purchase: "Buyers’ last accommodation and discounted ownership postcodes must match." joint_purchase: "Buyers’ last accommodation and discounted ownership postcodes must match."
@ -20,9 +21,19 @@ en:
joint_purchase: "Buyers’ last accommodation and discounted ownership postcodes must match." joint_purchase: "Buyers’ last accommodation and discounted ownership postcodes must match."
not_joint_purchase: "Buyer’s last accommodation and discounted ownership postcodes must match." not_joint_purchase: "Buyer’s last accommodation and discounted ownership postcodes must match."
invalid: "UPRN must be 12 digits or less." invalid: "UPRN must be 12 digits or less."
not_in_england: "It looks like you have an entered a postcode outside of England. Only create logs for lettings in England."
beds: beds:
bedsits_have_max_one_bedroom: "Number of bedrooms must be 1 if the property is a bedsit." bedsits_have_max_one_bedroom: "Number of bedrooms must be 1 if the property is a bedsit."
proptype: proptype:
bedsits_have_max_one_bedroom: "Answer cannot be 'Bedsit' if the property has 2 or more bedrooms." bedsits_have_max_one_bedroom: "Answer cannot be 'Bedsit' if the property has 2 or more bedrooms."
uprn_known: uprn_known:
invalid: "You must answer UPRN known?" invalid: "You must answer UPRN known?"
la:
not_in_england: "It looks like you have entered an address outside of England. Only create logs for lettings in England."
uprn_confirmation:
not_in_england: "It looks like you have entered an address outside of England. Only create logs for lettings in England."
uprn_selection:
not_in_england: "It looks like you have entered an address outside of England. Only create logs for lettings in England."
saledate:
postcode_not_in_england: "It looks like you have an entered a postcode outside of England. Only create logs for lettings in England."
address_not_in_england: "It looks like you have entered an address outside of England. Only create logs for lettings in England."

2
config/locales/validations/sales/sale_information.en.yml

@ -8,9 +8,11 @@ en:
hodate: hodate:
must_be_before_saledate: "Practical completion or handover date must be before sale completion date." must_be_before_saledate: "Practical completion or handover date must be before sale completion date."
must_be_less_than_3_years_from_saledate: "Practical completion or handover date must be less than 3 years before sale completion date." must_be_less_than_3_years_from_saledate: "Practical completion or handover date must be less than 3 years before sale completion date."
must_be_less_than_5_years_from_saledate: "Practical completion or handover date must be less than 5 years before sale completion date."
saledate: saledate:
must_be_after_hodate: "Sale completion date must be after practical completion or handover date." must_be_after_hodate: "Sale completion date must be after practical completion or handover date."
must_be_less_than_3_years_from_hodate: "Sale completion date must be less than 3 years after practical completion or handover date." must_be_less_than_3_years_from_hodate: "Sale completion date must be less than 3 years after practical completion or handover date."
must_be_less_than_5_years_from_hodate: "Sale completion date must be less than 5 years after practical completion or handover date."
must_be_after_exdate: "Sale completion date must be after contract exchange date." must_be_after_exdate: "Sale completion date must be after contract exchange date."
must_be_less_than_1_year_from_exdate: "Sale completion date must be less than 1 year after contract exchange date." must_be_less_than_1_year_from_exdate: "Sale completion date must be less than 1 year after contract exchange date."
mortgage_used_year: "You must answer either ‘yes’ or ‘no’ to the question ‘was a mortgage used’ for the selected year." mortgage_used_year: "You must answer either ‘yes’ or ‘no’ to the question ‘was a mortgage used’ for the selected year."

12
config/routes.rb

@ -382,6 +382,18 @@ Rails.application.routes.draw do
end end
end end
resources :csv_downloads, path: "csv-downloads" do
member do
get "/", to: "csv_downloads#show", as: "show"
get "download", to: "csv_downloads#download"
end
end
get "create-test-lettings-log", to: "lettings_logs#create_test_log"
get "create-test-sales-log", to: "sales_logs#create_test_log"
get "create-setup-test-lettings-log", to: "lettings_logs#create_setup_test_log"
get "create-setup-test-sales-log", to: "sales_logs#create_setup_test_log"
scope via: :all do scope via: :all do
match "/404", to: "errors#not_found" match "/404", to: "errors#not_found"
match "/429", to: "errors#too_many_requests", status: 429 match "/429", to: "errors#too_many_requests", status: 429

8
db/migrate/20241114154215_add_management_fee_fields.rb

@ -0,0 +1,8 @@
class AddManagementFeeFields < ActiveRecord::Migration[7.0]
def change
change_table :sales_logs, bulk: true do |t|
t.column :has_management_fee, :integer
t.column :management_fee, :decimal, precision: 10, scale: 2
end
end
end

12
db/migrate/20241118104046_add_csv_download_table.rb

@ -0,0 +1,12 @@
class AddCsvDownloadTable < ActiveRecord::Migration[7.0]
def change
create_table :csv_downloads do |t|
t.column :download_type, :string
t.column :filename, :string
t.column :expiration_time, :integer
t.timestamps
t.references :user
t.references :organisation
end
end
end

16
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.0].define(version: 2024_10_31_102744) do ActiveRecord::Schema[7.0].define(version: 2024_11_18_104046) 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"
@ -64,6 +64,18 @@ ActiveRecord::Schema[7.0].define(version: 2024_10_31_102744) do
t.datetime "discarded_at" t.datetime "discarded_at"
end end
create_table "csv_downloads", force: :cascade do |t|
t.string "download_type"
t.string "filename"
t.integer "expiration_time"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "user_id"
t.bigint "organisation_id"
t.index ["organisation_id"], name: "index_csv_downloads_on_organisation_id"
t.index ["user_id"], name: "index_csv_downloads_on_user_id"
end
create_table "csv_variable_definitions", force: :cascade do |t| create_table "csv_variable_definitions", force: :cascade do |t|
t.string "variable", null: false t.string "variable", null: false
t.string "definition", null: false t.string "definition", null: false
@ -746,6 +758,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_10_31_102744) do
t.integer "partner_under_16_value_check" t.integer "partner_under_16_value_check"
t.integer "multiple_partners_value_check" t.integer "multiple_partners_value_check"
t.bigint "created_by_id" t.bigint "created_by_id"
t.integer "has_management_fee"
t.decimal "management_fee", precision: 10, scale: 2
t.index ["assigned_to_id"], name: "index_sales_logs_on_assigned_to_id" t.index ["assigned_to_id"], name: "index_sales_logs_on_assigned_to_id"
t.index ["bulk_upload_id"], name: "index_sales_logs_on_bulk_upload_id" t.index ["bulk_upload_id"], name: "index_sales_logs_on_bulk_upload_id"
t.index ["created_by_id"], name: "index_sales_logs_on_created_by_id" t.index ["created_by_id"], name: "index_sales_logs_on_created_by_id"

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save