Browse Source

Merge branch 'main' into CLDC-2831-page-load

CLDC-2831-page-load
natdeanlewissoftwire 8 months ago
parent
commit
36800aae51
  1. 6
      .github/workflows/aws_deploy.yml
  2. 200
      .github/workflows/production_pipeline.yml
  3. 2
      app/components/check_answers_summary_list_card_component.html.erb
  4. 5
      app/components/check_answers_summary_list_card_component.rb
  5. 40
      app/controllers/form_controller.rb
  6. 10
      app/controllers/organisation_relationships_controller.rb
  7. 33
      app/controllers/organisations_controller.rb
  8. 3
      app/controllers/users_controller.rb
  9. 2
      app/helpers/form_page_helper.rb
  10. 13
      app/helpers/toggle_active_organisation_helper.rb
  11. 2
      app/jobs/create_addresses_csv_job.rb
  12. 2
      app/jobs/data_export_xml_job.rb
  13. 2
      app/jobs/email_csv_job.rb
  14. 2
      app/jobs/email_missing_addresses_csv_job.rb
  15. 2
      app/jobs/scheme_email_csv_job.rb
  16. 11
      app/models/form.rb
  17. 4
      app/models/form/lettings/pages/uprn.rb
  18. 2
      app/models/form/lettings/pages/uprn_selection.rb
  19. 2
      app/models/form/lettings/questions/ethnic_arab.rb
  20. 2
      app/models/form/lettings/questions/ethnic_asian.rb
  21. 2
      app/models/form/lettings/questions/ethnic_black.rb
  22. 2
      app/models/form/lettings/questions/ethnic_group.rb
  23. 2
      app/models/form/lettings/questions/ethnic_mixed.rb
  24. 6
      app/models/form/lettings/questions/managing_organisation.rb
  25. 2
      app/models/form/lettings/questions/nationality_all_group.rb
  26. 1
      app/models/form/lettings/questions/period.rb
  27. 2
      app/models/form/lettings/questions/person_working_situation.rb
  28. 7
      app/models/form/lettings/questions/referral_prp.rb
  29. 10
      app/models/form/lettings/questions/referral_supported_housing.rb
  30. 4
      app/models/form/lettings/questions/stock_owner.rb
  31. 4
      app/models/form/lettings/questions/working_situation1.rb
  32. 4
      app/models/form/page.rb
  33. 10
      app/models/form/question.rb
  34. 4
      app/models/form/sales/pages/uprn.rb
  35. 2
      app/models/form/sales/pages/uprn_selection.rb
  36. 2
      app/models/form/sales/questions/buyer1_ethnic_background_arab.rb
  37. 2
      app/models/form/sales/questions/buyer1_ethnic_background_asian.rb
  38. 2
      app/models/form/sales/questions/buyer1_ethnic_background_black.rb
  39. 2
      app/models/form/sales/questions/buyer1_ethnic_background_mixed.rb
  40. 2
      app/models/form/sales/questions/buyer1_ethnic_group.rb
  41. 2
      app/models/form/sales/questions/buyer1_live_in_property.rb
  42. 4
      app/models/form/sales/questions/buyer1_working_situation.rb
  43. 2
      app/models/form/sales/questions/buyer2_working_situation.rb
  44. 6
      app/models/form/sales/questions/managing_organisation.rb
  45. 10
      app/models/form/sales/questions/nationality_all_group.rb
  46. 4
      app/models/form/sales/questions/owning_organisation_id.rb
  47. 2
      app/models/form/sales/questions/person_working_situation.rb
  48. 2
      app/models/forms/bulk_upload_lettings/upload_your_file.rb
  49. 2
      app/models/forms/bulk_upload_sales/upload_your_file.rb
  50. 22
      app/models/location.rb
  51. 3
      app/models/organisation.rb
  52. 25
      app/models/scheme.rb
  53. 17
      app/models/user.rb
  54. 4
      app/models/validations/household_validations.rb
  55. 16
      app/policies/organisation_policy.rb
  56. 2
      app/services/bulk_update_from_csv/update_locations_from_csv_service.rb
  57. 2
      app/services/bulk_update_from_csv/update_schemes_from_csv_service.rb
  58. 2
      app/services/bulk_upload/downloader.rb
  59. 2
      app/services/bulk_upload/lettings/year2023/row_parser.rb
  60. 6
      app/services/feature_toggle.rb
  61. 6
      app/views/form/_check_answers_summary_list.html.erb
  62. 2
      app/views/form/_interruption_screen_question.html.erb
  63. 1
      app/views/form/check_answers.html.erb
  64. 7
      app/views/form/review.html.erb
  65. 2
      app/views/locations/show.html.erb
  66. 18
      app/views/organisation_relationships/managing_agents.html.erb
  67. 18
      app/views/organisation_relationships/stock_owners.html.erb
  68. 9
      app/views/organisations/show.html.erb
  69. 29
      app/views/organisations/toggle_active.html.erb
  70. 2
      app/views/schemes/confirm_secondary.html.erb
  71. 2
      app/views/schemes/secondary_client_group.html.erb
  72. 2
      app/views/schemes/show.html.erb
  73. 2
      app/views/schemes/support.html.erb
  74. 2
      app/views/users/new.html.erb
  75. 4
      app/views/users/show.html.erb
  76. 7
      config/locales/en.yml
  77. 2
      config/routes.rb
  78. 5
      db/migrate/20240304112411_add_reactivate_with_organisation_to_users.rb
  79. 12
      db/migrate/20240305112507_add_default_value_to_organisation_active_field.rb
  80. 3
      db/schema.rb
  81. 2
      lib/tasks/data_export.rake
  82. 4
      lib/tasks/import_address_from_csv.rake
  83. 4
      spec/features/form/check_answers_page_lettings_logs_spec.rb
  84. 6
      spec/features/form/form_navigation_spec.rb
  85. 10
      spec/features/form/validations_spec.rb
  86. 2
      spec/fixtures/files/sales_logs_csv_export_labels_23.csv
  87. 2
      spec/fixtures/files/sales_logs_csv_export_labels_23_during_24_period.csv
  88. 2
      spec/fixtures/files/sales_logs_csv_export_labels_24.csv
  89. 2
      spec/lib/tasks/correct_address_from_csv_spec.rb
  90. 4
      spec/lib/tasks/data_export_spec.rb
  91. 2
      spec/lib/tasks/update_schemes_and_locations_from_csv_spec.rb
  92. 2
      spec/models/form/lettings/pages/uprn_selection_spec.rb
  93. 4
      spec/models/form/lettings/pages/uprn_spec.rb
  94. 2
      spec/models/form/lettings/questions/gender_identity1_spec.rb
  95. 15
      spec/models/form/lettings/questions/managing_organisation_spec.rb
  96. 2
      spec/models/form/lettings/questions/nationality_all_group_spec.rb
  97. 2
      spec/models/form/lettings/questions/person_working_situation_spec.rb
  98. 104
      spec/models/form/lettings/questions/referral_prp_spec.rb
  99. 105
      spec/models/form/lettings/questions/referral_supported_housing_prp_spec.rb
  100. 101
      spec/models/form/lettings/questions/referral_supported_housing_spec.rb
  101. Some files were not shown because too many files have changed in this diff Show More

6
.github/workflows/aws_deploy.yml

@ -59,13 +59,17 @@ jobs:
run: | run: |
echo "image-exists=$(if aws ecr list-images --repository-name=$repository --query "imageIds[*].imageTag" | grep -q ${{ github.sha }}; then echo true; else echo false; fi)" >> $GITHUB_ENV echo "image-exists=$(if aws ecr list-images --repository-name=$repository --query "imageIds[*].imageTag" | grep -q ${{ github.sha }}; then echo true; else echo false; fi)" >> $GITHUB_ENV
- name: Build, tag, and push docker image to ECR - name: Build, tag, and push docker image to ECR if there is no image, failing for releases
id: build-image id: build-image
if: ${{ env.image-exists == 'false' }} if: ${{ env.image-exists == 'false' }}
env: env:
registry: ${{ steps.ecr-login.outputs.registry }} registry: ${{ steps.ecr-login.outputs.registry }}
commit_tag: ${{ github.sha }} commit_tag: ${{ github.sha }}
run: | run: |
if [[ ${{ inputs.environment }} == 'production' ]]; then
echo "Error: Deployment to production environment is not allowed as there is no docker image (i.e. the AWS deploy on staging was unsuccessful for this commit)."
exit 1
fi
docker build -t $registry/$repository:$commit_tag . --target=production docker build -t $registry/$repository:$commit_tag . --target=production
docker push $registry/$repository:$commit_tag docker push $registry/$repository:$commit_tag

200
.github/workflows/production_pipeline.yml

@ -5,213 +5,13 @@ on:
types: [released] types: [released]
workflow_dispatch: workflow_dispatch:
env:
REPO_URL: communitiesuk/submit-social-housing-lettings-and-sales-data
defaults: defaults:
run: run:
shell: bash shell: bash
jobs: jobs:
test:
name: Test
runs-on: ubuntu-latest
outputs:
releasetag: ${{ steps.latestrelease.outputs.releasetag }}
services:
postgres:
image: postgres:13.5
env:
POSTGRES_PASSWORD: password
POSTGRES_USER: postgres
POSTGRES_DB: data_collector
ports:
- 5432:5432
# Needed because the Postgres container does not provide a health check
# tmpfs makes database faster by using RAM
options: >-
--mount type=tmpfs,destination=/var/lib/postgresql/data
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
RAILS_ENV: test
GEMFILE_RUBY_VERSION: 3.1.1
DB_HOST: localhost
DB_DATABASE: data_collector
DB_USERNAME: postgres
DB_PASSWORD: password
RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
PARALLEL_TEST_PROCESSORS: 4
steps:
- name: Get latest release with tag
id: latestrelease
run: |
echo "releasetag=$(curl -s https://api.github.com/repos/${REPO_URL}/releases/latest | jq '.tag_name' | sed 's/\"//g')" >> $GITHUB_OUTPUT
- name: Confirm release tag
run: |
echo ${{ steps.latestrelease.outputs.releasetag }}
- name: Checkout tag
uses: actions/checkout@v3
with:
ref: ${{ steps.latestrelease.outputs.releasetag }}
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Set up node
uses: actions/setup-node@v3
with:
cache: yarn
node-version: 20
- name: Create database
run: |
bundle exec rake parallel:setup
- name: Compile Assets
run: |
bundle exec rake assets:precompile
- name: Run tests
run: |
bundle exec rake parallel:spec['spec\/(?!features)']
feature_test:
name: Feature Tests
if: '!github.event.pull_request.draft'
runs-on: ubuntu-latest
services:
postgres:
image: postgres:13.5
env:
POSTGRES_PASSWORD: password
POSTGRES_USER: postgres
POSTGRES_DB: data_collector
ports:
- 5432:5432
# Needed because the Postgres container does not provide a health check
# tmpfs makes database faster by using RAM
options: >-
--mount type=tmpfs,destination=/var/lib/postgresql/data
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
RAILS_ENV: test
GEMFILE_RUBY_VERSION: 3.1.1
DB_HOST: localhost
DB_DATABASE: data_collector
DB_USERNAME: postgres
DB_PASSWORD: password
RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Set up Node.js
uses: actions/setup-node@v3
with:
cache: yarn
node-version: 20
- name: Create database
run: |
bundle exec rake db:prepare
- name: Compile assets
run: |
bundle exec rake assets:precompile
- name: Run tests
run: |
bundle exec rspec spec/features --fail-fast
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Get latest release with tag
id: latestrelease
run: |
echo "::set-output name=releasetag::$(curl -s https://api.github.com/repos/${REPO_URL}/releases/latest | jq '.tag_name' | sed 's/\"//g')"
- name: Confirm release tag
run: |
echo ${{ steps.latestrelease.outputs.releasetag }}
- name: Checkout tag
uses: actions/checkout@v3
with:
ref: ${{ steps.latestrelease.outputs.releasetag }}
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Set up Node.js
uses: actions/setup-node@v3
with:
cache: yarn
node-version: 20
- name: Install packages and symlink local dependencies
run: |
yarn install --immutable --immutable-cache --check-cache
- name: Lint
run: |
bundle exec rake lint
audit:
name: Audit dependencies
runs-on: ubuntu-latest
steps:
- name: Get latest release with tag
id: latestrelease
run: |
echo "::set-output name=releasetag::$(curl -s https://api.github.com/repos/${REPO_URL}/releases/latest | jq '.tag_name' | sed 's/\"//g')"
- name: Confirm release tag
run: |
echo ${{ steps.latestrelease.outputs.releasetag }}
- name: Checkout tag
uses: actions/checkout@v3
with:
ref: ${{ steps.latestrelease.outputs.releasetag }}
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Audit
run: |
bundle exec bundler-audit
aws_deploy: aws_deploy:
name: AWS Deploy name: AWS Deploy
needs: [lint, test, feature_test, audit]
uses: ./.github/workflows/aws_deploy.yml uses: ./.github/workflows/aws_deploy.yml
with: with:
aws_account_id: 977287343304 aws_account_id: 977287343304

2
app/components/check_answers_summary_list_card_component.html.erb

@ -36,7 +36,7 @@
<% if @log.collection_period_open_for_editing? %> <% if @log.collection_period_open_for_editing? %>
<% row.with_action( <% row.with_action(
text: question.action_text(log), text: question.action_text(log),
href: action_href(log, question.page.id), href: action_href(question, log),
visually_hidden_text: question.check_answer_label.to_s.downcase, visually_hidden_text: question.check_answer_label.to_s.downcase,
) %> ) %>
<% end %> <% end %>

5
app/components/check_answers_summary_list_card_component.rb

@ -28,8 +28,9 @@ class CheckAnswersSummaryListCardComponent < ViewComponent::Base
"Person #{question.check_answers_card_number}" "Person #{question.check_answers_card_number}"
end end
def action_href(log, page_id, referrer = "check_answers") def action_href(question, log)
send("#{log.model_name.param_key}_#{page_id}_path", log, referrer:) referrer = question.displayed_as_answered?(log) ? "check_answers" : "check_answers_new_answer"
send("#{log.model_name.param_key}_#{question.page.id}_path", log, referrer:)
end end
private private

40
app/controllers/form_controller.rb

@ -9,6 +9,11 @@ class FormController < ApplicationController
def submit_form def submit_form
if @log if @log
@page = form.get_page(params[@log.model_name.param_key][:page]) @page = form.get_page(params[@log.model_name.param_key][:page])
shown_page_ids_with_unanswered_questions_before_update = @page.subsection.pages
.select { |page| page.routed_to?(@log, current_user) }
.select { |page| page.has_unanswered_questions?(@log) }
.map(&:id)
responses_for_page = responses_for_page(@page) responses_for_page = responses_for_page(@page)
mandatory_questions_with_no_response = mandatory_questions_with_no_response(responses_for_page) mandatory_questions_with_no_response = mandatory_questions_with_no_response(responses_for_page)
@ -18,7 +23,9 @@ class FormController < ApplicationController
updated_question_string = [updated_question&.question_number_string, updated_question&.check_answer_label.to_s.downcase].compact.join(": ") updated_question_string = [updated_question&.question_number_string, updated_question&.check_answer_label.to_s.downcase].compact.join(": ")
flash[:notice] = "You have successfully updated #{updated_question_string}" flash[:notice] = "You have successfully updated #{updated_question_string}"
end end
redirect_to(successful_redirect_path)
pages_requiring_update = pages_requiring_update(shown_page_ids_with_unanswered_questions_before_update)
redirect_to(successful_redirect_path(pages_requiring_update))
else else
mandatory_questions_with_no_response.map do |question| mandatory_questions_with_no_response.map do |question|
@log.errors.add question.id.to_sym, question.unanswered_error_message, category: :not_answered @log.errors.add question.id.to_sym, question.unanswered_error_message, category: :not_answered
@ -171,7 +178,7 @@ private
params[@log.model_name.param_key]["interruption_page_referrer_type"].presence params[@log.model_name.param_key]["interruption_page_referrer_type"].presence
end end
def successful_redirect_path def successful_redirect_path(pages_to_check)
if FeatureToggle.deduplication_flow_enabled? if FeatureToggle.deduplication_flow_enabled?
if is_referrer_type?("duplicate_logs") || is_referrer_type?("duplicate_logs_banner") if is_referrer_type?("duplicate_logs") || is_referrer_type?("duplicate_logs_banner")
return correcting_duplicate_logs_redirect_path return correcting_duplicate_logs_redirect_path
@ -195,7 +202,9 @@ private
previous_page = form.previous_page_id(@page, @log, current_user) previous_page = form.previous_page_id(@page, @log, current_user)
if next_page&.interruption_screen? || next_page_id == previous_page || CONFIRMATION_PAGE_IDS.include?(next_page_id) 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" }) return redirect_path_to_question(next_page, pages_to_check)
elsif pages_to_check.any?
return redirect_path_to_question(pages_to_check[0], pages_to_check)
else else
return send("#{@log.model_name.param_key}_#{form.subsection_for_page(@page).id}_check_answers_path", @log) return send("#{@log.model_name.param_key}_#{form.subsection_for_page(@page).id}_check_answers_path", @log)
end end
@ -204,8 +213,29 @@ private
return send("#{@log.class.name.underscore}_#{previous_interruption_screen_page_id}_path", @log, { referrer: previous_interruption_screen_referrer, original_log_id: original_duplicate_log_id_from_query }.compact) return send("#{@log.class.name.underscore}_#{previous_interruption_screen_page_id}_path", @log, { referrer: previous_interruption_screen_referrer, original_log_id: original_duplicate_log_id_from_query }.compact)
end end
redirect_path = form.next_page_redirect_path(@page, @log, current_user) is_new_answer_from_check_answers = is_referrer_type?("check_answers_new_answer")
send(redirect_path, @log) redirect_path = form.next_page_redirect_path(@page, @log, current_user, ignore_answered: is_new_answer_from_check_answers)
referrer = is_new_answer_from_check_answers ? "check_answers_new_answer" : nil
send(redirect_path, @log, { referrer: })
end
def redirect_path_to_question(page_to_show, unanswered_pages)
remaining_pages = unanswered_pages.excluding(page_to_show)
remaining_page_ids = remaining_pages.any? ? remaining_pages.map(&:id).join(",") : nil
send("#{@log.class.name.underscore}_#{page_to_show.id}_path", @log, { referrer: "check_answers", unanswered_pages: remaining_page_ids })
end
def pages_requiring_update(previously_visible_empty_page_ids)
return [] unless is_referrer_type?("check_answers")
currently_shown_pages = @page.subsection.pages
.select { |page| page.routed_to?(@log, current_user) }
existing_unanswered_pages = request.params["unanswered_pages"].nil? ? [] : request.params["unanswered_pages"].split(",")
currently_shown_pages
.reject { |page| previously_visible_empty_page_ids.include?(page.id) && !existing_unanswered_pages.include?(page.id) }
.select { |page| page.has_unanswered_questions?(@log) }
end end
def form def form

