Browse Source

Merge branch 'main' into CLDC-868-purchase-price-validations

# Conflicts:
#	app/models/form/sales/subsections/shared_ownership_scheme.rb
#	app/models/sales_log.rb
#	app/models/validations/sales/soft_validations.rb
#	db/schema.rb
#	spec/models/form/sales/subsections/shared_ownership_scheme_spec.rb
#	spec/models/form_handler_spec.rb
pull/1225/head
natdeanlewissoftwire 3 years ago
parent
commit
7861db285e
  1. 28
      app/controllers/bulk_upload_lettings_results_controller.rb
  2. 14
      app/controllers/lettings_logs_controller.rb
  3. 4
      app/controllers/modules/logs_filter.rb
  4. 5
      app/helpers/logs_helper.rb
  5. 27
      app/models/form/sales/pages/shared_ownership_deposit_value_check.rb
  6. 1
      app/models/form/sales/questions/mortgage_length.rb
  7. 23
      app/models/form/sales/questions/shared_ownership_deposit_value_check.rb
  8. 5
      app/models/form/sales/subsections/shared_ownership_scheme.rb
  9. 4
      app/models/log.rb
  10. 10
      app/models/sales_log.rb
  11. 4
      app/models/user.rb
  12. 10
      app/models/validations/sales/financial_validations.rb
  13. 9
      app/models/validations/sales/sale_information_validations.rb
  14. 13
      app/models/validations/sales/soft_validations.rb
  15. 1
      app/services/bulk_upload/lettings/validator.rb
  16. 11
      app/views/bulk_upload_lettings_results/resume.html.erb
  17. 54
      app/views/logs/_log_filters.erb
  18. 62
      app/views/logs/index.html.erb
  19. 4
      config/locales/en.yml
  20. 6
      config/routes.rb
  21. 7
      db/migrate/20230123101256_add_shared_ownership_deposit_value_check.rb
  22. 5
      db/migrate/20230126145529_add_column_to_bulk_upload_errors.rb
  23. 21
      db/schema.rb
  24. 68
      spec/controllers/bulk_upload_lettings_results_controller_spec.rb
  25. 4
      spec/models/form/sales/pages/buyer1_income_value_check_spec.rb
  26. 4
      spec/models/form/sales/pages/deposit_value_check_spec.rb
  27. 4
      spec/models/form/sales/pages/handover_date_check_spec.rb
  28. 4
      spec/models/form/sales/pages/household_wheelchair_check_spec.rb
  29. 4
      spec/models/form/sales/pages/mortgage_value_check_spec.rb
  30. 4
      spec/models/form/sales/pages/retirement_value_check_spec.rb
  31. 4
      spec/models/form/sales/pages/savings_value_check_spec.rb
  32. 50
      spec/models/form/sales/pages/shared_ownership_deposit_value_check_spec.rb
  33. 4
      spec/models/form/sales/questions/mortgage_length_spec.rb
  34. 61
      spec/models/form/sales/questions/shared_ownership_deposit_value_check_spec.rb
  35. 3
      spec/models/form/sales/subsections/shared_ownership_scheme_spec.rb
  36. 4
      spec/models/form_handler_spec.rb
  37. 8
      spec/models/sales_log_spec.rb
  38. 6
      spec/models/user_spec.rb
  39. 26
      spec/models/validations/sales/financial_validations_spec.rb
  40. 57
      spec/models/validations/sales/sale_information_validations_spec.rb
  41. 104
      spec/models/validations/sales/soft_validations_spec.rb
  42. 103
      spec/requests/lettings_logs_controller_spec.rb
  43. 9
      spec/services/bulk_upload/lettings/validator_spec.rb
  44. 34
      spec/support/bulk_upload/log_to_csv.rb

28
app/controllers/bulk_upload_lettings_results_controller.rb

@ -6,4 +6,32 @@ class BulkUploadLettingsResultsController < ApplicationController
def show
@bulk_upload = current_user.bulk_uploads.lettings.find(params[:id])
end
def resume
@bulk_upload = current_user.bulk_uploads.lettings.find(params[:id])
if @bulk_upload.lettings_logs.in_progress.count.positive?
set_bulk_upload_logs_filters
redirect_to(lettings_logs_path(bulk_upload_id: [@bulk_upload.id]))
else
reset_logs_filters
end
end
private
def reset_logs_filters
session["logs_filters"] = {}.to_json
end
def set_bulk_upload_logs_filters
hash = {
years: [""],
status: ["", "in_progress"],
user: "all",
}
session["logs_filters"] = hash.to_json
end
end

14
app/controllers/lettings_logs_controller.rb

@ -3,6 +3,9 @@ class LettingsLogsController < LogsController
before_action :session_filters, if: :current_user
before_action :set_session_filters, if: :current_user
before_action :extract_bulk_upload_from_session_filters, only: [:index]
before_action :redirect_if_bulk_upload_resolved, only: [:index]
def index
respond_to do |format|
format.html do
@ -109,6 +112,17 @@ class LettingsLogsController < LogsController
private
def redirect_if_bulk_upload_resolved
if @bulk_upload && @bulk_upload.lettings_logs.in_progress.count.zero?
redirect_to resume_bulk_upload_lettings_result_path(@bulk_upload)
end
end
def extract_bulk_upload_from_session_filters
id = ((@session_filters["bulk_upload_id"] || []).reject(&:blank?))[0]
@bulk_upload = current_user.bulk_uploads.find_by(id:)
end
def permitted_log_params
params.require(:lettings_log).permit(LettingsLog.editable_fields)
end

4
app/controllers/modules/logs_filter.rb

