diff --git a/.github/workflows/review_pipeline.yml b/.github/workflows/review_pipeline.yml
index 440dddf5c..d5921e049 100644
--- a/.github/workflows/review_pipeline.yml
+++ b/.github/workflows/review_pipeline.yml
@@ -18,7 +18,7 @@ jobs:
postgres:
name: Provision postgres
runs-on: ubuntu-latest
- environment: staging
+ environment: review
steps:
- name: Install Cloud Foundry CLI
@@ -44,7 +44,7 @@ jobs:
redis:
name: Provision redis
runs-on: ubuntu-latest
- environment: staging
+ environment: review
steps:
- name: Install Cloud Foundry CLI
@@ -70,7 +70,7 @@ jobs:
deploy:
name: Deploy review app
runs-on: ubuntu-latest
- environment: staging
+ environment: review
needs: [postgres, redis]
permissions:
issues: write
@@ -125,7 +125,7 @@ jobs:
cf set-env $APP_NAME IMPORT_PAAS_INSTANCE $IMPORT_PAAS_INSTANCE
cf set-env $APP_NAME EXPORT_PAAS_INSTANCE "dluhc-core-review-export-bucket"
cf set-env $APP_NAME S3_CONFIG $S3_CONFIG
- cf set-env $APP_NAME CSV_DOWNLOAD_PAAS_INSTANCE "dluhc-core-staging-csv-bucket"
+ cf set-env $APP_NAME CSV_DOWNLOAD_PAAS_INSTANCE "dluhc-core-review-csv-bucket"
cf set-env $APP_NAME SENTRY_DSN $SENTRY_DSN
cf set-env $APP_NAME APP_HOST "https://dluhc-core-review-${{ github.event.pull_request.number }}.london.cloudapps.digital"
diff --git a/.github/workflows/review_teardown_pipeline.yml b/.github/workflows/review_teardown_pipeline.yml
index 08032596d..3962243c4 100644
--- a/.github/workflows/review_teardown_pipeline.yml
+++ b/.github/workflows/review_teardown_pipeline.yml
@@ -14,7 +14,7 @@ jobs:
app:
name: Teardown app
runs-on: ubuntu-latest
- environment: staging
+ environment: review
steps:
- name: Install Cloud Foundry CLI
@@ -40,7 +40,7 @@ jobs:
postgres:
name: Teardown postgres
runs-on: ubuntu-latest
- environment: staging
+ environment: review
needs: [app]
steps:
@@ -67,7 +67,7 @@ jobs:
redis:
name: Teardown redis
runs-on: ubuntu-latest
- environment: staging
+ environment: review
needs: [app]
steps:
diff --git a/app/controllers/form_controller.rb b/app/controllers/form_controller.rb
index e7bbf38ec..dbc5ba490 100644
--- a/app/controllers/form_controller.rb
+++ b/app/controllers/form_controller.rb
@@ -126,7 +126,7 @@ private
next_page = form.get_page(next_page_id)
previous_page = form.previous_page_id(@page, @log, current_user)
- if next_page&.interruption_screen? || next_page_id == previous_page
+ if next_page&.interruption_screen? || next_page_id == previous_page || CONFIRMATION_PAGE_IDS.include?(next_page_id)
return send("#{@log.class.name.underscore}_#{next_page_id}_path", @log, { referrer: "check_answers" })
else
return send("#{@log.model_name.param_key}_#{form.subsection_for_page(@page).id}_check_answers_path", @log)
@@ -180,4 +180,6 @@ private
redirect_to lettings_log_path(@log) unless @log.collection_period_open?
end
+
+ CONFIRMATION_PAGE_IDS = %w[uprn_confirmation].freeze
end
diff --git a/app/controllers/merge_requests_controller.rb b/app/controllers/merge_requests_controller.rb
new file mode 100644
index 000000000..3d1aa3f5f
--- /dev/null
+++ b/app/controllers/merge_requests_controller.rb
@@ -0,0 +1,86 @@
+class MergeRequestsController < ApplicationController
+ before_action :find_resource, only: %i[update organisations update_organisations remove_merging_organisation]
+ before_action :authenticate_user!
+ before_action :authenticate_scope!, except: [:create]
+
+ def create
+ ActiveRecord::Base.transaction do
+ @merge_request = MergeRequest.create!(merge_request_params.merge(status: :unsubmitted))
+ MergeRequestOrganisation.create!({ merge_request: @merge_request, merging_organisation: @merge_request.requesting_organisation })
+ end
+ redirect_to organisations_merge_request_path(@merge_request)
+ rescue ActiveRecord::RecordInvalid
+ render_not_found
+ end
+
+ def organisations
+ @answer_options = organisations_answer_options
+ end
+
+ def update
+ if @merge_request.update(merge_request_params)
+ redirect_to next_page_path
+ else
+ render previous_template, status: :unprocessable_entity
+ end
+ end
+
+ def update_organisations
+ merge_request_organisation = MergeRequestOrganisation.new(merge_request_organisation_params)
+ @answer_options = organisations_answer_options
+ if merge_request_organisation.save
+ render :organisations
+ else
+ render :organisations, status: :unprocessable_entity
+ end
+ end
+
+ def remove_merging_organisation
+ MergeRequestOrganisation.find_by(merge_request_organisation_params)&.destroy!
+ @answer_options = organisations_answer_options
+ render :organisations
+ end
+
+private
+
+ def organisations_answer_options
+ answer_options = { "" => "Select an option" }
+
+ Organisation.all.pluck(:id, :name).each do |organisation|
+ answer_options[organisation[0]] = organisation[1]
+ end
+ answer_options
+ end
+
+ def merge_request_params
+ merge_params = params.fetch(:merge_request, {}).permit(:requesting_organisation_id, :other_merging_organisations, :status)
+
+ if merge_params[:requesting_organisation_id].present? && (current_user.data_coordinator? || current_user.data_provider?)
+ merge_params[:requesting_organisation_id] = current_user.organisation.id
+ end
+
+ merge_params
+ end
+
+ def merge_request_organisation_params
+ { merge_request: @merge_request, merging_organisation_id: params[:merge_request][:merging_organisation] }
+ end
+
+ def find_resource
+ @merge_request = MergeRequest.find(params[:id])
+ end
+
+ def next_page_path
+ absorbing_organisation_merge_request_path(@merge_request)
+ end
+
+ def previous_template
+ :organisations
+ end
+
+ def authenticate_scope!
+ if current_user.organisation != @merge_request.requesting_organisation && !current_user.support?
+ render_not_found
+ end
+ end
+end
diff --git a/app/controllers/organisations_controller.rb b/app/controllers/organisations_controller.rb
index 1bd4694ec..70943063f 100644
--- a/app/controllers/organisations_controller.rb
+++ b/app/controllers/organisations_controller.rb
@@ -137,6 +137,10 @@ class OrganisationsController < ApplicationController
end
end
+ def merge_request
+ @merge_request = MergeRequest.new
+ end
+
private
def org_params
diff --git a/app/controllers/start_controller.rb b/app/controllers/start_controller.rb
index d707d3e91..671b63805 100644
--- a/app/controllers/start_controller.rb
+++ b/app/controllers/start_controller.rb
@@ -28,4 +28,44 @@ class StartController < ApplicationController
type: "application/pdf",
)
end
+
+ def download_23_24_lettings_bulk_upload_template
+ send_file(
+ Rails.root.join("public/files/bulk-upload-lettings-template-2023-24.xlsx"),
+ filename: "2023-24-lettings-bulk-upload-template.xlsx",
+ type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ )
+ end
+
+ def download_23_24_lettings_bulk_upload_legacy_template
+ send_file(
+ Rails.root.join("public/files/bulk-upload-lettings-legacy-template-2023-24.xlsx"),
+ filename: "2023-24-lettings-bulk-upload-legacy-template.xlsx",
+ type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ )
+ end
+
+ def download_23_24_lettings_bulk_upload_specification
+ send_file(
+ Rails.root.join("public/files/bulk-upload-lettings-specification-2023-24.xlsx"),
+ filename: "2023-24-lettings-bulk-upload-specification.xlsx",
+ type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ )
+ end
+
+ def download_22_23_lettings_bulk_upload_template
+ send_file(
+ Rails.root.join("public/files/bulk-upload-lettings-template-2022-23.xlsx"),
+ filename: "2022-23-lettings-bulk-upload-template.xlsx",
+ type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ )
+ end
+
+ def download_22_23_lettings_bulk_upload_specification
+ send_file(
+ Rails.root.join("public/files/bulk-upload-lettings-specification-2022-23.xlsx"),
+ filename: "2022-23-lettings-bulk-upload-specification.xlsx",
+ type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ )
+ end
end
diff --git a/app/helpers/tasklist_helper.rb b/app/helpers/tasklist_helper.rb
index 6a12cff29..678ef044f 100644
--- a/app/helpers/tasklist_helper.rb
+++ b/app/helpers/tasklist_helper.rb
@@ -1,5 +1,6 @@
module TasklistHelper
include GovukLinkHelper
+ include CollectionTimeHelper
def get_next_incomplete_section(log)
log.form.subsections.find { |subsection| subsection.is_incomplete?(log) }
@@ -34,7 +35,9 @@ module TasklistHelper
"You can #{govuk_link_to 'review and make changes to this log', link} until #{log.form.end_date.to_formatted_s(:govuk_date)}.".html_safe
else
- "This log is from the #{log.form.start_date.year}/#{log.form.start_date.year + 1} collection window, which is now closed."
+ start_year = log.startdate ? collection_start_year_for_date(log.startdate) : log.form.start_date.year
+
+ "This log is from the #{start_year}/#{start_year + 1} collection window, which is now closed."
end
end
diff --git a/app/models/form.rb b/app/models/form.rb
index 80fed4444..cc73d2ca6 100644
--- a/app/models/form.rb
+++ b/app/models/form.rb
@@ -178,48 +178,78 @@ class Form
pages.reject { |p| p.routed_to?(log, current_user) }
end
- def invalidated_questions(log)
- invalidated_page_questions(log) + invalidated_conditional_questions(log)
- end
-
- def invalidated_page_questions(log, current_user = nil)
- # we're already treating these fields as a special case and reset their values upon saving a log
- callback_questions = %w[postcode_known la ppcodenk previous_la_known prevloc postcode_full ppostcode_full location_id address_line1 address_line2 town_or_city county]
- questions.reject { |q| q.page.routed_to?(log, current_user) || q.derived? || callback_questions.include?(q.id) } || []
- end
+ def reset_not_routed_questions_and_invalid_answers(log)
+ reset_checkbox_questions_if_not_routed(log)
- def reset_not_routed_questions(log)
- enabled_questions = enabled_page_questions(log)
- enabled_question_ids = enabled_questions.map(&:id)
+ reset_radio_questions_if_not_routed_or_invalid_answers(log)
- invalidated_page_questions(log).each do |question|
- if %w[radio checkbox].include?(question.type)
- enabled_answer_options = enabled_question_ids.include?(question.id) ? enabled_questions.find { |q| q.id == question.id }.answer_options : {}
- current_answer_option_valid = enabled_answer_options.present? ? enabled_answer_options.key?(log.public_send(question.id).to_s) : false
+ reset_free_user_input_questions_if_not_routed(log)
+ end
- if !current_answer_option_valid && log.respond_to?(question.id.to_s)
- Rails.logger.debug("Cleared #{question.id} value")
- log.public_send("#{question.id}=", nil)
+ def reset_checkbox_questions_if_not_routed(log)
+ checkbox_questions = routed_and_not_routed_questions_by_type(log, type: "checkbox")
+ checkbox_questions[:not_routed].each do |not_routed_question|
+ valid_options = checkbox_questions[:routed]
+ .select { |q| q.id == not_routed_question.id }
+ .flat_map { |q| q.answer_options.keys }
+ not_routed_question.answer_options.each_key do |invalid_option|
+ if !log.respond_to?(invalid_option) || valid_options.include?(invalid_option) || log.public_send(invalid_option).nil?
+ next
else
-
- (question.answer_options.keys - enabled_answer_options.keys).map do |invalid_answer_option|
- Rails.logger.debug("Cleared #{invalid_answer_option} value")
- log.public_send("#{invalid_answer_option}=", nil) if log.respond_to?(invalid_answer_option)
- end
+ clear_attribute(log, invalid_option)
end
+ end
+ end
+ end
+
+ def reset_radio_questions_if_not_routed_or_invalid_answers(log)
+ radio_questions = routed_and_not_routed_questions_by_type(log, type: "radio")
+ valid_radio_options = radio_questions[:routed]
+ .group_by(&:id)
+ .transform_values! { |q_array| q_array.flat_map { |q| q.answer_options.keys } }
+ radio_questions[:not_routed].each do |not_routed_question|
+ question_id = not_routed_question.id
+ if !log.respond_to?(question_id) || log.public_send(question_id).nil? || valid_radio_options.key?(question_id)
+ next
+ else
+ clear_attribute(log, question_id)
+ end
+ end
+ valid_radio_options.each do |question_id, valid_options|
+ if !log.respond_to?(question_id) || valid_options.include?(log.public_send(question_id).to_s)
+ next
+ else
+ clear_attribute(log, question_id)
+ end
+ end
+ end
+
+ def reset_free_user_input_questions_if_not_routed(log)
+ non_radio_checkbox_questions = routed_and_not_routed_questions_by_type(log)
+ enabled_question_ids = non_radio_checkbox_questions[:routed].map(&:id)
+ non_radio_checkbox_questions[:not_routed].each do |not_routed_question|
+ question_id = not_routed_question.id
+ if log.public_send(question_id).nil? || enabled_question_ids.include?(question_id)
+ next
else
- Rails.logger.debug("Cleared #{question.id} value")
- log.public_send("#{question.id}=", nil) unless enabled_question_ids.include?(question.id)
+ clear_attribute(log, question_id)
end
end
end
- def enabled_page_questions(log)
- questions - invalidated_page_questions(log)
+ def routed_and_not_routed_questions_by_type(log, type: nil, current_user: nil)
+ questions_by_type = if type
+ questions.reject { |q| q.type != type || q.disable_clearing_if_not_routed_or_dynamic_answer_options }
+ else
+ questions.reject { |q| %w[radio checkbox].include?(q.type) || q.disable_clearing_if_not_routed_or_dynamic_answer_options }
+ end
+ routed, not_routed = questions_by_type.partition { |q| q.page.routed_to?(log, current_user) || q.derived? }
+ { routed:, not_routed: }
end
- def invalidated_conditional_questions(log)
- questions.reject { |q| q.enabled?(log) } || []
+ def clear_attribute(log, attribute)
+ Rails.logger.debug("Cleared #{attribute} value")
+ log.public_send("#{attribute}=", nil)
end
def readonly_questions
@@ -230,6 +260,13 @@ class Form
questions.select { |q| q.type == "numeric" }
end
+ def previous_page(page_ids, page_index, log, current_user)
+ prev_page = get_page(page_ids[page_index - 1])
+ return prev_page.id if prev_page.routed_to?(log, current_user)
+
+ previous_page(page_ids, page_index - 1, log, current_user)
+ end
+
def send_chain(arr, log)
Array(arr).inject(log) { |o, a| o.public_send(*a) }
end
diff --git a/app/models/form/lettings/pages/max_rent_value_check.rb b/app/models/form/lettings/pages/max_rent_value_check.rb
index 8ae9b7d9c..956e07f44 100644
--- a/app/models/form/lettings/pages/max_rent_value_check.rb
+++ b/app/models/form/lettings/pages/max_rent_value_check.rb
@@ -1,7 +1,6 @@
class Form::Lettings::Pages::MaxRentValueCheck < ::Form::Page
- def initialize(id, hsh, subsection)
- super
- @id = "max_rent_value_check"
+ def initialize(id, hsh, subsection, check_answers_card_number: nil)
+ super(id, hsh, subsection)
@depends_on = [{ "rent_in_soft_max_range?" => true }]
@title_text = {
"translation" => "soft_validations.rent.outside_range_title",
@@ -23,9 +22,10 @@ class Form::Lettings::Pages::MaxRentValueCheck < ::Form::Page
},
],
}
+ @check_answers_card_number = check_answers_card_number
end
def questions
- @questions ||= [Form::Lettings::Questions::RentValueCheck.new(nil, nil, self)]
+ @questions ||= [Form::Lettings::Questions::RentValueCheck.new(nil, nil, self, check_answers_card_number: @check_answers_card_number)]
end
end
diff --git a/app/models/form/lettings/pages/min_rent_value_check.rb b/app/models/form/lettings/pages/min_rent_value_check.rb
index eda8819ee..86b53951c 100644
--- a/app/models/form/lettings/pages/min_rent_value_check.rb
+++ b/app/models/form/lettings/pages/min_rent_value_check.rb
@@ -1,7 +1,6 @@
class Form::Lettings::Pages::MinRentValueCheck < ::Form::Page
- def initialize(id, hsh, subsection)
- super
- @id = "min_rent_value_check"
+ def initialize(id, hsh, subsection, check_answers_card_number: nil)
+ super(id, hsh, subsection)
@depends_on = [{ "rent_in_soft_min_range?" => true }]
@title_text = {
"translation" => "soft_validations.rent.outside_range_title",
@@ -13,17 +12,16 @@ class Form::Lettings::Pages::MinRentValueCheck < ::Form::Page
}
@informative_text = {
"translation" => "soft_validations.rent.min_hint_text",
- "arguments" => [
- {
- "key" => "field_formatted_as_currency",
- "arguments_for_key" => "soft_min_for_period",
- "i18n_template" => "soft_min_for_period",
- },
- ],
+ "arguments" => [{
+ "key" => "field_formatted_as_currency",
+ "arguments_for_key" => "soft_min_for_period",
+ "i18n_template" => "soft_min_for_period",
+ }],
}
+ @check_answers_card_number = check_answers_card_number
end
def questions
- @questions ||= [Form::Lettings::Questions::RentValueCheck.new(nil, nil, self)]
+ @questions ||= [Form::Lettings::Questions::RentValueCheck.new(nil, nil, self, check_answers_card_number: @check_answers_card_number)]
end
end
diff --git a/app/models/form/lettings/questions/address_line1.rb b/app/models/form/lettings/questions/address_line1.rb
index 4c8b4151b..ef197e4fd 100644
--- a/app/models/form/lettings/questions/address_line1.rb
+++ b/app/models/form/lettings/questions/address_line1.rb
@@ -7,6 +7,7 @@ class Form::Lettings::Questions::AddressLine1 < ::Form::Question
@type = "text"
@plain_label = true
@check_answer_label = "Q12 - Address"
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def answer_label(log, _current_user = nil)
diff --git a/app/models/form/lettings/questions/address_line2.rb b/app/models/form/lettings/questions/address_line2.rb
index 16f7c8336..3b2c36dbc 100644
--- a/app/models/form/lettings/questions/address_line2.rb
+++ b/app/models/form/lettings/questions/address_line2.rb
@@ -5,6 +5,7 @@ class Form::Lettings::Questions::AddressLine2 < ::Form::Question
@header = "Address line 2 (optional)"
@type = "text"
@plain_label = true
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def hidden_in_check_answers?(_log = nil, _current_user = nil)
diff --git a/app/models/form/lettings/questions/county.rb b/app/models/form/lettings/questions/county.rb
index 360c0966c..9f0dc7138 100644
--- a/app/models/form/lettings/questions/county.rb
+++ b/app/models/form/lettings/questions/county.rb
@@ -5,6 +5,7 @@ class Form::Lettings::Questions::County < ::Form::Question
@header = "County (optional)"
@type = "text"
@plain_label = true
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def hidden_in_check_answers?(_log = nil, _current_user = nil)
diff --git a/app/models/form/lettings/questions/earnings.rb b/app/models/form/lettings/questions/earnings.rb
index 445710f09..fdf702740 100644
--- a/app/models/form/lettings/questions/earnings.rb
+++ b/app/models/form/lettings/questions/earnings.rb
@@ -10,7 +10,7 @@ class Form::Lettings::Questions::Earnings < ::Form::Question
@min = 0
@guidance_partial = "what_counts_as_income"
@hint_text = ""
- @step = 1
+ @step = 0.01
@prefix = "£"
@suffix = [
{ "label" => " every week", "depends_on" => { "incfreq" => 1 } },
diff --git a/app/models/form/lettings/questions/la.rb b/app/models/form/lettings/questions/la.rb
index 3cafda054..f9d650918 100644
--- a/app/models/form/lettings/questions/la.rb
+++ b/app/models/form/lettings/questions/la.rb
@@ -5,9 +5,10 @@ class Form::Lettings::Questions::La < ::Form::Question
@check_answer_label = "Local Authority"
@header = "What is the property’s local authority?"
@type = "select"
- @check_answers_card_number = 0
+ @check_answers_card_number = nil
@hint_text = ""
@question_number = 13
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def answer_options
diff --git a/app/models/form/lettings/questions/location_id.rb b/app/models/form/lettings/questions/location_id.rb
index 94196534f..80c2516e8 100644
--- a/app/models/form/lettings/questions/location_id.rb
+++ b/app/models/form/lettings/questions/location_id.rb
@@ -1,6 +1,7 @@
class Form::Lettings::Questions::LocationId < ::Form::Question
- def initialize(_id, hsh, page)
- super("location_id", hsh, page)
+ def initialize(id, hsh, page)
+ super
+ @id = "location_id"
@check_answer_label = "Location"
@header = header_text
@type = "radio"
@@ -11,6 +12,7 @@ class Form::Lettings::Questions::LocationId < ::Form::Question
},
}
@question_number = 10
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def answer_options
diff --git a/app/models/form/lettings/questions/postcode_for_full_address.rb b/app/models/form/lettings/questions/postcode_for_full_address.rb
index 015abc2e8..4f41867d7 100644
--- a/app/models/form/lettings/questions/postcode_for_full_address.rb
+++ b/app/models/form/lettings/questions/postcode_for_full_address.rb
@@ -17,6 +17,7 @@ class Form::Lettings::Questions::PostcodeForFullAddress < ::Form::Question
},
}
@plain_label = true
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def hidden_in_check_answers?(_log = nil, _current_user = nil)
diff --git a/app/models/form/lettings/questions/postcode_full.rb b/app/models/form/lettings/questions/postcode_full.rb
index 1f0a25c49..fc7d7691b 100644
--- a/app/models/form/lettings/questions/postcode_full.rb
+++ b/app/models/form/lettings/questions/postcode_full.rb
@@ -10,5 +10,6 @@ class Form::Lettings::Questions::PostcodeFull < ::Form::Question
@check_answers_card_number = 0
@hint_text = ""
@inferred_answers = { "la" => { "is_la_inferred" => true } }
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
end
diff --git a/app/models/form/lettings/questions/postcode_known.rb b/app/models/form/lettings/questions/postcode_known.rb
index 6af30bb19..ea9adb06b 100644
--- a/app/models/form/lettings/questions/postcode_known.rb
+++ b/app/models/form/lettings/questions/postcode_known.rb
@@ -8,6 +8,7 @@ class Form::Lettings::Questions::PostcodeKnown < ::Form::Question
@check_answers_card_number = 0
@hint_text = ""
@answer_options = ANSWER_OPTIONS
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = true
@conditional_for = { "postcode_full" => [1] }
@hidden_in_check_answers = { "depends_on" => [{ "postcode_known" => 0 }, { "postcode_known" => 1 }] }
end
diff --git a/app/models/form/lettings/questions/ppcodenk.rb b/app/models/form/lettings/questions/ppcodenk.rb
index 33b03d959..25557a166 100644
--- a/app/models/form/lettings/questions/ppcodenk.rb
+++ b/app/models/form/lettings/questions/ppcodenk.rb
@@ -11,6 +11,7 @@ class Form::Lettings::Questions::Ppcodenk < ::Form::Question
@conditional_for = { "ppostcode_full" => [1] }
@hidden_in_check_answers = { "depends_on" => [{ "ppcodenk" => 0 }, { "ppcodenk" => 1 }] }
@question_number = 80
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
ANSWER_OPTIONS = { "1" => { "value" => "Yes" }, "0" => { "value" => "No" } }.freeze
diff --git a/app/models/form/lettings/questions/ppostcode_full.rb b/app/models/form/lettings/questions/ppostcode_full.rb
index 0432d1b5b..86b4c7b31 100644
--- a/app/models/form/lettings/questions/ppostcode_full.rb
+++ b/app/models/form/lettings/questions/ppostcode_full.rb
@@ -11,5 +11,6 @@ class Form::Lettings::Questions::PpostcodeFull < ::Form::Question
@hint_text = ""
@inferred_answers = { "prevloc" => { "is_previous_la_inferred" => true } }
@question_number = 80
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
end
diff --git a/app/models/form/lettings/questions/previous_la_known.rb b/app/models/form/lettings/questions/previous_la_known.rb
index a9ff11e55..eb80eda4a 100644
--- a/app/models/form/lettings/questions/previous_la_known.rb
+++ b/app/models/form/lettings/questions/previous_la_known.rb
@@ -11,6 +11,7 @@ class Form::Lettings::Questions::PreviousLaKnown < ::Form::Question
@conditional_for = { "prevloc" => [1] }
@hidden_in_check_answers = { "depends_on" => [{ "previous_la_known" => 0 }, { "previous_la_known" => 1 }] }
@question_number = 81
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
ANSWER_OPTIONS = { "1" => { "value" => "Yes" }, "0" => { "value" => "No" } }.freeze
diff --git a/app/models/form/lettings/questions/prevloc.rb b/app/models/form/lettings/questions/prevloc.rb
index 229af2c18..e0082602e 100644
--- a/app/models/form/lettings/questions/prevloc.rb
+++ b/app/models/form/lettings/questions/prevloc.rb
@@ -9,6 +9,7 @@ class Form::Lettings::Questions::Prevloc < ::Form::Question
@check_answers_card_number = 0
@hint_text = "Select ‘Northern Ireland’, ‘Scotland’, ‘Wales’ or ‘Outside the UK’ if the household’s last settled home was outside England."
@question_number = 81
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def answer_options
diff --git a/app/models/form/lettings/questions/rent_value_check.rb b/app/models/form/lettings/questions/rent_value_check.rb
index eac22908a..6bca43e30 100644
--- a/app/models/form/lettings/questions/rent_value_check.rb
+++ b/app/models/form/lettings/questions/rent_value_check.rb
@@ -1,11 +1,11 @@
class Form::Lettings::Questions::RentValueCheck < ::Form::Question
- def initialize(id, hsh, page)
- super
+ def initialize(id, hsh, page, check_answers_card_number:)
+ super(id, hsh, page)
@id = "rent_value_check"
@check_answer_label = "Total rent confirmation"
@header = "Are you sure this is correct?"
@type = "interruption_screen"
- @check_answers_card_number = 0
+ @check_answers_card_number = check_answers_card_number
@answer_options = ANSWER_OPTIONS
@hidden_in_check_answers = { "depends_on" => [{ "rent_value_check" => 0 }, { "rent_value_check" => 1 }] }
end
diff --git a/app/models/form/lettings/questions/town_or_city.rb b/app/models/form/lettings/questions/town_or_city.rb
index f1eac8dff..501e9bec4 100644
--- a/app/models/form/lettings/questions/town_or_city.rb
+++ b/app/models/form/lettings/questions/town_or_city.rb
@@ -5,6 +5,7 @@ class Form::Lettings::Questions::TownOrCity < ::Form::Question
@header = "Town or city"
@type = "text"
@plain_label = true
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def hidden_in_check_answers?(_log = nil, _current_user = nil)
diff --git a/app/models/form/lettings/questions/uprn_known.rb b/app/models/form/lettings/questions/uprn_known.rb
index 816a31ffb..95c53ec6a 100644
--- a/app/models/form/lettings/questions/uprn_known.rb
+++ b/app/models/form/lettings/questions/uprn_known.rb
@@ -9,7 +9,18 @@ class Form::Lettings::Questions::UprnKnown < ::Form::Question
@hint_text = "The Unique Property Reference Number (UPRN) is a unique number system created by Ordnance Survey and used by housing providers and sectors UK-wide. For example 10010457355.
You can continue without the UPRN, but it means we will need you to enter the address of the property."
@conditional_for = { "uprn" => [1] }
- @hidden_in_check_answers = true
+ @inferred_check_answers_value = [
+ {
+ "condition" => { "uprn_known" => 0 },
+ "value" => "Not known",
+ },
+ ]
+ @hidden_in_check_answers = {
+ "depends_on" => [
+ { "uprn_known" => 0 },
+ { "uprn_known" => 1 },
+ ],
+ }
end
ANSWER_OPTIONS = {
diff --git a/app/models/form/lettings/subsections/income_and_benefits.rb b/app/models/form/lettings/subsections/income_and_benefits.rb
index 30ba3a7e0..25f5d0ce4 100644
--- a/app/models/form/lettings/subsections/income_and_benefits.rb
+++ b/app/models/form/lettings/subsections/income_and_benefits.rb
@@ -24,8 +24,8 @@ class Form::Lettings::Subsections::IncomeAndBenefits < ::Form::Subsection
Form::Lettings::Pages::RentBiWeekly.new(nil, nil, self),
Form::Lettings::Pages::Rent4Weekly.new(nil, nil, self),
Form::Lettings::Pages::RentMonthly.new(nil, nil, self),
- Form::Lettings::Pages::MinRentValueCheck.new(nil, nil, self),
- Form::Lettings::Pages::MaxRentValueCheck.new(nil, nil, self),
+ Form::Lettings::Pages::MinRentValueCheck.new("brent_min_rent_value_check", nil, self, check_answers_card_number: 0),
+ Form::Lettings::Pages::MaxRentValueCheck.new("brent_max_rent_value_check", nil, self, check_answers_card_number: 0),
Form::Lettings::Pages::Outstanding.new(nil, nil, self),
Form::Lettings::Pages::OutstandingAmount.new(nil, nil, self),
].compact
diff --git a/app/models/form/lettings/subsections/property_information.rb b/app/models/form/lettings/subsections/property_information.rb
index 1290f7cf2..0f921d4a5 100644
--- a/app/models/form/lettings/subsections/property_information.rb
+++ b/app/models/form/lettings/subsections/property_information.rb
@@ -10,6 +10,8 @@ class Form::Lettings::Subsections::PropertyInformation < ::Form::Subsection
@pages ||= [
uprn_questions,
Form::Lettings::Pages::PropertyLocalAuthority.new(nil, nil, self),
+ Form::Lettings::Pages::MinRentValueCheck.new("local_authority_min_rent_value_check", nil, self, check_answers_card_number: nil),
+ Form::Lettings::Pages::MaxRentValueCheck.new("local_authority_max_rent_value_check", nil, self, check_answers_card_number: nil),
Form::Lettings::Pages::FirstTimePropertyLetAsSocialHousing.new(nil, nil, self),
Form::Lettings::Pages::PropertyLetType.new(nil, nil, self),
Form::Lettings::Pages::PropertyVacancyReasonNotFirstLet.new(nil, nil, self),
@@ -20,6 +22,8 @@ class Form::Lettings::Subsections::PropertyInformation < ::Form::Subsection
Form::Lettings::Pages::PropertyBuildingType.new(nil, nil, self),
Form::Lettings::Pages::PropertyWheelchairAccessible.new(nil, nil, self),
Form::Lettings::Pages::PropertyNumberOfBedrooms.new(nil, nil, self),
+ Form::Lettings::Pages::MinRentValueCheck.new("beds_min_rent_value_check", nil, self, check_answers_card_number: 0),
+ Form::Lettings::Pages::MaxRentValueCheck.new("beds_max_rent_value_check", nil, self, check_answers_card_number: 0),
Form::Lettings::Pages::VoidDate.new(nil, nil, self),
Form::Lettings::Pages::VoidDateValueCheck.new(nil, nil, self),
Form::Lettings::Pages::PropertyMajorRepairs.new(nil, nil, self),
diff --git a/app/models/form/lettings/subsections/setup.rb b/app/models/form/lettings/subsections/setup.rb
index e2dbb7f8b..312ed5255 100644
--- a/app/models/form/lettings/subsections/setup.rb
+++ b/app/models/form/lettings/subsections/setup.rb
@@ -10,14 +10,22 @@ class Form::Lettings::Subsections::Setup < ::Form::Subsection
@pages ||= [
organisation_page,
stock_owner_page,
+ Form::Lettings::Pages::MinRentValueCheck.new("stock_owner_min_rent_value_check", nil, self),
+ Form::Lettings::Pages::MaxRentValueCheck.new("stock_owner_max_rent_value_check", nil, self),
managing_organisation_page,
created_by_page,
Form::Lettings::Pages::NeedsType.new(nil, nil, self),
Form::Lettings::Pages::Scheme.new(nil, nil, self),
Form::Lettings::Pages::Location.new(nil, nil, self),
+ Form::Lettings::Pages::MinRentValueCheck.new("needs_type_min_rent_value_check", nil, self),
+ Form::Lettings::Pages::MaxRentValueCheck.new("needs_type_max_rent_value_check", nil, self),
Form::Lettings::Pages::Renewal.new(nil, nil, self),
Form::Lettings::Pages::TenancyStartDate.new(nil, nil, self),
+ Form::Lettings::Pages::MinRentValueCheck.new("start_date_min_rent_value_check", nil, self),
+ Form::Lettings::Pages::MaxRentValueCheck.new("start_date_max_rent_value_check", nil, self),
Form::Lettings::Pages::RentType.new(nil, nil, self),
+ Form::Lettings::Pages::MinRentValueCheck.new("rent_type_min_rent_value_check", nil, self),
+ Form::Lettings::Pages::MaxRentValueCheck.new("rent_type_max_rent_value_check", nil, self),
Form::Lettings::Pages::TenantCode.new(nil, nil, self),
Form::Lettings::Pages::PropertyReference.new(nil, nil, self),
].compact
diff --git a/app/models/form/question.rb b/app/models/form/question.rb
index 8e8c26739..d4b7c9d76 100644
--- a/app/models/form/question.rb
+++ b/app/models/form/question.rb
@@ -1,5 +1,5 @@
class Form::Question
- attr_accessor :id, :header, :hint_text, :description, :questions,
+ attr_accessor :id, :header, :hint_text, :description, :questions, :disable_clearing_if_not_routed_or_dynamic_answer_options,
:type, :min, :max, :step, :width, :fields_to_add, :result_field,
:conditional_for, :readonly, :answer_options, :page, :check_answer_label,
:inferred_answers, :hidden_in_check_answers, :inferred_check_answers_value,
@@ -42,6 +42,7 @@ class Form::Question
@unresolved_hint_text = hsh["unresolved_hint_text"]
@question_number = hsh["question_number"]
@plain_label = hsh["plain_label"]
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = hsh["disable_clearing_if_not_routed_or_dynamic_answer_options"]
end
end
diff --git a/app/models/form/sales/pages/extra_borrowing_value_check.rb b/app/models/form/sales/pages/extra_borrowing_value_check.rb
index 18f975b8d..5fff74db2 100644
--- a/app/models/form/sales/pages/extra_borrowing_value_check.rb
+++ b/app/models/form/sales/pages/extra_borrowing_value_check.rb
@@ -9,8 +9,7 @@ class Form::Sales::Pages::ExtraBorrowingValueCheck < Form::Page
@title_text = {
"translation" => "soft_validations.extra_borrowing.title",
}
- @informative_text = {
- }
+ @informative_text = {}
end
def questions
diff --git a/app/models/form/sales/pages/uprn.rb b/app/models/form/sales/pages/uprn.rb
index ad247207f..c4ae5b357 100644
--- a/app/models/form/sales/pages/uprn.rb
+++ b/app/models/form/sales/pages/uprn.rb
@@ -11,10 +11,6 @@ class Form::Sales::Pages::Uprn < ::Form::Page
]
end
- def routed_to?(log, _current_user = nil)
- log.uprn_known == 1
- end
-
def skip_text
"Enter address instead"
end
diff --git a/app/models/form/sales/questions/address_line1.rb b/app/models/form/sales/questions/address_line1.rb
index a95051116..edee2e7ee 100644
--- a/app/models/form/sales/questions/address_line1.rb
+++ b/app/models/form/sales/questions/address_line1.rb
@@ -7,6 +7,7 @@ class Form::Sales::Questions::AddressLine1 < ::Form::Question
@type = "text"
@plain_label = true
@check_answer_label = "Q15 - Address"
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def answer_label(log, _current_user = nil)
diff --git a/app/models/form/sales/questions/address_line2.rb b/app/models/form/sales/questions/address_line2.rb
index 0b4ff661c..94396a2af 100644
--- a/app/models/form/sales/questions/address_line2.rb
+++ b/app/models/form/sales/questions/address_line2.rb
@@ -5,6 +5,7 @@ class Form::Sales::Questions::AddressLine2 < ::Form::Question
@header = "Address line 2 (optional)"
@type = "text"
@plain_label = true
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def hidden_in_check_answers?(_log = nil, _current_user = nil)
diff --git a/app/models/form/sales/questions/county.rb b/app/models/form/sales/questions/county.rb
index 080ac809f..6586cb9e6 100644
--- a/app/models/form/sales/questions/county.rb
+++ b/app/models/form/sales/questions/county.rb
@@ -5,6 +5,7 @@ class Form::Sales::Questions::County < ::Form::Question
@header = "County (optional)"
@type = "text"
@plain_label = true
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def hidden_in_check_answers?(_log = nil, _current_user = nil)
diff --git a/app/models/form/sales/questions/postcode.rb b/app/models/form/sales/questions/postcode.rb
index 0f72a9585..55e33199a 100644
--- a/app/models/form/sales/questions/postcode.rb
+++ b/app/models/form/sales/questions/postcode.rb
@@ -17,5 +17,6 @@ class Form::Sales::Questions::Postcode < ::Form::Question
"is_la_inferred" => true,
},
}
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
end
diff --git a/app/models/form/sales/questions/postcode_for_full_address.rb b/app/models/form/sales/questions/postcode_for_full_address.rb
index a1e6f8633..5d3b9f122 100644
--- a/app/models/form/sales/questions/postcode_for_full_address.rb
+++ b/app/models/form/sales/questions/postcode_for_full_address.rb
@@ -17,6 +17,7 @@ class Form::Sales::Questions::PostcodeForFullAddress < ::Form::Question
},
}
@plain_label = true
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def hidden_in_check_answers?(_log = nil, _current_user = nil)
diff --git a/app/models/form/sales/questions/previous_la_known.rb b/app/models/form/sales/questions/previous_la_known.rb
index 1a9a646f3..1a4a2f438 100644
--- a/app/models/form/sales/questions/previous_la_known.rb
+++ b/app/models/form/sales/questions/previous_la_known.rb
@@ -21,6 +21,7 @@ class Form::Sales::Questions::PreviousLaKnown < ::Form::Question
"prevloc" => [1],
}
@question_number = 58
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
ANSWER_OPTIONS = {
diff --git a/app/models/form/sales/questions/previous_postcode.rb b/app/models/form/sales/questions/previous_postcode.rb
index 568ba66ea..5eb8e6d54 100644
--- a/app/models/form/sales/questions/previous_postcode.rb
+++ b/app/models/form/sales/questions/previous_postcode.rb
@@ -18,5 +18,6 @@ class Form::Sales::Questions::PreviousPostcode < ::Form::Question
},
}
@question_number = 57
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
end
diff --git a/app/models/form/sales/questions/previous_postcode_known.rb b/app/models/form/sales/questions/previous_postcode_known.rb
index e83fda8d3..f53f52bf0 100644
--- a/app/models/form/sales/questions/previous_postcode_known.rb
+++ b/app/models/form/sales/questions/previous_postcode_known.rb
@@ -21,6 +21,7 @@ class Form::Sales::Questions::PreviousPostcodeKnown < ::Form::Question
],
}
@question_number = 57
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
ANSWER_OPTIONS = {
diff --git a/app/models/form/sales/questions/prevloc.rb b/app/models/form/sales/questions/prevloc.rb
index 75bb50e81..71606aa34 100644
--- a/app/models/form/sales/questions/prevloc.rb
+++ b/app/models/form/sales/questions/prevloc.rb
@@ -12,6 +12,7 @@ class Form::Sales::Questions::Prevloc < ::Form::Question
"value" => "Not known",
}]
@question_number = 58
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def answer_options
diff --git a/app/models/form/sales/questions/property_local_authority.rb b/app/models/form/sales/questions/property_local_authority.rb
index 04dbed347..df3fe5e88 100644
--- a/app/models/form/sales/questions/property_local_authority.rb
+++ b/app/models/form/sales/questions/property_local_authority.rb
@@ -6,6 +6,7 @@ class Form::Sales::Questions::PropertyLocalAuthority < ::Form::Question
@header = "What is the property’s local authority?"
@type = "select"
@question_number = 16
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def answer_options
diff --git a/app/models/form/sales/questions/purchase_price.rb b/app/models/form/sales/questions/purchase_price.rb
index f88938777..62e42f27b 100644
--- a/app/models/form/sales/questions/purchase_price.rb
+++ b/app/models/form/sales/questions/purchase_price.rb
@@ -9,17 +9,23 @@ class Form::Sales::Questions::PurchasePrice < ::Form::Question
@step = 0.01
@width = 5
@prefix = "£"
- @hint_text = "For all schemes, including Right to Acquire (RTA), Right to Buy (RTB), Voluntary Right to Buy (VRTB) or Preserved Right to Buy (PRTB) sales, enter the full price of the property without any discount"
+ @hint_text = hint_text
@ownership_sch = ownershipsch
@question_number = question_number
end
def question_number
case @ownership_sch
- when 2
+ when 2 # discounted ownership scheme
100
- when 3
+ when 3 # outright sale
110
end
end
+
+ def hint_text
+ return if @ownership_sch == 3 # outright sale
+
+ "For all schemes, including Right to Acquire (RTA), Right to Buy (RTB), Voluntary Right to Buy (VRTB) or Preserved Right to Buy (PRTB) sales, enter the full price of the property without any discount"
+ end
end
diff --git a/app/models/form/sales/questions/town_or_city.rb b/app/models/form/sales/questions/town_or_city.rb
index 9dde3aeb8..25acfe036 100644
--- a/app/models/form/sales/questions/town_or_city.rb
+++ b/app/models/form/sales/questions/town_or_city.rb
@@ -5,6 +5,7 @@ class Form::Sales::Questions::TownOrCity < ::Form::Question
@header = "Town or city"
@type = "text"
@plain_label = true
+ @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def hidden_in_check_answers?(_log = nil, _current_user = nil)
diff --git a/app/models/form/sales/questions/uprn_known.rb b/app/models/form/sales/questions/uprn_known.rb
index 22438e662..09f3c54c9 100644
--- a/app/models/form/sales/questions/uprn_known.rb
+++ b/app/models/form/sales/questions/uprn_known.rb
@@ -15,7 +15,12 @@ class Form::Sales::Questions::UprnKnown < ::Form::Question
"value" => "Not known",
},
]
- @hidden_in_check_answers = true
+ @hidden_in_check_answers = {
+ "depends_on" => [
+ { "uprn_known" => 0 },
+ { "uprn_known" => 1 },
+ ],
+ }
end
ANSWER_OPTIONS = {
diff --git a/app/models/form/sales/subsections/property_information.rb b/app/models/form/sales/subsections/property_information.rb
index 74a064dca..9efac327f 100644
--- a/app/models/form/sales/subsections/property_information.rb
+++ b/app/models/form/sales/subsections/property_information.rb
@@ -8,13 +8,13 @@ class Form::Sales::Subsections::PropertyInformation < ::Form::Subsection
def pages
@pages ||= [
- uprn_questions,
Form::Sales::Pages::PropertyNumberOfBedrooms.new(nil, nil, self),
Form::Sales::Pages::AboutPriceValueCheck.new("about_price_bedrooms_value_check", nil, self),
Form::Sales::Pages::PropertyUnitType.new(nil, nil, self),
Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_property_type_value_check", nil, self),
Form::Sales::Pages::PercentageDiscountValueCheck.new("percentage_discount_proptype_value_check", nil, self),
Form::Sales::Pages::PropertyBuildingType.new(nil, nil, self),
+ uprn_questions,
postcode_and_la_questions,
Form::Sales::Pages::AboutPriceValueCheck.new("about_price_la_value_check", nil, self),
Form::Sales::Pages::PropertyWheelchairAccessible.new(nil, nil, self),
diff --git a/app/models/forms/bulk_upload_lettings/prepare_your_file.rb b/app/models/forms/bulk_upload_lettings/prepare_your_file.rb
index c30cf88ad..b5d28a7a2 100644
--- a/app/models/forms/bulk_upload_lettings/prepare_your_file.rb
+++ b/app/models/forms/bulk_upload_lettings/prepare_your_file.rb
@@ -26,7 +26,12 @@ module Forms
end
def old_template_path
- "/files/bulk-upload-lettings-template-2022-23.xlsx"
+ case year
+ when 2022
+ "/files/bulk-upload-lettings-template-2022-23.xlsx"
+ when 2023
+ "/files/bulk-upload-lettings-legacy-template-2023-24.xlsx"
+ end
end
def template_path
diff --git a/app/models/lettings_log.rb b/app/models/lettings_log.rb
index 15055d4f4..2d91e2ad9 100644
--- a/app/models/lettings_log.rb
+++ b/app/models/lettings_log.rb
@@ -55,7 +55,7 @@ class LettingsLog < Log
scope :filter_by_organisation, ->(org, _user = nil) { where(owning_organisation: org).or(where(managing_organisation: org)) }
AUTOGENERATED_FIELDS = %w[id status created_at updated_at discarded_at].freeze
- OPTIONAL_FIELDS = %w[first_time_property_let_as_social_housing tenancycode propcode chcharge].freeze
+ OPTIONAL_FIELDS = %w[rent_value_check first_time_property_let_as_social_housing tenancycode propcode chcharge].freeze
RENT_TYPE_MAPPING_LABELS = { 1 => "Social Rent", 2 => "Affordable Rent", 3 => "Intermediate Rent" }.freeze
HAS_BENEFITS_OPTIONS = [1, 6, 8, 7].freeze
NUM_OF_WEEKS_FROM_PERIOD = { 2 => 26, 3 => 13, 4 => 12, 5 => 50, 6 => 49, 7 => 48, 8 => 47, 9 => 46, 1 => 52, 10 => 53 }.freeze
@@ -435,12 +435,22 @@ class LettingsLog < Log
end
def soft_min_for_period
- soft_min = LaRentRange.find_by(start_year: collection_start_year, la:, beds: beds_for_la_rent_range, lettype:).soft_min
+ soft_min = LaRentRange.find_by(
+ start_year: collection_start_year,
+ la:,
+ beds: beds_for_la_rent_range,
+ lettype:,
+ ).soft_min
"#{soft_value_for_period(soft_min)} #{SUFFIX_FROM_PERIOD[period].presence || 'every week'}"
end
def soft_max_for_period
- soft_max = LaRentRange.find_by(start_year: collection_start_year, la:, beds: beds_for_la_rent_range, lettype:).soft_max
+ soft_max = LaRentRange.find_by(
+ start_year: collection_start_year,
+ la:,
+ beds: beds_for_la_rent_range,
+ lettype:,
+ ).soft_max
"#{soft_value_for_period(soft_max)} #{SUFFIX_FROM_PERIOD[period].presence || 'every week'}"
end
diff --git a/app/models/log.rb b/app/models/log.rb
index 33d3c4547..4ee748eda 100644
--- a/app/models/log.rb
+++ b/app/models/log.rb
@@ -48,7 +48,7 @@ class Log < ApplicationRecord
service = UprnClient.new(uprn)
service.call
- return errors.add(:uprn, service.error) if service.error.present?
+ return errors.add(:uprn, :uprn_error, message: service.error) if service.error.present?
presenter = UprnDataPresenter.new(service.result)
@@ -174,7 +174,7 @@ private
def reset_invalidated_dependent_fields!
return unless form
- form.reset_not_routed_questions(self)
+ form.reset_not_routed_questions_and_invalid_answers(self)
reset_created_by!
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
new file mode 100644
index 000000000..111d25321
--- /dev/null
+++ b/app/models/merge_request.rb
@@ -0,0 +1,11 @@
+class MergeRequest < ApplicationRecord
+ belongs_to :requesting_organisation, class_name: "Organisation"
+ has_many :merge_request_organisations
+ has_many :merging_organisations, through: :merge_request_organisations, source: :merging_organisation
+
+ STATUS = {
+ "unsubmitted" => 0,
+ "submitted" => 1,
+ }.freeze
+ enum status: STATUS
+end
diff --git a/app/models/merge_request_organisation.rb b/app/models/merge_request_organisation.rb
new file mode 100644
index 000000000..08c1d6d45
--- /dev/null
+++ b/app/models/merge_request_organisation.rb
@@ -0,0 +1,34 @@
+class MergeRequestOrganisation < ApplicationRecord
+ belongs_to :merge_request, class_name: "MergeRequest"
+ belongs_to :merging_organisation, class_name: "Organisation"
+ validates :merge_request, presence: { message: I18n.t("validations.merge_request.merge_request_id.blank") }
+ validates :merging_organisation, presence: { message: I18n.t("validations.merge_request.merging_organisation_id.blank") }
+ validate :validate_merging_organisations
+
+ scope :not_unsubmitted, -> { joins(:merge_request).where.not(merge_requests: { status: "unsubmitted" }) }
+ scope :with_merging_organisation, ->(merging_organisation) { where(merging_organisation:) }
+
+ has_paper_trail
+
+private
+
+ def validate_merging_organisations
+ if MergeRequestOrganisation.where(merge_request:, merging_organisation:).count.positive?
+ errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_part_of_another_merge"))
+ end
+
+ if MergeRequestOrganisation.not_unsubmitted.with_merging_organisation(merging_organisation).count.positive?
+ 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_merge"))
+ end
+
+ if MergeRequest.not_unsubmitted.where.not(id: merge_request_id).where(requesting_organisation: merging_organisation).count.positive?
+ 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_merge"))
+ end
+
+ 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"))
+ end
+ end
+end
diff --git a/app/models/validations/setup_validations.rb b/app/models/validations/setup_validations.rb
index bd12d3249..ff814ea35 100644
--- a/app/models/validations/setup_validations.rb
+++ b/app/models/validations/setup_validations.rb
@@ -3,7 +3,7 @@ module Validations::SetupValidations
include CollectionTimeHelper
def validate_startdate_setup(record)
- return unless record.startdate && date_valid?("startdate", record) && FeatureToggle.startdate_collection_window_validation_enabled?
+ return unless record.startdate && date_valid?("startdate", record)
unless record.startdate.between?(active_collection_start_date, current_collection_end_date)
record.errors.add :startdate, startdate_validation_error_message
diff --git a/app/models/validations/shared_validations.rb b/app/models/validations/shared_validations.rb
index 0a0c3f63b..6a32563a7 100644
--- a/app/models/validations/shared_validations.rb
+++ b/app/models/validations/shared_validations.rb
@@ -7,6 +7,7 @@ module Validations::SharedValidations
main_field_label = main_label || main_field.to_s.humanize(capitalize: false)
other_field_label = other_label || other_field.to_s.humanize(capitalize: false)
if record[main_field] == value_other && record[other_field].blank?
+ record.errors.add main_field.to_sym, I18n.t("validations.other_field_missing", main_field_label:, other_field_label:)
record.errors.add other_field.to_sym, I18n.t("validations.other_field_missing", main_field_label:, other_field_label:)
end
diff --git a/app/models/validations/soft_validations.rb b/app/models/validations/soft_validations.rb
index cfcfb643b..5c571af6a 100644
--- a/app/models/validations/soft_validations.rb
+++ b/app/models/validations/soft_validations.rb
@@ -40,7 +40,12 @@ module Validations::SoftValidations
def rent_in_soft_max_range?
return unless brent && weekly_value(brent) && startdate
- rent_range = LaRentRange.find_by(start_year: collection_start_year, la:, beds: beds_for_la_rent_range, lettype: get_lettype)
+ rent_range = LaRentRange.find_by(
+ start_year: collection_start_year,
+ la:,
+ beds: beds_for_la_rent_range,
+ lettype: get_lettype,
+ )
if beds.present? && rent_range.present? && beds > LaRentRange::MAX_BEDS
weekly_value(brent) > rent_range.soft_max
elsif rent_range.present?
diff --git a/app/services/bulk_upload/lettings/year2022/row_parser.rb b/app/services/bulk_upload/lettings/year2022/row_parser.rb
index bfb7940ed..e7f212b55 100644
--- a/app/services/bulk_upload/lettings/year2022/row_parser.rb
+++ b/app/services/bulk_upload/lettings/year2022/row_parser.rb
@@ -278,58 +278,58 @@ class BulkUpload::Lettings::Year2022::RowParser
attribute :field_134, :integer
validates :field_1, presence: { message: I18n.t("validations.not_answered", question: "letting type") },
- inclusion: { in: (1..12).to_a, message: I18n.t("validations.invalid_option", question: "letting type") }
- validates :field_4, presence: { if: proc { [2, 4, 6, 8, 10, 12].include?(field_1) } }
-
- validates :field_12, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 1 must be a number or the letter R" }
- validates :field_13, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 2 must be a number or the letter R" }, allow_blank: true
- validates :field_14, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 3 must be a number or the letter R" }, allow_blank: true
- validates :field_15, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 4 must be a number or the letter R" }, allow_blank: true
- validates :field_16, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 5 must be a number or the letter R" }, allow_blank: true
- validates :field_17, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 6 must be a number or the letter R" }, allow_blank: true
- validates :field_18, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 7 must be a number or the letter R" }, allow_blank: true
- validates :field_19, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 8 must be a number or the letter R" }, allow_blank: true
-
- validates :field_96, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (day)") }
- validates :field_97, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (month)") }
- validates :field_98, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (year)") }
-
- validates :field_98, format: { with: /\A\d{2}\z/, message: I18n.t("validations.setup.startdate.year_not_two_digits") }
-
- validate :validate_data_types
- validate :validate_nulls
- validate :validate_relevant_collection_window
- validate :validate_la_with_local_housing_referral
- validate :validate_cannot_be_la_referral_if_general_needs_and_la
- validate :validate_leaving_reason_for_renewal
- validate :validate_lettings_type_matches_bulk_upload
- validate :validate_only_one_housing_needs_type
- validate :validate_no_disabled_needs_conjunction
- validate :validate_dont_know_disabled_needs_conjunction
- validate :validate_no_and_dont_know_disabled_needs_conjunction
-
- validate :validate_owning_org_data_given
- validate :validate_owning_org_exists
- validate :validate_owning_org_owns_stock
- validate :validate_owning_org_permitted
-
- validate :validate_managing_org_data_given
- validate :validate_managing_org_exists
- validate :validate_managing_org_related
-
- validate :validate_scheme_related
- validate :validate_scheme_exists
- validate :validate_scheme_data_given
-
- validate :validate_location_related
- validate :validate_location_exists
- validate :validate_location_data_given
-
- validate :validate_created_by_exists
- validate :validate_created_by_related
- validate :validate_rent_type
-
- validate :validate_valid_radio_option
+ inclusion: { in: (1..12).to_a, message: I18n.t("validations.invalid_option", question: "letting type") }, on: :after_log
+ validates :field_4, presence: { if: proc { [2, 4, 6, 8, 10, 12].include?(field_1) } }, on: :after_log
+
+ validates :field_12, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 1 must be a number or the letter R" }, on: :after_log
+ validates :field_13, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 2 must be a number or the letter R" }, allow_blank: true, on: :after_log
+ validates :field_14, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 3 must be a number or the letter R" }, allow_blank: true, on: :after_log
+ validates :field_15, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 4 must be a number or the letter R" }, allow_blank: true, on: :after_log
+ validates :field_16, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 5 must be a number or the letter R" }, allow_blank: true, on: :after_log
+ validates :field_17, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 6 must be a number or the letter R" }, allow_blank: true, on: :after_log
+ validates :field_18, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 7 must be a number or the letter R" }, allow_blank: true, on: :after_log
+ validates :field_19, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 8 must be a number or the letter R" }, allow_blank: true, on: :after_log
+
+ validates :field_96, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (day)") }, on: :after_log
+ validates :field_97, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (month)") }, on: :after_log
+ validates :field_98, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (year)") }, on: :after_log
+
+ validates :field_98, format: { with: /\A\d{2}\z/, message: I18n.t("validations.setup.startdate.year_not_two_digits") }, on: :after_log
+
+ validate :validate_data_types, on: :after_log
+ validate :validate_nulls, on: :after_log
+ validate :validate_relevant_collection_window, on: :after_log
+ validate :validate_la_with_local_housing_referral, on: :after_log
+ validate :validate_cannot_be_la_referral_if_general_needs_and_la, on: :after_log
+ validate :validate_leaving_reason_for_renewal, on: :after_log
+ validate :validate_lettings_type_matches_bulk_upload, on: :after_log
+ validate :validate_only_one_housing_needs_type, on: :after_log
+ validate :validate_no_disabled_needs_conjunction, on: :after_log
+ validate :validate_dont_know_disabled_needs_conjunction, on: :after_log
+ validate :validate_no_and_dont_know_disabled_needs_conjunction, on: :after_log
+
+ validate :validate_owning_org_data_given, on: :after_log
+ validate :validate_owning_org_exists, on: :after_log
+ validate :validate_owning_org_owns_stock, on: :after_log
+ validate :validate_owning_org_permitted, on: :after_log
+
+ validate :validate_managing_org_data_given, on: :after_log
+ validate :validate_managing_org_exists, on: :after_log
+ validate :validate_managing_org_related, on: :after_log
+
+ validate :validate_scheme_related, on: :after_log
+ validate :validate_scheme_exists, on: :after_log
+ validate :validate_scheme_data_given, on: :after_log
+
+ validate :validate_location_related, on: :after_log
+ validate :validate_location_exists, on: :after_log
+ validate :validate_location_data_given, on: :after_log
+
+ validate :validate_created_by_exists, on: :after_log
+ validate :validate_created_by_related, on: :after_log
+ validate :validate_rent_type, on: :after_log
+
+ validate :validate_valid_radio_option, on: :before_log
def self.question_for_field(field)
QUESTIONS[field]
@@ -340,9 +340,13 @@ class BulkUpload::Lettings::Year2022::RowParser
return true if blank_row?
+ super(:before_log)
+ before_errors = errors.dup
+
log.valid?
- super
+ super(:after_log)
+ errors.merge!(before_errors)
log.errors.each do |error|
fields = field_mapping_for_errors[error.attribute] || []
@@ -1089,6 +1093,8 @@ private
def voiddate
Date.new(field_91 + 2000, field_90, field_89) if field_91.present? && field_90.present? && field_89.present?
+ rescue Date::Error
+ Date.new
end
def majorrepairs
@@ -1097,6 +1103,8 @@ private
def mrcdate
Date.new(field_94 + 2000, field_93, field_92) if field_94.present? && field_93.present? && field_92.present?
+ rescue Date::Error
+ Date.new
end
def prevloc
diff --git a/app/services/bulk_upload/lettings/year2023/row_parser.rb b/app/services/bulk_upload/lettings/year2023/row_parser.rb
index 43b229aa3..84f30cc7b 100644
--- a/app/services/bulk_upload/lettings/year2023/row_parser.rb
+++ b/app/services/bulk_upload/lettings/year2023/row_parser.rb
@@ -280,59 +280,59 @@ class BulkUpload::Lettings::Year2023::RowParser
attribute :field_134, :decimal
validates :field_5, presence: { message: I18n.t("validations.not_answered", question: "letting type") },
- inclusion: { in: (1..12).to_a, message: I18n.t("validations.invalid_option", question: "letting type") }
- validates :field_16, presence: { if: proc { [2, 4, 6, 8, 10, 12].include?(field_5) } }
-
- validates :field_46, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 1 must be a number or the letter R" }
- validates :field_52, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 2 must be a number or the letter R" }, allow_blank: true
- validates :field_56, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 3 must be a number or the letter R" }, allow_blank: true
- validates :field_60, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 4 must be a number or the letter R" }, allow_blank: true
- validates :field_64, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 5 must be a number or the letter R" }, allow_blank: true
- validates :field_68, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 6 must be a number or the letter R" }, allow_blank: true
- validates :field_72, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 7 must be a number or the letter R" }, allow_blank: true
- validates :field_76, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 8 must be a number or the letter R" }, allow_blank: true
-
- validates :field_6, presence: { message: I18n.t("validations.not_answered", question: "property renewal") }
- validates :field_7, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (day)") }
- validates :field_8, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (month)") }
- validates :field_9, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (year)") }
-
- validates :field_9, format: { with: /\A\d{2}\z/, message: I18n.t("validations.setup.startdate.year_not_two_digits") }
-
- validate :validate_needs_type_present
- validate :validate_data_types
- validate :validate_nulls
- validate :validate_relevant_collection_window
- validate :validate_la_with_local_housing_referral
- validate :validate_cannot_be_la_referral_if_general_needs_and_la
- validate :validate_leaving_reason_for_renewal
- validate :validate_lettings_type_matches_bulk_upload
- validate :validate_only_one_housing_needs_type
- validate :validate_no_disabled_needs_conjunction
- validate :validate_dont_know_disabled_needs_conjunction
- validate :validate_no_and_dont_know_disabled_needs_conjunction
-
- validate :validate_owning_org_data_given
- validate :validate_owning_org_exists
- validate :validate_owning_org_owns_stock
- validate :validate_owning_org_permitted
-
- validate :validate_managing_org_data_given
- validate :validate_managing_org_exists
- validate :validate_managing_org_related
-
- validate :validate_scheme_related
- validate :validate_scheme_exists
- validate :validate_scheme_data_given
-
- validate :validate_location_related
- validate :validate_location_exists
- validate :validate_location_data_given
-
- validate :validate_created_by_exists
- validate :validate_created_by_related
-
- validate :validate_valid_radio_option
+ inclusion: { in: (1..12).to_a, message: I18n.t("validations.invalid_option", question: "letting type") }, on: :after_log
+ validates :field_16, presence: { if: proc { [2, 4, 6, 8, 10, 12].include?(field_5) } }, on: :after_log
+
+ validates :field_46, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 1 must be a number or the letter R" }, on: :after_log
+ validates :field_52, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 2 must be a number or the letter R" }, allow_blank: true, on: :after_log
+ validates :field_56, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 3 must be a number or the letter R" }, allow_blank: true, on: :after_log
+ validates :field_60, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 4 must be a number or the letter R" }, allow_blank: true, on: :after_log
+ validates :field_64, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 5 must be a number or the letter R" }, allow_blank: true, on: :after_log
+ validates :field_68, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 6 must be a number or the letter R" }, allow_blank: true, on: :after_log
+ validates :field_72, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 7 must be a number or the letter R" }, allow_blank: true, on: :after_log
+ validates :field_76, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 8 must be a number or the letter R" }, allow_blank: true, on: :after_log
+
+ validates :field_6, presence: { message: I18n.t("validations.not_answered", question: "property renewal") }, on: :after_log
+ validates :field_7, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (day)") }, on: :after_log
+ validates :field_8, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (month)") }, on: :after_log
+ validates :field_9, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (year)") }, on: :after_log
+
+ validates :field_9, format: { with: /\A\d{2}\z/, message: I18n.t("validations.setup.startdate.year_not_two_digits") }, on: :after_log
+
+ validate :validate_needs_type_present, on: :after_log
+ validate :validate_data_types, on: :after_log
+ validate :validate_nulls, on: :after_log
+ validate :validate_relevant_collection_window, on: :after_log
+ validate :validate_la_with_local_housing_referral, on: :after_log
+ validate :validate_cannot_be_la_referral_if_general_needs_and_la, on: :after_log
+ validate :validate_leaving_reason_for_renewal, on: :after_log
+ validate :validate_lettings_type_matches_bulk_upload, on: :after_log
+ validate :validate_only_one_housing_needs_type, on: :after_log
+ validate :validate_no_disabled_needs_conjunction, on: :after_log
+ validate :validate_dont_know_disabled_needs_conjunction, on: :after_log
+ validate :validate_no_and_dont_know_disabled_needs_conjunction, on: :after_log
+
+ validate :validate_owning_org_data_given, on: :after_log
+ validate :validate_owning_org_exists, on: :after_log
+ validate :validate_owning_org_owns_stock, on: :after_log
+ validate :validate_owning_org_permitted, on: :after_log
+
+ validate :validate_managing_org_data_given, on: :after_log
+ validate :validate_managing_org_exists, on: :after_log
+ validate :validate_managing_org_related, on: :after_log
+
+ validate :validate_scheme_related, on: :after_log
+ validate :validate_scheme_exists, on: :after_log
+ validate :validate_scheme_data_given, on: :after_log
+
+ validate :validate_location_related, on: :after_log
+ validate :validate_location_exists, on: :after_log
+ validate :validate_location_data_given, on: :after_log
+
+ validate :validate_created_by_exists, on: :after_log
+ validate :validate_created_by_related, on: :after_log
+
+ validate :validate_valid_radio_option, on: :before_log
def self.question_for_field(field)
QUESTIONS[field]
@@ -343,9 +343,13 @@ class BulkUpload::Lettings::Year2023::RowParser
return true if blank_row?
+ super(:before_log)
+ before_errors = errors.dup
+
log.valid?
- super
+ super(:after_log)
+ errors.merge!(before_errors)
log.errors.each do |error|
fields = field_mapping_for_errors[error.attribute] || []
@@ -1313,10 +1317,14 @@ private
def mrcdate
Date.new(field_38 + 2000, field_37, field_36) if field_38.present? && field_37.present? && field_36.present?
+ rescue Date::Error
+ Date.new
end
def voiddate
Date.new(field_35 + 2000, field_34, field_33) if field_35.present? && field_34.present? && field_33.present?
+ rescue Date::Error
+ Date.new
end
def first_time_property_let_as_social_housing
diff --git a/config/initializers/feature_toggle.rb b/app/services/feature_toggle.rb
similarity index 89%
rename from config/initializers/feature_toggle.rb
rename to app/services/feature_toggle.rb
index 9986af543..a25d99b84 100644
--- a/config/initializers/feature_toggle.rb
+++ b/app/services/feature_toggle.rb
@@ -4,10 +4,6 @@ class FeatureToggle
Rails.env.production? || Rails.env.test? || Rails.env.staging?
end
- def self.startdate_collection_window_validation_enabled?
- Rails.env.production? || Rails.env.test?
- end
-
def self.startdate_two_week_validation_enabled?
Rails.env.production? || Rails.env.test? || Rails.env.staging?
end
@@ -36,7 +32,11 @@ class FeatureToggle
true
end
- def self.bulk_upload_logs?
+ def self.bulk_upload_lettings_logs?
+ true
+ end
+
+ def self.bulk_upload_sales_logs?
!Rails.env.production?
end
diff --git a/app/services/imports/lettings_logs_import_service.rb b/app/services/imports/lettings_logs_import_service.rb
index 56fb0c0d2..1bc98925a 100644
--- a/app/services/imports/lettings_logs_import_service.rb
+++ b/app/services/imports/lettings_logs_import_service.rb
@@ -202,6 +202,14 @@ module Imports
attributes["first_time_property_let_as_social_housing"] = first_time_let(attributes["rsnvac"])
attributes["declaration"] = declaration(xml_doc)
+ attributes["uprn"] = string_or_nil(xml_doc, "UPRN")
+ attributes["uprn_known"] = attributes["uprn"].present? ? 1 : 0
+ attributes["uprn_confirmed"] = attributes["uprn"].present? ? 1 : 0
+ attributes["address_line1"] = string_or_nil(xml_doc, "AddressLine1")
+ attributes["address_line2"] = string_or_nil(xml_doc, "AddressLine2")
+ attributes["town_or_city"] = string_or_nil(xml_doc, "TownCity")
+ attributes["county"] = string_or_nil(xml_doc, "County")
+
set_partial_charges_to_zero(attributes)
# Supported Housing fields
@@ -211,15 +219,19 @@ module Imports
schemes = Scheme.where(old_visible_id: scheme_old_visible_id, owning_organisation_id: attributes["owning_organisation_id"])
location = Location.find_by(old_visible_id: location_old_visible_id, scheme: schemes)
- raise "No matching location for scheme #{scheme_old_visible_id} and location #{location_old_visible_id} (visible IDs)" if location.nil?
+ if location.nil? && [location_old_visible_id, scheme_old_visible_id].all?(&:present?) && previous_status != "saved"
+ raise "No matching location for scheme #{scheme_old_visible_id} and location #{location_old_visible_id} (visible IDs)"
+ end
- # Set the scheme via location, because the scheme old visible ID can be duplicated at import
- attributes["location_id"] = location.id
- attributes["scheme_id"] = location.scheme.id
- attributes["sheltered"] = unsafe_string_as_integer(xml_doc, "Q1e")
- attributes["chcharge"] = safe_string_as_decimal(xml_doc, "Q18b")
- attributes["household_charge"] = household_charge(xml_doc)
- attributes["is_carehome"] = is_carehome(location.scheme)
+ if location.present?
+ # Set the scheme via location, because the scheme old visible ID can be duplicated at import
+ attributes["location_id"] = location.id
+ attributes["scheme_id"] = location.scheme.id
+ attributes["sheltered"] = unsafe_string_as_integer(xml_doc, "Q1e")
+ attributes["chcharge"] = safe_string_as_decimal(xml_doc, "Q18b")
+ attributes["household_charge"] = household_charge(xml_doc)
+ attributes["is_carehome"] = is_carehome(location.scheme)
+ end
end
# Handles confidential schemes
@@ -333,6 +345,13 @@ module Imports
return save_lettings_log(attributes, previous_status)
end
+ if lettings_log.errors.of_kind?(:uprn, :uprn_error)
+ @logger.warn("Log #{lettings_log.old_id}: Setting uprn_known to no with error: #{lettings_log.errors[:uprn].join(', ')}")
+ @logs_overridden << lettings_log.old_id
+ attributes["uprn_known"] = 0
+ return save_lettings_log(attributes, previous_status)
+ end
+
@logger.error("Log #{lettings_log.old_id}: Failed to import")
lettings_log.errors.each do |error|
@logger.error("Validation error: Field #{error.attribute}:")
@@ -360,7 +379,7 @@ module Imports
end
def fields_not_present_in_softwire_data
- %w[majorrepairs illness_type_0 tshortfall_known pregnancy_value_check retirement_value_check rent_value_check net_income_value_check major_repairs_date_value_check void_date_value_check carehome_charges_value_check housingneeds_type housingneeds_other created_by]
+ %w[majorrepairs illness_type_0 tshortfall_known pregnancy_value_check retirement_value_check rent_value_check net_income_value_check major_repairs_date_value_check void_date_value_check carehome_charges_value_check housingneeds_type housingneeds_other created_by uprn_known uprn_confirmed]
end
def check_status_completed(lettings_log, previous_status)
diff --git a/app/services/imports/sales_logs_import_service.rb b/app/services/imports/sales_logs_import_service.rb
index 2d6551189..8702a8846 100644
--- a/app/services/imports/sales_logs_import_service.rb
+++ b/app/services/imports/sales_logs_import_service.rb
@@ -126,10 +126,6 @@ module Imports
attributes["postcode_full"] = parse_postcode(string_or_nil(xml_doc, "Q14Postcode"))
attributes["pcodenk"] = 0 if attributes["postcode_full"].present? # known if given
attributes["soctenant"] = 0 if attributes["ownershipsch"] == 1
- attributes["ethnic_group2"] = nil # 23/24 variable
- attributes["ethnicbuy2"] = nil # 23/24 variable
- attributes["prevshared"] = nil # 23/24 variable
- attributes["staircasesale"] = nil # 23/24 variable
attributes["previous_la_known"] = 1 if attributes["prevloc"].present?
if attributes["la"].present?
@@ -158,6 +154,27 @@ module Imports
attributes["buyer_livein_value_check"] = 0
attributes["percentage_discount_value_check"] = 0
+ # 2023/34 attributes
+ attributes["uprn"] = string_or_nil(xml_doc, "UPRN")
+ attributes["uprn_known"] = attributes["uprn"].present? ? 1 : 0
+ attributes["uprn_confirmed"] = attributes["uprn"].present? ? 1 : 0
+ attributes["address_line1"] = string_or_nil(xml_doc, "AddressLine1")
+ attributes["address_line2"] = string_or_nil(xml_doc, "AddressLine2")
+ attributes["town_or_city"] = string_or_nil(xml_doc, "TownCity")
+ attributes["county"] = string_or_nil(xml_doc, "County")
+
+ attributes["proplen_asked"] = 0 if attributes["proplen"]&.positive?
+ attributes["proplen_asked"] = 1 if attributes["proplen"]&.zero?
+
+ attributes["prevshared"] = unsafe_string_as_integer(xml_doc, "PREVSHARED")
+ attributes["ethnicbuy2"] = unsafe_string_as_integer(xml_doc, "P2Eth")
+ attributes["ethnic_group2"] = ethnic_group(attributes["ethnicbuy2"])
+ attributes["nationalbuy2"] = unsafe_string_as_integer(xml_doc, "P2Nat")
+ attributes["buy2living"] = unsafe_string_as_integer(xml_doc, "BUY2LIVEIN")
+
+ attributes["staircasesale"] = unsafe_string_as_integer(xml_doc, "STAIRCASESALE")
+ attributes["prevtenbuy2"] = unsafe_string_as_integer(xml_doc, "PREVTENBUY2")
+
# Sets the log creator
owner_id = meta_field_value(xml_doc, "owner-user-id").strip
if owner_id.present?
@@ -233,6 +250,11 @@ module Imports
@logs_overridden << sales_log.old_id
attributes.delete("mortgage")
save_sales_log(attributes, previous_status)
+ elsif sales_log.errors.of_kind?(:uprn, :uprn_error)
+ @logger.warn("Log #{sales_log.old_id}: Setting uprn_known to no with error: #{sales_log.errors[:uprn].join(', ')}")
+ @logs_overridden << sales_log.old_id
+ attributes["uprn_known"] = 0
+ save_sales_log(attributes, previous_status)
else
@logger.error("Log #{sales_log.old_id}: Failed to import")
sales_log.errors.each do |error|
@@ -282,7 +304,9 @@ module Imports
student_not_child_value_check
discounted_sale_value_check
buyer_livein_value_check
- percentage_discount_value_check]
+ percentage_discount_value_check
+ uprn_known
+ uprn_confirmed]
end
def check_status_completed(sales_log, previous_status)
@@ -461,12 +485,14 @@ module Imports
safe_string_as_decimal(xml_doc, "Q29MonthlyCharges")
when 2
safe_string_as_decimal(xml_doc, "Q37MonthlyCharges")
+ when 3
+ safe_string_as_decimal(xml_doc, "Q44MonthlyCharges")
end
end
def ownership_from_type(attributes)
case attributes["type"]
- when 2, 24, 18, 16, 28, 31, 30
+ when 2, 24, 18, 16, 28, 31, 30, 32
1 # shared ownership
when 8, 14, 27, 9, 29, 21, 22
2 # discounted ownership
@@ -562,6 +588,9 @@ module Imports
attributes["relat2"] ||= "R"
attributes["inc2mort"] ||= 3
attributes["buy2livein"] ||= 1 unless attributes["ownershipsch"] == 3
+ attributes["ethnic_group2"] ||= 17
+ attributes["ethnicbuy2"] ||= 17
+ attributes["nationalbuy2"] ||= 13
end
# other household members characteristics
diff --git a/app/services/imports/scheme_location_import_service.rb b/app/services/imports/scheme_location_import_service.rb
index a26139aec..93093e6da 100644
--- a/app/services/imports/scheme_location_import_service.rb
+++ b/app/services/imports/scheme_location_import_service.rb
@@ -77,7 +77,7 @@ module Imports
attributes["scheme_type"] = safe_string_as_integer(xml_doc, "scheme-type")
registered_under_care_act = safe_string_as_integer(xml_doc, "reg-home-type")
attributes["registered_under_care_act"] = registered_under_care_act&.zero? ? nil : registered_under_care_act
- attributes["support_type"] = safe_string_as_integer(xml_doc, "support-type")
+ attributes["support_type"] = support_type(xml_doc)
attributes["intended_stay"] = string_or_nil(xml_doc, "intended-stay")
attributes["mobility_type"] = string_or_nil(xml_doc, "mobility-type")
attributes["primary_client_group"] = string_or_nil(xml_doc, "client-group-1")
@@ -205,5 +205,12 @@ module Imports
date = string_or_nil(xml_doc, attribute)
Time.zone.parse(date) if date
end
+
+ def support_type(xml_doc)
+ type = safe_string_as_integer(xml_doc, "support-type")
+ return unless type
+
+ Scheme::SUPPORT_TYPE.value?(type) ? type : 0
+ end
end
end
diff --git a/app/views/bulk_upload_lettings_results/summary.html.erb b/app/views/bulk_upload_lettings_results/summary.html.erb
index 533e2af05..51aa652f8 100644
--- a/app/views/bulk_upload_lettings_results/summary.html.erb
+++ b/app/views/bulk_upload_lettings_results/summary.html.erb
@@ -20,7 +20,7 @@
<% end %>
<% c.with_tab(label: "Full error report") do %>
- <% @bulk_upload.bulk_upload_errors.group_by(&:row).each do |_row, errors_for_row| %>
+ <% @bulk_upload.bulk_upload_errors.order_by_cell.group_by(&:row).each do |_row, errors_for_row| %>
<%= render BulkUploadErrorRowComponent.new(bulk_upload_errors: errors_for_row) %>
<% end %>
<% end %>
diff --git a/app/views/form/_checkbox_question.html.erb b/app/views/form/_checkbox_question.html.erb
index c855e2d88..2a9c33941 100644
--- a/app/views/form/_checkbox_question.html.erb
+++ b/app/views/form/_checkbox_question.html.erb
@@ -6,14 +6,14 @@
hint: { text: question.hint_text&.html_safe } do %>
<% after_divider = false %>
- <% question.displayed_answer_options(@log).map do |key, options| %>
+ <% question.displayed_answer_options(@log).map do |key, option| %>
<% if key.starts_with?("divider") %>
- <% after_divider = true %>
- <%= f.govuk_check_box_divider %>
+ <% after_divider = true %>
+ <%= f.govuk_check_box_divider %>
<% else %>
<%= f.govuk_check_box question.id, key,
- label: { text: options["value"] },
- hint: { text: options["hint"] },
+ label: { text: option["value"] },
+ hint: { text: option["hint"] },
checked: @log[key] == 1,
exclusive: after_divider,
**stimulus_html_attributes(question) %>
diff --git a/app/views/layouts/_collection_resources.html.erb b/app/views/layouts/_collection_resources.html.erb
index 37e9c4b94..f583bd813 100644
--- a/app/views/layouts/_collection_resources.html.erb
+++ b/app/views/layouts/_collection_resources.html.erb
@@ -9,6 +9,21 @@
href: download_23_24_lettings_form_path,
metadata: "PDF, 278 KB, 8 pages",
},
+ {
+ name: "Lettings bulk upload template (2023/24) *new question ordering*",
+ href: download_23_24_lettings_bulk_upload_template_path,
+ metadata: "Microsoft Excel, 15 KB",
+ },
+ {
+ name: "Lettings bulk upload template (2023/24)",
+ href: download_23_24_lettings_bulk_upload_legacy_template_path,
+ metadata: "Microsoft Excel, 15 KB",
+ },
+ {
+ name: "Lettings bulk upload specification (2023/24)",
+ href: download_23_24_lettings_bulk_upload_specification_path,
+ metadata: "Microsoft Excel, 90 KB",
+ },
]) %>
Add all organisations to be merged - we have already added your own.
+ +<%= form_with model: @merge_request, url: organisations_merge_request_path, method: :patch do |f| %> + <%= f.govuk_error_summary %> +Start typing to search
+ <%= render partial: "organisation_relationships/related_organisation_select_question", locals: { + field: :merging_organisation, + question: Form::Question.new("", { "answer_options" => @answer_options }, nil), + f:, + } %> + <%= f.govuk_submit "Add organisation", classes: "govuk-button--secondary" %> + <%= govuk_table do |table| %> + <% @merge_request.merging_organisations.each do |merging_organisation| %> + <%= table.body do |body| %> + <%= body.row do |row| %> + <% row.cell(text: merging_organisation.name) %> + <% row.cell(html_attributes: { + scope: "row", + class: "govuk-!-text-align-right", + }) do %> + <% if @merge_request.requesting_organisation != merging_organisation %> + <%= govuk_link_to("Remove", organisations_remove_merge_request_path(merge_request: { merging_organisation: merging_organisation.id })) %> + <% end %> + <% end %> + <% end %> + <% end %> + <% end %> + <% end %> +<% end %> +<%= form_with model: @merge_request, url: merge_request_path(id: @merge_request.id), method: :patch do |f| %> + <%= govuk_details(summary_text: "I cannot find an organisation on the list") do %> + <%= f.govuk_text_area :other_merging_organisations, label: { text: "Other organisations" }, hint: { text: "List other organisations that are part of the merge but not registered on CORE." }, rows: 9 %> + <% end %> + <% if @merge_request.merging_organisations.count > 1 %> + <%= f.govuk_submit "Continue" %> + <% end %> +<% end %> +Is your organisation merging with another? <%= govuk_link_to "Let us know using this form", merge_organisation_path %>
+Is your organisation merging with another? <%= govuk_link_to "Let us know using this form", merge_request_organisation_path %>
<% end %> diff --git a/config/forms/2021_2022.json b/config/forms/2021_2022.json index cf8ac4cd9..84065e019 100644 --- a/config/forms/2021_2022.json +++ b/config/forms/2021_2022.json @@ -24,6 +24,7 @@ "header": "Do you know the property’s postcode?", "hint_text": "", "type": "radio", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "answer_options": { "1": { "value": "Yes" @@ -54,6 +55,7 @@ "hint_text": "", "type": "text", "width": 5, + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "inferred_answers": { "la": { "is_la_inferred": true @@ -82,6 +84,7 @@ "header": "What is the local authority of the property?", "hint_text": "", "type": "select", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "answer_options": { "": "Select an option", "E07000223": "Adur", @@ -6482,6 +6485,7 @@ "header": "Do you know the postcode of the household’s last settled accommodation?", "hint_text": "This is also known as the household’s ‘last settled home’.", "type": "radio", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "answer_options": { "1": { "value": "Yes" @@ -6512,6 +6516,7 @@ "hint_text": "", "type": "text", "width": 5, + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "inferred_answers": { "prevloc": { "is_previous_la_inferred": true @@ -6535,6 +6540,7 @@ "header": "Do you know the local authority of the household’s last settled accommodation?", "hint_text": "This is also known as the household’s ‘last settled home’.", "type": "radio", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "hidden_in_check_answers": { "depends_on": [ { @@ -6564,6 +6570,7 @@ "header": "Select a local authority", "hint_text": "Select ‘Northern Ireland’, ‘Scotland’, ‘Wales’ or ‘Outside the UK’ if the household’s last settled home was outside England.", "type": "select", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "answer_options": { "": "Select an option", "S12000033": "Aberdeen City", diff --git a/config/forms/2022_2023.json b/config/forms/2022_2023.json index 181d8a19c..c3b4e3341 100644 --- a/config/forms/2022_2023.json +++ b/config/forms/2022_2023.json @@ -24,6 +24,7 @@ "header": "Do you know the property’s postcode?", "hint_text": "", "type": "radio", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "answer_options": { "1": { "value": "Yes" @@ -54,6 +55,7 @@ "hint_text": "", "type": "text", "width": 5, + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "inferred_answers": { "la": { "is_la_inferred": true @@ -82,6 +84,7 @@ "header": "What is the local authority of the property?", "hint_text": "", "type": "select", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "answer_options": { "": "Select an option", "E07000223": "Adur", @@ -6435,6 +6438,7 @@ "header": "Do you know the postcode of the household’s last settled accommodation?", "hint_text": "This is also known as the household’s ‘last settled home’.", "type": "radio", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "answer_options": { "1": { "value": "Yes" @@ -6465,6 +6469,7 @@ "hint_text": "", "type": "text", "width": 5, + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "inferred_answers": { "prevloc": { "is_previous_la_inferred": true @@ -6488,6 +6493,7 @@ "header": "Do you know the local authority of the household’s last settled accommodation?", "hint_text": "This is also known as the household’s ‘last settled home’.", "type": "radio", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "hidden_in_check_answers": { "depends_on": [ { @@ -6517,6 +6523,7 @@ "header": "Select a local authority", "hint_text": "Select ‘Northern Ireland’, ‘Scotland’, ‘Wales’ or ‘Outside the UK’ if the household’s last settled home was outside England.", "type": "select", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "answer_options": { "": "Select an option", "S12000033": "Aberdeen City", diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb index 71d184ad6..928c8f671 100644 --- a/config/initializers/filter_parameter_logging.rb +++ b/config/initializers/filter_parameter_logging.rb @@ -2,5 +2,5 @@ # Configure sensitive parameters which will be filtered from the log file. Rails.application.config.filter_parameters += %i[ - passw secret token crypt salt certificate otp ssn + passw secret token crypt salt certificate otp ssn reset_password_token ] diff --git a/config/locales/en.yml b/config/locales/en.yml index 6d6a3558f..fd0a8296a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -369,7 +369,7 @@ en: over_20: "The lead tenant must be under 20 as you told us their housing situation immediately before this letting was a children’s home or foster care" ecstat: retired_over_70: "Person %{person_num} must be retired if over 70" - child_under_16: "Person’s %{person_num} working situation must be ’child under 16‘ as you told us they’re under 16" + child_under_16: "Person %{person_num}’s working situation must be ‘child under 16’ as you told us they’re under 16" child_over_16: "Answer cannot be ‘child under 16’ as you told us the person %{person_num} is older than 16" not_student_16_19: "Person’s %{person_num} working situation must be full-time student or prefers not to say as you told us they’re between 16 and 19." student_16_19: @@ -508,6 +508,9 @@ en: discounted_ownership_value: "Mortgage, deposit, and grant total must equal %{value_with_discount}" monthly_rent: higher_than_expected: "Basic monthly rent must be between £0.00 and £9,999.00" + merge_request: + organisation_part_of_another_merge: "This organisation is part of another merge - select a different one" + organisation_not_selected: "Select an organisation from the search list" soft_validations: net_income: diff --git a/config/routes.rb b/config/routes.rb index f6e00083b..e9542ebec 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -35,9 +35,17 @@ Rails.application.routes.draw do get "/accessibility-statement", to: "content#accessibility_statement" get "/privacy-notice", to: "content#privacy_notice" get "/data-sharing-agreement", to: "content#data_sharing_agreement" + + get "/download-23-24-lettings-form", to: "start#download_23_24_lettings_form" + get "/download-23-24-lettings-bulk-upload-template", to: "start#download_23_24_lettings_bulk_upload_template" + get "/download-23-24-lettings-bulk-upload-legacy-template", to: "start#download_23_24_lettings_bulk_upload_legacy_template" + get "/download-23-24-lettings-bulk-upload-specification", to: "start#download_23_24_lettings_bulk_upload_specification" + + get "/download-22-23-lettings-bulk-upload-template", to: "start#download_22_23_lettings_bulk_upload_template" + get "/download-22-23-lettings-bulk-upload-specification", to: "start#download_22_23_lettings_bulk_upload_specification" + get "/download-23-24-sales-form", to: "start#download_23_24_sales_form" get "/download-22-23-sales-form", to: "start#download_22_23_sales_form" - get "/download-23-24-lettings-form", to: "start#download_23_24_lettings_form" resource :account, only: %i[show edit], controller: "users" do get "edit/password", to: "users#edit_password" @@ -119,7 +127,16 @@ Rails.application.routes.draw do get "managing-agents/remove", to: "organisation_relationships#remove_managing_agent" post "managing-agents", to: "organisation_relationships#create_managing_agent" delete "managing-agents", to: "organisation_relationships#delete_managing_agent" - get "merge", to: "organisations#merge" + get "merge-request", to: "organisations#merge_request" + end + end + + resources :merge_requests, path: "/merge-request" do + member do + get "organisations" + patch "organisations", to: "merge_requests#update_organisations" + get "organisations/remove", to: "merge_requests#remove_merging_organisation" + get "absorbing-organisation" end end diff --git a/db/migrate/20230412111338_add_merge_requests_table.rb b/db/migrate/20230412111338_add_merge_requests_table.rb new file mode 100644 index 000000000..2aaeb2989 --- /dev/null +++ b/db/migrate/20230412111338_add_merge_requests_table.rb @@ -0,0 +1,9 @@ +class AddMergeRequestsTable < ActiveRecord::Migration[7.0] + def change + create_table :merge_requests do |t| + t.integer :requesting_organisation_id + t.text :other_merging_organisations + t.timestamps + end + end +end diff --git a/db/migrate/20230413135407_add_merge_organisations.rb b/db/migrate/20230413135407_add_merge_organisations.rb new file mode 100644 index 000000000..97178f973 --- /dev/null +++ b/db/migrate/20230413135407_add_merge_organisations.rb @@ -0,0 +1,9 @@ +class AddMergeOrganisations < ActiveRecord::Migration[7.0] + def change + create_table :merge_request_organisations do |t| + t.integer :merge_request_id, foreign_key: true + t.integer :merging_organisation_id, foreign_key: true + t.timestamps + end + end +end diff --git a/db/migrate/20230418095819_add_status_to_merge_request.rb b/db/migrate/20230418095819_add_status_to_merge_request.rb new file mode 100644 index 000000000..a6a3b01b9 --- /dev/null +++ b/db/migrate/20230418095819_add_status_to_merge_request.rb @@ -0,0 +1,5 @@ +class AddStatusToMergeRequest < ActiveRecord::Migration[7.0] + def change + add_column :merge_requests, :status, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index 9040aba7c..e87364403 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_04_12_143245) do +ActiveRecord::Schema[7.0].define(version: 2023_04_18_095819) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -354,6 +354,21 @@ ActiveRecord::Schema[7.0].define(version: 2023_04_12_143245) do t.string "collection" end + create_table "merge_request_organisations", force: :cascade do |t| + t.integer "merge_request_id" + t.integer "merging_organisation_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "merge_requests", force: :cascade do |t| + t.integer "requesting_organisation_id" + t.text "other_merging_organisations" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "status" + end + create_table "organisation_relationships", force: :cascade do |t| t.integer "child_organisation_id" t.integer "parent_organisation_id" diff --git a/public/files/bulk-upload-lettings-legacy-template-2023-24.xlsx b/public/files/bulk-upload-lettings-legacy-template-2023-24.xlsx new file mode 100644 index 000000000..22b630cd3 Binary files /dev/null and b/public/files/bulk-upload-lettings-legacy-template-2023-24.xlsx differ diff --git a/spec/factories/lettings_log.rb b/spec/factories/lettings_log.rb index 311e5fecc..58c81e8f3 100644 --- a/spec/factories/lettings_log.rb +++ b/spec/factories/lettings_log.rb @@ -158,6 +158,12 @@ FactoryBot.define do sheltered { 0 } household_charge { 0 } end + trait :sheltered_housing do + needstype { 2 } + end + trait :startdate_today do + startdate { Time.zone.today } + end created_at { Time.zone.today } updated_at { Time.zone.today } end diff --git a/spec/factories/sales_log.rb b/spec/factories/sales_log.rb index 4bea77a65..c61b0cd93 100644 --- a/spec/factories/sales_log.rb +++ b/spec/factories/sales_log.rb @@ -10,6 +10,30 @@ FactoryBot.define do type { 8 } saledate { Time.utc(2023, 2, 2, 10, 36, 49) } end + trait :shared_ownership do + ownershipsch { 1 } + type { 30 } + end + trait :privacy_notice_seen do + privacynotice { 1 } + end + trait :saledate_today do + saledate { Time.zone.today } + end + trait :shared_ownership_setup_complete do + saledate_today + ownershipsch { 1 } + type { 30 } + jointpur { 2 } + end + trait :outright_sale_setup_complete do + saledate_today + ownershipsch { 3 } + type { 10 } + companybuy { 2 } + buylivein { 1 } + jointpur { 2 } + end trait :completed do ownershipsch { 2 } type { 8 } diff --git a/spec/fixtures/files/lettings_logs_download.csv b/spec/fixtures/files/lettings_logs_download.csv index b9aefc3c9..075792363 100644 --- a/spec/fixtures/files/lettings_logs_download.csv +++ b/spec/fixtures/files/lettings_logs_download.csv @@ -1,2 +1,2 @@ -id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,needstype,renewal,startdate,rent_type_detail,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,hhmemb,relat2,age2,sex2,retirement_value_check,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,is_previous_la_inferred,prevloc_label,prevloc,illness_type_1,illness_type_2,is_la_inferred,la_label,la,postcode_known,postcode_full,previous_la_known,wchair,preg_occ,cbl,earnings,incfreq,net_income_value_check,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,renttype,lettype,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,tshortfall,chcharge,ppcodenk,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,has_benefits,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,rent_value_check,old_form_id,lar,irproduct,old_id,joint,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,unresolved,updated_by_id,uprn,uprn_known,uprn_confirmed,address_line1,address_line2,town_or_city,county,carehome_charges_value_check,status_cache,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate -{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,No,DLUHC,DLUHC,2021,Supported housing,,2 October 2021,London Affordable Rent,,,,,,,,,,,,,,,,,,,,No,,,,,No,Westminster,E09000033,,SE1 1TE,,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,8,0,0,0,,0,,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,0,,,,,,,,,,,,,,,,,,,9,1,,,,,,,,,,,,,,,,not_started,6,{scheme_code},{scheme_service_name},{scheme_sensitive},Missing,No,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,Bungalow,Fitted with equipment and adaptations,Westminster,{location_startdate} +id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,rent_value_check,needstype,renewal,startdate,rent_type_detail,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,hhmemb,relat2,age2,sex2,retirement_value_check,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,is_previous_la_inferred,prevloc_label,prevloc,illness_type_1,illness_type_2,is_la_inferred,la_label,la,postcode_known,postcode_full,previous_la_known,wchair,preg_occ,cbl,earnings,incfreq,net_income_value_check,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,renttype,lettype,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,tshortfall,chcharge,ppcodenk,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,has_benefits,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,old_form_id,lar,irproduct,old_id,joint,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,unresolved,updated_by_id,uprn,uprn_known,uprn_confirmed,address_line1,address_line2,town_or_city,county,carehome_charges_value_check,status_cache,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate +{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,No,DLUHC,DLUHC,2021,,Supported housing,,2 October 2021,London Affordable Rent,,,,,,,,,,,,,,,,,,,,No,,,,,No,Westminster,E09000033,,SE1 1TE,,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,8,0,0,0,,0,,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,0,,,,,,,,,,,,,,,,,,9,1,,,,,,,,,,,,,,,,not_started,6,{scheme_code},{scheme_service_name},{scheme_sensitive},Missing,No,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,Bungalow,Fitted with equipment and adaptations,Westminster,{location_startdate} diff --git a/spec/fixtures/files/lettings_logs_download_codes_only.csv b/spec/fixtures/files/lettings_logs_download_codes_only.csv index dec2664b5..76dde6a7b 100644 --- a/spec/fixtures/files/lettings_logs_download_codes_only.csv +++ b/spec/fixtures/files/lettings_logs_download_codes_only.csv @@ -1,2 +1,2 @@ -id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,needstype,renewal,startdate,rent_type_detail,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,hhmemb,relat2,age2,sex2,retirement_value_check,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,is_previous_la_inferred,prevloc_label,prevloc,illness_type_1,illness_type_2,is_la_inferred,la_label,la,postcode_known,postcode_full,previous_la_known,wchair,preg_occ,cbl,earnings,incfreq,net_income_value_check,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,renttype,lettype,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,tshortfall,chcharge,ppcodenk,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,has_benefits,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,rent_value_check,old_form_id,lar,irproduct,old_id,joint,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,unresolved,updated_by_id,uprn,uprn_known,uprn_confirmed,address_line1,address_line2,town_or_city,county,carehome_charges_value_check,status_cache,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate -{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,false,DLUHC,DLUHC,2021,2,,2 October 2021,2,,,,,,,,,,,,,,,,,,,,false,,,,,false,Westminster,E09000033,,SE1 1TE,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,8,0,0,0,,0,,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,0,,,,,,,,,,,,,,,,,,,9,1,,,,,,,,,,,,,,,,not_started,6,{scheme_code},{scheme_service_name},{scheme_sensitive},0,1,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,6,A,Westminster,{location_startdate} +id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,rent_value_check,needstype,renewal,startdate,rent_type_detail,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,hhmemb,relat2,age2,sex2,retirement_value_check,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,is_previous_la_inferred,prevloc_label,prevloc,illness_type_1,illness_type_2,is_la_inferred,la_label,la,postcode_known,postcode_full,previous_la_known,wchair,preg_occ,cbl,earnings,incfreq,net_income_value_check,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,renttype,lettype,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,tshortfall,chcharge,ppcodenk,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,has_benefits,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,old_form_id,lar,irproduct,old_id,joint,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,unresolved,updated_by_id,uprn,uprn_known,uprn_confirmed,address_line1,address_line2,town_or_city,county,carehome_charges_value_check,status_cache,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate +{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,false,DLUHC,DLUHC,2021,,2,,2 October 2021,2,,,,,,,,,,,,,,,,,,,,false,,,,,false,Westminster,E09000033,,SE1 1TE,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,8,0,0,0,,0,,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,0,,,,,,,,,,,,,,,,,,9,1,,,,,,,,,,,,,,,,not_started,6,{scheme_code},{scheme_service_name},{scheme_sensitive},0,1,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,6,A,Westminster,{location_startdate} diff --git a/spec/fixtures/forms/2021_2022.json b/spec/fixtures/forms/2021_2022.json index d25b575cc..139ac8399 100644 --- a/spec/fixtures/forms/2021_2022.json +++ b/spec/fixtures/forms/2021_2022.json @@ -411,6 +411,7 @@ "hint_text": "Type ahead to filter the options", "type": "select", "check_answer_label": "Accessible Select", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "answer_options": { "": "Select an option", "E07000223": "Adur", @@ -473,6 +474,7 @@ "hint_text": "Type ahead to filter the options", "type": "select", "check_answer_label": "Accessible Select", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "answer_options": { "": "Select an option", "E07000223": "Adur", @@ -500,6 +502,7 @@ "header": "Do you know the property’s postcode?", "hint_text": "", "type": "radio", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "answer_options": { "1": { "value": "Yes" @@ -521,6 +524,7 @@ "hint_text": "", "type": "text", "width": 5, + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "inferred_answers": { "la": { "is_la_inferred": true @@ -544,6 +548,7 @@ "header": "Do you know what local authority the property is located in?", "hint_text": "", "type": "radio", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "answer_options": { "0": { "value": "No" @@ -1153,6 +1158,7 @@ "header": "Postcode for the previous accommodation", "hint_text": "If the household has moved from settled accommodation immediately prior to being re-housed", "type": "text", + "disable_clearing_if_not_routed_or_dynamic_answer_options": true, "width": 5 } } diff --git a/spec/fixtures/imports/logs/00d2343e-d5fa-4c89-8400-ec3854b0f2b4.xml b/spec/fixtures/imports/logs/00d2343e-d5fa-4c89-8400-ec3854b0f2b4.xml index 8bc6a935c..cd0e03d74 100644 --- a/spec/fixtures/imports/logs/00d2343e-d5fa-4c89-8400-ec3854b0f2b4.xml +++ b/spec/fixtures/imports/logs/00d2343e-d5fa-4c89-8400-ec3854b0f2b4.xml @@ -131,6 +131,11 @@ <_2cYears/>