10
app/controllers/organisation_relationships_controller.rb

@ -14,7 +14,7 @@ class OrganisationRelationshipsController < ApplicationController
] ]
def stock_owners def stock_owners
stock_owners = organisation.stock_owners stock_owners = organisation.stock_owners.filter_by_active
unpaginated_filtered_stock_owners = filtered_collection(stock_owners, search_term) unpaginated_filtered_stock_owners = filtered_collection(stock_owners, search_term)
@pagy, @stock_owners = pagy(unpaginated_filtered_stock_owners) @pagy, @stock_owners = pagy(unpaginated_filtered_stock_owners)
@ -23,7 +23,7 @@ class OrganisationRelationshipsController < ApplicationController
end end
def managing_agents def managing_agents
managing_agents = organisation.managing_agents managing_agents = organisation.managing_agents.filter_by_active
unpaginated_filtered_managing_agents = filtered_collection(managing_agents, search_term) unpaginated_filtered_managing_agents = filtered_collection(managing_agents, search_term)
@pagy, @managing_agents = pagy(unpaginated_filtered_managing_agents) @pagy, @managing_agents = pagy(unpaginated_filtered_managing_agents)
@ -48,7 +48,7 @@ class OrganisationRelationshipsController < ApplicationController
flash[:notice] = "#{@organisation_relationship.parent_organisation.name} is now one of #{current_user.data_coordinator? ? 'your' : "this organisation's"} stock owners" flash[:notice] = "#{@organisation_relationship.parent_organisation.name} is now one of #{current_user.data_coordinator? ? 'your' : "this organisation's"} stock owners"
redirect_to stock_owners_organisation_path redirect_to stock_owners_organisation_path
else else
@organisations = Organisation.where.not(id: organisation.id).pluck(:id, :name) @organisations = Organisation.filter_by_active.where.not(id: organisation.id).pluck(:id, :name)
render "organisation_relationships/add_stock_owner", status: :unprocessable_entity render "organisation_relationships/add_stock_owner", status: :unprocessable_entity
end end
end end
@ -60,7 +60,7 @@ class OrganisationRelationshipsController < ApplicationController
flash[:notice] = "#{@organisation_relationship.child_organisation.name} is now one of #{current_user.data_coordinator? ? 'your' : "this organisation's"} managing agents" flash[:notice] = "#{@organisation_relationship.child_organisation.name} is now one of #{current_user.data_coordinator? ? 'your' : "this organisation's"} managing agents"
redirect_to managing_agents_organisation_path redirect_to managing_agents_organisation_path
else else
@organisations = Organisation.where.not(id: organisation.id).pluck(:id, :name) @organisations = Organisation.filter_by_active.where.not(id: organisation.id).pluck(:id, :name)
render "organisation_relationships/add_managing_agent", status: :unprocessable_entity render "organisation_relationships/add_managing_agent", status: :unprocessable_entity
end end
end end
@ -110,7 +110,7 @@ private
end end
def organisations def organisations
@organisations ||= Organisation.where.not(id: organisation.id).pluck(:id, :name) @organisations ||= Organisation.filter_by_active.where.not(id: organisation.id).pluck(:id, :name)
end end
def parent_organisation def parent_organisation

33
app/controllers/organisations_controller.rb

@ -94,10 +94,37 @@ class OrganisationsController < ApplicationController
end end
end end
def deactivate
authorize @organisation
render "toggle_active", locals: { action: "deactivate" }
end
def reactivate
authorize @organisation
render "toggle_active", locals: { action: "reactivate" }
end
def update def update
if current_user.data_coordinator? || current_user.support? if (current_user.data_coordinator? && org_params[:active].nil?) || current_user.support?
if @organisation.update(org_params) if @organisation.update(org_params)
flash[:notice] = I18n.t("organisation.updated") case org_params[:active]
when "false"
@organisation.users.filter_by_active.each do |user|
user.deactivate!(reactivate_with_organisation: true)
end
flash[:notice] = I18n.t("organisation.deactivated", organisation: @organisation.name)
when "true"
users_to_reactivate = @organisation.users.where(reactivate_with_organisation: true)
users_to_reactivate.each do |user|
user.reactivate!
user.send_confirmation_instructions
end
flash[:notice] = I18n.t("organisation.reactivated", organisation: @organisation.name)
else
flash[:notice] = I18n.t("organisation.updated")
end
redirect_to details_organisation_path(@organisation) redirect_to details_organisation_path(@organisation)
end end
else else
@ -239,7 +266,7 @@ private
end end
def org_params def org_params
params.require(:organisation).permit(:name, :address_line1, :address_line2, :postcode, :phone, :holds_own_stock, :provider_type, :housing_registration_no) params.require(:organisation).permit(:name, :address_line1, :address_line2, :postcode, :phone, :holds_own_stock, :provider_type, :housing_registration_no, :active)
end end
def codes_only_export? def codes_only_export?

3
app/controllers/users_controller.rb

@ -62,9 +62,10 @@ class UsersController < ApplicationController
else else
user_name = @user.name&.possessive || @user.email.possessive user_name = @user.name&.possessive || @user.email.possessive
if user_params[:active] == "false" if user_params[:active] == "false"
@user.update!(confirmed_at: nil, sign_in_count: 0, initial_confirmation_sent: false) @user.deactivate!
flash[:notice] = I18n.t("devise.activation.deactivated", user_name:) flash[:notice] = I18n.t("devise.activation.deactivated", user_name:)
elsif user_params[:active] == "true" elsif user_params[:active] == "true"
@user.reactivate!
@user.send_confirmation_instructions @user.send_confirmation_instructions
flash[:notice] = I18n.t("devise.activation.reactivated", user_name:) flash[:notice] = I18n.t("devise.activation.reactivated", user_name:)
elsif user_params.key?("email") elsif user_params.key?("email")

2
app/helpers/form_page_helper.rb

@ -43,7 +43,7 @@ module FormPageHelper
elsif returning_to_question_page?(page, referrer) elsif returning_to_question_page?(page, referrer)
send(log.form.cancel_path(page, log), log) send(log.form.cancel_path(page, log), log)
else else
page.skip_href(log) || send(log.form.next_page_redirect_path(page, log, current_user), log) page.skip_href(log) || send(log.form.next_page_redirect_path(page, log, current_user, ignore_answered: true), log)
end end
end end
end end

13
app/helpers/toggle_active_organisation_helper.rb

@ -0,0 +1,13 @@
module ToggleActiveOrganisationHelper
def toggle_organisation_form_path(action, organisation)
if action == "deactivate"
organisation_new_deactivation_path(organisation)
else
organisation_reactivate_path(organisation)
end
end
def date_type_question(action)
action == "deactivate" ? :deactivation_date_type : :reactivation_date_type
end
end

2
app/jobs/create_addresses_csv_job.rb

@ -14,7 +14,7 @@ class CreateAddressesCsvJob < ApplicationJob
filename = "#{['sales-logs-addresses', organisation.name, Time.zone.now].compact.join('-')}.csv" filename = "#{['sales-logs-addresses', organisation.name, Time.zone.now].compact.join('-')}.csv"
end end
storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["CSV_DOWNLOAD_PAAS_INSTANCE"]) storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["BULK_UPLOAD_BUCKET"])
storage_service.write_file(filename, BYTE_ORDER_MARK + csv_string) storage_service.write_file(filename, BYTE_ORDER_MARK + csv_string)
Rails.logger.info("Created addresses file: #{filename}") Rails.logger.info("Created addresses file: #{filename}")

2
app/jobs/data_export_xml_job.rb

@ -2,7 +2,7 @@ class DataExportXmlJob < ApplicationJob
queue_as :default queue_as :default
def perform(full_update: false) def perform(full_update: false)
storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["EXPORT_PAAS_INSTANCE"]) storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["EXPORT_BUCKET"])
export_service = Exports::LettingsLogExportService.new(storage_service) export_service = Exports::LettingsLogExportService.new(storage_service)
export_service.export_xml_lettings_logs(full_update:) export_service.export_xml_lettings_logs(full_update:)

2
app/jobs/email_csv_job.rb

@ -20,7 +20,7 @@ class EmailCsvJob < ApplicationJob
filename = "#{[log_type, 'logs', organisation&.name, Time.zone.now].compact.join('-')}.csv" filename = "#{[log_type, 'logs', organisation&.name, Time.zone.now].compact.join('-')}.csv"
storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["CSV_DOWNLOAD_PAAS_INSTANCE"]) storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["BULK_UPLOAD_BUCKET"])
storage_service.write_file(filename, BYTE_ORDER_MARK + csv_string) storage_service.write_file(filename, BYTE_ORDER_MARK + csv_string)
url = storage_service.get_presigned_url(filename, EXPIRATION_TIME) url = storage_service.get_presigned_url(filename, EXPIRATION_TIME)

2
app/jobs/email_missing_addresses_csv_job.rb

@ -18,7 +18,7 @@ class EmailMissingAddressesCsvJob < ApplicationJob
email_method = :send_missing_sales_addresses_csv_download_mail email_method = :send_missing_sales_addresses_csv_download_mail
end end
storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["CSV_DOWNLOAD_PAAS_INSTANCE"]) storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["BULK_UPLOAD_BUCKET"])
storage_service.write_file(filename, BYTE_ORDER_MARK + csv_string) storage_service.write_file(filename, BYTE_ORDER_MARK + csv_string)
url = storage_service.get_presigned_url(filename, EXPIRATION_TIME) url = storage_service.get_presigned_url(filename, EXPIRATION_TIME)

2
app/jobs/scheme_email_csv_job.rb

@ -23,7 +23,7 @@ class SchemeEmailCsvJob < ApplicationJob
filename = "#{['schemes-and-locations', organisation&.name, Time.zone.now].compact.join('-')}.csv" filename = "#{['schemes-and-locations', organisation&.name, Time.zone.now].compact.join('-')}.csv"
end end
storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["CSV_DOWNLOAD_PAAS_INSTANCE"]) storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["BULK_UPLOAD_BUCKET"])
storage_service.write_file(filename, BYTE_ORDER_MARK + csv_string) storage_service.write_file(filename, BYTE_ORDER_MARK + csv_string)
url = storage_service.get_presigned_url(filename, EXPIRATION_TIME) url = storage_service.get_presigned_url(filename, EXPIRATION_TIME)

11
app/models/form.rb

@ -80,7 +80,7 @@ class Form
subsections.find { |s| s.pages.find { |p| p.id == page.id } } subsections.find { |s| s.pages.find { |p| p.id == page.id } }
end end
def next_page_id(page, log, current_user) def next_page_id(page, log, current_user, ignore_answered: false)
return page.next_unresolved_page_id || :check_answers if log.unresolved return page.next_unresolved_page_id || :check_answers if log.unresolved
page_ids = subsection_for_page(page).pages.map(&:id) page_ids = subsection_for_page(page).pages.map(&:id)
@ -93,13 +93,14 @@ class Form
next_page = get_page(page_id) next_page = get_page(page_id)
return :check_answers if next_page.nil? return :check_answers if next_page.nil?
return next_page.id if next_page.routed_to?(log, current_user) return next_page.id if next_page.routed_to?(log, current_user) &&
(!ignore_answered || next_page.has_unanswered_questions?(log))
next_page_id(next_page, log, current_user) next_page_id(next_page, log, current_user, ignore_answered:)
end end
def next_page_redirect_path(page, log, current_user) def next_page_redirect_path(page, log, current_user, ignore_answered: false)
next_page_id = next_page_id(page, log, current_user) next_page_id = next_page_id(page, log, current_user, ignore_answered:)
if next_page_id == :check_answers if next_page_id == :check_answers
"#{type}_log_#{subsection_for_page(page).id}_check_answers_path" "#{type}_log_#{subsection_for_page(page).id}_check_answers_path"
else else

4
app/models/form/lettings/pages/uprn.rb

@ -24,9 +24,9 @@ class Form::Lettings::Pages::Uprn < ::Form::Page
return unless log return unless log
if form.start_year_after_2024? if form.start_year_after_2024?
"/#{log.model_name.param_key.dasherize}s/#{log.id}/address-matcher" "address-matcher"
else else
"/#{log.model_name.param_key.dasherize}s/#{log.id}/address" "address"
end end
end end
end end

2
app/models/form/lettings/pages/uprn_selection.rb

@ -27,6 +27,6 @@ class Form::Lettings::Pages::UprnSelection < ::Form::Page
def skip_href(log = nil) def skip_href(log = nil)
return unless log return unless log
"/#{log.model_name.param_key.dasherize}s/#{log.id}/address-matcher" "address-matcher"
end end
end end

2
app/models/form/lettings/questions/ethnic_arab.rb

@ -6,7 +6,7 @@ class Form::Lettings::Questions::EthnicArab < ::Form::Question
@header = "Which of the following best describes the lead tenant’s Arab background?" @header = "Which of the following best describes the lead tenant’s Arab background?"
@type = "radio" @type = "radio"
@check_answers_card_number = 1 @check_answers_card_number = 1
@hint_text = "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest." @hint_text = form.start_year_after_2024? ? "" : "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest."
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

2
app/models/form/lettings/questions/ethnic_asian.rb

@ -6,7 +6,7 @@ class Form::Lettings::Questions::EthnicAsian < ::Form::Question
@header = "Which of the following best describes the lead tenant’s Asian or Asian British background?" @header = "Which of the following best describes the lead tenant’s Asian or Asian British background?"
@type = "radio" @type = "radio"
@check_answers_card_number = 1 @check_answers_card_number = 1
@hint_text = "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest." @hint_text = form.start_year_after_2024? ? "" : "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest."
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

2
app/models/form/lettings/questions/ethnic_black.rb

@ -6,7 +6,7 @@ class Form::Lettings::Questions::EthnicBlack < ::Form::Question
@header = "Which of the following best describes the lead tenant’s Black, African, Caribbean or Black British background?" @header = "Which of the following best describes the lead tenant’s Black, African, Caribbean or Black British background?"
@type = "radio" @type = "radio"
@check_answers_card_number = 1 @check_answers_card_number = 1
@hint_text = "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest." @hint_text = form.start_year_after_2024? ? "" : "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest."
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

2
app/models/form/lettings/questions/ethnic_group.rb

@ -6,7 +6,7 @@ class Form::Lettings::Questions::EthnicGroup < ::Form::Question
@header = "What is the lead tenant’s ethnic group?" @header = "What is the lead tenant’s ethnic group?"
@type = "radio" @type = "radio"
@check_answers_card_number = 1 @check_answers_card_number = 1
@hint_text = "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest." @hint_text = form.start_year_after_2024? ? "" : "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest."
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

2
app/models/form/lettings/questions/ethnic_mixed.rb

@ -6,7 +6,7 @@ class Form::Lettings::Questions::EthnicMixed < ::Form::Question
@header = "Which of the following best describes the lead tenant’s Mixed or Multiple ethnic groups background?" @header = "Which of the following best describes the lead tenant’s Mixed or Multiple ethnic groups background?"
@type = "radio" @type = "radio"
@check_answers_card_number = 1 @check_answers_card_number = 1
@hint_text = "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest." @hint_text = form.start_year_after_2024? ? "" : "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest."
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

6
app/models/form/lettings/questions/managing_organisation.rb

@ -31,11 +31,11 @@ class Form::Lettings::Questions::ManagingOrganisation < ::Form::Question
end end
orgs = if user.support? orgs = if user.support?
log.owning_organisation.managing_agents log.owning_organisation.managing_agents.filter_by_active
elsif user.organisation.absorbed_organisations.include?(log.owning_organisation) elsif user.organisation.absorbed_organisations.include?(log.owning_organisation)
user.organisation.managing_agents + log.owning_organisation.managing_agents user.organisation.managing_agents.filter_by_active + log.owning_organisation.managing_agents.filter_by_active
else else
user.organisation.managing_agents user.organisation.managing_agents.filter_by_active
end end
user.organisation.absorbed_organisations.each do |absorbed_org| user.organisation.absorbed_organisations.each do |absorbed_org|

2
app/models/form/lettings/questions/nationality_all_group.rb

@ -6,7 +6,7 @@ class Form::Lettings::Questions::NationalityAllGroup < ::Form::Question
@header = "What is the nationality of the lead tenant?" @header = "What is the nationality of the lead tenant?"
@type = "radio" @type = "radio"
@check_answers_card_number = 1 @check_answers_card_number = 1
@hint_text = "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest. If the lead tenant is a dual national of the United Kingdom and another country, enter United Kingdom. If they are a dual national of two other countries, the tenant should decide which country to enter." @hint_text = "If the lead tenant is a dual national of the United Kingdom and another country, enter United Kingdom. If they are a dual national of two other countries, the tenant should decide which country to enter."
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
@conditional_for = { "nationality_all" => [12] } @conditional_for = { "nationality_all" => [12] }