@ -7,7 +7,9 @@ module Modules::LogsFilter
def load_session_filters(specific_org: false)
current_filters = session[:logs_filters]
new_filters = current_filters.present? ? JSON.parse(current_filters) : {}
current_user.logs_filters(specific_org:).each { |filter| new_filters[filter] = params[filter] if params[filter].present? }
current_user.logs_filters(specific_org:).each do |filter|
new_filters[filter] = params[filter] if params[filter].present?
end
params["organisation_select"] == "all" ? new_filters.except("organisation") : new_filters
end

5
app/helpers/logs_helper.rb

@ -18,4 +18,9 @@ module LogsHelper
bulk_upload_sales_log_path(id:)
end
end
def bulk_upload_options(bulk_upload)
array = bulk_upload ? [bulk_upload.id] : []
array.index_with { |_bulk_upload_id| "With logs from bulk upload" }
end
end

27
app/models/form/sales/pages/shared_ownership_deposit_value_check.rb

@ -0,0 +1,27 @@
class Form::Sales::Pages::SharedOwnershipDepositValueCheck < ::Form::Page
def initialize(id, hsh, subsection)
super
@depends_on = [
{
"shared_ownership_deposit_invalid?" => true,
},
]
@informative_text = {}
@title_text = {
"translation" => "soft_validations.shared_owhership_deposit.title_text",
"arguments" => [
{
"key" => "expected_shared_ownership_deposit_value",
"label" => false,
"i18n_template" => "expected_shared_ownership_deposit_value",
},
],
}
end
def questions
@questions ||= [
Form::Sales::Questions::SharedOwnershipDepositValueCheck.new(nil, nil, self),
]
end
end

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

@ -6,6 +6,7 @@ class Form::Sales::Questions::MortgageLength < ::Form::Question
@header = "What is the length of the mortgage?"
@type = "numeric"
@min = 0
@max = 60
@width = 5
@suffix = " years"
@hint_text = "You should round up to the nearest year. Value should not exceed 60 years."

23
app/models/form/sales/questions/shared_ownership_deposit_value_check.rb

@ -0,0 +1,23 @@
class Form::Sales::Questions::SharedOwnershipDepositValueCheck < ::Form::Question
def initialize(id, hsh, page)
super
@id = "shared_ownership_deposit_value_check"
@check_answer_label = "Shared ownership deposit confirmation"
@type = "interruption_screen"
@header = "Are you sure this is correct?"
@answer_options = {
"0" => { "value" => "Yes" },
"1" => { "value" => "No" },
}
@hidden_in_check_answers = {
"depends_on" => [
{
"shared_ownership_deposit_value_check" => 0,
},
{
"shared_ownership_deposit_value_check" => 1,
},
],
}
end
end

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

@ -22,15 +22,18 @@ class Form::Sales::Subsections::SharedOwnershipScheme < ::Form::Subsection
Form::Sales::Pages::PreviousTenure.new(nil, nil, self),
Form::Sales::Pages::AboutPriceSharedOwnership.new(nil, nil, self),
Form::Sales::Pages::AboutPriceSharedOwnershipValueCheck.new(nil, nil, self),
Form::Sales::Pages::SharedOwnershipDepositValueCheck.new("shared_ownership_equity_value_check", nil, self),
Form::Sales::Pages::Mortgageused.new("mortgage_used_shared_ownership", nil, self),
Form::Sales::Pages::MortgageAmount.new("mortgage_amount_shared_ownership", nil, self),
Form::Sales::Pages::SharedOwnershipDepositValueCheck.new("shared_ownership_mortgage_amount_value_check", nil, self),
Form::Sales::Pages::MortgageLender.new("mortgage_lender_shared_ownership", nil, self),
Form::Sales::Pages::MortgageLenderOther.new("mortgage_lender_other_shared_ownership", nil, self),
Form::Sales::Pages::MortgageLength.new("mortgage_length_shared_ownership", nil, self),
Form::Sales::Pages::ExtraBorrowing.new("extra_borrowing_shared_ownership", nil, self),
Form::Sales::Pages::AboutDepositWithDiscount.new(nil, nil, self),
Form::Sales::Pages::AboutDepositWithoutDiscount.new("about_deposit_shared_ownership", nil, self),
Form::Sales::Pages::DepositValueCheck.new("shared_ownership_deposit_value_check", nil, self),
Form::Sales::Pages::DepositValueCheck.new("deposit_value_check", nil, self),
Form::Sales::Pages::SharedOwnershipDepositValueCheck.new("shared_ownership_deposit_value_check", nil, self),
Form::Sales::Pages::MonthlyRent.new(nil, nil, self),
Form::Sales::Pages::LeaseholdCharges.new("leasehold_charges_shared_ownership", nil, self),
]

4
app/models/log.rb

@ -24,6 +24,10 @@ class Log < ApplicationRecord
where(created_by: user)
end
}
scope :filter_by_bulk_upload_id, lambda { |bulk_upload_id, user|
joins(:bulk_upload)
.where(bulk_upload: { id: bulk_upload_id, user: })
}
scope :created_by, ->(user) { where(created_by: user) }
def collection_start_year

10
app/models/sales_log.rb

@ -159,6 +159,12 @@ class SalesLog < Log
end
end
def expected_shared_ownership_deposit_value
return unless value && equity
(value * equity / 100).round(2)
end
def process_postcode(postcode, postcode_known_key, la_inferred_key, la_key)
return if postcode.blank?
@ -212,6 +218,10 @@ class SalesLog < Log
type == 24
end
def shared_ownership_scheme?
ownershipsch == 1
end
def purchase_price_soft_min
LaSaleRange.find_by(start_year: collection_start_year, la:, bedrooms: beds).soft_min
end

4
app/models/user.rb

@ -145,9 +145,9 @@ class User < ApplicationRecord
def logs_filters(specific_org: false)
if (support? && !specific_org) || organisation.has_managing_agents?
%w[status years user organisation]
%w[status years user organisation bulk_upload_id]
else
%w[status years user]
%w[status years user bulk_upload_id]
end
end