1
app/models/form/lettings/questions/period.rb

@ -20,6 +20,7 @@ class Form::Lettings::Questions::Period < ::Form::Question
"7" => { "value" => "Weekly for 48 weeks" }, "7" => { "value" => "Weekly for 48 weeks" },
"6" => { "value" => "Weekly for 49 weeks" }, "6" => { "value" => "Weekly for 49 weeks" },
"5" => { "value" => "Weekly for 50 weeks" }, "5" => { "value" => "Weekly for 50 weeks" },
"11" => { "value" => "Weekly for 51 weeks" },
"1" => { "value" => "Weekly for 52 weeks" }, "1" => { "value" => "Weekly for 52 weeks" },
"10" => { "value" => "Weekly for 53 weeks" }, "10" => { "value" => "Weekly for 53 weeks" },
}.freeze }.freeze

2
app/models/form/lettings/questions/person_working_situation.rb

@ -16,7 +16,7 @@ class Form::Lettings::Questions::PersonWorkingSituation < ::Form::Question
{ "1" => { "value" => "Full-time – 30 hours or more" }, { "1" => { "value" => "Full-time – 30 hours or more" },
"2" => { "value" => "Part-time – Less than 30 hours" }, "2" => { "value" => "Part-time – Less than 30 hours" },
"7" => { "value" => "Full-time student" }, "7" => { "value" => "Full-time student" },
"3" => { "value" => "In government training into work, such as New Deal" }, "3" => { "value" => "In government training into work" },
"4" => { "value" => "Jobseeker" }, "4" => { "value" => "Jobseeker" },
"6" => { "value" => "Not seeking work" }, "6" => { "value" => "Not seeking work" },
"8" => { "value" => "Unable to work because of long term sick or disability" }, "8" => { "value" => "Unable to work because of long term sick or disability" },

7
app/models/form/lettings/questions/referral_prp.rb

@ -18,14 +18,11 @@ class Form::Lettings::Questions::ReferralPrp < ::Form::Question
"hint" => "Where the tenant has moved to another social property owned by the same landlord.", "hint" => "Where the tenant has moved to another social property owned by the same landlord.",
}, },
"2" => { "2" => {
"value" => "Tenant applied directly (no nomination)", "value" => "Tenant applied directly (no referral or nomination)",
}, },
"3" => { "3" => {
"value" => "Nominated by a local housing authority", "value" => "Nominated by a local housing authority",
}, },
"4" => {
"value" => "Referred by local authority housing department",
},
"8" => { "8" => {
"value" => "Re-located through official housing mobility scheme", "value" => "Re-located through official housing mobility scheme",
}, },
@ -64,7 +61,7 @@ class Form::Lettings::Questions::ReferralPrp < ::Form::Question
"hint" => "Where the tenant has moved to another social property owned by the same landlord.", "hint" => "Where the tenant has moved to another social property owned by the same landlord.",
}, },
"2" => { "2" => {
"value" => "Tenant applied directly (no nomination)", "value" => "Tenant applied directly (no referral or nomination)",
}, },
"3" => { "3" => {
"value" => "Nominated by a local housing authority", "value" => "Nominated by a local housing authority",

10
app/models/form/lettings/questions/referral_supported_housing.rb

@ -18,10 +18,7 @@ class Form::Lettings::Questions::ReferralSupportedHousing < ::Form::Question
"hint" => "Where the tenant has moved to another social property owned by the same landlord.", "hint" => "Where the tenant has moved to another social property owned by the same landlord.",
}, },
"2" => { "2" => {
"value" => "Tenant applied directly (no referral)", "value" => "Tenant applied directly (no referral or nomination)",
},
"3" => {
"value" => "Nominated by a local housing authority",
}, },
"8" => { "8" => {
"value" => "Re-located through official housing mobility scheme", "value" => "Re-located through official housing mobility scheme",
@ -61,10 +58,7 @@ class Form::Lettings::Questions::ReferralSupportedHousing < ::Form::Question
"hint" => "Where the tenant has moved to another social property owned by the same landlord.", "hint" => "Where the tenant has moved to another social property owned by the same landlord.",
}, },
"2" => { "2" => {
"value" => "Tenant applied directly (no referral)", "value" => "Tenant applied directly (no referral or nomination)",
},
"3" => {
"value" => "Nominated by a local housing authority",
}, },
"8" => { "8" => {
"value" => "Re-located through official housing mobility scheme", "value" => "Re-located through official housing mobility scheme",

4
app/models/form/lettings/questions/stock_owner.rb

@ -30,7 +30,7 @@ class Form::Lettings::Questions::StockOwner < ::Form::Question
end end
if user.support? if user.support?
Organisation.where(holds_own_stock: true).find_each do |org| Organisation.filter_by_active.where(holds_own_stock: true).find_each do |org|
if org.merge_date.present? if org.merge_date.present?
answer_opts[org.id] = "#{org.name} (inactive as of #{org.merge_date.to_fs(:govuk_date)})" if org.merge_date >= FormHandler.instance.start_date_of_earliest_open_for_editing_collection_period answer_opts[org.id] = "#{org.name} (inactive as of #{org.merge_date.to_fs(:govuk_date)})" if org.merge_date >= FormHandler.instance.start_date_of_earliest_open_for_editing_collection_period
elsif org.absorbed_organisations.merged_during_open_collection_period.exists? && org.available_from.present? elsif org.absorbed_organisations.merged_during_open_collection_period.exists? && org.available_from.present?
@ -40,7 +40,7 @@ class Form::Lettings::Questions::StockOwner < ::Form::Question
end end
end end
else else
user.organisation.stock_owners.each do |stock_owner| user.organisation.stock_owners.filter_by_active.each do |stock_owner|
answer_opts[stock_owner.id] = stock_owner.name answer_opts[stock_owner.id] = stock_owner.name
end end
recently_absorbed_organisations.each do |absorbed_org| recently_absorbed_organisations.each do |absorbed_org|

4
app/models/form/lettings/questions/working_situation1.rb

@ -6,7 +6,7 @@ class Form::Lettings::Questions::WorkingSituation1 < ::Form::Question
@header = "Which of these best describes the lead tenant’s working situation?" @header = "Which of these best describes the lead tenant’s working situation?"
@type = "radio" @type = "radio"
@check_answers_card_number = 1 @check_answers_card_number = 1
@hint_text = "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest." @hint_text = form.start_year_after_2024? ? "" : "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest."
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end
@ -15,7 +15,7 @@ class Form::Lettings::Questions::WorkingSituation1 < ::Form::Question
"1" => { "value" => "Full-time – 30 hours or more" }, "1" => { "value" => "Full-time – 30 hours or more" },
"2" => { "value" => "Part-time – Less than 30 hours" }, "2" => { "value" => "Part-time – Less than 30 hours" },
"7" => { "value" => "Full-time student" }, "7" => { "value" => "Full-time student" },
"3" => { "value" => "In government training into work, such as New Deal" }, "3" => { "value" => "In government training into work" },
"4" => { "value" => "Jobseeker" }, "4" => { "value" => "Jobseeker" },
"6" => { "value" => "Not seeking work" }, "6" => { "value" => "Not seeking work" },
"8" => { "value" => "Unable to work because of long term sick or disability" }, "8" => { "value" => "Unable to work because of long term sick or disability" },

4
app/models/form/page.rb

@ -36,6 +36,10 @@ class Form::Page
end end
end end
def has_unanswered_questions?(log)
questions.any? { |question| question.displayed_to_user?(log) && question.unanswered?(log) }
end
def interruption_screen? def interruption_screen?
questions.all? { |question| question.type == "interruption_screen" } questions.all? { |question| question.type == "interruption_screen" }
end end

10
app/models/form/question.rb

@ -110,12 +110,16 @@ class Form::Question
end end
def action_text(log) def action_text(log)
displayed_as_answered?(log) ? "Change" : "Answer"
end
def displayed_as_answered?(log)
if is_derived_or_has_inferred_check_answers_value?(log) if is_derived_or_has_inferred_check_answers_value?(log)
"Change" true
elsif type == "checkbox" elsif type == "checkbox"
answer_options.keys.any? { |key| value_is_yes?(log[key]) } ? "Change" : "Answer" answer_options.keys.any? { |key| value_is_yes?(log[key]) }
else else
log[id].blank? ? "Answer" : "Change" log[id].present?
end end
end end

4
app/models/form/sales/pages/uprn.rb

@ -23,9 +23,9 @@ class Form::Sales::Pages::Uprn < ::Form::Page
return unless log return unless log
if form.start_year_after_2024? if form.start_year_after_2024?
"/#{log.model_name.param_key.dasherize}s/#{log.id}/address-matcher" "address-matcher"
else else
"/#{log.model_name.param_key.dasherize}s/#{log.id}/address" "address"
end end
end end
end end

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

@ -27,6 +27,6 @@ class Form::Sales::Pages::UprnSelection < ::Form::Page
def skip_href(log = nil) def skip_href(log = nil)
return unless log return unless log
"/#{log.model_name.param_key.dasherize}s/#{log.id}/address-matcher" "address-matcher"
end end
end end

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

@ -6,7 +6,7 @@ class Form::Sales::Questions::Buyer1EthnicBackgroundArab < ::Form::Question
@header = "Which of the following best describes buyer 1’s Arab background?" @header = "Which of the following best describes buyer 1’s Arab background?"
@type = "radio" @type = "radio"
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@hint_text = "Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest." @hint_text = form.start_year_after_2024? ? "" : "Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest."
@check_answers_card_number = 1 @check_answers_card_number = 1
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

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

@ -6,7 +6,7 @@ class Form::Sales::Questions::Buyer1EthnicBackgroundAsian < ::Form::Question
@header = "Which of the following best describes buyer 1’s Asian or Asian British background?" @header = "Which of the following best describes buyer 1’s Asian or Asian British background?"
@type = "radio" @type = "radio"
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@hint_text = "Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest." @hint_text = form.start_year_after_2024? ? "" : "Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest."
@check_answers_card_number = 1 @check_answers_card_number = 1
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

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

@ -6,7 +6,7 @@ class Form::Sales::Questions::Buyer1EthnicBackgroundBlack < ::Form::Question
@header = "Which of the following best describes buyer 1’s Black, African, Caribbean or Black British background?" @header = "Which of the following best describes buyer 1’s Black, African, Caribbean or Black British background?"
@type = "radio" @type = "radio"
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@hint_text = "Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest." @hint_text = form.start_year_after_2024? ? "" : "Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest."
@check_answers_card_number = 1 @check_answers_card_number = 1
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

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

@ -6,7 +6,7 @@ class Form::Sales::Questions::Buyer1EthnicBackgroundMixed < ::Form::Question
@header = "Which of the following best describes buyer 1’s Mixed or Multiple ethnic groups background?" @header = "Which of the following best describes buyer 1’s Mixed or Multiple ethnic groups background?"
@type = "radio" @type = "radio"
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@hint_text = "Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest." @hint_text = form.start_year_after_2024? ? "" : "Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest."
@check_answers_card_number = 1 @check_answers_card_number = 1
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

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

@ -6,7 +6,7 @@ class Form::Sales::Questions::Buyer1EthnicGroup < ::Form::Question
@header = "What is buyer 1’s ethnic group?" @header = "What is buyer 1’s ethnic group?"
@type = "radio" @type = "radio"
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@hint_text = "Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest." @hint_text = form.start_year_after_2024? ? "" : "Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest."
@check_answers_card_number = 1 @check_answers_card_number = 1
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

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

@ -6,7 +6,7 @@ class Form::Sales::Questions::Buyer1LiveInProperty < ::Form::Question
@header = "Will buyer 1 live in the property?" @header = "Will buyer 1 live in the property?"
@type = "radio" @type = "radio"
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@hint_text = "Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest." @hint_text = form.start_year_after_2024? ? "" : "Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest."
@check_answers_card_number = 1 @check_answers_card_number = 1
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

4
app/models/form/sales/questions/buyer1_working_situation.rb

@ -6,7 +6,7 @@ class Form::Sales::Questions::Buyer1WorkingSituation < ::Form::Question
@header = "Which of these best describes buyer 1's working situation?" @header = "Which of these best describes buyer 1's working situation?"
@type = "radio" @type = "radio"
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@hint_text = "Buyer 1 is the person in the household who does the most paid work. If it's a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest." @hint_text = form.start_year_after_2024? ? "" : "Buyer 1 is the person in the household who does the most paid work. If its a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest."
@check_answers_card_number = 1 @check_answers_card_number = 1
@inferred_check_answers_value = [{ @inferred_check_answers_value = [{
"condition" => { "condition" => {
@ -20,7 +20,7 @@ class Form::Sales::Questions::Buyer1WorkingSituation < ::Form::Question
ANSWER_OPTIONS = { ANSWER_OPTIONS = {
"1" => { "value" => "Full-time - 30 hours or more" }, "1" => { "value" => "Full-time - 30 hours or more" },
"2" => { "value" => "Part-time - Less than 30 hours" }, "2" => { "value" => "Part-time - Less than 30 hours" },
"3" => { "value" => "In government training into work, such as New Deal" }, "3" => { "value" => "In government training into work" },
"4" => { "value" => "Jobseeker" }, "4" => { "value" => "Jobseeker" },
"6" => { "value" => "Not seeking work" }, "6" => { "value" => "Not seeking work" },
"8" => { "value" => "Unable to work due to long term sick or disability" }, "8" => { "value" => "Unable to work due to long term sick or disability" },

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

@ -19,7 +19,7 @@ class Form::Sales::Questions::Buyer2WorkingSituation < ::Form::Question
ANSWER_OPTIONS = { ANSWER_OPTIONS = {
"1" => { "value" => "Full-time - 30 hours or more" }, "1" => { "value" => "Full-time - 30 hours or more" },
"2" => { "value" => "Part-time - Less than 30 hours" }, "2" => { "value" => "Part-time - Less than 30 hours" },
"3" => { "value" => "In government training into work, such as New Deal" }, "3" => { "value" => "In government training into work" },
"4" => { "value" => "Jobseeker" }, "4" => { "value" => "Jobseeker" },
"6" => { "value" => "Not seeking work" }, "6" => { "value" => "Not seeking work" },
"8" => { "value" => "Unable to work due to long term sick or disability" }, "8" => { "value" => "Unable to work due to long term sick or disability" },

6
app/models/form/sales/questions/managing_organisation.rb

@ -31,11 +31,11 @@ class Form::Sales::Questions::ManagingOrganisation < ::Form::Question
end end
orgs = if user.support? orgs = if user.support?
log.owning_organisation.managing_agents log.owning_organisation.managing_agents.filter_by_active
elsif user.organisation.absorbed_organisations.include?(log.owning_organisation) elsif user.organisation.absorbed_organisations.include?(log.owning_organisation)
user.organisation.managing_agents + log.owning_organisation.managing_agents user.organisation.managing_agents.filter_by_active + log.owning_organisation.managing_agents.filter_by_active
else else
user.organisation.managing_agents user.organisation.managing_agents.filter_by_active
end.pluck(:id, :name).to_h end.pluck(:id, :name).to_h
user.organisation.absorbed_organisations.each do |absorbed_org| user.organisation.absorbed_organisations.each do |absorbed_org|

10
app/models/form/sales/questions/nationality_all_group.rb

@ -4,7 +4,7 @@ class Form::Sales::Questions::NationalityAllGroup < ::Form::Question
@check_answer_label = "Buyer #{buyer_index}’s nationality" @check_answer_label = "Buyer #{buyer_index}’s nationality"
@header = "What is buyer #{buyer_index}’s nationality?" @header = "What is buyer #{buyer_index}’s nationality?"
@type = "radio" @type = "radio"
@hint_text = buyer_index == 1 ? "Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest." : "" @hint_text = "If buyer #{buyer_index} is a dual national of the United Kingdom and another country, enter United Kingdom. If they are a dual national of two other countries, the buyer should decide which country to enter."
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@check_answers_card_number = buyer_index @check_answers_card_number = buyer_index
@conditional_for = buyer_index == 1 ? { "nationality_all" => [12] } : { "nationality_all_buyer2" => [12] } @conditional_for = buyer_index == 1 ? { "nationality_all" => [12] } : { "nationality_all_buyer2" => [12] }
@ -19,14 +19,6 @@ class Form::Sales::Questions::NationalityAllGroup < ::Form::Question
"0" => { "value" => "Buyer prefers not to say" }, "0" => { "value" => "Buyer prefers not to say" },
}.freeze }.freeze
def hint_text
if @buyer_index == 1
"Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest. If buyer 1 is a dual national of the United Kingdom and another country, enter United Kingdom. If they are a dual national of two other countries, the buyer should decide which country to enter."
else
"If buyer 2 is a dual national of the United Kingdom and another country, enter United Kingdom. If they are a dual national of two other countries, the buyer should decide which country to enter."
end
end
def question_number def question_number
if form.start_date.year == 2023 if form.start_date.year == 2023
@buyer_index == 1 ? 24 : 32 @buyer_index == 1 ? 24 : 32

4
app/models/form/sales/questions/owning_organisation_id.rb

@ -25,7 +25,7 @@ class Form::Sales::Questions::OwningOrganisationId < ::Form::Question
answer_opts[user.organisation.id] = "#{user.organisation.name} (Your organisation)" answer_opts[user.organisation.id] = "#{user.organisation.name} (Your organisation)"
end end
user.organisation.stock_owners.where(holds_own_stock: true).find_each do |org| user.organisation.stock_owners.filter_by_active.where(holds_own_stock: true).find_each do |org|
answer_opts[org.id] = org.name answer_opts[org.id] = org.name
end end
end end
@ -44,7 +44,7 @@ class Form::Sales::Questions::OwningOrganisationId < ::Form::Question
end end
if user.support? if user.support?
Organisation.where(holds_own_stock: true).find_each do |org| Organisation.filter_by_active.where(holds_own_stock: true).find_each do |org|
if org.merge_date.present? if org.merge_date.present?
answer_opts[org.id] = "#{org.name} (inactive as of #{org.merge_date.to_fs(:govuk_date)})" if org.merge_date >= FormHandler.instance.start_date_of_earliest_open_for_editing_collection_period answer_opts[org.id] = "#{org.name} (inactive as of #{org.merge_date.to_fs(:govuk_date)})" if org.merge_date >= FormHandler.instance.start_date_of_earliest_open_for_editing_collection_period
elsif org.absorbed_organisations.merged_during_open_collection_period.exists? && org.available_from.present? elsif org.absorbed_organisations.merged_during_open_collection_period.exists? && org.available_from.present?

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

@ -20,7 +20,7 @@ class Form::Sales::Questions::PersonWorkingSituation < ::Form::Question
{ {
"1" => { "value" => "Full-time - 30 hours or more" }, "1" => { "value" => "Full-time - 30 hours or more" },
"2" => { "value" => "Part-time - Less than 30 hours" }, "2" => { "value" => "Part-time - Less than 30 hours" },
"3" => { "value" => "In government training into work, such as New Deal" }, "3" => { "value" => "In government training into work" },
"4" => { "value" => "Jobseeker" }, "4" => { "value" => "Jobseeker" },
"6" => { "value" => "Not seeking work" }, "6" => { "value" => "Not seeking work" },
"8" => { "value" => "Unable to work due to long term sick or disability" }, "8" => { "value" => "Unable to work due to long term sick or disability" },

2
app/models/forms/bulk_upload_lettings/upload_your_file.rb

@ -56,7 +56,7 @@ module Forms
def storage_service def storage_service
@storage_service ||= if upload_enabled? @storage_service ||= if upload_enabled?
Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["CSV_DOWNLOAD_PAAS_INSTANCE"]) Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["BULK_UPLOAD_BUCKET"])
else else
Storage::LocalDiskService.new Storage::LocalDiskService.new
end end

2
app/models/forms/bulk_upload_sales/upload_your_file.rb

@ -49,7 +49,7 @@ module Forms
def storage_service def storage_service
@storage_service ||= if FeatureToggle.upload_enabled? @storage_service ||= if FeatureToggle.upload_enabled?
Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["CSV_DOWNLOAD_PAAS_INSTANCE"]) Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["BULK_UPLOAD_BUCKET"])
else else
Storage::LocalDiskService.new Storage::LocalDiskService.new
end end

22
app/models/location.rb

@ -40,6 +40,7 @@ class Location < ApplicationRecord
if scopes.any? if scopes.any?
filtered_records = filtered_records filtered_records = filtered_records
.left_outer_joins(:location_deactivation_periods) .left_outer_joins(:location_deactivation_periods)
.joins(scheme: [:owning_organisation])
.order("location_deactivation_periods.created_at DESC") .order("location_deactivation_periods.created_at DESC")
.merge(scopes.reduce(&:or)) .merge(scopes.reduce(&:or))
end end
@ -53,27 +54,39 @@ class Location < ApplicationRecord
} }
scope :deactivated, lambda { scope :deactivated, lambda {
deactivated_by_organisation
.or(deactivated_directly)
}
scope :deactivated_by_organisation, lambda {
merge(Organisation.filter_by_inactive)
}
scope :deactivated_directly, lambda {
merge(LocationDeactivationPeriod.deactivations_without_reactivation) merge(LocationDeactivationPeriod.deactivations_without_reactivation)
.where("location_deactivation_periods.deactivation_date <= ?", Time.zone.now) .where("location_deactivation_periods.deactivation_date <= ?", Time.zone.now)
} }
scope :deactivating_soon, lambda { scope :deactivating_soon, lambda {
merge(LocationDeactivationPeriod.deactivations_without_reactivation) merge(LocationDeactivationPeriod.deactivations_without_reactivation)
.where("location_deactivation_periods.deactivation_date > ?", Time.zone.now) .where("location_deactivation_periods.deactivation_date > ?", Time.zone.now)
.where.not(id: joins(scheme: [:owning_organisation]).deactivated_by_organisation.pluck(:id))
} }
scope :reactivating_soon, lambda { scope :reactivating_soon, lambda {
where.not("location_deactivation_periods.reactivation_date IS NULL") where.not("location_deactivation_periods.reactivation_date IS NULL")
.where("location_deactivation_periods.reactivation_date > ?", Time.zone.now) .where("location_deactivation_periods.reactivation_date > ?", Time.zone.now)
.where.not(id: joins(scheme: [:owning_organisation]).deactivated_by_organisation.pluck(:id))
} }
scope :activating_soon, lambda { scope :activating_soon, lambda {
where("startdate > ?", Time.zone.now) where("locations.startdate > ?", Time.zone.now)
} }
scope :active_status, lambda { scope :active_status, lambda {
where.not(id: joins(:location_deactivation_periods).reactivating_soon.pluck(:id)) where.not(id: joins(:location_deactivation_periods).reactivating_soon.pluck(:id))
.where.not(id: joins(:location_deactivation_periods).deactivated.pluck(:id)) .where.not(id: joins(scheme: [:owning_organisation]).deactivated_by_organisation.pluck(:id))
.where.not(id: joins(:location_deactivation_periods).deactivated_directly.pluck(:id))
.where.not(id: incomplete.pluck(:id)) .where.not(id: incomplete.pluck(:id))
.where.not(id: joins(:location_deactivation_periods).deactivating_soon.pluck(:id)) .where.not(id: joins(:location_deactivation_periods).deactivating_soon.pluck(:id))
.where.not(id: activating_soon.pluck(:id)) .where.not(id: activating_soon.pluck(:id))
@ -142,7 +155,8 @@ class Location < ApplicationRecord
def status_at(date) def status_at(date)
return :deleted if discarded_at.present? return :deleted if discarded_at.present?
return :incomplete unless confirmed return :incomplete unless confirmed
return :deactivated if open_deactivation&.deactivation_date.present? && date >= open_deactivation.deactivation_date return :deactivated if scheme.owning_organisation.status_at(date) == :deactivated ||
open_deactivation&.deactivation_date.present? && date >= open_deactivation.deactivation_date
return :deactivating_soon if open_deactivation&.deactivation_date.present? && date < open_deactivation.deactivation_date return :deactivating_soon if open_deactivation&.deactivation_date.present? && date < open_deactivation.deactivation_date
return :reactivating_soon if last_deactivation_before(date)&.reactivation_date.present? && date < last_deactivation_before(date).reactivation_date return :reactivating_soon if last_deactivation_before(date)&.reactivation_date.present? && date < last_deactivation_before(date).reactivation_date
return :activating_soon if startdate.present? && date < startdate return :activating_soon if startdate.present? && date < startdate

3
app/models/organisation.rb

@ -36,6 +36,8 @@ class Organisation < ApplicationRecord
scope :search_by_name, ->(name) { where("name ILIKE ?", "%#{name}%") } scope :search_by_name, ->(name) { where("name ILIKE ?", "%#{name}%") }
scope :search_by, ->(param) { search_by_name(param) } scope :search_by, ->(param) { search_by_name(param) }
scope :filter_by_active, -> { where(active: true) }
scope :filter_by_inactive, -> { where(active: false) }
scope :merged_during_open_collection_period, -> { where("merge_date >= ?", FormHandler.instance.start_date_of_earliest_open_for_editing_collection_period) } scope :merged_during_open_collection_period, -> { where("merge_date >= ?", FormHandler.instance.start_date_of_earliest_open_for_editing_collection_period) }
has_paper_trail has_paper_trail
@ -137,6 +139,7 @@ class Organisation < ApplicationRecord
def status_at(date) def status_at(date)
return :merged if merge_date.present? && merge_date < date return :merged if merge_date.present? && merge_date < date
return :deactivated unless active
:active :active
end end

25
app/models/scheme.rb

@ -35,6 +35,7 @@ class Scheme < ApplicationRecord
if scopes.any? if scopes.any?
filtered_records = filtered_records filtered_records = filtered_records
.left_outer_joins(:scheme_deactivation_periods) .left_outer_joins(:scheme_deactivation_periods)
.joins(:owning_organisation)
.merge(scopes.reduce(&:or)) .merge(scopes.reduce(&:or))
end end
@ -45,35 +46,48 @@ class Scheme < ApplicationRecord
where.not(confirmed: true) where.not(confirmed: true)
.or(where(confirmed: nil)) .or(where(confirmed: nil))
.or(where.not(id: Location.select(:scheme_id).where(confirmed: true).distinct)) .or(where.not(id: Location.select(:scheme_id).where(confirmed: true).distinct))
.where.not(id: joins(:owning_organisation).deactivated_by_organisation.pluck(:id))
.where.not(id: joins(:scheme_deactivation_periods).deactivated_directly.pluck(:id))
.where.not(id: joins(:scheme_deactivation_periods).reactivating_soon.pluck(:id)) .where.not(id: joins(:scheme_deactivation_periods).reactivating_soon.pluck(:id))
.where.not(id: joins(:scheme_deactivation_periods).deactivated.pluck(:id))
.where.not(id: joins(:scheme_deactivation_periods).deactivating_soon.pluck(:id)) .where.not(id: joins(:scheme_deactivation_periods).deactivating_soon.pluck(:id))
} }
scope :deactivated, lambda { scope :deactivated, lambda {
deactivated_by_organisation
.or(deactivated_directly)
}
scope :deactivated_by_organisation, lambda {
merge(Organisation.filter_by_inactive)
}
scope :deactivated_directly, lambda {
merge(SchemeDeactivationPeriod.deactivations_without_reactivation) merge(SchemeDeactivationPeriod.deactivations_without_reactivation)
.where("scheme_deactivation_periods.deactivation_date <= ?", Time.zone.now) .where("scheme_deactivation_periods.deactivation_date <= ?", Time.zone.now)
} }
scope :deactivating_soon, lambda { scope :deactivating_soon, lambda {
merge(SchemeDeactivationPeriod.deactivations_without_reactivation) merge(SchemeDeactivationPeriod.deactivations_without_reactivation)
.where("scheme_deactivation_periods.deactivation_date > ? AND scheme_deactivation_periods.deactivation_date < ? ", Time.zone.now, 6.months.from_now) .where("scheme_deactivation_periods.deactivation_date > ? AND scheme_deactivation_periods.deactivation_date < ? ", Time.zone.now, 6.months.from_now)
.where.not(id: joins(:owning_organisation).deactivated_by_organisation.pluck(:id))
} }
scope :reactivating_soon, lambda { scope :reactivating_soon, lambda {
where.not("scheme_deactivation_periods.reactivation_date IS NULL") where.not("scheme_deactivation_periods.reactivation_date IS NULL")
.where("scheme_deactivation_periods.reactivation_date > ?", Time.zone.now) .where("scheme_deactivation_periods.reactivation_date > ?", Time.zone.now)
.where.not(id: joins(:owning_organisation).deactivated_by_organisation.pluck(:id))
} }
scope :activating_soon, lambda { scope :activating_soon, lambda {
where("startdate > ?", Time.zone.now) where("schemes.startdate > ?", Time.zone.now)
} }
scope :active_status, lambda { scope :active_status, lambda {
where.not(id: joins(:scheme_deactivation_periods).reactivating_soon.pluck(:id)) where.not(id: joins(:scheme_deactivation_periods).reactivating_soon.pluck(:id))
.where.not(id: joins(:scheme_deactivation_periods).deactivated.pluck(:id))
.where.not(id: incomplete.pluck(:id)) .where.not(id: incomplete.pluck(:id))
.where.not(id: joins(:scheme_deactivation_periods).deactivating_soon.pluck(:id)) .where.not(id: joins(:scheme_deactivation_periods).deactivating_soon.pluck(:id))
.where.not(id: joins(:owning_organisation).deactivated_by_organisation.pluck(:id))
.where.not(id: joins(:owning_organisation).joins(:scheme_deactivation_periods).deactivated_directly.pluck(:id))
.where.not(id: activating_soon.pluck(:id)) .where.not(id: activating_soon.pluck(:id))
} }
@ -268,7 +282,8 @@ class Scheme < ApplicationRecord
def status_at(date) def status_at(date)
return :deleted if discarded_at.present? return :deleted if discarded_at.present?
return :incomplete unless confirmed && locations.confirmed.any? return :incomplete unless confirmed && locations.confirmed.any?
return :deactivated if open_deactivation&.deactivation_date.present? && date >= open_deactivation.deactivation_date return :deactivated if owning_organisation.status_at(date) == :deactivated ||
(open_deactivation&.deactivation_date.present? && date >= open_deactivation.deactivation_date)
return :deactivating_soon if open_deactivation&.deactivation_date.present? && date < open_deactivation.deactivation_date return :deactivating_soon if open_deactivation&.deactivation_date.present? && date < open_deactivation.deactivation_date
return :reactivating_soon if last_deactivation_before(date)&.reactivation_date.present? && date < last_deactivation_before(date).reactivation_date return :reactivating_soon if last_deactivation_before(date)&.reactivation_date.present? && date < last_deactivation_before(date).reactivation_date
return :activating_soon if startdate.present? && date < startdate return :activating_soon if startdate.present? && date < startdate

17
app/models/user.rb

@ -134,6 +134,23 @@ class User < ApplicationRecord
update!(is_dpo: true) update!(is_dpo: true)
end end
def deactivate!(reactivate_with_organisation: false)
update!(
active: false,
confirmed_at: nil,
sign_in_count: 0,
initial_confirmation_sent: false,
reactivate_with_organisation:,
)
end
def reactivate!
update!(
active: true,
reactivate_with_organisation: false,
)
end
MFA_TEMPLATE_ID = "6bdf5ee1-8e01-4be1-b1f9-747061d8a24c".freeze MFA_TEMPLATE_ID = "6bdf5ee1-8e01-4be1-b1f9-747061d8a24c".freeze
RESET_PASSWORD_TEMPLATE_ID = "2c410c19-80a7-481c-a531-2bcb3264f8e6".freeze RESET_PASSWORD_TEMPLATE_ID = "2c410c19-80a7-481c-a531-2bcb3264f8e6".freeze
CONFIRMABLE_TEMPLATE_ID = "3fc2e3a7-0835-4b84-ab7a-ce51629eb614".freeze CONFIRMABLE_TEMPLATE_ID = "3fc2e3a7-0835-4b84-ab7a-ce51629eb614".freeze

4
app/models/validations/household_validations.rb

@ -178,10 +178,6 @@ module Validations::HouseholdValidations
record.errors.add :prevten, :internal_transfer_fixed_or_lifetime, message: I18n.t("validations.household.prevten.la_general_needs.internal_transfer") record.errors.add :prevten, :internal_transfer_fixed_or_lifetime, message: I18n.t("validations.household.prevten.la_general_needs.internal_transfer")
record.errors.add :referral, :internal_transfer_fixed_or_lifetime, message: I18n.t("validations.household.referral.la_general_needs.internal_transfer") record.errors.add :referral, :internal_transfer_fixed_or_lifetime, message: I18n.t("validations.household.referral.la_general_needs.internal_transfer")
end end
if record.owning_organisation.provider_type == "LA" && record.local_housing_referral?
record.errors.add :referral, I18n.t("validations.household.referral.prp.local_housing_referral")
end
end end
def validate_prevloc(record) def validate_prevloc(record)

16
app/policies/organisation_policy.rb

@ -0,0 +1,16 @@
class OrganisationPolicy
attr_reader :user, :organisation
def initialize(user, organisation)
@user = user
@organisation = organisation
end
def deactivate?
user.support? && organisation.status == :active
end
def reactivate?
user.support? && organisation.status == :deactivated
end
end

2
app/services/bulk_update_from_csv/update_locations_from_csv_service.rb

@ -5,7 +5,7 @@ class BulkUpdateFromCsv::UpdateLocationsFromCsvService
end end
def call def call
s3_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["CSV_DOWNLOAD_PAAS_INSTANCE"]) s3_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["BULK_UPLOAD_BUCKET"])
original_locations_csv = csv_from_path(@original_file_name, s3_service) original_locations_csv = csv_from_path(@original_file_name, s3_service)
updated_locations_csv = csv_from_path(@updated_file_name, s3_service) updated_locations_csv = csv_from_path(@updated_file_name, s3_service)

2
app/services/bulk_update_from_csv/update_schemes_from_csv_service.rb

@ -5,7 +5,7 @@ class BulkUpdateFromCsv::UpdateSchemesFromCsvService
end end
def call def call
s3_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["CSV_DOWNLOAD_PAAS_INSTANCE"]) s3_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["BULK_UPLOAD_BUCKET"])
original_schemes_csv = csv_from_path(@original_file_name, s3_service) original_schemes_csv = csv_from_path(@original_file_name, s3_service)
updated_schemes_csv = csv_from_path(@updated_file_name, s3_service) updated_schemes_csv = csv_from_path(@updated_file_name, s3_service)

2
app/services/bulk_upload/downloader.rb

@ -37,7 +37,7 @@ private
end end
def s3_storage_service def s3_storage_service
Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["CSV_DOWNLOAD_PAAS_INSTANCE"]) Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["BULK_UPLOAD_BUCKET"])
end end
def local_disk_storage_service def local_disk_storage_service

2
app/services/bulk_upload/lettings/year2023/row_parser.rb

@ -517,7 +517,7 @@ private
def validate_declaration_acceptance def validate_declaration_acceptance
unless field_45 == 1 unless field_45 == 1
errors.add(:field_45, I18n.t("validations.declaration.missing"), category: :setup) errors.add(:field_45, I18n.t("validations.declaration.missing.pre_2024"), category: :setup)
end end
end end

6
app/services/feature_toggle.rb

@ -28,14 +28,14 @@ class FeatureToggle
end end
def self.delete_scheme_enabled? def self.delete_scheme_enabled?
!Rails.env.production? true
end end
def self.delete_location_enabled? def self.delete_location_enabled?
!Rails.env.production? true
end end
def self.delete_user_enabled? def self.delete_user_enabled?
!Rails.env.production? true
end end
end end

6
app/views/form/_check_answers_summary_list.html.erb

@ -28,7 +28,11 @@
<% if @log.collection_period_open_for_editing? %> <% if @log.collection_period_open_for_editing? %>
<% row.with_action( <% row.with_action(
text: question.action_text(@log), text: question.action_text(@log),
href: action_href(@log, question.page.id, referrer), href: action_href(
@log,
question.page.id,
question.displayed_as_answered?(@log) || !defined?(referrer_unanswered) ? referrer : referrer_unanswered,
),
visually_hidden_text: question.check_answer_label.to_s.downcase, visually_hidden_text: question.check_answer_label.to_s.downcase,
) %> ) %>
<% end %> <% end %>