10
app/models/validations/sales/financial_validations.rb

@ -3,11 +3,19 @@ module Validations::Sales::FinancialValidations
# or 'validate_' to run on submit as well
def validate_income1(record)
if record.ecstat1 && record.income1 && record.ownershipsch == 1
if record.ecstat1 && record.income1 && record.la && record.ownershipsch == 1
if record.london_property?
record.errors.add :income1, I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000) if record.income1 > 90_000
record.errors.add :ecstat1, I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000) if record.income1 > 90_000
record.errors.add :ownershipsch, I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000) if record.income1 > 90_000
record.errors.add :la, I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000) if record.income1 > 90_000
record.errors.add :postcode_full, I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000) if record.income1 > 90_000
elsif record.income1 > 80_000
record.errors.add :income1, I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000)
record.errors.add :ecstat1, I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000)
record.errors.add :ownershipsch, I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000)
record.errors.add :la, I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000) if record.income1 > 80_000
record.errors.add :postcode_full, I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000) if record.income1 > 80_000
end
end
end

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

@ -63,4 +63,13 @@ module Validations::Sales::SaleInformationValidations
end
end
end
def validate_basic_monthly_rent(record)
return unless record.mrent && record.ownershipsch && record.type
if record.shared_owhership_scheme? && !record.old_persons_shared_ownership? && record.mrent > 9999
record.errors.add :mrent, I18n.t("validations.sale_information.monthly_rent.higher_than_expected")
record.errors.add :type, I18n.t("validations.sale_information.monthly_rent.higher_than_expected")
end
end
end

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

@ -55,6 +55,19 @@ module Validations::Sales::SoftValidations
sale_range.present? && !value.between?(sale_range.soft_min, sale_range.soft_max)
end
def shared_ownership_deposit_invalid?
return unless mortgage || mortgageused == 2
return unless cashdis || !is_type_discount?
return unless deposit && value && equity
cash_discount = cashdis || 0
mortgage_value = mortgage || 0
mortgage_value + deposit + cash_discount != value * equity / 100
end
def hodate_3_years_or_more_saledate?
return unless hodate && saledate
def purchase_price_min_or_max_text
value < sale_range.soft_min ? "minimum" : "maximum"
end

1
app/services/bulk_upload/lettings/validator.rb

@ -164,6 +164,7 @@ class BulkUpload::Lettings::Validator
property_ref: row_parser.field_100,
row:,
cell: "#{cols[field_number_for_attribute(error.attribute) - col_offset + 1]}#{row}",
col: cols[field_number_for_attribute(error.attribute) - col_offset + 1],
)
end
end

11
app/views/bulk_upload_lettings_results/resume.html.erb

@ -0,0 +1,11 @@
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<h1 class="govuk-heading-xl">There are no more logs that need updating</h1>
</div>
</div>
<p class="govuk-body-l">
You’ve completed all the logs that had errors from your bulk upload.
</p>
<%= govuk_button_link_to "Back to all logs", lettings_logs_path, button: true %>

54
app/views/logs/_log_filters.erb

@ -3,13 +3,48 @@
<div class="app-filter__header">
<h2 class="govuk-heading-m">Filters</h2>
</div>
<div class="app-filter__content">
<%= form_with html: { method: :get } do |f| %>
<% years = {"2021": "2021/22", "2022": "2022/23"} %>
<% all_or_yours = {"all": { label: "All" }, "yours": { label: "Yours" } } %>
<%= render partial: "filters/checkbox_filter", locals: { f: f, options: years, label: "Collection year", category: "years" } %>
<%= render partial: "filters/checkbox_filter", locals: { f: f, options: status_filters, label: "Status", category: "status" } %>
<%= render partial: "filters/radio_filter", locals: { f: f, options: all_or_yours, label: "Logs", category: "user", } %>
<% years = { "2021": "2021/22", "2022": "2022/23" } %>
<% all_or_yours = { "all": { label: "All" }, "yours": { label: "Yours" } } %>
<% if bulk_upload_options(@bulk_upload).present? %>
<%= render partial: "filters/checkbox_filter",
locals: {
f: f,
options: bulk_upload_options(@bulk_upload),
label: "Bulk upload",
category: "bulk_upload_id",
} %>
<% end %>
<% if bulk_upload_options(@bulk_upload).blank? %>
<%= render partial: "filters/checkbox_filter",
locals: {
f: f,
options: years,
label: "Collection year",
category: "years",
} %>
<%= render partial: "filters/checkbox_filter",
locals: {
f: f,
options: status_filters,
label: "Status",
category: "status",
} %>
<% end %>
<%= render partial: "filters/radio_filter",
locals: {
f: f,
options: all_or_yours,
label: "Logs",
category: "user",
} %>
<% if (@current_user.support? || @current_user.organisation.has_managing_agents?) && request.path == "/lettings-logs" %>
<%= render partial: "filters/radio_filter", locals: {
f: f,
@ -21,14 +56,15 @@
type: "select",
label: "Organisation",
category: "organisation",
options: organisations_filter_options(@current_user)
}
}
options: organisations_filter_options(@current_user),
},
},
},
label: "Organisation",
category: "organisation_select"
category: "organisation_select",
} %>
<% end %>
<%= f.govuk_submit "Apply filters", class: "govuk-!-margin-bottom-0" %>
<% end %>
</div>

62
app/views/logs/index.html.erb