2
app/views/form/_interruption_screen_question.html.erb

@ -19,6 +19,6 @@
<%= f.govuk_submit "Confirm and continue" %> <%= f.govuk_submit "Confirm and continue" %>
<%= govuk_link_to( <%= govuk_link_to(
(@page.skip_text || "Skip for now"), (@page.skip_text || "Skip for now"),
(@page.skip_href(@log) || send(@log.form.next_page_redirect_path(@page, @log, current_user), @log)), (@page.skip_href(@log) || send(@log.form.next_page_redirect_path(@page, @log, current_user, ignore_answered: true), @log)),
) %> ) %>
</div> </div>

1
app/views/form/check_answers.html.erb

@ -27,6 +27,7 @@
lettings_log: @log, lettings_log: @log,
questions: total_applicable_questions(subsection, @log, current_user), questions: total_applicable_questions(subsection, @log, current_user),
referrer: "check_answers", referrer: "check_answers",
referrer_unanswered: "check_answers_new_answer",
} %> } %>
<% end %> <% end %>

7
app/views/form/review.html.erb

@ -19,7 +19,12 @@
<h3 class="govuk-summary-card__title"><%= subsection.label %></h3> <h3 class="govuk-summary-card__title"><%= subsection.label %></h3>
</div> </div>
<div class="govuk-summary-card__content"> <div class="govuk-summary-card__content">
<%= render partial: "form/check_answers_summary_list", locals: { subsection:, questions: total_applicable_questions(subsection, @log, current_user), referrer: "check_answers" } %> <%= render partial: "form/check_answers_summary_list", locals: {
subsection:,
questions: total_applicable_questions(subsection, @log, current_user),
referrer: "check_answers",
referrer_unanswered: "check_answers_new_answer",
} %>
</div> </div>
</div> </div>
<% end %> <% end %>

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

@ -47,7 +47,7 @@
</div> </div>
</div> </div>
<% if LocationPolicy.new(current_user, @location).deactivate? %> <% if @location.scheme.owning_organisation.active? && LocationPolicy.new(current_user, @location).deactivate? %>
<%= toggle_location_link(@location) %> <%= toggle_location_link(@location) %>
<% end %> <% end %>

18
app/views/organisation_relationships/managing_agents.html.erb

@ -5,19 +5,35 @@
<%= render SubNavigationComponent.new( <%= render SubNavigationComponent.new(
items: secondary_items(request.path, @organisation.id), items: secondary_items(request.path, @organisation.id),
) %> ) %>
<% if !@organisation.active? %>
<%= govuk_notification_banner(title_text: "Important") do %>
<p class="govuk-notification-banner__heading govuk-!-width-full" style="max-width: fit-content">
This organisation is deactivated.
<p>
You cannot add any new managing agents.
<% end %>
<% end %>
<h2 class="govuk-visually-hidden">Managing Agents</h2> <h2 class="govuk-visually-hidden">Managing Agents</h2>
<p class="govuk-body">A managing agent can submit logs for this organisation.</p> <p class="govuk-body">A managing agent can submit logs for this organisation.</p>
<% if @total_count == 0 %> <% if @total_count == 0 %>
<p class="govuk-body">This organisation does not currently have any managing agents.</p> <p class="govuk-body">This organisation does not currently have any managing agents.</p>
<% end %> <% end %>
<% else %> <% else %>
<% if !@organisation.active? %>
<%= govuk_notification_banner(title_text: "Important") do %>
<p class="govuk-notification-banner__heading govuk-!-width-full" style="max-width: fit-content">
This organisation is deactivated.
<p>
You cannot add any new managing agents.
<% end %>
<% end %>
<%= render partial: "organisations/headings", locals: { main: "Your managing agents", sub: current_user.organisation.name } %> <%= render partial: "organisations/headings", locals: { main: "Your managing agents", sub: current_user.organisation.name } %>
<p class="govuk-body">A managing agent can submit logs for this organisation.</p> <p class="govuk-body">A managing agent can submit logs for this organisation.</p>
<% if @total_count == 0 %> <% if @total_count == 0 %>
<p class="govuk-body">This organisation does not currently have any managing agents.</p> <p class="govuk-body">This organisation does not currently have any managing agents.</p>
<% end %> <% end %>
<% end %> <% end %>
<% if current_user.support? || current_user.data_coordinator? %> <% if (current_user.support? || current_user.data_coordinator?) && @organisation.active? %>
<%= govuk_button_link_to "Add a managing agent", managing_agents_add_organisation_path, html: { method: :get } %> <%= govuk_button_link_to "Add a managing agent", managing_agents_add_organisation_path, html: { method: :get } %>
<% end %> <% end %>
<% if @total_count != 0 %> <% if @total_count != 0 %>

18
app/views/organisation_relationships/stock_owners.html.erb

@ -2,19 +2,35 @@
<% if current_user.support? %> <% if current_user.support? %>
<%= render partial: "organisations/headings", locals: { main: @organisation.name, sub: nil } %> <%= render partial: "organisations/headings", locals: { main: @organisation.name, sub: nil } %>
<%= render SubNavigationComponent.new(items: secondary_items(request.path, @organisation.id)) %> <%= render SubNavigationComponent.new(items: secondary_items(request.path, @organisation.id)) %>
<% if !@organisation.active? %>
<%= govuk_notification_banner(title_text: "Important") do %>
<p class="govuk-notification-banner__heading govuk-!-width-full" style="max-width: fit-content">
This organisation is deactivated.
<p>
You cannot add any new stock owners.
<% end %>
<% end %>
<h2 class="govuk-visually-hidden">Stock Owners</h2> <h2 class="govuk-visually-hidden">Stock Owners</h2>
<p class="govuk-body">This organisation can submit logs for its stock owners.</p> <p class="govuk-body">This organisation can submit logs for its stock owners.</p>
<% if @total_count == 0 %> <% if @total_count == 0 %>
<p class="govuk-body">This organisation does not currently have any stock owners.</p> <p class="govuk-body">This organisation does not currently have any stock owners.</p>
<% end %> <% end %>
<% else %> <% else %>
<% if !@organisation.active? %>
<%= govuk_notification_banner(title_text: "Important") do %>
<p class="govuk-notification-banner__heading govuk-!-width-full" style="max-width: fit-content">
This organisation is deactivated.
<p>
You cannot add any new stock owners.
<% end %>
<% end %>
<%= render partial: "organisations/headings", locals: { main: "Your stock owners", sub: current_user.organisation.name } %> <%= render partial: "organisations/headings", locals: { main: "Your stock owners", sub: current_user.organisation.name } %>
<p class="govuk-body">Your organisation can submit logs for its stock owners.</p> <p class="govuk-body">Your organisation can submit logs for its stock owners.</p>
<% if @total_count == 0 %> <% if @total_count == 0 %>
<p class="govuk-body">You do not currently have any stock owners.</p> <p class="govuk-body">You do not currently have any stock owners.</p>
<% end %> <% end %>
<% end %> <% end %>
<% if current_user.support? || current_user.data_coordinator? %> <% if (current_user.support? || current_user.data_coordinator?) && @organisation.active? %>
<%= govuk_button_link_to "Add a stock owner", stock_owners_add_organisation_path, html: { method: :get } %> <%= govuk_button_link_to "Add a stock owner", stock_owners_add_organisation_path, html: { method: :get } %>
<% end %> <% end %>
<% if @total_count != 0 %> <% if @total_count != 0 %>

9
app/views/organisations/show.html.erb

@ -40,3 +40,12 @@
<%= render partial: "organisations/merged_organisation_details" %> <%= render partial: "organisations/merged_organisation_details" %>
</div> </div>
</div> </div>
<% if OrganisationPolicy.new(current_user, @organisation).deactivate? %>
<%= govuk_button_link_to "Deactivate this organisation", deactivate_organisation_path(@organisation), warning: true %>
<% end %>
<% if OrganisationPolicy.new(current_user, @organisation).reactivate? %>
<span class="app-!-colour-muted govuk-!-margin-right-2">
<%= govuk_button_link_to "Reactivate this organisation", reactivate_organisation_path(@organisation) %>
</span>
<% end %>

29
app/views/organisations/toggle_active.html.erb

@ -0,0 +1,29 @@
<% title = "#{action.humanize} #{@organisation.name}" %>
<% content_for :title, title %>
<% content_for :before_content do %>
<%= govuk_back_link(
href: organisation_path(@organisation),
) %>
<% end %>
<%= form_for(@organisation, as: :organisation, html: { method: :patch }) do |f| %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<h1 class="govuk-heading-l">
<span class="govuk-caption-l"><%= @organisation.name %></span>
Are you sure you want to <%= action %> this organisation?
</h1>
<%= govuk_warning_text text: I18n.t("warnings.organisation.#{action}") %>
<% active_value = action != "deactivate" %>
<%= f.hidden_field :active, value: active_value %>
<%= f.govuk_submit "#{action.capitalize} this organisation" %>
<p class="govuk-body">
<%= govuk_link_to("Cancel", organisation_path(@organisation)) %>
</p>
</div>
</div>
<% end %>

2
app/views/schemes/confirm_secondary.html.erb

@ -2,7 +2,7 @@
<% content_for :before_content do %> <% content_for :before_content do %>
<%= govuk_back_link( <%= govuk_back_link(
href: request.query_parameters["check_answers"] ? "/schemes/#{@scheme.id}/check-answers" : "/schemes/#{@scheme.id}/primary-client-group", href: request.query_parameters["check_answers"] ? "check-answers" : "primary-client-group",
) %> ) %>
<% end %> <% end %>

2
app/views/schemes/secondary_client_group.html.erb

@ -2,7 +2,7 @@
<% content_for :before_content do %> <% content_for :before_content do %>
<%= govuk_back_link( <%= govuk_back_link(
href: request.query_parameters["check_answers"] ? "/schemes/#{@scheme.id}/check-answers" : "/schemes/#{@scheme.id}/confirm-secondary-client-group", href: request.query_parameters["check_answers"] ? "check-answers" : "confirm-secondary-client-group",
) %> ) %>
<% end %> <% end %>

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

@ -49,7 +49,7 @@
</div> </div>
</div> </div>
<% if SchemePolicy.new(current_user, @scheme).deactivate? %> <% if @scheme.owning_organisation.active? && SchemePolicy.new(current_user, @scheme).deactivate? %>
<%= toggle_scheme_link(@scheme) %> <%= toggle_scheme_link(@scheme) %>
<% end %> <% end %>

2
app/views/schemes/support.html.erb

@ -2,7 +2,7 @@
<% content_for :before_content do %> <% content_for :before_content do %>
<%= govuk_back_link( <%= govuk_back_link(
href: request.query_parameters["check_answers"] ? "/schemes/#{@scheme.id}/check-answers" : "/schemes/#{@scheme.id}/secondary-client-group", href: request.query_parameters["check_answers"] ? "check-answers" : "secondary-client-group",
) %> ) %>
<% end %> <% end %>

2
app/views/users/new.html.erb

@ -31,7 +31,7 @@
<% if current_user.support? %> <% if current_user.support? %>
<% null_option = [OpenStruct.new(id: "", name: "Select an option")] %> <% null_option = [OpenStruct.new(id: "", name: "Select an option")] %>
<% organisations = Organisation.all.map { |org| OpenStruct.new(id: org.id, name: org.name) } %> <% organisations = Organisation.filter_by_active.map { |org| OpenStruct.new(id: org.id, name: org.name) } %>
<% answer_options = null_option + organisations %> <% answer_options = null_option + organisations %>
<% if @organisation_id %> <% if @organisation_id %>

4
app/views/users/show.html.erb

@ -127,8 +127,8 @@
end %> end %>
<% end %> <% end %>
<div class="govuk-button-group"> <% if @user.organisation.active? && current_user.can_toggle_active?(@user) %>
<% if current_user.can_toggle_active?(@user) %> <div class="govuk-button-group">
<% if @user.active? %> <% if @user.active? %>
<%= govuk_button_link_to "Deactivate user", deactivate_user_path(@user), warning: true %> <%= govuk_button_link_to "Deactivate user", deactivate_user_path(@user), warning: true %>
<% if current_user.support? && @user.last_sign_in_at.nil? %> <% if current_user.support? && @user.last_sign_in_at.nil? %>

7
config/locales/en.yml

@ -34,6 +34,8 @@ en:
feedback_form: "https://forms.office.com/Pages/ResponsePage.aspx?id=EGg0v32c3kOociSi7zmVqC4YDsCJ3llAvEZelBFBLUBURFVUTzFDTUJPQlM4M0laTE5DTlNFSjJBQi4u" feedback_form: "https://forms.office.com/Pages/ResponsePage.aspx?id=EGg0v32c3kOociSi7zmVqC4YDsCJ3llAvEZelBFBLUBURFVUTzFDTUJPQlM4M0laTE5DTlNFSjJBQi4u"
organisation: organisation:
updated: "Organisation details updated" updated: "Organisation details updated"
reactivated: "%{organisation} has been reactivated."
deactivated: "%{organisation} has been deactivated."
user: user:
create_password: "Create a password to finish setting up your account" create_password: "Create a password to finish setting up your account"
reset_password: "Reset your password" reset_password: "Reset your password"
@ -531,8 +533,6 @@ en:
la_general_needs: la_general_needs:
internal_transfer: "Answer cannot be internal transfer as it’s the same landlord on the tenancy agreement and the household had either a fixed-term or lifetime local authority general needs tenancy immediately before this letting" internal_transfer: "Answer cannot be internal transfer as it’s the same landlord on the tenancy agreement and the household had either a fixed-term or lifetime local authority general needs tenancy immediately before this letting"
prp_referred_by_la: "The source of the referral cannot be referred by local authority housing department for a general needs log" prp_referred_by_la: "The source of the referral cannot be referred by local authority housing department for a general needs log"
prp:
local_housing_referral: "Answer cannot be ‘nominated by a local housing authority’ as a local authority is on the tenancy agreement"
homeless: homeless:
assessed: assessed:
internal_transfer: "Answer cannot be 'assessed as homeless' as this tenancy is an internal transfer" internal_transfer: "Answer cannot be 'assessed as homeless' as this tenancy is an internal transfer"
@ -860,6 +860,9 @@ Make sure these answers are correct."
offered: "Times previously offered since becoming available" offered: "Times previously offered since becoming available"
warnings: warnings:
organisation:
deactivate: "All schemes and users at this organisation will be deactivated. All the organisation's relationships will be removed. It will no longer be possible to create logs for this organisation."
reactivate: "All schemes, users, and relationships that were active when this organisation was deactivated will be reactivated."
location: location:
deactivate: deactivate:
existing_logs: "It will not be possible to add logs with this location if their tenancy start date is on or after the date you enter. Any existing logs may be affected." existing_logs: "It will not be possible to add logs with this location if their tenancy start date is on or after the date you enter. Any existing logs may be affected."

2
config/routes.rb

@ -179,6 +179,8 @@ Rails.application.routes.draw do
post "managing-agents", to: "organisation_relationships#create_managing_agent" post "managing-agents", to: "organisation_relationships#create_managing_agent"
delete "managing-agents", to: "organisation_relationships#delete_managing_agent" delete "managing-agents", to: "organisation_relationships#delete_managing_agent"
get "merge-request", to: "organisations#merge_request" get "merge-request", to: "organisations#merge_request"
get "deactivate", to: "organisations#deactivate"
get "reactivate", to: "organisations#reactivate"
end end
end end

5
db/migrate/20240304112411_add_reactivate_with_organisation_to_users.rb

@ -0,0 +1,5 @@
class AddReactivateWithOrganisationToUsers < ActiveRecord::Migration[7.0]
def change
add_column :users, :reactivate_with_organisation, :boolean
end
end

12
db/migrate/20240305112507_add_default_value_to_organisation_active_field.rb

@ -0,0 +1,12 @@
class AddDefaultValueToOrganisationActiveField < ActiveRecord::Migration[7.0]
def up
change_column :organisations, :active, :boolean, default: true
execute "UPDATE organisations
SET active = true;"
end
def down
change_column :organisations, :active, :boolean
end
end

3
db/schema.rb

@ -453,7 +453,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_03_19_122706) do
t.string "managing_agents_label" t.string "managing_agents_label"
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.boolean "active" t.boolean "active", default: true
t.integer "old_association_type" t.integer "old_association_type"
t.string "software_supplier_id" t.string "software_supplier_id"
t.string "housing_management_system" t.string "housing_management_system"
@ -764,6 +764,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_03_19_122706) do
t.string "unconfirmed_email" t.string "unconfirmed_email"
t.boolean "initial_confirmation_sent" t.boolean "initial_confirmation_sent"
t.datetime "discarded_at" t.datetime "discarded_at"
t.boolean "reactivate_with_organisation"
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
t.index ["email"], name: "index_users_on_email", unique: true t.index ["email"], name: "index_users_on_email", unique: true
t.index ["encrypted_otp_secret_key"], name: "index_users_on_encrypted_otp_secret_key", unique: true t.index ["encrypted_otp_secret_key"], name: "index_users_on_encrypted_otp_secret_key", unique: true