@ -11,32 +11,58 @@
title_id: "impacted-logs-banner",
) do |notification_banner| %>
<% notification_banner.heading(text: "A scheme has changed and it has affected #{@unresolved_count} #{'log'.pluralize(@unresolved_count)}") %>
<div class="govuk-notification-banner__heading">
<%= govuk_link_to "Update logs", update_logs_lettings_logs_path, class: "govuk-notification-banner__link" %>
</div>
<% end %>
<div class="govuk-notification-banner__heading">
<%= govuk_link_to "Update logs", update_logs_lettings_logs_path, class: "govuk-notification-banner__link" %>
</div>
<% end %>
<%= render partial: "organisations/headings", locals: current_user.support? ? { main: "Lettings logs", sub: nil } : { main: "Lettings logs", sub: current_user.organisation.name } %>
<% elsif current_page?(controller: 'sales_logs', action: 'index') %>
<%= render partial: "organisations/headings", locals: current_user.support? ? { main: "Sales logs", sub: nil } : { main: "Sales logs", sub: current_user.organisation.name } %>
<% end %>
<% end %>
<div class="app-filter-layout" data-controller="filter-layout">
<div class="govuk-button-group app-filter-toggle govuk-!-margin-bottom-6">
<% if current_page?(controller: 'lettings_logs', action: 'index') %>
<%= govuk_button_to "Create a new lettings log", lettings_logs_path, class: "govuk-!-margin-right-6" %>
<% end %>
<% if @bulk_upload.blank? %>
<%= render partial: "organisations/headings", locals: current_user.support? ? { main: "#{log_type_for_controller(controller).capitalize} logs", sub: nil } : { main: "#{log_type_for_controller(controller).capitalize} logs", sub: current_user.organisation.name } %>
<% else %>
<%= render partial: "organisations/headings",
locals: {
main: "You need to fix #{pluralize(@pagy.count, 'log')} from your bulk upload",
sub: "#{log_type_for_controller(controller).capitalize} logs (#{@bulk_upload.year_combo})",
} %>
<% if FeatureToggle.sales_log_enabled? && current_page?(controller: 'sales_logs', action: 'index') %>
<%= govuk_button_to "Create a new sales log", sales_logs_path, class: "govuk-!-margin-right-6" %>
<% end %>
<div class="app-card govuk-!-margin-bottom-9">
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<p class="govuk-body-l">
The following logs are from your recent bulk upload. They have some incorrect or incomplete data. You’ll need to answer a few more questions for each one to mark them as complete.
</p>
<% if FeatureToggle.bulk_upload_logs? %>
<%= govuk_button_link_to "Upload #{log_type_for_controller(controller)} logs in bulk", bulk_upload_path_for_controller(controller, id: "start"), secondary: true %>
<% end %>
<p class="govuk-body">
<strong>Bulk Upload details:</strong><br>
<%= @bulk_upload.filename %><br>
Uploaded on <%= @bulk_upload.created_at.to_fs(:govuk_date_and_time) %><br>
</p>
</div>
</div>
</div>
<% end %>
<div class="app-filter-layout" data-controller="filter-layout">
<% unless @bulk_upload %>
<div class="govuk-button-group app-filter-toggle govuk-!-margin-bottom-6">
<% if current_page?(controller: 'lettings_logs', action: 'index') %>
<%= govuk_button_to "Create a new lettings log", lettings_logs_path, class: "govuk-!-margin-right-6" %>
<% end %>
<% if FeatureToggle.sales_log_enabled? && current_page?(controller: 'sales_logs', action: 'index') %>
<%= govuk_button_to "Create a new sales log", sales_logs_path, class: "govuk-!-margin-right-6" %>
<% end %>
<% if FeatureToggle.bulk_upload_logs? %>
<%= govuk_button_link_to "Upload #{log_type_for_controller(controller)} logs in bulk", bulk_upload_path_for_controller(controller, id: "start"), secondary: true %>
<% end %>
</div>
<% end %>
<%= render partial: "log_filters" %>
<div class="app-filter-layout__content">
<%= render SearchComponent.new(current_user:, search_label: "Search by log ID, tenant code, property reference or postcode", value: @searched) %>
<%= govuk_section_break(visible: true, size: "m") %>

4
config/locales/en.yml

@ -429,6 +429,8 @@ en:
handover_before_exchange: "Practical completion or handover date must be before exchange date"
exchange_after_handover: "Exchange date must be after practical completion or handover date"
discounted_ownership_value: "Mortgage, deposit, and grant total must equal £%{value_with_discount}"
monthly_rent:
higher_than_expected: "Basic monthly rent must be between £0 and £9,999"
soft_validations:
net_income:
@ -465,6 +467,8 @@ en:
title_text: "You told us the time between the start of the tenancy and the major repairs completion date is more than 2 years"
void_date:
title_text: "You told us the time between the start of the tenancy and the void date is more than 2 years"
shared_owhership_deposit:
title_text: "Mortgage, deposit and cash discount total should equal £%{expected_shared_ownership_deposit_value}"
devise:
two_factor_authentication:

6
config/routes.rb

@ -134,7 +134,11 @@ Rails.application.routes.draw do
end
end
resources :bulk_upload_lettings_results, path: "bulk-upload-results", only: [:show]
resources :bulk_upload_lettings_results, path: "bulk-upload-results", only: [:show] do
member do
get :resume
end
end
get "update-logs", to: "lettings_logs#update_logs"
end

7
db/migrate/20230123101256_add_shared_ownership_deposit_value_check.rb

@ -0,0 +1,7 @@
class AddSharedOwnershipDepositValueCheck < ActiveRecord::Migration[7.0]
def change
change_table :sales_logs, bulk: true do |t|
t.column :shared_ownership_deposit_value_check, :integer
end
end
end

5
db/migrate/20230126145529_add_column_to_bulk_upload_errors.rb

@ -0,0 +1,5 @@
class AddColumnToBulkUploadErrors < ActiveRecord::Migration[7.0]
def change
add_column :bulk_upload_errors, :col, :text
end
end

21
db/schema.rb

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2023_01_24_111328) do
ActiveRecord::Schema[7.0].define(version: 2023_01_26_145529) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -25,6 +25,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_24_111328) do
t.text "error"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.text "col"
t.index ["bulk_upload_id"], name: "index_bulk_upload_errors_on_bulk_upload_id"
end
@ -69,17 +70,6 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_24_111328) do
t.index ["start_year", "lettype", "beds", "la"], name: "index_la_rent_ranges_on_start_year_and_lettype_and_beds_and_la", unique: true
end
create_table "la_sale_ranges", force: :cascade do |t|
t.string "la"
t.integer "bedrooms"
t.decimal "soft_min", precision: 10, scale: 2
t.decimal "soft_max", precision: 10, scale: 2
t.integer "start_year"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["start_year", "bedrooms", "la"], name: "index_la_sale_ranges_on_start_year_bedrooms_la", unique: true
end
create_table "legacy_users", force: :cascade do |t|
t.string "old_user_id"
t.integer "user_id"
@ -497,9 +487,9 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_24_111328) do
t.integer "hoyear"
t.integer "fromprop"
t.integer "socprevten"
t.integer "mortlen"
t.integer "mortgagelender"
t.string "mortgagelenderother"
t.integer "mortlen"
t.integer "extrabor"
t.integer "hhmemb"
t.integer "totadult"
@ -512,11 +502,12 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_24_111328) do
t.boolean "is_la_inferred"
t.bigint "bulk_upload_id"
t.integer "retirement_value_check"
t.integer "deposit_and_mortgage_value_check"
t.integer "grant_value_check"
t.integer "hodate_check"
t.integer "extrabor_value_check"
t.integer "grant_value_check"
t.integer "value_value_check"
t.index ["bulk_upload_id"], name: "index_sales_logs_on_bulk_upload_id"
t.integer "shared_ownership_deposit_value_check"
t.index ["created_by_id"], name: "index_sales_logs_on_created_by_id"
t.index ["owning_organisation_id"], name: "index_sales_logs_on_owning_organisation_id"
t.index ["updated_by_id"], name: "index_sales_logs_on_updated_by_id"

68
spec/controllers/bulk_upload_lettings_results_controller_spec.rb

@ -0,0 +1,68 @@
require "rails_helper"
RSpec.describe BulkUploadLettingsResultsController do
before do
sign_in user
end
describe "GET #resume /lettings-logs/bulk-upload-results/:ID/resume" do
let(:user) { create(:user) }
let(:bulk_upload) { create(:bulk_upload, :lettings, user:) }
context "when there are no logs left to resolve" do
render_views
it "displays copy to user" do
get :resume, params: { id: bulk_upload.id }
expect(response.body).to include("There are no more logs that need updating")
end
it "resets logs filters" do
get :resume, params: { id: bulk_upload.id }
expect(JSON.parse(session["logs_filters"])).to eql({})
end
end
context "when there are logs left to resolve" do
before do
create(:lettings_log, :in_progress, bulk_upload:)
end
it "clears the year filter" do
hash = {
years: ["", "2022"],
}
session["logs_filters"] = hash.to_json
get :resume, params: { id: bulk_upload.id }
expect(JSON.parse(session["logs_filters"])["years"]).to eql([""])
end
it "sets the status filter to in progress" do
session["logs_filters"] ||= {}.to_json
get :resume, params: { id: bulk_upload.id }
expect(JSON.parse(session["logs_filters"])["status"]).to eql(["", "in_progress"])
end
it "sets the user filter to all" do
session["logs_filters"] ||= {}.to_json
get :resume, params: { id: bulk_upload.id }
expect(JSON.parse(session["logs_filters"])["user"]).to eql("all")
end
it "redirects to logs with bulk upload filter applied" do
get :resume, params: { id: bulk_upload.id }
expect(response).to redirect_to("/lettings-logs?bulk_upload_id%5B%5D=#{bulk_upload.id}")
end
end
end
end

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

@ -30,4 +30,8 @@ RSpec.describe Form::Sales::Pages::Buyer1IncomeValueCheck, type: :model do
},
])
end
it "is interruption screen page" do
expect(page.interruption_screen?).to eq(true)
end
end

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

@ -30,4 +30,8 @@ RSpec.describe Form::Sales::Pages::DepositValueCheck, type: :model do
},
])
end
it "is interruption screen page" do
expect(page.interruption_screen?).to eq(true)
end
end

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

@ -30,4 +30,8 @@ RSpec.describe Form::Sales::Pages::HandoverDateCheck, type: :model do
},
])
end
it "is interruption screen page" do
expect(page.interruption_screen?).to eq(true)
end
end

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

@ -30,4 +30,8 @@ RSpec.describe Form::Sales::Pages::HouseholdWheelchairCheck, type: :model do
},
])
end
it "is interruption screen page" do
expect(page.interruption_screen?).to eq(true)
end
end

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

@ -23,6 +23,10 @@ RSpec.describe Form::Sales::Pages::MortgageValueCheck, type: :model do
expect(page.header).to be_nil
end
it "is interruption screen page" do
expect(page.interruption_screen?).to eq(true)
end
it "has correct depends_on" do
expect(page.depends_on).to eq([
{

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

@ -600,4 +600,8 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do
end
end
end
it "is interruption screen page" do
expect(page.interruption_screen?).to eq(true)
end
end

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

@ -30,4 +30,8 @@ RSpec.describe Form::Sales::Pages::SavingsValueCheck, type: :model do
},
])
end
it "is interruption screen page" do
expect(page.interruption_screen?).to eq(true)
end
end

50
spec/models/form/sales/pages/shared_ownership_deposit_value_check_spec.rb