2
lib/tasks/data_export.rake

@ -9,7 +9,7 @@ namespace :core do
desc "Export all data XMLs for import into Central Data System (CDS)" desc "Export all data XMLs for import into Central Data System (CDS)"
task :full_data_export_xml, %i[year] => :environment do |_task, args| task :full_data_export_xml, %i[year] => :environment do |_task, args|
collection_year = args[:year].present? ? args[:year].to_i : nil collection_year = args[:year].present? ? args[:year].to_i : nil
storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["EXPORT_PAAS_INSTANCE"]) storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["EXPORT_BUCKET"])
export_service = Exports::LettingsLogExportService.new(storage_service) export_service = Exports::LettingsLogExportService.new(storage_service)
export_service.export_xml_lettings_logs(full_update: true, collection_year:) export_service.export_xml_lettings_logs(full_update: true, collection_year:)

4
lib/tasks/import_address_from_csv.rake

@ -5,7 +5,7 @@ namespace :data_import do
raise "Usage: rake data_import:import_lettings_addresses_from_csv['csv_file_name']" if file_name.blank? raise "Usage: rake data_import:import_lettings_addresses_from_csv['csv_file_name']" if file_name.blank?
s3_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["CSV_DOWNLOAD_PAAS_INSTANCE"]) s3_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["BULK_UPLOAD_BUCKET"])
file_io = s3_service.get_file_io(file_name) file_io = s3_service.get_file_io(file_name)
file_io.set_encoding_by_bom file_io.set_encoding_by_bom
addresses_csv = CSV.parse(file_io, headers: true) addresses_csv = CSV.parse(file_io, headers: true)
@ -69,7 +69,7 @@ namespace :data_import do
raise "Usage: rake data_import:import_sales_addresses_from_csv['csv_file_name']" if file_name.blank? raise "Usage: rake data_import:import_sales_addresses_from_csv['csv_file_name']" if file_name.blank?
s3_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["CSV_DOWNLOAD_PAAS_INSTANCE"]) s3_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["BULK_UPLOAD_BUCKET"])
file_io = s3_service.get_file_io(file_name) file_io = s3_service.get_file_io(file_name)
file_io.set_encoding_by_bom file_io.set_encoding_by_bom
addresses_csv = CSV.parse(file_io, headers: true) addresses_csv = CSV.parse(file_io, headers: true)

4
spec/features/form/check_answers_page_lettings_logs_spec.rb

@ -89,11 +89,11 @@ RSpec.describe "Lettings Log Check Answers Page" do
# Regex explanation: match the string "Answer" but not if it's follow by "the missing questions" # Regex explanation: match the string "Answer" but not if it's follow by "the missing questions"
# This way only the links in the table will get picked up # This way only the links in the table will get picked up
it "has an answer link for questions missing an answer" do it "has an answer link with the check_answers_new_answer referrer for questions missing an answer" do
visit("/lettings-logs/#{empty_lettings_log.id}/#{subsection}/check-answers?referrer=check_answers") visit("/lettings-logs/#{empty_lettings_log.id}/#{subsection}/check-answers?referrer=check_answers")
assert_selector "a", text: /Answer (?!the missing questions)/, count: 4 assert_selector "a", text: /Answer (?!the missing questions)/, count: 4
assert_selector "a", text: "Change", count: 0 assert_selector "a", text: "Change", count: 0
expect(page).to have_link("Answer", href: "/lettings-logs/#{empty_lettings_log.id}/person-1-age?referrer=check_answers") expect(page).to have_link("Answer", href: "/lettings-logs/#{empty_lettings_log.id}/person-1-age?referrer=check_answers_new_answer")
end end
it "has a change link for answered questions" do it "has a change link for answered questions" do

6
spec/features/form/form_navigation_spec.rb

@ -66,12 +66,12 @@ RSpec.describe "Form Navigation" do
expect(page).to have_field("lettings-log-age1-field") expect(page).to have_field("lettings-log-age1-field")
end end
it "a question page leads to the next question defined in the form definition" do it "a question page leads to the next unanswered question defined in the form definition" do
pages = question_answers.map { |_key, val| val[:path] } pages = question_answers.map { |_key, val| val[:path] }
pages[0..-2].each_with_index do |val, index| pages[0..-2].each_with_index do |val, index|
visit("/lettings-logs/#{id}/#{val}") visit("/lettings-logs/#{empty_lettings_log.id}/#{val}")
click_link("Skip for now") click_link("Skip for now")
expect(page).to have_current_path("/lettings-logs/#{id}/#{pages[index + 1]}") expect(page).to have_current_path("/lettings-logs/#{empty_lettings_log.id}/#{pages[index + 1]}")
end end
end end

10
spec/features/form/validations_spec.rb

@ -171,15 +171,17 @@ RSpec.describe "validations" do
expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success") expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success")
end end
it "returns the user back to the check_your_answers after fixing a validation from check_your_anwers" do it "returns the user back to the check_your_answers after fixing a validation from check_your_answers" do
lettings_log.update!(earnings: income_over_soft_limit, incfreq: 1) lettings_log.update!(earnings: income_under_soft_limit, incfreq: 1, net_income_value_check: 1)
visit("/lettings-logs/#{lettings_log.id}/income-and-benefits/check-answers") visit("/lettings-logs/#{lettings_log.id}/income-and-benefits/check-answers")
click_link("Answer", href: "/lettings-logs/#{lettings_log.id}/net-income-value-check?referrer=check_answers") click_link("Change", href: "/lettings-logs/#{lettings_log.id}/net-income?referrer=check_answers", match: :first)
expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income?referrer=check_answers")
fill_in("lettings-log-earnings-field", with: income_over_soft_limit)
click_button("Save changes")
expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-value-check?referrer=check_answers") expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-value-check?referrer=check_answers")
click_link("Change", href: "/lettings-logs/#{lettings_log.id}/net-income?referrer=interruption_screen", match: :first) click_link("Change", href: "/lettings-logs/#{lettings_log.id}/net-income?referrer=interruption_screen", match: :first)
expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income?referrer=interruption_screen") expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income?referrer=interruption_screen")
fill_in("lettings-log-earnings-field", with: income_under_soft_limit) fill_in("lettings-log-earnings-field", with: income_under_soft_limit)
choose("lettings-log-incfreq-1-field", allow_label_click: true)
click_button("Save and continue") click_button("Save and continue")
expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-value-check?referrer=check_answers") expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-value-check?referrer=check_answers")
click_button("Confirm and continue") click_button("Confirm and continue")

2
spec/fixtures/files/sales_logs_csv_export_labels_23.csv vendored

@ -1,2 +1,2 @@
id,status,duplicate_set_id,created_at,updated_at,old_form_id,collection_start_year,creation_method,is_dpo,address_line1_as_entered,address_line2_as_entered,town_or_city_as_entered,county_as_entered,postcode_full_as_entered,la_as_entered,assigned_to,owning_organisation_name,managing_organisation_name,created_by,day,month,year,purchid,ownershipsch,type,othtype,companybuy,buylivein,jointpur,jointmore,beds,proptype,builtype,pcodenk,uprn,uprn_confirmed,address_line1_input,postcode_full_input,uprn_selection,address_line1,address_line2,town_or_city,county,pcode1,pcode2,la_known,la,la_label,wchair,noint,privacynotice,age1,sex1,ethnic_group,ethnic,nationality_all,national,ecstat1,buy1livein,relat2,age2,sex2,ethnic_group2,ethnicbuy2,nationality_all_buyer2,nationalbuy2,ecstat2,buy2livein,hholdcount,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,prevten,ppcodenk,ppostc1,ppostc2,previous_la_known,prevloc,prevloc_label,pregyrha,pregother,pregla,pregghb,pregblank,buy2living,prevtenbuy2,hhregres,hhregresstill,armedforcesspouse,disabled,wheel,income1nk,income1,inc1mort,income2nk,income2,inc2mort,hb,savingsnk,savings,prevown,prevshared,proplen,staircase,stairbought,stairowned,staircasesale,resale,exday,exmonth,exyear,hoday,homonth,hoyear,lanomagr,soctenant,frombeds,fromprop,socprevten,value,equity,mortgageused,mortgage,mortgagelender,mortgagelenderother,mortlen,extrabor,deposit,cashdis,mrent,has_mscharge,mscharge,discount,grant id,status,duplicate_set_id,created_at,updated_at,old_form_id,collection_start_year,creation_method,is_dpo,address_line1_as_entered,address_line2_as_entered,town_or_city_as_entered,county_as_entered,postcode_full_as_entered,la_as_entered,assigned_to,owning_organisation_name,managing_organisation_name,created_by,day,month,year,purchid,ownershipsch,type,othtype,companybuy,buylivein,jointpur,jointmore,beds,proptype,builtype,pcodenk,uprn,uprn_confirmed,address_line1_input,postcode_full_input,uprn_selection,address_line1,address_line2,town_or_city,county,pcode1,pcode2,la_known,la,la_label,wchair,noint,privacynotice,age1,sex1,ethnic_group,ethnic,nationality_all,national,ecstat1,buy1livein,relat2,age2,sex2,ethnic_group2,ethnicbuy2,nationality_all_buyer2,nationalbuy2,ecstat2,buy2livein,hholdcount,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,prevten,ppcodenk,ppostc1,ppostc2,previous_la_known,prevloc,prevloc_label,pregyrha,pregother,pregla,pregghb,pregblank,buy2living,prevtenbuy2,hhregres,hhregresstill,armedforcesspouse,disabled,wheel,income1nk,income1,inc1mort,income2nk,income2,inc2mort,hb,savingsnk,savings,prevown,prevshared,proplen,staircase,stairbought,stairowned,staircasesale,resale,exday,exmonth,exyear,hoday,homonth,hoyear,lanomagr,soctenant,frombeds,fromprop,socprevten,value,equity,mortgageused,mortgage,mortgagelender,mortgagelenderother,mortlen,extrabor,deposit,cashdis,mrent,has_mscharge,mscharge,discount,grant
,completed,,2023-12-08T00:00:00+00:00,2024-01-01T00:00:00+00:00,,2023,single log,false,address line 1 as entered,address line 2 as entered,town or city as entered,county as entered,AB1 2CD,la as entered,,DLUHC,DLUHC,billyboy@eyeklaud.com,8,12,2023,,Yes - a discounted ownership scheme,Right to Acquire (RTA),,,,Yes,Yes,2,Flat or maisonette,Purpose built,0,,,,,,Address line 1,,Town or city,,SW1A,1AA,1,E09000003,Barnet,Yes,Yes,1,30,Non-binary,Buyer prefers not to say,17,,United Kingdom,Full-time - 30 hours or more,Yes,Partner,35,Non-binary,Buyer prefers not to say,,,Buyer prefers not to say,Full-time - 30 hours or more,Yes,3,Child,14,Non-binary,,Other,Not known,Non-binary,"In government training into work, such as New Deal",Prefers not to say,Not known,Prefers not to say,Prefers not to say,,,,,Local authority tenant,No,,,No,,,1,1,1,1,,Don't know,,Yes,Yes,No,Yes,Yes,Yes,10000,Yes,Yes,10000,Yes,"Don’t know ",No,,Yes,No,10,,,,,,,,,,,,,,,,,110000.0,,Yes,20000.0,Cambridge Building Society,,10,Yes,80000.0,,,Yes,100.0,,10000.0 ,completed,,2023-12-08T00:00:00+00:00,2024-01-01T00:00:00+00:00,,2023,single log,false,address line 1 as entered,address line 2 as entered,town or city as entered,county as entered,AB1 2CD,la as entered,,DLUHC,DLUHC,billyboy@eyeklaud.com,8,12,2023,,Yes - a discounted ownership scheme,Right to Acquire (RTA),,,,Yes,Yes,2,Flat or maisonette,Purpose built,0,,,,,,Address line 1,,Town or city,,SW1A,1AA,1,E09000003,Barnet,Yes,Yes,1,30,Non-binary,Buyer prefers not to say,17,,United Kingdom,Full-time - 30 hours or more,Yes,Partner,35,Non-binary,Buyer prefers not to say,,,Buyer prefers not to say,Full-time - 30 hours or more,Yes,3,Child,14,Non-binary,,Other,Not known,Non-binary,In government training into work,Prefers not to say,Not known,Prefers not to say,Prefers not to say,,,,,Local authority tenant,No,,,No,,,1,1,1,1,,Don't know,,Yes,Yes,No,Yes,Yes,Yes,10000,Yes,Yes,10000,Yes,"Don’t know ",No,,Yes,No,10,,,,,,,,,,,,,,,,,110000.0,,Yes,20000.0,Cambridge Building Society,,10,Yes,80000.0,,,Yes,100.0,,10000.0

1 id status duplicate_set_id created_at updated_at old_form_id collection_start_year creation_method is_dpo address_line1_as_entered address_line2_as_entered town_or_city_as_entered county_as_entered postcode_full_as_entered la_as_entered assigned_to owning_organisation_name managing_organisation_name created_by day month year purchid ownershipsch type othtype companybuy buylivein jointpur jointmore beds proptype builtype pcodenk uprn uprn_confirmed address_line1_input postcode_full_input uprn_selection address_line1 address_line2 town_or_city county pcode1 pcode2 la_known la la_label wchair noint privacynotice age1 sex1 ethnic_group ethnic nationality_all national ecstat1 buy1livein relat2 age2 sex2 ethnic_group2 ethnicbuy2 nationality_all_buyer2 nationalbuy2 ecstat2 buy2livein hholdcount relat3 age3 sex3 ecstat3 relat4 age4 sex4 ecstat4 relat5 age5 sex5 ecstat5 relat6 age6 sex6 ecstat6 prevten ppcodenk ppostc1 ppostc2 previous_la_known prevloc prevloc_label pregyrha pregother pregla pregghb pregblank buy2living prevtenbuy2 hhregres hhregresstill armedforcesspouse disabled wheel income1nk income1 inc1mort income2nk income2 inc2mort hb savingsnk savings prevown prevshared proplen staircase stairbought stairowned staircasesale resale exday exmonth exyear hoday homonth hoyear lanomagr soctenant frombeds fromprop socprevten value equity mortgageused mortgage mortgagelender mortgagelenderother mortlen extrabor deposit cashdis mrent has_mscharge mscharge discount grant
2 completed 2023-12-08T00:00:00+00:00 2024-01-01T00:00:00+00:00 2023 single log false address line 1 as entered address line 2 as entered town or city as entered county as entered AB1 2CD la as entered DLUHC DLUHC billyboy@eyeklaud.com 8 12 2023 Yes - a discounted ownership scheme Right to Acquire (RTA) Yes Yes 2 Flat or maisonette Purpose built 0 Address line 1 Town or city SW1A 1AA 1 E09000003 Barnet Yes Yes 1 30 Non-binary Buyer prefers not to say 17 United Kingdom Full-time - 30 hours or more Yes Partner 35 Non-binary Buyer prefers not to say Buyer prefers not to say Full-time - 30 hours or more Yes 3 Child 14 Non-binary Other Not known Non-binary In government training into work, such as New Deal In government training into work Prefers not to say Not known Prefers not to say Prefers not to say Local authority tenant No No 1 1 1 1 Don't know Yes Yes No Yes Yes Yes 10000 Yes Yes 10000 Yes Don’t know No Yes No 10 110000.0 Yes 20000.0 Cambridge Building Society 10 Yes 80000.0 Yes 100.0 10000.0

2
spec/fixtures/files/sales_logs_csv_export_labels_23_during_24_period.csv vendored