@ -0,0 +1,50 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::SharedOwnershipDepositValueCheck, type: :model do
subject(:page) { described_class.new(page_id, page_definition, subsection) }
let(:page_id) { "shared_ownership_deposit_value_check" }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection) }
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[shared_ownership_deposit_value_check])
end
it "has the correct id" do
expect(page.id).to eq("shared_ownership_deposit_value_check")
end
it "has the correct header" do
expect(page.header).to be_nil
end
it "has correct depends_on" do
expect(page.depends_on).to eq([
{
"shared_ownership_deposit_invalid?" => true,
},
])
end
it "has the correct title_text" do
expect(page.title_text).to eq({
"translation" => "soft_validations.shared_owhership_deposit.title_text",
"arguments" => [
{
"key" => "expected_shared_ownership_deposit_value",
"label" => false,
"i18n_template" => "expected_shared_ownership_deposit_value",
},
],
})
end
it "has the correct informative_text" do
expect(page.informative_text).to eq({})
end
end

4
spec/models/form/sales/questions/mortgage_length_spec.rb

@ -48,4 +48,8 @@ RSpec.describe Form::Sales::Questions::MortgageLength, type: :model do
it "has correct min" do
expect(question.min).to eq(0)
end
it "has correct max" do
expect(question.max).to eq(60)
end
end

61
spec/models/form/sales/questions/shared_ownership_deposit_value_check_spec.rb

@ -0,0 +1,61 @@
require "rails_helper"
RSpec.describe Form::Sales::Questions::SharedOwnershipDepositValueCheck, 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) }
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("shared_ownership_deposit_value_check")
end
it "has the correct header" do
expect(question.header).to eq("Are you sure this is correct?")
end
it "has the correct check_answer_label" do
expect(question.check_answer_label).to eq("Shared ownership deposit confirmation")
end
it "has the correct type" do
expect(question.type).to eq("interruption_screen")
end
it "is not marked as derived" do
expect(question.derived?).to be false
end
it "has the correct hint" do
expect(question.hint_text).to be_nil
end
# it "has a correct check_answers_card_number" do
# expect(question.check_answers_card_number).to eq(0)
# end
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"0" => { "value" => "Yes" },
"1" => { "value" => "No" },
})
end
it "has the correct hidden_in_check_answers" do
expect(question.hidden_in_check_answers).to eq({
"depends_on" => [
{
"shared_ownership_deposit_value_check" => 0,
},
{
"shared_ownership_deposit_value_check" => 1,
},
],
})
end
end

3
spec/models/form/sales/subsections/shared_ownership_scheme_spec.rb

@ -28,14 +28,17 @@ RSpec.describe Form::Sales::Subsections::SharedOwnershipScheme, type: :model do
shared_ownership_previous_tenure
about_price_shared_ownership
about_price_shared_ownership_value_check
shared_ownership_equity_value_check
mortgage_used_shared_ownership
mortgage_amount_shared_ownership
shared_ownership_mortgage_amount_value_check
mortgage_lender_shared_ownership
mortgage_lender_other_shared_ownership
mortgage_length_shared_ownership
extra_borrowing_shared_ownership
about_deposit_with_discount
about_deposit_shared_ownership
deposit_value_check
shared_ownership_deposit_value_check
monthly_rent
leasehold_charges_shared_ownership

4
spec/models/form_handler_spec.rb

@ -52,14 +52,14 @@ RSpec.describe FormHandler do
it "is able to load a current sales form" do
form = form_handler.get_form("current_sales")
expect(form).to be_a(Form)
expect(form.pages.count).to eq(189)
expect(form.pages.count).to eq(192)
expect(form.name).to eq("2022_2023_sales")
end
it "is able to load a previous sales form" do
form = form_handler.get_form("previous_sales")
expect(form).to be_a(Form)
expect(form.pages.count).to eq(189)
expect(form.pages.count).to eq(192)
expect(form.name).to eq("2021_2022_sales")
end
end

8
spec/models/sales_log_spec.rb

@ -339,4 +339,12 @@ RSpec.describe SalesLog, type: :model do
expect(record_from_db["prevloc"]).to eq(nil)
end
end
describe "expected_shared_ownership_deposit_value" do
let!(:completed_sales_log) { create(:sales_log, :completed, ownershipsch: 1, type: 2, value: 1000, equity: 50) }
it "is set to completed for a completed sales log" do
expect(completed_sales_log.expected_shared_ownership_deposit_value).to eq(500)
end
end
end

6
spec/models/user_spec.rb

@ -123,7 +123,7 @@ RSpec.describe User, type: :model do
end
it "can filter lettings logs by user, year and status" do
expect(user.logs_filters).to eq(%w[status years user])
expect(user.logs_filters).to eq(%w[status years user bulk_upload_id])
end
end
@ -133,7 +133,7 @@ RSpec.describe User, type: :model do
end
it "can filter lettings logs by user, year, status and organisation" do
expect(user.logs_filters).to eq(%w[status years user organisation])
expect(user.logs_filters).to eq(%w[status years user organisation bulk_upload_id])
end
end
end
@ -159,7 +159,7 @@ RSpec.describe User, type: :model do
end
it "can filter lettings logs by user, year, status and organisation" do
expect(user.logs_filters).to eq(%w[status years user organisation])
expect(user.logs_filters).to eq(%w[status years user organisation bulk_upload_id])
end
end

26
spec/models/validations/sales/financial_validations_spec.rb

@ -6,7 +6,7 @@ RSpec.describe Validations::Sales::FinancialValidations do
let(:validator_class) { Class.new { include Validations::Sales::FinancialValidations } }
describe "income validations" do
let(:record) { FactoryBot.create(:sales_log, ownershipsch: 1) }
let(:record) { FactoryBot.create(:sales_log, ownershipsch: 1, la: "E08000035") }
context "with shared ownership" do
context "and non london borough" do
@ -17,6 +17,14 @@ RSpec.describe Validations::Sales::FinancialValidations do
financial_validator.validate_income1(record)
expect(record.errors["income1"])
.to include(match I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000))
expect(record.errors["ecstat1"])
.to include(match I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000))
expect(record.errors["ownershipsch"])
.to include(match I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000))
expect(record.errors["la"])
.to include(match I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000))
expect(record.errors["postcode_full"])
.to include(match I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000))
end
end
@ -25,6 +33,10 @@ RSpec.describe Validations::Sales::FinancialValidations do
record.ecstat1 = 1
financial_validator.validate_income1(record)
expect(record.errors["income1"]).to be_empty
expect(record.errors["ecstat1"]).to be_empty
expect(record.errors["ownershipsch"]).to be_empty
expect(record.errors["la"]).to be_empty
expect(record.errors["postcode_full"]).to be_empty
end
end
@ -41,6 +53,14 @@ RSpec.describe Validations::Sales::FinancialValidations do
financial_validator.validate_income1(record)
expect(record.errors["income1"])
.to include(match I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000))
expect(record.errors["ecstat1"])
.to include(match I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000))
expect(record.errors["ownershipsch"])
.to include(match I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000))
expect(record.errors["la"])
.to include(match I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000))
expect(record.errors["postcode_full"])
.to include(match I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000))
end
end
@ -49,6 +69,10 @@ RSpec.describe Validations::Sales::FinancialValidations do
record.ecstat1 = 1
financial_validator.validate_income1(record)
expect(record.errors["income1"]).to be_empty
expect(record.errors["ecstat1"]).to be_empty
expect(record.errors["ownershipsch"]).to be_empty
expect(record.errors["la"]).to be_empty
expect(record.errors["postcode_full"]).to be_empty
end
end
end

57
spec/models/validations/sales/sale_information_validations_spec.rb

@ -371,4 +371,61 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
end
end
end
describe "#validate_basic_monthly_rent" do
context "when within permitted bounds" do
let(:record) { build(:sales_log, mrent: 9998, ownershipsch: 1, type: 2) }
it "does not add an error" do
sale_information_validator.validate_basic_monthly_rent(record)
expect(record.errors[:mrent]).not_to be_present
expect(record.errors[:type]).not_to be_present
end
end
context "when the rent is blank" do
let(:record) { build(:sales_log, mrent: nil, ownershipsch: 1, type: 2) }
it "does not add an error" do
sale_information_validator.validate_basic_monthly_rent(record)
expect(record.errors[:mrent]).not_to be_present
expect(record.errors[:type]).not_to be_present
end
end
context "when the type is old persons shared ownership" do
let(:record) { build(:sales_log, mrent: 100_000, ownershipsch: 1, type: 24) }
it "does not add an error" do
sale_information_validator.validate_basic_monthly_rent(record)
expect(record.errors[:mrent]).not_to be_present
expect(record.errors[:type]).not_to be_present
end
end
context "when the type is blank" do
let(:record) { build(:sales_log, mrent: 100_000, ownershipsch: 1, type: nil) }
it "does not add an error" do
sale_information_validator.validate_basic_monthly_rent(record)
expect(record.errors[:mrent]).not_to be_present
expect(record.errors[:type]).not_to be_present
end
end
context "when higher than upper bound" do
let(:record) { build(:sales_log, mrent: 100_000, ownershipsch: 1, type: 2) }
it "adds an error" do
sale_information_validator.validate_basic_monthly_rent(record)
expect(record.errors[:mrent]).to include(I18n.t("validations.sale_information.monthly_rent.higher_than_expected"))
expect(record.errors[:type]).to include(I18n.t("validations.sale_information.monthly_rent.higher_than_expected"))
end
end
end
end

104
spec/models/validations/sales/soft_validations_spec.rb

@ -321,6 +321,110 @@ RSpec.describe Validations::Sales::SoftValidations do
.not_to be_deposit_over_soft_max
end
end
context "when validating shared ownership deposit" do
it "returns false if MORTGAGE + DEPOSIT + CASHDIS are equal VALUE * EQUITY/100" do
record.mortgage = 1000
record.deposit = 1000
record.cashdis = 1000
record.value = 3000
record.equity = 100
expect(record)
.not_to be_shared_ownership_deposit_invalid
end
it "returns false if mortgage is used and no mortgage is given" do
record.mortgage = nil
record.deposit = 1000
record.cashdis = 1000
record.value = 3000
record.equity = 100
expect(record)
.not_to be_shared_ownership_deposit_invalid
end
it "returns true if mortgage is not used and no mortgage is given" do
record.mortgage = nil
record.mortgageused = 2
record.deposit = 1000
record.cashdis = 1000
record.value = 3000
record.equity = 100
expect(record)
.to be_shared_ownership_deposit_invalid
end
it "returns false if no deposit is given" do
record.mortgage = 1000
record.deposit = nil
record.cashdis = 1000
record.value = 3000
record.equity = 100
expect(record)
.not_to be_shared_ownership_deposit_invalid
end
it "returns false if no cashdis is given and cashdis is routed to" do
record.mortgage = 1000
record.deposit = 1000
record.type = 18
record.cashdis = nil
record.value = 3000
record.equity = 100
expect(record)
.not_to be_shared_ownership_deposit_invalid
end
it "returns true if no cashdis is given and cashdis is not routed to" do
record.mortgage = 1000
record.deposit = 1000
record.type = 2
record.cashdis = nil
record.value = 3000
record.equity = 100
expect(record)
.to be_shared_ownership_deposit_invalid
end
it "returns false if no value is given" do
record.mortgage = 1000
record.deposit = 1000
record.cashdis = 1000
record.value = nil
record.equity = 100
expect(record)
.not_to be_shared_ownership_deposit_invalid
end
it "returns false if no equity is given" do
record.mortgage = 1000
record.deposit = 1000
record.cashdis = 1000
record.value = 3000
record.equity = nil
expect(record)
.not_to be_shared_ownership_deposit_invalid
end
it "returns true if MORTGAGE + DEPOSIT + CASHDIS are not equal VALUE * EQUITY/100" do
record.mortgage = 1000
record.deposit = 1000
record.cashdis = 1000
record.value = 4323
record.equity = 100
expect(record)
.to be_shared_ownership_deposit_invalid
end
end
end
describe "hodate_more_than_3_years_before_exdate" do