@ -1,2 +1,2 @@
id,status,duplicate_set_id,created_at,updated_at,old_form_id,collection_start_year,creation_method,is_dpo,address_line1_as_entered,address_line2_as_entered,town_or_city_as_entered,county_as_entered,postcode_full_as_entered,la_as_entered,assigned_to,owning_organisation_name,managing_organisation_name,created_by,day,month,year,purchid,ownershipsch,type,othtype,companybuy,buylivein,jointpur,jointmore,noint,privacynotice,uprn,uprn_confirmed,address_line1_input,postcode_full_input,uprn_selection,address_line1,address_line2,town_or_city,county,pcode1,pcode2,la_known,la,la_label,beds,proptype,builtype,pcodenk,wchair,age1,sex1,ethnic_group,ethnic,national,nationality_all,ecstat1,buy1livein,relat2,age2,sex2,ethnic_group2,ethnicbuy2,nationalbuy2,nationality_all_buyer2,ecstat2,buy2livein,hholdcount,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,prevten,ppcodenk,ppostc1,ppostc2,previous_la_known,prevloc,prevloc_label,pregyrha,pregother,pregla,pregghb,pregblank,buy2living,prevtenbuy2,hhregres,hhregresstill,armedforcesspouse,disabled,wheel,income1nk,income1,inc1mort,income2nk,income2,inc2mort,hb,savingsnk,savings,prevown,prevshared,proplen,staircase,stairbought,stairowned,staircasesale,resale,exday,exmonth,exyear,hoday,homonth,hoyear,lanomagr,soctenant,frombeds,fromprop,socprevten,value,equity,mortgageused,mortgage,mortgagelender,mortgagelenderother,mortlen,extrabor,deposit,cashdis,mrent,has_mscharge,mscharge,discount,grant id,status,duplicate_set_id,created_at,updated_at,old_form_id,collection_start_year,creation_method,is_dpo,address_line1_as_entered,address_line2_as_entered,town_or_city_as_entered,county_as_entered,postcode_full_as_entered,la_as_entered,assigned_to,owning_organisation_name,managing_organisation_name,created_by,day,month,year,purchid,ownershipsch,type,othtype,companybuy,buylivein,jointpur,jointmore,noint,privacynotice,uprn,uprn_confirmed,address_line1_input,postcode_full_input,uprn_selection,address_line1,address_line2,town_or_city,county,pcode1,pcode2,la_known,la,la_label,beds,proptype,builtype,pcodenk,wchair,age1,sex1,ethnic_group,ethnic,national,nationality_all,ecstat1,buy1livein,relat2,age2,sex2,ethnic_group2,ethnicbuy2,nationalbuy2,nationality_all_buyer2,ecstat2,buy2livein,hholdcount,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,prevten,ppcodenk,ppostc1,ppostc2,previous_la_known,prevloc,prevloc_label,pregyrha,pregother,pregla,pregghb,pregblank,buy2living,prevtenbuy2,hhregres,hhregresstill,armedforcesspouse,disabled,wheel,income1nk,income1,inc1mort,income2nk,income2,inc2mort,hb,savingsnk,savings,prevown,prevshared,proplen,staircase,stairbought,stairowned,staircasesale,resale,exday,exmonth,exyear,hoday,homonth,hoyear,lanomagr,soctenant,frombeds,fromprop,socprevten,value,equity,mortgageused,mortgage,mortgagelender,mortgagelenderother,mortlen,extrabor,deposit,cashdis,mrent,has_mscharge,mscharge,discount,grant
,completed,,2023-12-08T00:00:00+00:00,2024-05-01T00:00:00+01:00,,2023,single log,false,address line 1 as entered,address line 2 as entered,town or city as entered,county as entered,AB1 2CD,la as entered,,DLUHC,DLUHC,billyboy@eyeklaud.com,8,12,2023,,Yes - a discounted ownership scheme,Right to Acquire (RTA),,,,Yes,Yes,Yes,1,,,,,,Address line 1,,Town or city,,SW1A,1AA,1,E09000003,Barnet,2,Flat or maisonette,Purpose built,0,Yes,30,Non-binary,Buyer prefers not to say,17,United Kingdom,,Full-time - 30 hours or more,Yes,Partner,35,Non-binary,Buyer prefers not to say,,Buyer prefers not to say,,Full-time - 30 hours or more,Yes,3,Child,14,Non-binary,,Other,Not known,Non-binary,"In government training into work, such as New Deal",Prefers not to say,Not known,Prefers not to say,Prefers not to say,,,,,Local authority tenant,No,,,No,,,1,1,1,1,,Don't know,,Yes,Yes,No,Yes,Yes,Yes,10000,Yes,Yes,10000,Yes,"Don’t know ",No,,Yes,No,10,,,,,,,,,,,,,,,,,110000.0,,Yes,20000.0,Cambridge Building Society,,10,Yes,80000.0,,,Yes,100.0,,10000.0 ,completed,,2023-12-08T00:00:00+00:00,2024-05-01T00:00:00+01:00,,2023,single log,false,address line 1 as entered,address line 2 as entered,town or city as entered,county as entered,AB1 2CD,la as entered,,DLUHC,DLUHC,billyboy@eyeklaud.com,8,12,2023,,Yes - a discounted ownership scheme,Right to Acquire (RTA),,,,Yes,Yes,Yes,1,,,,,,Address line 1,,Town or city,,SW1A,1AA,1,E09000003,Barnet,2,Flat or maisonette,Purpose built,0,Yes,30,Non-binary,Buyer prefers not to say,17,United Kingdom,,Full-time - 30 hours or more,Yes,Partner,35,Non-binary,Buyer prefers not to say,,Buyer prefers not to say,,Full-time - 30 hours or more,Yes,3,Child,14,Non-binary,,Other,Not known,Non-binary,In government training into work,Prefers not to say,Not known,Prefers not to say,Prefers not to say,,,,,Local authority tenant,No,,,No,,,1,1,1,1,,Don't know,,Yes,Yes,No,Yes,Yes,Yes,10000,Yes,Yes,10000,Yes,"Don’t know ",No,,Yes,No,10,,,,,,,,,,,,,,,,,110000.0,,Yes,20000.0,Cambridge Building Society,,10,Yes,80000.0,,,Yes,100.0,,10000.0

1 id status duplicate_set_id created_at updated_at old_form_id collection_start_year creation_method is_dpo address_line1_as_entered address_line2_as_entered town_or_city_as_entered county_as_entered postcode_full_as_entered la_as_entered assigned_to owning_organisation_name managing_organisation_name created_by day month year purchid ownershipsch type othtype companybuy buylivein jointpur jointmore noint privacynotice uprn uprn_confirmed address_line1_input postcode_full_input uprn_selection address_line1 address_line2 town_or_city county pcode1 pcode2 la_known la la_label beds proptype builtype pcodenk wchair age1 sex1 ethnic_group ethnic national nationality_all ecstat1 buy1livein relat2 age2 sex2 ethnic_group2 ethnicbuy2 nationalbuy2 nationality_all_buyer2 ecstat2 buy2livein hholdcount relat3 age3 sex3 ecstat3 relat4 age4 sex4 ecstat4 relat5 age5 sex5 ecstat5 relat6 age6 sex6 ecstat6 prevten ppcodenk ppostc1 ppostc2 previous_la_known prevloc prevloc_label pregyrha pregother pregla pregghb pregblank buy2living prevtenbuy2 hhregres hhregresstill armedforcesspouse disabled wheel income1nk income1 inc1mort income2nk income2 inc2mort hb savingsnk savings prevown prevshared proplen staircase stairbought stairowned staircasesale resale exday exmonth exyear hoday homonth hoyear lanomagr soctenant frombeds fromprop socprevten value equity mortgageused mortgage mortgagelender mortgagelenderother mortlen extrabor deposit cashdis mrent has_mscharge mscharge discount grant
2 completed 2023-12-08T00:00:00+00:00 2024-05-01T00:00:00+01:00 2023 single log false address line 1 as entered address line 2 as entered town or city as entered county as entered AB1 2CD la as entered DLUHC DLUHC billyboy@eyeklaud.com 8 12 2023 Yes - a discounted ownership scheme Right to Acquire (RTA) Yes Yes Yes 1 Address line 1 Town or city SW1A 1AA 1 E09000003 Barnet 2 Flat or maisonette Purpose built 0 Yes 30 Non-binary Buyer prefers not to say 17 United Kingdom Full-time - 30 hours or more Yes Partner 35 Non-binary Buyer prefers not to say Buyer prefers not to say Full-time - 30 hours or more Yes 3 Child 14 Non-binary Other Not known Non-binary In government training into work, such as New Deal In government training into work Prefers not to say Not known Prefers not to say Prefers not to say Local authority tenant No No 1 1 1 1 Don't know Yes Yes No Yes Yes Yes 10000 Yes Yes 10000 Yes Don’t know No Yes No 10 110000.0 Yes 20000.0 Cambridge Building Society 10 Yes 80000.0 Yes 100.0 10000.0

2
spec/fixtures/files/sales_logs_csv_export_labels_24.csv vendored

@ -1,2 +1,2 @@
id,status,duplicate_set_id,created_at,updated_at,old_form_id,collection_start_year,creation_method,is_dpo,address_line1_as_entered,address_line2_as_entered,town_or_city_as_entered,county_as_entered,postcode_full_as_entered,la_as_entered,assigned_to,owning_organisation_name,managing_organisation_name,created_by,day,month,year,purchid,ownershipsch,type,othtype,companybuy,buylivein,jointpur,jointmore,noint,privacynotice,uprn,uprn_confirmed,address_line1_input,postcode_full_input,uprn_selection,address_line1,address_line2,town_or_city,county,pcode1,pcode2,la_known,la,la_label,beds,proptype,builtype,pcodenk,wchair,age1,sex1,ethnic_group,ethnic,national,nationality_all,ecstat1,buy1livein,relat2,age2,sex2,ethnic_group2,ethnicbuy2,nationalbuy2,nationality_all_buyer2,ecstat2,buy2livein,hholdcount,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,prevten,ppcodenk,ppostc1,ppostc2,previous_la_known,prevloc,prevloc_label,pregyrha,pregother,pregla,pregghb,pregblank,buy2living,prevtenbuy2,hhregres,hhregresstill,armedforcesspouse,disabled,wheel,income1nk,income1,inc1mort,income2nk,income2,inc2mort,hb,savingsnk,savings,prevown,prevshared,proplen,staircase,stairbought,stairowned,staircasesale,resale,exday,exmonth,exyear,hoday,homonth,hoyear,lanomagr,soctenant,frombeds,fromprop,socprevten,value,equity,mortgageused,mortgage,mortgagelender,mortgagelenderother,mortlen,extrabor,deposit,cashdis,mrent,has_mscharge,mscharge,discount,grant id,status,duplicate_set_id,created_at,updated_at,old_form_id,collection_start_year,creation_method,is_dpo,address_line1_as_entered,address_line2_as_entered,town_or_city_as_entered,county_as_entered,postcode_full_as_entered,la_as_entered,assigned_to,owning_organisation_name,managing_organisation_name,created_by,day,month,year,purchid,ownershipsch,type,othtype,companybuy,buylivein,jointpur,jointmore,noint,privacynotice,uprn,uprn_confirmed,address_line1_input,postcode_full_input,uprn_selection,address_line1,address_line2,town_or_city,county,pcode1,pcode2,la_known,la,la_label,beds,proptype,builtype,pcodenk,wchair,age1,sex1,ethnic_group,ethnic,national,nationality_all,ecstat1,buy1livein,relat2,age2,sex2,ethnic_group2,ethnicbuy2,nationalbuy2,nationality_all_buyer2,ecstat2,buy2livein,hholdcount,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,prevten,ppcodenk,ppostc1,ppostc2,previous_la_known,prevloc,prevloc_label,pregyrha,pregother,pregla,pregghb,pregblank,buy2living,prevtenbuy2,hhregres,hhregresstill,armedforcesspouse,disabled,wheel,income1nk,income1,inc1mort,income2nk,income2,inc2mort,hb,savingsnk,savings,prevown,prevshared,proplen,staircase,stairbought,stairowned,staircasesale,resale,exday,exmonth,exyear,hoday,homonth,hoyear,lanomagr,soctenant,frombeds,fromprop,socprevten,value,equity,mortgageused,mortgage,mortgagelender,mortgagelenderother,mortlen,extrabor,deposit,cashdis,mrent,has_mscharge,mscharge,discount,grant
,in_progress,,2024-05-01T00:00:00+01:00,2024-05-01T00:00:00+01:00,,2024,single log,false,address line 1 as entered,address line 2 as entered,town or city as entered,county as entered,AB1 2CD,la as entered,,DLUHC,DLUHC,billyboy@eyeklaud.com,1,5,2024,,Yes - a discounted ownership scheme,Right to Acquire (RTA),,,,Yes,Yes,Yes,1,,,,,,Address line 1,,Town or city,,SW1A,1AA,1,E09000003,Barnet,2,Flat or maisonette,Purpose built,0,Yes,30,Non-binary,Buyer prefers not to say,17,,Australia,Full-time - 30 hours or more,Yes,Partner,35,Non-binary,Buyer prefers not to say,,13,,Full-time - 30 hours or more,Yes,3,Child,14,Non-binary,Child under 16,Other,Not known,Non-binary,"In government training into work, such as New Deal",Prefers not to say,Not known,Prefers not to say,Prefers not to say,,,,,Local authority tenant,Yes,SW1A,1AA,Yes,E09000003,Barnet,1,1,1,1,,Don't know,,Yes,Yes,No,Yes,Yes,Yes,10000,Yes,Yes,10000,Yes,"Don’t know ",No,,Yes,No,10,,,,,,,,,,,,,,,,,110000.0,,Yes,20000.0,Cambridge Building Society,,10,Yes,80000.0,,,Yes,100.0,,10000.0 ,in_progress,,2024-05-01T00:00:00+01:00,2024-05-01T00:00:00+01:00,,2024,single log,false,address line 1 as entered,address line 2 as entered,town or city as entered,county as entered,AB1 2CD,la as entered,,DLUHC,DLUHC,billyboy@eyeklaud.com,1,5,2024,,Yes - a discounted ownership scheme,Right to Acquire (RTA),,,,Yes,Yes,Yes,1,,,,,,Address line 1,,Town or city,,SW1A,1AA,1,E09000003,Barnet,2,Flat or maisonette,Purpose built,0,Yes,30,Non-binary,Buyer prefers not to say,17,,Australia,Full-time - 30 hours or more,Yes,Partner,35,Non-binary,Buyer prefers not to say,,13,,Full-time - 30 hours or more,Yes,3,Child,14,Non-binary,Child under 16,Other,Not known,Non-binary,In government training into work,Prefers not to say,Not known,Prefers not to say,Prefers not to say,,,,,Local authority tenant,Yes,SW1A,1AA,Yes,E09000003,Barnet,1,1,1,1,,Don't know,,Yes,Yes,No,Yes,Yes,Yes,10000,Yes,Yes,10000,Yes,"Don’t know ",No,,Yes,No,10,,,,,,,,,,,,,,,,,110000.0,,Yes,20000.0,Cambridge Building Society,,10,Yes,80000.0,,,Yes,100.0,,10000.0

1 id status duplicate_set_id created_at updated_at old_form_id collection_start_year creation_method is_dpo address_line1_as_entered address_line2_as_entered town_or_city_as_entered county_as_entered postcode_full_as_entered la_as_entered assigned_to owning_organisation_name managing_organisation_name created_by day month year purchid ownershipsch type othtype companybuy buylivein jointpur jointmore noint privacynotice uprn uprn_confirmed address_line1_input postcode_full_input uprn_selection address_line1 address_line2 town_or_city county pcode1 pcode2 la_known la la_label beds proptype builtype pcodenk wchair age1 sex1 ethnic_group ethnic national nationality_all ecstat1 buy1livein relat2 age2 sex2 ethnic_group2 ethnicbuy2 nationalbuy2 nationality_all_buyer2 ecstat2 buy2livein hholdcount relat3 age3 sex3 ecstat3 relat4 age4 sex4 ecstat4 relat5 age5 sex5 ecstat5 relat6 age6 sex6 ecstat6 prevten ppcodenk ppostc1 ppostc2 previous_la_known prevloc prevloc_label pregyrha pregother pregla pregghb pregblank buy2living prevtenbuy2 hhregres hhregresstill armedforcesspouse disabled wheel income1nk income1 inc1mort income2nk income2 inc2mort hb savingsnk savings prevown prevshared proplen staircase stairbought stairowned staircasesale resale exday exmonth exyear hoday homonth hoyear lanomagr soctenant frombeds fromprop socprevten value equity mortgageused mortgage mortgagelender mortgagelenderother mortlen extrabor deposit cashdis mrent has_mscharge mscharge discount grant
2 in_progress 2024-05-01T00:00:00+01:00 2024-05-01T00:00:00+01:00 2024 single log false address line 1 as entered address line 2 as entered town or city as entered county as entered AB1 2CD la as entered DLUHC DLUHC billyboy@eyeklaud.com 1 5 2024 Yes - a discounted ownership scheme Right to Acquire (RTA) Yes Yes Yes 1 Address line 1 Town or city SW1A 1AA 1 E09000003 Barnet 2 Flat or maisonette Purpose built 0 Yes 30 Non-binary Buyer prefers not to say 17 Australia Full-time - 30 hours or more Yes Partner 35 Non-binary Buyer prefers not to say 13 Full-time - 30 hours or more Yes 3 Child 14 Non-binary Child under 16 Other Not known Non-binary In government training into work, such as New Deal In government training into work Prefers not to say Not known Prefers not to say Prefers not to say Local authority tenant Yes SW1A 1AA Yes E09000003 Barnet 1 1 1 1 Don't know Yes Yes No Yes Yes Yes 10000 Yes Yes 10000 Yes Don’t know No Yes No 10 110000.0 Yes 20000.0 Cambridge Building Society 10 Yes 80000.0 Yes 100.0 10000.0

2
spec/lib/tasks/correct_address_from_csv_spec.rb

@ -15,7 +15,7 @@ RSpec.describe "data_import" do
allow(Storage::S3Service).to receive(:new).and_return(storage_service) allow(Storage::S3Service).to receive(:new).and_return(storage_service)
allow(Configuration::EnvConfigurationService).to receive(:new).and_return(env_config_service) allow(Configuration::EnvConfigurationService).to receive(:new).and_return(env_config_service)
allow(ENV).to receive(:[]) allow(ENV).to receive(:[])
allow(ENV).to receive(:[]).with("CSV_DOWNLOAD_PAAS_INSTANCE").and_return(instance_name) allow(ENV).to receive(:[]).with("BULK_UPLOAD_BUCKET").and_return(instance_name)
allow(ENV).to receive(:[]).with("VCAP_SERVICES").and_return("dummy") allow(ENV).to receive(:[]).with("VCAP_SERVICES").and_return("dummy")
WebMock.stub_request(:get, /api\.postcodes\.io/) WebMock.stub_request(:get, /api\.postcodes\.io/)

4
spec/lib/tasks/data_export_spec.rb

@ -2,7 +2,7 @@ require "rails_helper"
require "rake" require "rake"
describe "rake core:data_export", type: task do describe "rake core:data_export", type: task do
let(:export_instance) { "export_instance" } let(:export_bucket) { "export_bucket" }
let(:storage_service) { instance_double(Storage::S3Service) } let(:storage_service) { instance_double(Storage::S3Service) }
let(:export_service) { instance_double(Exports::LettingsLogExportService) } let(:export_service) { instance_double(Exports::LettingsLogExportService) }
@ -14,7 +14,7 @@ describe "rake core:data_export", type: task do
allow(Storage::S3Service).to receive(:new).and_return(storage_service) allow(Storage::S3Service).to receive(:new).and_return(storage_service)
allow(Exports::LettingsLogExportService).to receive(:new).and_return(export_service) allow(Exports::LettingsLogExportService).to receive(:new).and_return(export_service)
allow(ENV).to receive(:[]) allow(ENV).to receive(:[])
allow(ENV).to receive(:[]).with("EXPORT_PAAS_INSTANCE").and_return(export_instance) allow(ENV).to receive(:[]).with("EXPORT_BUCKET").and_return(export_bucket)
end end
context "when exporting lettings logs with no parameters" do context "when exporting lettings logs with no parameters" do

2
spec/lib/tasks/update_schemes_and_locations_from_csv_spec.rb

@ -22,7 +22,7 @@ RSpec.describe "bulk_update" do
allow(Storage::S3Service).to receive(:new).and_return(storage_service) allow(Storage::S3Service).to receive(:new).and_return(storage_service)
allow(Configuration::EnvConfigurationService).to receive(:new).and_return(env_config_service) allow(Configuration::EnvConfigurationService).to receive(:new).and_return(env_config_service)
allow(ENV).to receive(:[]) allow(ENV).to receive(:[])
allow(ENV).to receive(:[]).with("CSV_DOWNLOAD_PAAS_INSTANCE").and_return(instance_name) allow(ENV).to receive(:[]).with("BULK_UPLOAD_BUCKET").and_return(instance_name)
WebMock.stub_request(:get, /api\.postcodes\.io/) WebMock.stub_request(:get, /api\.postcodes\.io/)
.to_return(status: 200, body: "{\"status\":404,\"error\":\"Postcode not found\"}", headers: {}) .to_return(status: 200, body: "{\"status\":404,\"error\":\"Postcode not found\"}", headers: {})

2
spec/models/form/lettings/pages/uprn_selection_spec.rb

@ -34,7 +34,7 @@ RSpec.describe Form::Lettings::Pages::UprnSelection, type: :model do
it "has the correct skip_href" do it "has the correct skip_href" do
expect(page.skip_href(log)).to eq( expect(page.skip_href(log)).to eq(
"/lettings-logs/#{log.id}/address-matcher", "address-matcher",
) )
end end

4
spec/models/form/lettings/pages/uprn_spec.rb

@ -50,7 +50,7 @@ RSpec.describe Form::Lettings::Pages::Uprn, type: :model do
context "with 2023/24 form" do context "with 2023/24 form" do
it "points to address page" do it "points to address page" do
expect(page.skip_href(log)).to eq( expect(page.skip_href(log)).to eq(
"/lettings-logs/#{log.id}/address", "address",
) )
end end
@ -66,7 +66,7 @@ RSpec.describe Form::Lettings::Pages::Uprn, type: :model do
it "points to address search page" do it "points to address search page" do
expect(page.skip_href(log)).to eq( expect(page.skip_href(log)).to eq(
"/lettings-logs/#{log.id}/address-matcher", "address-matcher",
) )
end end

2
spec/models/form/lettings/questions/gender_identity1_spec.rb

@ -6,7 +6,7 @@ RSpec.describe Form::Lettings::Questions::GenderIdentity1, type: :model do
let(:question_definition) { nil } let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) } let(:page) { instance_double(Form::Page) }
let(:subsection) { instance_double(Form::Subsection) } let(:subsection) { instance_double(Form::Subsection) }
let(:form) { instance_double(Form, start_date: Time.zone.local(2023, 4, 1)) } let(:form) { instance_double(Form, start_date: Time.zone.local(2023, 4, 1), start_year_after_2024?: false) }
before do before do
allow(page).to receive(:subsection).and_return(subsection) allow(page).to receive(:subsection).and_return(subsection)

15
spec/models/form/lettings/questions/managing_organisation_spec.rb

@ -57,6 +57,7 @@ RSpec.describe Form::Lettings::Questions::ManagingOrganisation, type: :model do
let(:managing_org1) { create(:organisation, name: "Managing org 1") } let(:managing_org1) { create(:organisation, name: "Managing org 1") }
let(:managing_org2) { create(:organisation, name: "Managing org 2") } let(:managing_org2) { create(:organisation, name: "Managing org 2") }
let(:managing_org3) { create(:organisation, name: "Managing org 3") } let(:managing_org3) { create(:organisation, name: "Managing org 3") }
let(:inactive_managing_org) { create(:organisation, name: "Inactive managing org", active: false) }
let(:log) { create(:lettings_log, managing_organisation: managing_org1) } let(:log) { create(:lettings_log, managing_organisation: managing_org1) }
let!(:org_rel1) do let!(:org_rel1) do
@ -76,7 +77,9 @@ RSpec.describe Form::Lettings::Questions::ManagingOrganisation, type: :model do
} }
end end
it "shows current managing agent at top, followed by user's org (with hint), followed by the managing agents of the user's org" do it "shows current managing agent at top, followed by user's org (with hint), followed by the active managing agents of the user's org" do
create(:organisation_relationship, parent_organisation: user.organisation, child_organisation: inactive_managing_org)
expect(question.displayed_answer_options(log, user)).to eq(options) expect(question.displayed_answer_options(log, user)).to eq(options)
end end
end end
@ -100,6 +103,10 @@ RSpec.describe Form::Lettings::Questions::ManagingOrganisation, type: :model do
create(:organisation_relationship, parent_organisation: log_owning_org, child_organisation: managing_org3) create(:organisation_relationship, parent_organisation: log_owning_org, child_organisation: managing_org3)
end end
before do
create(:organisation, name: "Inactive managing org", active: false)
end
context "when org owns stock" do context "when org owns stock" do
let(:options) do let(:options) do
{ {
@ -111,7 +118,7 @@ RSpec.describe Form::Lettings::Questions::ManagingOrganisation, type: :model do
} }
end end
it "shows current managing agent at top, followed by the current owning organisation (with hint), followed by the managing agents of the current owning organisation" do it "shows current managing agent at top, followed by the current owning organisation (with hint), followed by the active managing agents of the current owning organisation" do
log_owning_org.update!(holds_own_stock: true) log_owning_org.update!(holds_own_stock: true)
expect(question.displayed_answer_options(log, user)).to eq(options) expect(question.displayed_answer_options(log, user)).to eq(options)
end end
@ -133,7 +140,7 @@ RSpec.describe Form::Lettings::Questions::ManagingOrganisation, type: :model do
org_rel2.child_organisation.update!(merge_date: Time.zone.local(2023, 8, 2), absorbing_organisation_id: log_owning_org.id) org_rel2.child_organisation.update!(merge_date: Time.zone.local(2023, 8, 2), absorbing_organisation_id: log_owning_org.id)
end end
it "shows current managing agent at top, followed by the current owning organisation (with hint), followed by the managing agents of the current owning organisation" do it "shows current managing agent at top, followed by the current owning organisation (with hint), followed by the active managing agents of the current owning organisation" do
log_owning_org.update!(holds_own_stock: true) log_owning_org.update!(holds_own_stock: true)
expect(question.displayed_answer_options(log, user)).to eq(options) expect(question.displayed_answer_options(log, user)).to eq(options)
end end
@ -149,7 +156,7 @@ RSpec.describe Form::Lettings::Questions::ManagingOrganisation, type: :model do
} }
end end
it "shows current managing agent at top, followed by the managing agents of the current owning organisation" do it "shows current managing agent at top, followed by the active managing agents of the current owning organisation" do
log_owning_org.update!(holds_own_stock: false) log_owning_org.update!(holds_own_stock: false)
expect(question.displayed_answer_options(log, user)).to eq(options) expect(question.displayed_answer_options(log, user)).to eq(options)
end end

2
spec/models/form/lettings/questions/nationality_all_group_spec.rb

@ -26,7 +26,7 @@ RSpec.describe Form::Lettings::Questions::NationalityAllGroup, type: :model do
end end
it "has the correct hint_text" do it "has the correct hint_text" do
expect(question.hint_text).to eq("The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest. If the lead tenant is a dual national of the United Kingdom and another country, enter United Kingdom. If they are a dual national of two other countries, the tenant should decide which country to enter.") expect(question.hint_text).to eq("If the lead tenant is a dual national of the United Kingdom and another country, enter United Kingdom. If they are a dual national of two other countries, the tenant should decide which country to enter.")
end end
it "has the correct answer_options" do it "has the correct answer_options" do

2
spec/models/form/lettings/questions/person_working_situation_spec.rb

@ -24,7 +24,7 @@ RSpec.describe Form::Lettings::Questions::PersonWorkingSituation, type: :model d
"1" => { "value" => "Full-time – 30 hours or more" }, "1" => { "value" => "Full-time – 30 hours or more" },
"10" => { "value" => "Person prefers not to say" }, "10" => { "value" => "Person prefers not to say" },
"2" => { "value" => "Part-time – Less than 30 hours" }, "2" => { "value" => "Part-time – Less than 30 hours" },
"3" => { "value" => "In government training into work, such as New Deal" }, "3" => { "value" => "In government training into work" },
"4" => { "value" => "Jobseeker" }, "4" => { "value" => "Jobseeker" },
"5" => { "value" => "Retired" }, "5" => { "value" => "Retired" },
"6" => { "value" => "Not seeking work" }, "6" => { "value" => "Not seeking work" },

104
spec/models/form/lettings/questions/referral_prp_spec.rb

@ -0,0 +1,104 @@
require "rails_helper"
RSpec.describe Form::Lettings::Questions::ReferralPrp, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
let(:subsection) { instance_double(Form::Subsection) }
let(:form) { instance_double(Form, start_date: Time.zone.local(2023, 4, 1)) }
before do
allow(form).to receive(:start_year_after_2024?).and_return(false)
allow(page).to receive(:subsection).and_return(subsection)
allow(subsection).to receive(:form).and_return(form)
end
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("referral")
end
it "has the correct header" do
expect(question.header).to eq("What was the source of referral for this letting?")
end
it "has the correct check_answer_label" do
expect(question.check_answer_label).to eq("Source of referral for letting")
end
it "has the correct type" do
expect(question.type).to eq("radio")
end
it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to eq(0)
end
it "has the correct hint" do
expect(question.hint_text).to eq("You told us that the needs type is general needs. We have removed some options because of this.")
end
it "is not marked as derived" do
expect(question).not_to be_derived(nil)
end
context "with 2023/24 form" do
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"1" => { "value" => "Internal transfer", "hint" => "Where the tenant has moved to another social property owned by the same landlord." },
"2" => { "value" => "Tenant applied directly (no referral or nomination)" },
"3" => { "value" => "Nominated by a local housing authority" },
"4" => { "value" => "Referred by local authority housing department" },
"8" => { "value" => "Re-located through official housing mobility scheme" },
"10" => { "value" => "Other social landlord" },
"9" => { "value" => "Community learning disability team" },
"14" => { "value" => "Community mental health team" },
"15" => { "value" => "Health service" },
"12" => { "value" => "Police, probation or prison" },
"7" => { "value" => "Voluntary agency" },
"13" => { "value" => "Youth offending team" },
"17" => { "value" => "Children’s Social Care" },
"16" => { "value" => "Other" },
})
end
it "has the correct question number" do
expect(question.question_number).to eq(85)
end
end
context "with 2024/25 form" do
let(:form) { instance_double(Form, start_date: Time.zone.local(2024, 4, 1)) }
before do
allow(form).to receive(:start_year_after_2024?).and_return(true)
end
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"1" => { "value" => "Internal transfer", "hint" => "Where the tenant has moved to another social property owned by the same landlord." },
"2" => { "value" => "Tenant applied directly (no referral or nomination)" },
"3" => { "value" => "Nominated by a local housing authority" },
"8" => { "value" => "Re-located through official housing mobility scheme" },
"10" => { "value" => "Other social landlord" },
"9" => { "value" => "Community learning disability team" },
"14" => { "value" => "Community mental health team" },
"15" => { "value" => "Health service" },
"18" => { "value" => "Police, probation, prison or youth offending team – tenant had custodial sentence" },
"19" => { "value" => "Police, probation, prison or youth offending team – no custodial sentence" },
"7" => { "value" => "Voluntary agency" },
"17" => { "value" => "Children’s Social Care" },
"16" => { "value" => "Other" },
})
end
it "has the correct question number" do
expect(question.question_number).to eq(84)
end
end
end

105
spec/models/form/lettings/questions/referral_supported_housing_prp_spec.rb

@ -0,0 +1,105 @@
require "rails_helper"
RSpec.describe Form::Lettings::Questions::ReferralSupportedHousingPrp, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
let(:subsection) { instance_double(Form::Subsection) }
let(:form) { instance_double(Form, start_date: Time.zone.local(2023, 4, 1)) }
before do
allow(form).to receive(:start_year_after_2024?).and_return(false)
allow(page).to receive(:subsection).and_return(subsection)
allow(subsection).to receive(:form).and_return(form)
end
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("referral")
end
it "has the correct header" do
expect(question.header).to eq("What was the source of referral for this letting?")
end
it "has the correct check_answer_label" do
expect(question.check_answer_label).to eq("Source of referral for letting")
end
it "has the correct type" do
expect(question.type).to eq("radio")
end
it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to eq(0)
end
it "has the correct hint" do
expect(question.hint_text).to eq("")
end
it "is not marked as derived" do
expect(question).not_to be_derived(nil)
end
context "with 2023/24 form" do
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"1" => { "value" => "Internal transfer", "hint" => "Where the tenant has moved to another social property owned by the same landlord." },
"2" => { "value" => "Tenant applied directly (no referral or nomination)" },
"3" => { "value" => "Nominated by a local housing authority" },
"4" => { "value" => "Referred by local authority housing department" },
"8" => { "value" => "Re-located through official housing mobility scheme" },
"10" => { "value" => "Other social landlord" },
"9" => { "value" => "Community learning disability team" },
"14" => { "value" => "Community mental health team" },
"15" => { "value" => "Health service" },
"12" => { "value" => "Police, probation or prison" },
"7" => { "value" => "Voluntary agency" },
"13" => { "value" => "Youth offending team" },
"17" => { "value" => "Children’s Social Care" },
"16" => { "value" => "Other" },
})
end
it "has the correct question number" do
expect(question.question_number).to eq(85)
end
end
context "with 2024/25 form" do
let(:form) { instance_double(Form, start_date: Time.zone.local(2024, 4, 1)) }
before do
allow(form).to receive(:start_year_after_2024?).and_return(true)
end
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"1" => { "value" => "Internal transfer", "hint" => "Where the tenant has moved to another social property owned by the same landlord." },
"2" => { "value" => "Tenant applied directly (no referral or nomination)" },
"3" => { "value" => "Nominated by a local housing authority" },
"4" => { "value" => "Referred by local authority housing department" },
"8" => { "value" => "Re-located through official housing mobility scheme" },
"10" => { "value" => "Other social landlord" },
"9" => { "value" => "Community learning disability team" },
"14" => { "value" => "Community mental health team" },
"15" => { "value" => "Health service" },
"18" => { "value" => "Police, probation, prison or youth offending team – tenant had custodial sentence" },
"19" => { "value" => "Police, probation, prison or youth offending team – no custodial sentence" },
"7" => { "value" => "Voluntary agency" },
"17" => { "value" => "Children’s Social Care" },
"16" => { "value" => "Other" },
})
end
it "has the correct question number" do
expect(question.question_number).to eq(84)
end
end
end

101
spec/models/form/lettings/questions/referral_supported_housing_spec.rb

@ -0,0 +1,101 @@
require "rails_helper"
RSpec.describe Form::Lettings::Questions::ReferralSupportedHousing, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
let(:subsection) { instance_double(Form::Subsection) }
let(:form) { instance_double(Form, start_date: Time.zone.local(2023, 4, 1)) }
before do
allow(form).to receive(:start_year_after_2024?).and_return(false)
allow(page).to receive(:subsection).and_return(subsection)
allow(subsection).to receive(:form).and_return(form)
end
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("referral")
end
it "has the correct header" do
expect(question.header).to eq("What was the source of referral for this letting?")
end
it "has the correct check_answer_label" do
expect(question.check_answer_label).to eq("Source of referral for letting")
end
it "has the correct type" do
expect(question.type).to eq("radio")
end
it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to eq(0)
end
it "has the correct hint" do
expect(question.hint_text).to eq("You told us that you are a local authority. We have removed some options because of this.")
end
it "is not marked as derived" do
expect(question).not_to be_derived(nil)
end
context "with 2023/24 form" do
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"1" => { "value" => "Internal transfer", "hint" => "Where the tenant has moved to another social property owned by the same landlord." },
"2" => { "value" => "Tenant applied directly (no referral or nomination)" },
"8" => { "value" => "Re-located through official housing mobility scheme" },
"10" => { "value" => "Other social landlord" },
"9" => { "value" => "Community learning disability team" },
"14" => { "value" => "Community mental health team" },
"15" => { "value" => "Health service" },
"12" => { "value" => "Police, probation or prison" },
"7" => { "value" => "Voluntary agency" },
"13" => { "value" => "Youth offending team" },
"17" => { "value" => "Children’s Social Care" },
"16" => { "value" => "Other" },
})
end
it "has the correct question number" do
expect(question.question_number).to eq(85)
end
end
context "with 2024/25 form" do
let(:form) { instance_double(Form, start_date: Time.zone.local(2024, 4, 1)) }
before do
allow(form).to receive(:start_year_after_2024?).and_return(true)
end
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"1" => { "value" => "Internal transfer", "hint" => "Where the tenant has moved to another social property owned by the same landlord." },
"2" => { "value" => "Tenant applied directly (no referral or nomination)" },
"8" => { "value" => "Re-located through official housing mobility scheme" },
"10" => { "value" => "Other social landlord" },
"9" => { "value" => "Community learning disability team" },
"14" => { "value" => "Community mental health team" },
"15" => { "value" => "Health service" },
"18" => { "value" => "Police, probation, prison or youth offending team – tenant had custodial sentence" },
"19" => { "value" => "Police, probation, prison or youth offending team – no custodial sentence" },
"7" => { "value" => "Voluntary agency" },
"17" => { "value" => "Children’s Social Care" },
"16" => { "value" => "Other" },
})
end
it "has the correct question number" do
expect(question.question_number).to eq(84)
end
end
end

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

Loading…
Cancel
Save