103
spec/requests/lettings_logs_controller_spec.rb

@ -400,6 +400,109 @@ RSpec.describe LettingsLogsController, type: :request do
expect(page).not_to have_link(lettings_log_2022.id.to_s)
end
end
context "with bulk_upload_id filter" do
context "with bulk upload that belongs to current user" do
let(:organisation) { create(:organisation) }
let(:user) { create(:user, organisation:) }
let(:bulk_upload) { create(:bulk_upload, user:) }
let!(:included_log) { create(:lettings_log, :in_progress, bulk_upload:, owning_organisation: organisation) }
let!(:excluded_log) { create(:lettings_log, :in_progress, owning_organisation: organisation) }
it "returns logs only associated with the bulk upload" do
get "/lettings-logs?bulk_upload_id[]=#{bulk_upload.id}"
expect(page).to have_content(included_log.id)
expect(page).not_to have_content(excluded_log.id)
end
it "dislays how many logs remaining to fix" do
get "/lettings-logs?bulk_upload_id[]=#{bulk_upload.id}"
expect(page).to have_content("You need to fix 1 log")
end
it "displays filter" do
get "/lettings-logs?bulk_upload_id[]=#{bulk_upload.id}"
expect(page).to have_content("With logs from bulk upload")
end
it "hides collection year filter" do
get "/lettings-logs?bulk_upload_id[]=#{bulk_upload.id}"
expect(page).not_to have_content("Collection year")
end
it "hides status filter" do
get "/lettings-logs?bulk_upload_id[]=#{bulk_upload.id}"
expect(page).not_to have_content("Status")
end
it "hides button to create a new log" do
get "/lettings-logs?bulk_upload_id[]=#{bulk_upload.id}"
expect(page).not_to have_content("Create a new lettings log")
end
it "displays card with help info" do
get "/lettings-logs?bulk_upload_id[]=#{bulk_upload.id}"
expect(page).to have_content("The following logs are from your recent bulk upload")
end
it "displays meta info about the bulk upload" do
get "/lettings-logs?bulk_upload_id[]=#{bulk_upload.id}"
expect(page).to have_content(bulk_upload.filename)
expect(page).to have_content(bulk_upload.created_at.to_fs(:govuk_date_and_time))
end
end
context "with bulk upload that belongs to another user" do
let(:organisation) { create(:organisation) }
let(:user) { create(:user, organisation:) }
let(:other_user) { create(:user, organisation:) }
let(:bulk_upload) { create(:bulk_upload, user: other_user) }
let!(:excluded_log) { create(:lettings_log, bulk_upload:, owning_organisation: organisation) }
let!(:also_excluded_log) { create(:lettings_log, owning_organisation: organisation) }
it "does not return any logs" do
get "/lettings-logs?bulk_upload_id[]=#{bulk_upload.id}"
expect(page).not_to have_content(excluded_log.id)
expect(page).not_to have_content(also_excluded_log.id)
end
end
context "when bulk upload has been resolved" do
let(:organisation) { create(:organisation) }
let(:user) { create(:user, organisation:) }
let(:bulk_upload) { create(:bulk_upload, user:) }
it "redirects to resume the bulk upload" do
get "/lettings-logs?bulk_upload_id[]=#{bulk_upload.id}"
expect(response).to redirect_to(resume_bulk_upload_lettings_result_path(bulk_upload))
end
end
end
context "without bulk_upload_id" do
it "does not display filter" do
get "/lettings-logs"
expect(page).not_to have_content("With logs from bulk upload")
end
it "displays button to create a new log" do
get "/lettings-logs"
expect(page).to have_content("Create a new lettings log")
end
it "does not display card with help info" do
get "/lettings-logs"
expect(page).not_to have_content("The following logs are from your recent bulk upload")
end
end
end
end

9
spec/services/bulk_upload/lettings/validator_spec.rb

@ -43,7 +43,14 @@ RSpec.describe BulkUpload::Lettings::Validator do
validator.call
error = BulkUploadError.first
expect(error.row).to eq("7")
expect(error.field).to eql("field_96")
expect(error.error).to eql("blank")
expect(error.tenant_code).to eql("123")
expect(error.property_ref).to be_nil
expect(error.row).to eql("7")
expect(error.cell).to eql("CS7")
expect(error.col).to eql("CS")
end
end

34
spec/support/bulk_upload/log_to_csv.rb

@ -159,12 +159,7 @@ class BulkUpload::LogToCsv
end
def renewal
case log.renewal
when 1
1
when 0
2
end
checkbox_value(log.renewal)
end
def london_affordable_rent
@ -210,12 +205,7 @@ class BulkUpload::LogToCsv
end
def previous_postcode_known
case log.ppcodenk
when 1
1
when 0
2
end
checkbox_value(log.ppcodenk)
end
def homeless
@ -228,25 +218,19 @@ class BulkUpload::LogToCsv
end
def cbl
case log.cbl
when 0
2
when 1
1
end
checkbox_value(log.cbl)
end
def chr
case log.chr
when 0
2
when 1
1
end
checkbox_value(log.chr)
end
def cap
case log.cap
checkbox_value(log.cap)
end
def checkbox_value(field)
case field
when 0
2
when 1

Loading…
Cancel
Save